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
19QT_BEGIN_NAMESPACE
20
21using namespace Qt3DCore;
22
23namespace Qt3DRender {
24namespace Render {
25
26Skeleton::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
36void 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
46void 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
110void Skeleton::setStatus(QSkeletonLoader::Status status)
111{
112 if (status != m_status)
113 m_status = status;
114}
115
116void 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
125void 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
132void 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
141QVector<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
161SkeletonFunctor::SkeletonFunctor(AbstractRenderer *renderer,
162 SkeletonManager *skeletonManager,
163 JointManager *jointManager)
164 : m_renderer(renderer)
165 , m_skeletonManager(skeletonManager)
166 , m_jointManager(jointManager)
167{
168}
169
170Qt3DCore::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
179Qt3DCore::QBackendNode *SkeletonFunctor::get(Qt3DCore::QNodeId id) const
180{
181 return m_skeletonManager->lookupResource(id);
182}
183
184void SkeletonFunctor::destroy(Qt3DCore::QNodeId id) const
185{
186 m_skeletonManager->releaseResource(id);
187}
188
189} // namespace Render
190} // namespace Qt3DRender
191
192QT_END_NAMESPACE
193

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