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 on other sites
Odd, looks like your getting scaling with the rotation. Can't help you much without some code though....

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 on other sites
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 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 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 on other sites

Is it a must to normalize?
When should i normalise?
Obviously normalizing every quaternion is going to result in quite a hit in performance?

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 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 on other sites
Still stuck with no clues.

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 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 on other sites
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 on other sites
Quote:
 Original post by Anonymous Posteryour 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 on other sites
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.

Share on other sites
Quote:
 Original post by GamerSgSorry 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]

Create an account

Register a new account

• Forum Statistics

• Total Topics
628305
• Total Posts
2981962

• 9
• 12
• 11
• 12
• 11