#include "md3.h"


extern GLfloat	LightPosition[];

extern PFNGLMULTITEXCOORD1FARBPROC	glMultiTexCoord1fARB;
extern PFNGLMULTITEXCOORD2FARBPROC	glMultiTexCoord2fARB;
extern PFNGLACTIVETEXTUREARBPROC	glActiveTextureARB;

extern bool multitextureSupported;


#define MAT( p, row, col ) (p)[((col)*3)+(row)]
#define MATGL( p, row, col ) (p)[((row)*3)+(col)]
#define X 0
#define Y 1
#define Z 2
#define W 3
#define DELTA 0.1

typedef enum { qfalse, qtrue } qboolean;

char		com_token[1024];
qboolean	com_eof;

//extern void Sys_Printf (char *text, ...);
//extern void Sys_ClearPrintf (void);
//extern void Sys_SetTitle (char *text, ...);

// table normals
//extern float	anorms[256][256][3];
float	anorms[256][256][3];

extern atexture		*texture[MAX_TEXTURES];

extern ascreenmode screen_mode;

// animations
char animString[40][MAX_ANIMATIONS] = {
	"BOTH_DEATH1",
	"BOTH_DEAD1",
	"BOTH_DEATH2",
	"BOTH_DEAD2",
	"BOTH_DEATH3",
	"BOTH_DEAD3",

	"TORSO_GESTURE",

	"TORSO_ATTACK",
	"TORSO_ATTACK2",

	"TORSO_DROP",
	"TORSO_RAISE",

	"TORSO_STAND",
	"TORSO_STAND2",

	"LEGS_WALKCR",
	"LEGS_WALK",
	"LEGS_RUN",
	"LEGS_BACK",
	"LEGS_SWIM",

	"LEGS_JUMP",
	"LEGS_LAND",

	"LEGS_JUMPB",
	"LEGS_LANDB",

	"LEGS_IDLE",
	"LEGS_IDLECR",

	"LEGS_TURN",

	"MAX_ANIMATIONS"
};

int CheckFile(char *filename)
{
	FILE *f;

	f = fopen(filename, "rb");

	if (f)
	{
		fclose(f);
		return 1;
	}
	else return 0;
}


char *GetAnimName(int a)
{
	return animString[a];
}


int ParsNum(const char *str)
{
	while (*str)
	{
		if (*str < '0' || *str > '9') return 1;
		str++;
	}

	return 1;
}

char *COM_Parse (char *data)
{
	int		c;
	int		len;
	
	len = 0;
	com_token[0] = 0;
	
	if (!data)
		return NULL;
		
// skip whitespace
skipwhite:
	while ( (c = *data) <= ' ')
	{
		if (c == 0)
		{
			com_eof = qtrue;
			return NULL;			// end of file;
		}
		data++;
	}
	
// skip // comments
	if (c=='/' && data[1] == '/')
	{
		while (*data && *data != '\n')
			data++;
		goto skipwhite;
	}
	

// handle quoted strings specially
	if (c == '\"')
	{
		data++;
		do
		{
			c = *data++;
			if (c=='\"')
			{
				com_token[len] = 0;
				return data;
			}
			com_token[len] = c;
			len++;
		} while (1);
	}

// parse single characters
	if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':' || c==',' || c=='/')
	{
		com_token[len] = c;
		len++;
		com_token[len] = 0;
		return data+1;
	}

// parse a regular word
	do
	{
		com_token[len] = c;
		data++;
		len++;
		c = *data;
	if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':' || c==',' || c=='/')
			break;
	} while (c>32);
	
	com_token[len] = 0;
	return data;
}


int initmd3(md3_model *model)
{
	double alpha, beta;
    float a, b, c;
	int i, j;

	for (i=0; i<256; i++)
	for (j=0; j<256; j++)
	{
		alpha = i * 2.0 * M_PI / 255.0;
		beta = j * 2.0 * M_PI / 255.0;
		a = (float) (cos(beta) * sin(alpha));
		b = (float) (sin(beta) * sin(alpha));
		c = (float) cos(alpha);

		anorms[i][j][0] = a;
		anorms[i][j][1] = b;
		anorms[i][j][2] = c;
	}

	setallmeshestexure(model,0);
	
	model->frame = 0;
	model->nextframe = 0;

	model->fps = 10;	//	frames per second

	model->startframe = 0;
	model->endframe = 0;

	model->tex_nr = 0;
	return true;
}

int destroymd3(md3_model *model)
{
	int i;

	if (model->md3name) free(model->md3name);

	if (model->boneframes) free(model->boneframes);

	if (model->tags) free(model->tags);
	
	for (i=0; i<model->header.numTags; i++)
		if (model->links[i]) model->links[i] = NULL;

	for (i=0; i<model->header.numMeshes; i++)
	{

		if (model->meshes[ i ].skins) free(model->meshes[ i ].skins);

		if (model->meshes[ i ].triangles) free(model->meshes[ i ].triangles);
		
		if (model->meshes[ i ].tex_coords) free(model->meshes[ i ].tex_coords);
		
		if (model->meshes[ i ].vertices) free(model->meshes[ i ].vertices);
	}
	
	free(model->meshes);

	return true;
}

/*
===============
LoadModel

return:
 1 : all is Ok
-1 : can't find file
-2 : bad header for md3 file
===============
*/
int loadmd3model(md3_model *model,char *filename)
{
	FILE			*md3file;
	int				i;

	// check if file exist
	if ( CheckFile(filename) == 0)
		return -1;

	// open file
	md3file = fopen(filename, "rb");

	// copy name
	model->md3name = (char *) malloc(strlen(filename));
	strcpy(model->md3name, filename);

//
//	read header
//
	fread(&model->header, 1, sizeof(md3_header_t), md3file);

	static char ver[5];
	sprintf(ver, "%c%c%c%c", model->header.id[0], model->header.id[1], model->header.id[2], model->header.id[3]);

	if ( strcmp(ver, "IDP3") || model->header.version != 15)
	{
		fclose(md3file);
		return -2;
	}
	
//
//	read boneframes
//
	model->boneframes = (boneframe_t *) malloc(sizeof(boneframe_t) * model->header.numBoneFrames);
	fread(model->boneframes, sizeof(boneframe_t), model->header.numBoneFrames, md3file);

//
//	read tags
//
	model->tags = (tag_t *) malloc(sizeof(tag_t) * model->header.numBoneFrames * model->header.numTags);
	fread(model->tags, sizeof(tag_t), model->header.numBoneFrames * model->header.numTags, md3file);
		
//
//	init links
//
	model->links =(void**) ( (md3_model **) malloc(sizeof(md3_model) * model->header.numTags) );
	
	for (i=0; i<model->header.numTags; i++)
		model->links[i] = NULL;

//
//	read meshes
//
	model->meshes = (mesh_t *) malloc(sizeof(mesh_t) * model->header.numMeshes);
		
	long mesh_offset = ftell(md3file);

	for (i=0; i<model->header.numMeshes; i++)
	{
		fseek(md3file, mesh_offset, SEEK_SET);
		fread(&model->meshes[ i ].mesh_h, sizeof(mesh_header_t), 1, md3file);

		// ------------------------------------------
		model->meshes[ i ].skins = (skin_t *) malloc(sizeof(skin_t) * model->meshes[ i ].mesh_h.numSkins);
		fread(model->meshes[ i ].skins, sizeof(skin_t), model->meshes[ i ].mesh_h.numSkins, md3file);

		// ------------------------------------------
		fseek(md3file, mesh_offset+model->meshes[ i ].mesh_h.tri_start, SEEK_SET);
		model->meshes[ i ].triangles = (triangle_t *) malloc(sizeof(triangle_t) * model->meshes[ i ].mesh_h.numTriangles);
		fread(model->meshes[ i ].triangles, sizeof(triangle_t), model->meshes[ i ].mesh_h.numTriangles, md3file);

		// ------------------------------------------			
		fseek(md3file, mesh_offset+model->meshes[ i ].mesh_h.texvec_start, SEEK_SET);
		model->meshes[ i ].tex_coords = (tex_coord_t *) malloc(sizeof(tex_coord_t) * model->meshes[ i ].mesh_h.numVertexs);
		fread(model->meshes[ i ].tex_coords, sizeof(tex_coord_t), model->meshes[ i ].mesh_h.numVertexs, md3file);

		// ------------------------------------------			
		fseek(md3file, mesh_offset+model->meshes[ i ].mesh_h.vertex_start, SEEK_SET);
		model->meshes[ i ].vertices = (vertice_t *) malloc(sizeof(vertice_t) * model->meshes[ i ].mesh_h.numVertexs * model->meshes[ i ].mesh_h.numMeshFrames);
		fread(model->meshes[ i ].vertices, sizeof(vertice_t), model->meshes[ i ].mesh_h.numMeshFrames * model->meshes[ i ].mesh_h.numVertexs, md3file);

		mesh_offset += model->meshes[ i ].mesh_h.meshsize;
	}

	// close file
	fclose(md3file);

	// warning: the last frame for the quake3 model is header.numBoneFrames - 1
	model->header.numBoneFrames -= 1;

	// set the start, end frame
	model->startframe = 0;
	model->endframe = model->header.numBoneFrames;

	return 1;
}

/*
===============
DumpInfo

Dump all quake3 model info
===============
*/
void md3infodump(md3_model *model)
{
	int i, j;
	// int n;

	console("----dump %s ---", model->md3name);

	console("id: %c%c%c%c", model->header.id[0], model->header.id[1], model->header.id[2], model->header.id[3]);
	console("version: %d", model->header.version);
	console("filename: %s", model->header.filename);

	console("numBoneFrames: %d", model->header.numBoneFrames);
	console("numTags: %d", model->header.numTags);
	console("numMeshes: %d", model->header.numMeshes);
	console("numMaxSkins: %d", model->header.numMaxSkins);
	console("headerlength: %d", model->header.headerlength);
	console("tag_start: %d", model->header.tag_start);
	console("tag_end: %d", model->header.tag_end);
	console("filesize: %d", model->header.filesize);
/*	
	for (i=0; i<header.numBoneFrames; i++)
	 Sys_Printf("mins: %f %f %f maxs: %f %f %f position: %f %f %f scale: %f creator: %s\n", \
		boneframes[ i ].mins[0], boneframes[ i ].mins[1], boneframes[ i ].mins[2], \
		boneframes[ i ].maxs[0], boneframes[ i ].maxs[1], boneframes[ i ].maxs[2], \
		boneframes[ i ].position[0], boneframes[ i ].position[1], boneframes[ i ].position[2], \
	  boneframes[ i ].scale, \
	  boneframes[ i ].creator);

	for (i=0; i<header.numBoneFrames; i++)
	for (j=0; j<header.numTags; j++)
	 Sys_Printf("name: %s position: %.3f %.3f %.3f ratation: ...\n", \
		tags[ i * header.numTags + j ].name, \
		tags[ i * header.numTags + j ].position[0], tags[ i * header.numTags + j ].position[1], tags[ i * header.numTags + j ].position[2]);
*/
	for (i=0; i<model->header.numMeshes; i++)
	{
		console("mesh %d --------------------", i);

		console("id: %s", model->meshes[ i ].mesh_h.id);
		console("name: %s", model->meshes[ i ].mesh_h.name);
			
		console("numMeshFrames: %d", model->meshes[ i ].mesh_h.numMeshFrames);
		console("numSkins: %d", model->meshes[ i ].mesh_h.numSkins);
		console("numVertexs: %d", model->meshes[ i ].mesh_h.numVertexs);
		console("numTriangles: %d", model->meshes[ i ].mesh_h.numTriangles);

		console("tri_start: %d", model->meshes[ i ].mesh_h.tri_start);
		console("headersize: %d", model->meshes[ i ].mesh_h.headersize);
		console("texvec_start: %d", model->meshes[ i ].mesh_h.texvec_start);
		console("vertex_start: %d", model->meshes[ i ].mesh_h.vertex_start);
		console("meshsize: %d", model->meshes[ i ].mesh_h.meshsize);

		for (j=0; j<model->meshes[ i ].mesh_h.numSkins; j++)
		 console("skin.name: %s", model->meshes[ i ].skins[ j ].name);
/*
		for (j=0; j<meshes[ i ].mesh_h.numTriangles; j++)
		 Sys_Printf("triangle.vertex: %d %d %d\n", \
			meshes[ i ].triangles[ j ].vertex[ 0 ], meshes[ i ].triangles[ j ].vertex[ 1 ], meshes[ i ].triangles[ j ].vertex[ 2 ]);

		for (j=0; j<meshes[ i ].mesh_h.numVertexs; j++)
		 Sys_Printf("tex_coord.texvec: %f %f\n", \
			meshes[ i ].tex_coords[ j ].texvec[ 0 ], meshes[ i ].tex_coords[ j ].texvec[ 1 ]);

		for (n=0; n<meshes[ i ].mesh_h.numMeshFrames; n++)
		for (j=0; j<meshes[ i ].mesh_h.numVertexs; j++)
		 Sys_Printf("vertices.Vec: %f %f %f\n", \
			meshes[ i ].vertices[ n * meshes[ i ].mesh_h.numVertexs + j].Vec[ 0 ] / 64.f, \
			meshes[ i ].vertices[ n * meshes[ i ].mesh_h.numVertexs + j].Vec[ 1 ] / 64.f, \
			meshes[ i ].vertices[ n * meshes[ i ].mesh_h.numVertexs + j].Vec[ 2 ] / 64.f);
*/
	}

}

/*
===============
DrawModelFrame

Draw only one frame (curFrame)
===============
*/
float vec_Dot (float *u, float *v)
{
	return u[0]*v[0] + u[1]*v[1] + u[2]*v[2];
}


void vec_Normalise (float *vert)
{
	float len = (float)sqrt (vec_Dot (vert, vert));
	vert[0] /= len;
	vert[1] /= len;
	vert[2] /= len;
}



void drawmodelframe(md3_model *model,int curFrame)
{
	int i, j, o, tri_num, cur_mesh, cur_offset_vertex, cur_vertex;
	int au, av;
	float v[3], s, t;
	float n[3];

	for (o=0; o<model->header.numMeshes; o++)
	{
		cur_mesh = o;
		cur_offset_vertex = curFrame * model->meshes[ cur_mesh ].mesh_h.numVertexs;
		
		tri_num = model->meshes[ cur_mesh ].mesh_h.numTriangles;

		
		//glBindTexture(GL_TEXTURE_2D,texture[0]->data);
		if (model->meshes[ o ].texture_n!=0)
			if (texture[model->meshes[ o ].texture_n]!=NULL)
			{
				if (multitextureSupported)
				{
					glActiveTextureARB(GL_TEXTURE0_ARB);
					glBindTexture(GL_TEXTURE_2D,texture[model->meshes[ o ].texture_n]->data);
				} else
					glBindTexture(GL_TEXTURE_2D,texture[model->meshes[ o ].texture_n]->data);
			}

		for (i=0; i<tri_num; i++)
		{
			glBegin(GL_TRIANGLES);

			for (j=0; j<3; j++)
			{
				cur_vertex = model->meshes[ cur_mesh ].triangles[ i ].vertex[ j ];

				// VERTEXS ----------------------------------
				v[0] = (float) model->meshes[ cur_mesh ].vertices[ cur_offset_vertex + cur_vertex ].Vec[ 0 ] / 64.f;
				v[1] = (float) model->meshes[ cur_mesh ].vertices[ cur_offset_vertex + cur_vertex ].Vec[ 1 ] / 64.f;
				v[2] = (float) model->meshes[ cur_mesh ].vertices[ cur_offset_vertex + cur_vertex ].Vec[ 2 ] / 64.f;

				// NORMALS ----------------------------------
				au = model->meshes[ cur_mesh ].vertices[ cur_offset_vertex + cur_vertex ].Normal[0];
				av = model->meshes[ cur_mesh ].vertices[ cur_offset_vertex + cur_vertex ].Normal[1];

				n[0] = anorms[au][av][0];
				n[1] = anorms[au][av][1];
				n[2] = anorms[au][av][2];
				
				s = (float) model->meshes[ cur_mesh ].tex_coords[ cur_vertex ].texvec[ 0 ];
				t = (float) model->meshes[ cur_mesh ].tex_coords[ cur_vertex ].texvec[ 1 ];


				if (multitextureSupported)
					glMultiTexCoord2fARB(GL_TEXTURE0_ARB,s,t);
				else
					glTexCoord2f(s, t);


				glNormal3f(n[0], n[1], n[2]);
				

				//vec_Normalise (bv); <--- have n instead
				//float cv = vec_Dot(n, eye); //eye is our light instead
				vector eye={LightPosition[0],LightPosition[1],LightPosition[2]};
				vec_Normalise(eye);
				float cv=0;
				cv=vec_Dot(n, eye); 
				cv = cv < 0 ? 0 : cv;
				//cv=1.f-cv;
				
				if (screen_mode.cell_shade)
					if (multitextureSupported)
						glMultiTexCoord1fARB(GL_TEXTURE1_ARB,cv);
					else
						glTexCoord1f(cv);
				//console("%.3f",cv);
				

				/*if (screen_mode.cell_shade)
				{
					if (cv==1.0f)
						glColor3f(1.0f,1.0f,1.0f);
					else
						glColor3f(0.1f,0.1f,0.1f);
				}*/
				
				//glColor3f(cv,cv,cv);
				
				glVertex3f(v[0], v[1], v[2]);
			}
		
			glEnd();
		}
/*
		// Draw normals for DEBUG ----------------------------------
		glColor3f(0, 1, 0);
		for (i=0; i<tri_num; i++)
		{
			for (j=0; j<3; j++)
			{
				glBegin(GL_LINES);
				cur_vertex = meshes[ cur_mesh ].triangles[ i ].vertex[ j ];

				v[0] = (float) meshes[ cur_mesh ].vertices[ cur_offset_vertex + cur_vertex ].Vec[ 0 ] / 64.f;
				v[1] = (float) meshes[ cur_mesh ].vertices[ cur_offset_vertex + cur_vertex ].Vec[ 1 ] / 64.f;
				v[2] = (float) meshes[ cur_mesh ].vertices[ cur_offset_vertex + cur_vertex ].Vec[ 2 ] / 64.f;

				au = meshes[ cur_mesh ].vertices[ cur_offset_vertex + cur_vertex ].Normal[0];
				av = meshes[ cur_mesh ].vertices[ cur_offset_vertex + cur_vertex ].Normal[1];

				n[0] = anorms[au][av][0];
				n[1] = anorms[au][av][1];
				n[2] = anorms[au][av][2];

				glVertex3f(v[0], v[1], v[2]);
				glVertex3f(v[0]+n[0], v[1]+n[1], v[2]+n[2]);
				glEnd();
			}
		
		}
		glColor3f(1, 1, 1);
*/
	}
}

/*
===============
DrawModelItp

Draw interpolated frame (curFrame -> nextFrame, pol=[0..1])
===============
*/

void drawiltpframe(md3_model *model,int curFrame, int nextFrame, float pol)
{
	int i, j, o, tri_num, cur_mesh, cur_offset_vertex, cur_vertex;
	int au, av;
	float v[3], s, t;
	float n[3];

	int next_cur_offset_vertex;
	float next_v[3];
	float next_n[3];

	for (o=0; o<model->header.numMeshes; o++)
	{
		cur_mesh = o;
		cur_offset_vertex = curFrame * model->meshes[ cur_mesh ].mesh_h.numVertexs;



		// !interpolation
		next_cur_offset_vertex = nextFrame * model->meshes[ cur_mesh ].mesh_h.numVertexs;
		
		tri_num = model->meshes[ cur_mesh ].mesh_h.numTriangles;

		
		//gg
		
		glBindTexture(GL_TEXTURE_2D,texture[0]->data);
		if (model->meshes[ o ].texture_n!=0)
			if (texture[model->meshes[ o ].texture_n]!=NULL)
				glBindTexture(GL_TEXTURE_2D,texture[model->meshes[ o ].texture_n]->data);
				
	

		for (i=0; i<tri_num; i++)
		{
			glBegin(GL_TRIANGLES);

			for (j=0; j<3; j++)
			{
				cur_vertex = model->meshes[ cur_mesh ].triangles[ i ].vertex[ j ];

				// VERTEXS ----------------------------------
				v[0] = (float) model->meshes[ cur_mesh ].vertices[ cur_offset_vertex + cur_vertex ].Vec[ 0 ] / 64.f;
				v[1] = (float) model->meshes[ cur_mesh ].vertices[ cur_offset_vertex + cur_vertex ].Vec[ 1 ] / 64.f;
				v[2] = (float) model->meshes[ cur_mesh ].vertices[ cur_offset_vertex + cur_vertex ].Vec[ 2 ] / 64.f;
		
				// !interpolation
				next_v[0] = (float) model->meshes[ cur_mesh ].vertices[ next_cur_offset_vertex + cur_vertex ].Vec[ 0 ] / 64.f;
				next_v[1] = (float) model->meshes[ cur_mesh ].vertices[ next_cur_offset_vertex + cur_vertex ].Vec[ 1 ] / 64.f;
				next_v[2] = (float) model->meshes[ cur_mesh ].vertices[ next_cur_offset_vertex + cur_vertex ].Vec[ 2 ] / 64.f;

				// NORMALS ----------------------------------
				au = model->meshes[ cur_mesh ].vertices[ cur_offset_vertex + cur_vertex ].Normal[0];
				av = model->meshes[ cur_mesh ].vertices[ cur_offset_vertex + cur_vertex ].Normal[1];

				n[0] = anorms[au][av][0];
				n[1] = anorms[au][av][1];
				n[2] = anorms[au][av][2];

				// !interpolation
				au = model->meshes[ cur_mesh ].vertices[ next_cur_offset_vertex + cur_vertex ].Normal[0];
				av = model->meshes[ cur_mesh ].vertices[ next_cur_offset_vertex + cur_vertex ].Normal[1];

				next_n[0] = anorms[au][av][0];
				next_n[1] = anorms[au][av][1];
				next_n[2] = anorms[au][av][2];

				s = (float) model->meshes[ cur_mesh ].tex_coords[ cur_vertex ].texvec[ 0 ];
				t = (float) model->meshes[ cur_mesh ].tex_coords[ cur_vertex ].texvec[ 1 ];

				glTexCoord2f(s, t);
				
				// !interpolation
				glNormal3f
					(
					n[0] + pol * (next_n[0] - n[0]),
					n[1] + pol * (next_n[1] - n[1]),
					n[2] + pol * (next_n[2] - n[2])
					);

				//vec_Normalise (bv); <--- have n instead
				//float cv = vec_Dot(n, eye); //eye is our light instead
				vector eye={LightPosition[0],LightPosition[1],LightPosition[2]};
				vec_Normalise(eye);
				float cv=0;
				vector new_n={n[0] + pol * (next_n[0] - n[0]),n[1] + pol * (next_n[1] - n[1]),n[2] + pol * (next_n[2] - n[2])};
				cv=vec_Dot(new_n, eye); 
				cv = cv < 0 ? 0 : cv;
				cv=1.f-cv;
				
				if (screen_mode.cell_shade)
					glTexCoord1f(cv);
				/*
				if (screen_mode.cell_shade)
				{
					if (cv==1.0f)
						glColor3f(1.0f,1.0f,1.0f);
					else
						glColor3f(0.1f,0.1f,0.1f);
				}
				*/
				glColor3f(cv,cv,cv);


				// !interpolation
				glVertex3f
					(
					v[0] + pol * (next_v[0] - v[0]),
					v[1] + pol * (next_v[1] - v[1]),
					v[2] + pol * (next_v[2] - v[2])
					);
			}
		
			glEnd();
		}
	}
}

/*
===============
DrawModel

Draw model, function use frame, nextframe, fps, newTime, oldTime
===============
*/
void drawmodel(md3_model *model)
{
	drawmodelframe(model,model->frame);
	//drawiltpframe(model,model->frame, model->nextframe, model->fps*(model->newTime-model->oldTime));
}


/*
===============
UpdateFrameTime


Update time and frame for curent model

startframe	- start frame
endframe	- end frame
frame		- curent frame to draw
nextframe	- nextframe to draw (only if interpolated)
===============
*/
void updateframetime(md3_model *model,float time)
{
	model->newTime = time;

	if (model->newTime-model->oldTime>1./model->fps)
	{
		model->frame = model->nextframe;
		model->nextframe += 1;
		
		if (model->nextframe > model->endframe) 
			model->nextframe=model->startframe;

		model->oldTime = model->newTime;
	}
}

void quat_from_matrix( float *quat, float *m )
{
	float  tr, s, q[4];
	int    i, j, k;
	int    nxt[3] = {1, 2, 0};  

	tr = MAT(m, 0, 0) + MAT(m, 1, 1) + MAT(m, 2, 2);

	// check the diagonal
	if (tr > 0.0)
	{
		s = (float)sqrt (tr + 1.f);
		quat[W] = s / 2.0f;
		s = 0.5f / s;
		quat[X] = (MAT(m,1,2) - MAT(m,2,1)) * s;
		quat[Y] = (MAT(m,2,0) - MAT(m,0,2)) * s;
		quat[Z] = (MAT(m,0,1) - MAT(m,1,0)) * s;
	} 
	else
	{                
		// diagonal is negative
		i = 0;
		if (MAT(m,1,1) > MAT(m,0,0)) i = 1;
		if (MAT(m,2,2) > MAT(m,i,i)) i = 2;
		j = nxt[i];
		k = nxt[j];

	    s = (float)sqrt ((MAT(m,i,i) - (MAT(m,j,j) + MAT(m,k,k))) + 1.0);
                       
		q[i] = s * (float)0.5;
                             
		if (s != 0.0f) s = 0.5f / s;

		q[3] = (MAT(m,j,k) - MAT(m,k,j)) * s;
		q[j] = (MAT(m,i,j) + MAT(m,j,i)) * s;
		q[k] = (MAT(m,i,k) + MAT(m,k,i)) * s;

		quat[X] = q[0];
		quat[Y] = q[1];
		quat[Z] = q[2];
		quat[W] = q[3];
	}
}

void quat_slerp(float *from, float *to, float t, float *res)
{
	float           to1[4];
	float        omega, cosom, sinom, scale0, scale1;

	// calc cosine
	cosom = from[X]*to[X] + from[Y]*to[Y] + from[Z]*to[Z] + from[W]*to[W];

	// adjust signs (if necessary)
	if ( cosom <0.0 )
	{ 
		  cosom = -cosom; 
		  to1[0] = - to[X];
          to1[1] = - to[Y];
          to1[2] = - to[Z];
          to1[3] = - to[W];
	} 
	else 
	{
          to1[0] = to[X];
          to1[1] = to[Y];
          to1[2] = to[Z];
          to1[3] = to[W];
	}

	 // calculate coefficients
	if ( (1.0 - cosom) > DELTA )
	{
          // standard case (slerp)
          omega = (float)acos(cosom);
          sinom = (float)sin(omega);
          scale0 = (float)sin((1.0 - t) * omega) / sinom;
          scale1 = (float)sin(t * omega) / sinom;

	} 
	else
	{        
      // "from" and "to" quaternions are very close 
      //  ... so we can do a linear interpolation
          scale0 = 1.0f - t;
          scale1 = t;
	}

	// calculate final values
	res[X] = scale0 * from[X] + scale1 * to1[0];
	res[Y] = scale0 * from[Y] + scale1 * to1[1];
	res[Z] = scale0 * from[Z] + scale1 * to1[2];
	res[W] = scale0 * from[W] + scale1 * to1[3];
}

void matrix_from_quat( float *m, float *quat )
{
	float wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2;
             
	// calculate coefficients
    
	x2 = quat[X] + quat[X]; 
	y2 = quat[Y] + quat[Y]; 
	z2 = quat[Z] + quat[Z];
	xx = quat[X] * x2;   xy = quat[X] * y2;   xz = quat[X] * z2;
	yy = quat[Y] * y2;   yz = quat[Y] * z2;   zz = quat[Z] * z2;
	wx = quat[W] * x2;   wy = quat[W] * y2;   wz = quat[W] * z2;

	MAT(m,0,0) = 1.f - (yy + zz);    MAT(m,0,1) = xy - wz;
	MAT(m,0,2) = xz + wy;            //MAT(m,0,3) = 0.0;
                  
	MAT(m,1,0) = xy + wz;            MAT(m,1,1) = 1.f - (xx + zz);
	MAT(m,1,2) = yz - wx;            //MAT(m,1,3) = 0.0;

	MAT(m,2,0) = xz - wy;            MAT(m,2,1) = yz + wx;
	MAT(m,2,2) = 1.f - (xx + yy);    //MAT(m,2,3) = 0.f;

	//MAT(m,3,0) = 0;                  MAT(m,3,1) = 0;
	//MAT(m,3,2) = 0;                  MAT(m,3,3) = 1.f;         
}

/*
===============
DrawSkeleton

Draw curent model (mod) and all his links if exist
===============
*/

void drawskeleton(md3_model *model)
{
	int i; float pol;
	void *md3link;
		
	float m[16], quat1[4], quat2[4], resQuat[4], fm[9];
	float *position, *matrix, *imatrix, *next_position, *next_matrix;

	drawmodel(model);

	for (i=0; i<model->header.numTags; i++)
	{
		md3link = model->links[i];
		
		if (md3link!=NULL)
		{
			pol = model->fps*(model->newTime-model->oldTime);

			position = model->tags[ model->frame * model->header.numTags + i].position;
			matrix = &model->tags[ model->frame * model->header.numTags + i].rotation[0][0];

			next_position = model->tags[ model->nextframe * model->header.numTags + i].position;
			next_matrix = &model->tags[ model->nextframe * model->header.numTags + i].rotation[0][0];

			quat_from_matrix( quat1, matrix );
			quat_from_matrix( quat2, next_matrix );
			quat_slerp( quat1, quat2, pol, resQuat );
			matrix_from_quat( fm, resQuat );
			imatrix = fm;
				
			m[0] = MATGL(imatrix,0,0); m[4] = MATGL(imatrix,0,1); m[8] = MATGL(imatrix,0,2); m[12] = position[0];
			m[1] = MATGL(imatrix,1,0); m[5] = MATGL(imatrix,1,1); m[9] = MATGL(imatrix,1,2); m[13] = position[1];
			m[2] = MATGL(imatrix,2,0); m[6] = MATGL(imatrix,2,1); m[10]= MATGL(imatrix,2,2); m[14] = position[2];
			m[3] = 0;               m[7] = 0;               m[11]= 0;               m[15] = 1;

			m[12] = position[0] + pol * (next_position[0] - position[0]);
			m[13] = position[1] + pol * (next_position[1] - position[1]);
			m[14] = position[2] + pol * (next_position[2] - position[2]);
			
			glPushMatrix();
			glMultMatrixf(m);
			drawskeleton((md3_model*)md3link);
			glPopMatrix();
		}

	}
}

/*
===============
SetMesh_Texture

Set texture for a mesh (meshname)
===============
*/


int setmeshtexture(md3_model *model,char *meshname,int tex_id)
{
	int i;

	for (i=0; i<model->header.numMeshes; i++)
	{
		if ( !strcmp(model->meshes[ i ].mesh_h.name, meshname) ) 
		{
			console("mesh %s set to texture %d",meshname,tex_id);
			model->meshes[ i ].texture_n=tex_id;
			return i;
		}
	}

	return -1;
}

/*
===============
SetAllMeshes_Texure

Set the same texture for all the meshes
===============
*/


void setallmeshestexure(md3_model *model,int tex_id)
{
	for (int i=0; i<model->header.numMeshes; i++)
	{
		console("mesh %d: %s",i,model->meshes[ i ].mesh_h.name);
		model->meshes[ i ].texture_n=tex_id;
	}
}

/*
===============
LinkModel

Link model to tag ("tag_weapon" to model weapon)
===============
*/
int linkmodel(md3_model *model,char *tagname,md3_model *md3link)
{
	int i;

	for (i=0; i<model->header.numTags; i++)
		if ( !strcmp(model->tags[ i ].name, tagname) ) 
		{
			model->links[ i ] = md3link;
			return i;
		}

	return -1;
}

/*
===============
UnLinkModel


UnLink model from the tag
===============
*/
void unlinkmodel(md3_model *model,char *tagname)
{
	int i;

	for (i=0; i<model->header.numTags; i++)
		if ( !strcmp(model->tags[ i ].name, tagname) ) 
		{
			model->links[ i ] = NULL;
			return;
		}
}

/*
===============
LoadSkin

Load .skin file for curent model and *.TGA *.JPG for the curent .skin

filename	- name for the skin file with .skin extension
imagepath	- directory for the texure to load *.TGA *.JPG
===============
*/
void loadskin(md3_model *model,char *filename, char *imagepath)
{
	FILE	*f;
	char	*text_line, *buffer;
	char	name_mesh[1024], name_image[1024], path[1024];
	unsigned char	ismesh, istag, isimage;
	
	console("loading skin file %s ....\n", filename);

	if ( CheckFile(filename) == 0)
		return;

	f=fopen(filename, "rt");
	buffer = (char *) malloc(1024);
	
	while ( !feof(f) )
	{
		ismesh = 0;
		istag = 0;
		isimage = 0;
		text_line = buffer;
		text_line[0] = '\0';
		fscanf(f, "%s", text_line);
		
		if ( text_line && text_line[0])
		{
			console("%s\n", text_line);
			
			while (text_line)
			{
				text_line = COM_Parse(text_line);

				//console("%s\n", com_token);
				
				if (strstr(_strlwr(com_token), "tag_") && ismesh==0 && istag==0)
					istag=1;

				else if (ismesh == 0 && istag == 0)
					{ strcpy(name_mesh, _strlwr(com_token)); ismesh = 1; }

				else if ( (strstr(_strlwr(com_token), ".tga") || strstr(_strlwr(com_token), ".jpg")) && ismesh == 1 && istag == 0)
					{ strcpy(name_image, _strlwr(com_token)); isimage = 1; }
			}

			if (ismesh && isimage)
			{
				
				console("loading skin %s for mesh %s", name_image,name_mesh);
//setallmeshestexure(model,0);
				sprintf(path, "%s\\%s", imagepath, name_image);
				if (addtexture(name_mesh,path))
					console("texture for %s mesh (%s) loaded",name_mesh,path);
				else 
					console("failed: texture for %s mesh (%s) loaded",name_mesh,path);

				int texn=0;
				//if (findtexture(name_mesh,&model->texture_id))
				findtexture(name_mesh,&texn);
				setmeshtexture(model,name_mesh,texn);
					
				model->tex_nr++;
			
			}
		}
	}
	
	free(buffer);
	fclose(f);
}



/*
===============
LoadAnim

Load animation.cfg for Q3Player
===============
*/
void loadq3playeranim(q3_player *q3p,char *filename)
{
	FILE	*f;
	char	*text_line, *buffer;
	char	c;
	int		i;
	int		a=0, t=0;
	int		issex, isheadoffset, isfootsteps;
	
	console("loading anim file %s ....\n", filename);

	if ( CheckFile(filename) == 0)
	return;

	f=fopen(filename, "rt");
	buffer = (char *) malloc(1024);

	while ( !feof(f) )
	{
		c=fgetc(f); 

		i=0;
		text_line = buffer;
		text_line[0] = 0;
		// 10 - new line
		// 32 - space
		// 255 - eof (-1)
		while( c != 10 && c != 255 && c != -1)
		{
			text_line[i]=c; i++;
			c=fgetc(f);			
		}
		text_line[i]='\0';
		
		//if (text_line[0]!=0) 
		//	console("text: %s\n", text_line);

		t=0;
		issex=0;
		isheadoffset=0;
		isfootsteps=0;
		while (text_line)
		{
			text_line = COM_Parse(text_line);

			if (com_token && com_token[0]!=0) 
			{
				//console("token: %s\n", com_token);

				if (strstr(_strlwr(com_token), "sex")) issex=1;
				if (strstr(_strlwr(com_token), "headoffset")) isheadoffset=1;
				if (strstr(_strlwr(com_token), "footsteps")) isfootsteps=1;

				if ( ParsNum(com_token) && !issex && !isheadoffset && !isfootsteps ) 
				{
					if (t==0) { q3p->anim[a].first_frame = atoi(com_token); t++; }
					else if (t==1) { q3p->anim[a].num_frames = atoi(com_token); t++; }
					else if (t==2) { q3p->anim[a].looping_frames = atoi(com_token); t++; }
					else if (t==3) { q3p->anim[a].frames_per_second = atoi(com_token); t++; }
					
					if (t==4) a++;
				}
			}
		}
	}

	int	 skip = q3p->anim[LEGS_WALKCR].first_frame - q3p->anim[TORSO_GESTURE].first_frame;

	for(a=LEGS_WALKCR; a<MAX_ANIMATIONS; a++)
		q3p->anim[a].first_frame -= skip;

	for(a=0; a<MAX_ANIMATIONS; a++)
		if (q3p->anim[a].num_frames>0)
			q3p->anim[a].num_frames -= 1;

	//for(a=0; a<MAX_ANIMATIONS; a++)
	//	console("%d: %d %d %d %d\n", a, q3p->anim[a].first_frame, q3p->anim[a].num_frames, q3p->anim[a].looping_frames, q3p->anim[a].frames_per_second);

	free(buffer);
	fclose(f);
}

/*
===============
SetAnim

Set curent animation
===============
*/
void setq3panim(q3_player *q3p,int a)
{
	if (a>=0 && a<=5)
	{ 
		q3p->l.fps = q3p->anim[a].frames_per_second;
		q3p->u.fps = q3p->anim[a].frames_per_second;
		
		q3p->l.startframe = q3p->anim[a].first_frame;
		q3p->u.startframe = q3p->anim[a].first_frame;

		q3p->l.endframe = q3p->anim[a].first_frame + q3p->anim[a].num_frames;
		q3p->u.endframe = q3p->anim[a].first_frame + q3p->anim[a].num_frames;

		q3p->anim_l=a;
		q3p->anim_u=a;
	}
	else if (a>5 && a<13)
	{
		q3p->u.fps = q3p->anim[a].frames_per_second;
		q3p->u.nextframe = q3p->anim[a].first_frame;
		
		q3p->u.startframe = q3p->anim[a].first_frame;
		q3p->u.endframe = q3p->anim[a].first_frame + q3p->anim[a].num_frames;

		q3p->anim_u=a;
	}
	else if(a>=14 && a<=24)
	{
		q3p->l.fps = q3p->anim[a].frames_per_second;
		q3p->l.nextframe = q3p->anim[a].first_frame;
		
		q3p->l.startframe = q3p->anim[a].first_frame;
		q3p->l.endframe = q3p->anim[a].first_frame + q3p->anim[a].num_frames;

		q3p->anim_l=a;
	}
}

/*
===============
LoadPlayer

Load Q3Player (md3 files, skins, animation file)
===============
*/
int loadq3player(q3_player *q3p,char *path, char *skin)
{
	char l_mpath[1024], u_mpath[1024], h_mpath[1024];
	char l_spath[1024], u_spath[1024], h_spath[1024];
	char a_path[1024];

	sprintf(l_mpath, "%s\\lower.md3", path);
	sprintf(u_mpath, "%s\\upper.md3", path);
	sprintf(h_mpath, "%s\\head.md3", path);

	sprintf(l_spath, "%s\\lower_%s.skin", path, skin);
	sprintf(u_spath, "%s\\upper_%s.skin", path, skin);
	sprintf(h_spath, "%s\\head_%s.skin", path, skin);

	sprintf(a_path, "%s\\animation.cfg", path);

	loadmd3model(&q3p->l,l_mpath);
	loadmd3model(&q3p->u,u_mpath);
	loadmd3model(&q3p->h,h_mpath);
		/*
	q3p->l.LoadModel(l_mpath);
	q3p->u.LoadModel(u_mpath);
	q3p->h.LoadModel(h_mpath);
*/
	loadskin(&q3p->l,l_spath,path);
	loadskin(&q3p->u,u_spath,path);
	loadskin(&q3p->h,h_spath,path);
	/*
	q3p->l.LoadSkin(l_spath, path);
	q3p->u.LoadSkin(u_spath, path);
	q3p->h.LoadSkin(h_spath, path);
*/
	

	loadq3playeranim(q3p,a_path);
	
	q3p->l.startframe=0; q3p->l.endframe=0;
	q3p->u.startframe=0; q3p->u.endframe=0;
	q3p->h.startframe=0; q3p->h.endframe=0;

	setq3panim(q3p,TORSO_STAND);
	setq3panim(q3p,LEGS_RUN);

	linkmodel(&q3p->l,"tag_torso",&q3p->u);
	linkmodel(&q3p->u,"tag_head",&q3p->h);

	//q3p->l.LinkModel("tag_torso", &u);
	//q3p->u.LinkModel("tag_head", &h);

	// l.DumpInfo();
	// u.DumpInfo();
	// h.DumpInfo();

	return true;
}

/*
===============
Draw

Draw all models for curent Q3Player
===============
*/
void drawq3player(q3_player *q3p)
{
	//gg
	drawskeleton(&q3p->l);
	
	// Sys_Printf("(%d %d) %d %d %d (%d %d) %d %d %d",
	//	l.startframe, l.endframe, l.frame, l.nextframe, l.loopframes,
	//	u.startframe, u.endframe, u.frame, u.nextframe, u.loopframes);
}

/*
===============
Update

Update time for all models for curent Q3Player
===============
*/
void updateq3player(q3_player *q3p,float time)
{
	updateframetime(&q3p->l,time);
	updateframetime(&q3p->u,time);
	updateframetime(&q3p->h,time);
	/*
	q3p->l.UpdateFrameTime(time);
	q3p->u.UpdateFrameTime(time);
	q3p->h.UpdateFrameTime(time);
	*/
}