1// Copyright (C) 2014 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 "qtransform.h"
5#include "qtransform_p.h"
6
7#include <Qt3DCore/private/qmath3d_p.h>
8
9QT_BEGIN_NAMESPACE
10
11namespace Qt3DCore {
12
13QTransformPrivate::QTransformPrivate()
14 : QComponentPrivate()
15 , m_rotation()
16 , m_scale(1.0f, 1.0f, 1.0f)
17 , m_translation()
18 , m_eulerRotationAngles()
19 , m_matrixDirty(false)
20{
21 m_shareable = false;
22}
23
24QTransformPrivate::~QTransformPrivate()
25{
26}
27
28/*!
29 \qmltype Transform
30 \inqmlmodule Qt3D.Core
31 \inherits Component3D
32 \instantiates Qt3DCore::QTransform
33 \since 5.6
34 \brief Used to perform transforms on meshes.
35
36 The Transform component is not shareable between multiple Entity's.
37 The transformation is held as vector3d scale, quaternion rotation and
38 vector3d translation components. The transformations are applied to the
39 mesh in that order. When Transform::matrix property is set, it is decomposed
40 to these transform components and corresponding transform signals are emitted.
41
42 Several helper functions are provided to set up the Transform;
43 fromAxisAndAngle and fromAxesAndAngles can be used to set the rotation around
44 specific axes, fromEulerAngles can be used to set the rotation based on euler
45 angles and rotateAround can be used to rotate the object around specific point
46 relative to local origin.
47 */
48
49/*!
50 \qmlproperty matrix4x4 Transform::matrix
51
52 Holds the matrix4x4 of the transform.
53 \note When the matrix property is set, it is decomposed to translation, rotation and scale components.
54 */
55
56/*!
57 \qmlproperty real Transform::rotationX
58
59 Holds the x rotation of the transform as Euler angle.
60 */
61
62/*!
63 \qmlproperty real Transform::rotationY
64
65 Holds the y rotation of the transform as Euler angle.
66 */
67
68/*!
69 \qmlproperty real Transform::rotationZ
70
71 Holds the z rotation of the transform as Euler angle.
72 */
73
74/*!
75 \qmlproperty vector3d Transform::scale3D
76
77 Holds the scale of the transform as vector3d.
78 */
79
80/*!
81 \qmlproperty real Transform::scale
82
83 Holds the uniform scale of the transform. If the scale has been set with scale3D, holds
84 the x value only.
85 */
86
87/*!
88 \qmlproperty quaternion Transform::rotation
89
90 Holds the rotation of the transform as quaternion.
91 */
92
93/*!
94 \qmlproperty vector3d Transform::translation
95
96 Holds the translation of the transform as vector3d.
97 */
98
99/*!
100 \qmlproperty matrix4x4 QTransform::worldMatrix
101
102 Holds the world transformation matrix for the transform. This assumes the
103 Transform component is being referenced by an Entity. This makes it more
104 convenient to identify when an Entity part of a subtree has been
105 transformed in the world even though its local transformation might not
106 have changed.
107
108 \since 5.14
109 */
110
111/*!
112 \qmlmethod quaternion Transform::fromAxisAndAngle(vector3d axis, real angle)
113 Creates a quaternion from \a axis and \a angle.
114 Returns the resulting quaternion.
115 */
116
117/*!
118 \qmlmethod quaternion Transform::fromAxisAndAngle(real x, real y, real z, real angle)
119 Creates a quaternion from \a x, \a y, \a z, and \a angle.
120 Returns the resulting quaternion.
121 */
122
123/*!
124 \qmlmethod quaternion Transform::fromAxesAndAngles(vector3d axis1, real angle1,
125 vector3d axis2, real angle2)
126 Creates a quaternion from \a axis1, \a angle1, \a axis2, and \a angle2.
127 Returns the resulting quaternion.
128 */
129
130/*!
131 \qmlmethod quaternion Transform::fromAxesAndAngles(vector3d axis1, real angle1,
132 vector3d axis2, real angle2,
133 vector3d axis3, real angle3)
134 Creates a quaternion from \a axis1, \a angle1, \a axis2, \a angle2, \a axis3, and \a angle3.
135 Returns the resulting quaternion.
136 */
137
138/*!
139 \qmlmethod quaternion Transform::fromEulerAngles(vector3d eulerAngles)
140 Creates a quaternion from \a eulerAngles.
141 Returns the resulting quaternion.
142 */
143
144/*!
145 \qmlmethod quaternion Transform::fromEulerAngles(real pitch, real yaw, real roll)
146 Creates a quaternion from \a pitch, \a yaw, and \a roll.
147 Returns the resulting quaternion.
148 */
149
150/*!
151 \qmlmethod matrix4x4 Transform::rotateAround(vector3d point, real angle, vector3d axis)
152 Creates a rotation matrix from \a axis and \a angle around \a point relative to local origin.
153 Returns the resulting matrix4x4.
154 */
155
156/*!
157 \class Qt3DCore::QTransform
158 \inmodule Qt3DCore
159 \inherits Qt3DCore::QComponent
160 \since 5.6
161 \brief Used to perform transforms on meshes.
162
163 The QTransform component is not shareable between multiple QEntity's.
164 The transformation is held as QVector3D scale, QQuaternion rotation and
165 QVector3D translation components. The transformations are applied to the
166 mesh in that order. When QTransform::matrix property is set, it is decomposed
167 to these transform components and corresponding signals are emitted.
168
169 Several helper functions are provided to set up the QTransform;
170 fromAxisAndAngle and fromAxesAndAngles can be used to set the rotation around
171 specific axes, fromEulerAngles can be used to set the rotation based on euler
172 angles and rotateAround can be used to rotate the object around specific point
173 relative to local origin.
174 */
175
176/*!
177 Constructs a new QTransform with \a parent.
178 */
179QTransform::QTransform(QNode *parent)
180 : QComponent(*new QTransformPrivate, parent)
181{
182}
183
184/*!
185 \internal
186 */
187QTransform::~QTransform()
188{
189}
190
191/*!
192 \internal
193 */
194QTransform::QTransform(QTransformPrivate &dd, QNode *parent)
195 : QComponent(dd, parent)
196{
197}
198
199void QTransformPrivate::setWorldMatrix(const QMatrix4x4 &worldMatrix)
200{
201 Q_Q(QTransform);
202 if (m_worldMatrix == worldMatrix)
203 return;
204 const bool blocked = q->blockNotifications(block: true);
205 m_worldMatrix = worldMatrix;
206 emit q->worldMatrixChanged(worldMatrix);
207 q->blockNotifications(block: blocked);
208}
209
210void QTransformPrivate::update()
211{
212 if (!m_blockNotifications)
213 m_dirty = true;
214 markDirty(changes: QScene::TransformDirty);
215 QNodePrivate::update();
216}
217
218void QTransform::setMatrix(const QMatrix4x4 &m)
219{
220 Q_D(QTransform);
221 if (m != matrix()) {
222 d->m_matrix = m;
223 d->m_matrixDirty = false;
224
225 QVector3D s;
226 QVector3D t;
227 QQuaternion r;
228 decomposeQMatrix4x4(m, position&: t, orientation&: r, scale&: s);
229 d->m_scale = s;
230 d->m_rotation = r;
231 d->m_translation = t;
232 d->m_eulerRotationAngles = d->m_rotation.toEulerAngles();
233 emit scale3DChanged(scale: s);
234 emit rotationChanged(rotation: r);
235 emit translationChanged(translation: t);
236 const bool wasBlocked = blockNotifications(block: true);
237 emit matrixChanged();
238 emit scaleChanged(scale: d->m_scale.x());
239 emit rotationXChanged(rotationX: d->m_eulerRotationAngles.x());
240 emit rotationYChanged(rotationY: d->m_eulerRotationAngles.y());
241 emit rotationZChanged(rotationZ: d->m_eulerRotationAngles.z());
242 blockNotifications(block: wasBlocked);
243 }
244}
245
246void QTransform::setRotationX(float rotationX)
247{
248 Q_D(QTransform);
249
250 if (d->m_eulerRotationAngles.x() == rotationX)
251 return;
252
253 d->m_eulerRotationAngles.setX(rotationX);
254 QQuaternion rotation = QQuaternion::fromEulerAngles(eulerAngles: d->m_eulerRotationAngles);
255 if (rotation != d->m_rotation) {
256 d->m_rotation = rotation;
257 d->m_matrixDirty = true;
258 emit rotationChanged(rotation);
259 }
260
261 const bool wasBlocked = blockNotifications(block: true);
262 emit rotationXChanged(rotationX);
263 emit matrixChanged();
264 blockNotifications(block: wasBlocked);
265}
266
267void QTransform::setRotationY(float rotationY)
268{
269 Q_D(QTransform);
270
271 if (d->m_eulerRotationAngles.y() == rotationY)
272 return;
273
274 d->m_eulerRotationAngles.setY(rotationY);
275 QQuaternion rotation = QQuaternion::fromEulerAngles(eulerAngles: d->m_eulerRotationAngles);
276 if (rotation != d->m_rotation) {
277 d->m_rotation = rotation;
278 d->m_matrixDirty = true;
279 emit rotationChanged(rotation);
280 }
281
282 const bool wasBlocked = blockNotifications(block: true);
283 emit rotationYChanged(rotationY);
284 emit matrixChanged();
285 blockNotifications(block: wasBlocked);
286}
287
288void QTransform::setRotationZ(float rotationZ)
289{
290 Q_D(QTransform);
291 if (d->m_eulerRotationAngles.z() == rotationZ)
292 return;
293
294 d->m_eulerRotationAngles.setZ(rotationZ);
295 QQuaternion rotation = QQuaternion::fromEulerAngles(eulerAngles: d->m_eulerRotationAngles);
296 if (rotation != d->m_rotation) {
297 d->m_rotation = rotation;
298 d->m_matrixDirty = true;
299 emit rotationChanged(rotation);
300 }
301
302 const bool wasBlocked = blockNotifications(block: true);
303 emit rotationZChanged(rotationZ);
304 emit matrixChanged();
305 blockNotifications(block: wasBlocked);
306}
307
308/*!
309 \property Qt3DCore::QTransform::matrix
310
311 Holds the QMatrix4x4 of the transform.
312 \note When the matrix property is set, it is decomposed to translation, rotation and scale components.
313 */
314QMatrix4x4 QTransform::matrix() const
315{
316 Q_D(const QTransform);
317 if (d->m_matrixDirty) {
318 composeQMatrix4x4(position: d->m_translation, orientation: d->m_rotation, scale: d->m_scale, m&: d->m_matrix);
319 d->m_matrixDirty = false;
320 }
321 return d->m_matrix;
322}
323
324/*!
325 \property QTransform::worldMatrix
326
327 Holds the world transformation matrix for the transform. This assumes the
328 QTransform component is being referenced by a QEntity. This makes it more
329 convenient to identify when a QEntity part of a subtree has been
330 transformed in the world even though its local transformation might not
331 have changed.
332
333 \since 5.14
334 */
335
336/*!
337 Returns the world transformation matrix associated to the QTransform when
338 referenced by a QEntity which may be part of a QEntity hierarchy.
339
340 \since 5.14
341 */
342QMatrix4x4 QTransform::worldMatrix() const
343{
344 Q_D(const QTransform);
345 return d->m_worldMatrix;
346}
347
348/*!
349 \property Qt3DCore::QTransform::rotationX
350
351 Holds the x rotation of the transform as Euler angle.
352 */
353float QTransform::rotationX() const
354{
355 Q_D(const QTransform);
356 return d->m_eulerRotationAngles.x();
357}
358
359/*!
360 \property Qt3DCore::QTransform::rotationY
361
362 Holds the y rotation of the transform as Euler angle.
363 */
364float QTransform::rotationY() const
365{
366 Q_D(const QTransform);
367 return d->m_eulerRotationAngles.y();
368}
369
370/*!
371 \property Qt3DCore::QTransform::rotationZ
372
373 Holds the z rotation of the transform as Euler angle.
374 */
375float QTransform::rotationZ() const
376{
377 Q_D(const QTransform);
378 return d->m_eulerRotationAngles.z();
379}
380
381void QTransform::setScale3D(const QVector3D &scale)
382{
383 Q_D(QTransform);
384 if (scale != d->m_scale) {
385 d->m_scale = scale;
386 d->m_matrixDirty = true;
387 emit scale3DChanged(scale);
388
389 const bool wasBlocked = blockNotifications(block: true);
390 emit matrixChanged();
391 blockNotifications(block: wasBlocked);
392 }
393}
394
395/*!
396 \property Qt3DCore::QTransform::scale3D
397
398 Holds the scale of the transform as QVector3D.
399 */
400QVector3D QTransform::scale3D() const
401{
402 Q_D(const QTransform);
403 return d->m_scale;
404}
405
406void QTransform::setScale(float scale)
407{
408 Q_D(QTransform);
409 if (scale != d->m_scale.x()) {
410 setScale3D(QVector3D(scale, scale, scale));
411
412 const bool wasBlocked = blockNotifications(block: true);
413 emit scaleChanged(scale);
414 blockNotifications(block: wasBlocked);
415 }
416}
417
418/*!
419 \property Qt3DCore::QTransform::scale
420
421 Holds the uniform scale of the transform. If the scale has been set with setScale3D, holds
422 the x value only.
423 */
424float QTransform::scale() const
425{
426 Q_D(const QTransform);
427 return d->m_scale.x();
428}
429
430void QTransform::setRotation(const QQuaternion &rotation)
431{
432 Q_D(QTransform);
433 if (rotation != d->m_rotation) {
434 d->m_rotation = rotation;
435 const QVector3D oldRotation = d->m_eulerRotationAngles;
436 d->m_eulerRotationAngles = d->m_rotation.toEulerAngles();
437 d->m_matrixDirty = true;
438 emit rotationChanged(rotation);
439
440 const bool wasBlocked = blockNotifications(block: true);
441 emit matrixChanged();
442 if (d->m_eulerRotationAngles.x() != oldRotation.x())
443 emit rotationXChanged(rotationX: d->m_eulerRotationAngles.x());
444 if (d->m_eulerRotationAngles.y() != oldRotation.y())
445 emit rotationYChanged(rotationY: d->m_eulerRotationAngles.y());
446 if (d->m_eulerRotationAngles.z() != oldRotation.z())
447 emit rotationZChanged(rotationZ: d->m_eulerRotationAngles.z());
448 blockNotifications(block: wasBlocked);
449 }
450}
451
452/*!
453 \property Qt3DCore::QTransform::rotation
454
455 Holds the rotation of the transform as QQuaternion.
456 */
457QQuaternion QTransform::rotation() const
458{
459 Q_D(const QTransform);
460 return d->m_rotation;
461}
462
463void QTransform::setTranslation(const QVector3D &translation)
464{
465 Q_D(QTransform);
466 if (translation != d->m_translation) {
467 d->m_translation = translation;
468 d->m_matrixDirty = true;
469 emit translationChanged(translation);
470
471 const bool wasBlocked = blockNotifications(block: true);
472 emit matrixChanged();
473 blockNotifications(block: wasBlocked);
474 }
475}
476
477/*!
478 \property Qt3DCore::QTransform::translation
479
480 Holds the translation of the transform as QVector3D.
481 */
482QVector3D QTransform::translation() const
483{
484 Q_D(const QTransform);
485 return d->m_translation;
486}
487
488/*!
489 Creates a QQuaternion from \a axis and \a angle.
490 Returns the resulting QQuaternion.
491 */
492QQuaternion QTransform::fromAxisAndAngle(const QVector3D &axis, float angle)
493{
494 return QQuaternion::fromAxisAndAngle(axis, angle);
495}
496
497/*!
498 Creates a QQuaternion from \a x, \a y, \a z, and \a angle.
499 Returns the resulting QQuaternion.
500 */
501QQuaternion QTransform::fromAxisAndAngle(float x, float y, float z, float angle)
502{
503 return QQuaternion::fromAxisAndAngle(x, y, z, angle);
504}
505
506/*!
507 Creates a QQuaternion from \a axis1, \a angle1, \a axis2, and \a angle2.
508 Returns the resulting QQuaternion.
509 */
510QQuaternion QTransform::fromAxesAndAngles(const QVector3D &axis1, float angle1,
511 const QVector3D &axis2, float angle2)
512{
513 const QQuaternion q1 = QQuaternion::fromAxisAndAngle(axis: axis1, angle: angle1);
514 const QQuaternion q2 = QQuaternion::fromAxisAndAngle(axis: axis2, angle: angle2);
515 return q2 * q1;
516}
517
518/*!
519 Creates a QQuaternion from \a axis1, \a angle1, \a axis2, \a angle2, \a axis3, and \a angle3.
520 Returns the resulting QQuaternion.
521 */
522QQuaternion QTransform::fromAxesAndAngles(const QVector3D &axis1, float angle1,
523 const QVector3D &axis2, float angle2,
524 const QVector3D &axis3, float angle3)
525{
526 const QQuaternion q1 = QQuaternion::fromAxisAndAngle(axis: axis1, angle: angle1);
527 const QQuaternion q2 = QQuaternion::fromAxisAndAngle(axis: axis2, angle: angle2);
528 const QQuaternion q3 = QQuaternion::fromAxisAndAngle(axis: axis3, angle: angle3);
529 return q3 * q2 * q1;
530}
531
532/*!
533 Creates a QQuaterniom definining a rotation from the axes \a xAxis, \a yAxis and \a zAxis.
534 \since 5.11
535 */
536QQuaternion QTransform::fromAxes(const QVector3D &xAxis, const QVector3D &yAxis, const QVector3D &zAxis)
537{
538 return QQuaternion::fromAxes(xAxis, yAxis, zAxis);
539}
540
541/*!
542 Creates a QQuaternion from \a eulerAngles.
543 Returns the resulting QQuaternion.
544 */
545QQuaternion QTransform::fromEulerAngles(const QVector3D &eulerAngles)
546{
547 return QQuaternion::fromEulerAngles(eulerAngles);
548}
549
550/*!
551 Creates a QQuaternion from \a pitch, \a yaw, and \a roll.
552 Returns the resulting QQuaternion.
553 */
554QQuaternion QTransform::fromEulerAngles(float pitch, float yaw, float roll)
555{
556 return QQuaternion::fromEulerAngles(pitch, yaw, roll);
557}
558
559/*!
560 Creates a rotation matrix from \a axis and \a angle around \a point.
561 Returns the resulting QMatrix4x4.
562 */
563QMatrix4x4 QTransform::rotateAround(const QVector3D &point, float angle, const QVector3D &axis)
564{
565 QMatrix4x4 m;
566 m.translate(vector: point);
567 m.rotate(angle, vector: axis);
568 m.translate(vector: -point);
569 return m;
570}
571
572/*!
573 Returns a rotation matrix defined from the axes \a xAxis, \a yAxis, \a zAxis.
574 \since 5.11
575 */
576QMatrix4x4 QTransform::rotateFromAxes(const QVector3D &xAxis, const QVector3D &yAxis, const QVector3D &zAxis)
577{
578 return QMatrix4x4(xAxis.x(), yAxis.x(), zAxis.x(), 0.0f,
579 xAxis.y(), yAxis.y(), zAxis.y(), 0.0f,
580 xAxis.z(), yAxis.z(), zAxis.z(), 0.0f,
581 0.0f, 0.0f, 0.0f, 1.0f);
582}
583
584} // namespace Qt3DCore
585
586QT_END_NAMESPACE
587
588#include "moc_qtransform.cpp"
589

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