Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquick3dnode.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5
8
9#include <QtQuick3DRuntimeRender/private/qssgrendernode_p.h>
10#include <QtQuick3DUtils/private/qssgutils_p.h>
11#include <QtQuick3D/private/qquick3dobject_p.h>
12
13#include <QtMath>
14
16
17QQuick3DNodePrivate::QQuick3DNodePrivate(QQuick3DObjectPrivate::Type t,
18 QQuick3DContentLayer::LayerFlag layerFlag)
19 : QQuick3DObjectPrivate(t)
20 , m_tag(layerFlag)
21{
22
23}
24
25QQuick3DNodePrivate::~QQuick3DNodePrivate()
26{
27
28}
29
30void QQuick3DNodePrivate::init()
31{
32
33}
34
35void QQuick3DNodePrivate::setIsHiddenInEditor(bool isHidden)
36{
37 Q_Q(QQuick3DNode);
38 if (isHidden == m_isHiddenInEditor)
39 return;
40 m_isHiddenInEditor = isHidden;
41 q->update();
42}
43
44void QQuick3DNodePrivate::setLocalTransform(const QMatrix4x4 &transform)
45{
46 Q_Q(QQuick3DNode);
47
48 // decompose the 4x4 affine transform into scale, rotation and translation
49 QVector3D scale;
50 QQuaternion rotation;
51 QVector3D position;
52
53 if (QSSGUtils::mat44::decompose(transform, position, scale, rotation)) {
54 q->setScale(scale);
55 q->setRotation(rotation);
56 q->setPosition(position);
57 }
58
59 // We set the local transform as-is regardless if it could be decomposed or not.
60 // We'd likely want some way to notify about this, but for now the transform
61 // can potentially change silently.
62 m_localTransform = transform;
63 // Note: If any of the transform properties are set before the update
64 // the explicit local transform should be ignored by setting this value to false.
65 m_hasExplicitLocalTransform = true;
66
67 q->update();
68}
69
70/*!
71 \qmltype Node
72 \inherits Object3D
73 \inqmlmodule QtQuick3D
74 \brief The base component for an object that exists in a 3D scene.
75
76 The Node type serves as the base class for other spatial types, such as, \l Model, \l Camera, \l Light.
77 These objects represent an entity that exists in the 3D scene, due to having a position and
78 other properties in the 3D world. With the exception of the root node(s), all Node types are
79 transformed relative to their parent Node, that is, in local coordinates. In many ways the Node
80 type serves the same purpose in Qt Quick 3D scenes as \l Item does for Qt Quick scenes.
81
82 In addition to types deriving from Node, it is also possible to parent other types to
83 a Node. This includes QObject instances, where the Node merely serves as the
84 \l{QObject::parent()}{QObject parent}, and \l{Qt Quick 3D Scenes with 2D Content}{Qt
85 Quick items}.
86
87 Wrapping other objects for the purpose of grouping them into components or sub-trees can be
88 a convenient way to, for example, animated a group of nodes as a whole. This snippet shows how
89 to use Node to animate a camera:
90
91 \qml
92 Node {
93 PerspectiveCamera {
94 position: Qt.vector3d(0, 0, -600)
95 }
96
97 SequentialAnimation on eulerRotation.y {
98 loops: Animation.Infinite
99 PropertyAnimation {
100 duration: 5000
101 from: 0
102 to: 360
103 }
104 }
105 }
106 \endqml
107
108 Node has to be used also if creating a scene outside of \l View3D, for example for the
109 purpose of switching scenes on the fly, or showing the same scene on multiple views.
110
111 \qml
112 Node {
113 id: standAloneScene
114
115 DirectionalLight {}
116
117 Model {
118 source: "#Sphere"
119 materials: [ DefaultMaterial {} ]
120 }
121
122 PerspectiveCamera {
123 z: 600
124 }
125 }
126
127 View3D {
128 importScene: standAloneScene
129 }
130 \endqml
131*/
132
133QQuick3DNode::QQuick3DNode(QQuick3DNode *parent)
134 : QQuick3DObject(*(new QQuick3DNodePrivate(QQuick3DNodePrivate::Type::Node)), parent)
135{
136 Q_D(QQuick3DNode);
137 d->init();
138}
139
140QQuick3DNode::QQuick3DNode(QQuick3DNodePrivate &dd, QQuick3DNode *parent)
141 : QQuick3DObject(dd, parent)
142{
143 Q_ASSERT_X(QSSGRenderGraphObject::isNodeType(dd.type), "", "Type needs to be identified as a node type!");
144
145 Q_D(QQuick3DNode);
146 d->init();
147}
148
149QQuick3DNode::~QQuick3DNode() {}
150
151/*!
152 \qmlproperty real QtQuick3D::Node::x
153
154 This property contains the x value of the position translation in
155 local coordinate space.
156
157 \sa position
158*/
159float QQuick3DNode::x() const
160{
161 Q_D(const QQuick3DNode);
162 return d->m_position.x();
163}
164
165/*!
166 \qmlproperty real QtQuick3D::Node::y
167
168 This property contains the y value of the position translation in
169 local coordinate space.
170
171 \sa position
172*/
173float QQuick3DNode::y() const
174{
175 Q_D(const QQuick3DNode);
176 return d->m_position.y();
177}
178
179/*!
180 \qmlproperty real QtQuick3D::Node::z
181
182 This property contains the z value of the position translation in
183 local coordinate space.
184
185 \sa position
186*/
187float QQuick3DNode::z() const
188{
189 Q_D(const QQuick3DNode);
190 return d->m_position.z();
191}
192
193/*!
194 \qmlproperty quaternion QtQuick3D::Node::rotation
195
196 This property contains the rotation values for the node.
197 These values are stored as a quaternion.
198*/
199QQuaternion QQuick3DNode::rotation() const
200{
201 Q_D(const QQuick3DNode);
202 return d->m_rotation;
203}
204
205/*!
206 \qmlproperty vector3d QtQuick3D::Node::position
207
208 This property contains the position translation in local coordinate space.
209
210 \sa x, y, z
211*/
212QVector3D QQuick3DNode::position() const
213{
214 Q_D(const QQuick3DNode);
215 return d->m_position;
216}
217
218
219/*!
220 \qmlproperty vector3d QtQuick3D::Node::scale
221
222 This property contains the scale values for the x, y, and z axis.
223*/
224QVector3D QQuick3DNode::scale() const
225{
226 Q_D(const QQuick3DNode);
227 return d->m_scale;
228}
229
230/*!
231 \qmlproperty vector3d QtQuick3D::Node::pivot
232
233 This property contains the pivot values for the x, y, and z axis. These
234 values are used as the pivot points when applying rotations to the node.
235
236*/
237QVector3D QQuick3DNode::pivot() const
238{
239 Q_D(const QQuick3DNode);
240 return d->m_pivot;
241}
242
243/*!
244 \qmlproperty real QtQuick3D::Node::opacity
245
246 This property contains the local opacity value of the Node. Since Node
247 objects are not necessarily visible, this value might not have any effect,
248 but this value is inherited by all children of the Node, which might be visible.
249
250*/
251float QQuick3DNode::localOpacity() const
252{
253 Q_D(const QQuick3DNode);
254 return d->m_opacity;
255}
256
257/*!
258 \qmlproperty bool QtQuick3D::Node::visible
259
260 When this property is true, the Node (and its children) can be visible.
261
262*/
263bool QQuick3DNode::visible() const
264{
265 Q_D(const QQuick3DNode);
266 return d->m_visible;
267}
268
269/*!
270 \qmlproperty int QtQuick3D::Node::staticFlags
271 \since 5.15
272
273 This property defines the static flags that are used to evaluate how the node is rendered.
274 Currently doesn't do anything but act as a placeholder for a future implementation.
275*/
276int QQuick3DNode::staticFlags() const
277{
278 Q_D(const QQuick3DNode);
279 return d->m_staticFlags;
280}
281
282QQuick3DNode *QQuick3DNode::parentNode() const
283{
284 // The parent of a QQuick3DNode should never be anything else than a (subclass
285 // of) QQuick3DNode (but the children/leaf nodes can be something else).
286 return static_cast<QQuick3DNode *>(parentItem());
287}
288
289/*!
290 \qmlproperty vector3d QtQuick3D::Node::forward
291 \readonly
292
293 This property returns a normalized vector of the nodes forward direction
294 in scene space.
295
296 \sa up, right, mapDirectionToScene
297*/
298QVector3D QQuick3DNode::forward() const
299{
300 return mapDirectionToScene(QVector3D(0, 0, -1)).normalized();
301}
302
303/*!
304 \qmlproperty vector3d QtQuick3D::Node::up
305 \readonly
306
307 This property returns a normalized vector of the nodes up direction
308 in scene space.
309
310 \sa forward, right, mapDirectionToScene
311*/
312QVector3D QQuick3DNode::up() const
313{
314 return mapDirectionToScene(QVector3D(0, 1, 0)).normalized();
315}
316
317/*!
318 \qmlproperty vector3d QtQuick3D::Node::right
319 \readonly
320
321 This property returns a normalized vector of the nodes right direction
322 in scene space.
323
324 \sa forward, up, mapDirectionToScene
325*/
326QVector3D QQuick3DNode::right() const
327{
328 return mapDirectionToScene(QVector3D(1, 0, 0)).normalized();
329}
330/*!
331 \qmlproperty vector3d QtQuick3D::Node::scenePosition
332 \readonly
333
334 This property returns the position of the node in scene space.
335
336 \note This is sometimes also referred to as the global position. But
337 then in the meaning "global in the 3D world", and not "global to the
338 screen or desktop" (which is usually the interpretation in other Qt APIs).
339 \note the position will be reported in the same orientation as the node.
340
341 \sa mapPositionToScene
342*/
343QVector3D QQuick3DNode::scenePosition() const
344{
345 return QSSGUtils::mat44::getPosition(sceneTransform());
346}
347
348/*!
349 \qmlproperty quaternion QtQuick3D::Node::sceneRotation
350 \readonly
351
352 This property returns the rotation of the node in scene space.
353*/
354QQuaternion QQuick3DNode::sceneRotation() const
355{
356 Q_D(const QQuick3DNode);
357 return QQuaternion::fromRotationMatrix(QSSGUtils::mat44::getUpper3x3(d->sceneRotationMatrix())).normalized();
358}
359
360/*!
361 \qmlproperty vector3d QtQuick3D::Node::sceneScale
362 \readonly
363
364 This property returns the scale of the node in scene space.
365*/
366QVector3D QQuick3DNode::sceneScale() const
367{
368 return QSSGUtils::mat44::getScale(sceneTransform());
369}
370
371/*!
372 \qmlproperty matrix4x4 QtQuick3D::Node::sceneTransform
373 \readonly
374
375 This property returns the global transform matrix for this node.
376 \note the return value will be in right-handed coordinates.
377*/
378QMatrix4x4 QQuick3DNode::sceneTransform() const
379{
380 Q_D(const QQuick3DNode);
381 if (d->m_sceneTransformDirty)
382 const_cast<QQuick3DNodePrivate *>(d)->calculateGlobalVariables();
383 return d->m_sceneTransform;
384}
385
386void QQuick3DNodePrivate::calculateGlobalVariables()
387{
388 Q_Q(QQuick3DNode);
389 m_sceneTransformDirty = false;
390 QMatrix4x4 localTransform = QSSGRenderNode::calculateTransformMatrix(m_position, m_scale, m_pivot, m_rotation);
391 QQuick3DNode *parent = q->parentNode();
392 if (!parent) {
393 m_sceneTransform = localTransform;
394 m_hasInheritedUniformScale = true;
395 return;
396 }
397 QQuick3DNodePrivate *privateParent = QQuick3DNodePrivate::get(parent);
398
399 if (privateParent->m_sceneTransformDirty)
400 privateParent->calculateGlobalVariables();
401 m_sceneTransform = privateParent->m_sceneTransform * localTransform;
402
403 // Check if we have an ancestor with non-uniform scale. This will decide whether
404 // or not we can use the sceneTransform to extract sceneRotation and sceneScale.
405 m_hasInheritedUniformScale = privateParent->m_hasInheritedUniformScale;
406 if (m_hasInheritedUniformScale) {
407 const QVector3D ps = privateParent->m_scale;
408 m_hasInheritedUniformScale = qFuzzyCompare(ps.x(), ps.y()) && qFuzzyCompare(ps.x(), ps.z());
409 }
410}
411
412QMatrix4x4 QQuick3DNodePrivate::localRotationMatrix() const
413{
414 return QMatrix4x4(m_rotation.toRotationMatrix());
415}
416
417QMatrix4x4 QQuick3DNodePrivate::sceneRotationMatrix() const
418{
419 Q_Q(const QQuick3DNode);
420
421 if (m_sceneTransformDirty) {
422 // Ensure m_hasInheritedUniformScale is up to date
423 const_cast<QQuick3DNodePrivate *>(this)->calculateGlobalVariables();
424 }
425
426 if (m_hasInheritedUniformScale) {
427 // When we know that every node up to the root have a uniform scale, we can extract the
428 // rotation directly from the sceneTransform(). This is optimizing, since we reuse that
429 // matrix for more than just calculating the sceneRotation.
430 QMatrix4x4 rotationMatrix = q->sceneTransform();
431 QSSGUtils::mat44::normalize(rotationMatrix);
432 return rotationMatrix;
433 }
434
435 // When we have an ancestor that has a non-uniform scale, we cannot extract
436 // the rotation from the sceneMatrix directly. Instead, we need to calculate
437 // it separately, which is slightly more costly.
438 const QMatrix4x4 parentRotationMatrix = QQuick3DNodePrivate::get(q->parentNode())->sceneRotationMatrix();
439 return parentRotationMatrix * localRotationMatrix();
440}
441
442void QQuick3DNodePrivate::emitChangesToSceneTransform()
443{
444 Q_Q(QQuick3DNode);
445 const QVector3D prevPosition = QSSGUtils::mat44::getPosition(m_sceneTransform);
446 const QQuaternion prevRotation = QQuaternion::fromRotationMatrix(QSSGUtils::mat44::getUpper3x3(m_sceneTransform)).normalized();
447 const QVector3D prevScale = QSSGUtils::mat44::getScale(m_sceneTransform);
448 QVector3D prevForward, prevUp, prevRight;
449 QVector3D newForward, newUp, newRight;
450 // Do direction (forward, up, right) calculations only if they have connections
451 bool emitDirectionChanges = (m_directionConnectionCount > 0);
452 if (emitDirectionChanges) {
453 // Instead of calling forward(), up() and right(), calculate them here.
454 // This way m_sceneTransform isn't updated due to m_sceneTransformDirty and
455 // common theDirMatrix operations are not duplicated.
456 QMatrix3x3 theDirMatrix = m_sceneTransform.normalMatrix();
457 prevForward = QSSGUtils::mat33::transform(theDirMatrix, QVector3D(0, 0, -1)).normalized();
458 prevUp = QSSGUtils::mat33::transform(theDirMatrix, QVector3D(0, 1, 0)).normalized();
459 prevRight = QSSGUtils::mat33::transform(theDirMatrix, QVector3D(1, 0, 0)).normalized();
460 }
461
462 calculateGlobalVariables();
463
464 const QVector3D newPosition = QSSGUtils::mat44::getPosition(m_sceneTransform);
465 const QQuaternion newRotation = QQuaternion::fromRotationMatrix(QSSGUtils::mat44::getUpper3x3(m_sceneTransform)).normalized();
466 const QVector3D newScale = QSSGUtils::mat44::getScale(m_sceneTransform);
467 if (emitDirectionChanges) {
468 QMatrix3x3 theDirMatrix = m_sceneTransform.normalMatrix();
469 newForward = QSSGUtils::mat33::transform(theDirMatrix, QVector3D(0, 0, -1)).normalized();
470 newUp = QSSGUtils::mat33::transform(theDirMatrix, QVector3D(0, 1, 0)).normalized();
471 newRight = QSSGUtils::mat33::transform(theDirMatrix, QVector3D(1, 0, 0)).normalized();
472 }
473
474 const bool positionChanged = prevPosition != newPosition;
475 const bool rotationChanged = prevRotation != newRotation;
476 const bool scaleChanged = !qFuzzyCompare(prevScale, newScale);
477
478 if (!positionChanged && !rotationChanged && !scaleChanged)
479 return;
480
481 emit q->sceneTransformChanged();
482
483 if (positionChanged)
484 emit q->scenePositionChanged();
485 if (rotationChanged)
486 emit q->sceneRotationChanged();
487 if (scaleChanged)
488 emit q->sceneScaleChanged();
489 if (emitDirectionChanges) {
490 const bool forwardChanged = prevForward != newForward;
491 const bool upChanged = prevUp != newUp;
492 const bool rightChanged = prevRight != newRight;
493 if (forwardChanged)
494 Q_EMIT q->forwardChanged();
495 if (upChanged)
496 Q_EMIT q->upChanged();
497 if (rightChanged)
498 Q_EMIT q->rightChanged();
499 }
500}
501
502bool QQuick3DNodePrivate::isSceneTransformRelatedSignal(const QMetaMethod &signal) const
503{
504 // Return true if its likely that we need to emit
505 // the given signal when our global transform changes.
506 static const QMetaMethod sceneTransformSignal = QMetaMethod::fromSignal(&QQuick3DNode::sceneTransformChanged);
507 static const QMetaMethod scenePositionSignal = QMetaMethod::fromSignal(&QQuick3DNode::scenePositionChanged);
508 static const QMetaMethod sceneRotationSignal = QMetaMethod::fromSignal(&QQuick3DNode::sceneRotationChanged);
509 static const QMetaMethod sceneScaleSignal = QMetaMethod::fromSignal(&QQuick3DNode::sceneScaleChanged);
510
511 return (signal == sceneTransformSignal
512 || signal == scenePositionSignal
513 || signal == sceneRotationSignal
514 || signal == sceneScaleSignal);
515}
516
517bool QQuick3DNodePrivate::isDirectionRelatedSignal(const QMetaMethod &signal) const
518{
519 // Return true if its likely that we need to emit
520 // the given signal when our global transform changes.
521 static const QMetaMethod forwardSignal = QMetaMethod::fromSignal(&QQuick3DNode::forwardChanged);
522 static const QMetaMethod upSignal = QMetaMethod::fromSignal(&QQuick3DNode::upChanged);
523 static const QMetaMethod rightSignal = QMetaMethod::fromSignal(&QQuick3DNode::rightChanged);
524
525 return (signal == forwardSignal
526 || signal == upSignal
527 || signal == rightSignal);
528}
529
530void QQuick3DNode::connectNotify(const QMetaMethod &signal)
531{
532 Q_D(QQuick3DNode);
533 // Since we want to avoid calculating the global transform in the frontend
534 // unnecessary, we keep track of the number of connections/QML bindings
535 // that needs it. If there are no connections, we can skip calculating it
536 // whenever our geometry changes (unless someone asks for it explicitly).
537 if (d->isSceneTransformRelatedSignal(signal))
538 d->m_sceneTransformConnectionCount++;
539 if (d->isDirectionRelatedSignal(signal))
540 d->m_directionConnectionCount++;
541}
542
543void QQuick3DNode::disconnectNotify(const QMetaMethod &signal)
544{
545 Q_D(QQuick3DNode);
546 if (d->isSceneTransformRelatedSignal(signal))
547 d->m_sceneTransformConnectionCount--;
548 if (d->isDirectionRelatedSignal(signal))
549 d->m_directionConnectionCount--;
550}
551
552void QQuick3DNode::componentComplete()
553{
554 Q_D(QQuick3DNode);
555 QQuick3DObject::componentComplete();
556 if (d->m_sceneTransformConnectionCount > 0 || d->m_directionConnectionCount > 0)
557 d->emitChangesToSceneTransform();
558}
559
560void QQuick3DNodePrivate::markSceneTransformDirty()
561{
562 Q_Q(QQuick3DNode);
563 // Note: we recursively set m_sceneTransformDirty to true whenever our geometry
564 // changes. But we only set it back to false if someone actually queries our global
565 // transform (because only then do we need to calculate it). This means that if no
566 // one ever does that, m_sceneTransformDirty will remain true, perhaps through out
567 // the life time of the node. This is in contrast with the backend, which need to
568 // update dirty transform nodes for every scene graph sync (and clear the backend
569 // dirty transform flags - QQuick3DObjectPrivate::dirtyAttributes).
570 // This means that for most nodes, calling markSceneTransformDirty() should be
571 // cheap, since we normally expect to return early in the following test.
572 if (m_sceneTransformDirty)
573 return;
574
575 m_sceneTransformDirty = true;
576
577 if (m_sceneTransformConnectionCount > 0 || m_directionConnectionCount > 0)
578 emitChangesToSceneTransform();
579
580 auto children = QQuick3DObjectPrivate::get(q)->childItems;
581 for (auto child : children) {
582 if (auto node = qobject_cast<QQuick3DNode *>(child)) {
583 QQuick3DNodePrivate::get(node)->markSceneTransformDirty();
584 }
585 }
586}
587
588void QQuick3DNode::setX(float x)
589{
590 Q_D(QQuick3DNode);
591 if (qFuzzyCompare(d->m_position.x(), x))
592 return;
593
594 d->m_position.setX(x);
595 d->markSceneTransformDirty();
596 emit positionChanged();
597 emit xChanged();
598 update();
599}
600
601void QQuick3DNode::setY(float y)
602{
603 Q_D(QQuick3DNode);
604 if (qFuzzyCompare(d->m_position.y(), y))
605 return;
606
607 d->m_position.setY(y);
608 d->markSceneTransformDirty();
609 emit positionChanged();
610 emit yChanged();
611 update();
612}
613
614void QQuick3DNode::setZ(float z)
615{
616 Q_D(QQuick3DNode);
617 if (qFuzzyCompare(d->m_position.z(), z))
618 return;
619
620 d->m_position.setZ(z);
621 d->markSceneTransformDirty();
622 emit positionChanged();
623 emit zChanged();
624 update();
625}
626
627void QQuick3DNode::setRotation(const QQuaternion &rotation)
628{
629 Q_D(QQuick3DNode);
630 if (d->m_rotation == rotation)
631 return;
632
633 d->m_hasExplicitLocalTransform = false;
634 d->m_rotation = rotation;
635 d->markSceneTransformDirty();
636 emit rotationChanged();
637 emit eulerRotationChanged();
638
639 update();
640}
641
642void QQuick3DNode::setPosition(const QVector3D &position)
643{
644 Q_D(QQuick3DNode);
645 if (d->m_position == position)
646 return;
647
648 const bool xUnchanged = qFuzzyCompare(position.x(), d->m_position.x());
649 const bool yUnchanged = qFuzzyCompare(position.y(), d->m_position.y());
650 const bool zUnchanged = qFuzzyCompare(position.z(), d->m_position.z());
651
652 d->m_position = position;
653 d->markSceneTransformDirty();
654 emit positionChanged();
655
656 if (!xUnchanged)
657 emit xChanged();
658 if (!yUnchanged)
659 emit yChanged();
660 if (!zUnchanged)
661 emit zChanged();
662
663 d->m_hasExplicitLocalTransform = false;
664
665 update();
666}
667
668void QQuick3DNode::setScale(const QVector3D &scale)
669{
670 Q_D(QQuick3DNode);
671 if (d->m_scale == scale)
672 return;
673
674 d->m_hasExplicitLocalTransform = false;
675 d->m_scale = scale;
676 d->markSceneTransformDirty();
677 emit scaleChanged();
678 update();
679}
680
681void QQuick3DNode::setPivot(const QVector3D &pivot)
682{
683 Q_D(QQuick3DNode);
684 if (d->m_pivot == pivot)
685 return;
686
687 d->m_hasExplicitLocalTransform = false;
688 d->m_pivot = pivot;
689 d->markSceneTransformDirty();
690 emit pivotChanged();
691 update();
692}
693
694void QQuick3DNode::setLocalOpacity(float opacity)
695{
696 Q_D(QQuick3DNode);
697 if (qFuzzyCompare(d->m_opacity, opacity))
698 return;
699
700 d->m_opacity = opacity;
701 emit localOpacityChanged();
702 update();
703}
704
705void QQuick3DNode::setVisible(bool visible)
706{
707 Q_D(QQuick3DNode);
708 if (d->m_visible == visible)
709 return;
710
711 d->m_visible = visible;
712 emit visibleChanged();
713 update();
714}
715
716void QQuick3DNode::setStaticFlags(int staticFlags)
717{
718 Q_D(QQuick3DNode);
719 if (d->m_staticFlags == staticFlags)
720 return;
721
722 d->m_staticFlags = staticFlags;
723 emit staticFlagsChanged();
724 update();
725}
726
727void QQuick3DNode::setEulerRotation(const QVector3D &eulerRotation) {
728 Q_D(QQuick3DNode);
729
730 if (d->m_rotation == eulerRotation)
731 return;
732
733 d->m_hasExplicitLocalTransform = false;
734 d->m_rotation = eulerRotation;
735
736 emit rotationChanged();
737 d->markSceneTransformDirty();
738 emit eulerRotationChanged();
739 update();
740}
741
742/*!
743 \qmlmethod QtQuick3D::Node::rotate(real degrees, vector3d axis, enumeration space)
744
745 Rotates this node around an \a axis by the given \a degrees. The specified
746 rotation will be added to the node's current rotation. The axis can
747 be specified relative to different \a {space}s.
748
749 \value Node.LocalSpace
750 Axis is relative to the local orientation of this node.
751 \value Node.ParentSpace
752 Axis is relative to the local orientation of the parent node.
753 \value Node.SceneSpace
754 Axis is relative to the scene.
755
756*/
757void QQuick3DNode::rotate(qreal degrees, const QVector3D &axis, TransformSpace space)
758{
759 Q_D(QQuick3DNode);
760
761 const QQuaternion addRotationQuat = QQuaternion::fromAxisAndAngle(axis, float(degrees));
762 const QMatrix4x4 addRotationMatrix = QMatrix4x4(addRotationQuat.toRotationMatrix());
763 QMatrix4x4 newRotationMatrix;
764
765 switch (space) {
766 case LocalSpace:
767 newRotationMatrix = d->localRotationMatrix() * addRotationMatrix;
768 break;
769 case ParentSpace:
770 newRotationMatrix = addRotationMatrix * d->localRotationMatrix();
771 break;
772 case SceneSpace:
773 if (const auto parent = parentNode()) {
774 const QMatrix4x4 lrm = d->localRotationMatrix();
775 const QMatrix4x4 prm = QQuick3DNodePrivate::get(parent)->sceneRotationMatrix();
776 newRotationMatrix = prm.inverted() * addRotationMatrix * prm * lrm;
777 } else {
778 newRotationMatrix = d->localRotationMatrix() * addRotationMatrix;
779 }
780 break;
781 }
782
783 const QQuaternion newRotationQuaternion = QQuaternion::fromRotationMatrix(QSSGUtils::mat44::getUpper3x3(newRotationMatrix)).normalized();
784
785 if (d->m_rotation == newRotationQuaternion)
786 return;
787
788 d->m_hasExplicitLocalTransform = false;
789 d->m_rotation = newRotationQuaternion;
790 d->markSceneTransformDirty();
791
792 emit rotationChanged();
793 emit eulerRotationChanged();
794
795 update();
796}
797
798QSSGRenderGraphObject *QQuick3DNode::updateSpatialNode(QSSGRenderGraphObject *node)
799{
800 Q_D(QQuick3DNode);
801 if (!node) {
802 markAllDirty();
803 node = new QSSGRenderNode();
804 }
805 QQuick3DObject::updateSpatialNode(node);
806 auto spacialNode = static_cast<QSSGRenderNode *>(node);
807 bool transformIsDirty = false;
808
809 if (spacialNode->pivot != d->m_pivot) {
810 transformIsDirty = true;
811 spacialNode->pivot = d->m_pivot;
812 }
813
814 if (!qFuzzyCompare(spacialNode->localOpacity, d->m_opacity)) {
815 spacialNode->localOpacity = d->m_opacity;
816 spacialNode->markDirty(QSSGRenderNode::DirtyFlag::OpacityDirty);
817 }
818
819 if (d->m_hasExplicitLocalTransform) {
820 spacialNode->localTransform = d->m_localTransform;
821 spacialNode->markDirty(QSSGRenderNode::DirtyFlag::TransformDirty);
822 d->m_hasExplicitLocalTransform = false;
823 } else {
824 if (!transformIsDirty && !qFuzzyCompare(d->m_position, QSSGUtils::mat44::getPosition(spacialNode->localTransform)))
825 transformIsDirty = true;
826
827 if (!transformIsDirty && !qFuzzyCompare(d->m_scale, QSSGUtils::mat44::getScale(spacialNode->localTransform)))
828 transformIsDirty = true;
829
830 if (!transformIsDirty && !qFuzzyCompare(d->m_rotation, QQuaternion::fromRotationMatrix(QSSGUtils::mat44::getUpper3x3(spacialNode->localTransform))))
831 transformIsDirty = true;
832
833 if (transformIsDirty) {
834 spacialNode->localTransform = QSSGRenderNode::calculateTransformMatrix(d->m_position, d->m_scale, d->m_pivot, d->m_rotation);
835 spacialNode->markDirty(QSSGRenderNode::DirtyFlag::TransformDirty);
836 }
837 }
838
839 if (d->m_tag != spacialNode->tag) {
840 spacialNode->tag = d->m_tag;
841 spacialNode->markDirty(QSSGRenderNode::DirtyFlag::TagDirty);
842 }
843
844 spacialNode->staticFlags = d->m_staticFlags;
845
846 // The Hidden in Editor flag overrides the visible value
847 if (d->m_isHiddenInEditor)
848 spacialNode->setState(QSSGRenderNode::LocalState::Active, false);
849 else
850 spacialNode->setState(QSSGRenderNode::LocalState::Active, d->m_visible);
851
852 DebugViewHelpers::ensureDebugObjectName(spacialNode, this);
853
854 return spacialNode;
855}
856
857/*!
858 \qmlmethod vector3d QtQuick3D::Node::mapPositionToScene(vector3d localPosition)
859
860 Transforms \a localPosition from local space to scene space.
861
862 \note "Scene space" is sometimes also referred to as the "global space". But
863 then in the meaning "global in the 3D world", and not "global to the
864 screen or desktop" (which is usually the interpretation in other Qt APIs).
865
866 \sa mapPositionFromScene, mapPositionToNode, mapPositionFromNode
867*/
868QVector3D QQuick3DNode::mapPositionToScene(const QVector3D &localPosition) const
869{
870 return QSSGUtils::mat44::transform(sceneTransform(), localPosition);
871}
872
873/*!
874 \qmlmethod vector3d QtQuick3D::Node::mapPositionFromScene(vector3d scenePosition)
875
876 Transforms \a scenePosition from scene space to local space.
877
878 \sa mapPositionToScene, mapPositionToNode, mapPositionFromNode
879*/
880QVector3D QQuick3DNode::mapPositionFromScene(const QVector3D &scenePosition) const
881{
882 return QSSGUtils::mat44::transform(sceneTransform().inverted(), scenePosition);
883}
884
885/*!
886 \qmlmethod vector3d QtQuick3D::Node::mapPositionToNode(QtQuick3D::Node node, vector3d localPosition)
887
888 Transforms \a localPosition from the local space of this node to
889 the local space of \a node.
890
891 \note If \a node is null, then \a localPosition will be transformed into scene space coordinates.
892
893 \sa mapPositionToScene, mapPositionFromScene, mapPositionFromNode
894*/
895QVector3D QQuick3DNode::mapPositionToNode(const QQuick3DNode *node, const QVector3D &localPosition) const
896{
897 const auto scenePositionSelf = mapPositionToScene(localPosition);
898 return node ? node->mapPositionFromScene(scenePositionSelf) : scenePositionSelf;
899}
900
901/*!
902 \qmlmethod vector3d QtQuick3D::Node::mapPositionFromNode(QtQuick3D::Node node, vector3d localPosition)
903
904 Transforms \a localPosition from the local space of \a node to
905 the local space of this node.
906
907 \note If \a node is null, then \a localPosition is interpreted as it is in scene space coordinates.
908
909 \sa mapPositionToScene, mapPositionFromScene, mapPositionToNode
910*/
911QVector3D QQuick3DNode::mapPositionFromNode(const QQuick3DNode *node, const QVector3D &localPosition) const
912{
913 const auto scenePositionOther = node ? node->mapPositionToScene(localPosition) : localPosition;
914 return mapPositionFromScene(scenePositionOther);
915}
916
917/*!
918 \qmlmethod vector3d QtQuick3D::Node::mapDirectionToScene(vector3d localDirection)
919
920 Transforms \a localDirection from local space to scene space.
921 The return value is not affected by the (inherited) scale or
922 position of the node.
923
924 \note the return value will have the same length as \a localDirection
925 (i.e. not normalized).
926
927 \sa mapDirectionFromScene, mapDirectionToNode, mapDirectionFromNode
928*/
929QVector3D QQuick3DNode::mapDirectionToScene(const QVector3D &localDirection) const
930{
931 QMatrix3x3 theDirMatrix = sceneTransform().normalMatrix();
932 return QSSGUtils::mat33::transform(theDirMatrix, localDirection);
933}
934
935/*!
936 \qmlmethod vector3d QtQuick3D::Node::mapDirectionFromScene(vector3d sceneDirection)
937
938 Transforms \a sceneDirection from scene space to local space.
939 The return value is not affected by the (inherited) scale or
940 position of the node.
941
942 \note the return value will have the same length as \a sceneDirection
943 (i.e not normalized).
944
945 \sa mapDirectionToScene, mapDirectionToNode, mapDirectionFromNode
946*/
947QVector3D QQuick3DNode::mapDirectionFromScene(const QVector3D &sceneDirection) const
948{
949 QMatrix3x3 theDirMatrix = QSSGUtils::mat44::getUpper3x3(sceneTransform());
950 theDirMatrix = theDirMatrix.transposed();
951 return QSSGUtils::mat33::transform(theDirMatrix, sceneDirection);
952}
953
954/*!
955 \qmlmethod vector3d QtQuick3D::Node::mapDirectionToNode(QtQuick3D::Node node, vector3d localDirection)
956
957 Transforms \a localDirection from this nodes local space to the
958 local space of \a node.
959 The return value is not affected by the (inherited) scale or
960 position of the node.
961
962 \note the return value will have the same length as \a localDirection
963 (i.e. not normalized).
964
965 \note if \a node is null, then the returned direction will be transformed into scene space coordinates.
966
967 \sa mapDirectionFromNode, mapDirectionFromScene, mapDirectionToScene
968*/
969QVector3D QQuick3DNode::mapDirectionToNode(const QQuick3DNode *node, const QVector3D &localDirection) const
970{
971 const auto sceneDirectionSelf = mapDirectionToScene(localDirection);
972 return node ? node->mapDirectionFromScene(sceneDirectionSelf) : sceneDirectionSelf;
973}
974
975/*!
976 \qmlmethod vector3d QtQuick3D::Node::mapDirectionFromNode(QtQuick3D::Node node, vector3d localDirection)
977
978 Transforms \a localDirection from the local space of \a node to the
979 local space of this node.
980 The return value is not affected by the (inherited) scale or
981 position of the node.
982
983 \note the return value will have the same length as \a localDirection
984 (i.e. not normalized).
985
986 \note If \a node is null, then \a localDirection is interpreted as it is in scene space coordinates.
987
988 \sa mapDirectionToNode, mapDirectionFromScene, mapDirectionToScene
989*/
990QVector3D QQuick3DNode::mapDirectionFromNode(const QQuick3DNode *node, const QVector3D &localDirection) const
991{
992 const auto sceneDirectionOther = node ? node->mapDirectionToScene(localDirection) : localDirection;
993 return mapDirectionFromScene(sceneDirectionOther);
994}
995
996void QQuick3DNode::markAllDirty()
997{
998 Q_D(QQuick3DNode);
999
1000 d->markSceneTransformDirty();
1001 QQuick3DObject::markAllDirty();
1002}
1003
1004/*!
1005 \qmlproperty vector3d QtQuick3D::Node::eulerRotation
1006
1007 This property contains the rotation values for the x, y, and z axis.
1008 These values are stored as a vector3d. Rotation order is assumed to
1009 be ZXY.
1010
1011 \sa QQuaternion::fromEulerAngles()
1012*/
1013
1014QVector3D QQuick3DNode::eulerRotation() const
1015{
1016 const Q_D(QQuick3DNode);
1017
1018 return d->m_rotation;
1019}
1020
1021void QQuick3DNode::itemChange(ItemChange change, const ItemChangeData &)
1022{
1023 if (change == QQuick3DObject::ItemParentHasChanged)
1024 QQuick3DNodePrivate::get(this)->markSceneTransformDirty();
1025}
1026
1027/*!
1028 \qmlproperty int QtQuick3D::Node::layers
1029
1030 This property defines the content layers that this node belongs to.
1031 The layers can be used to group nodes that belongs together, for example
1032 to be rendered together or to be filtered out from rendering.
1033 Setting this property on the active \l Camera will cause the camera to
1034 only render nodes that belong to the specified layers.
1035 The value is a bitmask, where each bit represents a layer.
1036
1037 By default nodes are assigned to the \c main layer, which is \l {ContentLayer.Layer0}{Layer0}.
1038
1039 \sa ContentLayer
1040*/
1041
1042int QQuick3DNode::layers() const
1043{
1044 const Q_D(QQuick3DNode);
1045
1046 return QQuick3DContentLayer::LayerFlags::fromInt(d->m_tag.value());
1047}
1048
1049void QQuick3DNode::setLayers(int newLayers)
1050{
1051 Q_D(QQuick3DNode);
1052
1053 if (newLayers < 0) {
1054 qWarning("QQuick3DNode::setLayers: value must be non-negative");
1055 return;
1056 }
1057
1058 if (d->m_tag == quint32(newLayers))
1059 return;
1060
1061 d->m_tag.setValue(newLayers);
1062 emit layersChanged();
1063 update();
1064}
1065
1066QT_END_NAMESPACE
Combined button and popup list for selecting options.