Qt Quick 3D - Simple Skinning Example
Demonstrates how to render a simple skinning animation in Qt Quick 3D.
Generally most skin animations will be designed by modeling tools and Quick3D also supports glTF formats by Balsam importer and Qt Design Studio. This example shows how each property is used for the skin animation in Quick3D.
Note: All the data in this example come from gfTF-Tutorial Skins.
Make a skinning geometry.
To use custom geometry data, we will define a geometry having positions, joints, weights, and indexes.
Q_OBJECT QML_NAMED_ELEMENT(SkinGeometry) Q_PROPERTY(QList<QVector3D> positions READ positions WRITE setPositions NOTIFY positionsChanged) Q_PROPERTY(QList<qint32> joints READ joints WRITE setJoints NOTIFY jointsChanged) Q_PROPERTY(QList<float> weights READ weights WRITE setWeights NOTIFY weightsChanged) Q_PROPERTY(QList<quint32> indexes READ indexes WRITE setIndexes NOTIFY indexesChanged)
Each position is a vertex position and each vertex has 4 joint's indexes and corresponding weights.
Set up skinned data in QML
Position data and indexes
We will draw 8 trianges with 10 vertexes.
positions: [ Qt.vector3d(0.0, 0.0, 0.0), // vertex 0 Qt.vector3d(1.0, 0.0, 0.0), // vertex 1 Qt.vector3d(0.0, 0.5, 0.0), // vertex 2 Qt.vector3d(1.0, 0.5, 0.0), // vertex 3 Qt.vector3d(0.0, 1.0, 0.0), // vertex 4 Qt.vector3d(1.0, 1.0, 0.0), // vertex 5 Qt.vector3d(0.0, 1.5, 0.0), // vertex 6 Qt.vector3d(1.0, 1.5, 0.0), // vertex 7 Qt.vector3d(0.0, 2.0, 0.0), // vertex 8 Qt.vector3d(1.0, 2.0, 0.0) // vertex 9 ] indexes: [ 0, 1, 3, // triangle 0 0, 3, 2, // triangle 1 2, 3, 5, // triangle 2 2, 5, 4, // triangle 3 4, 5, 7, // triangle 4 4, 7, 6, // triangle 5 6, 7, 9, // triangle 6 6, 9, 8 // triangle 7 ]
Joints and weights data
Our geometry will have just 2 joint nodes and remaining 2 joint data will be 0.
joints: [ 0, 1, 0, 0, // vertex 0 0, 1, 0, 0, // vertex 1 0, 1, 0, 0, // vertex 2 0, 1, 0, 0, // vertex 3 0, 1, 0, 0, // vertex 4 0, 1, 0, 0, // vertex 5 0, 1, 0, 0, // vertex 6 0, 1, 0, 0, // vertex 7 0, 1, 0, 0, // vertex 8 0, 1, 0, 0 // vertex 9 ]
Corresponding 2 weight values are as below.
weights: [ 1.00, 0.00, 0.0, 0.0, // vertex 0 1.00, 0.00, 0.0, 0.0, // vertex 1 0.75, 0.25, 0.0, 0.0, // vertex 2 0.75, 0.25, 0.0, 0.0, // vertex 3 0.50, 0.50, 0.0, 0.0, // vertex 4 0.50, 0.50, 0.0, 0.0, // vertex 5 0.25, 0.75, 0.0, 0.0, // vertex 6 0.25, 0.75, 0.0, 0.0, // vertex 7 0.00, 1.00, 0.0, 0.0, // vertex 8 0.00, 1.00, 0.0, 0.0 // vertex 9 ]
Skeleton and Joint hierarchy
These 2 Joints are connected in a Skeleton. We will rotate joint1 45 degrees around the z-axis. For skinning, Model should have a proper skeleton property.
skeleton: Skeleton { id: qmlskeleton Joint { id: joint0 index: 0 skeletonRoot: qmlskeleton Joint { id: joint1 index: 1 skeletonRoot: qmlskeleton eulerRotation.z: 45 } } }
inverseBindPoses
We want to place our Model into the origin. It is possible to transform it but in this example, we will move it with a property inverseBindPoses. For two Joints' initial position we will simply give the same transform which translates -0.5 along the x-axis and -1.0 along the y-axis.
inverseBindPoses: [ Qt.matrix4x4(1, 0, 0, -0.5, 0, 1, 0, -1, 0, 0, 1, 0, 0, 0, 0, 1), Qt.matrix4x4(1, 0, 0, -0.5, 0, 1, 0, -1, 0, 0, 1, 0, 0, 0, 0, 1) ]
Animate with Joint nodes
Now, we prepared a skinned object and we can animate it with changing Joints' properties; position, rotation, scale.
Files: