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 | |
7 | QT_BEGIN_NAMESPACE |
8 | |
9 | namespace Qt3DCore { |
10 | |
11 | QJointPrivate::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 | */ |
95 | QJoint::QJoint(Qt3DCore::QNode *parent) |
96 | : QNode(*new QJointPrivate, parent) |
97 | { |
98 | } |
99 | |
100 | /*! \internal */ |
101 | QJoint::~QJoint() |
102 | { |
103 | } |
104 | |
105 | /*! |
106 | \property Qt3DCore::QJoint::scale |
107 | |
108 | Holds the scale of the joint. |
109 | */ |
110 | QVector3D 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 | */ |
121 | QQuaternion 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 | */ |
132 | QVector3D 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 | */ |
146 | QMatrix4x4 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 | */ |
157 | float 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 | */ |
168 | float 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 | */ |
179 | float QJoint::rotationZ() const |
180 | { |
181 | Q_D(const QJoint); |
182 | return d->m_eulerRotationAngles.z(); |
183 | } |
184 | |
185 | void 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 | |
195 | void 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 | |
216 | void 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 | |
226 | void 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 | |
236 | void 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 | |
250 | void 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 | |
264 | void 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 | |
277 | void 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 | */ |
290 | void 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 | */ |
302 | void 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 | */ |
323 | void 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 | */ |
340 | QList<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 | */ |
349 | QString QJoint::name() const |
350 | { |
351 | Q_D(const QJoint); |
352 | return d->m_name; |
353 | } |
354 | |
355 | } // namespace Qt3DCore |
356 | |
357 | QT_END_NAMESPACE |
358 | |
359 | #include "moc_qjoint.cpp" |
360 | |