model animation

Started by
12 comments, last by Mercenarey 19 years, 6 months ago
I'm trying to get ms3d animation working using the code from rsn.gamedev.net modified to fit into my math code and the unfunctioning framework I had before I found that site. However, I've run into some problems, it appears that the joints are being rotated in place correctly but not being translated to be in the correct place in relation to the parent joint. I think it has to just be something stupid I'm missing, but I've been searching around for two days and have found nothing, and I can't get the actual code used from rsn, I'm at a loss. I'll just post pretty much everything, great thanks to anyone willing to spend the time to look through it. SetupJoints:

Matrix33 invmat;
		m_Joints[x].m_Relative = Matrix33::BuildRotationMatrix(m_Joints[x].m_Rotation.x, 
								m_Joints[x].m_Rotation.y, m_Joints[x].m_Rotation.z);

		invmat = Matrix33::BuildRotationMatrix(-m_Joints[x].m_Rotation.x, 
								-m_Joints[x].m_Rotation.y, -m_Joints[x].m_Rotation.z);

		if(m_Joints[x].m_ParentJoint != -1)
		{
			m_Joints[x].m_Absolute = m_Joints[m_Joints[x].m_ParentJoint].m_Absolute * m_Joints[x].m_Relative;
			m_Joints[x].m_AbsoluteInverse = m_Joints[m_Joints[x].m_ParentJoint].m_AbsoluteInverse * invmat;
			m_Joints[x].m_AbsoluteTranslation = m_Joints[m_Joints[x].m_ParentJoint].m_AbsoluteTranslation + m_Joints[x].m_Translation;
		}
		else	//i have no daddy
		{
			m_Joints[x].m_Absolute = m_Joints[x].m_Relative;
			m_Joints[x].m_AbsoluteInverse = invmat;
			m_Joints[x].m_AbsoluteTranslation = m_Joints[x].m_Translation;
		}

		//transfor joint's vertices back to origin so we don't have to do it each frame
		for(uint i=0; i<m_Joints[x].m_NumVertices; ++i)
		{
			Vertex& vert = m_Vertices[m_Joints[x].m_Vertices];
			vert.SetCoordinates(vert.GetCoordinates() - m_Joints[x].m_AbsoluteTranslation);
			vert.SetCoordinates(vert.GetCoordinates()*(m_Joints[x].m_AbsoluteInverse));

			//also need to transform normals
			vert.SetNormal(vert.GetNormal()*(m_Joints[x].m_AbsoluteInverse));
		}

AdvanceAnimation:

for(uint x=0; x<m_NumJoints; ++x)
	{
		Vector3 trans; Quaternion rot;
		while(m_Joints[x].m_RotationKeyframes[m_Joints[x].m_CurRotKeyframe].m_Time < m_CurTime &&
				m_Joints[x].m_CurRotKeyframe <= m_Joints[x].m_NumRotationKeyframes)
			++m_Joints[x].m_CurRotKeyframe;

		if(m_Joints[x].m_CurRotKeyframe == 0)
			rot = Quaternion::BuildFromEulerAngles(m_Joints[x].m_RotationKeyframes[0].m_Data);
		else if(m_Joints[x].m_CurRotKeyframe >= m_Joints[x].m_NumRotationKeyframes)
			rot = Quaternion::BuildFromEulerAngles(m_Joints[x].m_RotationKeyframes[m_Joints[x].m_NumRotationKeyframes-1].m_Data);
		else
		{
			ModelKeyframe& prevFrame = m_Joints[x].m_RotationKeyframes[m_Joints[x].m_CurRotKeyframe-1];
			ModelKeyframe& curFrame = m_Joints[x].m_RotationKeyframes[m_Joints[x].m_CurRotKeyframe];

			//need to interpolate values
			float interpolation = (float)(m_CurTime - prevFrame.m_Time) / 
				(curFrame.m_Time - prevFrame.m_Time);

			Quaternion prev = Quaternion::BuildFromEulerAngles(prevFrame.m_Data);
			Quaternion cur = Quaternion::BuildFromEulerAngles(curFrame.m_Data);
			rot = Quaternion::Slerp(prev, cur, interpolation);
		}

		//find translation
		while(m_Joints[x].m_TranslationKeyframes[m_Joints[x].m_CurTransKeyframe].m_Time < m_CurTime &&
				m_Joints[x].m_CurTransKeyframe <= m_Joints[x].m_NumTranslationKeyframes)
			++m_Joints[x].m_CurTransKeyframe;

		if(m_Joints[x].m_CurTransKeyframe == 0)
			trans = m_Joints[x].m_TranslationKeyframes[0].m_Data;
		else if(m_Joints[x].m_CurTransKeyframe >= m_Joints[x].m_NumRotationKeyframes)
			trans = m_Joints[x].m_TranslationKeyframes[m_Joints[x].m_NumTranslationKeyframes-1].m_Data;
		else
		{
			ModelKeyframe& prevFrame = m_Joints[x].m_TranslationKeyframes[m_Joints[x].m_CurTransKeyframe-1];
			ModelKeyframe& curFrame = m_Joints[x].m_TranslationKeyframes[m_Joints[x].m_CurTransKeyframe];

			//need to interpolate values
			float interpolation = (float)(m_CurTime - prevFrame.m_Time) / 
				(curFrame.m_Time - prevFrame.m_Time);

			trans = prevFrame.m_Data;
			trans += (curFrame.m_Data - prevFrame.m_Data) * interpolation;
		}

		//create final mat and transform to use when moving verts
		m_Joints[x].m_FinalMat = m_Joints[x].m_Relative * rot.BuildRotationMatrix();
		m_Joints[x].m_FinalTransformation = m_Joints[x].m_Translation + trans;

		//now we need to use our parent ro make the final absolute instead of relative
		if(m_Joints[x].m_ParentJoint != -1)
		{
			m_Joints[x].m_FinalMat = m_Joints[m_Joints[x].m_ParentJoint].m_FinalMat * m_Joints[x].m_FinalMat;
			m_Joints[x].m_FinalTransformation = m_Joints[m_Joints[x].m_ParentJoint].m_FinalTransformation + m_Joints[x].m_FinalTransformation;
		}

		//transformify the vertices
		for(uint i=0; i<m_Joints[x].m_NumVertices; ++i)
		{
			uint vert = m_Joints[x].m_Vertices;
			m_TransformedVerts[vert].SetCoordinates((m_Vertices[vert].GetCoordinates() 
				* m_Joints[x].m_FinalMat) + m_Joints[x].m_FinalTransformation);

			m_TransformedVerts[vert].SetNormal(m_Vertices[vert].GetNormal() * m_Joints[x].m_FinalMat);
			//all other parts of transformed verts must have been set during initialization	
		}
	}

Advertisement
probably a stupid question, but have you verified that ParentJoint is being stored (i.e. not a -1)?
Yes, all but the first joint(obviously) have a parent.
God I hate to bump, and I know it's a lot of code for someone to read through, but I really need some help. It seems like I should just have to do some translation before rotating to get it to follow the parent node's movement but it always ends up really deformed.
Consider this half a bump and half a suggestion... sorry i dont have the stomach to look through anyone elses code right now (im sick enough of my own) :-p but ill just throw a couple things out there...

for skeletal animation.... the final matrix is all of the parent matrices ADDED together... i know that threw me at first...

does the bind pos work?

what are the symptoms? legs shooting every which way? :-D

heh, not much of a help but i gave it a shot lol
-Dan
When General Patton died after World War 2 he went to the gates of Heaven to talk to St. Peter. The first thing he asked is if there were any Marines in heaven. St. Peter told him no, Marines are too rowdy for heaven. He then asked why Patton wanted to know. Patton told him he was sick of the Marines overshadowing the Army because they did more with less and were all hard-core sons of bitches. St. Peter reassured him there were no Marines so Patton went into Heaven. As he was checking out his new home he rounded a corner and saw someone in Marine Dress Blues. He ran back to St. Peter and yelled "You lied to me! There are Marines in heaven!" St. Peter said "Who him? That's just God. He wishes he were a Marine."
Yeah the final matrix is a cumulative multiplication of all the parent matrices.

Not entirely sure what you mean by the bind pos?

At first glance it looks like it's just the joints not getting translated correctly to the end of the parent joints. Like the joints all rotate in place but never move. It seems like it should be something obvious that I'm missing, but I really can't find it, with the number of times I think I could be overlooking just about anything.

Thanks for the ideas, beggars can't be chosers.
well... this may be wrong... but it believe its supposed to be the ADDITION rather than the multiplication of the bones... i know it seems counter intuitive... but hey... its worth a shot right? (its possible that i heard wrong... but as i said... its probably worth a shot)

-Dan
When General Patton died after World War 2 he went to the gates of Heaven to talk to St. Peter. The first thing he asked is if there were any Marines in heaven. St. Peter told him no, Marines are too rowdy for heaven. He then asked why Patton wanted to know. Patton told him he was sick of the Marines overshadowing the Army because they did more with less and were all hard-core sons of bitches. St. Peter reassured him there were no Marines so Patton went into Heaven. As he was checking out his new home he rounded a corner and saw someone in Marine Dress Blues. He ran back to St. Peter and yelled "You lied to me! There are Marines in heaven!" St. Peter said "Who him? That's just God. He wishes he were a Marine."
Neah, it IS multiplication.

I've implemented animation for Blizzard's MDX/MDL models a while ago, so I can list some things that I've been doing wrong(until I've found and correct them in the end):
- check the order of multiplications - rotations or translations first?
- check the order of linking with parent matrix, it is different when you are using left-handed or right-handed coordinates.

Quote:
//create final mat and transform to use when moving verts
m_Joints[x].m_FinalMat = m_Joints[x].m_Relative * rot.BuildRotationMatrix();
m_Joints[x].m_FinalTransformation = m_Joints[x].m_Translation + trans;

- I'm pretty sure you should apply parent's transformation to your _m_FinalTransformation_ vector (consider your model's left hand moves to the left - when the corpus is rotated(pi/2 to the right), the hand moves forward!!), considering the symptoms it seems that here is the main case hidden...
- you should assure, that the joints' final matrices are computed in BFS order in the skeletal-tree,

And one more: I was using 4x4 matrices only, so I wouldn't have to worry about rotations and translations inheritance, just the final matrix...

That's all that came to my mind on-the-fly.
Hope there's nothing left ;)
/def
Crazed:
Grab the MSViewer demo from MilkShape and have it compile (yes, I know it is OGL, do it nonetheless). Then be sure that the exact same data is loaded into both your program and MSViewer.

After that, you debug, stepping through each line in both your program and MSViewer, and compare the results of each step.

I can recommend locking the AdvanceFrame to a fixed number (in the beginning of AnimationAdvance() I put a line:

m_currenttime = 12.0f; (or 0.5f if you use from 0-1 instead of 1-FPS, like MSViewer does).

This effectively locks the frame at one specific time, which makes it easy for you to compare.

I can't be arsed to parse through your code, but here you have some hints to how I got it to work.

PS:
When you test, then don't bother with RH->LH conversions. The data for both programs has to be the same.
Quote:CalvinI am only polite because I don't know enough foul languageQuote:Original post by superpigI think the reason your rating has dropped so much, Mercenarey, is that you come across as an arrogant asshole.
This was covered by a previous poster, but the translation component isn't quite right. Everything else looks correct to me, although 4x4 matrices do make this a bit easier in my opinion.

m_Joints[x].m_AbsoluteTranslation =    m_Joints[m_Joints[x].m_ParentJoint].m_AbsoluteTranslation   + m_Joints[x].m_Translation;


You should rotate the current bone's translation by the parent bone's orientation before adding in the parent bone's translation. Otherwise you're not translating in the right directions away from the parent bone.

m_Joints[x].m_AbsoluteTranslation =     (m_Joints[x].m_Translation * m_Joints[m_Joints[x].m_ParentJoint].m_Absolute)    + m_Joints[m_Joints[x].m_ParentJoint].m_AbsoluteTranslation;

This topic is closed to new replies.

Advertisement