#define N_IMPLEMENTS nMD2Ipol
//------------------------------------------------------------------------------
//  nmeshnode_main.cc
//  (C) 2000..2001 RadonLabs GmbH -- A.Weissflog
//------------------------------------------------------------------------------
#include "node/nmd2ipol.h"
#include "node/nmd2.h"
#include "gfx/nvertexbuffer.h"
#include "gfx/ngfxserver.h"
#include "gfx/nscenegraph2.h"
#include "gfx/nindexbuffer.h"
#include "gfx/nchannelcontext.h"
#include "shadow/nshadowserver.h"
#include "shadow/nshadowcaster.h"
#include "math/nmath.h"
#include "kernel/ntypes.h"
#include <vector>

using namespace stlport;

const char *N_MD2_ANIMATION="ani";
//------------------------------------------------------------------------------
/**
*/
nMD2Ipol::~nMD2Ipol()
{
    // free optional shadow caster
    if (this->refShadowCaster.isvalid())
    {
        this->refShadowCaster->Release();
        this->refShadowCaster.invalidate();
    }

    // free vertex buffer
    if (this->ref_vb.isvalid()) {
        this->ref_vb->Release();
        this->ref_vb.invalidate();
    }

    // free index buffer
    if (this->ref_ibuf.isvalid()) {
        this->ref_ibuf->Release();
        this->ref_ibuf.invalidate();
    }

	// free Animations inforation
	if(this->Anims != NULL)
	{
		n_delete [] this->Anims;
	}
}

//------------------------------------------------------------------------------
/**
    Set filename of MD2 file (Quake MD2 file).

    @param  fname       filename of mesh file
*/
void 
nMD2Ipol::SetFilename(const char *fname)
{
    n_assert(fname);
    char buf[N_MAXPATH];

    // invalidate current mesh
    if (this->ref_vb.isvalid()) {
        this->ref_vb->Release();
        this->ref_vb.invalidate();
    }
    if (this->ref_ibuf.isvalid()) {
        this->ref_ibuf->Release();
        this->ref_ibuf.invalidate();
    }

    // set new mesh file name, reload will happen
    // in next frame
    getcwd(buf,sizeof(buf));
    this->rsrc_path.Set(ks->fs,fname,buf);
}

//------------------------------------------------------------------------------
/**
    Get the filename of mesh file.

    @return     filename of mesh file (can be NULL).
*/
const char *
nMD2Ipol::GetFilename(void)
{    
    return this->rsrc_path.GetPath();
}

//------------------------------------------------------------------------------
/**
    Attach mesh node to scene.

    @param  sceneGraph      pointer to scene graph object
    @return                 true if success
*/
bool 
nMD2Ipol::Attach(nSceneGraph2 *sceneGraph)
{
    n_assert(sceneGraph);

    if (nVisNode::Attach(sceneGraph)) 
	{
		// don't attach to scene if we are a readonly mesh
		if (!this->dyn_vb.GetReadOnly())
		{
			sceneGraph->AttachVisualNode(this);
		}
		return true;
    } 
    return false;
}

//------------------------------------------------------------------------------
/**
    Update myself and render to dynamic mesh, or set as current vertex
    buffer in scene graph if I'm read only

    @param  sceneGraph      pointer to scene graph object

    - 14-Feb-02   ghoust		created
	- 08-Apr-02	ghoust		cleaned
*/
void
nMD2Ipol::Compute(nSceneGraph2* sceneGraph)
{
    n_assert(sceneGraph);
    nVisNode::Compute(sceneGraph);

    // file name set?
    if (this->rsrc_path.GetPath())
    {
        // (re)-load mesh on demand
        if (!this->ref_vb.isvalid())
        {
            this->load_mesh();
        }
	}

	float tscale = this->scale;
    float min_t = 0.0f;
    float max_t = (this->m_pCurrAnim->frames/float(this->m_FPS));

    if (max_t > 0.0) 
	{
        // get current anim channel value
        nChannelContext* chnContext = sceneGraph->GetChannelContext();
        n_assert(chnContext);
        float t = chnContext->GetChannel1f(this->localChannelIndex);
	
		//new animation set so start counting from here on (so t begins always at 0.0)
		if(this->m_AnimStart < 0.0f)
			this->m_AnimStart = t + 0.0001f;

		t = t - this->m_AnimStart;//from here we did start counting so loop from this time on..

		// include animation speed modifier
		t = t * tscale;

        // handle loop/oneshot behaviour
        if (this->repType == N_REPTYPE_LOOP) 
        {
            // in loop mode, normalize on loop time
            t = t - float(floor(t/max_t)*max_t);
        }
		else // ONESHOOT
		{
			// clamp to vaild values
			if(t > (max_t))
			{
				t= max_t - 0.0001f;
			}
			else if(t < 0.0f)
			{
				t = 0.0001f;
			}	
		}

		// get the frames we are inbetween
		//int last = int(floor(t*this->m_FPS));
		int curr = int(ceil(t*this->m_FPS));

		// adjustment if we came from a totaly other animation
		if(this->m_pLastAnim != this->m_pCurrAnim)
		{
			if(curr <= 1)
				m_CurrFrame = 1;
			else
			{
				this->m_pLastAnim = this->m_pCurrAnim;
				m_LastFrame = 1;
				m_CurrFrame = curr;
			};
		}
		else
		{
			// only needed if we are realy in another frame otherwise save 1 cycle
			if(m_CurrFrame != curr)
			{
				m_LastFrame = m_CurrFrame;
				m_CurrFrame = curr;
			}
		}

		// calculate frame positions in vertexbuffer
		int frameA = 0;
		nMD2Animation *ptr = this->Anims;
		while(ptr != m_pLastAnim) 
		{
			frameA += ptr->frames;
			ptr++;
		}
		frameA += m_LastFrame -1;

		int frameB = 0;
		ptr = this->Anims;
		while(ptr != m_pCurrAnim)
		{
			frameB += ptr->frames;
			ptr++;
		}
		frameB += m_CurrFrame -1;

        // clear scene graph vertex buffer after child nodes wrote to it
        sceneGraph->SetVertexBuffer(0);

        // initialize dynamic vertex buffer if not happened yet
        if (!this->dyn_vb.IsValid()) 
        {
			n_assert(this->ref_vb.isvalid())
            this->dyn_vb.Initialize(this->ref_vb->GetVertexType(), this->m_vertices);
        
            // make sure that the vbuffers we get are bigger
            // then the source buffers, that's a compromise
            // we make to not make the code overly difficult
            if (this->m_vertices > this->dyn_vb.GetNumVertices()) 
            {
                n_printf("ERROR: source vertex buffers are greater then target vertex buffer!\n");
                n_error("Aborting.\n");
            }
        }

		// calculate lerp
		float l = t - float((floor(t/(1.0f/this->m_FPS)) / (this->m_FPS)));
		l = l/(1.0f/this->m_FPS);
   
        // generate and render interpolated vertex buffer
       this->interpolate(sceneGraph, l, frameA, frameB);

    }// end of if max_t > 0

    //sceneGraph->SetIndexBuffer(this->ref_ibuf.get());
    if (this->castShadows && this->refShadowCaster.isvalid())
    {
        sceneGraph->SetShadowCaster(this->refShadowCaster.get());
    }

}

//------------------------------------------------------------------------------
/**
    Load mesh from the file defined by SetFilename().
	First look if mesh is already loaded if not load from file.

    @return     true if success

	- 09-Apr-2002	Cleaned, splitted 
*/
bool 
nMD2Ipol::load_mesh(void)
{
    n_assert(!this->ref_vb.isvalid());
    n_assert(!this->ref_ibuf.isvalid());

    // first see if the mesh and anims are already loaded
    char rsrc_name[N_MAXPATH];
	char fanim[N_MAXPATH];

    const char *fname = this->rsrc_path.GetAbsPath();
    n_assert(fname);
    n_strncpy2(rsrc_name,fname,sizeof(rsrc_name)-3);
	n_strncpy2(fanim,fname,strlen(fname)-2);
	strcat(fanim,N_MD2_ANIMATION);

    // alter resource name based on readonly flag
    if (this->readonly) strcat(rsrc_name,"_ro");
    else                strcat(rsrc_name,"_rw");

    // if the mesh file has already been loaded by somebody
    // else, just use this one
    nVertexBuffer *vb = this->refGfx->FindVertexBuffer(rsrc_name);
    if (vb) 
    {
        this->ref_vb = vb;
        
        // find index buffer
        this->ref_ibuf = this->refGfx->FindIndexBuffer(rsrc_name);
        n_assert(this->ref_ibuf.isvalid());

        // find shadow caster
        this->refShadowCaster = this->refShadowServer->FindCaster(rsrc_name);

		if(load_anim(fanim))
		{
			this->m_vertices= vb->GetNumVertices() / this->m_totalframes;
			return true;
		}
		else
			return false;
    }
	else
	{
		if(load_raw(fname,fanim,rsrc_name))
			return true;
		else
			return false;
	}
}

//------------------------------------------------------------------------------
/**
    Model not load so far, load from files. 
	Loads animations then loads q2 raw mesh and squeezes q2 mesh into nebula mesh definition

    @return     true if success

	- 09-Apr-2002	Cleaned
*/
bool 
nMD2Ipol::load_raw(const char *wf_name, const char *an_name, const char *rsrc_name)
{
	bool writing = true;

	// open the animation file
    FILE *animfile = fopen(an_name,"r");
    if (animfile) 
    {
        load_anim(an_name);
		writing = false;
	}
	
	// open the wavefront file
    FILE *modelfile = fopen(wf_name,"rb");
    if (!modelfile) 
    {
        n_printf("Could not open mesh file '%s'\n",wf_name);
        return false;
    }

    char		g_skins[MAX_MD2SKINS][64];
    dstvert_t	base_st[MAX_VERTS];
    byte		buffer[MAX_VERTS*4+128]; // don't ask me why, legacy code
    SMD2Header	modelheader;
	
    dtriangle_t     tri;
    daliasframe_t	*out;
	
    // read header
    fread( &modelheader, 1, sizeof(modelheader), modelfile );
	
    modelheader.framesize = (int)&((daliasframe_t *)0)->verts[modelheader.num_xyz];
	
	make_index_list *m_index_list = n_new make_index_list [modelheader.num_tris];
	make_frame_list *m_frame_list = n_new make_frame_list [modelheader.num_frames];
	
	for( int i = 0; i < modelheader.num_frames; i++)
		m_frame_list[i].vertex = n_new make_vertex_list [modelheader.num_xyz];
	
	// read skin information
	fread( g_skins, 1, modelheader.num_skins * MAX_SKINNAME, modelfile );
	// read index to polymeshes
	fread( base_st, 1, modelheader.num_st * sizeof(base_st[0]), modelfile );
	
	int	max_tex_u = 0, max_tex_v = 0;

	for( i = 0; i < modelheader.num_tris; i++ ) 
	{
		// read vertices
		fread( &tri, 1, sizeof(dtriangle_t), modelfile);
		
		(m_index_list)[i].a = tri.index_xyz[2];
		(m_index_list)[i].b = tri.index_xyz[1];
		(m_index_list)[i].c = tri.index_xyz[0];
	
		// texture coordinates
		(m_index_list)[i].a_s = base_st[tri.index_st[2]].s;
		(m_index_list)[i].a_t = base_st[tri.index_st[2]].t;
		(m_index_list)[i].b_s = base_st[tri.index_st[1]].s;
		(m_index_list)[i].b_t = base_st[tri.index_st[1]].t;
		(m_index_list)[i].c_s = base_st[tri.index_st[0]].s;
		(m_index_list)[i].c_t = base_st[tri.index_st[0]].t;
		max_tex_u = n_max( max_tex_u, base_st[tri.index_st[0]].s );
		max_tex_u = n_max( max_tex_u, base_st[tri.index_st[1]].s );
		max_tex_u = n_max( max_tex_u, base_st[tri.index_st[2]].s );
		max_tex_v = n_max( max_tex_v, base_st[tri.index_st[0]].t );
		max_tex_v = n_max( max_tex_v, base_st[tri.index_st[1]].t );
		max_tex_v = n_max( max_tex_v, base_st[tri.index_st[2]].t );
	}

	// MD2 stores texture coords dependant on pixelbasis (depending on the original size of the texture)
	// we need to recalc this
	for ( i = 0; i < modelheader.num_tris; i++ ) 
	{
		m_index_list[ i ].a_s /= max_tex_u;
		m_index_list[ i ].b_s /= max_tex_u;
		m_index_list[ i ].c_s /= max_tex_u;
		m_index_list[ i ].a_t /= max_tex_v;
		m_index_list[ i ].b_t /= max_tex_v;
		m_index_list[ i ].c_t /= max_tex_v;
	}

	if(!writing)
	{
		// read vertex data of all animations
		for( i = 0; i < modelheader.num_frames; i++ ) 
		{
			out = (daliasframe_t *)buffer;
			fread( out, 1, modelheader.framesize, modelfile );

			for( int j = 0; j < modelheader.num_xyz; j++ ) 
			{
				(m_frame_list)[i].vertex[j].x = out->verts[j].v[0] * out->scale[0] + out->translate[0];
				(m_frame_list)[i].vertex[j].y = out->verts[j].v[1] * out->scale[1] + out->translate[1];
				(m_frame_list)[i].vertex[j].z = out->verts[j].v[2] * out->scale[2] + out->translate[2];
			}
		}
	}
	else // write anim file and read vertex data
	{
		vector<nMD2Animation> stlAnims;
		char curr[16] = "";
		char * pnew = NULL;
		int cnt = 0;

		// read vertex data of all animations 
		for( i = 0; i < modelheader.num_frames; i++ ) 
		{
			out = (daliasframe_t *)buffer;
			fread( out, 1, modelheader.framesize, modelfile );

			if (out->name)
			{
				n_printf(out->name);
				pnew = strpbrk(out->name, "0123456789");
				*pnew = '\0';
				if(strcmp(out->name,curr) == 0)
				{
					cnt++;
				}
				else
				{
					if(cnt != 0)
					{
						//write stuff to anim set counter to 1 and copy anim name
						nMD2Animation anim;
						strcpy(anim.name, curr);
						strcpy(curr, out->name);
						anim.frames = cnt;
						stlAnims.push_back(anim);
						cnt = 1;
					}
					else
					{
						// only in init case (first call ) sucks should do a while and first read ahead
						strcpy(curr, out->name);
						cnt++;
					}
				}
			}
				
			for( int j = 0; j < modelheader.num_xyz; j++ ) 
			{
				(m_frame_list)[i].vertex[j].x = out->verts[j].v[0] * out->scale[0] + out->translate[0];
				(m_frame_list)[i].vertex[j].y = out->verts[j].v[1] * out->scale[1] + out->translate[1];
				(m_frame_list)[i].vertex[j].z = out->verts[j].v[2] * out->scale[2] + out->translate[2];
			}
		}
		
		nMD2Animation anim;
		strcpy(anim.name, curr);
		anim.frames = cnt;
		stlAnims.push_back(anim);

		//init animations array
		if(this->Anims)
			n_delete [] this->Anims;
		this->Anims = n_new nMD2Animation[stlAnims.size()];
		n_assert(this->Anims);

		vector <nMD2Animation>::iterator iter;
		// copy animations to array
		fprintf(animfile, "%d\n",stlAnims.size());

		for(iter = stlAnims.begin(), i = 0; iter != stlAnims.end(); iter++, i++)
		{
			this->Anims[i].frames = (*iter).frames;
			strcpy(this->Anims[i].name, (*iter).name);
			fprintf(animfile, "%s %d\n", (*iter).name, (*iter).frames);
		}

		this->m_anims = stlAnims.size();
		fclose(animfile);		
	}

	fclose (modelfile);

	// init to first animation stand or idel anim recommended
	this->m_pCurrAnim = this->Anims;
	this->m_pLastAnim = this->m_pCurrAnim;
	this->m_AnimStart = -1.0f;
	this->m_LastFrame = this->Anims->frames;
	this->m_CurrFrame = 1;

    // get the vertex type (only verts, textures and colors, no normals and we also only use one color,e.g. make it up) 
    int vt;
    vt = N_VT_COORD;
    vt |= N_VT_RGBA;
    vt |= N_VT_UV0;

    readonly=true;

    // create vertex buffer
    nVBufType vbt = this->readonly ? N_VBTYPE_READONLY : N_VBTYPE_STATIC;
    // tricount * framecount * 3 to get the total number of vertices
	nVertexBuffer *vb = this->refGfx->FindVertexBuffer(rsrc_name);
    vb = this->refGfx->NewVertexBuffer(rsrc_name,vbt,vt,(modelheader.num_tris * modelheader.num_frames * 3));
    n_assert(vb);
    this->ref_vb = vb;

    // create index buffer
    this->ref_ibuf = this->refGfx->NewIndexBuffer(rsrc_name);
    nColorFormat color_format = vb->GetColorFormat();
    n_assert(this->ref_ibuf.isvalid());

	// TODO FIXME
    // create and initialize shadow caster
  /*  if (num_we > 0)
    {
        this->refShadowCaster = this->refShadowServer->NewCaster(rsrc_name);
        this->refShadowCaster->Initialize(num_v, num_we);
        this->refShadowCaster->Lock();
    }
*/
    // lock vertex buffer for writing
    if (!vb->LockVertices()) 
    {
        n_printf("nMD2Ipol::load_mesh(): nVertexBuffer::LockVertices() failed!\n");
        n_error("Aborting!\n");
    }

    // we need to create a bounding box for the mesh buffer!
    bbox3 bbox;
    int act_v    = 0;
    bbox.begin_grow();

	for ( i = 0; i < modelheader.num_frames; i++ )
	{
		for( int j = 0; j < modelheader.num_tris; j++) 
		{
			//  beware y and z are flipped in MD2 format as they use different
			//  coordinate system
			vector3 v1(m_frame_list[i].vertex[m_index_list[j].a].x,
				    m_frame_list[i].vertex[m_index_list[j].a].z,
				    m_frame_list[i].vertex[m_index_list[j].a].y);
            // grow bounding box
            bbox.grow(v1);
            // write coord to vertex buffer
            vb->Coord(act_v,v1);
            if (this->refShadowCaster.isvalid())
            {
                // write coord to shadow caster
                this->refShadowCaster->Coord(act_v, v1);
            }
			//COLOR
			if (N_COLOR_RGBA == color_format) 
            {
                vb->Color(act_v,n_f2rgba(1.0f,1.0f,1.0f,1.0f));
            } 
            else 
            {
                vb->Color(act_v,n_f2bgra(1.0f,1.0f,1.0f,1.0f));
            }
			//texture
			vector2 v2(m_index_list[j].a_s,m_index_list[j].a_t);
					vb->Uv(act_v,0,v2);
			// next vertex
			act_v++;
			// .a 's are now .b 's that's the difference
			vector3 v3(m_frame_list[i].vertex[m_index_list[j].b].x,
				    m_frame_list[i].vertex[m_index_list[j].b].z,
				    m_frame_list[i].vertex[m_index_list[j].b].y);

            // grow bounding box
            bbox.grow(v3);
            // write coord to vertex buffer
            vb->Coord(act_v,v3);
            if (this->refShadowCaster.isvalid())
            {
                // write coord to shadow caster
                this->refShadowCaster->Coord(act_v, v3);
            }
			//COLOR
			if (N_COLOR_RGBA == color_format) 
            {
                vb->Color(act_v,n_f2rgba(1.0f,1.0f,1.0f,1.0f));
            } 
            else 
            {
                vb->Color(act_v,n_f2bgra(1.0f,1.0f,1.0f,1.0f));
            }

			vector2 v4(m_index_list[j].b_s,m_index_list[j].b_t);
					vb->Uv(act_v,0,v4);

			act_v++;
				// .a 's are now .b 's that's the difference
			vector3 v5(m_frame_list[i].vertex[m_index_list[j].c].x,
				    m_frame_list[i].vertex[m_index_list[j].c].z,
				    m_frame_list[i].vertex[m_index_list[j].c].y);
            // grow bounding box
            bbox.grow(v5);
            // write coord to vertex buffer
            vb->Coord(act_v,v5);
            if (this->refShadowCaster.isvalid())
            {
                // write coord to shadow caster
                this->refShadowCaster->Coord(act_v, v5);
            }
			//COLOR
			if (N_COLOR_RGBA == color_format) 
            {
                vb->Color(act_v,n_f2rgba(1.0f,1.0f,1.0f,1.0f));
            } 
            else 
            {
                vb->Color(act_v,n_f2bgra(1.0f,1.0f,1.0f,1.0f));
            }
			//texture
			vector2 v6(m_index_list[j].c_s,m_index_list[j].c_t);
					vb->Uv(act_v,0,v6);
			act_v++;
		}
	}

	// clean up allocated stuff
	for( i = 0; i < modelheader.num_frames; i++)
		n_delete[] m_frame_list[i].vertex;

	n_delete[] m_index_list;
	n_delete[] m_frame_list;

	m_totalframes = modelheader.num_frames;
	m_vertices   = modelheader.num_tris *3;

    // as all frames will use the same index, we need only tris * 3 
	nIndexBuffer *ib = this->ref_ibuf.get();
   
    ib->Begin(N_IBTYPE_STATIC, N_PTYPE_TRIANGLE_LIST, (modelheader.num_tris * 3));
	for (i=0; i<(modelheader.num_tris * 3); i+=3) 
    {
		//need to do this to have clockwise tri's  (front facing)
        ib->Index(i,i);
		ib->Index(i+1,i+2);
		ib->Index(i+2,i+1);
    }
    ib->End();
    
    vb->UnlockVertices();
    if (this->refShadowCaster.isvalid())
    {
        this->refShadowCaster->Unlock();
    }
    vb->SetBBox(bbox);

	return true;
}

void 
nMD2Ipol::interpolate(nSceneGraph2* sceneGraph, float l, int frameA, int frameB)
{
    n_assert(sceneGraph);

     n_assert(this->ref_ibuf.isvalid());
	 n_assert(this->ref_vb.isvalid());

//	 n_printf("%d | %d\n",frameA, frameB);

    nVertexBuffer *vb0 = ref_vb.get();
    // get destination vertex buffer
    nVertexBuffer *vb2 = this->dyn_vb.Begin(
        this->ref_ibuf.get(),
        sceneGraph->GetPixelShader(),
        sceneGraph->GetTextureArray());

    // lock source buffers to gain read access
    ref_vb->LockVertices();
    
    // get the required pointers
	int i = 0;
    float *coord0;
	float *coord1;
	float *coord2;
    ulong *color0;
	ulong *color2;
    float *uv00;
	float *uv01;
	float *uv02;

	coord0 = vb0->coord_ptr + (frameA * m_vertices * (vb0->stride4));
	coord1 = vb0->coord_ptr + (frameB * m_vertices * (vb0->stride4));
    coord2 = vb2->coord_ptr;

    color0 = vb0->color_ptr + (frameA * m_vertices * (vb0->stride4));
	color2 = vb2->color_ptr;

	uv00 = vb0->uv_ptr[0] + (frameA * m_vertices * (vb0->stride4));
	uv01 = vb0->uv_ptr[0] + (frameB * m_vertices * (vb0->stride4));
	uv02 = vb2->uv_ptr[0];
  
    // the destination buffer must be written continously, because
    // it may be placed in uncached memory (AGP or video mem)
    // it may actually be better to keep coords, norms, etc 
    // in separate arrays -> DX8!
    //int num_v = vb0->GetNumVertices();
    for (i=0; i<m_vertices; i++) 
    {

		coord2[0] = coord0[0] + ((coord1[0]-coord0[0])*l);
		coord2[1] = coord0[1] + ((coord1[1]-coord0[1])*l);
		coord2[2] = coord0[2] + ((coord1[2]-coord0[2])*l);

		coord0 += vb0->stride4;
		coord1 += vb0->stride4;
		coord2 += vb2->stride4;

        color2 = color0;// colors are all the same this could be chnaged in future
        color0 += vb0->stride4;
        color2 += vb2->stride4;

        uv02[0] = uv00[0] + ((uv01[0]-uv00[0])*l);
        uv02[1] = uv00[1] + ((uv01[1]-uv00[1])*l);

        uv00 += vb0->stride4;
		uv01 += vb0->stride4;
        uv02 += vb2->stride4;
    }
	// do NOT lerp bounding box, our bounding box is biggest anim possible all the time
    bbox3 vb2_bbox(ref_vb->GetBBox().vmin, ref_vb->GetBBox().vmax);
    vb2->SetBBox(vb2_bbox);

    // unlock source vertex buffers, and render dynamic vertex buffer
    ref_vb->UnlockVertices();
    //vb0->UnlockVertices();
    this->dyn_vb.End(m_vertices, this->ref_ibuf->GetNumIndices());

    // if we are readonly, attach vertex buffer to current scene graph node
    if (this->dyn_vb.GetReadOnly())
    {
        sceneGraph->SetVertexBuffer(vb2);
    }
    else
    {
        sceneGraph->SetVertexBuffer(0);
    }
}


//------------------------------------------------------------------------------
/**
    Set name of current animation

    @param     name of animation  
*/
void
nMD2Ipol::SetAnimation(const char *str)
{
    n_assert(str);
	if(this->Anims == NULL)
	{
		const char *fname = this->rsrc_path.GetAbsPath();
		if(fname)
		{
			char rsrc_anim[N_MAXPATH];
			n_assert(fname);
			n_strncpy2(rsrc_anim,fname,strlen(fname)-2);
			strcat(rsrc_anim,N_MD2_ANIMATION);
			this->load_anim(rsrc_anim);
		}
		else
			return;
	}

	n_assert(this->Anims);
	for(int i = 0; i < this->m_anims; i++)
	{
		if(strcmp(this->Anims[i].name, str)== 0)
		{
			// save old values
			this->m_pLastAnim = this->m_pCurrAnim;
			this->m_LastFrame = this->m_CurrFrame;
			// paste new values
			this->m_pCurrAnim = &(this->Anims[i]);
			//lastframe and currfarme need no handling

			break;
		}
	}

    this->m_AnimStart = -1.0f;

	return;

}

//------------------------------------------------------------------------------
/**
    Loads the animations of a q2 model from a file.
	This file will be created by the first load if not already present, otherwise you can
	provide one. Be sure to match 
	Format is plain ascii:
	First line is the number of lines (number of animations)\n
	As many lines as defined with a description of the animation and the framecount. The name must
	not exceed 16 chars. <br>
	TODO: allow loading of a q3 similar definition (loop definition)
	<pre>
	Example file with 2 anims (stand and walk and their framecounts):

	2
	stand 30
	walk 5
	</pre>


    @param     name of animation  
*/
bool nMD2Ipol::load_anim(const char * fname)
{
	FILE *animfile = fopen(fname,"r");
    if (animfile) 
    {
		//total anims present
		fscanf(animfile, "%d\n", &(this->m_anims));

		if((this->Anims))
			n_delete [] this->Anims;

		n_assert(this->m_anims > 0);

		this->Anims = n_new nMD2Animation[(this->m_anims)];//this->m_anims];
		n_assert(this->Anims);

		this->m_totalframes = 0;

		for(int i=0; i < (this->m_anims); i++)
		{
			fscanf(animfile, "%s %d\n", &(this->Anims[i].name), &(this->Anims[i].frames));
			this->m_totalframes += this->Anims[i].frames;
		}
		fclose(animfile);

		return true;
	}
	else
	{
		return false;
	}

}
//-------------------------------------------------------------------
//  EOF
//-------------------------------------------------------------------







                

                
