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 "qjoint.h"
5#include "qjoint_p.h"
6
7QT_BEGIN_NAMESPACE
8
9namespace Qt3DCore {
10
11QJointPrivate::QJointPrivate()
12 : QNodePrivate()
13 , m_inverseBindMatrix()
14 , m_rotation()
15 , m_translation()
16 , m_scale(1.0f, 1.0f, 1.0f)
17{
18}
19
20/*!
21 \qmltype Joint
22 \inqmlmodule Qt3D.Core
23 \inherits Node
24 \instantiates Qt3DCore::QJoint
25 \since 5.10
26 \brief Used to transforms parts of skinned meshes.
27
28 The Joint node is used to build skeletons as part of the skinned mesh
29 support in Qt 3D. A joint can be transformed by way of its scale, rotation
30 and translation properties. Any mesh vertices that are bound to the joint
31 will have their transformations updated accordingly.
32*/
33
34/*!
35 \qmlproperty vector3d Joint::scale
36
37 Holds the uniform scale of the joint.
38*/
39
40/*!
41 \qmlproperty quaternion Joint::rotation
42
43 Holds the rotation of the joint as quaternion.
44*/
45
46/*!
47 \qmlproperty vector3d Joint::translation
48
49 Holds the translation of the joint as vector3d.
50*/
51
52/*!
53 \qmlproperty real Joint::rotationX
54
55 Holds the x rotation of the joint as an Euler angle.
56*/
57
58/*!
59 \qmlproperty real Joint::rotationY
60
61 Holds the y rotation of the joint as an Euler angle.
62*/
63
64/*!
65 \qmlproperty real Joint::rotationZ
66
67 Holds the z rotation of the joint as an Euler angle.
68*/
69
70/*!
71 \qmlproperty matrix4x4 Joint::inverseBindMatrix
72
73 Holds the inverse bind matrix of the joint. This is used to transform
74 vertices from model space into the space of this joint so they can
75 subsequently be multiplied by the joint's global transform to perform
76 the skinning operation.
77*/
78
79/*!
80 \class Qt3DCore::QJoint
81 \inmodule Qt3DCore
82 \inherits Qt3DCore::QNode
83 \since 5.10
84 \brief Used to transforms parts of skinned meshes.
85
86 The QJoint node is used to build skeletons as part of the skinned mesh
87 support in Qt 3D. A joint can be transformed by way of its scale, rotation
88 and translation properties. Any mesh vertices that are bound to the joint
89 will have their transformations updated accordingly.
90*/
91
92/*!
93 Constructs a new QJoint with \a parent.
94*/
95QJoint::QJoint(Qt3DCore::QNode *parent)
96 : QNode(*new QJointPrivate, parent)
97{
98}
99
100/*! \internal */
101QJoint::~QJoint()
102{
103}
104
105/*!
106 \property Qt3DCore::QJoint::scale
107
108 Holds the scale of the joint.
109*/
110QVector3D QJoint::scale() const
111{
112 Q_D(const QJoint);
113 return d->m_scale;
114}
115
116/*!
117 \property Qt3DCore::QJoint::rotation
118
119 Holds the rotation of the joint as QQuaternion.
120*/
121QQuaternion QJoint::rotation() const
122{
123 Q_D(const QJoint);
124 return d->m_rotation;
125}
126
127/*!
128 \property Qt3DCore::QJoint::translation
129
130 Holds the translation of the joint as QVector3D.
131*/
132QVector3D QJoint::translation() const
133{
134 Q_D(const QJoint);
135 return d->m_translation;
136}
137
138/*!
139 \property Qt3DCore::QJoint::inverseBindMatrix
140
141 Holds the inverse bind matrix of the joint. This is used to transform
142 vertices from model space into the space of this joint so they can
143 subsequently be multiplied by the joint's global transform to perform
144 the skinning operation.
145*/
146QMatrix4x4 QJoint::inverseBindMatrix() const
147{
148 Q_D(const QJoint);
149 return d->m_inverseBindMatrix;
150}
151
152/*!
153 \property Qt3DCore::QJoint::rotationX
154
155 Holds the x rotation of the joint as an Euler angle.
156*/
157float QJoint::rotationX() const
158{
159 Q_D(const QJoint);
160 return d->m_eulerRotationAngles.x();
161}
162
163/*!
164 \property Qt3DCore::QJoint::rotationY
165
166 Holds the y rotation of the joint as an Euler angle.
167*/
168float QJoint::rotationY() const
169{
170 Q_D(const QJoint);
171 return d->m_eulerRotationAngles.y();
172}
173
174/*!
175 \property Qt3DCore::QJoint::rotationZ
176
177 Holds the z rotation of the joint as an Euler angle.
178*/
179float QJoint::rotationZ() const
180{
181 Q_D(const QJoint);
182 return d->m_eulerRotationAngles.z();
183}
184
185void QJoint::setScale(const QVector3D &scale)
186{
187 Q_D(QJoint);
188 if (scale == d->m_scale)
189 return;
190
191 d->m_scale = scale;
192 emit scaleChanged(scale);
193}
194
195void QJoint::setRotation(const QQuaternion &rotation)
196{
197 Q_D(QJoint);
198 if (rotation == d->m_rotation)
199 return;
200
201 d->m_rotation = rotation;
202 const QVector3D oldRotation = d->m_eulerRotationAngles;
203 d->m_eulerRotationAngles = d->m_rotation.toEulerAngles();
204 emit rotationChanged(rotation);
205
206 const bool wasBlocked = blockNotifications(block: true);
207 if (!qFuzzyCompare(p1: d->m_eulerRotationAngles.x(), p2: oldRotation.x()))
208 emit rotationXChanged(rotationX: d->m_eulerRotationAngles.x());
209 if (!qFuzzyCompare(p1: d->m_eulerRotationAngles.y(), p2: oldRotation.y()))
210 emit rotationYChanged(rotationY: d->m_eulerRotationAngles.y());
211 if (!qFuzzyCompare(p1: d->m_eulerRotationAngles.z(), p2: oldRotation.z()))
212 emit rotationZChanged(rotationZ: d->m_eulerRotationAngles.z());
213 blockNotifications(block: wasBlocked);
214}
215
216void QJoint::setTranslation(const QVector3D &translation)
217{
218 Q_D(QJoint);
219 if (translation == d->m_translation)
220 return;
221
222 d->m_translation = translation;
223 emit translationChanged(translation);
224}
225
226void QJoint::setInverseBindMatrix(const QMatrix4x4 &inverseBindMatrix)
227{
228 Q_D(QJoint);
229 if (d->m_inverseBindMatrix == inverseBindMatrix)
230 return;
231
232 d->m_inverseBindMatrix = inverseBindMatrix;
233 emit inverseBindMatrixChanged(inverseBindMatrix);
234}
235
236void QJoint::setRotationX(float rotationX)
237{
238 Q_D(QJoint);
239
240 if (qFuzzyCompare(p1: d->m_eulerRotationAngles.x(), p2: rotationX))
241 return;
242
243 const auto eulers = QVector3D(rotationX,
244 d->m_eulerRotationAngles.y(),
245 d->m_eulerRotationAngles.z());
246 const QQuaternion r = QQuaternion::fromEulerAngles(eulerAngles: eulers);
247 setRotation(r);
248}
249
250void QJoint::setRotationY(float rotationY)
251{
252 Q_D(QJoint);
253
254 if (qFuzzyCompare(p1: d->m_eulerRotationAngles.y(), p2: rotationY))
255 return;
256
257 const auto eulers = QVector3D(d->m_eulerRotationAngles.x(),
258 rotationY,
259 d->m_eulerRotationAngles.z());
260 const QQuaternion r = QQuaternion::fromEulerAngles(eulerAngles: eulers);
261 setRotation(r);
262}
263
264void QJoint::setRotationZ(float rotationZ)
265{
266 Q_D(QJoint);
267 if (qFuzzyCompare(p1: d->m_eulerRotationAngles.z(), p2: rotationZ))
268 return;
269
270 const auto eulers = QVector3D(d->m_eulerRotationAngles.x(),
271 d->m_eulerRotationAngles.y(),
272 rotationZ);
273 const QQuaternion r = QQuaternion::fromEulerAngles(eulerAngles: eulers);
274 setRotation(r);
275}
276
277void QJoint::setName(const QString &name)
278{
279 Q_D(QJoint);
280 if (d->m_name == name)
281 return;
282
283 d->m_name = name;
284 emit nameChanged(name);
285}
286
287/*!
288 Sets the transform matrix for this joint to the identity matrix.
289*/
290void QJoint::setToIdentity()
291{
292 setScale(QVector3D(1.0f, 1.0f, 1.0f));
293 setRotation(QQuaternion());
294 setTranslation(QVector3D());
295}
296
297/*!
298 Adds \a joint as a child of this joint. If \a joint has no parent, then
299 this joint takes ownership of it. Child joints are in the coordinate system
300 of their parent joint.
301*/
302void QJoint::addChildJoint(QJoint *joint)
303{
304 Q_D(QJoint);
305 if (!d->m_childJoints.contains(t: joint)) {
306 d->m_childJoints.push_back(t: joint);
307 // Force creation in backend by setting parent
308 if (!joint->parent())
309 joint->setParent(this);
310
311 // Ensures proper bookkeeping
312 d->registerDestructionHelper(node: joint, func: &QJoint::removeChildJoint, d->m_childJoints);
313
314 if (d->m_changeArbiter != nullptr)
315 d->update();
316 }
317}
318
319/*!
320 Removes \a joint from this joint's list of children. The child joint is not
321 destroyed.
322*/
323void QJoint::removeChildJoint(QJoint *joint)
324{
325 Q_D(QJoint);
326 if (d->m_childJoints.contains(t: joint)) {
327 if (d->m_changeArbiter != nullptr)
328 d->update();
329
330 d->m_childJoints.removeOne(t: joint);
331
332 // Remove bookkeeping connection
333 d->unregisterDestructionHelper(node: joint);
334 }
335}
336
337/*!
338 The vector of joints this joint has as children.
339*/
340QList<QJoint *> QJoint::childJoints() const
341{
342 Q_D(const QJoint);
343 return d->m_childJoints;
344}
345
346/*!
347 Returns the name of the joint.
348*/
349QString QJoint::name() const
350{
351 Q_D(const QJoint);
352 return d->m_name;
353}
354
355} // namespace Qt3DCore
356
357QT_END_NAMESPACE
358
359#include "moc_qjoint.cpp"
360

source code of qt3d/src/core/transforms/qjoint.cpp