Step by Step Skeletal Animation in C++ and OpenGL, Using COLLADA
Last update December 2010
Level: Beginner to Intermediate
Introduction: Hi I am waZim and welcome to my first tutorial(s) on Skeletal Animation. This series of tutorials consists of two parts.
1) Reading and Understanding COLLADA. (high level overview of COLLADA documents)
2) Actual Implementations in C++ using OpenGL, of what we learn in Part 1.
These two parts are further broken down into sub categories or parts which will be explained as we progress through the tutorials.
Who should read these tutorials?
This tutorial is written for those programmers (Not artists) who want to implement Skeletal Animations in General and Skeletal Animations using COLLADA in Specific, Using C++ and OpenGL, in their (Game) Engines. It is also helpful to programmers who want to learn how to load static 3D COLLADA models in their engines;
What we will and will not cover?
In this set of tutorials I will try to explain step by step how to read Geometry, Skeleton and Animation Data from COLLADA documents and how to implement and animate your characters using skeletal animations, in C++ and OpenGL. We will not be covering EVERY aspect of COLLADA; rather we will take some assumptions which will help us to understand the COLLADA document format easily.
We will also not discuss things like shaders and FX using COLLADA.
What you need to know, to get the Most out of these tutorials?
Any-one who wants to get the most benefit out of these tutorials should have solid knowledge of programming in general and C++ programming in particular. Advanced OpenGL programming is not required although it is a plus. You should know what a vector is and what a texture coordinate is, how geometry and animation is represented, what an XML format is and so on. For the rest, I hope you will learn everything you need here.
What you need to have, to learn and read through these tutorials?
You need a C++ compiler with OpenGL Support and working drivers for OpenGL.
Some COLLADA example documents. Which you can find on www.COLLADA.org , find model bank, or in downloads section of this website (www.wazim.com).
A good Text Editor like Notepad++
Background and Motivation:
Over the years when I first started graphics programming, I was already tired of writing geometry by hand and drawing everything vertex by vertex :). Then the time came when I saw some simple demos over the internet, reading Geometry data from ASCII files in OpenGL, like .OBJ file format etc.. After some time, I wanted to animate the models so I found MD2 Demos. When I started creating my own MD2 files, it was really hard to find a robust importer for MD2 files for my own engine. In the end, I decided to forget about all those formats and read Autodesk Maya (.ma) or Autodesk Studio Max (.max) files. But reading Geometry and animation data from those two files are not recommended by the authors of those two Authoring softwares. I don't know much about Maya .mb or .ma files but I love Studio Max and .max files and yet they are unfortunately poorly documented. And even if you start to write an exporter for any of these files it's really hard to port your models from Maya to Max or vice versa. There is always something missing when you import those files into other software.
The solution that I found was COLLADA. COLLADA documents are cross platform because they are stored in UTF-8 format using XML schema. I think you already know something about COLLADA that is why you are here! So let's not waste any time and jump straight to the implementation. If you want to read more about COLLADA documents then go visit (www.COLLADA.org) and keep reading the COLLADA wiki and COLLADA forums.
So after I have found COLLADA the solution to my problems I have been working with COLLADA. There are many implementations of COLLADA which are too generic, for example the first thing I saw was the COLLADA DOM which is available on COLLADA.org. And you can write your own code on top of DOM to extract all the information from COLLADA document which is loaded in DOM and export it to your Custom Format. This is the easy way to go but still you need to understand the COLLADA format and then DOM has its own problems. You might find it very hard to get what you really need and what you don't need. What I found annoying with DOM was that it can read Texture Coordinates with (s,t) two components while can't read (s,t,p) type of Texture coordinates which are default to Studio Max COLLADA Exporter. So at the end I decided to write my own exporter and it's really not that hard as it seems.
When I started reading about COLLADA I was unable to find a step by step tutorial, so the purpose of this tutorial is to give you the COLLADA document format in easy to learn chunks and C++ code to implement, with step by step explanation.
Reading and Understanding COLLADA documents
As we have discussed in the introduction part, this tutorial is split in to two parts, first one general introduction to COLLADA documents which is irrespective of any programming language. So if you want to skip this part and directly jump to the implementation part, it is quite possible that you will find it hard to understand what is going on. So it's highly recommended, for those who know nothing or very little about COLLADA documents, to read this part before moving to the next part.
Enough Ranting :), let's start working.
Before we dig into COLLADA documents I would like you to download the example file which we will be using through out the tutorial. It's available in the COLLADA Model Bank and is also shipped with COLLADA DOM. It's called "astroBoy_walk.dae" if you can't find this file anywhere then its available in downloads section of this tutorial's website, and you can find it there.
Like we discussed earlier (in case you read the intro part) COLLADA documents are stored in XML format. If you open astroBoy_walk.dae, which I will call example file from now on, in your favorite text editor, you can see a root node called "COLLADA" and if your text editor supports expanding or collapsing XML nodes (if you are using Notepad++ you can set the language type to XML from the "language" menu to get XML Editing enabled) then you can click on the (-) sign in front of <COLLADA> tag to Collapse it to one line, there you will see that the whole file is Collapsed to one line and thatís all the COLLADA document is about.
Figure 1: COLLADA document in short
Within this <COLLADA> node which is the root of this .dae or .xml file, you will find many other libraries which are used for storing different types of information like <library_geometries> is used to store geometry data and <library_lights> is used to store Lights in the scene. Refer to figure 1. See it's not rocket science :) and then within those libraries you will find the actual data. For example a geometry library will have <geometry> nodes and a lights library can have <light> nodes, which means those libraries are used to store more then one lights or geometries in your scene. Now let's analyze all of those libraries one by one by increasing importance to the tutorial and our COLLADA exporter, in their corresponding sections.
First of all let's make things easier for us. Just like I said in the beginning we will not be discussing COLLADA documents from all aspects; rather we will be taking some assumptions, to remove complexities.
List of Assumptions:
1) Although COLLADA documents exported from Max or Maya Should be the same but in some cases they are different. We will only talk about COLLADA document exported from Studio Max, which of course does not mean that this tutorial might not help those who work in Maya. Because I am still positive that COLLADA documents exported from Maya should be the same, if we export them with backed matrices and triangulate options checked, from the COLLADA Exporter Options dialog. But I have never worked with Maya and don't know where my exporter might fail.
2) The COLLADA document must have at least and at most a mesh, which means, anything in the asset's Max file, should be attached. So we must not have more then one <mesh> in the <library_geometries> in the COLLADA document. If we are able to read one <mesh> then we can read a 10000 too.
3) Geometry in COLLADA should be triangulated, since thatís the better (If not best) option, we can provide OpenGL, so we let the triangulation work done by Max.
4) Later in the implementations part we will assume our Model which was exported to COLLADA document has only One Texture file.
5) Animations in COLLADA must have at least or at most one Skeleton, with only one Root Bone (Typical). And I think thatís why we are here, to implement skeletal animation.
6) Animation exported to COLLADA must be baked in matrices, which essentially in some cases makes 1 channel of animation and in others 16 channels of animation (Now what is channel? It should be explained later).
7) Animations can only be valid if the channel targets the "Transform" of the targeted entity, just to keep things clear and easy. When you will bake matrices, then you will have this automatically, so don't need to worry about that.
8) Animations can't have nested animations.
9) Only Skeletal Animation is supported (No baked animation yet).
10) Every bone in the hierarchy must be attached as effecter on the skin. In other words, it must be added to the skin.
Keeping those assumptions in mind all the times, let's start explaining different libraries in their related sections. You might also find it easy and handy to switch to Implementation part for each section immediately, when you read that section, before you finish reading the first part completely. The link to the implementation of each section is given at the end of that section.
Reading Geometry Data from COLLADA document
This is by far the most important library of all of the COLLADA libraries; if you have a character to animate we need its geometric data, which is provided in <library_geometries>.
This library contains <geometry> type nodes which contain separate geometries in the scene, keep in mind COLLADA is not just an asset file format rather you can put a complete scene and many scenes in them. Like we assumed we are only concerned with one <geometry> node which will have one <mesh> node. So if so far you have found that node lets analyze it.
Mesh is the node where we will find our geometry data. If you try to analyze the node you will see further at least 1 or 2 <source> nodes, which, depending on its type, gives information about Vertices, Normals and Texture Coordinates etc. In the example file (if you have downloaded from COLLADA.org, it will not have backed animation in it, so you have to import it in Max and then export it back with Backed Matrices and "triangulate" checkbox checked as shown in figure 2), you will find 3 <source> nodes and we are lucky that each source node is defined in the same way in COLLADA.
Figure 2: Export Options from COLLADAMax
Remember all these XML nodes discussed so far have an ID associated with them which is used for locating the Node in COLLADA document when referred from any place. And Source Nodes are not any exception to that. Now <source> can have many children nodes but the most important ones are <float_array> or <NAME_array> and <technique_common>.
As the names suggests <float_array> contains floats which can be used for different purposes which are described by <technique_common> of that source and the difference between <float_array> and <NAME_array> is that, the former contains floats and the later contains strings.
Now we use the <technique_common>'s <accessor> child node to specify what kind of data is in those arrays <float_array>, <NAME_array> or any other type of many types of arrays. <accessor> node has a "source" attribute which says "What kind of "array" are we talking about?". And a "count" attribute says how many elements we have in the "array". The "stride" attribute says after how many values the next element starts.
I hope I am not talking Chinese but let's explain it with a figure and example COLLADA source.
<source id="vertices_source" name="Vertices">
<float_array id="values" count="6"> 0.3 0.5 0.7 0.2 0.4 0.6 </float_array>
<accessor source="#values" count="2" stride="3">
<param name="X" type="float"/>
<param name="Y" type="float"/>
<param name="Z" type="float"/>
Figure 3: Structure of a source
As you can see in figure 3, there are float array's (count="6") values which are 3 (x, y, z) float components of <accessor>'s (count="2") number of vertices. And when we have 3 <param> child nodes in <accessor> then we have 3 (x, y, z) components in each Vertex (3D vertex) (it can also be 3 Components of Normal, or 3 Components of Texture Coordinates). This information is very important since I can't find it in COLLADA Specifications and it took me a lot of time to understand this concept (may be I am dumb) :( So if you don't understand, please read again.
In short, this source says, "I have 2 vertices with 3 components each, which are saved in <floats_array> as 6 float values". Components are called "X", "Y" and "Z". And they are float type values. If we had a <source> which saves texture coordinates, then those components would be "S", "T" and "P".
So thatís all a source is about. Now as we discussed before there are 3 <source> nodes in the example COLLADA document. And as you might have guessed already, the other two sources are for Normals and Texture Coordinates, if you have exported your Model with other attributes then you will have more <source> nodes, like bitangents and tangets etc.
Now when we are able to decode <source>s, we still can't just decide on the order of those sources which one is Vertices and which one is Normals etc. we have to read one other child of <mesh> which is called <vertices> to find the vertices source, although I really don't understand the reason why they do this in COLLADA but for the sake of completeness you have to read this Node it has at least one Child node called <input> with a semantic attribute of "POSITIONS" value, which references the vertices <source> with another source name/id. And then you refer to this ID when ever you need the vertices source. If you don't understand these sections then skip to the next section and you hopefully will understand.
Now as we have assumed we are only considering COLLADA documents triangulated so you will only see <triangles> types nodes as children of <mesh> otherwise you might see <polylist> etc nodes, which we are least concerned with.
This <triangles> node tells all the information we need to make a triangle out of those 3 sources (in this case) which we read earlier. A <triangles> node says how many triangles we have in this <triangles> node with a "count" attribute and also lists "material" attribute which is used to find the material from the <library_material> and that material is used to render the list of triangles from this <triangles> node. So you might see many triangle groups in one Mesh, which are separated by Materials. So we have to read all the <triangles> nodes.
To decode <triangles> nodes we have to read its children in which <input> and <p> are the most important. The number of <input> nodes says the number of attributes per vertex. And <p> has the indices of those attributes in their corresponding <source> nodes. Let's see an example.
<input semantic="POSITION" source="#position"/>
<triangles count="2" material="Bricks">
<input semantic="VERTEX" source="#verts" offset="0"/>
<input semantic="NORMAL" source="#normal" offset="1"/>
<input semantic="TEXCOORD" source="#textureCoords" offset="2" set="1" />
0 0 1 3 2 1
0 0 2 1 3 2
Figure 4: Structure of a Triangle
As you can see from the above example <vertices> node is renaming the "position" source with "verts" and then defining the triangles vertices source with "verts" name. So this is why we need to read <vertices> node to find the real position <source> from the <source>s.
If you read the Children of <triangles> node you can see that it has 3 <input> nodes with values of "VERTEX" "NORMAL" and "TEXCOORD" for its semantic attribute. This essentially means that our triangles have 3 attributes per vertex, first Vertex Position, second Vertex Normal and third Vertex Texture Coordinate.
And how we know which one is first in the list of indices in <p>? We can see that
<input> node with semantic = "VERTEX" has offset = "0",
<input> node with semantic = "NORMAL" has offset = "1" and
<input> node with semantic = "TEXCOORD" has offset = "2".
So when ever we will read values from <p> for each triangle's each vertex,
The first one will be the Index of "VERTEX" Position from the "positions" <source>,
The second one will be the index of the "NORMAL" from the "normal" <source> and
The third one is the index of "TEXCOORD" from the "textureCoords" <source>.
Now one thing I would like to make clear here is, that all these values we read from <p> are "indices" not actual values. And all the data for all the triangles are saved as indexed data to save space in case of repetitions of attributes data. To find the real data we have to refer to the corresponding <source>s and pick the corresponding data at that particular index.
Making Triangles is very easy now. All you have to do is keep on reading 3 * (Number of Input Nodes in <triangles>) values from <p> and read the corresponding attribute values from the corresponding <source>s. If we have only one attribute per vertex for any number of triangles then we will have the following <triangles> Node, with only one <input> child. In this case all you have to do to read the triangle is to keep on reading 3 values from <p> and read the corresponding vertex values from the corresponding "verts" source.
<triangles count="2" material="Bricks">
<input semantic="VERTEX" source="#verts" offset="0"/>
0 3 2
0 2 1
One thing we still need to know is the "material" attribute of a <triangles> node. This attribute references the material used from the "library_materials> which will we discuss later in the tutorial.
Thatís all for the geometry data. If you understand this part correctly I hope you will have no problems going on from here onwards. Other wise go back and read again until you understand completely. Now if you directly want to jump to the implementation part (part 2) of the tutorial, you should be able to read and display static 3D objects in your engine. And if you want to render them with Materials and texture maps as well as animate them, you will have to keep on reading this part of the tutorial completely.
Click here to go to: Implementation section in part 2, for this section of part 1
Reading Texture filename from COLLADA document
As you know we took some assumptions in the beginning, one of which was only one texture file can be used in the COLLADA document. This will make finding the file name very easy.
All we have to do is to read the <library_images> and read the "id" attribute of the "Only" <image> node in it. Usually that will be the file name of the texture file used in COLLADA. But it might not be correct file name, and COLLADA might create that ID different then the file name. So, to correctly read the file name we must read the <init_from> child node of <image> node, which gives the whole path, with file name. For our export purposes I am only interested in the file name, not the whole path, so I will tokenize the full path and save the file name only.
Click here to go to: Implementation section in part 2, for this section of par 1
Reading Materials from COLLADA document
We discussed in the section "Reading Geometry Data from COLLADA document" that each triangle group is separated by a "material" and the Material ID is the value of the "material" attribute in <triangles> node. Now to find those materials with those IDs we have to read <library_materials>. In <library_materials> you will find <material> nodes with those IDs which were referenced from <triangles> nodes. But unfortunately those <material> nodes have only one type of child named <instance_effect> which has one attributes called "url". All what its saying is that this specific <material> node is referring to an effect from the <library_effects>, which in turn defines the material completely.
So we take the value of this "url" attribute for this specific <material> and find it in <library_effects>, now unfortunately this library <library_effects> is the most complicated library of COLLADA as much as I know. It can get very complicated when shaders and what not is included in the COLLADA document. But since I promised to keep things clear and easy, we will only read the data that we desperately need for defining a material.
Once we have found the <effect> node with id of the value of the "url" attribute for any material, we have to find either <phong> or <blin> node in <profile_COMMON> child node of the <effects> node. <phong> or <blin> are usually inside <technique> child node of <profile_COMMON>. Once we have found either <phong> or <blin> keep looking for all the parameters of the Material we are looking for, like "ambient" "diffuse" "specular" "emission" "shininess" and "transparency" etc. what ever you need for the material to look good. Usually "diffuse" "shininess" and "transparency" is enough to create a good looking material.
How can we read the data from those nodes is very easy? Usually ambient, emission, diffuse and specular nodes has 4 float values, inside a <color> child node, which corresponds to "RGBA" components of that particular material's property, while reflectivity and transparency etc have 1 float value.
<color>1.0 1.0 1.0 1.0</color>
If we have placed a texture map on the diffuse color of the material then <diffuse> will not have a <color> child, rather that texture image. But for the sake of ease we will not worry about this and assume the texture map is always applied on the <diffuse> component of the material, which means we will not be reading <diffuse> values from COLLADA. But we will be using a default value for diffuse inside our OpenGL implementation.
That's all what we need for any static geometry. So if you are only interested in reading Static geometry from COLLADA you can stop reading this part and jump to the implementation. Other wise keep on reading for extracting animation data from COLLADA documents.
Click here to go to: Implementation section in part 2, for this section of part 1
Reading Skeleton from COLLADA document
We assumed that we will only read COLLADA documents with skeletal animations, and not those with backed animations, so we have to read the skeleton from the COLLADA document. By skeleton, I mean reading the Joints (Bones) information. We also have to read the hierarchy of the Joints. This will tell us, who are the child of whom! And who is the parent of a joint etc. In the following figure all these terminologies are explained. Remember that Bones and Joints are One and the same thing, they are just convenience names, and the data that we read from COLLADA is actually a Joint, a Bone is an imaginary line connecting two Joints.
Figure 5: Skeleton Terminologies
In the following figure you can see the skeleton of our example file and the Skin attached to it.
Figure 6: Complete Character in one pose of animation
The red circles on the left in figure 5 are the joints we read from COLLADA and the lines connecting those circles are the imaginary bones, which are used to animate the skin. On the right you can see the skin attached to the skeleton in another frame.
You might remember we took some assumptions, one of which was that, all the joints are added to the skin, which will make your <library_visual_scenes> very simple to read. All you have to do is find the root Joint (bone) <node> of the skeleton in the visual scene and then read the whole tree of joints. One of the disadvantages with this is that you will have a lot of joints considered affecting the skin, but in real they will not have any effect on the skin. And if you don't add all the bones to the skin, then you will see <node>s of type = "JOINT" and type ="NODE" mixed in the hierarchy. But if you add every bone to skin you will have full tree of type="JOINT"s only. This is also the default behavior for many engine exporters. When you have <node>s with type="JOINT" and type="NODE" mixed in the hierarchy then you have to read <instance_controller> from <library_visual_scenes> and then read <skeleton> each time you have to read a joint. Those <node>s which are not of type="JOINT" are still Joints but they are not effectors, which means they are not effecting the skin. And thatís why we assumed everything must be attached to the skin, to keep things easy and simple.
To read the hierarchy of the bones, you need to have a data structure which can hold a number of children and a parent of its own kind. (This will get clear in the implementation part). You might also need to save the "SID" attribute of <node>s. Once we have the data structure setup, we will find the root and start reading its children and then their children and so on (recursively) and keep saving them in the data structure. At the end of the whole read process your whole data structure should be able to answer questions like, which joint is the child of whom? And who is the parent of a joint.
Now how can we find the root joint of the skeleton? Since we know we only have one model in the COLLADA document so we don't have to read the <scene> node to find where the scene is instanced from. We directly go to <library_visual_scenes> and for all the direct (immediate) children of the only <visual_scene> we have to find the <node> who has a children of type <instance_controller>, read the <skeleton> child of this <instance_controller> and that will give you the ID of the root node. Since we have added all the bones to the skin, we will only have one <skeleton> child of <instance_controller>. Now this node is our root of the skeleton and everything connected to this <node> is part of the skeleton.
If you see the structure of this <node> in COLLADA document you will see most of the nodes have <matrix> as the first child. And this <matrix> contains 16 float values, which makes the Joint Matrix of the bone. This is also called the local bone transformation matrix. When we connect all the joints we have to multiply the World Matrix of the parent to the child's Joint Matrix and save it as world bone transformation matrix for the child. For the root Joint, who doesn't have a parent, the Joint Matrix becomes the World transformation matrix.
By now you should be able to read the skeleton and derive the bind pose of the skeleton from the Joint matrices you read for each <node>. In the next section we read the skinning information which connects this skeleton with the skin.
Click here to go to: Implementation section in part 2, for this section of part 1
Reading Skinning information from COLLADA document
So far we have read the geometry (vertices attributes information, materials, texture filename) as well as skeleton of the model. What we need to know now is how this skeleton is connected to the skin (Geometry). We have read many Joints in the skeleton. But we still don't know which Joint influence which vertex. Some of the joints might not even influence any vertex at all. But if you remember we took an assumption that all the Joints must be added to the skin. In that case every joint must be influencing the skin theoretically.
To properly connect the skin (geometry) to the skeleton we need skinning information, and this section will try to help you understand, where we can find skinning information in COLLADA?
There is one other thing I would like to explain before we go further. If we have a character who's each vertex is connected to only one Joint. When ever that Joint moves the vertex connected to it must also move. And you will see very rigid animation. But this is not how things work. Almost every vertex is connected to more then one Joint. And we specify the influence of each joint on that vertex with the help of vertex weights. Each joint influences that particular vertex some percentage of the total influence which totals to 1. So vertex weights are very important part of the skinning information.
<library_controllers> contains all the influences (joints) and their connectivity through vertex weights for the whole model. According to our assumptions we only have one mesh and one skeleton. So we will have only one <controller> node in the list of children of <library_controllers>. Once we have found the one and only <controller> node, we have to find its child node <skin>. In the <skin> node, find the <source> whose attribute's "name"s value is "JOINT" in the <param> child node of the <accessor> child of the <technique_common>, (I will not explain all this again since we have already decoded <source> nodes when we were reading Geometry data) and the <NAME_array> will give you the Names of all the joints in the skeleton. Now you know that you can find the number of bones used from the "count" attribute of the <NAME_array> node in this source. An example <source> is given as follows.
<NAME_array id="skin-joints-array" count="5">Bone1 Bone2 Bone3 Bone4 Bone5<NAME_array />
<acessor source="#skin-joints-array" count="5" stride="1">
<param name="JOINT" type="Name" />
And if you go back and see your skeleton <node>s from <library_visual_scene>, you will see that all these names of Joints you read from <NAME_array> are actually the SID's of these <node>s.
To properly read the skinning data, we first need to read the Bind shape matrix of the skin to the skeleton, which is usually the first child of <skin> node, if it's not the first child we will iterate through all the children and find and save it. And then we start reading from the node called <vertex_weights> who's "count" attribute's value gives the number of weights, as far as I know this count must be equal to number of vertices in the model which we read earlier when we were reading Geometry data because we have to define the vertex weights for each and every vertex at least and at most once.
If you see the structure of the <vertex_weights> node you will see at least 2 <input> nodes, one with attribute semantic="JOINT" and second with semantic="WEIGHT", One <vcount> node and one <v> node.
When we have to read the weights for each vertex we iterate <vertex_weight>'s attribute "count" number of times into the <vcount> node values. And each value from the <vcount> is the number of Joints affecting that particular vertex, on which we are currently iterating. So we iterate nested for that specific value in the <vcount> (number of joints time) and read pairs of indices from <v> (Here I assume we have only two <input> nodes in <vertex_weight>).
The first one index the "JOINT" name in the "JOINT" <source>'s <NAME_array> (Here I assume that the value of "offset" attribute of the <input> who's semantic="JOINT" is "0"), we mentioned how to find this source earlier as well, but here you can get the ID of this source from the "source" attribute of the <input> child of the <vertex_weight> node with semantic="JOINT".
And the second one index the weight value from the <source> who's ID you can get from the "source" attribute of the <input> child of the <vertex_weight> node with semantic="WEIGHT" (Here I assume that the value of "offset" attribute of the <input> who's semantic="WEIGHT" is "1").
<input semantic="JOINT" source="#joints" offset="0"/>
<input semantic="WEIGHT" source="#weights" offset="1"/>
<vcount>3 2 2 3</vcount>
1 0 0 1 1 2
1 3 1 4
1 3 2 4
1 0 3 1 2 2
In this example you can see that this <vertex_weight> node is defining weights (influences) for 4 vertices, first vertex has 3 influences, First vertex's first influence's joint index is 1 which is the index from the JOINTS <source>s <NAME_array> values. And its weight index in the <float_array> of the <source> of weights is 0.
There is one other very important child of <skin> node which is called <joints> and it usually have two <input> nodes, the first one with attribute semantic="JOINT" references the <source> node with Joint names, through the "source" attribute. And the second <input> with semantic="INV_BIND_MATRIX" references the source with inverse bind matrices for each Joint through the attribute "source". The source with inverse bind matrices contains (Joints_count * 16) values which makes Joints_count inverse bind matrices. These inverse bind matrices are needed for skinning. And will be clear when we read the implementation part.
Once we have completely read the <controller> node we should have one Bind shape matrix, a number of Joints and their Inverse bind matrices, and we have read their Joint matrices from the <visual_scene> earlier. Each vertex must be influenced by one or more then one bone (Remember this is contrary to Each Joint must be influencing at least one or more Vertex, which is not true, since their might be Joints, influencing no vertices). And we must have their weights accordingly.
Now if you have come this far, you should be able to read the geometry data, as well as the skeleton and skinning data from COLLADA documents. And you should be able to draw the model in raw triangles, as well as draw the skeleton. Although I haven't discussed how you can accumulate the world matrices for each joint and then draw in world coordinates for debugging purposes but I think I gave a hint that we have to multiply parent joint's world matrix with current joint's Joint matrix and save the result in current joint's world matrix. We have to start this process from the root bone. So that we don't have dirty world matrices from parents, and the root Joint's world matrix becomes the Joint matrix, since root don't have any parent. If you are also reading the COLLADA specification version 1.5 you can find the skinning equation so you should also be able to put the model in bind shape. How can we animate this model is still not covered and will be covered in the following sections.
Click here to go to: Implementation section in part 2, for this section of part 1
Reading Animation data from COLLADA document
So far we are able to read every thing related to the static pose of the character, the only thing we still have to understand and read is the animation data part. Animation is not very strong in COLLADA and is still in its infancy, as time passes and COLLADA gets mature this will get better. But for our purposes we have a lot to worry about :).
In this library we have all the animations data saved. For each joint animated you will see an <animation> node which further have the animation data for that specific Joint. Remember that an <animation> channel replaces the transform of the target on which it applies, which in this case will be joints.
You will see three types of children nodes in <animation>, first one as usual will be <source>s of data, second one is called <sampler> and the third one is <channel>. You need <sampler> and <channel> nodes to define the target on which the animation data is applied.
From <channel> node you pick the target which gives you the ID of the Object on which the Animation data will be applied. And you also get the Sampler ID from where you will pick the sources from which you will pick the animation Data.
Remember the example I am presenting here is a case which will never occur in our example COLLADA documents, because of our assumption of backing matrices. But this is an easy example to understand.
<float_array id="astroBoy-input-array" count="2">0 1.16667</float_array>
<accessor source="#astroBoy-input-array" count="2" stride="1">
<param name="TIME" type="float"/>
<float_array id="astroBoy-output-array" count="2">2.2 3.5</float_array>
<accessor source="#astroBoy-output-array" count="2" stride="1">
<param name="TRANSFORM" type="float"/>
<NAME_array id="astroBoy-interpolations-array" count="2">LINEAR LINEAR</NAME_array>
< accessor source="#astroBoy-interpolations-array" count="2" stride="1">
<param name="INTERPOLATION" type="float"/>
<input semantic="INPUT" source="#astroBoy-input"/>
<input semantic="OUTPUT" source="#astroBoy-output"/>
<input semantic="INTERPOLATION" source="#astroBoy-interpolations"/>
<channel source="#astroSkeleton-sampler" target="astroBoy_Skeleton/trans.X"/>
Now Lets read from the Bottom <channel> node.
This says that there is one Entity (in our case will be Joint) called "astroBoy_Skeleton" in the scene who's "X translation values" is being animated by the sampler "astroSkeleton-sampler".
So we need to know how "astroSkeleton-sampler" animated that "X translation value", we read <sampler> which says.
There are three types of inputs you must read to read the animation data.
First <intput> node is: INPUT
Second <intput> node is: OUTPUT
Third <intput> node is: INTERPOLATION
When we start reading the <input>s from the <sampler>,
The "input" with attribute semantic = "INPUT" gives you the input <source> for the animation
The "input" with attribute semantic = "OUTPUT" gives you the output <source> for the animation
The "input" with attribute semantic = "INTERPOLATON" gives you the interpolation <source> for the animation
Now when we go and read those <source>s, we see that the source who was referred to semantic = "INPUT" by the sampler has name="TIME" in <param> child of <accessor> child of the <technique_common> child of that <source>, in short it says this sources has time values of the animation in floats.
The source who was referred to semantic = "OUTPUT" by the sampler has name="TRANSFORM" in <param> child of <accessor> child of the <technique_common> child of that <source>, which says this sources has "X translation" values in floats for those times which we read from the previous source.
The source who was referred to semantic = "INTERPOLATION" by the sampler has name="INTERPOLATION" in <param> child of <accessor> child of the <technique_common> child of that <source>, which says this sources has the interpolation values in Strings for those OUTPUTS which we read from the previous source. (In studio max those interpolation values are usually "LINEAR" so we will not read this source and assume they are all "LINEAR")
What this last source means is that, if you see the <source>s, you are given OUTPUT values for two values of time. What happens if we want to run the animation on a time which falls in between those two time values? Then we apply interpolation to find the middle OUTPUT value for the in-between time value. And in this case it's LINEAR interpolated.
Now if you see the TIME Source, that's actually your key frames. And the OUTPUT one is the key frames data for the "X translation" of the Entity (joint).
So you pick the corresponding OUTPUT value for any time and apply that to the "X translation factor" of that entity in your code and the entity will be animated. To calculate the in-between values for more smooth animation you use the LINEAR interpolation.
Click here to go to: Implementation section in part 2, for this section of part 1
What interpolation means?
Interpolation means calculating any in-between value between (among) two (or more) values.
Let's say we have "X" and "Y" values, to calculate the "Middle" value in between those two values we can use an interpolation factor of "0.5" which we call "T". And to find the 3-Quater value in-between "X" and "Y" we use "T = 0.75" and so on.
You can run a loop on "T" from lets say 0.0 to 1.0 have any increment i.e 0.001, 0.01, 0.05 etc and you can get "Loop" many in-between values.
Now Linear Interpolation is a simple form of Interpolations. And the formula is as follows
float Interpolate( float a_Value1, float a_Value2, float a_T)
return ((a_Value2 * a_T) + ((1 - a_T) * a_Value1));
If you see the code it says that if "a_T" is "Zero" then give me a_Value1, and if it's "One" then give me a_Value2. And if it's in-between "Zero and One" then it will give you value in-between a_Value1 and a_Value2.
Now there are other forms of Interpolations as well. Like Bezier, cubic etc. They have further complicated formulas. And they also consider more then two values to interpolate in-between. But we will only be using linear interpolations for the sake of simplicity.
Now as we discussed before. This is not the case which we will ever have in our example COLLADA documents. So let's see what we will have?
Keeping our assumptions in mind, we will only have two types of <animation> nodes. We will either have 16 * 3 = 48 <source>s, 16 <sampler>s and 16 <channel> nodes or we will have 3 <source>s, 1 <sampler> and 1 <channel> node. In the first case the "target" attribute will have "transform (X) (Y)" after the last "/" in its value and in the second case the "target" attribute will have "transform" after the last "/" in its value.
Either <channel source="#astroSkeleton-sampler" target="astroBoy_Skeleton/transform (0) (0)"/>
Or <channel source="#astroSkeleton-sampler" target="astroBoy_Skeleton/transform"/>
In the second form the values of the matrix which we backed in, are given in one <source> out of those 3 <source>s, just like the one <source> we read in reading inverse bind matrices from controller. While in the first form values of each component of the 4 x 4 matrix are given in different <source>s. And we have to combine them in one matrix when we read the data.
Now if you remember we read the Joint matrices for each joint from the <visual_scene> node. These values (which will be matrices, since we backed matrices) which we read from <animation> nodes for those joints, which are targeted through the "target" attribute of the <channel> of that <animation> will replace the Joint matrices, we read earlier from <visual_scene> for each key frame defined in the animation. And to calculate the world transformation matrix for each joint we will have to take this new Joint Matrix and multiply it with the parent Joint's world transformation matrix.
And thatís all pretty much it. If you have read the whole tutorial from start to end, I guess you should be able to write your own exporter for COLLADA documents now. And you are ready to start reading the next part if you haven't read in between the implementation sections.
List of Figures
Figure 1: COLLADA document.jpg
Figure 2: COLLADA Export.jpg
Figure 3: Source Node.jpg
Figure 4: Triangles Node.jpg
Figure 5: Bones.jpg
Figure 6: Skeleton.jpg
Skinning in COLLADA, Software Skinning, Skeletal Animation System in COLLADA, Skeletal Animation in COLLADA, COLLADA Animation Tutorial, COLLADA Skeletal Animation Tutorial, COLLADA Skeleton Animation Tutorial, Animation Data in COLLADA, Beginning COLLADA Programming, Starting COLLADA, COLLADA for Beginners, Step by Step Programming COLLADA, Reading COLLADA documents Step by Step, Exporting COLLADA documents, Importing Data from COLLADA, Importing Skeleton from COLLADA, Importing Exporting COLLADA documents in C++, COLLADA DOM, Feelings Software, COLLADA Conditioners, Working with COLLADA in C++, Simple Animation in COLLADA. COLLADA Step by Step Tutorial, Learn COLLADA in days, Skeletal Animation in OpenGL, Reading COLLADA documents in C++ Using OpenGL, Reading COLLADA documents Step by Step in OpenGL, COLLADA documents in DirectX, Character Animations in COLLADA, Step by Step Character Animations using COLLADA. Step by Step Character animation Tutorial using C++