/*
    Copyright 1999 by Nate 'm|d' Miller
    For non-commercial use only!

    File        -- ase.c
    Date        -- 5/29/99
    Author      -- Nate 'm|d' Miller
    Contact     -- vandals1@home.com
    Web         -- http://members.home.com/vandals1

    Code to load the ASE file.  

    Please note that I swap Y and Z values because that is what 3D Studio Max 
    works.  Also in 3DSM negative Z goes out of the screen, we want negative to 
    go INTO the screen.

    There is some code below that uses the 'eye' structure.  This can be ripped
	out because it was only provided for this demo where only one model would be 
	loaded.
*/

/*
	Modifies and Addons by Alejandro Barrio:  (you can obtain the original files contacting with Nate)
	(c) 2001-2002 by Alejandro Barrio.
	For non-commercial use only.

			 e-mail: thdcode@teleline.es
			 Web: http://thd.iespana.es

		HISTORY
		-------
					start: (4/8/2001)
		- entire scenes loading support.
				+ multiple objects and materials.
				+ no multitexture support.
				+ scene_t structure. Contains all objects, materials and info about the loaded scene.
				+ function drawScene. Draws all models in the scene.
				+ the function drawModel needs the texture of the model.
				+ the functions to get info about the scene are divided in different functions object by object.
		- Type of rendering, fill polygons or wireframe.
					finish: (8/10/2001)

					start: (8/10/2001)
		- load lighting.
				+ omnidirectional lights.
				+ spot lights.
				+ Only renders 8 lights.
					finish: (8/14/2001)

					start: (8/18/2001)
		- Animation
						start(6/15/2002) - After a long period of inactivity...
				+ Play animation directly from loaded scene
		- Cameras

(9/5/2001)**Detected a problem when loads an object with a large number of vertex.
				Posible cause -> incorrect allocation of memory.
		  **(9/15/2001) - Fixed.

					start: (6/14/2002)
		-Materials
				+ Materials contain textures, the objects have materials, not textures				
				+ Fixed crash when object havenīt texture

*/

//#define DEBUG // uncomment to see model info
/*#include <stdio.h>
#include <string.h>
#include <stdlib.h>*/
//#include "stdinclu.h"
#include "general.h"
#include "ase.h"
#include "tga.h"
#include "mathlib.h"

//extern vec3_t eye;
// cringe global, here till i find a better way to handle texture ids
int texIds = 1;

/*
    Function -- aseGetInfo

    Gets general info about the ase file.  Num verts, num faces etc.  
*/
/*void aseGetInfo (FILE *s, model_t *p)
{
	char data[255];

	rewind (s);

	while (!feof (s))
	{
		fscanf (s, "%s", &data);

		if (!strcmp (data, NUM_VERTEX))
			fscanf (s, "%d", &p->numVertex);
		else if (!strcmp (data, NUM_FACES))
			fscanf (s, "%d", &p->numFaces);
		else if (!strcmp (data, NUM_TVERTEX))
			fscanf (s, "%d", &p->numTexVertex);
		else if (!strcmp (data, NUM_TFACES))
			fscanf (s, "%d", &p->numTexFaces);
		else if (!strcmp (data, NORMALS))
			p->normals = 1;
		else fgets (data, sizeof (data), s);
	}	
}*/

/*
    Function -- aseGetInfo

    Gets general info about the ase file.  Num verts, num faces etc.  
*/
void modelGetInfo (FILE *s, scene_t *scn)
{
	char data[255];

	rewind (s);

	int objRead = -1;

	fscanf (s, "%s", &data);

	while ((objRead < scn->numObjects) && (!feof(s)))
		{
			if (!strcmp (data, NUM_VERTEX))
				fscanf (s, "%d", &scn->model[objRead].numVertex);
			else if (!strcmp (data, NUM_FACES))
				fscanf (s, "%d", &scn->model[objRead].numFaces);
			else if (!strcmp (data, NUM_TVERTEX))
				fscanf (s, "%d", &scn->model[objRead].numTexVertex);
			else if (!strcmp (data, NUM_TFACES))
				fscanf (s, "%d", &scn->model[objRead].numTexFaces);
			else if (!strcmp (data, NORMALS))
				scn->model[objRead].normals = 1;
			else if (!strcmp (data, MAT_REF))
				fscanf (s, "%i", &scn->model[objRead].material);
			else if (!strcmp (data, OBJ_NAME))
			{
				fscanf (s, " \"%s", &scn->model[objRead].name);
				scn->model[objRead].name[strlen (scn->model[objRead].name) - 1] = '\0';
			}
			else if (!strcmp (data, OBJECT))
				objRead ++;
			else fgets (data, sizeof (data), s);

			fscanf (s, "%s", &data);
		}
		
}

/*
    Function -- aseGetVertex

    Reads a vertex from the ase file.  
*/
void aseGetVertex (FILE *s, model_t *p)
{
	int index;

	fscanf (s, "%d", &index);
	// swap y and z cause 3dsm likes too
	fscanf (s, "%f %f %f", 
		&p->verts[index][0], 
		&p->verts[index][2],
		&p->verts[index][1]);
	
	// in 3dsm negative z goes out of the screen, we want it to go in
	p->verts[index][2] = -p->verts[index][2];

	// find best eye position, take this out for anything else other than a demo
	//eye[2] = (p->verts[index][2] > eye[2]) ? p->verts[index][2] : eye[2];
}
/*
    Function -- aseGetTVertex

    Reads a texture coord from the file.  
*/
void aseGetTVertex (FILE *s, model_t *p, texture_t *t)
{
	int index;

	// we ignore the z value
	fscanf (s, "%d", &index);
	fscanf (s, "%f %f", &p->texVerts[index][0], &p->texVerts[index][1]);

	p->texVerts[index][0] *= t->uTile;
	p->texVerts[index][1] *= t->vTile;
}
/*
    Function -- aseGetNVertex

    Reads a vertex normal from the file
*/
void aseGetNVertex (FILE *s, model_t *p)
{
	int index;

	fscanf (s, "%d", &index);
	fscanf (s, "%f %f %f", 
		&p->vertNorms[index][0], 
		&p->vertNorms[index][2],
		&p->vertNorms[index][1]); 

	p->vertNorms[index][2] = -p->vertNorms[index][2];
}
/*
    Function -- aseGetFaceNormal

    Gets a face normal from the file
*/
void aseGetFaceNormal (FILE *s, model_t *p)
{
	int index;

	fscanf (s, "%d", &index);
	fscanf (s, "%f %f %f", 
		&p->faces[index].normal[0], 
		&p->faces[index].normal[2],
		&p->faces[index].normal[1]); 

	p->faces[index].normal[2] = -p->faces[index].normal[2];
}
/*
    Function -- aseGetFace

    Reads the indicies for a face from the file
*/
void aseGetFace (FILE *s, model_t *p)
{
	int index, ab, bc, ca, smooth;

	fscanf (s, "%d:", &index);
	fscanf (s, "\tA:\t%d B:\t%d C:\t%d AB:    %i BC:    %i CA:    %i\t *MESH_SMOOTHING %i \t*MESH_MTLID %i", 
		&p->faces[index].vertIndex[0], 
		&p->faces[index].vertIndex[1], 
		&p->faces[index].vertIndex[2],
		&ab, &bc, &ca, &smooth,
		&p->faces[index].texID);
}
/*
    Function -- aseGetTFace

    Reads  the indicies for a texture mapped face
*/
void aseGetTFace (FILE *s, model_t *p)
{
	int index;

	fscanf (s, "%d:", &index);

	fscanf (s, "%d %d %d", 
		&p->faces[index].coordIndex[0], 
		&p->faces[index].coordIndex[1], 
		&p->faces[index].coordIndex[2]); 
}
/*
    Function -- aseGetTextureName

    Reads in the teture name that the model will use
*/
void aseGetTextureName (FILE *s, texture_t *t)
{
	fscanf (s, " \"%s", &t->texName);
	
	t->texName[strlen (t->texName) - 1] = '\0';

	t->recipient = (int)t->texName;
}
/*
    Function -- aseGetFloatVal

    Reads a float value from the file, expects data to be like this SOMETHING 4.2323
*/
vec_t aseGetFloatVal (FILE *s)
{
	vec_t v;

	fscanf (s, " %f", &v);

	return v;
}
/*
    Function -- aseGetData

    Fill up out model structure will the model data
*//*
void aseGetData (FILE *s, model_t *p)
{
	char data[255];

	rewind (s);

	while (!feof (s))
	{
		fscanf (s, "%s", &data);

		if (!strcmp (data, VERTEX))
			aseGetVertex (s, p);
		else if (!strcmp (data, TVERTEX))
			aseGetTVertex (s, p);
		else if (!strcmp (data, NVERTEX))
			aseGetNVertex (s, p);
		else if (!strcmp (data, FACE_NORMAL))
			aseGetFaceNormal (s, p);
		else if (!strcmp (data, FACE))
			aseGetFace (s, p);
		else if (!strcmp (data, TFACE))
			aseGetTFace (s, p);
		else if (!strcmp (data, TEXTURE))
			aseGetTextureName (s, p);
		else if (!strcmp (data, UTILE))
			p->texture.uTile = aseGetFloatVal (s);
		else if (!strcmp (data, VTILE))
			p->texture.vTile = aseGetFloatVal (s);
		else if (!strcmp (data, UOFFSET))
			p->texture.uOffset = aseGetFloatVal (s);
		else if (!strcmp (data, VOFFSET))
			p->texture.vOffset = aseGetFloatVal (s);
		else fgets (data, sizeof (data), s);
	}
}*/

/*
    Function -- animationGetData

    Fill up out animation structure with the animation data
*/
int animationGetData(FILE *s, animation_t *anim)
{
	//Get number of position keys
	int posKeys = 0, rotKeys = 0;
	fpos_t fpos;
	char data[255];

	fgetpos(s, &fpos);
	fscanf (s, "%s", &data);
	while ((!feof (s)) && (strcmp (data, CAMERA)) && (strcmp (data, OBJECT)) && (strcmp (data, LIGHT)) && (strcmp (data, SHAPE)))
	{
		fscanf (s, "%s", &data);

		if (!strcmp (data, BEZIER_POS_KEY) || !strcmp (data, POS_SAMPLE))
			posKeys++;
		else if (!strcmp (data, ROT_KEY) || !strcmp (data, ROT_SAMPLE) || !strcmp (data, TCB_ROT_KEY))
			rotKeys++;
	}

	anim->numPosKeys = posKeys;
	anim->numRotKeys = rotKeys;

	//Allocate mem for animation
	anim->posKey = (key_t *)malloc (sizeof (key_t) * anim->numPosKeys);
	anim->rotKey = (key_t *)malloc (sizeof (key_t) * anim->numRotKeys);

	//Read positions and rotations
	fsetpos(s, &fpos);
	fscanf (s, "%s", &data);
	fscanf (s, "%s", &data);

	int pos = 0;
	while ((!feof (s)) && (pos < anim->numPosKeys))
	{
		fscanf (s, "%s", &data);
		if (!strcmp (data, BEZIER_POS_KEY))
		{
			key_t *key = &anim->posKey[pos];
			fscanf (s, "%i", &key->instant);
			fscanf (s, "\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%i", 
						&key->key[0], 
						&key->key[1], 
						&key->key[2],
						&key->bezLeft[0],
						&key->bezLeft[1],
						&key->bezLeft[2],
						&key->bezRight[0],
						&key->bezRight[1],
						&key->bezRight[2],
						&key->val);
			pos++;
		}
		else if  (!strcmp (data, POS_SAMPLE))
		{
			key_t *key = &anim->posKey[pos];
			fscanf (s, "%i", &key->instant);
			fscanf (s, "\t%f\t%f\t%f", 
						&key->key[0], 
						&key->key[1], 
						&key->key[2]);
			pos++;
		}

	}

	fscanf (s, "%s", &data);
	fscanf (s, "%s", &data);

	pos = 0;
	while ((!feof (s)) && (pos < anim->numRotKeys))
	{
		fscanf (s, "%s", &data);
		if (!strcmp (data, ROT_KEY) || !strcmp (data, TCB_ROT_KEY))
		{
			key_t *key = &anim->rotKey[pos];
			fscanf (s, "%i", &key->instant);
			fscanf (s, "\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%i", 
						&key->key[0],			//intensidad en x	
						&key->key[1],			//intensidad en y
						&key->key[2],			//intensidad en z
						&key->bezLeft[0],		//angulo de giro en radianes
						&key->bezLeft[1],
						&key->bezLeft[2],
						&key->bezRight[0],
						&key->bezRight[1],
						&key->bezRight[2],
						&key->val);
			pos++;
		}
		else if  (!strcmp (data, ROT_SAMPLE))
		{
			key_t *key = &anim->rotKey[pos];
			fscanf (s, "%i", &key->instant);
			fscanf (s, "\t%f\t%f\t%f\t%f", 
						&key->key[0],			//intensidad en x
						&key->key[1],			//intensidad en y
						&key->key[2],			//intensidad en z
						&key->bezLeft[0]);		//angulo de giro en radianes
			pos++;
		}
	}

	return 0;
}

/*
    Function -- modelGetData

    Fill up out model structure with the model data
*/
int modelGetData (FILE *s, model_t *p, material_t *m, int cont)
{
	int temp = 0;

	char data[255];

	rewind (s);

	//Set up to start read model info
	while (temp < cont)
	{
		fscanf (s, "%s", &data);
		if (!strcmp (data, OBJECT))
			temp ++;
		if (feof (s)) return 1;
	}

	fscanf (s, "%s", &data);

	while (!feof (s) && (strcmp (data, LIGHT)) && (strcmp (data, OBJECT)) && (strcmp (data, SHAPE)) && (strcmp (data, CAMERA)))
	{
		if (!strcmp (data, VERTEX))
			aseGetVertex (s, p);
		else if (!strcmp (data, TVERTEX))
			aseGetTVertex (s, p, &m->texture);
		else if (!strcmp (data, NVERTEX))
			aseGetNVertex (s, p);
		else if (!strcmp (data, FACE_NORMAL))
			aseGetFaceNormal (s, p);
		else if (!strcmp (data, FACE))
			aseGetFace (s, p);
		else if (!strcmp (data, TFACE))
			aseGetTFace (s, p);
		else if (!strcmp (data, ANIMATION))
			animationGetData(s, &p->anim);
		else if (!strcmp (data, POSITION))
			fscanf (s, "%f %f %f", 
						&p->position[0], 
						&p->position[1],
						&p->position[2]); 
		else if (!strcmp (data, WIRE_COLOR))
			fscanf (s, "%f %f %f", 
						&p->wireColor[0], 
						&p->wireColor[1],
						&p->wireColor[2]); 
		else fgets (data, sizeof (data), s);

		fscanf (s, "%s", &data);
	}
}

/*
    Function -- materialGetData

    Fill up out material structure with the material data
*/
int materialGetData (FILE *s, material_t *m, int cont)
{
	int temp = 0;

	char data[255];

	rewind (s);

	//Set up to start read material info
	while (temp < cont)
	{
		fscanf (s, "%s", &data);
		if (!strcmp (data, MATERIAL))
			temp ++;
		if (feof (s)) return 1;
	}

	fscanf (s, "%s", &data);

	while ((!feof (s)) && (strcmp (data, MATERIAL)))
	{
		if (!strcmp (data, TEXTURE)){
			aseGetTextureName (s, &m->texture);
			m->withTex = 1;
		}
		else if (!strcmp (data, UTILE))
			m->texture.uTile = aseGetFloatVal (s);
		else if (!strcmp (data, VTILE))
			m->texture.vTile = aseGetFloatVal (s);
		else if (!strcmp (data, UOFFSET))
			m->texture.uOffset = aseGetFloatVal (s);
		else if (!strcmp (data, VOFFSET))
			m->texture.vOffset = aseGetFloatVal (s);
		else if (!strcmp (data, MAT_NAME)){
				fscanf (s, " \"%s", &m->name);
				m->name[strlen (m->name) - 1] = '\0';
			}
		else if (!strcmp (data, MAT_CLASS)){
				fscanf (s, " \"%s", &m->type);
				m->type[strlen (m->type) - 1] = '\0';
			}
		else if (!strcmp (data, MAT_AMBIENT))
			fscanf (s, " %f\t%f\t%f", 
					&m->ambient[0], 
					&m->ambient[1],
					&m->ambient[2]); 
		else if (!strcmp (data, MAT_DIFFUSE))
			fscanf (s, " %f\t%f\t%f", 
					&m->diffuse[0], 
					&m->diffuse[1],
					&m->diffuse[2]); 
		else if (!strcmp (data, MAT_SPECULAR))
			fscanf (s, " %f\t%f\t%f", 
					&m->specular[0], 
					&m->specular[1],
					&m->specular[2]); 
		else if (!strcmp (data, MAT_SHINE))
			m->shine = aseGetFloatVal (s);
		else if (!strcmp (data, MAT_SHINESTRENGTH))
			m->shineStrength = aseGetFloatVal (s);
		else if (!strcmp (data, MAT_TRANSPARENCY))
			m->transparency = aseGetFloatVal (s);
		else if (!strcmp (data, MAT_WIRE_SIZE))
			m->wireSize = aseGetFloatVal (s);
		else if (!strcmp (data, MAT_SELFILUM))
			m->selfIlum = aseGetFloatVal (s);
		else fgets (data, sizeof (data), s);

		fscanf (s, "%s", &data);
	}
}

/*
    Function -- lightGetData

    Fill up out light structure with the light data
*/
int lightGetData (FILE *s, light_t *l, int cont)
{
	int temp = 0;
	int pos = 0;

	char data[255];

	rewind (s);

	//Set up to start read light info
	while (temp < cont)
	{
		fscanf (s, "%s", &data);
		if (!strcmp (data, LIGHT))
			temp ++;
		if (feof (s)) return 1;
	}

	fscanf (s, "%s", &data);

	while ((!feof (s)) && (strcmp (data, LIGHT)) && (strcmp (data, OBJECT)) && (strcmp (data, SHAPE)) && (strcmp (data, CAMERA)))
	{
		if (!strcmp (data, POSITION))
		{
			if (pos)
			{
				fscanf (s, "%f %f %f", 
						&l->tgPosition[0], 
						&l->tgPosition[1],
						&l->tgPosition[2]); 
				l->type = SPOT;
			}
			else fscanf (s, "%f %f %f", 
						&l->position[0], 
						&l->position[1],
						&l->position[2]); 
			pos = 1;
		}
		else if (!strcmp (data, L_COLOR))
			fscanf (s, "%f %f %f", 
					&l->color[0], 
					&l->color[1],
					&l->color[2]); 
		else if (!strcmp (data, L_INTES))
			l->intes = aseGetFloatVal (s);
		else if (!strcmp (data, SPOT_FALLOFF))
			l->falloff = aseGetFloatVal (s);
		else if (!strcmp (data, ANIMATION))
			animationGetData(s, &l->anim);
		else if (!strcmp (data, NAME)){
				fscanf (s, " \"%s", &l->name);
				l->name[strlen (l->name) - 1] = '\0';
			}
		else fgets (data, sizeof (data), s);

		fscanf (s, "%s", &data);
	}
}

/*
    Function -- cameraGetData

    Fill up out camera structure with the camera data
*/
int cameraGetData (FILE *s, camera_t *c, int cont)
{
	int temp = 0;

	char data[255];

	rewind (s);

	//Set up to start read light info
	while (temp < cont)
	{
		fscanf (s, "%s", &data);
		if (!strcmp (data, CAMERA))
			temp ++;
		if (feof (s)) return 1;
	}

	fpos_t fpos;
	fscanf (s, "%s", &data);


	int pos = 0;

	while ((!feof (s)) && (strcmp (data, CAMERA)) && (strcmp (data, OBJECT)) && (strcmp (data, LIGHT)) && (strcmp (data, SHAPE)))
	{
		if (!strcmp (data, POSITION))
			if(!pos){			
				fscanf (s, "%f %f %f", 
						&c->position[0], 
						&c->position[1],
						&c->position[2]); 

				pos = 1;
			}
			else{			
				fscanf (s, "%f %f %f", 
						&c->view[0], 
						&c->view[1],
						&c->view[2]); 
			}
			
		else if (!strcmp (data, ROT_AXIS))			
				fscanf (s, "%f %f %f", 
					&c->rotAxis[0], 
					&c->rotAxis[1],
					&c->rotAxis[2]); 				

		else if (!strcmp (data, CAM_NEAR)){
			c->cnear = aseGetFloatVal (s);
			if(c->cnear == 0){
				c->cnear = 0.001f;
			}
		}
		else if (!strcmp (data, CAM_FAR))
			c->cfar = aseGetFloatVal (s);
		else if (!strcmp (data, CAM_FOV))
			c->fov = aseGetFloatVal (s);
		else if (!strcmp (data, CAM_TDIST))
			c->tdist = aseGetFloatVal (s);
		else if (!strcmp (data, OBJ_NAME))
			{
				fscanf (s, " \"%s", &c->name);
				c->name[strlen (c->name) - 1] = '\0';
			}
		else if (!strcmp (data, ANIMATION))
			animationGetData(s, &c->anim);
		else fgets (data, sizeof (data), s);

		fscanf (s, "%s", &data);
	}
}


/*
    Function -- aseAllocate

    Allocate memory for the model
*/
void modelAllocate (model_t *p)
{
	p->verts = (float (*)[3])malloc (sizeof (vec3_t) * p->numVertex);

	p->faces = (triangle_t *)malloc (sizeof (triangle_t) * p->numFaces);

	if (p->numTexVertex != 0)
		p->texVerts = (float (*)[2])malloc (sizeof (vec2_t) * p->numTexVertex);

	if (p->normals)
		p->vertNorms = (float (*)[3])malloc (sizeof (vec3_t) * p->numVertex);
}
/*
    Function -- aseFree

    Releases all memory the model was using
*/
/*void aseFree (model_t *p)
{
	if (p->verts != NULL)
		free (p->verts);

	if (p->faces != NULL)
		free (p->faces);
	
	if (p->texVerts != NULL)
		free (p->texVerts);
	
	if (p->vertNorms != NULL)
		free (p->vertNorms);
}
*/
void aseFree (scene_t *scn)
{
	for (int i=0; i<scn->numObjects; i++)
	{
		if (&scn->model[i].verts != NULL)
			free (scn->model[i].verts);

		if (&scn->model[i].faces != NULL)
			free (scn->model[i].faces);
	
		if (&scn->model[i].texVerts != NULL)
			free (scn->model[i].texVerts);
	
		if (&scn->model[i].vertNorms != NULL)
			free (scn->model[i].vertNorms);
	}

//	if (scn->model != NULL)
//		free(&scn->model[0]);

	if (&scn->material[0] != NULL)
		free(&scn->material[0]);

//	if (&scn->light[0] != NULL)
//		free(&scn->light[0]);

}

/*
    Function -- aseLoadTexture

    Loads the texture for the model, uses my custom TGA loading code.  If you
	want to use your own code change this.  
*/
/*void aseLoadTexture (model_t *p)
{
	if (p->texture.texName[0] != '\0')
		loadTGA (p->texture.texName, texIds);

	p->texture.texId = texIds;
	texIds ++;
}*/

void aseLoadTexture (texture_t *t)
{
	if (t->texName[0] != '\0')
		loadTGA (t->texName, t->recipient);
}

/*
    Function -- aseDumpInfo
    
    Dumps info regaring the passed model.
*/
void aseDumpInfo (model_t *p)
{
	printf ("# Model Info\n");
	printf ("# ------------------------------\n");
	printf ("# Num Vertices %d\n", p->numVertex);
	printf ("# Num Tex Vertices %d\n", p->numTexVertex);
	printf ("# Num Faces %d\n", p->numFaces);
	printf ("# Num Tex Faces %d\n", p->numTexFaces);
	printf ("# Model has normals : %s\n", (p->normals) ? "Yes" : "No");
//	printf ("# Texture Name : %s\n", 
//		(p->texture.texName[0] != '\0') ? p->texture.texName : "None");
}
/*
    Function -- aseInit

    Setup the model
*/
/*void aseInit (model_t *p)
{
	p->numVertex = p->numFaces = p->numTexFaces = p->normals = 0;
	p->verts = p->vertNorms = NULL;
	p->faces = NULL;
	p->texVerts = NULL;
	p->texture.uTile = p->texture.vTile = p->texture.uOffset = p->texture.uOffset = 0.0f;
	p->texture.texId = 0;
	memset (p->texture.texName, '\0', sizeof (p->texture.texName));
}*/

/*
    Function -- modelInit

    Setup the model
*/
void modelInit (model_t *p)
{
	p->numVertex = p->numFaces = p->numTexFaces = p->normals = 0;
	p->verts = p->vertNorms = NULL;
	p->faces = NULL;
	p->texVerts = NULL;
	p->anim.numPosKeys = p->anim.numRotKeys = 0;
	p->anim.posKey = NULL;
	p->anim.rotKey = NULL;
	p->anim.posCalc = NULL;
	p->anim.rotCalc = NULL;
	p->material = -1;
	memset (p->name, '\0', sizeof (p->name));
	p->position[0] = p->position[1] = p->position[2] = 0.0f;
	p->wireColor[0] = p->wireColor[0] = p->wireColor[0] = 1.0f;
}

/*
    Function -- materialInit

    Setup the material
*/
void materialInit (material_t *m)
{
	m->texture.uTile = m->texture.vTile = m->texture.uOffset = m->texture.vOffset = 0.0f;
	m->texture.texId = 0;
	memset (m->texture.texName, '\0', sizeof (m->texture.texName));
	memset (m->name, '\0', sizeof (m->name));
	memset (m->type, '\0', sizeof (m->type));
	m->ambient[0] = m->ambient[1] = m->ambient[2] = 0.0f;
	m->diffuse[0] = m->diffuse[1] = m->diffuse[2] = 0.0f;
	m->specular[0] = m->specular[1] = m->specular[2] = 0.0f;
	m->matId = 0;
	m->shine = 0.0f;
	m->shineStrength = 0.0f;
	m->transparency = 0.0f;
	m->selfIlum = 0.0f;
	m->withTex = 0;
}

/*
    Function -- lightInit

    Setup the light
*/
void lightInit (light_t *l)
{
	l->color[0] = l->color[1] = l->color[2] = 0.0f;
	l->color[3] = 1.0f;
	l->position[0] = l->position[1] = l->position[2] = l->position[3] = 0.0f;
	l->tgPosition[0] = l->tgPosition[1] = l->tgPosition[2] = l->tgPosition[3] = 0.0f;
	l->intes = 0.0f;
	l->type = OMNI;
	l->falloff = 0.0f;
	l->anim.numPosKeys = l->anim.numRotKeys = 0;
	l->anim.posKey = NULL;
	l->anim.rotKey = NULL;
	l->anim.posCalc = NULL;
	l->anim.rotCalc = NULL;
	memset (l->name, '\0', sizeof (l->name));
}

/*
    Function -- cameraInit

    Setup the camera
*/
void cameraInit (camera_t *c)
{
	c->cfar = c->cnear = c->fov = c->tdist = c->rotAngle = 0.0f;
	c->position[0] = c->position[1] = c->position[2] = 0.0f;
	c->view[0] = c->view[1] = c->view[2] = 0.0f;
	c->rotAxis[0] = c->rotAxis[1] = c->rotAxis[2] = 0.0f;
	c->anim.numPosKeys = c->anim.numRotKeys = 0;
	c->anim.posKey = NULL;
	c->anim.rotKey = NULL;
	c->anim.posCalc = NULL;
	c->anim.rotCalc = NULL;
	memset (c->name, '\0', sizeof (c->name));
}

/*
    Function -- sceneInit

    Setup the scene
*/
void sceneInit (FILE *s, scene_t *scn)
{
	scn->numLights = scn->numObjects  = scn->numMaterials = 0;
	scn->model = NULL;
	scn->light = NULL;
	scn->material = NULL;

	int numObjects = 0;
	int numLights = 0;
	int numCameras = 0;
	char data[255];

	rewind(s);

	while (!feof (s))
	{
		fscanf (s, "%s", &data);

		if (!strcmp (data, OBJECT))					//Count number of objects
			numObjects ++;
		else if (!strcmp (data, NUM_MATERIALS))		//Materials in the scene
			fscanf (s, "%i", &scn->numMaterials);		
		else if (!strcmp (data, LIGHT))				//Lights in the scene
			numLights ++;
		else if (!strcmp (data, CAMERA))			//Cameras in the scene
			numCameras ++;
		else if (!strcmp (data, FIRST_FRAME))
			fscanf (s, "%i", &scn->firstFrame);	
		else if (!strcmp (data, LAST_FRAME))
			fscanf (s, "%i", &scn->lastFrame);		
		else if (!strcmp (data, FRAME_SPEED))
			fscanf (s, "%i", &scn->frameSpeed);		
		else if (!strcmp (data, TICKS_PER_FRAME))
			fscanf (s, "%i", &scn->ticksPerFrame);		

	}

	scn->numObjects = numObjects;	
	scn->numLights = numLights;	
	scn->numCameras = numCameras;


	//Allocate mem for scene
	scn->model = (model_t *)malloc (sizeof (model_t) * scn->numObjects);
	scn->material = (material_t *)malloc (sizeof (material_t) * scn->numMaterials);
	scn->light = (light_t *)malloc (sizeof (light_t) * scn->numLights);
	scn->camera = (camera_t *)malloc (sizeof (camera_t) * scn->numCameras);

	//Initialize models in the scene
	for (int cont=0; cont < scn->numObjects; cont++)
	{
		modelInit(&scn->model[cont]);
	}

	//Initialize materials in the scene
	for (cont=0; cont < scn->numMaterials; cont++)
	{
		materialInit(&scn->material[cont]);
	}

	//Initialize lights in the scene
	for (cont=0; cont < scn->numLights; cont++)
	{
		lightInit(&scn->light[cont]);
	}

	//Initialize cameras
	for (cont=0; cont < scn->numCameras; cont++)
	{
		cameraInit(&scn->camera[cont]);
	}
}

/*
    Function -- aseLoad

    Load a model from a ase file
*/
/*int aseLoad (char *name, model_t *p)
{
	FILE *s;
	
	if (!(s = fopen (name, "r")))
	{
		// file not found, bummer
		printf ("# %s not found!\n", name);
		return 0;
	}
	
	aseInit (p);
	aseGetInfo (s, p);
	aseAllocate (p);
	aseGetData (s, p);
	aseLoadTexture (p);
#ifdef DEBUG
	aseDumpInfo (p);
#endif
	fclose (s);

	return 1;
}*/

/*
    Function -- aseLoad

     Load the scene from a ase file
*/
int aseLoad (char *name, scene_t *scn)
{
	FILE *s;

	if (!(s = fopen (name, "r")))
	{
		// file not found, bummer
		printf ("# %s not found!\n", name);
		return 0;
	}

	sceneInit (s, scn);
	modelGetInfo (s, scn);

	rewind(s);

	for (int cont1=0; cont1 < scn->numMaterials; cont1++)
	{
		materialGetData (s, &scn->material[cont1], cont1+1);
		aseLoadTexture (&scn->material[cont1].texture);
	}

	for (int cont=0; cont < scn->numObjects; cont++)
	{
		modelAllocate (&scn->model[cont]);
		modelGetData (s, &scn->model[cont], &scn->material[scn->model[cont].material], cont+1);
		//aseLoadTexture (&scn->model[cont]);
		#ifdef DEBUG
		aseDumpInfo (&scn->model[cont]);
		#endif

		//Animation
		calculateAnim(scn, &scn->model[cont].name[0], 1);
	}

	for (cont=0; cont < scn->numLights; cont++)
	{
		lightGetData(s, &scn->light[cont], cont+1);

		//Animation
		calculateAnim(scn, &scn->light[cont].name[0], 2);
	}

	for (cont=0; cont < scn->numCameras; cont++)
	{
		cameraGetData(s, &scn->camera[cont], cont+1);

		//Animation
		calculateAnim(scn, &scn->camera[cont].name[0], 0);
	}

	fclose (s);

	return 1;
}

/*
    Function -- drawModel
    
    Draws a model to the screen, its a mess because of all the rendering 
	options.
*/
void drawModel (model_t *p, material_t *mat, int mode, int textureOn, int lightOn, int faceLight)
{
	int i, j;
	vec_t matx[16];
	vec3_t t;

	glGetFloatv (GL_MODELVIEW_MATRIX, matx);

	//Draw mode
	if (mode == 1)
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	else if (mode == 2)
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	// is texturing enabled?
	if (textureOn){
		glEnable (GL_TEXTURE_2D);
		glBindTexture (GL_TEXTURE_2D, mat->texture.recipient);
	}

	//set up material
	float ambient[4];
	float diffuse[4];
	float specular[4];
	float emission[4];
	int shininess;

	if (lightOn && mat){		
		if(textureOn){
			
			ambient[0] = 1.0f;
			ambient[1] = 1.0f;
			ambient[2] = 1.0f;
			ambient[3] = 1.0f;

			diffuse[0] = 1.0f;
			diffuse[1] = 1.0f;
			diffuse[2] = 1.0f;
			diffuse[3] = 1.0f;
/*
			specular[0] = 1.0f;
			specular[1] = 1.0f;
			specular[2] = 1.0f;
			specular[3] = 1.0f;

			emission[0] = 0.0f;
			emission[1] = 0.0f;
			emission[2] = 0.0f;
			emission[3] = 0.0f;
*/		}
		else{
			ambient[0] = mat->ambient[0];
			ambient[1] = mat->ambient[1]; 
			ambient[2] = mat->ambient[2];
			ambient[3] = 1.0f;

			diffuse[0] = mat->diffuse[0];
			diffuse[1] = mat->diffuse[1];
			diffuse[2] = mat->diffuse[2];
			diffuse[3] = 1.0f;
		}

			specular[0] = mat->specular[0];
			specular[1] = mat->specular[1];
			specular[2] = mat->specular[2];
			specular[3] = 1.0f;

			emission[0] = mat->diffuse[0];
			emission[1] = mat->diffuse[1];
			emission[2] = mat->diffuse[2];
			emission[3] = mat->selfIlum;			
//		}

		shininess = int(mat->shine *128);						
	}
	else{		
		diffuse[0] = p->wireColor[0];
		diffuse[1] = p->wireColor[1];
		diffuse[2] = p->wireColor[2];
		diffuse[3] = 1.0f;

		ambient[0] = 1.0f;
		ambient[1] = 1.0f;
		ambient[2] = 1.0f;
		ambient[3] = 1.0f;			

		specular[0] = 1.0f;
		specular[1] = 1.0f;
		specular[2] = 1.0f;
		specular[3] = 1.0f;

		emission[0] = 0.0f;
		emission[1] = 0.0f;
		emission[2] = 0.0f;
		emission[3] = 0.0f;		

		shininess = 0;
	}

	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, &ambient[0]);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, &diffuse[0]);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, &specular[0]);
	glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, &emission[0]);
	glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, shininess);



	for (i = 0; i < p->numFaces; i ++)
	{
		glBegin (GL_TRIANGLES);
			for (j = 0; j < 3; j ++)
			{
				int temp1 = p->faces[i].vertIndex[j];
				// is lighting enabled? 
				if (lightOn)
				{
					// face lighting or vertex?
					if (faceLight)
						vectorCopy (p->faces[i].normal, t);
					else vectorCopy (p->vertNorms[temp1], t);
					matrixMultiplyVector (matx, t);
					vectorNormalize (t, t);
					glNormal3fv (t);
				}
				if (textureOn)
				{
					int temp2 = p->faces[i].coordIndex[j];
					glTexCoord2f (p->texVerts[temp2][0] + mat->texture.uOffset,
								p->texVerts[temp2][1] + mat->texture.vOffset);
				}

				glVertex3fv (p->verts[temp1]);
			}
		glEnd ();
	}
	if (textureOn && mat->withTex)
		glDisable (GL_TEXTURE_2D);

	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}


void spotDir (float *pos, float *tgPos, float *spdir)
{
	spdir[0] = tgPos[0] - pos[0];
	spdir[1] = tgPos[1] - pos[1];
	spdir[2] = tgPos[2] - pos[2];
	spdir[3] = 1.0f;
}

void vectMinus (float *vect, float num, float *result)
{
	result[0] = vect[0] - (1.0 - num);
	result[1] = vect[1] - (1.0 - num);
	result[2] = vect[2] - (1.0 - num);
	result[3] = vect[3];
}

/*
    Function -- drawScene
    
    Draws all objects in the scene
*/
void drawScene (scene_t *scn, int mode, int textureOn, int lightOn, int faceLight, int player, int key)
{
	if (lightOn)
	{
		float ambient[4];
		glEnable(GL_LIGHTING);
		for(int i=0; i < scn->numLights; i++)
		{
			int oglLight;
			switch (i)
			{
				case 0: oglLight = GL_LIGHT0; break;
				case 1: oglLight = GL_LIGHT1; break;
				case 2: oglLight = GL_LIGHT2; break;
				case 3: oglLight = GL_LIGHT3; break;
				case 4: oglLight = GL_LIGHT4; break;
				case 5: oglLight = GL_LIGHT5; break;
				case 6: oglLight = GL_LIGHT6; break;
				case 7: oglLight = GL_LIGHT7; break;
			}
			light_t *temp = &scn->light[i];

			if(player){				
			//	if(scn->light[i].anim.numRotKeys)
			//		glRotatef(scn->light[i].anim.rotCalc[key].deg, scn->light[i].anim.rotCalc[key].rot[0], scn->light[i].anim.rotCalc[key].rot[1], scn->light[i].anim.rotCalc[key].rot[2]);			
				if(scn->light[i].anim.numPosKeys){
					float keyPos[4];

					keyPos[0]=scn->light[i].anim.posCalc[key][0];
					keyPos[1]=scn->light[i].anim.posCalc[key][1];
					keyPos[2]=scn->light[i].anim.posCalc[key][2];
					keyPos[3]=scn->light[i].position[3];

					glLightfv(oglLight, GL_POSITION, &keyPos[0]);
				}
			
			}
			else
				glLightfv(oglLight, GL_POSITION, temp->position);
			
			vectMinus(scn->light[i].color, temp->intes, &ambient[0]);
			glLightfv(oglLight, GL_AMBIENT, ambient);			
			glLightfv(oglLight, GL_DIFFUSE, temp->color);
			//glLightfv(i, GL_SPECULAR, scn->light[i].color);
			if (temp->type == SPOT)
			{
				float spdir[4];
				spotDir(temp->position, temp->tgPosition, &spdir[0]);
				glLightfv(i, GL_SPOT_DIRECTION, spdir);
				glLightf(i, GL_SPOT_CUTOFF, 45.0f);
				glLightf(i, GL_SPOT_EXPONENT, 100.0f);
			}
			glEnable(oglLight);	
		}
	}
	else glDisable(GL_LIGHTING);

	//int rot=0;

	for(int i=0; i < scn->numObjects; i++){
		if(player){
			glPushMatrix();					
			if(scn->model[i].anim.numPosKeys)
				glTranslatef(scn->model[i].anim.posCalc[key][0], scn->model[i].anim.posCalc[key][2], scn->model[i].anim.posCalc[key][1]);
			if(scn->model[i].anim.numRotKeys){
				//glPushMatrix();
				glTranslatef(-scn->model[i].position[0],-scn->model[i].position[2],-scn->model[i].position[1]);
				glRotatef(scn->model[i].anim.rotCalc[key].deg, scn->model[i].anim.rotCalc[key].rot[0], scn->model[i].anim.rotCalc[key].rot[2], scn->model[i].anim.rotCalc[key].rot[1]);			
				glTranslatef(scn->model[i].position[0],scn->model[i].position[2],scn->model[i].position[1]);				
				//rot=1;
			}
		}

		//Si hay algun material y el material del modelo no es -1
		if(scn->numMaterials && (scn->model[i].material >= 0))
			//Si el material contiene textura
			if(scn->material[scn->model[i].material].withTex)
				drawModel(&scn->model[i], &scn->material[scn->model[i].material], mode, textureOn, lightOn, faceLight);
			else  //Material sin textura
				drawModel(&scn->model[i], &scn->material[scn->model[i].material], mode, 0, lightOn, faceLight);
		else  //Material por defecto
			drawModel(&scn->model[i], &scn->material[0], mode, 0, lightOn, faceLight);

	//	if(rot)
	//		glPopMatrix();

		if(player)
			glPopMatrix();			
	}

	if (lightOn) glDisable(GL_LIGHTING);

}

void setCamera(camera_t *cam){

	glViewport(0,0,xRes,yRes);

	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();									// Reset The Projection Matrix

	// Calculate The Aspect Ratio Of The Window
	gluPerspective(50 * cam->fov,(GLfloat)xRes/(GLfloat)yRes, cam->cnear, cam->cfar);

	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix	

}

void cameraView(camera_t *cam, int player, int key){

	if(player){
			glPushMatrix();					
			if(cam->anim.numPosKeys)
				//glTranslatef(cam->anim.posCalc[key][0], cam->anim.posCalc[key][2], cam->anim.posCalc[key][1]);
				gluLookAt(cam->anim.posCalc[key][0], cam->anim.posCalc[key][2], cam->anim.posCalc[key][1],  cam->view[0],cam->view[2],cam->view[1],  0,1,0);
			if(cam->anim.numRotKeys){
				//glPushMatrix();
				glTranslatef(-cam->position[0],-cam->position[2],-cam->position[1]);
				glRotatef(cam->anim.rotCalc[key].deg, cam->anim.rotCalc[key].rot[0], cam->anim.rotCalc[key].rot[2], cam->anim.rotCalc[key].rot[1]);			
				glTranslatef(cam->position[0],cam->position[2],cam->position[1]);				
				//rot=1;
			}
		}

}

void calculateAnim (scene_t *scn, char *objName, int type)
{
	BOOL found = FALSE;
	int i= 0;
	int maxi = 0;

	if (!type)
		maxi = scn->numCameras;
	else maxi = scn->numObjects;

	animation_t *anim;

	while (!found && (i<maxi))
	{
		switch (type){
			case 0:	if (!strcmp (objName, scn->camera[i].name))
					{
						found = TRUE;
						anim = &scn->camera[i].anim;
					}
					break;
			case 1: if (!strcmp (objName, scn->model[i].name))
					{
						found = TRUE;
						anim = &scn->model[i].anim;
					}
					break;
			case 2: if (!strcmp (objName, scn->light[i].name))
					{
						found = TRUE;
						anim = &scn->light[i].anim;
					}
					break;
		}

		if (found)
		{
			int ticksPerFrame = scn->ticksPerFrame;

			if (anim->numPosKeys != 0)
			{
				long posframes = (anim->posKey[anim->numPosKeys - 1].instant - anim->posKey[0].instant)/ticksPerFrame;
				anim->posCalc = (vec3_t *) malloc (sizeof(vec3_t) * posframes);

				int instantI = (anim->posKey[0].instant) / ticksPerFrame;
				int instantF = (anim->posKey[1].instant) / ticksPerFrame;
				int posInst = 1;
				int pos = 0;
				int subpos = 0;

				float x, y, z;

				BOOL posOK = FALSE;

				while (!posOK)
				{
					vec3_t instF;
					instF[0] = anim->posKey[posInst].key[0];
					instF[1] = anim->posKey[posInst].key[1];
					instF[2] = anim->posKey[posInst].key[2];
					vec3_t instI;
					instI[0] = anim->posKey[posInst-1].key[0];
					instI[1] = anim->posKey[posInst-1].key[1];
					instI[2] = anim->posKey[posInst-1].key[2];

					//Lineal interpolation
					long instants = instantF - instantI;

					if(subpos == 0)
					{
						x = instI[0];
						y = instI[1];
						z = instI[2];
					}
					else
					{
						x = (((instF[0] - instI[0])/instants) * subpos) + instI[0];
						y = (((instF[1] - instI[1])/instants) * subpos) + instI[1];
						z = (((instF[2] - instI[2])/instants) * subpos) + instI[2];
					}

					anim->posCalc[pos][0] = x;
					anim->posCalc[pos][1] = y;
					anim->posCalc[pos][2] = z;

					if (pos == instantF-1)
					{
						instantI = instantF;
						posInst ++;
						subpos = 0;
						if (posInst == anim->numPosKeys)
							posOK = TRUE;
						else instantF = (anim->posKey[posInst].instant) / ticksPerFrame;
					}
					pos++;
					subpos++;
				}
			}

			if (anim->numRotKeys != 0)
			{
				//Obtener el numero de frames a calcular:
				//         (tick final - tick inicial) / ticks por frame
				long rotframes = (anim->rotKey[anim->numRotKeys - 1].instant - anim->rotKey[0].instant)/ticksPerFrame;
				//Reservar memoria para los frames a calcular
				anim->rotCalc = (rotGrad_t *) malloc (sizeof(rotGrad_t) * rotframes);

				//Inicializar variables

				//Instante inicial actual
				int instantIrot = (anim->rotKey[0].instant) / ticksPerFrame;
				//Instante final actual
				int instantFrot = (anim->rotKey[1].instant) / ticksPerFrame;
				//Instante numero 1
				int rotInst = 1;
				//contador
				int rot = 0;
				//contador para las interpolaciones
				int subrot = 0;
				//Giro en radianes de los instantes inicial y final actuales
				float rotateI = 0.0f;
				float rotateF = 0.0f;

				//   0			 1			 2			 3			(valores dados por el modelo)
				//   | 0 1 2 3 4 |			 |			 |			(interpolacion lineal)
				//	 |.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.				

				float rotx, roty, rotz, deg;

				BOOL rotOK = FALSE;

				while (!rotOK)
				{
					//X Y Z del instante final actual
					vec3_t instFrot;
					instFrot[0] = anim->rotKey[rotInst].key[0];
					instFrot[1] = anim->rotKey[rotInst].key[1];
					instFrot[2] = anim->rotKey[rotInst].key[2];
					//Angulo de giro en radianes del instante final actual
					rotateF = anim->rotKey[rotInst].bezLeft[0];
					//X Y Z del instante inicial actual
					vec3_t instIrot;
					instIrot[0] = anim->rotKey[rotInst-1].key[0];
					instIrot[1] = anim->rotKey[rotInst-1].key[1];
					instIrot[2] = anim->rotKey[rotInst-1].key[2];
					//Angulo de giro en radianes del instante inicial actual
					rotateI = anim->rotKey[rotInst-1].bezLeft[0];

					//Lineal interpolation
					//Numero de instantes que transcurren desde el inicial actual hasta el final actual
					long instantsRot = instantFrot - instantIrot;

					//Si es el primer calculo del intervalo, el resultado sera el del inicial actual
					if(subrot == 0)
					{
						rotx = instIrot[0];
						roty = instIrot[1];
						rotz = instIrot[2];

						//Paso de radianes a grados
						deg = (rotateI*180)/PI;
					}
					else // Si no es el primero
					{
						//En cada eje (X Y Z) y dentro del intervalo que estamos interpolando:
						//  (((instante final actual - inicial actual) / numero de instantes) * posicion interpolada) + valor del instante inicial actual
						rotx = (((instFrot[0] - instIrot[0])/instantsRot) * subrot) + instIrot[0];
						roty = (((instFrot[1] - instIrot[1])/instantsRot) * subrot) + instIrot[1];
						rotz = (((instFrot[2] - instIrot[2])/instantsRot) * subrot) + instIrot[2];

						deg = (((((rotateF - rotateI)/instantsRot) * subrot) + rotateI)*180)/PI;
					}

					anim->rotCalc[rot].rot[0] = rotx;
					anim->rotCalc[rot].rot[1] = roty;
					anim->rotCalc[rot].rot[2] = rotz;

					anim->rotCalc[rot].deg = deg;

					if (rot == instantFrot-1)
					{
						instantIrot = instantFrot;
						rotInst ++;
						subrot = 0;
						if (rotInst == anim->numRotKeys)
							rotOK = TRUE;
						else instantFrot = (anim->rotKey[rotInst].instant) / ticksPerFrame;
					}
					rot++;
					subrot++;
				}
			}
			break;
		}
		i++;
	}
}