Results 1 to 3 of 3

Thread: XSI collada and animations

  1. #1
    acme_myst
    Guest

    XSI collada and animations

    *I'm not sure if I'm posting this in the right forum. If not, could a moderator please move this to the appropriate forum?*

    I've been trying for a while to get bone animations to work from a Collada file exported with XSI, but I really couldn't get it to work for the world, so I figured maybe someone on these boards could help me.

    The Collada file is loaded in a conversion tool, which loads all the needed data from it and exports it to a binary file, which is then loaded into my game engine. I have no troubles loading static meshes, but for some reason the animation data still doesn't come out correct.

    Here's the (maybe a bit confusing) code from my tool. This *seems* to work just fine, but I might be wrong.

    Code :
    // this is called for each controller instance in the nodes
    private static void parseController(Document.InstanceController ic)
            {          
     
                foreach (Document.Controller c in d.controllers)
                {
                    if (c.id == ic.url.Fragment) // corresponding controller found
                    {
                        if (c.controller.GetType() == typeof(Document.Skin))
                        {
                            result.nodes.Add(new Node());                                     
                            addSkin(c);
                        }
                    }                
                }

    Code :
    private static void addSkin(Document.Controller contr)
            {            
                Document.Skin skin = (Document.Skin)contr.controller;
                int boneCount = -1;
                foreach (Document.Input inp in skin.joint.inputs)
                {
                    if (inp.semantic == "JOINT")
                    {
                        boneCount = ((Document.Source)inp.source).accessor.count; 
                    }
                }
     
                // find the array number the weights are stored in
                int weightArrayId = -1;            
                for (int i = 0; i < skin.vertexWeights.inputs.Count; i++)
                {
                    if (skin.vertexWeights.inputs[i].semantic == "WEIGHT")
                    {
                        weightArrayId = i;
                    }
                }
     
                // add geometry
                string geometryId = skin.source.Fragment;
                int vertexSize = result.vertices.Count;
                foreach (Document.Geometry geo in d.geometries)
                {
                    if (geo.id == geometryId) // corresponding geometry found
                    {
                        addMesh(geo.mesh);
                    }
                }
     
                // create buffer to store the links to each bone per vertex
                List<BoneLink> bonelinksBuff = new List<BoneLink>();
     
                // find the weights and links of each vertex to each bone
                int vertexNr = 0;
                for (int i = 0; i < skin.vertexWeights.v.Length; i += (int)skin.vertexWeights.vcount[vertexNr]*2)
                {
                    for (int j = 0; j < skin.vertexWeights.vcount[vertexNr]*2; j += 2)
                    {
                        BoneLink bl = new BoneLink();
                        bl.boneIdx = skin.vertexWeights.v[ i + j ];
                        bl.weight = ((Document.Array<float>)((Document.Source)skin.vertexWeights.inputs[weightArrayId].source).array).arr[ skin.vertexWeights.v[ i + j + 1] ];
     
                        bonelinksBuff.Add(bl);                    
                    }
                }
     
                // add bonelinks to geometry
                int vertexId = 0;
                for (int i = 0; i < bonelinksBuff.Count; i+= boneCount)
                {
                    for (int j = 0; j < boneCount; j++)
                    {
                        result.vertices[vertexId].boneLinks.Add(bonelinksBuff[i + j]);
                    }
                    vertexId++;
                }            
     
                // find the bone names
                List<string> boneNames = new List<string>();
     
                foreach (Document.Source s in skin.sources)
                {
                    if (s.arrayType == "IDREF_array") // contains the bone names
                    {
                        foreach (string st in ((Document.Array<string>)s.array).arr)
                        {
                            boneNames.Add(st);
                        }                    
                    }
                }
     
                // parse the nodes again to get each bone
                getBonesFromNames(boneNames, null, null);
     
                // add pivot (controller matrix) to each bone
                foreach (Document.Source s in skin.sources)
                {
                    if (s.accessor.stride == 16) // contains the matrices
                    {
                        int bonePivotNr = 0;
     
                        for (int pp = 0; pp < ((Document.Array<float>)s.array).arr.Length; pp += 16)
                        {
                            Matrix4x4 pivot = new Matrix4x4();
     
                            int j = 0;
                            for (int y = 0; y < 4; y++)
                            {
                                for (int x = 0; x < 4; x++)
                                {
                                    // also convert from row based to column based
                                    pivot.m[ (y*4) + x ] = ((Document.Array<float>)s.array).arr[pp + (x*4 + y)];
                                    j++;
                                }
                            }
     
     
                            result.boneList[bonePivotNr].pivot = pivot;
                            bonePivotNr++;
                        }
                    }
                }
     
     
     
                // now get the animation data for the bones
                foreach (Bone b in result.bones)
                {
                    getAnimationsForBone(b);
                }
     
     
                // add bonelinks to geometry
     
                Node n = result.nodes[result.nodes.Count - 1];
     
                foreach (Polygon p in n.polygons)
                {
                    foreach (Vertex v in p.vertices)
                    {
                        for (int i = 0; i < boneCount; i++)
                        {
                            v.boneLinks.Add(bonelinksBuff[v.vertexIdx - vertexSize + i]);                        
                        }
                    }
                }
     
                // convert all bone matrices to actual transformation matrices
                foreach (Bone b in result.bones)
                {
                    createTransformMatrix(b);                
                }
     
                // done, complete animated mesh added                       
     
            }
     
            }

    Code :
     
    private static void getBonesFromNames(List<string> boneNames, Document.Node node, Bone parent)
            {
                if (node == null)
                {                
                    foreach (Document.VisualScene vs in d.visualScenes)
                    {
                        // call the root nodes
                        foreach (Document.Node n in vs.nodes)
                        {
                            getBonesFromNames(boneNames, n, null);
                        }
                    }
                }
                else
                {
                    Bone newBone = null;
                    // if the node is a bone
                    if (boneNames.Contains(node.id))
                    {
                        // create a new bone object
                        newBone = new Bone();
                        newBone.name = node.id;
     
                        // create pivot point matrix
                        foreach (Document.TransformNode tn in node.transforms)
                        {
                            if (tn.GetType() == typeof(Document.Translate))
                            {
                                newBone.pivotPoint.m[0] = ((Document.Translate)tn)[0];
                                newBone.pivotPoint.m[1] = ((Document.Translate)tn)[1];
                                newBone.pivotPoint.m[2] = ((Document.Translate)tn)[2];
                            }
     
                            if (tn.GetType() == typeof(Document.Rotate))
                            {
                                if (tn[0] == 1) // x axis rotation
                                    newBone.pivotPoint.m[4] = ((Document.Rotate)tn)[3];
                                if (tn[1] == 1) // y axis rotation
                                    newBone.pivotPoint.m[5] = ((Document.Rotate)tn)[3];
                                if (tn[2] == 1) // z axis rotation
                                    newBone.pivotPoint.m[6] = ((Document.Rotate)tn)[3];  
                            }
                        }
     
                        if (parent != null)
                        {
                            // add this bone as a child of its parent
                            parent.children.Add(newBone);
                            result.boneList.Add(newBone);
                        }
                        else
                        {
                            // add this bone as the root of the animation tree
                            result.bones.Add(newBone);
                            result.boneList.Add(newBone);
                        }
                    }
     
                    // call for all the children
                    if (node.children != null)
                    {
                        foreach (Document.Node n in node.children)
                        {
                            getBonesFromNames(boneNames, n, newBone);
                        }
                    }
                }
     
            }

    Code :
    private static void getAnimationsForBone(Bone b)
            {
                // get animation data
                List<int> animIndexes = new List<int>();            
                for (int i = 0; i < d.animations.Count; i++)
                {
                    // check if this animation contains data for this bone
                    if ((d.animations[i].id.Substring(0, b.name.Length) == b.name) &&
                        (d.animations[i].id[b.name.Length] == '_'))
                    {
                        animIndexes.Add(i);                    
                    }
                }
     
                // now that we know which elements the bone matrix consist of, we can construct the matrices
     
     
                // first we determine the number of frames needed and all intervals 
                List<Frame> frames = new List<Frame>();
                foreach (int i in animIndexes)
                {
                    // call it 'a' for easy referencing
                    Document.Animation a = d.animations[i];
     
                    foreach (Document.Source s in a.sources)
                    {
                        foreach (Document.Param param in s.accessor.parameters)
                        {
                            if (param.name == "TIME")
                            {
                                foreach (float fl in ((Document.Array<float>)s.array).arr)
                                {
                                    Frame f = new Frame();
                                    f.time = fl;
     
                                    bool contains = false;
     
                                    foreach (Frame fr in frames)
                                    {
                                        if (fr.time == f.time)
                                        {
                                            contains = true;
                                            break;
                                        }
                                    }
     
                                    if (!contains)
                                    {
                                        frames.Add(f);
                                    }
                                }
     
                            }
                        }
                    }
                }
     
                foreach (Frame f in frames)
                {
                    foreach (int i in animIndexes)
                    {
                        // call it 'a' for easy referencing
                        Document.Animation a = d.animations[i];
     
                        foreach (Document.Source s in a.sources)
                        {
                            foreach (Document.Param param in s.accessor.parameters)
                            {
                                if (param.name == "TIME")
                                {
                                    // check each element on the timeline with the timings this animelement has                           
                                    int index = -1;
                                    for (int j = 0; j < ((Document.Array<float>)s.array).arr.Length; j++)
                                    {
                                        // the keyframe is equal
                                        if (((Document.Array<float>)s.array).arr[j] == f.time)
                                        {
                                            index = j;
                                            break;                                        
                                        }
                                        if (((Document.Array<float>)s.array).arr[j] > f.time)
                                        {
                                            // todo: implement interpolation
     
                                            index = j-1;
                                            break;
                                        }
                                    }
     
                                    // set the correct value in this frame's matrix                                
                                    int matrixIndex = getMatrixIndexFromAnimName(a.id);                                                             
     
                                    // find the index of the array with the output values
                                    foreach (Document.Source s2 in a.sources)
                                    {
                                        foreach (Document.Param param2 in s2.accessor.parameters)
                                        {
                                            if (param2.name == "VALUE")
                                            {
                                                f.matrix.m[matrixIndex] = ((Document.Array<float>)s2.array).arr[index];
                                            }
                                        }
                                    }                                
     
                                }
                            }
                        }
     
                    }
                }
     
                // add the frames to the bone          
                b.frames = frames;
     
                // get animations for all the childs
                foreach (Bone child in b.children)
                {
                    getAnimationsForBone(child);
                }
            }

    Code :
    private static void createTransformMatrix(Bone b)
            {
                float deg2rad = (float)Math.PI / 180.0f;
                foreach (Frame f in b.frames)
                {
     
                    float xRotAngle = f.matrix.m[0] * deg2rad;
                    float yRotAngle = f.matrix.m[1] * deg2rad;
                    float zRotAngle = f.matrix.m[2] * deg2rad;
     
                    Matrix xRot = new Matrix(new double[4, 4]{
                        { 1,0,0,0},
                        { 0, Math.Cos(xRotAngle) , -Math.Sin(xRotAngle), 0},
                        { 0, Math.Sin(xRotAngle) ,  Math.Cos(xRotAngle), 0},
                        { 0,0,0,1}
                    });
     
                    Matrix yRot = new Matrix(new double[4, 4]{
                        { Math.Cos(yRotAngle),0,Math.Sin(yRotAngle),0},
                        { 0,1,0,0},
                        { -Math.Sin(yRotAngle),0,Math.Cos(yRotAngle),0},
                        { 0,0,0,1}
                    });
     
                    Matrix zRot = new Matrix(new double[4, 4]{
                        { Math.Cos(zRotAngle), -Math.Sin(zRotAngle),0,0},
                        { Math.Sin(zRotAngle),  Math.Cos(zRotAngle),0,0},
                        { 0,0,1,0},
                        { 0,0,0,1}
                    });
     
                    Matrix translationMatrix = new Matrix(new double[4, 4]{
                                { 1 , 0 , 0 , 0 },
                                { 0 , 1 , 0 , 0 },
                                { 0 , 0 , 1 , 0 },
                                { f.matrix.m[4] , f.matrix.m[5] , f.matrix.m[6] , 1 }
                            });
     
                    // todo: check if they are multiplied in the correct order
                    Matrix transform = new Matrix(4, 4);
                    transform = ((xRot * yRot) * zRot) * translationMatrix;
     
                    int i = 0;
                    for (int y = 0; y < transform.Rows; y++)
                    {
                        for (int x = 0; x < transform.Cols; x++)
                        {                    
                            f.matrix.m[ i ] = (float)transform[x, y];
                            i++;
                        }
                    }
                }
     
                // transform pivot point matrix rotations to radians
                b.pivotPoint.m[4] = b.pivotPoint.m[4] * deg2rad;
                b.pivotPoint.m[5] = b.pivotPoint.m[5] * deg2rad;
                b.pivotPoint.m[6] = b.pivotPoint.m[6] * deg2rad;
     
     
                foreach (Bone child in b.children)
                {
                    createTransformMatrix(child);
                }
            }


    When this is all done, it gets exported, and loaded into my game engine. Here's the code for that. The CgModel class has a list of vertices which are rendered after the animation is complete. All the vertex transformations are done in the code below. Note that some datatypes might sound a bit weird, I had to convert this to pseudo-pseudo-code cause there's some stuff in here that I'm not allowed to publish.


    Code :
    void CgModel::applyAnimation()
    {	
     
    	for (int i = 0; i < bones_size; ++i)
    	{
    		updateBoneAnimation(bones[i]);
    	}
     
    	for (int i = 0; i < baseVertices_size; ++i)
    	{
    		// store in 32 bit vector to not lose data
    		VectorFx32 vec, startVec, finalVec;
    		startVec.x = baseVertices[i].vertex.x;
    		startVec.y = baseVertices[i].vertex.y;
    		startVec.z = baseVertices[i].vertex.z;
     
    		finalVec.x = 0;
    		finalVec.y = 0;
    		finalVec.z = 0;
     
    		vector<BoneLink>::iterator i_links = baseVertices[i].boneLinks.begin();
    		for ( ; i_links != baseVertices[i].boneLinks.end(); i_links++)
    		{
    			// get the matrix and vector to transform			
    			MatrixFx32_4x4 transform44 = boneList[ (*i_links).boneId ]->finalMatrix;
     
    			vec.x = startVec.x;
    			vec.y = startVec.y;
    			vec.z = startVec.z;					
     
    			// put transformation matrix in 4x3 matrix
    			MatrixFx32_4x3 transform;
    			ConvertMatrix(&transform44, &transform);			
     
    			// transform the vertex			
    		        vec = vec * transform;
     
    			// add the transformed vertex to the final position
    			finalVec.x += vec.x * (*i_links).weight;
    			finalVec.y += vec.y * (*i_links).weight;
    			finalVec.z += vec.z * (*i_links).weight;
    		}
     
    		vertices[i].vertex.x = finalVec.x;
    		vertices[i].vertex.y = finalVec.y;
    		vertices[i].vertex.z = finalVec.z;
    	}
    }

    Code :
    void CgModel::updateBoneAnimation(CgBone* curBone)
    {
    	curBone->finalMatrix = curBone->frames[ currentFrame ];
     
    	if (curBone->parent != NULL)
    		curBone->finalMatrix =  curBone->finalMatrix * curBone->parent->finalMatrix;
     
    	for (int i = 0; i < curBone->childs.size(); ++i)
    	{
    		updateBoneAnimation(curBone->childs[i]);
    	}	
    }

    Now, at this point I'm basically completely clueless about what goes wrong or where. If anybody has any suggestions, that'd be more than welcome. I understand there's quite a bit of code in this post, so if you read through all that, thanks for your troubles!

    Thomas Krak

  2. #2
    Junior Member
    Join Date
    Jul 2006
    Location
    Montrťal
    Posts
    13
    Hi,

    are you able to import animation data at all? I mean, a simple mesh animated in position - which is not a skin deformer? You should treat animation in a generic way. Bone animation is not different then regular node animation. You should ckech if you support the same transform stack that XSI outputs in your engine or if you should preprocess the data first and generate a matrix per frame.

    The only special thing to know about XSI and COLLADA skinning, is that since XSI performs world based skinning (the final deformation of the mesh vertices does not take into account the current transform of the mesh nor itís animation) as opposed to COLLADA, we have to reparent the skinned mesh under the root node if the mesh or one of itís parents is animated. Luckily, it is not a common practice to animate a skinned mesh transform or itís parents one so the exported node transforms hierarchies does corresponds to the XSI hierarchies 99% of the time.

    Hoping this helps,
    LB

  3. #3
    goulart
    Guest

    There is another application to create animations

    There is another application that let you create GLSL effects and apply them to animations or a movie. It's a very young project and it's called Mewa Film.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •