Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎮 Unity

Assimp Bones/skinning issue

Started by Philipp Christoph Apr 19, 2013 at 2:33 PM 3 replies 5.5k views
Original Post
Philipp Christoph
Philipp Christoph

Hi!

I'm currently trying to implement Bone animation using SharpDX and Assimp (loading a COLLADA file exported from Blender).

I've gotten to a point where I can visualize the bone nodes and their animation with simple cubes (disregard the strange shading) to a certain extent. It still seems that the position and scaling of the skeleton differs from position & scaling of the actual mesh, but I think that doesn't seem to have an effect on the final vertex positions.

The animation itself seems to have some effect on the vertices as well. The strange thing is, while the cubes used to visualize the bone structure seem to rotate to a full extent, the vertices themselves barely move. You can see that some movement is occurring (probably even in the right direction) but not to the extent of what the actual animation's supposed to look like. I feel like I'm missing a simple matrix multiplication here, but for the love of god, I can't figure out where or which. If you'd kindly have a look at my code and see if you can find something wrong with the calculations, I'd be very grateful! If you need any additional pieces of code, just tell me!

This is what the mesh looks like while animating (also notice the strange rotation, I guess that's just a bug of Assimp/the COLLADA exporter of Blender):

hbmg801l.png

Note that both wing bones are rotated upward. The wings' vertices though are barely rotating at all (you have to zoom in to actually see them move).

Here's the code that renders the model:


private void RenderSkinnedAnimation(ImportedMesh skeletalMesh, Transform transform)
        {
            skeletalMesh.ActiveAnimation.CurrentTime += 0.01;
            skeletalMesh.ActiveAnimation.ComputeNodeTransforms(skeletalMesh.RootNode);

            RenderDebugMeshes(skeletalMesh.RootNode, Matrix.Identity);

            var boneMatrices = new Matrix[skeletalMesh.Bones.Count];

            /* Calculate bone matrices */
            for (int i = 0; i < skeletalMesh.Bones.Count; i++)
            {
                Matrix4x4 boneTransform = skeletalMesh.Bones.Definition.OffsetMatrix;
                MeshNode node = skeletalMesh.RootNode.FindNode(b => b.Name == skeletalMesh.Bones.Definition.Name);

                boneTransform.Transpose();
                boneMatrices = Util.GraphicsHelper.ConvertAssimp2DX(boneTransform);

                while (node.Parent != null)
                {
                    boneMatrices = node.AnimTransform * boneMatrices;

                    node = node.Parent;
                }
            }

            var resultPos = new Vector3[skeletalMesh.Mesh.VertexCount];
            var resultNorm = new Vector3[skeletalMesh.Mesh.VertexCount];
            var finalVertices = (Vertex[])skeletalMesh.Mesh.Vertices.Clone();

            for(int i = 0; i < skeletalMesh.Mesh.VertexCount; i++)
            {
                resultPos = skeletalMesh.Mesh.Vertices.Position;
            }

            /* Calculate final vertex positions by vertex weight and bone matrices */
            for(int i = 0; i < skeletalMesh.Bones.Count; i++)
            {
                for(int c = 0; c < skeletalMesh.Bones.Definition.VertexWeightCount; c++)
                {
                    var weight = skeletalMesh.Bones.Definition.VertexWeights[c];

                    resultPos[weight.VertexID] += weight.Weight * Vector3.TransformCoordinate(skeletalMesh.Mesh.Vertices[weight.VertexID].Position, boneMatrices);
                }
            }

            for(int i = 0; i < skeletalMesh.Mesh.VertexCount; i++)
            {
                finalVertices.Position = resultPos;
            }

            skeletalMesh.Mesh.UpdateVertexBuffer(finalVertices);
            skeletalMesh.Mesh.Render(Matrix.Scaling(transform.Scale) * Matrix.Translation(transform.Position));
        }

I'm updating my vertex array manually atm, this will be outsourced to a shader as soon as I get the animation to work on the CPU.

I've been taking inspiration from the code snippets posted over on this post in Assimp's forum.
It'd be awesome if someone could help me out!

Best Regards

Philipp

Starnick
Starnick

First off, if you haven't, here's a link to a superb explanation of the Assimp animation data structures.

Secondly, a question, how are you using Assimp? The older Assimp .net wrapper, AssimpNet, or SharpDX Toolkit (which uses AssimpNet)?

Note that the Matrix4x4 in Assimp will be transposed compared to the Matrix struct in SharpDX (both use right handed conventions, but Assimp is a column vector matrix and SharpDX uses row vectors), maybe that's your problem? Nvm I didn't notice the transpose() call :)

Philipp Christoph
Philipp Christoph

Thank you very much for the link you posted! I'll read through it right away and see if there's something I missed.

I'm using AssimpNET, since bone animation hasn't yet been implemented in SharpDX Toolkit.

[Edit]

Okay, I've read through the post, but to the best of my knowledge, that's how I implemented the system. I begin to suspect that there's something awry with the line that transforms the actual vertex's position:


resultPos[weight.VertexID] += weight.Weight * Vector3.TransformCoordinate(skeletalMesh.Mesh.Vertices[weight.VertexID].Position, boneMatrices);
 

I'm just not sure what it is..

[Edit 2]

I've taken a step back and tried to implement basic node object animation without bones first. This mostly works. There's one issue I've come across though:

bP4r50hl.png 2PaXhRTl.png

To the left is the (faulty scaling) imported scene (DirectX *.x) in my SharpDX-Engine. To the right is the correct display in the AssimpViewer. At first I assumed the imported scaling was wrong, but that doesn't seem to be the case as changes in Scaling in Blender reflect accordingly on the exported model. I then assumed there was an issue with the imported transformations in general, but they appear to be correct. I don't think that my import code is flawed either, because it simply transfers the same primitive types to my internal structures (e.g. float X = Vertex.X, etc.).

I do have a function that transfers Assimp matrices to SharpDX:


public static Matrix ConvertAssimp2DX(Assimp.Matrix4x4 input)
        {
            input.Transpose();

            var retVal = new Matrix(input.A1, input.A2, input.A3, input.A4,
                input.B1, input.B2, input.B3, input.B4,
                input.C1, input.C2, input.C3, input.C4,
                input.D1, input.D2, input.D3, input.D4);

            return retVal;
        }

But again, I don't see an issue with simple value transfer there. So the issue must be somewhere in my matrix transformations.. This is the code I used to render this basic node animation:



private void RenderNodeMesh(ImportedModel model, MeshNode root, Matrix transform)
        {
            Vector3 pos, scale;
            SharpDX.Quaternion rot;

            root.Transform.Decompose(out scale, out rot, out pos);

            var finalTransform = root.Transform * transform;

            foreach (int index in root.MeshIndices)
                model.Meshes[index].Render(finalTransform);

            foreach (MeshNode childNode in root.Children)
                RenderNodeMesh(model, childNode, finalTransform);

            RenderDebugMeshes(root.DebugMeshes, finalTransform);
        }

The first call of this function uses Matrix.Identity for transform. Checking the values returned from root.Transform.Decompose(), they all appear correct. I'm completely baffled by this behavior..

Best Regards smile.png

Starnick
Starnick

I notice that you're transposing the boneTransform before calling ConvertAssimp2DX, which transposes the matrix again, or just a post scribe oversight?

Philipp Christoph
Philipp Christoph

Whoops, sorry! That was my mistake. I outsourced the Transpose()-call to ConvertAssimp2DX while implementing the basic node animation. It wasn't there when I wrote my original post. Also, obviously the Transpose()-Call doesn't affect my node animation, since it's not called using RenderNodeMesh atm.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.