Vertex Skinning

Introduction

Qt Quick 3D supports vertex skinning for skeletal animation of mesh geometries.

See the Simple Skinning Example for a practical demonstration of skeletal animation.

In most cases, application developers will not be using the skinning API manually. The normal workflow is to use an external content creation tool to define the skeleton and the skin (this is sometimes also referred to as rigging), and then use the Balsam Asset Import Tool to convert the asset to Qt Quick 3D's native format.

Defining a skeleton

The basis of skeletal animation is the Skeleton. This is an abstract representation of how the model can move, inspired by how a physical skeleton works for vertebrates. The "bones" of the skeleton is represented by a hierarchy of Joint nodes. These do not necessarily need to represent actual bones, of course.

 Skeleton {
     id: qmlskeleton
     Joint {
         id: joint0
         index: 0
         skeletonRoot: qmlskeleton
         Joint {
             id: joint1
             index: 1
             skeletonRoot: qmlskeleton
         }
         Joint {
             id: joint2
             index: 2
             skeletonRoot: qmlskeleton
         }

     }
 }

Connecting a skeleton to a model

To apply a skeleton to a model, set the model's skeleton property:

 Model {
     skeleton: qmlskeleton
     ...

In order for the skeleton to have an effect, the model's geometry needs to include skinning information. This is done by including vertex attributes with JointSemantic and WeightSemantic in the vertex buffer.

The JointSemantic attribute determines which of the joints in the skeleton can influence a given vertex. This uses the index values specified by Joint.index. Since this attribute contains 4 indexes, a maximum of 4 joints can influence one vertex.

The WeightSemantic attribute describes the strength of the influence of those joints. It contains four floating point values, each value determining the weight given to the joint with the index at the corresponding position in the JointSemantic attribute.

For example, given the skeleton above, if a vertex has these attributes:

JointSemantic attributeWeightSemantic attribute
QVector4D(2, 0, 0, 0)QVector4D(1.0, 0.0, 0.0, 0.0)

that vertex will be 100% influenced by joint2, and it will move exactly as much as that joint. The last three indexes in the JointSemantic attribute are ignored since the corresponding weights are 0.0.

As another example, with these attributes:

JointSemantic attributeWeightSemantic attribute
QVector4D(1, 2, 0, 0)QVector4D(0.5, 0.25, 0.0, 0.0)

the vertex will be moved by 50% of joint1's movement plus 25% of joint2's movement.

In addition, since the skeleton is an abstract representation, the model need to specify geometry information for the joints. For performance reasons, this is not done by specifying the information directly. Instead, Model.inverseBindPoses contains the inverse of the transformation matrix needed to move each joint to its initial position.

Animating the skeleton

Transforming a joint in a skeleton will move all vertexes connected to that joint. Since Joint inheriths from Node, a skeleton can be animated simply by using standard QML animations.

 NumberAnimation {
     target: joint1
     property: "eulerRotation.z"
     duration: 5000
     from: -90
     to: 90
     running: true
 }

While it is possible to create complex animations by nesting SequentialAnimation, ParallelAnimation and NumberAnimation, it is generally more convenient to use timeline animations for animating skinned models.