summaryrefslogtreecommitdiffstats
path: root/src/Runtime/ogl-runtime/src/runtimerender/graphobjects/Qt3DSRenderNode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Runtime/ogl-runtime/src/runtimerender/graphobjects/Qt3DSRenderNode.cpp')
-rw-r--r--src/Runtime/ogl-runtime/src/runtimerender/graphobjects/Qt3DSRenderNode.cpp499
1 files changed, 499 insertions, 0 deletions
diff --git a/src/Runtime/ogl-runtime/src/runtimerender/graphobjects/Qt3DSRenderNode.cpp b/src/Runtime/ogl-runtime/src/runtimerender/graphobjects/Qt3DSRenderNode.cpp
new file mode 100644
index 00000000..bf47bb8d
--- /dev/null
+++ b/src/Runtime/ogl-runtime/src/runtimerender/graphobjects/Qt3DSRenderNode.cpp
@@ -0,0 +1,499 @@
+/****************************************************************************
+**
+** 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 "Qt3DSRenderModel.h"
+#include "Qt3DSRenderNode.h"
+#include "Qt3DSRenderText.h"
+#include "Qt3DSRenderer.h"
+#include "Qt3DSRenderPathManager.h"
+#include "Qt3DSRenderPath.h"
+
+using namespace qt3ds::render;
+
+SNode::SNode(GraphObjectTypes::Enum inGraphObjectType)
+ : SGraphObject(inGraphObjectType)
+ , m_Rotation(0, 0, 0) // Radians
+ , m_Position(0, 0, 0)
+ , m_Scale(1, 1, 1)
+ , m_Pivot(0, 0, 0)
+ , m_RotationOrder(EulOrdYXZs)
+ , m_LocalOpacity(1.0f)
+ , m_GlobalOpacity(1.0f)
+ , m_SkeletonId(-1)
+ , m_Parent(NULL)
+ , m_NextSibling(NULL)
+ , m_PreviousSibling(NULL)
+ , m_FirstChild(NULL)
+ , m_DFSIndex(0)
+{
+ m_Flags.SetDirty(true);
+ m_Flags.SetTransformDirty(true);
+ m_Flags.SetLeftHanded(true);
+ m_Flags.SetActive(true);
+ m_Flags.SetLocallyPickable(true);
+}
+
+SNode::SNode(const SNode &inCloningObject, NVAllocatorCallback &inAllocator)
+ : SGraphObject(inCloningObject, inAllocator)
+ , m_Rotation(inCloningObject.m_Rotation) // Radians
+ , m_Position(inCloningObject.m_Position)
+ , m_Scale(inCloningObject.m_Scale)
+ , m_Pivot(inCloningObject.m_Pivot)
+ , m_RotationOrder(inCloningObject.m_RotationOrder)
+ , m_LocalOpacity(inCloningObject.m_LocalOpacity)
+ , m_LocalTransform(inCloningObject.m_LocalTransform)
+ , m_GlobalTransform(inCloningObject.m_GlobalTransform)
+ , m_GlobalOpacity(inCloningObject.m_GlobalOpacity)
+ , m_SkeletonId(inCloningObject.m_SkeletonId)
+ , m_Parent(NULL)
+ , m_NextSibling(NULL)
+ , m_PreviousSibling(NULL)
+ , m_FirstChild(NULL)
+ , m_DFSIndex(0)
+{
+ m_Flags.SetDirty(true);
+ m_Flags.SetTransformDirty(true);
+ m_Flags.SetLeftHanded(true);
+ m_Flags.SetActive(true);
+ m_Flags.SetLocallyPickable(true);
+
+ // for ( SNode* theChild = m_FirstChild; theChild != NULL; theChild = theChild->m_NextSibling )
+ //{
+ // SNode* theClonedChild = static_cast<SNode*>( CGraphObjectFactory::CloneGraphObject(
+ //*theChild, inAllocator ) );
+ // AddChild( *theClonedChild );
+ //}
+}
+
+// Sets this object dirty and walks down the graph setting all
+// children who are not dirty to be dirty.
+void SNode::MarkDirty(NodeTransformDirtyFlag::Enum inTransformDirty)
+{
+ if (m_Flags.IsTransformDirty() == false)
+ m_Flags.SetTransformDirty(inTransformDirty != NodeTransformDirtyFlag::TransformNotDirty);
+ if (m_Flags.IsDirty() == false) {
+ m_Flags.SetDirty(true);
+ for (SNode *child = m_FirstChild; child; child = child->m_NextSibling)
+ child->MarkDirty(inTransformDirty);
+ }
+}
+
+// Calculate global transform and opacity
+// Walks up the graph ensure all parents are not dirty so they have
+// valid global transforms.
+
+bool SNode::CalculateGlobalVariables()
+{
+ bool retval = m_Flags.IsDirty();
+ if (retval) {
+ m_Flags.SetDirty(false);
+ if (m_Flags.IsTransformDirty())
+ CalculateLocalTransform();
+ m_GlobalOpacity = m_LocalOpacity;
+ if (m_Parent) {
+ // Layer transforms do not flow down but affect the final layer's rendered
+ // representation.
+ retval = m_Parent->CalculateGlobalVariables() || retval;
+ if (m_Parent->m_Type != GraphObjectTypes::Layer) {
+ m_GlobalOpacity *= m_Parent->m_GlobalOpacity;
+ if (m_Flags.IsIgnoreParentTransform() == false)
+ m_GlobalTransform = m_Parent->m_GlobalTransform * m_LocalTransform;
+ else
+ m_GlobalTransform = m_LocalTransform;
+ } else
+ m_GlobalTransform = m_LocalTransform;
+
+ m_Flags.SetGlobalActive(m_Flags.IsActive() && m_Parent->m_Flags.IsGloballyActive());
+ m_Flags.SetGloballyPickable(m_Flags.IsLocallyPickable()
+ || m_Parent->m_Flags.IsGloballyPickable());
+ } else {
+ m_GlobalTransform = m_LocalTransform;
+ m_Flags.SetGlobalActive(m_Flags.IsActive());
+ m_Flags.SetGloballyPickable(m_Flags.IsLocallyPickable());
+ }
+ }
+ // We always clear dirty in a reasonable manner but if we aren't active
+ // there is no reason to tell the universe if we are dirty or not.
+ return retval && m_Flags.IsActive();
+}
+
+// Create some mapping of euler angles to their axis mapping.
+#define ITERATE_POSSIBLE_EULER_ANGLES \
+ HANDLE_EULER_ANGLE(EulOrdXYZs, X, Y, Z) \
+ HANDLE_EULER_ANGLE(EulOrdXYXs, X, Y, X) \
+ HANDLE_EULER_ANGLE(EulOrdXZYs, X, Z, Y) \
+ HANDLE_EULER_ANGLE(EulOrdXZXs, X, Z, X) \
+ HANDLE_EULER_ANGLE(EulOrdYZXs, Y, Z, X) \
+ HANDLE_EULER_ANGLE(EulOrdYZYs, Y, Z, Y) \
+ HANDLE_EULER_ANGLE(EulOrdYXZs, Y, X, Z) \
+ HANDLE_EULER_ANGLE(EulOrdYXYs, Y, X, Y) \
+ HANDLE_EULER_ANGLE(EulOrdZXYs, Z, X, Y) \
+ HANDLE_EULER_ANGLE(EulOrdZXZs, Z, X, Z) \
+ HANDLE_EULER_ANGLE(EulOrdZYXs, Z, Y, X) \
+ HANDLE_EULER_ANGLE(EulOrdZYZs, Z, Y, Z) \
+ HANDLE_EULER_ANGLE(EulOrdZYXr, Z, Y, X) \
+ HANDLE_EULER_ANGLE(EulOrdXYXr, X, Y, X) \
+ HANDLE_EULER_ANGLE(EulOrdYZXr, Y, Z, X) \
+ HANDLE_EULER_ANGLE(EulOrdXZXr, X, Z, X) \
+ HANDLE_EULER_ANGLE(EulOrdXZYr, X, Z, Y) \
+ HANDLE_EULER_ANGLE(EulOrdYZYr, Y, Z, Y) \
+ HANDLE_EULER_ANGLE(EulOrdZXYr, Z, X, Y) \
+ HANDLE_EULER_ANGLE(EulOrdYXYr, Y, X, Y) \
+ HANDLE_EULER_ANGLE(EulOrdYXZr, Y, X, Z) \
+ HANDLE_EULER_ANGLE(EulOrdZXZr, Z, X, Z) \
+ HANDLE_EULER_ANGLE(EulOrdXYZr, X, Y, Z) \
+ HANDLE_EULER_ANGLE(EulOrdZYZr, Z, Y, Z)
+
+inline EulerAngles RotationAndOrderToShoemake(QT3DSVec3 inRotation, QT3DSU32 inOrder)
+{
+ EulerAngles retval;
+ retval.w = (QT3DSF32)inOrder;
+ int X = 0;
+ int Y = 1;
+ int Z = 2;
+
+ switch (inOrder) {
+#define HANDLE_EULER_ANGLE(order, xIdx, yIdx, zIdx) \
+ case order: \
+ retval.x = -inRotation[xIdx]; \
+ retval.y = -inRotation[yIdx]; \
+ retval.z = -inRotation[zIdx]; \
+ break;
+ ITERATE_POSSIBLE_EULER_ANGLES
+#undef HANDLE_EULER_ANGLE
+ default:
+ QT3DS_ASSERT(false);
+ retval.x = inRotation[X];
+ retval.y = inRotation[Y];
+ retval.z = inRotation[Z];
+ break;
+ }
+ return retval;
+}
+
+QT3DSVec3 SNode::GetRotationVectorFromRotationMatrix(const QT3DSMat33 &inMatrix) const
+{
+ QT3DSMat44 theConvertMatrix(inMatrix, QT3DSVec3(0, 0, 0));
+ if (m_Flags.IsLeftHanded())
+ SNode::FlipCoordinateSystem(theConvertMatrix);
+ qt3ds::render::CEulerAngleConverter theConverter;
+ qt3ds::render::HMatrix *theHMatrix =
+ reinterpret_cast<qt3ds::render::HMatrix *>(theConvertMatrix.front());
+ qt3ds::render::EulerAngles theAngles = theConverter.Eul_FromHMatrix(*theHMatrix, m_RotationOrder);
+ return GetRotationVectorFromEulerAngles(theAngles);
+}
+
+QT3DSVec3 SNode::GetRotationVectorFromEulerAngles(const EulerAngles &inAngles)
+{
+ QT3DSVec3 retval(0, 0, 0);
+ int X = 0;
+ int Y = 1;
+ int Z = 2;
+ switch ((int)inAngles.w) {
+#define HANDLE_EULER_ANGLE(order, xIdx, yIdx, zIdx) \
+ case order: \
+ retval[xIdx] = -inAngles.x; \
+ retval[yIdx] = -inAngles.y; \
+ retval[zIdx] = -inAngles.z; \
+ break;
+ ITERATE_POSSIBLE_EULER_ANGLES
+#undef HANDLE_EULER_ANGLE
+ default:
+ QT3DS_ASSERT(false);
+ retval.x = inAngles.x;
+ retval.y = inAngles.y;
+ retval.z = inAngles.z;
+ break;
+ }
+
+ return retval;
+}
+
+void SNode::CalculateRotationMatrix(QT3DSMat44 &outMatrix) const
+{
+ StaticAssert<sizeof(QT3DSMat44) == sizeof(HMatrix)>::valid_expression();
+ CEulerAngleConverter theConverter;
+ EulerAngles theAngles(RotationAndOrderToShoemake(m_Rotation, (int)m_RotationOrder));
+ HMatrix *theMatrix = reinterpret_cast<HMatrix *>(&outMatrix);
+ theConverter.Eul_ToHMatrix(theAngles, *theMatrix);
+}
+
+void SNode::FlipCoordinateSystem(QT3DSMat44 &inMatrix)
+{
+ QT3DSF32 *writePtr(inMatrix.front());
+ // rotation conversion
+ writePtr[0 * 4 + 2] *= -1;
+ writePtr[1 * 4 + 2] *= -1;
+ writePtr[2 * 4 + 0] *= -1;
+ writePtr[2 * 4 + 1] *= -1;
+
+ // translation conversion
+ writePtr[3 * 4 + 2] *= -1;
+}
+
+void SNode::CalculateLocalTransform()
+{
+ m_Flags.SetTransformDirty(false);
+ bool leftHanded = m_Flags.IsLeftHanded();
+ m_LocalTransform = QT3DSMat44::createIdentity();
+ m_GlobalTransform = m_LocalTransform;
+ QT3DSF32 *writePtr = m_LocalTransform.front();
+ QT3DSVec3 theScaledPivot(-m_Pivot[0] * m_Scale[0], -m_Pivot[1] * m_Scale[1],
+ -m_Pivot[2] * m_Scale[2]);
+ m_LocalTransform.column0[0] = m_Scale[0];
+ m_LocalTransform.column1[1] = m_Scale[1];
+ m_LocalTransform.column2[2] = m_Scale[2];
+
+ writePtr[12] = theScaledPivot[0];
+ writePtr[13] = theScaledPivot[1];
+ if (leftHanded)
+ writePtr[14] = theScaledPivot[2];
+ else
+ writePtr[14] = -theScaledPivot[2];
+
+ QT3DSMat44 theRotationTransform;
+ CalculateRotationMatrix(theRotationTransform);
+ // may need column conversion in here somewhere.
+ m_LocalTransform = theRotationTransform * m_LocalTransform;
+
+ writePtr[12] += m_Position[0];
+ writePtr[13] += m_Position[1];
+ if (leftHanded)
+ writePtr[14] = writePtr[14] + m_Position[2];
+ else
+ writePtr[14] = writePtr[14] - m_Position[2];
+
+ if (leftHanded) {
+ FlipCoordinateSystem(m_LocalTransform);
+ }
+}
+
+void SNode::SetLocalTransformFromMatrix(QT3DSMat44 &inTransform)
+{
+ m_Flags.SetTransformDirty(true);
+
+ // clear pivot
+ m_Pivot[0] = m_Pivot[1] = m_Pivot[2] = 0.0f;
+
+ // set translation
+ m_Position[0] = inTransform[3][0];
+ m_Position[1] = inTransform[3][1];
+ m_Position[2] = inTransform[3][2];
+ // set scale
+ m_Scale[0] = inTransform.column0.magnitude();
+ m_Scale[1] = inTransform.column1.magnitude();
+ m_Scale[2] = inTransform.column2.magnitude();
+
+ // make sure there is no zero value
+ m_Scale[0] = (m_Scale[0] == 0.0) ? 1.0f : m_Scale[0];
+ m_Scale[1] = (m_Scale[1] == 0.0) ? 1.0f : m_Scale[1];
+ m_Scale[2] = (m_Scale[2] == 0.0) ? 1.0f : m_Scale[2];
+
+ // extract rotation by first dividing through scale value
+ float invScaleX = 1.0f / m_Scale[0];
+ float invScaleY = 1.0f / m_Scale[1];
+ float invScaleZ = 1.0f / m_Scale[2];
+
+ inTransform[0][0] *= invScaleX;
+ inTransform[0][1] *= invScaleX;
+ inTransform[0][2] *= invScaleX;
+ inTransform[1][0] *= invScaleY;
+ inTransform[1][1] *= invScaleY;
+ inTransform[1][2] *= invScaleY;
+ inTransform[2][0] *= invScaleZ;
+ inTransform[2][1] *= invScaleZ;
+ inTransform[2][2] *= invScaleZ;
+
+ QT3DSMat33 theRotationMatrix(inTransform.column0.getXYZ(), inTransform.column1.getXYZ(),
+ inTransform.column2.getXYZ());
+ m_Rotation = GetRotationVectorFromRotationMatrix(theRotationMatrix);
+}
+
+void SNode::AddChild(SNode &inChild)
+{
+ if (inChild.m_Parent)
+ inChild.m_Parent->RemoveChild(inChild);
+ inChild.m_Parent = this;
+ if (m_FirstChild == nullptr) {
+ m_FirstChild = &inChild;
+ inChild.m_NextSibling = nullptr;
+ inChild.m_PreviousSibling = nullptr;
+ } else {
+ SNode *lastChild = GetLastChild();
+ if (lastChild) {
+ lastChild->m_NextSibling = &inChild;
+ inChild.m_PreviousSibling = lastChild;
+ inChild.m_NextSibling = nullptr;
+ } else {
+ QT3DS_ASSERT(false); // no last child but first child isn't null?
+ }
+ }
+}
+
+void SNode::RemoveChild(SNode &inChild)
+{
+ if (inChild.m_Parent != this) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ for (SNode *child = m_FirstChild; child; child = child->m_NextSibling) {
+ if (child == &inChild) {
+ if (child->m_PreviousSibling)
+ child->m_PreviousSibling->m_NextSibling = child->m_NextSibling;
+ if (child->m_NextSibling)
+ child->m_NextSibling->m_PreviousSibling = child->m_PreviousSibling;
+ child->m_Parent = NULL;
+ if (m_FirstChild == child)
+ m_FirstChild = child->m_NextSibling;
+ child->m_NextSibling = NULL;
+ child->m_PreviousSibling = NULL;
+ return;
+ }
+ }
+ QT3DS_ASSERT(false);
+}
+
+SNode *SNode::GetLastChild()
+{
+ SNode *lastChild = NULL;
+ // empty loop intentional
+ for (lastChild = m_FirstChild; lastChild && lastChild->m_NextSibling;
+ lastChild = lastChild->m_NextSibling) {
+ }
+ return lastChild;
+}
+
+void SNode::RemoveFromGraph()
+{
+ if (m_Parent)
+ m_Parent->RemoveChild(*this);
+
+ m_NextSibling = NULL;
+
+ // Orphan all of my children.
+ SNode *nextSibling = NULL;
+ for (SNode *child = m_FirstChild; child != NULL; child = nextSibling) {
+ child->m_PreviousSibling = NULL;
+ child->m_Parent = NULL;
+ nextSibling = child->m_NextSibling;
+ child->m_NextSibling = NULL;
+ }
+}
+
+NVBounds3 SNode::GetBounds(IBufferManager &inManager, IPathManager &inPathManager,
+ bool inIncludeChildren, IQt3DSRenderNodeFilter *inChildFilter) const
+{
+ NVBounds3 retval;
+ retval.setEmpty();
+ if (inIncludeChildren)
+ retval = GetChildBounds(inManager, inPathManager, inChildFilter);
+
+ if (m_Type == GraphObjectTypes::Model)
+ retval.include(static_cast<const SModel *>(this)->GetModelBounds(inManager));
+ else if (m_Type == GraphObjectTypes::Text)
+ retval.include(static_cast<const SText *>(this)->GetTextBounds());
+ else if (m_Type == GraphObjectTypes::Path)
+ retval.include(inPathManager.GetBounds(*static_cast<const SPath *>(this)));
+ return retval;
+}
+
+NVBounds3 SNode::GetChildBounds(IBufferManager &inManager, IPathManager &inPathManager,
+ IQt3DSRenderNodeFilter *inChildFilter) const
+{
+ NVBounds3 retval;
+ retval.setEmpty();
+ for (SNode *child = m_FirstChild; child != NULL; child = child->m_NextSibling) {
+ if (inChildFilter == NULL || inChildFilter->IncludeNode(*child)) {
+ NVBounds3 childBounds;
+ if (child->m_Flags.IsTransformDirty())
+ child->CalculateLocalTransform();
+ childBounds = child->GetBounds(inManager, inPathManager);
+ if (childBounds.isEmpty() == false) {
+ // Transform the bounds into our local space.
+ childBounds.transform(child->m_LocalTransform);
+ retval.include(childBounds);
+ }
+ }
+ }
+ return retval;
+}
+
+QT3DSVec3 SNode::GetGlobalPos() const
+{
+ return m_GlobalTransform.getPosition();
+}
+
+QT3DSVec3 SNode::GetDirection() const
+{
+ const QT3DSF32 *dataPtr(m_GlobalTransform.front());
+ QT3DSVec3 retval(dataPtr[8], dataPtr[9], dataPtr[10]);
+ retval.normalize();
+ return retval;
+}
+
+QT3DSVec3 SNode::GetScalingCorrectDirection() const
+{
+ QT3DSMat33 theDirMatrix(m_GlobalTransform.getUpper3x3().getInverse().getTranspose());
+ QT3DSVec3 theOriginalDir(0, 0, -1);
+ QT3DSVec3 retval = theDirMatrix.transform(theOriginalDir);
+ retval.normalize();
+ return retval;
+}
+
+QT3DSVec3 SNode::GetGlobalPivot() const
+{
+ QT3DSVec3 retval(m_Position);
+ retval.z *= -1;
+
+ if (m_Parent && m_Parent->m_Type != GraphObjectTypes::Layer)
+ return m_Parent->m_GlobalTransform.transform(retval);
+
+ return retval;
+}
+
+void SNode::CalculateMVPAndNormalMatrix(const QT3DSMat44 &inViewProjection, QT3DSMat44 &outMVP,
+ QT3DSMat33 &outNormalMatrix) const
+{
+ outMVP = inViewProjection * m_GlobalTransform;
+ CalculateNormalMatrix(outNormalMatrix);
+}
+
+void SNode::GetMatrixUpper3x3(QT3DSMat33 &outDest, const QT3DSMat44 &inSrc)
+{
+ outDest.column0 = QT3DSVec3(inSrc.column0[0], inSrc.column0[1], inSrc.column0[2]);
+ outDest.column1 = QT3DSVec3(inSrc.column1[0], inSrc.column1[1], inSrc.column1[2]);
+ outDest.column2 = QT3DSVec3(inSrc.column2[0], inSrc.column2[1], inSrc.column2[2]);
+}
+
+void SNode::CalculateNormalMatrix(QT3DSMat33 &outNormalMatrix) const
+{
+ GetMatrixUpper3x3(outNormalMatrix, m_GlobalTransform);
+ outNormalMatrix = outNormalMatrix.getInverse().getTranspose();
+}