flipCode - Tech File - Alexander Herz [an error occurred while processing this directive]
Alexander Herz
Click the name for some bio info

E-Mail: softcore@web.de



   06/24/1999, Bones Animation


'lo again...

I think I've forgotten the ';' after most structs..so don't wounder in case you copy...

as said I'm gona talk about bones animation this time... this eg is there basic..and could be much more optimized..it is to provide the idea only.. (eg.you should have the vertexes that way(to do vertex arrays and stuff):

float *Vertexes;//array of floats..3 give a vec)
WORD *Node_Index;

I made it recursive which is good to do hierachys of nodes also...

we have a Node:

struct Node_
{
	Node_ *Child_Nodes;//pointer to array of subnodes..
	WORD Num_Childs;//howmany of em?
	WORD Node_ID;//every vertex has an ID..every node a unique one..so 
                     //we know which vert belongs to which node..
	float Pos[3];//actual position of this node(cache)
	float xw,yw,zw;//actual rotation(cache)
}

struct Vertex_		//minimum vertex..normally you'll save along
                        //color+alpha and other stuff here
{
	float Pos[3];//guess what
	WORD Node_ID;//which nodes does this vert belong to?
}

//we need some struct for the surfs this way..I'll assume trias here..

struct Surface_
{
	WORD Verts[3];//indexs into vertex array for this tria
	DWORD TexID;
	//.. other shit
}
but we need to have the animation stored somehow ..
keyframes are rather common..and easy to use..:

struct KeyFrame_
{
	float Pos[3];//pos of the node(and all child ones)
	float xw,yw,zw;//rotation of the node(and all child ones)
	DWORD Time;//offset of this keyframe to the fisrt one 
			//...needed for interpolation...
}

struct Animation
{
	KeyFrame_ *KeyFrames;
	WORD Num_KeyFrames;
	DWORD StartTime;//all the times in KeyFrame_->Time are stored
                        //relative to 0 so we have to substract the
                        //starttime from the actual time..
	WORD Act_KeyFrame;
}

// we also need some struct providing the initial Node positions..
//and maintaining other shit..

struct Mesh_
{
	Vertex_ *Verts;
	DWORD Num_Verts;
	Surface_ *Surfs;
	DWORD Num_Surfs;
	Node_ *Nodes;
	WORD Num_Nodes;	
	float *Node_Pos;//contains the positions of the nodes
			//I stored the pos of the node with id x at
                        //(Nodes_Pos+x*3)
	Animation_ *Animations;
	WORD Num_Animations;
	WORD Act_Animation;
}
how to fill this structs is kinda logical..

the plain way to render that..many optimizations can be added here either..

I'll explain my renderpipline another day...

//HERE COMES THE INTRESTING PART...

the following two funcs do hierachical transforms and rotations..

void Rec_RotateNode(Mesh_ *Mesh,Node_ *Node,float xw,float yw,float zw)
//rotates the passed Node and all of its childs
{
	for(int i=0;i<Mesh->Num_Verts;i++)
	{
		if((Verts+i)->Node_ID==Node_)
			//Rotate Vertex about
                        //(Mesh->Nodes+Node->Node_ID*3) by xw,yw,zw
			//Rotate it's normal
	}
	for(i=0;i<Node->Num_Childs;i++)
		Rec_RotateNode(Mesh,(Node->Child_Nodes+i),xw,yw,zw);
}

void Rec_TranslateNode(Mesh_ *Mesh,Node_ *Node,float *Pos)
//translate the passed Node and all of its childs
{
	for(int i=0;i<Mesh->Num_Verts;i++)
	{
		if((Verts+i)->Node_ID==Node_)
			//Add pos to 
                        //(Mesh->Nodes+Node->Node_ID*3) 
	}
	for(i=0;i<Node->Num_Childs;i++)
		Rec_TranslateNode(Mesh,(Node->Child_Nodes+i),Pos);
}
interpolates between the two passed keyframes
and returns true if the next keyframe was reached

bool InterpolateAngles(DWORD WorldTime,DWORD StartTime,KeyFrame_ *KF1,
                      KeyFrame_ *KF2,float *xw,float *yw,float *zw)
//KF1 is the actual
{
	float angles[3];
	WorldTime-=StartTime;//get offset
	float steps=KF2->Time-KF1->Time;
	angles[0]=(KF2->xw-KF1->xw)/steps;
	angles[1]=(KF2->yw-KF1->yw)/steps;
	angles[2]=(KF2->zw-KF1->zw)/steps;
	//we only have some delta now..gotta add that to the KF1
	angles[0]=(WorldTime-KF1->Time)*angles[0]+KF1->xw;
	angles[1]=(WorldTime-KF1->Time)*angles[1]+KF1->yw;
	angles[2]=(WorldTime-KF1->Time)*angles[2]+KF1->zw;
	*xw=angles[0];
	*yw=angles[1];
	*zw=angles[2];
	if(WorldTime>KF2->Time)
	{
		//you could save events along with every keyframe..
		//you'd execute the event assigned to KF2(maybe play a
                //sound or such) now
		return 1;
	}
	return 0;
}

void InterpolatePosition(DWORD WorldTime,DWORD StartTime,KeyFrame_ *KF1,
                         KeyFrame_ *KF2,float *Pos)
//guess what it does..
{
	float pos[3];
	WorldTime-=StartTime;
	float steps=(KF2->Time-KF1->Time);
	pos[0]=(KF2->Pos.x-KF1->Pos[0])/steps;
	pos[1]=(KF2->Pos.y-KF1->Pos[1])/steps;
	pos[2]=(KF2->Pos.z-KF1->Pos[2])/steps;
	pos[0]=(WorldTime-KF1->Time)*pos[0]+KF1->Pos[0];
	pos[1]=(WorldTime-KF1->Time)*pos[1]+KF1->Pos[1];
	pos[2]=(WorldTime-KF1->Time)*pos[2]+KF1->Pos[2];
	
	Pos[0]=pos[0];
	Pos[1]=pos[1];
	Pos[2]=pos[2];
}

void Rec_Animate(Mesh_ *Mesh,Mesh_ *Cache,Node_ *Node,DWORD WorldTime)
{
	for(int i=0;i<Node->Num_Childs;i++)
		Rec_Animate(Mesh,Cache,(Node->Child_Nodes+i),WorldTime);

	if(Mesh->Num_Animations)//any anim there?
	{
		float xw,yw,zw;
		float Pos[3];
		if((Mesh->Animations+Mesh->Act_Animation-1)->Act_KeyFrame<
		(Mesh->Animations+Mesh->Act_Animation-1)->Num_Frames)
		{
			bool NextFrame=InterpolateAngles(WolrdTime,
			(Mesh->Animations+Mesh->Act_Animation-1),
                  ((Mesh->Animations+Mesh->Act_Animation-1)->
			KeyFrames+(Mesh->Animations+Mesh->Act_Animation-1)
			->Act_KeyFrame),
			((Mesh->Animations+Mesh->Act_Animation-1)->
			KeyFrames+(Mesh->Animations+Mesh->Act_Animation-1)
			->Act_KeyFrame+1),&xw,&yw,&zw);

			if(NextFrame)
			{
				(Mesh->Animations+Mesh->Act_Animation-1)
				->Act_KeyFrame++;				
			bool NextFrame=InterpolateAngles(WolrdTime,
			(Mesh->Animations+Mesh->Act_Animation-1),
                 ((Mesh->Animations+Mesh->Act_Animation-1)->
			KeyFrames+(Mesh->Animations+Mesh->Act_Animation-1)
			->Act_KeyFrame),
			((Mesh->Animations+Mesh->Act_Animation-1)->
			KeyFrames+(Mesh->Animations+Mesh->Act_Animation-1)
			->Act_KeyFrame+1),&xw,&yw,&zw);
				if(NextFrame)
				{
					//there has been a bigger breake
					//Set the anim to the last
                              //valid frame
					return;
				}
			}

			InterpolatePosition(WolrdTime,
			(Mesh->Animations+Mesh->Act_Animation-1),	
                  ((Mesh->Animations+Mesh->Act_Animation-1)->
			KeyFrames+(Mesh->Animations+Mesh->Act_Animation-1)
			->Act_KeyFrame),
			((Mesh->Animations+Mesh->Act_Animation-1)->
			KeyFrames+(Mesh->Animations+Mesh->Act_Animation-1)
			->Act_KeyFrame+1),Pos);

			Node->Pos=Pos;
			Node->xw=xw;
			Node->yw=yw;
			Node->zw=zw;
			
		}
		else //anim finished..may loop or such..
		
	}
	//translate + rotate always..we cache last anim values in the 	//node..
	Rec_RotateNode(Mesh,Node,Node->xw,Node->yw,Node->zw);
	Rec_TranslateNode(Mesh,Node,Node->Pos);
}

void RenderAnimObj(Mesh_ *Mesh,Camera_ *Camera,DWORD WorldTime)
{
	Rec_Animate(Mesh,WorldTime,Mesh->Nodes);
	//here follows the normal code for any static mesh..
	//get lighting
	//transform to viewpos
	//display
} 
ok..very late now..
we have a maths test tomorrow..
and I haven't learned a shit..

but there are a few things about Rec_Animate...
firstly..as you might probably guess..Rec_ means recursive..

we start with the lowest node in the hierachy..
(the for all childs shit)

and calc the interpolation for the angles..
if we have to advance one frame
(if (NextFrame))
we do so and calc(after testing for brake)
we real angles+pos for this node...

we cache that shit unto the node
and rotate+translate the node's vertexes(and all its childs)
to the desired pos
then we go up the hierachy and do the same for all over nodes..

why do we start with the lowest node??
well..
that way we avoid having to translate/rotate any node..
normally if you start with the highest node you have to translate/rotate all lowerlevel nodes as well...

if you start at the bottom their rots/transls have aklready been made..so we don't need their worldpos

if you got more quests ask me..

after that has been done..the mesh is completely animated..that means it can be handeled like a static one..which allows more optimisations to be added and you have to rotate the normals of every vertex along with the vertex(every time in the Rec_Animate) otherwise you won't have worldpos normals available for lighting it..

if anybody is interested in some source for saving/loading node hierachies.. tell me..

Alex
I hope I won't fail tomorrow..

cya





  • 07/05/2000 - fastculling
  • 05/15/2000 - parties&fpu fun
  • 04/03/2000 - Tech File Update
  • 02/11/2000 - Tech File Update
  • 01/14/2000 - Tech File Update
  • 06/28/1999 - Rendering Cities
  • 06/24/1999 - Bones Animation
  • 06/22/1999 - Introduction

  • This document may not be reproduced in any way without explicit permission from the author and flipCode. All Rights Reserved. Best viewed at a high resolution. The views expressed in this document are the views of the author and NOT neccesarily of anyone else associated with flipCode.

    [an error occurred while processing this directive]