Jump to content

  • Log In with Google      Sign In   
  • Create Account

[SOLVED] Skeletal animation rotation problems


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
3 replies to this topic

#1 EngineCoder   Members   -  Reputation: 246

Like
0Likes
Like

Posted 28 May 2010 - 07:49 AM

I'm trying to render COLLADA animation exported from Blender. My test animation moves a human's hand up and left. I got the basic skinning working fine, but the animation rotates to the wrong direction. The animation is split to 4 animations: translate, rotateX, rotateY and rotateZ. I assume that I must combine those values to form a key frame's joint matrix and I must multiply it with the joint's parent's world matrix and store the result to joint's world matrix. Here's some code: Methods from Mesh class:
/**
 * Applies matrices to skeleton. Should start from the root node.
 * Joint's world transformation is parent's world transformation * local trans.
 **/
void applySkin( Joint* joint )
{
    assert( joint );

    for (size_t c = 0; c < joint->children.size(); ++c) {
        Math::multiplyMatrix( joint->children[ c ].transform,
                              joint->worldTransform,
                              joint->children[ c ].worldTransform );
        applySkin( &joint->children[ c ] );
    }
}
 
    // Replaces joint named "name"'s transform matrix with matrix. Should start searching from root.
    void replaceJointMatrix( const std::string& name, Joint* j, float matrix[ 16 ] )
    {
        assert( j );
        assert( matrix );

        if (name == j->name) {
            for (int i = 0; i < 16; ++i) {
                j->transform[ i ] = matrix[ i ];
            }
            return;
        }
        else {
            for (size_t c = 0; c < j->children.size(); ++c) {
                replaceJointMatrix( name, &j->children[ c ], matrix );
            }
        }
    }

    // Animations change rotX, rotY, rotZ, posX, posY and posZ. From these values the new joint transform matrix is constructed.
    void applyAnimation()
    {
        for (std::map< std::string, Animation >::iterator a = animations.begin(); a != animations.end(); ++a) {
            // Skips this animation if it's not running.
            if (a->second.stepStartTime == 0) { continue; }

            // If true, proceed to the next time step.
            if ((SDL_GetTicks() - a->second.stepStartTime) / 1000.0f >= a->second.input[ a->second.inputIndex ]) {
                ++a->second.inputIndex;
                a->second.stepStartTime = SDL_GetTicks();
            }
            // If true, the animation is finished.
            if (a->second.inputIndex == (int)a->second.input.size()) {
                animations.erase( a->first );
                continue;
            }

            Joint joint;
            findJoint( a->second.joint, &skeleton[ 0 ], &joint );

            float t = (SDL_GetTicks() - a->second.stepStartTime) / 1000.0f;
            int ind = a->second.inputIndex;
            t /= a->second.input[ ind ];

            float animRotationMatrix[ 16 ];
            Math::identityMatrix( animRotationMatrix );

            if (a->second.operation == "translate") {
                float xv = (a->second.output[ ind - 1 == -1? 0 : (ind - 1)*3+0 ]);
                posX = interpolate( xv, a->second.output[ ind * 3 + 0 ], t );

                float yv = (a->second.output[ ind - 1 == -1? 1 : (ind - 1)*3+1 ]);
                posY = interpolate( yv, a->second.output[ ind * 3 + 1 ], t );

                float zv = (a->second.output[ ind - 1 == -1? 2 : (ind - 1)*3+2 ]);
                posZ = interpolate( zv, a->second.output[ ind * 3 + 2 ], t );

                Math::makeRotationMatrix4x4( animRotationMatrix, rotX, rotY, rotZ );
                float transMatrix[ 16 ];
                Math::identityMatrix( transMatrix );
                transMatrix[ 3 ] = posX;
                transMatrix[ 7 ] = posY;
                transMatrix[11 ] = posZ;

                Math::multiplyMatrix( transMatrix, animRotationMatrix, animRotationMatrix );
                replaceJointMatrix( a->second.joint, &skeleton[ 0 ],
                                    animRotationMatrix );
            }
           // Output values are changing X angle.
            if (a->second.operation == "rotateX.ANGLE") {
                float val1 = a->second.output[ ind - 1 == -1? 0 : ind - 1 ];
                float val2 = a->second.output[ ind ];

                rotX = interpolate( val1, val2, t );
                Math::makeRotationMatrix4x4( animRotationMatrix, rotX, rotY, rotZ );
                float transMatrix[ 16 ];
                Math::identityMatrix( transMatrix );
                transMatrix[ 3 ] = posX;
                transMatrix[ 7 ] = posY;
                transMatrix[11 ] = posZ;

                Math::multiplyMatrix( transMatrix, animRotationMatrix, animRotationMatrix );

                replaceJointMatrix( a->second.joint, &skeleton[ 0 ],
                                    animRotationMatrix );
            }

// Y and Z-angles are changed the same way, no need to paste them here.

        vertexDeformed.clear();

        applySkin( &skeleton[ 0 ] );

        // Applies the deform.
        // From COLLADA spec: outv = sum{ (v * BSM) * IBMi * JMi * JW }
        for (size_t v = 0; v < influences.size(); ++v) {
            Influence& i = influences[ v ];
            vertexDeformed.push_back( Vector() );
            Vector4 temp;
            Vector4 v4 = Vector4( vertex[ v ].x, vertex[ v ].y, vertex[ v ].z, 1.0f );
            Math::vectorMultMatrix4x4( v4, bindShapeMatrix, &temp ); // v * BSM

            for (size_t j = 0; j < i.jointIndices.size(); ++j) {
                // Finds a joint for this influence.
                std::string jointName = jointArray[ i.jointIndices[ j ] ];
                Joint joint;
                findJoint( jointName, &skeleton[ 0 ], &joint );
                Vector4 temp2;
                Math::vectorMultMatrix4x4( temp, joint.ibm, &temp2 ); // * IBMi

                Vector4 temp3;
                Math::vectorMultMatrix4x4( temp2, joint.worldTransform, &temp3 ); // * JMi

                temp3 *= weights[ i.weightIndices[ j ] ]; // * JW
                vertexDeformed.back() += Vector( temp3.x, temp3.y, temp3.z ); // outv
            }
        }
    }


I noticed that if I manually change a joint's translation (joint.transform[3, 7 and 11] ) nothing seems to change. From the rendered animation I think that my animation's pivot point is wrong. edit: I solved this problem. Solution in the last post. [Edited by - EngineCoder on May 29, 2010 6:33:16 AM]

Sponsor:

#2 karwosts   Members   -  Reputation: 840

Like
0Likes
Like

Posted 28 May 2010 - 08:00 AM

Does your game use the same coordinate system as blender? (Up:Z Right:X Out:+Y)

You might try to describe the situation more specifically than "it rotates in the wrong direction", that's kind of vague.

#3 EngineCoder   Members   -  Reputation: 246

Like
0Likes
Like

Posted 28 May 2010 - 08:28 AM

Quote:
Original post by karwosts
Does your game use the same coordinate system as blender? (Up:Z Right:X Out:+Y)

You might try to describe the situation more specifically than "it rotates in the wrong direction", that's kind of vague.


My code uses Y-up, but swapping Y and Z in the rotation code does not make it right. Also I have checked Y-up in Blender's armature properties.

I'll post some screenshots tomorrow to describe how it looks and how it should look.

#4 EngineCoder   Members   -  Reputation: 246

Like
0Likes
Like

Posted 29 May 2010 - 12:19 AM

I finally got it working, after trying to find the problem for a week! I had to transpose the matrices that came with the COLLADA file and I had to use negative rotation angles.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS