1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qquick3djoint_p.h"
5#include "qquick3dskeleton_p.h"
6#include "qquick3dobject_p.h"
7#include "qquick3dscenemanager_p.h"
8#include "qquick3dnode_p_p.h"
9
10#include <QtQuick3DRuntimeRender/private/qssgrendergraphobject_p.h>
11#include <QtQuick3DRuntimeRender/private/qssgrendernode_p.h>
12#include <QtQuick3DRuntimeRender/private/qssgrenderjoint_p.h>
13
14QT_BEGIN_NAMESPACE
15
16/*!
17 \qmltype Joint
18 \inherits Node
19 \inqmlmodule QtQuick3D
20 \brief Defines a node in a skeletal animation hierarchy.
21
22 A joint is a transformable node inside a \l {Skeleton}, used for \l {Vertex Skinning}
23 {skeletal animation}. It is called a "joint" because it can be seen as a joint between the bones
24 of a skeleton.
25
26 All the joints must be contained inside a Skeleton, and each joint must have a \l skeletonRoot
27 pointing back to that skeleton.
28
29 \qml
30 Skeleton {
31 id: qmlskeleton
32 Joint {
33 id: joint0
34 index: 0
35 skeletonRoot: qmlskeleton
36 Joint {
37 id: joint1
38 index: 1
39 skeletonRoot: qmlskeleton
40 }
41 }
42 }
43 \endqml
44
45*/
46
47QQuick3DJoint::QQuick3DJoint(QQuick3DNode *parent)
48 : QQuick3DNode(*(new QQuick3DNodePrivate(QQuick3DNodePrivate::Type::Joint)), parent)
49{
50}
51
52QQuick3DJoint::~QQuick3DJoint()
53{
54 disconnect(m_skeletonConnection);
55}
56
57/*!
58 \qmlproperty int Joint::index
59
60 Specifies the index of this joint. This index value is used in the \c JointSemantic
61 \l {QQuick3DGeometry::addAttribute}{custom geometry attribute}.
62
63 \note Index values must be unique within the same \l {Skeleton}.
64 \note Negative values cannot be assigned.
65
66 \sa {QQuick3DGeometry::addAttribute}, {Qt Quick 3D - Simple Skinning Example}
67*/
68
69qint32 QQuick3DJoint::index() const
70{
71 return m_index;
72}
73
74/*!
75 \qmlproperty Skeleton Joint::skeletonRoot
76
77 Specifies the \l {Skeleton} that contains this joint.
78
79 \note All the \l {Joint}s in the \l {Skeleton} must have the same skeletonRoot.
80 If not, the animation will be broken.
81
82 \sa {Skeleton}
83*/
84
85QQuick3DSkeleton *QQuick3DJoint::skeletonRoot() const
86{
87 return m_skeletonRoot;
88}
89
90void QQuick3DJoint::setIndex(qint32 index)
91{
92 if (m_index == index)
93 return;
94 if (index < 0)
95 return;
96
97 m_index = index;
98 m_indexDirty = true;
99 emit indexChanged();
100}
101
102void QQuick3DJoint::setSkeletonRoot(QQuick3DSkeleton *skeleton)
103{
104 if (skeleton == m_skeletonRoot)
105 return;
106
107 QQuick3DObjectPrivate::attachWatcher(context: this, setter: &QQuick3DJoint::setSkeletonRoot, newO: skeleton, oldO: m_skeletonRoot);
108 if (m_skeletonRoot)
109 QObject::disconnect(m_skeletonConnection);
110
111 m_skeletonRoot = skeleton;
112
113 if (m_skeletonRoot) {
114 m_skeletonConnection = connect(sender: this, signal: &QQuick3DJoint::sceneTransformChanged,
115 context: skeleton, slot: [skeleton]() {
116 auto skeletonNode = static_cast<QSSGRenderSkeleton *>(QQuick3DNodePrivate::get(node: skeleton)->spatialNode);
117 if (skeletonNode)
118 skeletonNode->skinningDirty = true;
119 });
120 }
121 m_skeletonRootDirty = true;
122 emit skeletonRootChanged();
123}
124
125
126void QQuick3DJoint::markAllDirty()
127{
128 m_indexDirty = true;
129 m_skeletonRootDirty = true;
130 QQuick3DNode::markAllDirty();
131}
132
133QSSGRenderGraphObject *QQuick3DJoint::updateSpatialNode(QSSGRenderGraphObject *node)
134{
135 if (!m_skeletonRoot)
136 return node;
137
138 if (!node) {
139 markAllDirty();
140 node = new QSSGRenderJoint();
141 }
142
143 QQuick3DNode::updateSpatialNode(node);
144
145 auto jointNode = static_cast<QSSGRenderJoint *>(node);
146
147 QQuick3DObjectPrivate *skeletonPriv = QQuick3DObjectPrivate::get(item: m_skeletonRoot);
148
149 if (m_skeletonRootDirty) {
150 if (skeletonPriv && skeletonPriv->spatialNode)
151 jointNode->skeletonRoot = static_cast<QSSGRenderSkeleton *>(skeletonPriv->spatialNode);
152 }
153
154 if (m_indexDirty) {
155 jointNode->index = m_index;
156 m_indexDirty = false;
157
158 if (jointNode->skeletonRoot) {
159 Q_ASSERT(m_skeletonRoot);
160 m_skeletonRoot->skeletonNodeDirty();
161
162 if (jointNode->skeletonRoot->maxIndex < m_index) {
163 jointNode->skeletonRoot->maxIndex = m_index;
164 }
165 }
166 }
167 return node;
168}
169
170QT_END_NAMESPACE
171

source code of qtquick3d/src/quick3d/qquick3djoint.cpp