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.

"Vertex positions and geomery"

 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
 ]

"2 joints in the geometry"

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
         }
     }
 }

"Initial Skeleton"

inverseBindPoses

Now the model is placed on the upper right position on the screen.

"Initial Position"

Once a model has a valid skeleton, it cannot be transformed by its global transformation. You need to transform it with each Joint's transformation. However, it is cumbersome to calculate the Joint's transform when considering the initial positions. inverseBindPoses can be used to set the initial states of the Joints. In order to move it to the center, we will simply set 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)
 ]

"Transformed by InversebindPoses"

Animate with Joint nodes

"Final Scene"

Now, we prepared a skinned object and we can animate it with changing Joints' properties; position, rotation, scale.

Files: