summaryrefslogtreecommitdiffstats
path: root/src/Runtime/Source/runtimerender/Qt3DSRenderRay.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Runtime/Source/runtimerender/Qt3DSRenderRay.cpp')
-rw-r--r--src/Runtime/Source/runtimerender/Qt3DSRenderRay.cpp164
1 files changed, 164 insertions, 0 deletions
diff --git a/src/Runtime/Source/runtimerender/Qt3DSRenderRay.cpp b/src/Runtime/Source/runtimerender/Qt3DSRenderRay.cpp
new file mode 100644
index 00000000..5376b8b9
--- /dev/null
+++ b/src/Runtime/Source/runtimerender/Qt3DSRenderRay.cpp
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "Qt3DSRenderRay.h"
+#include "foundation/Qt3DSPlane.h"
+
+using namespace qt3ds::render;
+
+// http://www.siggraph.org/education/materials/HyperGraph/raytrace/rayplane_intersection.htm
+
+Option<QT3DSVec3> SRay::Intersect(const NVPlane &inPlane) const
+{
+ QT3DSF32 Vd = inPlane.n.dot(m_Direction);
+ if (fabs(Vd) < .0001f)
+ return Empty();
+ QT3DSF32 V0 = -1.0f * (inPlane.n.dot(m_Origin) + inPlane.d);
+ QT3DSF32 t = V0 / Vd;
+ return m_Origin + (m_Direction * t);
+}
+
+Option<SRayIntersectionResult> SRay::IntersectWithAABB(const QT3DSMat44 &inGlobalTransform,
+ const NVBounds3 &inBounds,
+ bool inForceIntersect) const
+{
+ // Intersect the origin with the AABB described by bounds.
+
+ // Scan each axis separately. This code basically finds the distance
+ // distance from the origin to the near and far bbox planes for a given
+ // axis. It then divides this distance by the direction for that axis to
+ // get a range of t [near,far] that the ray intersects assuming the ray is
+ // described via origin + t*(direction). Running through all three axis means
+ // that you need to min/max those ranges together to find a global min/max
+ // that the pick could possibly be in.
+
+ // Transform pick origin and direction into the subset's space.
+ QT3DSMat44 theOriginTransform = inGlobalTransform.getInverse();
+
+ QT3DSVec3 theTransformedOrigin = theOriginTransform.transform(m_Origin);
+ QT3DSF32 *outOriginTransformPtr(theOriginTransform.front());
+ outOriginTransformPtr[12] = outOriginTransformPtr[13] = outOriginTransformPtr[14] = 0.0f;
+ QT3DSVec3 theTransformedDirection = theOriginTransform.rotate(m_Direction);
+
+ static const QT3DSF32 KD_FLT_MAX = 3.40282346638528860e+38;
+ static const QT3DSF32 kEpsilon = 1e-5f;
+
+ QT3DSF32 theMinWinner = -KD_FLT_MAX;
+ QT3DSF32 theMaxWinner = KD_FLT_MAX;
+
+ for (QT3DSU32 theAxis = 0; theAxis < 3; ++theAxis) {
+ // Extract the ranges and direction for this axis
+ QT3DSF32 theMinBox = inBounds.minimum[theAxis];
+ QT3DSF32 theMaxBox = inBounds.maximum[theAxis];
+ QT3DSF32 theDirectionAxis = theTransformedDirection[theAxis];
+ QT3DSF32 theOriginAxis = theTransformedOrigin[theAxis];
+
+ QT3DSF32 theMinAxis = -KD_FLT_MAX;
+ QT3DSF32 theMaxAxis = KD_FLT_MAX;
+ if (theDirectionAxis > kEpsilon) {
+ theMinAxis = (theMinBox - theOriginAxis) / theDirectionAxis;
+ theMaxAxis = (theMaxBox - theOriginAxis) / theDirectionAxis;
+ } else if (theDirectionAxis < -kEpsilon) {
+ theMinAxis = (theMaxBox - theOriginAxis) / theDirectionAxis;
+ theMaxAxis = (theMinBox - theOriginAxis) / theDirectionAxis;
+ } else if ((theOriginAxis < theMinBox || theOriginAxis > theMaxBox)
+ && inForceIntersect == false) {
+ // Pickray is roughly parallel to the plane of the slab
+ // so, if the origin is not in the range, we have no intersection
+ return Empty();
+ }
+
+ // Shrink the intersections to find the closest hit
+ theMinWinner = NVMax(theMinWinner, theMinAxis);
+ theMaxWinner = NVMin(theMaxWinner, theMaxAxis);
+
+ if ((theMinWinner > theMaxWinner || theMaxWinner < 0) && inForceIntersect == false)
+ return Empty();
+ }
+
+ QT3DSVec3 scaledDir = theTransformedDirection * theMinWinner;
+ QT3DSVec3 newPosInLocal = theTransformedOrigin + scaledDir;
+ QT3DSVec3 newPosInGlobal = inGlobalTransform.transform(newPosInLocal);
+ QT3DSVec3 cameraToLocal = m_Origin - newPosInGlobal;
+
+ QT3DSF32 rayLengthSquared = cameraToLocal.magnitudeSquared();
+
+ QT3DSF32 xRange = inBounds.maximum.x - inBounds.minimum.x;
+ QT3DSF32 yRange = inBounds.maximum.y - inBounds.minimum.y;
+
+ QT3DSVec2 relXY;
+ relXY.x = (newPosInLocal[0] - inBounds.minimum.x) / xRange;
+ relXY.y = (newPosInLocal[1] - inBounds.minimum.y) / yRange;
+
+ return SRayIntersectionResult(rayLengthSquared, relXY);
+}
+
+Option<QT3DSVec2> SRay::GetRelative(const QT3DSMat44 &inGlobalTransform, const NVBounds3 &inBounds,
+ SBasisPlanes::Enum inPlane) const
+{
+ QT3DSMat44 theOriginTransform = inGlobalTransform.getInverse();
+
+ QT3DSVec3 theTransformedOrigin = theOriginTransform.transform(m_Origin);
+ QT3DSF32 *outOriginTransformPtr(theOriginTransform.front());
+ outOriginTransformPtr[12] = outOriginTransformPtr[13] = outOriginTransformPtr[14] = 0.0f;
+ QT3DSVec3 theTransformedDirection = theOriginTransform.rotate(m_Direction);
+
+ // The XY plane is going to be a plane with either positive or negative Z direction that runs
+ // through
+ QT3DSVec3 theDirection(0, 0, 1);
+ QT3DSVec3 theRight(1, 0, 0);
+ QT3DSVec3 theUp(0, 1, 0);
+ switch (inPlane) {
+ case SBasisPlanes::XY:
+ break;
+ case SBasisPlanes::XZ:
+ theDirection = QT3DSVec3(0, 1, 0);
+ theUp = QT3DSVec3(0, 0, 1);
+ break;
+ case SBasisPlanes::YZ:
+ theDirection = QT3DSVec3(1, 0, 0);
+ theRight = QT3DSVec3(0, 0, 1);
+ break;
+ }
+ NVPlane thePlane(theDirection, theDirection.dot(theTransformedDirection) > 0.0f
+ ? theDirection.dot(inBounds.maximum)
+ : theDirection.dot(inBounds.minimum));
+
+ SRay relativeRay(theTransformedOrigin, theTransformedDirection);
+ Option<QT3DSVec3> localIsect = relativeRay.Intersect(thePlane);
+ if (localIsect.hasValue()) {
+ QT3DSF32 xRange = theRight.dot(inBounds.maximum) - theRight.dot(inBounds.minimum);
+ QT3DSF32 yRange = theUp.dot(inBounds.maximum) - theUp.dot(inBounds.minimum);
+ QT3DSF32 xOrigin = xRange / 2.0f + theRight.dot(inBounds.minimum);
+ QT3DSF32 yOrigin = yRange / 2.0f + theUp.dot(inBounds.minimum);
+ return QT3DSVec2((theRight.dot(*localIsect) - xOrigin) / xRange,
+ (theUp.dot(*localIsect) - yOrigin) / yRange);
+ }
+ return Empty();
+}