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

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