| /**************************************************************************** |
| ** |
| ** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt3D module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** 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 Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or 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.GPL2 and 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-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qjoint.h" |
| #include "qjoint_p.h" |
| |
| #include <Qt3DCore/qnodecreatedchange.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| namespace Qt3DCore { |
| |
| QJointPrivate::QJointPrivate() |
| : QNodePrivate() |
| , m_inverseBindMatrix() |
| , m_rotation() |
| , m_translation() |
| , m_scale(1.0f, 1.0f, 1.0f) |
| { |
| } |
| |
| /*! |
| \qmltype Joint |
| \inqmlmodule Qt3D.Core |
| \inherits Node |
| \instantiates Qt3DCore::QJoint |
| \since 5.10 |
| \brief Used to transforms parts of skinned meshes. |
| |
| The Joint node is used to build skeletons as part of the skinned mesh |
| support in Qt 3D. A joint can be transformed by way of its scale, rotation |
| and translation properties. Any mesh vertices that are bound to the joint |
| will have their transformations updated accordingly. |
| */ |
| |
| /*! |
| \qmlproperty vector3d Joint::scale |
| |
| Holds the uniform scale of the joint. |
| */ |
| |
| /*! |
| \qmlproperty quaternion Joint::rotation |
| |
| Holds the rotation of the joint as quaternion. |
| */ |
| |
| /*! |
| \qmlproperty vector3d Joint::translation |
| |
| Holds the translation of the joint as vector3d. |
| */ |
| |
| /*! |
| \qmlproperty real Joint::rotationX |
| |
| Holds the x rotation of the joint as an Euler angle. |
| */ |
| |
| /*! |
| \qmlproperty real Joint::rotationY |
| |
| Holds the y rotation of the joint as an Euler angle. |
| */ |
| |
| /*! |
| \qmlproperty real Joint::rotationZ |
| |
| Holds the z rotation of the joint as an Euler angle. |
| */ |
| |
| /*! |
| \qmlproperty matrix4x4 Joint::inverseBindMatrix |
| |
| Holds the inverse bind matrix of the joint. This is used to transform |
| vertices from model space into the space of this joint so they can |
| subsequently be multiplied by the joint's global transform to perform |
| the skinning operation. |
| */ |
| |
| /*! |
| \class Qt3DCore::QJoint |
| \inmodule Qt3DCore |
| \inherits Qt3DCore::QNode |
| \since 5.10 |
| \brief Used to transforms parts of skinned meshes. |
| |
| The QJoint node is used to build skeletons as part of the skinned mesh |
| support in Qt 3D. A joint can be transformed by way of its scale, rotation |
| and translation properties. Any mesh vertices that are bound to the joint |
| will have their transformations updated accordingly. |
| */ |
| |
| /*! |
| Constructs a new QJoint with \a parent. |
| */ |
| QJoint::QJoint(Qt3DCore::QNode *parent) |
| : QNode(*new QJointPrivate, parent) |
| { |
| } |
| |
| /*! \internal */ |
| QJoint::~QJoint() |
| { |
| } |
| |
| /*! |
| \property Qt3DCore::QJoint::scale |
| |
| Holds the scale of the joint. |
| */ |
| QVector3D QJoint::scale() const |
| { |
| Q_D(const QJoint); |
| return d->m_scale; |
| } |
| |
| /*! |
| \property Qt3DCore::QJoint::rotation |
| |
| Holds the rotation of the joint as QQuaternion. |
| */ |
| QQuaternion QJoint::rotation() const |
| { |
| Q_D(const QJoint); |
| return d->m_rotation; |
| } |
| |
| /*! |
| \property Qt3DCore::QJoint::translation |
| |
| Holds the translation of the joint as QVector3D. |
| */ |
| QVector3D QJoint::translation() const |
| { |
| Q_D(const QJoint); |
| return d->m_translation; |
| } |
| |
| /*! |
| \property Qt3DCore::QJoint::inverseBindMatrix |
| |
| Holds the inverse bind matrix of the joint. This is used to transform |
| vertices from model space into the space of this joint so they can |
| subsequently be multiplied by the joint's global transform to perform |
| the skinning operation. |
| */ |
| QMatrix4x4 QJoint::inverseBindMatrix() const |
| { |
| Q_D(const QJoint); |
| return d->m_inverseBindMatrix; |
| } |
| |
| /*! |
| \property Qt3DCore::QJoint::rotationX |
| |
| Holds the x rotation of the joint as an Euler angle. |
| */ |
| float QJoint::rotationX() const |
| { |
| Q_D(const QJoint); |
| return d->m_eulerRotationAngles.x(); |
| } |
| |
| /*! |
| \property Qt3DCore::QJoint::rotationY |
| |
| Holds the y rotation of the joint as an Euler angle. |
| */ |
| float QJoint::rotationY() const |
| { |
| Q_D(const QJoint); |
| return d->m_eulerRotationAngles.y(); |
| } |
| |
| /*! |
| \property Qt3DCore::QJoint::rotationZ |
| |
| Holds the z rotation of the joint as an Euler angle. |
| */ |
| float QJoint::rotationZ() const |
| { |
| Q_D(const QJoint); |
| return d->m_eulerRotationAngles.z(); |
| } |
| |
| void QJoint::setScale(const QVector3D &scale) |
| { |
| Q_D(QJoint); |
| if (scale == d->m_scale) |
| return; |
| |
| d->m_scale = scale; |
| emit scaleChanged(scale); |
| } |
| |
| void QJoint::setRotation(const QQuaternion &rotation) |
| { |
| Q_D(QJoint); |
| if (rotation == d->m_rotation) |
| return; |
| |
| d->m_rotation = rotation; |
| const QVector3D oldRotation = d->m_eulerRotationAngles; |
| d->m_eulerRotationAngles = d->m_rotation.toEulerAngles(); |
| emit rotationChanged(rotation); |
| |
| const bool wasBlocked = blockNotifications(true); |
| if (!qFuzzyCompare(d->m_eulerRotationAngles.x(), oldRotation.x())) |
| emit rotationXChanged(d->m_eulerRotationAngles.x()); |
| if (!qFuzzyCompare(d->m_eulerRotationAngles.y(), oldRotation.y())) |
| emit rotationYChanged(d->m_eulerRotationAngles.y()); |
| if (!qFuzzyCompare(d->m_eulerRotationAngles.z(), oldRotation.z())) |
| emit rotationZChanged(d->m_eulerRotationAngles.z()); |
| blockNotifications(wasBlocked); |
| } |
| |
| void QJoint::setTranslation(const QVector3D &translation) |
| { |
| Q_D(QJoint); |
| if (translation == d->m_translation) |
| return; |
| |
| d->m_translation = translation; |
| emit translationChanged(translation); |
| } |
| |
| void QJoint::setInverseBindMatrix(const QMatrix4x4 &inverseBindMatrix) |
| { |
| Q_D(QJoint); |
| if (d->m_inverseBindMatrix == inverseBindMatrix) |
| return; |
| |
| d->m_inverseBindMatrix = inverseBindMatrix; |
| emit inverseBindMatrixChanged(inverseBindMatrix); |
| } |
| |
| void QJoint::setRotationX(float rotationX) |
| { |
| Q_D(QJoint); |
| |
| if (qFuzzyCompare(d->m_eulerRotationAngles.x(), rotationX)) |
| return; |
| |
| const auto eulers = QVector3D(rotationX, |
| d->m_eulerRotationAngles.y(), |
| d->m_eulerRotationAngles.z()); |
| const QQuaternion r = QQuaternion::fromEulerAngles(eulers); |
| setRotation(r); |
| } |
| |
| void QJoint::setRotationY(float rotationY) |
| { |
| Q_D(QJoint); |
| |
| if (qFuzzyCompare(d->m_eulerRotationAngles.y(), rotationY)) |
| return; |
| |
| const auto eulers = QVector3D(d->m_eulerRotationAngles.x(), |
| rotationY, |
| d->m_eulerRotationAngles.z()); |
| const QQuaternion r = QQuaternion::fromEulerAngles(eulers); |
| setRotation(r); |
| } |
| |
| void QJoint::setRotationZ(float rotationZ) |
| { |
| Q_D(QJoint); |
| if (qFuzzyCompare(d->m_eulerRotationAngles.z(), rotationZ)) |
| return; |
| |
| const auto eulers = QVector3D(d->m_eulerRotationAngles.x(), |
| d->m_eulerRotationAngles.y(), |
| rotationZ); |
| const QQuaternion r = QQuaternion::fromEulerAngles(eulers); |
| setRotation(r); |
| } |
| |
| void QJoint::setName(const QString &name) |
| { |
| Q_D(QJoint); |
| if (d->m_name == name) |
| return; |
| |
| d->m_name = name; |
| emit nameChanged(name); |
| } |
| |
| /*! |
| Sets the transform matrix for this joint to the identity matrix. |
| */ |
| void QJoint::setToIdentity() |
| { |
| setScale(QVector3D(1.0f, 1.0f, 1.0f)); |
| setRotation(QQuaternion()); |
| setTranslation(QVector3D()); |
| } |
| |
| /*! |
| Adds \a joint as a child of this joint. If \a joint has no parent, then |
| this joint takes ownership of it. Child joints are in the coordinate system |
| of their parent joint. |
| */ |
| void QJoint::addChildJoint(QJoint *joint) |
| { |
| Q_D(QJoint); |
| if (!d->m_childJoints.contains(joint)) { |
| d->m_childJoints.push_back(joint); |
| // Force creation in backend by setting parent |
| if (!joint->parent()) |
| joint->setParent(this); |
| |
| // Ensures proper bookkeeping |
| d->registerDestructionHelper(joint, &QJoint::removeChildJoint, d->m_childJoints); |
| |
| if (d->m_changeArbiter != nullptr) |
| d->updateNode(joint, "childJoint", PropertyValueAdded); |
| } |
| } |
| |
| /*! |
| Removes \a joint from this joint's list of children. The child joint is not |
| destroyed. |
| */ |
| void QJoint::removeChildJoint(QJoint *joint) |
| { |
| Q_D(QJoint); |
| if (d->m_childJoints.contains(joint)) { |
| if (d->m_changeArbiter != nullptr) |
| d->updateNode(joint, "childJoint", PropertyValueRemoved); |
| |
| d->m_childJoints.removeOne(joint); |
| |
| // Remove bookkeeping connection |
| d->unregisterDestructionHelper(joint); |
| } |
| } |
| |
| /*! |
| The vector of joints this joint has as children. |
| */ |
| QVector<QJoint *> QJoint::childJoints() const |
| { |
| Q_D(const QJoint); |
| return d->m_childJoints; |
| } |
| |
| /*! |
| Returns the name of the joint. |
| */ |
| QString QJoint::name() const |
| { |
| Q_D(const QJoint); |
| return d->m_name; |
| } |
| |
| /*! \internal */ |
| Qt3DCore::QNodeCreatedChangeBasePtr QJoint::createNodeCreationChange() const |
| { |
| auto creationChange = Qt3DCore::QNodeCreatedChangePtr<QJointData>::create(this); |
| auto &data = creationChange->data; |
| Q_D(const QJoint); |
| data.inverseBindMatrix = d->m_inverseBindMatrix; |
| data.childJointIds = qIdsForNodes(d->m_childJoints); |
| data.rotation = d->m_rotation; |
| data.scale = d->m_scale; |
| data.translation = d->m_translation; |
| data.name = d->m_name; |
| return creationChange; |
| } |
| |
| } // namespace Qt3DCore |
| |
| QT_END_NAMESPACE |