1/****************************************************************************
2**
3** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL3$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "skeleton_p.h"
38
39#include <QCoreApplication>
40#include <QFile>
41#include <QFileInfo>
42
43#include <Qt3DCore/qjoint.h>
44#include <Qt3DRender/private/abstractrenderer_p.h>
45#include <Qt3DRender/private/managers_p.h>
46#include <Qt3DRender/private/nodemanagers_p.h>
47#include <Qt3DRender/private/renderlogging_p.h>
48#include <Qt3DCore/private/qskeletoncreatedchange_p.h>
49#include <Qt3DCore/private/qskeleton_p.h>
50#include <Qt3DCore/private/qskeletonloader_p.h>
51#include <Qt3DCore/private/qmath3d_p.h>
52
53QT_BEGIN_NAMESPACE
54
55using namespace Qt3DCore;
56
57namespace Qt3DRender {
58namespace Render {
59
60Skeleton::Skeleton()
61 : BackendNode(Qt3DCore::QBackendNode::ReadWrite)
62 , m_status(Qt3DCore::QSkeletonLoader::NotReady)
63 , m_createJoints(false)
64 , m_dataType(Unknown)
65 , m_skeletonManager(nullptr)
66 , m_jointManager(nullptr)
67{
68}
69
70void Skeleton::cleanup()
71{
72 m_source.clear();
73 m_status = Qt3DCore::QSkeletonLoader::NotReady;
74 m_createJoints = false;
75 m_rootJointId = Qt3DCore::QNodeId();
76 clearData();
77 setEnabled(false);
78}
79
80void Skeleton::syncFromFrontEnd(const QNode *frontEnd, bool firstTime)
81{
82 BackendNode::syncFromFrontEnd(frontEnd, firstTime);
83 const QAbstractSkeleton *node = qobject_cast<const QAbstractSkeleton *>(object: frontEnd);
84 if (!node)
85 return;
86 const QSkeleton *skeletonNode = qobject_cast<const QSkeleton *>(object: frontEnd);
87 const QSkeletonLoader *loaderNode = qobject_cast<const QSkeletonLoader *>(object: frontEnd);
88
89 if (firstTime) {
90 m_skeletonHandle = m_skeletonManager->lookupHandle(id: peerId());
91
92 if (skeletonNode) {
93 m_dataType = Data;
94 m_rootJointId = skeletonNode->rootJoint()->id();
95 if (!m_rootJointId.isNull()) {
96 markDirty(changes: AbstractRenderer::SkeletonDataDirty);
97 m_skeletonManager->addDirtySkeleton(dirtyFlag: SkeletonManager::SkeletonDataDirty, skeletonHandle: m_skeletonHandle);
98 }
99 }
100
101 if (loaderNode) {
102 m_dataType = File;
103 m_source = loaderNode->source();
104 if (!m_source.isEmpty()) {
105 markDirty(changes: AbstractRenderer::SkeletonDataDirty);
106 m_skeletonManager->addDirtySkeleton(dirtyFlag: SkeletonManager::SkeletonDataDirty, skeletonHandle: m_skeletonHandle);
107 }
108 }
109 }
110
111 if (loaderNode) {
112 if (loaderNode->source() != m_source) {
113 m_source = loaderNode->source();
114 markDirty(changes: AbstractRenderer::SkeletonDataDirty);
115 m_skeletonManager->addDirtySkeleton(dirtyFlag: SkeletonManager::SkeletonDataDirty, skeletonHandle: m_skeletonHandle);
116 }
117 m_createJoints = loaderNode->isCreateJointsEnabled();
118
119 auto newJointId = Qt3DCore::qIdForNode(node: loaderNode->rootJoint());
120 if (newJointId != m_rootJointId) {
121 m_rootJointId = newJointId;
122
123 // If using a QSkeletonLoader to create frontend QJoints, when those joints are
124 // set on the skeleton, we end up here. In order to allow the subsequent call
125 // to loadSkeleton(), see below, to build the internal data from the frontend
126 // joints rather than from the source url again, we need to change the data type
127 // to Data.
128 m_dataType = Data;
129
130 // If the joint changes, we need to rebuild our internal SkeletonData and
131 // the relationships between joints and skeleton. Mark the skeleton data as
132 // dirty so that we get a loadSkeletonJob executed to process this skeleton.
133 if (!m_rootJointId.isNull()) {
134 markDirty(changes: AbstractRenderer::SkeletonDataDirty);
135 m_skeletonManager->addDirtySkeleton(dirtyFlag: SkeletonManager::SkeletonDataDirty, skeletonHandle: m_skeletonHandle);
136 }
137 }
138 }
139
140 auto d = Qt3DCore::QAbstractSkeletonPrivate::get(q: node);
141 m_skeletonData.localPoses = d->m_localPoses;
142}
143
144void Skeleton::setStatus(QSkeletonLoader::Status status)
145{
146 if (status != m_status)
147 m_status = status;
148}
149
150void Skeleton::clearData()
151{
152 m_name.clear();
153 m_skeletonData.joints.clear();
154 m_skeletonData.localPoses.clear();
155 m_skeletonData.jointNames.clear();
156 m_skeletonData.jointIndices.clear();
157}
158
159void Skeleton::setSkeletonData(const SkeletonData &data)
160{
161 m_skeletonData = data;
162 m_skinningPalette.resize(asize: m_skeletonData.joints.size());
163}
164
165// Called from UpdateSkinningPaletteJob
166void Skeleton::setLocalPose(HJoint jointHandle, const Qt3DCore::Sqt &localPose)
167{
168 // Find the corresponding index into the JointInfo vector
169 // and set the local pose
170 const int jointIndex = m_skeletonData.jointIndices.value(akey: jointHandle, adefaultValue: -1);
171 Q_ASSERT(jointIndex != -1);
172 m_skeletonData.localPoses[jointIndex] = localPose;
173}
174
175QVector<QMatrix4x4> Skeleton::calculateSkinningMatrixPalette()
176{
177 const QVector<Sqt> &localPoses = m_skeletonData.localPoses;
178 QVector<JointInfo> &joints = m_skeletonData.joints;
179 for (int i = 0; i < m_skeletonData.joints.size(); ++i) {
180 // Calculate the global pose of this joint
181 JointInfo &joint = joints[i];
182 if (joint.parentIndex == -1) {
183 joint.globalPose = localPoses[i].toMatrix();
184 } else {
185 JointInfo &parentJoint = joints[joint.parentIndex];
186 joint.globalPose = parentJoint.globalPose * localPoses[i].toMatrix();
187 }
188
189 m_skinningPalette[i] = joint.globalPose * joint.inverseBindPose;
190 }
191 return m_skinningPalette;
192}
193
194
195SkeletonFunctor::SkeletonFunctor(AbstractRenderer *renderer,
196 SkeletonManager *skeletonManager,
197 JointManager *jointManager)
198 : m_renderer(renderer)
199 , m_skeletonManager(skeletonManager)
200 , m_jointManager(jointManager)
201{
202}
203
204Qt3DCore::QBackendNode *SkeletonFunctor::create(const Qt3DCore::QNodeCreatedChangeBasePtr &change) const
205{
206 Skeleton *backend = m_skeletonManager->getOrCreateResource(id: change->subjectId());
207 backend->setRenderer(m_renderer);
208 backend->setSkeletonManager(m_skeletonManager);
209 backend->setJointManager(m_jointManager);
210 return backend;
211}
212
213Qt3DCore::QBackendNode *SkeletonFunctor::get(Qt3DCore::QNodeId id) const
214{
215 return m_skeletonManager->lookupResource(id);
216}
217
218void SkeletonFunctor::destroy(Qt3DCore::QNodeId id) const
219{
220 m_skeletonManager->releaseResource(id);
221}
222
223} // namespace Render
224} // namespace Qt3DRender
225
226QT_END_NAMESPACE
227

source code of qt3d/src/render/geometry/skeleton.cpp