1 | // Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "skeleton_p.h" |
5 | |
6 | #include <QCoreApplication> |
7 | #include <QFile> |
8 | #include <QFileInfo> |
9 | |
10 | #include <Qt3DCore/qjoint.h> |
11 | #include <Qt3DRender/private/abstractrenderer_p.h> |
12 | #include <Qt3DRender/private/managers_p.h> |
13 | #include <Qt3DRender/private/nodemanagers_p.h> |
14 | #include <Qt3DRender/private/renderlogging_p.h> |
15 | #include <Qt3DCore/private/qskeleton_p.h> |
16 | #include <Qt3DCore/private/qskeletonloader_p.h> |
17 | #include <Qt3DCore/private/qmath3d_p.h> |
18 | |
19 | QT_BEGIN_NAMESPACE |
20 | |
21 | using namespace Qt3DCore; |
22 | |
23 | namespace Qt3DRender { |
24 | namespace Render { |
25 | |
26 | Skeleton::Skeleton() |
27 | : BackendNode(Qt3DCore::QBackendNode::ReadWrite) |
28 | , m_status(Qt3DCore::QSkeletonLoader::NotReady) |
29 | , m_createJoints(false) |
30 | , m_dataType(Unknown) |
31 | , m_skeletonManager(nullptr) |
32 | , m_jointManager(nullptr) |
33 | { |
34 | } |
35 | |
36 | void Skeleton::cleanup() |
37 | { |
38 | m_source.clear(); |
39 | m_status = Qt3DCore::QSkeletonLoader::NotReady; |
40 | m_createJoints = false; |
41 | m_rootJointId = Qt3DCore::QNodeId(); |
42 | clearData(); |
43 | setEnabled(false); |
44 | } |
45 | |
46 | void Skeleton::syncFromFrontEnd(const QNode *frontEnd, bool firstTime) |
47 | { |
48 | BackendNode::syncFromFrontEnd(frontEnd, firstTime); |
49 | const QAbstractSkeleton *node = qobject_cast<const QAbstractSkeleton *>(object: frontEnd); |
50 | if (!node) |
51 | return; |
52 | const QSkeleton *skeletonNode = qobject_cast<const QSkeleton *>(object: frontEnd); |
53 | const QSkeletonLoader *loaderNode = qobject_cast<const QSkeletonLoader *>(object: frontEnd); |
54 | |
55 | if (firstTime) { |
56 | m_skeletonHandle = m_skeletonManager->lookupHandle(id: peerId()); |
57 | |
58 | if (skeletonNode) { |
59 | m_dataType = Data; |
60 | m_rootJointId = skeletonNode->rootJoint()->id(); |
61 | if (!m_rootJointId.isNull()) { |
62 | markDirty(changes: AbstractRenderer::SkeletonDataDirty); |
63 | m_skeletonManager->addDirtySkeleton(dirtyFlag: SkeletonManager::SkeletonDataDirty, skeletonHandle: m_skeletonHandle); |
64 | } |
65 | } |
66 | |
67 | if (loaderNode) { |
68 | m_dataType = File; |
69 | m_source = loaderNode->source(); |
70 | if (!m_source.isEmpty()) { |
71 | markDirty(changes: AbstractRenderer::SkeletonDataDirty); |
72 | m_skeletonManager->addDirtySkeleton(dirtyFlag: SkeletonManager::SkeletonDataDirty, skeletonHandle: m_skeletonHandle); |
73 | } |
74 | } |
75 | } |
76 | |
77 | if (loaderNode) { |
78 | if (loaderNode->source() != m_source) { |
79 | m_source = loaderNode->source(); |
80 | markDirty(changes: AbstractRenderer::SkeletonDataDirty); |
81 | m_skeletonManager->addDirtySkeleton(dirtyFlag: SkeletonManager::SkeletonDataDirty, skeletonHandle: m_skeletonHandle); |
82 | } |
83 | m_createJoints = loaderNode->isCreateJointsEnabled(); |
84 | |
85 | auto newJointId = Qt3DCore::qIdForNode(node: loaderNode->rootJoint()); |
86 | if (newJointId != m_rootJointId) { |
87 | m_rootJointId = newJointId; |
88 | |
89 | // If using a QSkeletonLoader to create frontend QJoints, when those joints are |
90 | // set on the skeleton, we end up here. In order to allow the subsequent call |
91 | // to loadSkeleton(), see below, to build the internal data from the frontend |
92 | // joints rather than from the source url again, we need to change the data type |
93 | // to Data. |
94 | m_dataType = Data; |
95 | |
96 | // If the joint changes, we need to rebuild our internal SkeletonData and |
97 | // the relationships between joints and skeleton. Mark the skeleton data as |
98 | // dirty so that we get a loadSkeletonJob executed to process this skeleton. |
99 | if (!m_rootJointId.isNull()) { |
100 | markDirty(changes: AbstractRenderer::SkeletonDataDirty); |
101 | m_skeletonManager->addDirtySkeleton(dirtyFlag: SkeletonManager::SkeletonDataDirty, skeletonHandle: m_skeletonHandle); |
102 | } |
103 | } |
104 | } |
105 | |
106 | auto d = Qt3DCore::QAbstractSkeletonPrivate::get(q: node); |
107 | m_skeletonData.localPoses = d->m_localPoses; |
108 | } |
109 | |
110 | void Skeleton::setStatus(QSkeletonLoader::Status status) |
111 | { |
112 | if (status != m_status) |
113 | m_status = status; |
114 | } |
115 | |
116 | void Skeleton::clearData() |
117 | { |
118 | m_name.clear(); |
119 | m_skeletonData.joints.clear(); |
120 | m_skeletonData.localPoses.clear(); |
121 | m_skeletonData.jointNames.clear(); |
122 | m_skeletonData.jointIndices.clear(); |
123 | } |
124 | |
125 | void Skeleton::setSkeletonData(const SkeletonData &data) |
126 | { |
127 | m_skeletonData = data; |
128 | m_skinningPalette.resize(size: m_skeletonData.joints.size()); |
129 | } |
130 | |
131 | // Called from UpdateSkinningPaletteJob |
132 | void Skeleton::setLocalPose(HJoint jointHandle, const Qt3DCore::Sqt &localPose) |
133 | { |
134 | // Find the corresponding index into the JointInfo vector |
135 | // and set the local pose |
136 | const int jointIndex = m_skeletonData.jointIndices.value(key: jointHandle, defaultValue: -1); |
137 | Q_ASSERT(jointIndex != -1); |
138 | m_skeletonData.localPoses[jointIndex] = localPose; |
139 | } |
140 | |
141 | QVector<QMatrix4x4> Skeleton::calculateSkinningMatrixPalette() |
142 | { |
143 | const QVector<Sqt> &localPoses = m_skeletonData.localPoses; |
144 | QVector<JointInfo> &joints = m_skeletonData.joints; |
145 | for (int i = 0; i < m_skeletonData.joints.size(); ++i) { |
146 | // Calculate the global pose of this joint |
147 | JointInfo &joint = joints[i]; |
148 | if (joint.parentIndex == -1) { |
149 | joint.globalPose = localPoses[i].toMatrix(); |
150 | } else { |
151 | JointInfo &parentJoint = joints[joint.parentIndex]; |
152 | joint.globalPose = parentJoint.globalPose * localPoses[i].toMatrix(); |
153 | } |
154 | |
155 | m_skinningPalette[i] = joint.globalPose * joint.inverseBindPose; |
156 | } |
157 | return m_skinningPalette; |
158 | } |
159 | |
160 | |
161 | SkeletonFunctor::SkeletonFunctor(AbstractRenderer *renderer, |
162 | SkeletonManager *skeletonManager, |
163 | JointManager *jointManager) |
164 | : m_renderer(renderer) |
165 | , m_skeletonManager(skeletonManager) |
166 | , m_jointManager(jointManager) |
167 | { |
168 | } |
169 | |
170 | Qt3DCore::QBackendNode *SkeletonFunctor::create(Qt3DCore::QNodeId id) const |
171 | { |
172 | Skeleton *backend = m_skeletonManager->getOrCreateResource(id); |
173 | backend->setRenderer(m_renderer); |
174 | backend->setSkeletonManager(m_skeletonManager); |
175 | backend->setJointManager(m_jointManager); |
176 | return backend; |
177 | } |
178 | |
179 | Qt3DCore::QBackendNode *SkeletonFunctor::get(Qt3DCore::QNodeId id) const |
180 | { |
181 | return m_skeletonManager->lookupResource(id); |
182 | } |
183 | |
184 | void SkeletonFunctor::destroy(Qt3DCore::QNodeId id) const |
185 | { |
186 | m_skeletonManager->releaseResource(id); |
187 | } |
188 | |
189 | } // namespace Render |
190 | } // namespace Qt3DRender |
191 | |
192 | QT_END_NAMESPACE |
193 | |