Parsing a Collada file - Fish-In-A-Suit/Conquest GitHub Wiki

There are two main components that we want to load up from files for use in the animation system. One is animated models and the other is animations.

For animated models (AnimatedModel), we are going to need to extract the mesh (skin) and the skeleton (hierarchy of joints).

MESH DATA is just everything that is stored in a VAO: vertex position, texture coordinates, normals, IDs of joints and weights. When you're parsing skinning information (ie joint ids and weights), you need to be aware of the limitations of the animation system that we've built: each vertex can only have a maximum of three joints affecting it (so 3 joint ids and 3 weights) --> therefore, you'll only have to take the three most influential joint id and weights. The second thing to be careful with is that all of the weights that affect a vertex must add up to one. Since we are going to be taking the three most influential ids (weights; there are likely to be more than three joints that affect a certain vertex defined in collada files), the weights won't add up to one --> normalize the weight values.

The skeleton is made up of a hierarchy of _Joint_s and each Joint needs the following data: index, name and the original bind transform of the joint (either in bone-space, relative to the parent joint, or in model-space, if the exported format supports that --> in this case assign it (model-space bind transform) to inverseBindTransform): public Joint(int index, String name, Matrix4f bindLocalTransform).

  • The joint index is the position in the jointTransforms (in animatedEntityVertex.glsl) array and it has to correspond with the joint IDs (in_jointIndices in animatedEntityVertex.glsl) - you have to use indexes that the file is using for a joint, rather than coming up with your own order.
  • name is used to identify a joint and in Animation data structure to indicate which transform applies to each joint.

ForANIMATION DATA, all that there is to be loaded up is the information about the keyframes. For each KeyFrame we need to load up the time at which it occurs (timeStamp) and the bone-space transform of the joints (pose), which would be ideally stored as a Vector3f position and Quaternion rotation, but they may be in a different format like in Collada files where they are stored as matrices --> convert.

Collada files are structured in .xml format. It is split into multiple sections. The sections (libraries) that are important for our animation system are: geometries (which contains the mesh information), animations (which contains all the data about the animation keyframes), controllers (which holds skinning information - joint IDs and weights) and visual scenes (contains information about the joint hierarchy).

collada format structure

Geometries library stores basic mesh data, same as the one you'd expect to find in a .obj file. From here, we can parse vertex position, texture coordinates, normals and indices.

collada geometries lib

In comparison to .obj, the Collada format already gives you the number of floats that are in a desired array, specified by the count variable. Accessor section gives further information about model data - for vertex positions, it tells how many floats comprise one vertex position and how many vertex positional vectors there are in total. <accessor source="#Cube_000-mesh-positions-array" count="911" stride="3"> <-- This tells that there are 911 vertex positional vectors and that each vector is comprised of 3 floats. Similar for normals and textures.

Indices are defined in the Polylist node - similar to face elements in .obj, just that Collada has all of this information specified in one single line. Prior to reading indices specified in <p> node make sure to take a look at input nodes to see how many indexes comprise one vertex:

 <polylist material="Material-material" count="1822">
          <input semantic="VERTEX" source="#Cube_000-mesh-vertices" offset="0"/>
          <input semantic="NORMAL" source="#Cube_000-mesh-normals" offset="1"/>
          <input semantic="TEXCOORD" source="#Cube_000-mesh-map-0" offset="2" set="0"/>
          <vcount> 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 ... etc
          <p> 8 0 0 19 0 1 2 0 2 75 1 3 79 1 4 56 1 5 77 2 6 59 2 7 80 2 8 14 3 9 13 3 10 16 3 11
</polylist>

In my example, indices follow vertex_position vertex_normal and vertex_texcoord paradigm. For example, 8 0 0 defines that that face is comprised of the vertex position at index 8 (+1 +2 ?), vertex normal at index 0 (+1 +2 ?) and texture coordinate at index 0 (+1 ?) in corresponding arrays.

SKINNING DATA (joints and weights that affect each vertex) is found in the controllers library.

An example of joints section would be the following:

<controller id="Armature_tobby-skin" name="Armature">
      <skin source="#Cube_000-mesh">
        <bind_shape_matrix>0.9999999 0 0 0 0 1 -7.45058e-9 1.11759e-8 0 -7.45058e-9 1 -4.76837e-7 0 0 0 1</bind_shape_matrix>
        <source id="Armature_tobby-skin-joints">
          <Name_array id="Armature_tobby-skin-joints-array" count="29">torso chest neck head Upper_Arm_L Lower_Arm_L Hand_L Fingers01_L Fingers02_L Thumb01_L Thumb02_L Upper_Arm_R Lower_Arm_R Hand_R Fingers01_R Fingers02_R Thumb01_R Thumb02_R Upper_Leg_L Lower_Leg_L Upper_Leg_R Lower_Leg_R Lower_Leg_L_001 Lower_Leg_L_002 Toes_Control_L Lower_Leg_R_001 Lower_Leg_R_002 Toes_Control_R Hat</Name_array>
          <technique_common>
            <accessor source="#Armature_tobby-skin-joints-array" count="29" stride="1">
              <param name="JOINT" type="name"/>
            </accessor>
          </technique_common>
        </source>
<source id="Armature_tobby-skin-bind_poses">
          <float_array id="Armature_tobby-skin-bind_poses-array" count="464">0.3604596 0 0 0.008701086 3.03334e-7 0.01854109 0.3599773 -2.092182 0 -0.3599824 0.01854079 -0.09485429 ... etc</float_array>
          <technique_common>
            <accessor source="#Armature_tobby-skin-bind_poses-array" count="29" stride="16">
              <param name="TRANSFORM" type="float4x4"/>
            </accessor>
          </technique_common>
        </source>

The line <Name_array id="Armature_tobby-skin-joints-array" count="29"> ... is important, since it tells us the index of all of the joints in the model. torso joint is index 0, chest joint is index 1, neck joint is index 2 and so on. To confirm that this information refers to the list of joints, take a look at the joints node (xml section below). The line <input semantic="JOINT" source="#Armature_tobby-skin-joints"/> tells that joints can be found at source specified by #Armature_tobby-skin-joints String, which is the same as the source of the node where the names of the joints are listed.

Furthermore, all of the inverse bind transforms of joints can also be found (<input semantic="INV_BIND_MATRIX" source="#Armature_tobby-skin-bind_poses"/>). Therefore, inverse bind transforms can be loaded directly from the file, rather than calculating them in the program. These transforms are 4x4 matrices and are stored row by row. So the inverse bind matrix for the torso joint (which is the first joint specified) can be obtained by reading the first 16 values of the bind poses array (4 rows, each of 4 elements = 16 elements in total which comprise the inverse bind matrix).

An example of weights section would be the following:

<source id="Armature_tobby-skin-weights">
    <float_array id="Armature_tobby-skin-weights-array" count="1917">0.2986837 0.3652646 0.008785247 0.3272664 0.5671062 0.1636662 0.2692276 ... etc
    <technique_common>
        <accessor source="#Armature_tobby-skin-weights-array" count="1917" stride="1">
            <param name="WEIGHT" type="float"/>
        </accessor>
    </technique_common>
</source>
     <joints>
         <input semantic="JOINT" source="#Armature_tobby-skin-joints"/>
         <input semantic="INV_BIND_MATRIX" source="#Armature_tobby-skin-bind_poses"/>
     </joints>
     <vertex_weights count="911">
         <input semantic="JOINT" source="#Armature_tobby-skin-joints" offset="0"/>
         <input semantic="WEIGHT" source="#Armature_tobby-skin-weights" offset="1"/>
         <vcount>4 3 3 4 4 4 4 4 3 3 5 4 3 2 2 2 2 2 5 5 3 4 4 3 4 4 5 5 2 4 4 2 2 4 ... etc
         <v>12 0 13 1 14 2 16 3 12 4 13 5 16 6 12 7 13 8 14 9 12 10 13 11 14 12 ... etc
    </vertex_weights>

At the beginning (<float_array id="Armature_tobby-skin-weights-array" count="1917">0.2986837 0.3652646 0.008785247 0.3272664 0.5671062 0.1636662 0.2692276 ... etc) the weight values of the model are specified. When parsing weights, reading this data (and storing weight values as floats) would be the first step. The majority of the important stuff regarding weights is found in the vertex_weights node. The count value referes to the number of vertices in the model. The above input information tells us that the data in node is in the format joint weight, like so:

collada jw pair

vcount data tells us how many joints affect each vertex (there should be x values here, where x is equal to the number of vertices a model has). 4 at the beginning of vcount tells us that the first vertex is affected by 4 joints: 12 0, 13 1,14 2 and 16 3. Do note that rather than going at the start of the v line again, these three joints are taken from the end of where the line has been read by the previous vertex-joint relations from vcount. For example, the second vertex is affected by three joints: 12 4, 13 5 and 16 6, RATHER THAN 12 0, 13 1 and 14 2. One more note: these integers in vcount and v do not refer to the actual weight values. They are indexes which have to be used to get the actual weight value from the array of weights.

colllada weight

To find out about the hierarchy of the joints, we have to navigate to the visual_scenes library. There, we can find the root joint in the first node section (type="JOINT" confirms that it is a joint). The id gives us the name of this joint --> we can get the index of this joint by finding its position in the list of joints from controllers section. Each specified joint has indented nodes of children joints (or none if there are no children joints) and it's original bone-space transform.

ANIMATION DATA is in the animations library. This contains the animation data for each of the joints of a model. This looks like:

animlib

If you open one of the animation nodes, you are greeted by the following:

torsoajk

Animation data for each joint comprises of input and output, specified in sampler node:

<sampler id="Armature_torso_pose_matrix-sampler">
        <input semantic="INPUT" source="#Armature_torso_pose_matrix-input"/>
        <input semantic="OUTPUT" source="#Armature_torso_pose_matrix-output"/>
        <input semantic="INTERPOLATION" source="#Armature_torso_pose_matrix-interpolation"/>
</sampler>

From the source of input, we can obtain the list of times for each of the keyframes of this joint:

<source id="Armature_torso_pose_matrix-input">
        <float_array id="Armature_torso_pose_matrix-input-array" count="11">0.04166662 0.125 0.2083333 0.375 0.5416667 0.7083333 ... 

The output is the list of bone-space transforms at each of those keyframes:

<source id="Armature_torso_pose_matrix-output">
        <float_array id="Armature_torso_pose_matrix-output-array" count="176">0.9550999 0.04036125 -0.2935219 0.01349609 0.2885617 ...

For example, at time 0.04166662 the jont will have this 0.9550999 0.04036125 -0.2935219 0.01349609 0.2885617 ... up to element 16 transform.

The target attribute in the channel node tells us the name of the joint to which these keyframes refer to.

⚠️ **GitHub.com Fallback** ⚠️