| /**************************************************************************** |
| ** |
| ** Copyright (C) 2019 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of Qt Quick 3D. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 or (at your option) any later version |
| ** approved by the KDE Free Qt Foundation. The licenses are as published by |
| ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qquick3dnode_p.h" |
| #include "qquick3dnode_p_p.h" |
| |
| #include <QtQuick3DRuntimeRender/private/qssgrendernode_p.h> |
| #include <QtQuick3DUtils/private/qssgutils_p.h> |
| #include <QtQuick3DUtils/private/qssgrendereulerangles_p.h> |
| #include <QtQuick3D/private/qquick3dobject_p_p.h> |
| |
| #include <QtMath> |
| |
| QT_BEGIN_NAMESPACE |
| |
| QQuick3DNodePrivate::QQuick3DNodePrivate() |
| { |
| |
| } |
| |
| QQuick3DNodePrivate::~QQuick3DNodePrivate() |
| { |
| |
| } |
| |
| void QQuick3DNodePrivate::init() |
| { |
| |
| } |
| |
| void QQuick3DNodePrivate::setIsHiddenInEditor(bool isHidden) |
| { |
| Q_Q(QQuick3DNode); |
| if (isHidden == m_isHiddenInEditor) |
| return; |
| m_isHiddenInEditor = isHidden; |
| q->update(); |
| } |
| |
| /*! |
| \qmltype Node |
| \inherits Object3D |
| \inqmlmodule QtQuick3D |
| \brief The base component for an object that exists in a 3D Scene. |
| |
| Node type can be used to wrap other objects for the purpose of grouping them, or animating them. |
| This snippet shows how to use Node to animate a camera. |
| |
| \qml |
| Node { |
| PerspectiveCamera { |
| position: Qt.vector3d(0, 0, -600) |
| } |
| |
| SequentialAnimation on rotation { |
| loops: Animation.Infinite |
| PropertyAnimation { |
| duration: 5000 |
| to: Qt.vector3d(0, 360, 0) |
| from: Qt.vector3d(0, 0, 0) |
| } |
| } |
| } |
| \endqml |
| |
| Node has to be used also if creating a scene outside of \l View3D, for example for the purpose |
| of switching scenes on the fly, or showing the same scene on multiple views. |
| |
| \qml |
| Node { |
| id: standAloneScene |
| |
| DirectionalLight {} |
| |
| Model { |
| source: "#Sphere" |
| materials: [ CopperMaterial {} ] |
| } |
| |
| PerspectiveCamera { |
| z: -600 |
| } |
| } |
| |
| View3D { |
| importScene: standAloneScene |
| } |
| \endqml |
| */ |
| |
| QQuick3DNode::QQuick3DNode(QQuick3DNode *parent) |
| : QQuick3DObject(*(new QQuick3DNodePrivate), parent) |
| { |
| Q_D(QQuick3DNode); |
| d->init(); |
| } |
| |
| QQuick3DNode::QQuick3DNode(QQuick3DNodePrivate &dd, QQuick3DNode *parent) |
| : QQuick3DObject(dd, parent) |
| { |
| Q_D(QQuick3DNode); |
| d->init(); |
| } |
| |
| QQuick3DNode::~QQuick3DNode() {} |
| |
| /*! |
| \qmlproperty real QtQuick3D::Node::x |
| |
| This property contains the x value of the position translation in |
| local coordinate space. |
| |
| \sa position |
| */ |
| float QQuick3DNode::x() const |
| { |
| Q_D(const QQuick3DNode); |
| return d->m_position.x(); |
| } |
| |
| /*! |
| \qmlproperty real QtQuick3D::Node::y |
| |
| This property contains the y value of the position translation in |
| local coordinate space. |
| |
| \sa position |
| */ |
| float QQuick3DNode::y() const |
| { |
| Q_D(const QQuick3DNode); |
| return d->m_position.y(); |
| } |
| |
| /*! |
| \qmlproperty real QtQuick3D::Node::z |
| |
| This property contains the z value of the position translation in |
| local coordinate space. |
| |
| \sa position |
| */ |
| float QQuick3DNode::z() const |
| { |
| Q_D(const QQuick3DNode); |
| return d->m_position.z(); |
| } |
| |
| /*! |
| \qmlproperty vector3d QtQuick3D::Node::rotation |
| |
| This property contains the rotation values for the x, y, and z axis. |
| These values are stored as euler angles. |
| */ |
| QVector3D QQuick3DNode::rotation() const |
| { |
| Q_D(const QQuick3DNode); |
| return d->m_rotation; |
| } |
| |
| /*! |
| \qmlproperty vector3d QtQuick3D::Node::position |
| |
| This property contains the position translation in local coordinate space. |
| |
| \sa x, y, z |
| */ |
| QVector3D QQuick3DNode::position() const |
| { |
| Q_D(const QQuick3DNode); |
| return d->m_position; |
| } |
| |
| |
| /*! |
| \qmlproperty vector3d QtQuick3D::Node::scale |
| |
| This property contains the scale values for the x, y, and z axis. |
| */ |
| QVector3D QQuick3DNode::scale() const |
| { |
| Q_D(const QQuick3DNode); |
| return d->m_scale; |
| } |
| |
| /*! |
| \qmlproperty vector3d QtQuick3D::Node::pivot |
| |
| This property contains the pivot values for the x, y, and z axis. These |
| values are used as the pivot points when applying rotations to the node. |
| |
| */ |
| QVector3D QQuick3DNode::pivot() const |
| { |
| Q_D(const QQuick3DNode); |
| return d->m_pivot; |
| } |
| |
| /*! |
| \qmlproperty real QtQuick3D::Node::opacity |
| |
| This property contains the local opacity value of the Node. Since Node |
| objects are not necessarialy visible, this value might not have any affect, |
| but this value is inherited by all children of the Node, which might be visible. |
| |
| */ |
| float QQuick3DNode::localOpacity() const |
| { |
| Q_D(const QQuick3DNode); |
| return d->m_opacity; |
| } |
| |
| /*! |
| \qmlproperty enumeration QtQuick3D::Node::rotationOrder |
| |
| This property defines in what order the \l rotation properties components |
| are applied in. |
| |
| \value Node.XYZ |
| Left-handed XYZ rotation order. |
| \value Node.YZX |
| Left-handed YZX rotation order. |
| \value Node.ZXY |
| Left-handed ZXY rotation order. |
| \value Node.XZY |
| Left-handed XZY rotation order. |
| \value Node.YXZ |
| Left-handed YXZ rotation order. |
| \value Node.ZYX |
| Left-handed ZYX rotation order. |
| \value Node.XYZr |
| Right-handed XYZ rotation order. |
| \value Node.YZXr |
| Right-handed YZX rotation order. |
| \value Node.ZXYr |
| Right-handed ZXY rotation order. |
| \value Node.XZYr |
| Right-handed XZY rotation order. |
| \value Node.YXZr |
| Right-handed YXZ rotation order. |
| \value Node.ZYXr |
| Right-handed ZYX rotation order. |
| */ |
| QQuick3DNode::RotationOrder QQuick3DNode::rotationOrder() const |
| { |
| Q_D(const QQuick3DNode); |
| return d->m_rotationorder; |
| } |
| |
| /*! |
| \qmlproperty enumeration QtQuick3D::Node::orientation |
| |
| This property defines whether the Node is using a right-handed or left-handed |
| coordinate system. |
| |
| \value Node.LeftHanded |
| Left-handed coordinate system. |
| \value Node.RightHanded |
| Right-handed coordinate system. |
| |
| */ |
| QQuick3DNode::Orientation QQuick3DNode::orientation() const |
| { |
| Q_D(const QQuick3DNode); |
| return d->m_orientation; |
| } |
| |
| /*! |
| \qmlproperty bool QtQuick3D::Node::visible |
| |
| When this property is true, the Node (and its children) can be visible. |
| |
| */ |
| bool QQuick3DNode::visible() const |
| { |
| Q_D(const QQuick3DNode); |
| return d->m_visible; |
| } |
| |
| QQuick3DNode *QQuick3DNode::parentNode() const |
| { |
| // The parent of a QQuick3DNode should never be anything else than a (subclass |
| // of) QQuick3DNode (but the children/leaf nodes can be something else). |
| return static_cast<QQuick3DNode *>(parentItem()); |
| } |
| |
| /*! |
| \qmlproperty vector3d QtQuick3D::Node::forward |
| |
| This property returns a normalized vector of the nodes forward direction |
| in scene space. |
| |
| \sa up, right, mapDirectionToScene |
| */ |
| QVector3D QQuick3DNode::forward() const |
| { |
| return mapDirectionToScene(QVector3D(0, 0, 1)).normalized(); |
| } |
| |
| /*! |
| \qmlproperty vector3d QtQuick3D::Node::up |
| |
| This property returns a normalized vector of the nodes up direction |
| in scene space. |
| |
| \sa forward, right, mapDirectionToScene |
| */ |
| QVector3D QQuick3DNode::up() const |
| { |
| return mapDirectionToScene(QVector3D(0, 1, 0)).normalized(); |
| } |
| |
| /*! |
| \qmlproperty vector3d QtQuick3D::Node::right |
| |
| This property returns a normalized vector of the nodes right direction |
| in scene space. |
| |
| \sa forward, up, mapDirectionToScene |
| */ |
| QVector3D QQuick3DNode::right() const |
| { |
| return mapDirectionToScene(QVector3D(1, 0, 0)).normalized(); |
| } |
| /*! |
| \qmlproperty vector3d QtQuick3D::Node::scenePosition |
| |
| This property returns the position of the node in scene space. |
| |
| \note This is sometimes also referred to as the global position. But |
| then in the meaning "global in the 3D world", and not "global to the |
| screen or desktop" (which is usually the interpretation in other Qt APIs). |
| \note the position will be reported in the same orientation as the node. |
| |
| \sa mapPositionToScene |
| */ |
| QVector3D QQuick3DNode::scenePosition() const |
| { |
| return mat44::getPosition(sceneTransform()); |
| } |
| |
| /*! |
| \qmlproperty vector3d QtQuick3D::Node::sceneRotation |
| |
| This property returns the rotation of the node in scene space. |
| */ |
| QVector3D QQuick3DNode::sceneRotation() const |
| { |
| Q_D(const QQuick3DNode); |
| return mat44::getRotation(d->sceneRotationMatrix(), EulerOrder(d->m_rotationorder)); |
| } |
| |
| /*! |
| \qmlproperty vector3d QtQuick3D::Node::sceneScale |
| |
| This property returns the scale of the node in scene space. |
| */ |
| QVector3D QQuick3DNode::sceneScale() const |
| { |
| return mat44::getScale(sceneTransform()); |
| } |
| |
| /*! |
| \qmlproperty matrix4x4 QtQuick3D::Node::sceneTransform |
| |
| This property returns the global transform matrix for this node. |
| \note the return value will be \c LeftHanded or \c RightHanded |
| depending on \l orientation. |
| */ |
| QMatrix4x4 QQuick3DNode::sceneTransform() const |
| { |
| Q_D(const QQuick3DNode); |
| return d->m_orientation == LeftHanded ? sceneTransformLeftHanded() : sceneTransformRightHanded(); |
| } |
| |
| /* |
| This function returns the global transform matrix for this node |
| as a left-handed coordinate system, regardless of orientation. |
| */ |
| QMatrix4x4 QQuick3DNode::sceneTransformLeftHanded() const |
| { |
| QMatrix4x4 transform = sceneTransformRightHanded(); |
| mat44::flip(transform); |
| return transform; |
| } |
| |
| /* |
| This function returns the global transform matrix for this node |
| as a right-handed coordinate system, regardless of orientation. |
| */ |
| QMatrix4x4 QQuick3DNode::sceneTransformRightHanded() const |
| { |
| Q_D(const QQuick3DNode); |
| if (d->m_sceneTransformDirty) |
| const_cast<QQuick3DNodePrivate *>(d)->calculateGlobalVariables(); |
| return d->m_sceneTransformRightHanded; |
| } |
| |
| void QQuick3DNodePrivate::calculateGlobalVariables() |
| { |
| Q_Q(QQuick3DNode); |
| m_sceneTransformDirty = false; |
| QMatrix4x4 localTransformRightHanded = calculateLocalTransformRightHanded(); |
| QQuick3DNode *parent = q->parentNode(); |
| if (!parent) { |
| m_sceneTransformRightHanded = localTransformRightHanded; |
| m_hasInheritedUniformScale = true; |
| return; |
| } |
| QQuick3DNodePrivate *privateParent = QQuick3DNodePrivate::get(parent); |
| |
| if (privateParent->m_sceneTransformDirty) |
| privateParent->calculateGlobalVariables(); |
| m_sceneTransformRightHanded = privateParent->m_sceneTransformRightHanded * localTransformRightHanded; |
| |
| // Check if we have an ancestor with non-uniform scale. This will decide whether |
| // or not we can use the sceneTransform to extract sceneRotation and sceneScale. |
| m_hasInheritedUniformScale = privateParent->m_hasInheritedUniformScale; |
| if (m_hasInheritedUniformScale) { |
| const QVector3D ps = privateParent->m_scale; |
| m_hasInheritedUniformScale = qFuzzyCompare(ps.x(), ps.y()) && qFuzzyCompare(ps.x(), ps.z()); |
| } |
| } |
| |
| QMatrix4x4 QQuick3DNodePrivate::calculateLocalTransformRightHanded() |
| { |
| const QVector3D pivot = -m_pivot * m_scale; |
| QMatrix4x4 localTransform; |
| |
| localTransform(0, 0) = m_scale[0]; |
| localTransform(1, 1) = m_scale[1]; |
| localTransform(2, 2) = m_scale[2]; |
| |
| localTransform(0, 3) = pivot[0]; |
| localTransform(1, 3) = pivot[1]; |
| localTransform(2, 3) = pivot[2]; |
| |
| localTransform = localRotationMatrix() * localTransform; |
| |
| localTransform(0, 3) += m_position[0]; |
| localTransform(1, 3) += m_position[1]; |
| localTransform(2, 3) += m_position[2]; |
| |
| if (Q_LIKELY(m_orientation == QQuick3DNode::LeftHanded)) |
| mat44::flip(localTransform); |
| |
| return localTransform; |
| } |
| |
| QMatrix4x4 QQuick3DNodePrivate::localRotationMatrix() const |
| { |
| const QVector3D radians = degToRad(m_rotation); |
| return QSSGEulerAngleConverter::createRotationMatrix(radians, EulerOrder(m_rotationorder)); |
| } |
| |
| QMatrix4x4 QQuick3DNodePrivate::sceneRotationMatrix() const |
| { |
| Q_Q(const QQuick3DNode); |
| |
| if (m_sceneTransformDirty) { |
| // Ensure m_hasInheritedUniformScale is up to date |
| const_cast<QQuick3DNodePrivate *>(this)->calculateGlobalVariables(); |
| } |
| |
| if (m_hasInheritedUniformScale) { |
| // When we know that every node up to the root have a uniform scale, we can extract the |
| // rotation directly from the sceneTransform(). This is optimizing, since we reuse that |
| // matrix for more than just calculating the sceneRotation. |
| QMatrix4x4 rotationMatrix = q->sceneTransform(); |
| mat44::normalize(rotationMatrix); |
| return rotationMatrix; |
| } else { |
| // When we have an ancestor that has a non-uniform scale, we cannot extract |
| // the rotation from the sceneMatrix directly. Instead, we need to calculate |
| // it separately, which is slightly more costly. |
| const QMatrix4x4 parentRotationMatrix = QQuick3DNodePrivate::get(q->parentNode())->sceneRotationMatrix(); |
| return localRotationMatrix() * parentRotationMatrix; |
| } |
| } |
| |
| QQuick3DObject::Type QQuick3DNode::type() const |
| { |
| return QQuick3DObject::Node; |
| } |
| |
| void QQuick3DNodePrivate::emitChangesToSceneTransform() |
| { |
| Q_Q(QQuick3DNode); |
| const QVector3D prevPosition = mat44::getPosition(m_sceneTransformRightHanded); |
| const QVector3D prevRotation = mat44::getRotation(m_sceneTransformRightHanded, EulerOrder(m_rotationorder)); |
| const QVector3D prevScale = mat44::getScale(m_sceneTransformRightHanded); |
| |
| calculateGlobalVariables(); |
| |
| const QVector3D newPosition = mat44::getPosition(m_sceneTransformRightHanded); |
| const QVector3D newRotation = mat44::getRotation(m_sceneTransformRightHanded, EulerOrder(m_rotationorder)); |
| const QVector3D newScale = mat44::getScale(m_sceneTransformRightHanded); |
| |
| const bool positionChanged = prevPosition != newPosition; |
| const bool rotationChanged = prevRotation != newRotation; |
| const bool scaleChanged = prevScale != newScale; |
| |
| if (!positionChanged && !rotationChanged && !scaleChanged) |
| return; |
| |
| emit q->sceneTransformChanged(); |
| |
| if (positionChanged) |
| emit q->scenePositionChanged(); |
| if (rotationChanged) |
| emit q->sceneRotationChanged(); |
| if (scaleChanged) |
| emit q->sceneScaleChanged(); |
| } |
| |
| bool QQuick3DNodePrivate::isSceneTransformRelatedSignal(const QMetaMethod &signal) const |
| { |
| // Return true if its likely that we need to emit |
| // the given signal when our global transform changes. |
| static const QMetaMethod sceneTransformSignal = QMetaMethod::fromSignal(&QQuick3DNode::sceneTransformChanged); |
| static const QMetaMethod scenePositionSignal = QMetaMethod::fromSignal(&QQuick3DNode::scenePositionChanged); |
| static const QMetaMethod sceneRotationSignal = QMetaMethod::fromSignal(&QQuick3DNode::sceneRotationChanged); |
| static const QMetaMethod sceneScaleSignal = QMetaMethod::fromSignal(&QQuick3DNode::sceneScaleChanged); |
| |
| return (signal == sceneTransformSignal |
| || signal == scenePositionSignal |
| || signal == sceneRotationSignal |
| || signal == sceneScaleSignal); |
| } |
| |
| void QQuick3DNode::connectNotify(const QMetaMethod &signal) |
| { |
| Q_D(QQuick3DNode); |
| // Since we want to avoid calculating the global transform in the frontend |
| // unnecessary, we keep track of the number of connections/QML bindings |
| // that needs it. If there are no connections, we can skip calculating it |
| // whenever our geometry changes (unless someone asks for it explicitly). |
| if (d->isSceneTransformRelatedSignal(signal)) |
| d->m_sceneTransformConnectionCount++; |
| } |
| |
| void QQuick3DNode::disconnectNotify(const QMetaMethod &signal) |
| { |
| Q_D(QQuick3DNode); |
| if (d->isSceneTransformRelatedSignal(signal)) |
| d->m_sceneTransformConnectionCount--; |
| } |
| |
| void QQuick3DNode::componentComplete() |
| { |
| Q_D(QQuick3DNode); |
| QQuick3DObject::componentComplete(); |
| if (d->m_sceneTransformConnectionCount > 0) |
| d->emitChangesToSceneTransform(); |
| } |
| |
| void QQuick3DNodePrivate::markSceneTransformDirty() |
| { |
| Q_Q(QQuick3DNode); |
| // Note: we recursively set m_sceneTransformDirty to true whenever our geometry |
| // changes. But we only set it back to false if someone actually queries our global |
| // transform (because only then do we need to calculate it). This means that if no |
| // one ever does that, m_sceneTransformDirty will remain true, perhaps through out |
| // the life time of the node. This is in contrast with the backend, which need to |
| // update dirty transform nodes for every scene graph sync (and clear the backend |
| // dirty transform flags - QQuick3DObjectPrivate::dirtyAttributes). |
| // This means that for most nodes, calling markSceneTransformDirty() should be |
| // cheap, since we normally expect to return early in the following test. |
| if (m_sceneTransformDirty) |
| return; |
| |
| m_sceneTransformDirty = true; |
| |
| if (m_sceneTransformConnectionCount > 0) |
| emitChangesToSceneTransform(); |
| |
| auto children = QQuick3DObjectPrivate::get(q)->childItems; |
| for (auto child : children) { |
| if (auto node = qobject_cast<QQuick3DNode *>(child)) { |
| QQuick3DNodePrivate::get(node)->markSceneTransformDirty(); |
| } |
| } |
| } |
| |
| void QQuick3DNode::setX(float x) |
| { |
| Q_D(QQuick3DNode); |
| if (qFuzzyCompare(d->m_position.x(), x)) |
| return; |
| |
| d->m_position.setX(x); |
| d->markSceneTransformDirty(); |
| emit positionChanged(); |
| emit xChanged(); |
| update(); |
| } |
| |
| void QQuick3DNode::setY(float y) |
| { |
| Q_D(QQuick3DNode); |
| if (qFuzzyCompare(d->m_position.y(), y)) |
| return; |
| |
| d->m_position.setY(y); |
| d->markSceneTransformDirty(); |
| emit positionChanged(); |
| emit yChanged(); |
| update(); |
| } |
| |
| void QQuick3DNode::setZ(float z) |
| { |
| Q_D(QQuick3DNode); |
| if (qFuzzyCompare(d->m_position.z(), z)) |
| return; |
| |
| d->m_position.setZ(z); |
| d->markSceneTransformDirty(); |
| emit positionChanged(); |
| emit zChanged(); |
| update(); |
| } |
| |
| void QQuick3DNode::setRotation(const QVector3D &rotation) |
| { |
| Q_D(QQuick3DNode); |
| if (d->m_rotation == rotation) |
| return; |
| |
| d->m_rotation = rotation; |
| d->markSceneTransformDirty(); |
| emit rotationChanged(); |
| update(); |
| } |
| |
| void QQuick3DNode::setPosition(const QVector3D &position) |
| { |
| Q_D(QQuick3DNode); |
| if (d->m_position == position) |
| return; |
| |
| const bool xUnchanged = qFuzzyCompare(position.x(), d->m_position.x()); |
| const bool yUnchanged = qFuzzyCompare(position.y(), d->m_position.y()); |
| const bool zUnchanged = qFuzzyCompare(position.z(), d->m_position.z()); |
| |
| d->m_position = position; |
| d->markSceneTransformDirty(); |
| emit positionChanged(); |
| |
| if (!xUnchanged) |
| emit xChanged(); |
| if (!yUnchanged) |
| emit yChanged(); |
| if (!zUnchanged) |
| emit zChanged(); |
| |
| update(); |
| } |
| |
| void QQuick3DNode::setScale(const QVector3D &scale) |
| { |
| Q_D(QQuick3DNode); |
| if (d->m_scale == scale) |
| return; |
| |
| d->m_scale = scale; |
| d->markSceneTransformDirty(); |
| emit scaleChanged(); |
| update(); |
| } |
| |
| void QQuick3DNode::setPivot(const QVector3D &pivot) |
| { |
| Q_D(QQuick3DNode); |
| if (d->m_pivot == pivot) |
| return; |
| |
| d->m_pivot = pivot; |
| d->markSceneTransformDirty(); |
| emit pivotChanged(); |
| update(); |
| } |
| |
| void QQuick3DNode::setLocalOpacity(float opacity) |
| { |
| Q_D(QQuick3DNode); |
| if (qFuzzyCompare(d->m_opacity, opacity)) |
| return; |
| |
| d->m_opacity = opacity; |
| emit localOpacityChanged(); |
| update(); |
| } |
| |
| void QQuick3DNode::setRotationOrder(QQuick3DNode::RotationOrder rotationorder) |
| { |
| Q_D(QQuick3DNode); |
| if (d->m_rotationorder == rotationorder) |
| return; |
| |
| d->m_rotationorder = rotationorder; |
| d->markSceneTransformDirty(); |
| emit rotationOrderChanged(); |
| update(); |
| } |
| |
| void QQuick3DNode::setOrientation(QQuick3DNode::Orientation orientation) |
| { |
| Q_D(QQuick3DNode); |
| if (d->m_orientation == orientation) |
| return; |
| |
| d->m_orientation = orientation; |
| d->markSceneTransformDirty(); |
| emit orientationChanged(); |
| update(); |
| } |
| |
| void QQuick3DNode::setVisible(bool visible) |
| { |
| Q_D(QQuick3DNode); |
| if (d->m_visible == visible) |
| return; |
| |
| d->m_visible = visible; |
| emit visibleChanged(); |
| update(); |
| } |
| |
| /*! |
| \qmlproperty enumeration QQuick3D::Node::TransformSpace |
| |
| Defines the relationship of the axis. |
| |
| \value Node.LocalSpace |
| Axis is relative to the local orientation of this node. |
| \value Node.ParentSpace |
| Axis is relative to the local orientation of the parent node. |
| \value Node.SceneSpace |
| Axis is relative to the scene. |
| */ |
| |
| /*! |
| \qmlmethod QQuick3D::Node::rotate(real degrees, vector3d axis, TransformSpace space) |
| |
| Rotates this node around an \a axis by the given \a degrees. The specified |
| rotation will be added to the node's current rotation. The axis can |
| be specified relative to different \l {TransformSpace}{\a {space}s}. |
| */ |
| void QQuick3DNode::rotate(qreal degrees, const QVector3D &axis, TransformSpace space) |
| { |
| Q_D(QQuick3DNode); |
| |
| const QQuaternion addRotationQuat = QQuaternion::fromAxisAndAngle(axis, float(degrees)); |
| const QMatrix4x4 addRotationMatrix = QMatrix4x4(addRotationQuat.toRotationMatrix()); |
| QMatrix4x4 newRotationMatrix; |
| |
| switch (space) { |
| case LocalSpace: |
| newRotationMatrix = d->localRotationMatrix() * addRotationMatrix; |
| break; |
| case ParentSpace: |
| newRotationMatrix = addRotationMatrix * d->localRotationMatrix(); |
| break; |
| case SceneSpace: |
| if (const auto parent = parentNode()) { |
| const QMatrix4x4 lrm = d->localRotationMatrix(); |
| const QMatrix4x4 prm = QQuick3DNodePrivate::get(parent)->sceneRotationMatrix(); |
| newRotationMatrix = prm.inverted() * addRotationMatrix * prm * lrm; |
| } else { |
| newRotationMatrix = d->localRotationMatrix() * addRotationMatrix; |
| } |
| break; |
| } |
| |
| const QVector3D newRotationEuler = mat44::getRotation(newRotationMatrix, EulerOrder(d->m_rotationorder)); |
| |
| if (d->m_rotation == newRotationEuler) |
| return; |
| |
| d->m_rotation = newRotationEuler; |
| d->markSceneTransformDirty(); |
| emit rotationChanged(); |
| update(); |
| } |
| |
| QSSGRenderGraphObject *QQuick3DNode::updateSpatialNode(QSSGRenderGraphObject *node) |
| { |
| Q_D(QQuick3DNode); |
| if (!node) { |
| markAllDirty(); |
| node = new QSSGRenderNode(); |
| } |
| |
| auto spacialNode = static_cast<QSSGRenderNode *>(node); |
| bool transformIsDirty = false; |
| if (spacialNode->position != d->m_position) { |
| transformIsDirty = true; |
| spacialNode->position = d->m_position; |
| } |
| |
| const QVector3D rotation = degToRad(d->m_rotation); |
| if (spacialNode->rotation != rotation) { |
| transformIsDirty = true; |
| spacialNode->rotation = rotation; |
| } |
| |
| if (spacialNode->scale != d->m_scale) { |
| transformIsDirty = true; |
| spacialNode->scale = d->m_scale; |
| } |
| if (spacialNode->pivot != d->m_pivot) { |
| transformIsDirty = true; |
| spacialNode->pivot = d->m_pivot; |
| } |
| |
| if (spacialNode->rotationOrder != EulerOrder(d->m_rotationorder)) { |
| transformIsDirty = true; |
| spacialNode->rotationOrder = EulerOrder(d->m_rotationorder); |
| } |
| |
| spacialNode->localOpacity = d->m_opacity; |
| |
| const bool leftHanded = d->m_orientation == LeftHanded; |
| if (spacialNode->flags.testFlag(QSSGRenderNode::Flag::LeftHanded) != leftHanded) { |
| transformIsDirty = true; |
| spacialNode->flags.setFlag(QSSGRenderNode::Flag::LeftHanded, leftHanded); |
| } |
| |
| // The Hidden in Editor flag overrides the visible value |
| if (d->m_isHiddenInEditor) |
| spacialNode->flags.setFlag(QSSGRenderNode::Flag::Active, false); |
| else |
| spacialNode->flags.setFlag(QSSGRenderNode::Flag::Active, d->m_visible); |
| |
| if (transformIsDirty) { |
| spacialNode->markDirty(QSSGRenderNode::TransformDirtyFlag::TransformIsDirty); |
| spacialNode->calculateGlobalVariables(); |
| // Still needs to be marked dirty if it will show up correctly in the backend |
| // Note: no longer sure if this is still needed after we now do our own |
| // calculation of the global matrix from the front-end. |
| spacialNode->flags.setFlag(QSSGRenderNode::Flag::Dirty, true); |
| } else { |
| spacialNode->markDirty(QSSGRenderNode::TransformDirtyFlag::TransformNotDirty); |
| } |
| |
| return spacialNode; |
| } |
| |
| /*! |
| \qmlmethod vector3d QtQuick3D::Node::mapPositionToScene(vector3d localPosition) |
| |
| Transforms \a localPosition from local space to scene space. |
| |
| \note "Scene space" is sometimes also reffered to as the "global space". But |
| then in the meaning "global in the 3D world", and not "global to the |
| screen or desktop" (which is usually the interpretation in other Qt APIs). |
| |
| \sa mapPositionFromScene, mapPositionToNode, mapPositionFromNode |
| */ |
| QVector3D QQuick3DNode::mapPositionToScene(const QVector3D &localPosition) const |
| { |
| return mat44::transform(sceneTransform(), localPosition); |
| } |
| |
| /*! |
| \qmlmethod vector3d QtQuick3D::Node::mapPositionFromScene(vector3d scenePosition) |
| |
| Transforms \a scenePosition from scene space to local space. |
| |
| \sa mapPositionToScene, mapPositionToNode, mapPositionFromNode |
| */ |
| QVector3D QQuick3DNode::mapPositionFromScene(const QVector3D &scenePosition) const |
| { |
| return mat44::transform(sceneTransform().inverted(), scenePosition); |
| } |
| |
| /*! |
| \qmlmethod vector3d QtQuick3D::Node::mapPositionToNode(QtQuick3D::Node node, vector3d localPosition) |
| |
| Transforms \a localPosition from the local space of this node to |
| the local space of \a node. |
| |
| \sa mapPositionToScene, mapPositionFromScene, mapPositionFromNode |
| */ |
| QVector3D QQuick3DNode::mapPositionToNode(QQuick3DNode *node, const QVector3D &localPosition) const |
| { |
| return node->mapPositionFromScene(mapPositionToScene(localPosition)); |
| } |
| |
| /*! |
| \qmlmethod vector3d QtQuick3D::Node::mapPositionFromNode(QtQuick3D::Node node, vector3d localPosition) |
| |
| Transforms \a localPosition from the local space of \a node to |
| the local space of this node. |
| |
| \sa mapPositionToScene, mapPositionFromScene, mapPositionToNode |
| */ |
| QVector3D QQuick3DNode::mapPositionFromNode(QQuick3DNode *node, const QVector3D &localPosition) const |
| { |
| return mapPositionFromScene(node->mapPositionToScene(localPosition)); |
| } |
| |
| /*! |
| \qmlmethod vector3d QtQuick3D::Node::mapDirectionToScene(vector3d localDirection) |
| |
| Transforms \a localDirection from local space to scene space. |
| The return value is not affected by the (inherited) scale or |
| position of the node. |
| |
| \note the return value will have the same length as \a localDirection |
| (i.e. not normalized). |
| |
| \sa mapDirectionFromScene, mapDirectionToNode, mapDirectionFromNode |
| */ |
| QVector3D QQuick3DNode::mapDirectionToScene(const QVector3D &localDirection) const |
| { |
| QMatrix3x3 theDirMatrix = mat44::getUpper3x3(sceneTransform()); |
| theDirMatrix = mat33::getInverse(theDirMatrix).transposed(); |
| return mat33::transform(theDirMatrix, localDirection); |
| } |
| |
| /*! |
| \qmlmethod vector3d QtQuick3D::Node::mapDirectionFromScene(vector3d sceneDirection) |
| |
| Transforms \a sceneDirection from scene space to local space. |
| The return value is not affected by the (inherited) scale or |
| position of the node. |
| |
| \note the return value will have the same length as \a sceneDirection |
| (i.e not normalized). |
| |
| \sa mapDirectionToScene, mapDirectionToNode, mapDirectionFromNode |
| */ |
| QVector3D QQuick3DNode::mapDirectionFromScene(const QVector3D &sceneDirection) const |
| { |
| QMatrix3x3 theDirMatrix = mat44::getUpper3x3(sceneTransform()); |
| theDirMatrix = theDirMatrix.transposed(); |
| return mat33::transform(theDirMatrix, sceneDirection); |
| } |
| |
| /*! |
| \qmlmethod vector3d QtQuick3D::Node::mapDirectionToNode(QtQuick3D::Node node, vector3d localDirection) |
| |
| Transforms \a localDirection from this nodes local space to the |
| local space of \a node. |
| The return value is not affected by the (inherited) scale or |
| position of the node. |
| |
| \note the return value will have the same length as \a localDirection |
| (i.e. not normalized). |
| |
| \sa mapDirectionFromNode, mapDirectionFromScene, mapDirectionToScene |
| */ |
| QVector3D QQuick3DNode::mapDirectionToNode(QQuick3DNode *node, const QVector3D &localDirection) const |
| { |
| return node->mapDirectionFromScene(mapDirectionToScene(localDirection)); |
| } |
| |
| /*! |
| \qmlmethod vector3d QtQuick3D::Node::mapDirectionFromNode(QtQuick3D::Node node, vector3d localDirection) |
| |
| Transforms \a localDirection from the local space of \a node to the |
| local space of this node. |
| The return value is not affected by the (inherited) scale or |
| position of the node. |
| |
| \note the return value will have the same length as \a localDirection |
| (i.e. not normalized). |
| |
| \sa mapDirectionToNode, mapDirectionFromScene, mapDirectionToScene |
| */ |
| QVector3D QQuick3DNode::mapDirectionFromNode(QQuick3DNode *node, const QVector3D &localDirection) const |
| { |
| return mapDirectionFromScene(node->mapDirectionToScene(localDirection)); |
| } |
| |
| void QQuick3DNode::markAllDirty() |
| { |
| Q_D(QQuick3DNode); |
| |
| d->markSceneTransformDirty(); |
| QQuick3DObject::markAllDirty(); |
| } |
| |
| QT_END_NAMESPACE |