Sign in to follow this  
GamerSg

Quaternion problem (with exe)

Recommended Posts

I recently finished doing skeletal animation, but every once in awhile, there will be bones which dont seem to animate the way they should. In other words, they dont take the path they should and rotate weirdly. Example Here Click on the Skeletal.zip link. [Edited by - GamerSg on September 23, 2004 1:55:44 AM]

Share this post


Link to post
Share on other sites
There is totally no scaling/translation in my code, so im only doing rotation via quaternions. As you can see, it works normally for all the other bones, but screws up for the right hand.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Maybe your animation matrices do not match with the amount of reference matrices.

In other words:
if your reference mesh has 25 BONES and your animation does not,
every time you transform the inverted world vertices wia tha animation world matrix, you have to check if it is the same, for example:

for ( i=every bone in reference mesh )
for ( j=every bone in animation mesh )
if ( ref_bone[i].name == anim_bone[i].name )
do transform
else
keep reference bone.

Share this post


Link to post
Share on other sites
I was thinking the problem lies more with my Quaternion Interpolation, if you observe the animation closely, the figure is doing a jumping jack (couldnt think of any other animation) which goes something like this (in keyframes)

1)
o
| |
||

2)

__ o __
| |
/ \
3)
o
| |
||

4)

\ o /
| |
/


If you notice, the right hand does reach the keyframes. The problem lies with the path it takes to reach the next keyframe. For some reason, the left hand seems to work well.

Share this post


Link to post
Share on other sites
Perhaps you need to normalize some quaternions somewhere... like after transforming a child bone's rotation by a parent bone's? (Your download link didnt work when I tried so couldn't get a better look)

I've had some pretty interesting looking animations before when I forgot to normalize.

Share this post


Link to post
Share on other sites
You certainly need to normalise before you build a rotation matrix from your quaternion (unless your quat to matrix routine auto-normalises), otherwise you'll get shearing or scaling problems.
You don't need to normalise every time you multiply quaternions together.

Share this post


Link to post
Share on other sites
Hmm i just looked through my code and realised that i am not doing the rotation multiplication with quaternions, instead im converting a bone's quaternion to a matrix first, before multiplying the matrix with the parents matrix.

Also, all my quaternions are normalised before they are exported.

Share this post


Link to post
Share on other sites
Still stuck with no clues.

Link should be fixed now.
Here is my quaternion interpolation code which i had obtained from the quaternion article on gamasutra.

void Math::InterpolateQuat(Quat& first,Quat& second,Quat& result,float time)
{
//Interpolates between 2 quaternions based on time factor ranging from 0-1
//Variables
double cosine;
Quat temp; //temp quaternion
float scale0,scale1;

//dot product between the 2 quats
cosine = first.x * second.x + first.y * second.y + first.z * second.z + first.w * second.w;


if(cosine<0.0)
{
//if it is negative, make it positive
cosine = -cosine;
temp.x = -second.x;
temp.y = -second.y;
temp.z = -second.z;
temp.w = -second.w;
}
else
{
temp.x = second.x;
temp.y = second.y;
temp.z = second.z;
temp.w = second.w;
}

if ( (1.0 - cosine) > 0.0001 ) {
double omega, sinom;
// standard case (slerp)
omega = acos(cosine);
sinom = sin(omega);
scale0 = static_cast<float>(sin((1.0 - time) * omega) / sinom);
scale1 = static_cast<float>(sin(time * omega) / sinom);
}
else {//else do LERP
scale0 = 1.0f - time;
scale1 = time;
}


result.x = first.x * scale0 + second.x * scale1;
result.y = first.y * scale0 + second.y * scale1;
result.z = first.z * scale0 + second.z * scale1;
result.w = first.w * scale0 + second.w * scale1;
}

Share this post


Link to post
Share on other sites
I have a feeling the bone for your model's left arm is falling into the 'invert cosine' special case. It's probably the only bone that's going through that case too, based on the angles that the bones are rotating through. The if statement for LERP/SLERP is there just to protect the acos(x) function ... so odds are the LERP portion is never being used. Personally, I'm not sure falling back to a LERP is necessary -- I've never needed it myself.

The important part is that if you invert the cosine, you need to normalize the slerped quaternion.

temp.x = first.x * scale0 + second.x * scale1;
temp.y = first.y * scale0 + second.y * scale1;
temp.z = first.z * scale0 + second.z * scale1;
temp.w = first.w * scale0 + second.w * scale1;

if (cosineWasInverted) { result = temp.Normalize(); }
else { result = temp; }


There are two other interesting things to note, instead of inverting the cosine, you should also be able to invert the scale0 factor. When you invert the cosine, what you're saying is, instead of slerping clockwise, slerp counter clockwise, because the shorter arc is in that direction. Inverting scale0 lets you precalculate scale0 and scale1 and then do an if{} block for the scale / normalize. I'm also inverting sine omega, because we want to minimize our slow floating-point divides for the CPU. I'd also recommend you implement inlined quaternion operators for *scale and +quaterion (that's definitely just me though).


double omega, sinom;
// standard case (slerp)
omega = acos(cosine);
inv_sinom = 1/sin(omega);
scale0 = static_cast<float>(sin((1.0 - time) * omega) * inv_sinom);
scale1 = static_cast<float>(sin(time * omega) * inv_sinom);
if (cosine < 0.0f) {
// invert the scale and normalize
scale0 = -scale0;

// Would be nice to see some Quaternion operators here
// e.g. result = first*scale0 + second*scale1 !!!
result.x = first.x * scale0 + second.x * scale1;
result.y = first.y * scale0 + second.y * scale1;
result.z = first.z * scale0 + second.z * scale1;
result.w = first.w * scale0 + second.w * scale1;

// Now normalize the final quaternion
result.Normalize();
}
else {
result.x = first.x * scale0 + second.x * scale1;
result.y = first.y * scale0 + second.y * scale1;
result.z = first.z * scale0 + second.z * scale1;
result.w = first.w * scale0 + second.w * scale1;
}


Check the code with breakpoints first. If the cosine inversion is ONLY happening with the left-arm bone, then you know there's a solid chance that this is your problem. Then try to apply the minimal fix from the first code snippet. If that works you can consider some other options for improving the slerp.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Just FYI,

your exe does not shutdown properly. I downloaded it to look at it and closed out of it... three hours later I noticed my system was running really slow and went into taskmanager and it was still there taking up 98% of the CPU

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by Anonymous Poster
your exe does not shutdown properly. I downloaded it to look at it and closed out of it... three hours later I noticed my system was running really slow and went into taskmanager and it was still there taking up 98% of the CPU


Does the same thing on my system too.

Share this post


Link to post
Share on other sites
Quote:
Original post by GamerSg
Sorry about the shutdown thing, it will shutdown properly if you use Esc key, i don't know how to shut it down properly from the X button in GLFW.


if(!glfwGetWindowParam(GLFW_OPENED)) tell it to quit.

Of course, you could always look in the reference to find this kind of thing out.

[Edited by - Nathaniel Hammen on September 25, 2004 10:22:17 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this