// Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qjoint.h" #include "qjoint_p.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 \nativetype 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->update(); } } /*! 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->update(); d->m_childJoints.removeOne(joint); // Remove bookkeeping connection d->unregisterDestructionHelper(joint); } } /*! The vector of joints this joint has as children. */ QList QJoint::childJoints() const { Q_D(const QJoint); return d->m_childJoints; } /*! \property Qt3DCore::QJoint::name \brief the name of the joint. Holds the name of the joint, which can be used to identify the joint within a skeleton. */ /*! Returns the name of the joint. */ QString QJoint::name() const { Q_D(const QJoint); return d->m_name; } } // namespace Qt3DCore QT_END_NAMESPACE #include "moc_qjoint.cpp"