Adding Rotations

Started by cyberkilla, February 16, 2007, 02:19:28 PM

Previous topic - Next topic

cyberkilla

I have two rotations, similar to below.

<rotate angle="0.224545">
<axis x="0.999774" y="-0.019487" z="0.008553"/>
</rotate>


I need to "add" them together, to get the final rotation.
For some reason, it doesn't appear to work like this...


matrix1.matMul(matrix2);


The result appears to swap the actual axis.
It is hard to explain. Basically...

The first matrix is the bone's rotation axis, and the second is the rotation of the current keyframe.
It is my assumption that the keyframe is relative to the rest pose/rotation of the bone.

If I have a rest rotation that has an axis pointing 45 degrees up on the Y axis,
applying a keyframe rotation with an axis of 45 degrees up on y also, it is almost as if it stays 45 degrees up Y, but moves ~45 degrees along another axis.

Strange, huh?

EDIT:
Worth mentioning...
The skeletal system works BRILLIANT, if the rest post has no rotations.

If I make a snake, with all the bones in a line, i can animate it any way I want,
and it works fine.
It seems to be due to the adding of rest rotation with keyframe rotations.
http://futurerp.net - Text Based MMORPG
http://beta.rpwar.com - 3D Isometric MMORPG

EgonOlsen

I'm not sure if the order is correct...what happens if you switch them? And how are matrix1 and -2 calculated? Via rotateAxis() or something?

cyberkilla

yes, both are rotateaxis
http://futurerp.net - Text Based MMORPG
http://beta.rpwar.com - 3D Isometric MMORPG

cyberkilla

Swapping doesnt work:)

Egon, if I give you the source code(commented),
and some model files, and how they are supposed to look,
could you check it out?

Actually, if you ran through a few of the methods, I am sure you would pick up on something, if it is an obvious mistake.

Its only really SimpleSkeleton's buildJoints(), and advanceAnimation()
that appear to be in error.

What is strange, is that the rest pose of the skeleton is perfect.
So is the movement of vertices in relation to their joint(s).

The problem is that the rotational keyframes are not being applied correctly.
They all move relative to the parent joint, but not in the correct direction!:)

As I mentioned above, IF I use a model that has NO rotation in its rest pose,
the keyframe rotations seem to work. NOT in the right direction - but they moved like they do in the modeler.

It is almost as if the Keyframes are rotated -90 degrees along y(?) axis, compared to the MESH, and the skeletons REST POSE.

This first bit DOES put the bones in the right REST pose.
/**
* Initializes the skeleton's bones, by setting up their
* transformation matrices.
*/
private void setupBones()
{
sortBones();

for (SimpleBone bone : bones)
{
bone.transRelative = new SimpleVector(bone.localTranslation);

Matrix localRotation = new Matrix();
localRotation.rotateAxis(bone.localRotationAxis,-bone.localRotationAngle);

bone.rotationAbsolute = localRotation;//.invert3x3();

if (bone.parent != null)
   {
//=========================================================
// If bone has a parent, apply its rotation matrix to
// the child bone.
//=========================================================
bone.rotationAbsolute.matMul(bone.parent.rotationAbsolute);

bone.transRelative.matMul(bone.parent.rotationAbsolute);
//---------------------------------------------------------
// Inherit the absolute transformation from the parent
// bone, adding it to this child bone's relative
// transformation, to get this bone's absolute
// transformation.
//---------------------------------------------------------
    bone.transAbsolute = new SimpleVector(bone.parent.transAbsolute);
    bone.transAbsolute.add(bone.transRelative);
   }
   else
   {
    //========================================================
    // This is a parent bone.
    // We do not inherit any transformations,
    // and do not have any parent rotation matrices to apply.
    //========================================================
       bone.transAbsolute = new SimpleVector(bone.transRelative);
   }
}
}



/**
* Advances the animation sequence.
* The faster this method is called, the higher the
* fidelity of the animation.
*
* The keyframes are self timed, and will not be
* made faster by more calls to this method.
*/
public void advanceAnimation()
{
SkeletalAnimation animation = animations[currentAnimation];

//=================================================================
// Ensure that the animation time has not been exceeded.
// If so, restart animation sequence, or clip to end time,
// if the animation is non-looping.
//================================================================
double time = timer.getTime();

if (time > animation.length)
{
if (animation.looping)
{
restartAnimation();
time = 0;
}
else
{
       time = animation.length;
}
}
//================================================================
// Calculate the final transform of all bones at this time
// in the animation.
//
// Inbetween keyframes are interpolated for smoother transition.
//================================================================
for( int i = 0; i < bones.length; i++)
{
SimpleVector transVec = new SimpleVector();
   SimpleBone   bone     = bones[i];
   int frame;
   
   //------------------------------------------------------------
   // If the bone has no keyframes, then skip it.
   //------------------------------------------------------------
   if (bone.rotationKeyframes[currentAnimation] == null &&
       bone.translationKeyframes[currentAnimation] == null)
   {
    bone.transFinal.set(bone.transAbsolute);
       continue;
   }
   //------------------------------------------------------------
   // Ensure we are at the correct keyframe for this time
   // in the animation.
   //------------------------------------------------------------
   frame = bone.currentTranslationKeyframe;
   while(frame < bone.translationKeyframes[currentAnimation].length &&
        bone.translationKeyframes[currentAnimation][frame].time < time)
   {
    frame++;
   }
   bone.currentTranslationKeyframe = frame;
   //------------------------------------------------------------
   // Find the correct translation vector for this time in the
   // animation, for this bone.
   //
   // If the frame is at the start, or the end, then the
   // vector is taken directly from the keyframes.
   // However, if it is neither, the vector is interpolated
   // from the current keyframe, and the previous keyframe.
   //------------------------------------------------------------
   if (frame == 0)
   {
    transVec.set(bone.translationKeyframes[currentAnimation][0].translation);
   }
   else if (frame == bone.translationKeyframes[currentAnimation].length)
   {
    transVec.set(bone.translationKeyframes[currentAnimation][frame-1].translation);
   }
   else
   {
    TranslationKeyframe curFrame  = bone.translationKeyframes[currentAnimation][frame];
    TranslationKeyframe prevFrame = bone.translationKeyframes[currentAnimation][frame-1];
    float timeDelta = (curFrame.time)-(prevFrame.time);
       float interpValue = (float)(time-(prevFrame.time))/timeDelta;

       SimpleVector curTranslation  = curFrame.translation;
       SimpleVector prevTranslation = prevFrame.translation;
         
       transVec.x = prevTranslation.x + (curTranslation.x - prevTranslation.x) * interpValue;
       transVec.y = prevTranslation.y + (curTranslation.y - prevTranslation.y) * interpValue;
       transVec.z = prevTranslation.z + (curTranslation.z - prevTranslation.z) * interpValue;
   }
   //-------------------------------------------------------------
   // Ensure we are at the correct rotational keyframe for this
   // time in the animation sequence.
   //-------------------------------------------------------------
   frame = bone.currentRotationKeyframe;
   while(frame < bone.rotationKeyframes[currentAnimation].length &&
        bone.rotationKeyframes[currentAnimation][frame].time < time)
   {
    frame++;
   }
   bone.currentRotationKeyframe = frame;
   //-------------------------------------------------------------
   // Same as above, but for the rotational keyframes instead.
   //-------------------------------------------------------------
   if (frame == 0)
   {
    bone.rotationFinal = bone.rotationKeyframes[currentAnimation][0].rotationMatrix;
   }
   else if (frame == bone.rotationKeyframes[currentAnimation].length)
   {
        bone.rotationFinal = bone.rotationKeyframes[currentAnimation][frame-1].rotationMatrix;
   }
   else
   {
    RotationKeyframe curFrame  = bone.rotationKeyframes[currentAnimation][frame];
    RotationKeyframe prevFrame = bone.rotationKeyframes[currentAnimation][frame-1];
       
    Matrix curMatrix  = curFrame.rotationMatrix;
    Matrix prevMatrix = prevFrame.rotationMatrix;
       
    float timeDelta   = (curFrame.time)-(prevFrame.time);
       float interpValue = (float)(time-(prevFrame.time))/timeDelta;

       Matrix matrix = new Matrix();
       matrix.interpolate(prevMatrix,curMatrix, interpValue);
       
       //---------------------------------------------------------
       // Set the bones rotationFinal matrix, for use by
       // vertices, and child bones.
       //---------------------------------------------------------
       bone.rotationFinal = matrix;//prevMatrix.cloneMatrix();
   }
   //-------------------------------------------------------------
   // Apply the transformation vector to the relativeFinal
   // transformation of this bone.
   //-------------------------------------------------------------
   SimpleVector relativeFinal = new SimpleVector(bone.transRelative);
   relativeFinal.add(transVec);
   
   if(bone.parent == null)
   {
    //---------------------------------------------------------
    // We are a parent bone, so just use the relative final
    // as the final transform.
    // There are no rotations, or transformations to inherit.
    //---------------------------------------------------------
    bone.transFinal = relativeFinal;
   }
   else
   {
    //---------------------------------------------------------
    // We are a child bone, so inherit any rotations,
    // and inherit the parent bone's final transformation
    // to get our own.
    //---------------------------------------------------------
    bone.rotationFinal.matMul(bone.parent.rotationFinal);
   
    relativeFinal.matMul(bone.parent.rotationFinal);
       bone.transFinal.set(bone.parent.transFinal);
       bone.transFinal.add(relativeFinal);
   }
}
}
http://futurerp.net - Text Based MMORPG
http://beta.rpwar.com - 3D Isometric MMORPG

EgonOlsen

You may give me the code, but i'm not sure if i manage to have a closer look before wednesday. I think you'll be faster by checking this out yourself. Anyway, jkust to make sure that i understand this correctly: You mean that something like this:


   Matrix m1=new Matrix();
   Matrix m2=new Matrix();
   
   m1.rotateAxis(new SimpleVector(1,0,0), -(float) Math.PI/4);
   m2.rotateAxis(new SimpleVector(1,0,0), -(float) Math.PI/4);
   
   SimpleVector s=new SimpleVector(0,0,1);
   m1.matMul(m2);
   s.matMul(m1);
   System.out.println(s);


doesn't create a point (0,-1,0) but something like (0.5,-0.7,0.5) instead?

cyberkilla

Possibly. I know it isnt the most accurate thing in the world.

But this is almost as if it is working properly, but in the WRONG direction.

I have a snake model. It moves its tail up in the air.
In my skeletal api, it's tail rotates the right amount, but across the ground.

Almost like the keyframes are 90/-90 degrees rotated, in comparison to the actual model.

The rest pose of the bone(without any keyframes), is orientated correctly.

Do you understand me now? Strange, isnt it!
http://futurerp.net - Text Based MMORPG
http://beta.rpwar.com - 3D Isometric MMORPG

EgonOlsen

Could it be that the axis of the keyframe's rotation has to be transformed into "rest space" first? I.e. if the rest space's rotation is 90° around X and the keyframe's axis is (0,0,1), that the actual rotation of the keyframe has to be around (0,0,1)"*x90°"=(0,-1,0) and not around (0,0,1)?
I've no clue...i'm just guessing here, and that's the first thing that came to my mind.

Edit: Fixed mixed up vectors..

cyberkilla

Could this be so, even if the rotation keyframes "appear" relative?
Ill try this.
I know i had to apply the invert of the bones rest position to the vertices to  "undo" the rest position. I am not sure it that would for for the bones themselves, but, worth a try.
http://futurerp.net - Text Based MMORPG
http://beta.rpwar.com - 3D Isometric MMORPG

EgonOlsen

I don't mean to undo the "rest rotation" on the keyframe's axis but to apply it. But both things could be possible and both are worth a try. When you don't know how these rotations are actucally related, there's not much left than trying.

cyberkilla

I know very little about matrices, and the more I read, the less I feel I can predict the outcome of math involving them.

The rest position is already applied to the keyframe, so I dont think it could be this.
When I remove it, I get strange results.

It is quite frustrating that I cannot understand the problem.
If I could at least understand it, I could find some kind of answer.
http://futurerp.net - Text Based MMORPG
http://beta.rpwar.com - 3D Isometric MMORPG

EgonOlsen

Does "applied to the keyframe" mean that it's applied to the rotation axis of it? I guess so...
Matrix math IS terrible. It always twists my head after some time of thinking.

So we have a rest rotation around an axis in object space(?) and a keyframe rotation on top of this in...object space(?), rest space(?)...? And while the rest rotation is fine, the keyframe rotation is wrong, i.e. it goes in the wrong direction? Did i get it?

raft

excuse me if this is a silly question but did you check if ogre3d's and jPCT's coordinate systems differ ?

for instance 3d max's is different from jPCT's so during loading, one needs to rotate objects -90 degrees around X axis. only after this they look as they did in 3d max

Object3D.rotateX((float)-Math.PI/2)

EgonOlsen

Quote from: "raft"excuse me if this is a silly question but did you check if ogre3d's and jPCT's coordinate systems differ ?

for instance 3d max's is different from jPCT's so during loading, one needs to rotate objects -90 degrees around X axis. only after this they look as they did in 3d max
Good point. That's worth a look too. They differ for sure, because no other engine that i know of uses my strange system... :wink:

cyberkilla

Ogre is Y up.

I did not think it would matter though, because all keyframes, are supposed to be in object space?

Yes, I think you have it Egon:).

The c++ code Im basing this off, doesnt have all of the strange absoluteRotation matrices I have.

Their implementation of matrix appears to "store" an axis.
Observe....

joint.m_relative.setRotationRadians( joint.m_localRotation );
joint.m_relative.setTranslation( joint.m_localTranslation );

They apply it straight to the relative matrix.
This makes no sense, unless milkshape format stores bones very different to ogre.

EDIT:
I do rotate the object by 180 degrees to correct the Y axis, but do not call rotateMesh, because that misaligns the vertices in object space, and makes it even worse:)
http://futurerp.net - Text Based MMORPG
http://beta.rpwar.com - 3D Isometric MMORPG

EgonOlsen

Quote from: "cyberkilla"Ogre is Y up.

I did not think it would matter though, because all keyframes, are supposed to be in object space?
It will still matter, because object space uses the same system. Just to make this crystal clear to everyone: In jPCT, y is down, z goes INTO the screen and x to the left. Others, where y goes up, have z pointing out of the screen.