#include <windows.h>
#include <windowsx.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <io.h>
#include <stdio.h>
#include <GL/gl.h>
#include <GL/glut.h>
#include "glModelUt.h"

// Macro for finding string in file.
#define FindStr(str, fHandle)\
{ \
	do fgets(szString, 128, fHandle);\
	while((strncmp(str, szString, sizeof(str)-1))&&(!feof(fHandle)));\
}

// This func creates OpenGL display list from .ASE file.
// It handles vertex coords, face normals, texture coords for all vertices.
// In ListNum var it returns display list index for loaded mesh.
// Now func applies material properties to mesh.
//
// You must set material and texture mapping for mesh in 3DS or this func
// will hang up.

int glLoadModel(char *filename,GLuint *ListNum)
{
	GLuint glListNum;
	FILE *fModel;
	char szString[128];

	unsigned int Vertices=0,Faces=0,i,TVert=0,TFaces=0;

	GLfloat *vx,*vy,*vz;	// Coords of vertices
	GLfloat	*u,*v;			// Texture coords for vertices
	GLfloat	*nx,*ny,*nz;	// Face normals coords

	GLfloat mat_a_r,mat_a_g,mat_a_b;	// Ambient component of material
	GLfloat mat_d_r,mat_d_g,mat_d_b;	// Diffuse component
	GLfloat mat_s_r,mat_s_g,mat_s_b;	// Specular component
	GLfloat shine,shine_strength;		// Did you see 3DSMax?

	unsigned int *fa,*fb,*fc;			// Faces
	unsigned int *tfa,*tfb,*tfc;			// TFaces

	fModel=fopen(filename,"rb");
	if(!fModel) return GL_MODEL_NOSUCHFILE;

	FindStr("\t\t*MATERIAL_AMBIENT",fModel);
	sscanf(szString,"\t\t*MATERIAL_AMBIENT %f %f %f",&mat_a_r,&mat_a_g,&mat_a_b);

	FindStr("\t\t*MATERIAL_DIFFUSE",fModel);
	sscanf(szString,"\t\t*MATERIAL_DIFFUSE %f %f %f",&mat_d_r,&mat_d_g,&mat_d_b);

	FindStr("\t\t*MATERIAL_SPECULAR",fModel);
	sscanf(szString,"\t\t*MATERIAL_SPECULAR %f %f %f",&mat_s_r,&mat_s_g,&mat_s_b);

	FindStr("\t\t*MATERIAL_SHINE",fModel);
	sscanf(szString,"\t\t*MATERIAL_SHINE %f",&shine);

	FindStr("\t\t*MATERIAL_SHINESTRENGTH",fModel);
	sscanf(szString,"\t\t*MATERIAL_SHINESTRENGTH %f",&shine_strength);

	FindStr("\t\t*MESH_NUMVERTEX",fModel);
	sscanf(szString,"\t\t*MESH_NUMVERTEX %d",&Vertices);

	FindStr("\t\t*MESH_NUMFACES",fModel);
	sscanf(szString,"\t\t*MESH_NUMFACES %d",&Faces);

	vx=new GLfloat[Vertices];
	vy=new GLfloat[Vertices];
	vz=new GLfloat[Vertices];

	if(!vx || !vy || !vz) {return GL_MODEL_NOMEMORY;fclose(fModel);}

	fa=new unsigned int[Faces];
	fb=new unsigned int[Faces];
	fc=new unsigned int[Faces];

	if(!fa || !fb || !fc) {return GL_MODEL_NOMEMORY;fclose(fModel);}

	nx=new GLfloat[Faces];
	ny=new GLfloat[Faces];
	nz=new GLfloat[Faces];

	if(!nx || !ny || !nz) {return GL_MODEL_NOMEMORY;fclose(fModel);}

	for(i=0;i<Vertices;i++)
	{
		int tp;
		FindStr("\t\t\t*MESH_VERTEX",fModel);
		sscanf(szString,"\t\t\t*MESH_VERTEX %d %f %f %f",&tp,&(vx[i]),&(vy[i]),&(vz[i]));
	}

	for(i=0;i<Faces;i++)
	{
		int tp;
		FindStr("\t\t\t*MESH_FACE",fModel);
		sscanf(szString,"\t\t\t*MESH_FACE %d:    A: %d B: %d C: %d",&tp,&(fa[i]),&(fb[i]),&(fc[i]));
	}

	FindStr("\t\t*MESH_NUMTVERTEX",fModel);
	sscanf(szString,"\t\t*MESH_NUMTVERTEX %d",&TVert);

	u=new GLfloat[TVert];
	v=new GLfloat[TVert];

	if(!u || !v) {return GL_MODEL_NOMEMORY;fclose(fModel);}

	for(i=0;i<TVert;i++)
	{
		int tp1,tp2;
		FindStr("\t\t\t*MESH_TVERT",fModel);
		sscanf(szString,"\t\t\t*MESH_TVERT %d %f %f %f",&tp1,&(u[i]),&(v[i]),&tp2);
	}

	FindStr("\t\t*MESH_NUMTVFACES",fModel);
	sscanf(szString,"\t\t*MESH_NUMTVFACES %d",&TFaces);

	if(Faces!=TFaces)  // Cannot be, but .......
	{
		delete(vx);delete(vy);delete(vz);
		delete(nx);delete(ny);delete(nz);
		delete(fa);delete(fb);delete(fc);
		delete(u);delete(v);
		fclose(fModel);
		return GL_MODEL_INVALIDFORMAT;
	}

	tfa=new unsigned int[TFaces];
	tfb=new unsigned int[TFaces];
	tfc=new unsigned int[TFaces];

	for(i=0;i<TFaces;i++)
	{
		int tp;
		FindStr("\t\t\t*MESH_TFACE",fModel);
		sscanf(szString,"\t\t\t*MESH_TFACE %d %d %d %d",&tp,&(tfa[i]),&(tfb[i]),&(tfc[i]));
	}

	for(i=0;i<Faces;i++)
	{
		int tp;
		FindStr("\t\t\t*MESH_FACENORMAL",fModel);
		sscanf(szString,"\t\t\t*MESH_FACENORMAL %d %f %f %f",&tp,&(nx[i]),&(ny[i]),&(nz[i]));
	}

	glListNum=glGenLists(1);
	if(!glListNum) return GL_MODEL_NOLIST;

	GLfloat mat_ambient[]={mat_a_r,mat_a_g,mat_a_b};
	GLfloat mat_diffuse[]={mat_d_r,mat_d_g,mat_d_b};
	GLfloat mat_specular[]={mat_s_r,mat_s_g,mat_s_b};
	GLfloat mat_shine[]={shine*shine_strength*128};

	glMatrixMode(GL_MODELVIEW);glPushMatrix();glLoadIdentity();

	glNewList(glListNum,GL_COMPILE);

	glMaterialfv(GL_FRONT,GL_AMBIENT,mat_ambient);
	glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse);
	glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular);
	glMaterialfv(GL_FRONT,GL_SHININESS,mat_shine);

	glBegin(GL_TRIANGLES);
	for(i=0;i<Faces;i++)
	{
		glNormal3f(nx[i],ny[i],nz[i]);glTexCoord2f(u[tfa[i]],v[tfa[i]]);glVertex3f(vx[fa[i]],vy[fa[i]],vz[fa[i]]);
		glNormal3f(nx[i],ny[i],nz[i]);glTexCoord2f(u[tfb[i]],v[tfb[i]]);glVertex3f(vx[fb[i]],vy[fb[i]],vz[fb[i]]);
		glNormal3f(nx[i],ny[i],nz[i]);glTexCoord2f(u[tfc[i]],v[tfc[i]]);glVertex3f(vx[fc[i]],vy[fc[i]],vz[fc[i]]);
	}

	glEnd();glEndList();glPopMatrix();

	delete(vx);delete(vy);delete(vz);
	delete(nx);delete(ny);delete(nz);
	delete(fa);delete(fb);delete(fc);
	delete(tfa);delete(tfb);delete(tfc);
	delete(u);delete(v);
	*ListNum=glListNum;
	fclose(fModel);
	return GL_MODEL_OK;
}