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
qquick3dskin.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 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
9
10#include <QtQuick3DRuntimeRender/private/qssgrendergraphobject_p.h>
11#include <QtQuick3DRuntimeRender/private/qssgrenderskin_p.h>
12
13#include <QtQuick3DUtils/private/qssgutils_p.h>
14
16
17/*!
18 \qmltype Skin
19 \inherits Object3D
20 \inqmlmodule QtQuick3D
21 \brief Defines a skinning animation.
22
23 A skin defines how a model can be animated using \l {Vertex Skinning}
24 {skeletal animation}. It contains a list of \l {Node}s and an optional list
25 of the Inverse Bind Pose Matrices.
26 Each \l {Node}'s transform becomes a transform of the bone with the
27 corresponding index in the list.
28
29 \qml
30 Skin {
31 id: skin0
32 joints: [
33 node0,
34 node1,
35 node2
36 ]
37 inverseBindPoses: [
38 Qt.matrix4x4(...),
39 Qt.matrix4x4(...),
40 Qt.matrix4x4(...)
41 ]
42 }
43 \endqml
44
45 \note \l {Skeleton} and \l {Joint} will be deprecated.
46*/
47
48QQuick3DSkin::QQuick3DSkin(QQuick3DObject *parent)
49 : QQuick3DObject(*(new QQuick3DObjectPrivate(QQuick3DObjectPrivate::Type::Skin)), parent)
50{
51}
52
53QQuick3DSkin::~QQuick3DSkin()
54{
55 for (const auto &conn : m_jointsConnections) {
56 disconnect(conn.first);
57 disconnect(conn.second);
58 }
59}
60
61/*!
62 \qmlproperty List<QtQuick3D::Node> Skin::joints
63
64 This property contains a list of nodes used for a hierarchy of joints.
65 The order in the list becomes the index of the joint, which is used in the
66 \c SkinSemantic \l {QQuick3DGeometry::addAttribute}{custom geometry attribute}.
67
68 \note A value 'undefined' will be ignored and if a node which doesn't exist is
69 described, the result is unpredictable.
70
71 \sa {QQuick3DGeometry::addAttribute}, {Qt Quick 3D - Simple Skinning Example}
72*/
73QQmlListProperty<QQuick3DNode> QQuick3DSkin::joints()
74{
75 return QQmlListProperty<QQuick3DNode>(this,
76 nullptr,
77 QQuick3DSkin::qmlAppendJoint,
78 QQuick3DSkin::qmlJointsCount,
79 QQuick3DSkin::qmlJointAt,
80 QQuick3DSkin::qmlClearJoints);
81}
82
83#define POS4BONETRANS(x) (sizeof(float) * 16 * (x) * 2)
84#define POS4BONENORM(x) (sizeof(float) * 16 * ((x) * 2 + 1))
85
86void QQuick3DSkin::qmlAppendJoint(QQmlListProperty<QQuick3DNode> *list, QQuick3DNode *joint)
87{
88 if (joint == nullptr)
89 return;
90 QQuick3DSkin *self = static_cast<QQuick3DSkin *>(list->object);
91 if (!self->m_jointsConnections.contains(joint)) {
92 self->m_jointsConnections[joint] =
93 std::make_pair(
94 connect(
95 joint, &QQuick3DNode::sceneTransformChanged,
96 self, [self, joint]() {
97 self->m_dirtyJoints.insert(joint);
98 self->update();
99 }),
100 connect(
101 joint, &QQuick3DNode::destroyed,
102 self, [self, joint]() {
103 self->m_dirtyJoints.remove(joint);
104 self->m_removedJoints.insert(joint);
105 self->update();
106 })
107 );
108 }
109
110 self->m_joints.push_back(joint);
111 self->m_dirtyJoints.insert(joint);
112 self->update();
113}
114
115QQuick3DNode *QQuick3DSkin::qmlJointAt(QQmlListProperty<QQuick3DNode> *list, qsizetype index)
116{
117 QQuick3DSkin *self = static_cast<QQuick3DSkin *>(list->object);
118 return self->m_joints.at(index);
119}
120
121qsizetype QQuick3DSkin::qmlJointsCount(QQmlListProperty<QQuick3DNode> *list)
122{
123 QQuick3DSkin *self = static_cast<QQuick3DSkin *>(list->object);
124 return self->m_joints.size();
125}
126
127void QQuick3DSkin::qmlClearJoints(QQmlListProperty<QQuick3DNode> *list)
128{
129 QQuick3DSkin *self = static_cast<QQuick3DSkin *>(list->object);
130 for (const auto &conn : self->m_jointsConnections) {
131 disconnect(conn.first);
132 disconnect(conn.second);
133 }
134 self->m_jointsConnections.clear();
135
136 self->m_joints.clear();
137 self->m_boneData.clear();
138 self->m_dirtyJoints.clear();
139 self->m_removedJoints.clear();
140 self->update();
141}
142
143
144/*!
145 \qmlproperty List<matrix4x4> Skin::inverseBindPoses
146
147 This property contains a list of Inverse Bind Pose matrixes used for the
148 skinning animation. Each inverseBindPose matrix means the inverse of the
149 global transform of the corresponding node in \l {Skin::joints},
150 used initially.
151
152 \note This property is an optional property. That is, if some or all of the
153 matrices are not set, identity values will be used.
154*/
155QList<QMatrix4x4> QQuick3DSkin::inverseBindPoses() const
156{
157 return m_inverseBindPoses;
158}
159
160void QQuick3DSkin::setInverseBindPoses(const QList<QMatrix4x4> &poses)
161{
162 if (m_inverseBindPoses == poses)
163 return;
164
165 m_updatedByNewInverseBindPoses = qMax(poses.size(), m_inverseBindPoses.size());
166 m_inverseBindPoses = poses;
167
168 emit inverseBindPosesChanged();
169 update();
170}
171
172QSSGRenderGraphObject *QQuick3DSkin::updateSpatialNode(QSSGRenderGraphObject *node)
173{
174 if (!node) {
175 markAllDirty();
176 node = new QSSGRenderSkin();
177 }
178 QQuick3DObject::updateSpatialNode(node);
179 auto skinNode = static_cast<QSSGRenderSkin *>(node);
180
181 if (!m_removedJoints.empty()) {
182 for (int i = m_joints.size() - 1; i >= 0; --i) {
183 const auto &joint = m_joints.at(i);
184 if (m_removedJoints.contains(joint)) {
185 m_joints.removeAt(i);
186 m_boneData.remove(POS4BONETRANS(i),
187 sizeof(float) * 16 * 2);
188 }
189 }
190 m_removedJoints.clear();
191 }
192
193 if (skinNode->boneCount != quint32(m_joints.size())) {
194 skinNode->boneCount = quint32(m_joints.size());
195 const int boneTexWidth = qCeil(qSqrt(skinNode->boneCount * 4 * 2));
196 const int textureSizeInBytes = boneTexWidth * boneTexWidth * 16; //NB: Assumes RGBA32F set above (16 bytes per color)
197 m_boneData.resize(textureSizeInBytes);
198 skinNode->setSize(QSize(boneTexWidth, boneTexWidth));
199 }
200
201 if (m_updatedByNewInverseBindPoses > 0 || !m_dirtyJoints.empty()) {
202 for (int i = 0; i < m_joints.size(); ++i) {
203 const auto &joint = m_joints.at(i);
204 if (i < m_updatedByNewInverseBindPoses || m_dirtyJoints.contains(joint)) {
205 QMatrix4x4 jointGlobal = joint->sceneTransform();
206 if (m_inverseBindPoses.size() > i)
207 jointGlobal *= m_inverseBindPoses.at(i);
208 memcpy(m_boneData.data() + POS4BONETRANS(i),
209 reinterpret_cast<const void *>(jointGlobal.constData()),
210 sizeof(float) * 16);
211 memcpy(m_boneData.data() + POS4BONENORM(i),
212 reinterpret_cast<const void *>(QMatrix4x4(jointGlobal.normalMatrix()).constData()),
213 sizeof(float) * 11);
214 }
215 }
216 m_dirtyJoints.clear();
217 m_updatedByNewInverseBindPoses = 0;
218 }
219
220 skinNode->setTextureData(m_boneData);
221 return node;
222}
223
224QT_END_NAMESPACE
Combined button and popup list for selecting options.
#define POS4BONETRANS(x)
#define POS4BONENORM(x)