Sign in to follow this  
Endar

md2's and interpolation

Recommended Posts

I found this for md2 animations, and I'm currently assuming that fps is the frames per second that the animation should be played at. I'm trying to get it to work, but with no avail.
struct MD2Animation{
	unsigned int begin;
	unsigned int end;
	unsigned int fps;
};

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
};

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
};

This is a little snip of code that I'm using to calculate the amount to interpolate every frame by to play the animation at the right speed.
// 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) );
I'm for some animation's I'm getting a value of almost 1 or greater, which doesn't really work very well. Is there a better way to do this? Or are md2 animations supposed to be interpolated at a constant rate?

Share this post


Link to post
Share on other sites
I'm a little lost as to how you are iterating through your keyframes. The third column is the fps, as you already know. So in STAND, if 1000 / 9 ( =~ 111) ticks have expired, then you proceed to the next frame. In RUN, if 100 ticks have expired, you move on to the next frame.

(end - begin) / fps = time the animation will run for
fps / (end - begin) = one divided by the time the animation will run for

I don't think that the equation is helping much, unless I've misunderstood your intention.

Share this post


Link to post
Share on other sites

/**
* Set an animation for the md2 model based on the animation state.
* \param state The state of the model to be animated.
*/

void animate(MD2AnimationState state)
{
// if the state is invalid
if( !(state >= 0 && state < NUM_OF_ANIM) ){
util::Message::print("CAnimatedMeshMD2::animate: invalid animation state.");
return; // return without starting the animation
}

m_animState = state; // change the animation state
// start the animation
animate(MD2AnimationList[m_animState].begin, MD2AnimationList[m_animState].end);
}

/**
* Set the animation for the MD2 model.
* \param start_frame Set the starting frame for the animation
* \param end_frame Set the end frame for the animation
*/

void animate(unsigned int start_frame, unsigned int end_frame)
{
// if the parameters are valid
if( start_frame < m_numFrames && end_frame < m_numFrames){
// if we don't want any animation
if( start_frame == end_frame ){
m_startFrame = start_frame;
m_endFrame = end_frame;
m_interpol = 0.0f;

// We don't assign currentFrame here because we might be going from
// animating to not animation, so we'll use m_currentFrame as the difference.
// Eg. startFrame and endFrame could be 50, so we want to display frame 50 with
// no animation. If we're going from frame 70, then currentFrame will still be 70,
// but if currentFrame is equal to startFrame and endFrame, then we don't have to
// reassign the vertices, we can just stop interpolation.
return;
}

m_startFrame = start_frame; // set the start frame of the animation
m_currentFrame = start_frame; // set the start frame of the animation
m_endFrame = end_frame; // set the end frame of the animation
m_interpol = 0.0f; // set current interpolation to 0
}
else
util::Message::print("CAnimatedMeshMD2::animate: invalid start and/or end frame.");
}

/**
* Updates the animation of the model.
* Constructs the vertices between the two keyframes and
* stores them in m_displayVertexList[i].
* \param percent The percent to interpolate by (the percentage to advance through each frame, each
* interpolation call). The percentage is a decimal point: 0.25 for 25%
*/

void updateInterpolation(float percent = 0.01f)
{
util::CVector3d v1; // current frame point values
util::CVector3d v2; // next frame point values

scene::CVertex3d vertex; // temporarily holds an interpolated vertex

// If the start and end frames are the same, then that means that
// we don't want any animation.
if( m_startFrame == m_endFrame ){
// if we don't want animation, we have to assign the right vertices for the frame
if( m_displayVertexList.getSize() != m_numVertices ){
m_displayVertexList.clear();
m_displayVertexList.resize(m_numVertices); // size array
}

// Maybe we're already on the frame we want, so we don't have to reassign
// all the vertices in the model.
if( m_currentFrame != m_startFrame ){
// reassign all the vertices in the model
m_currentFrame = m_startFrame;
for(int i=0; i < m_numVertices; i++)
m_displayVertexList[i] = m_vertexList[(m_currentFrame*m_numVertices) + i];
}

return;
}

// copy the required vertices into the array
m_displayVertexList.clear();
m_displayVertexList.reserve( m_numVertices );

// If interpolation of a whole frame was done, increment the current frame and next frame.
// Note:: this wraps around again to the beginning of the whole animation.
if (m_interpol >= 1.0f){
m_interpol = 0.0f; // set interpolation to 0
m_currentFrame++; // start at next frame

if (m_currentFrame >= m_endFrame)
m_currentFrame = m_startFrame; // wrap around to the start

m_nextFrame = m_currentFrame + 1; // assign the next frame to interpolate to

if (m_nextFrame >= m_endFrame || m_nextFrame >= m_numFrames)
m_nextFrame = m_startFrame; // wrap around to the start
}

// update the interpolation for each vertex
for(int i = 0; i < m_numVertices; i++){
// get the points of each frame to interpolate between
v1 = m_vertexList[(m_currentFrame*m_numVertices) + i].point;

v2 = m_vertexList[(m_nextFrame*m_numVertices) + i].point;

// store first interpolated vertex of triangle
vertex.point.x = v1.x + m_interpol * (v2.x - v1.x);
vertex.point.y = v1.y + m_interpol * (v2.y - v1.y);
vertex.point.z = v1.z + m_interpol * (v2.z - v1.z);

// render properly textured triangle
m_displayVertexList.push_back(vertex);
}

m_interpol += percent; // increase percentage of interpolation between frames

}

/**
* Render the model using indexed triangle lists.
*/

void render()
{
if(m_texture)
m_texture->apply(); // apply the texture

// 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[i].begin && m_currentFrame < MD2AnimationList[i].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);

// draw the indexed triangle list
m_sceneManager->getVideoDriver()->drawIndexedTriangleList(
m_displayVertexList.getPtr(), m_displayVertexList.getSize(),
m_displayIndexList.getPtr(), m_displayIndexList.getSize() );

ISceneGraphNode::render(); // render all the children
}






Basically, the 'updateInterpolation' function generally takes care of changing the current frame, and I'm just using the fps inside the 'render' function to calculate the percentage I have to interpolate by this frame (next frame) to keep the proper fps for the animation.

Share this post


Link to post
Share on other sites
I appear to have lost all my MD2 stuff. :( Regardless, I believe this is what you're getting at. The FPS value in the MD2 data is just for your convenience - code 1000 ticks (one second) / fps to determine the length of each frame. For each animation the length of each frame is constant (although as you've noticed some animations use more FPS or less, depending).

Step 1. Find the time relative to the current frame.
Step 1b. If we're greater than the length of this frame, move to the next. Go back to Step 1.
Step 2. Say we're 40 ticks into a frame that's 100 ticks long. That's 40%
Step 3. vertices = current_frame.vertices * 0.4 + next_frame.vertices * 0.6


Hope that helps.

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