# md2 animation speed

This topic is 5090 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

I've got a list of start frames, end frames and fps for each of the animations for an md2 model. How can I play them at the right speed when the fps changes for each animation?
/**
* Structure representing the frame data for
* an md2 animation frame.
*/
struct MD2Animation{
unsigned int begin;
unsigned int end;
unsigned int fps;
};

/**
* Array containing the animation data for a regular
* quake 2 md2 model.
* Contains animation data for 198 frames (0-197).
*/
MD2Animation MD2AnimationList[20] ={
{   0,  39,  9 },   // STAND
{  40,  45, 10 },   // RUN
{  46,  53, 10 },   // ATTACK
{  54,  57,  7 },   // PAIN_A
{  58,  61,  7 },   // PAIN_B
{  62,  65,  7 },   // PAIN_C
{  66,  71,  7 },   // JUMP
{  72,  83,  7 },   // FLIP
{  84,  94,  7 },   // SALUTE
{  95, 111, 10 },   // FALLBACK
{ 112, 122,  7 },   // WAVE
{ 123, 134,  6 },   // POINT
{ 135, 153, 10 },   // CROUCH_STAND
{ 154, 159,  7 },   // CROUCH_WALK
{ 160, 168, 10 },   // CROUCH_ATTACK
{ 196, 172,  7 },   // CROUCH_PAIN
{ 173, 177,  5 },   // CROUCH_DEATH
{ 178, 183,  7 },   // DEATH_FALLBACK
{ 184, 189,  7 },   // DEATH_FALLFORWARD
{ 190, 197,  7 },   // DEATH_FALLBACKSLOW
};

/**
* An enumeration containing the different animation states
* for an MD2 model.
*/
enum MD2AnimationState{
MD2_ANIM_STAND = 0,
MD2_ANIM_RUN,
MD2_ANIM_ATTACK,
MD2_ANIM_PAIN_A,
MD2_ANIM_PAIN_B,
MD2_ANIM_PAIN_C,
MD2_ANIM_JUMP,
MD2_ANIM_FLIP,
MD2_ANIM_SALUTE,
MD2_ANIM_FALLBACK,
MD2_ANIM_WAVE,
MD2_ANIM_POINT,
MD2_ANIM_CROUCH_STAND,
MD2_ANIM_CROUCH_WALK,
MD2_ANIM_CROUCH_ATTACK,
MD2_ANIM_CROUCH_PAIN,
MD2_ANIM_CROUCH_DEATH,
MD2_ANIM_DEATH_FALLBACK,
MD2_ANIM_DEATH_FALLFORWARD,
MD2_ANIM_DEATH_FALLBACKSLOW,

NUM_OF_ANIM
};


// inside the render function

// if the animation state is NOT correct for the currentFrame
if( !(m_currentFrame >= MD2AnimationList[m_animState].begin &&
m_currentFrame < MD2AnimationList[m_animState].end) ){

// set the animation state correctly for the currentFrame of animation
for(int i=0; i < NUM_OF_ANIM; i++)
if( m_currentFrame >= MD2AnimationList.begin && m_currentFrame < MD2AnimationList.end ){
m_animState = (MD2AnimationState) i;
break;
}
}

// calculate the percentage for advance through the frames depending
// on the animation data and the frames-per-second for each anim
float p = (float)( MD2AnimationList[m_animState].fps /
(float)( MD2AnimationList[m_animState].end - MD2AnimationList[m_animState].begin) );

// update the animation
updateInterpolation(p);


Obviously, the problem is that for this to work properly, 'p' should always be less than 1. Unfortunately, due to the laws of division, this doens't always work. Is this the way I should be doing it?

##### Share on other sites
You shouldn't calculate p that way, the value will always be constant. Instead, you need to track the amount of time the model has been between those two frames and then divide that by the amount of time it should spend between those two frames.

##### Share on other sites
Well, the way I'm doing it, p should be constant, but only between frames of each animation. For example, from frame 0 to frame 39, p should always be 0.23 because the fps for the first animation is 9.

##### Share on other sites
p should not be constant. You're update function should look something like this:

float model_time = 0;void    tick_md2( float delta_time ) // time since last frame{    model_time += delta_time;    if ( model_time >= 1.0f / MD2AnimationList[m_animState].fps )    {        ++m_currentFrame;        if ( m_currentFrame > MD2AnimationList[m_animState].end )            m_currentFrame = MD2AnimationList[m_animState].begin;        model_time = 0.0f;    }    updateInterpolation( model_time / ( 1.0f / MD2AnimationList[m_animState].fps ) );    }

##### Share on other sites
I've run into a slight problem. (Note:: I have started using BezBen's code as a pre-render update function) Since I need to update the time, I have a class CHiResTimer that I use as a wrapper for QueryPerformanceTimer.

/** * A class that uses QueryPerformanceTimer */class CHiResTimer{public:	LARGE_INTEGER m_startTime;	LARGE_INTEGER m_ticksPerSecond;	/**	 * Default constructor	 */	CHiResTimer()	{	}	/**	 * If the hi res timer is present, the tick rate is stored and the function	 * returns true. Otherwise, the function returns false, and the timer should	 * not be used.	 * \return True if the system supports the hi-res timer, else false.	 */	bool init()	{		if( !QueryPerformanceFrequency(&m_ticksPerSecond) ){			// return false because the system doens't support hi-res timer			m_ticksPerSecond.QuadPart = 0;		// sys doens't support hi-res timer			return false;		}			// system supports hi-res timer		QueryPerformanceCounter(&m_startTime);		return true;	}	/**	 * Returns the number of ticks per second for the hi resolution timer.	 * \return The number of ticks per second for the hi resolution timer.	 */	LARGE_INTEGER getTicks() const	{		return m_ticksPerSecond;	}	/**	 * Returns the total time in seconds (as a float) that has elapsed since the 	 * function was last called.	 * Note::	'QueryPerformanceCounter' returns 0 if there is no hi-res timer on the system.	 * \return The number of elapsed seconds (as a float) that has elapsed since the function was last called.	 */	float getElapsedSeconds()	{		static LARGE_INTEGER s_lastTime = m_startTime;		LARGE_INTEGER currentTime;		QueryPerformanceCounter(¤tTime);		float seconds = ((float)currentTime.QuadPart - (float)s_lastTime.QuadPart) /				(float)m_ticksPerSecond.QuadPart;		// reset the timer		s_lastTime = currentTime;				return seconds;	}};

I've got one that's part of the main engine class that is used to count FPS, and I just added another to the scene graph to send to the new preRender and postRender functions that will be used to update before rendering.

Now here's my problem - I have this loop in my main function:
// temp vars for counting the FPSunsigned int lastFPS = 0;char str[1024];while( engine->run() ){			driver->beginScene();	// reset the world/modelview matrix to the identity matrix	driver->resetMatrix(video::WORLD_MATRIX);	t->addRotationY(0.1f);	engine->getSceneManager()->render();	driver->endScene();	// count the FPS	unsigned int temp = driver->getFPSCounter().getFPS();	// update at most once every second	if( temp != lastFPS ){		lastFPS = temp;		sprintf(str, "Current FPS: %d", temp);		engine->setWindowCaption(str);	}}

And apart from the fact that the animation still looks like it's going too fast, the fps isn't showing up in the window caption, which leads me to belive that I can't instanciate two CHiResTimer objects as I've got them written.

##### Share on other sites
@Bezben

with your
model_time = 0.0f;
don't you loose some accuracy?

should it not better be:
model_time -= 1.0f / MD2AnimationList[m_animState].fps;
?

##### Share on other sites
Yeah, that would be better I expect. To be honest though, you're normally dealing with tiny timeslices so small I doubt you'd ever notice. But yes, technically I think you are dead on.

##### Share on other sites
Quote:
 Original post by Anonymous Poster@Bezbenwith yourmodel_time = 0.0f;don't you loose some accuracy?should it not better be:model_time -= 1.0f / MD2AnimationList[m_animState].fps;?

How does that work?

##### Share on other sites
Quote:
Original post by Endar
Quote:
 Original post by Anonymous Poster@Bezbenwith yourmodel_time = 0.0f;don't you loose some accuracy?should it not better be:model_time -= 1.0f / MD2AnimationList[m_animState].fps;?

How does that work?

It doesn't. I think the AP meant to say,

model_time -= (total_time_in_seconds_for_that_anim)

##### Share on other sites
Still going, I see :). Well, what you started off in this thread, I shall hope to resolve (I've recoded all my MD2 stuff since then from scratch, and implemented a generic keyframe animation model). Let's go nuts and/or sensible.

Because I have nothing better to do, I'm going to start off slow, and you can just skip the stuff you already know.

First of all, the MD2AnimationList shows rows of:
begin, end, fps
begin and end are in reference to which frames to begin and end on, and fps is obviously how many frames to process per second. So, we need a way of keeping track of time. I'm assuming you've done that (it looks like it).

For each time you want to update the model, you must find a few things:

The world time
The time relative to the beginning of the world (easy). Also known as the Global Time.

The animation time
The time relative to when the animation starts. This is calculated by taking the time the animation started (which you'd set when you hit the "play" function), and subtracting it from the world time. That is, if we're 30 seconds into the program ("world time"), and we started the animation at 29.5 seconds, then the animation time is 0.5 seconds (and since I'm using ticks, 500 ticks). That is, we're 500 ticks into this loop of the animation.

Now that we know how many ticks we're into the animation (we know our animation time), we have to figure out whether or not to proceed to the next frame. We do this by summing all lengths of the previous frames and adding our length on as well. This, in MD2 files, is easier to do because all frames have the same fps, thus the same length (1000 / fps). So:
(current_frame_index - beginning_of_animation_frame_index + 1) * 1000 / fps
That equals the time in ticks that sum to the start of our current frame (from the beginning of the animation). Thus: if we're 11 frames into the STAND animation, we're at frame 10 (zero-based). So we have:
(10 - 0 + 1) * 1000 / fps= 11 * 1000 / 9 = 1222.222-
If the animation time is more than that, then we should move to the next frame, and probably check again. I do - I have this code basically in a loop. That means that if we experience (oh god) 30 seconds of lag, then when we resume normal operation, we'll be at the right spot. This involves more complex stuff, like subracting the total animation time to the animation_start_time once we hit the end of the animation, where our animation time is still greater than the total length of our animation. I'll go over that if I need to.

The frame time
This is simply the animation time minus the sum of all the proceeding frame's lengths, as in:
](current_frame_index - beginning_of_animation_frame_index) * 1000 / fps
It should give you a value between zero and the length of the current frame (in ticks, of course). This is what we use to calculate the interpolation value.

The interpolation value
frame_time / current_frame_length.

HOMG done. Yeah, now you have the interpolation value, you can interpolate between the current frame and the next one (making sure to check that the next frame shouldn't be the first frame if you're at the end). I don't have a particularly elegant way of ending this little... diatribe. I'll leave it on this:

Next to conquer is MD5! :)

• ### Game Developer Survey

We are looking for qualified game developers to participate in a 10-minute online survey. Qualified participants will be offered a \$15 incentive for your time and insights. Click here to start!

• 37
• 16
• 18
• 25