/*  TA3D, a remake of Total Annihilation
    Copyright (C) 2005  Roland BROCHARD

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA*/

/*-----------------------------------------------------------------------------------\
|                                         3do.cpp                                    |
|  ce fichier contient les structures, classes et fonctions nécessaires à la lecture |
| des fichiers 3do de total annihilation qui sont les fichiers contenant les modèles |
| 3d des objets du jeu.                                                              |
|                                                                                    |
\-----------------------------------------------------------------------------------*/

#include <allegro.h>
#include <alleggl.h>
#include <GL/glu.h>
#include "matrix.h"
#include "ta3dbase.h"
#include "3do.h"
#include "particles.h"

TEXTURE_MANAGER	texture_manager;
MODEL_MANAGER	model_manager;

	int TEXTURE_MANAGER::load_gaf(byte *data)
	{
		int nb_entry=get_gaf_nb_entry(data);
		int n_nbtex=nbtex+nb_entry;
		GLuint *n_gl=(GLuint*) malloc(sizeof(GLuint)*n_nbtex);
		char **n_name=(char**) malloc(sizeof(char*)*n_nbtex);
		BITMAP **n_bmp=(BITMAP**) malloc(sizeof(BITMAP*)*n_nbtex);
		int i;
		for(i=0;i<nbtex;i++) {
			n_gl[i]=gltex[i];
			n_name[i]=name[i];
			n_bmp[i]=bmptex[i];
			}
		free(gltex);
		free(name);
		name=n_name;
		gltex=n_gl;
		bmptex=n_bmp;
		for(i=0;i<nb_entry;i++) {
			int nb_img=get_gaf_nb_img(data,i);
			BITMAP *img=read_gaf_img(data,i,0,NULL,NULL,false);
			BITMAP *tmp=create_bitmap(img->w,img->h);
			blit(img,tmp,0,0,0,0,img->w,img->h);
			destroy_bitmap(img);
			img=tmp;
			name[nbtex+i]=get_gaf_entry_name(data,i);
			if(g_useTextureCompression)
				allegro_gl_set_texture_format(GL_COMPRESSED_RGB_ARB);
			else
				allegro_gl_set_texture_format(GL_RGB5);
			gltex[nbtex+i]=allegro_gl_make_texture(img);
			glBindTexture(GL_TEXTURE_2D,gltex[nbtex+i]);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
			bmptex[nbtex+i]=img;
			}
		nbtex+=nb_entry;
		return 0;
	}

	void SCRIPT_DATA::move(float dt)
	{
		for(int i=0;i<3;i++)
			for(int e=0;e<nb_piece;e++) {
				float a=axe[i][e].move_distance;
				if(a!=0.0f) {
					float c=axe[i][e].move_speed*dt;
					axe[i][e].move_distance-=c;
					axe[i][e].pos+=c;
					if((a>0.0f && axe[i][e].move_distance<0.0f) || (a<0.0f && axe[i][e].move_distance>0.0f)) {
						axe[i][e].pos+=axe[i][e].move_distance;
						axe[i][e].move_distance=0.0f;
						}
					}
				while(axe[i][e].angle>180.0f)	axe[i][e].angle-=360.0f;		// Maintient l'angle dans les limites
				while(axe[i][e].angle<-180.0f)	axe[i][e].angle+=360.0f;
				a=axe[i][e].rot_angle;
				if((axe[i][e].rot_speed!=0.0f || axe[i][e].rot_accel!=0.0f) && ((a!=0.0f && axe[i][e].rot_limit) || !axe[i][e].rot_limit)) {
					float b=axe[i][e].rot_speed;
					if(b<-7200.0f)
						b=axe[i][e].rot_speed=-7200.0f;
					if(b>7200.0f)
						b=axe[i][e].rot_speed=7200.0f;
					axe[i][e].rot_speed+=axe[i][e].rot_accel*dt;
					if(axe[i][e].rot_speed_limit)
						if((b<0.0f && axe[i][e].rot_speed-axe[i][e].rot_target_speed>0.0f) || (b>0.0f && axe[i][e].rot_speed-axe[i][e].rot_target_speed<0.0f)) {
							axe[i][e].rot_accel=0.0f;
							axe[i][e].rot_speed=axe[i][e].rot_target_speed;
							axe[i][e].rot_speed_limit=false;
							}
					float c=axe[i][e].rot_speed*dt;
					axe[i][e].angle+=c;
					if(axe[i][e].rot_limit) {
						axe[i][e].rot_angle-=c;
						if((a>0.0f && axe[i][e].rot_angle<0.0f) || (a<0.0f && axe[i][e].rot_angle>0.0f)) {
							axe[i][e].angle+=axe[i][e].rot_angle;
							axe[i][e].rot_angle=0.0f;
							}
						}
					}
				}
	}

	int OBJECT::load_obj(byte *data,int offset,int dec)
	{
		destroy();					// Au cas où l'objet ne serait pas vierge

		tagObject header;				// Lit l'en-tête
		header.VersionSignature=*((int*)(data+offset));
		header.NumberOfVertexes=*((int*)(data+offset+4));
		header.NumberOfPrimitives=*((int*)(data+offset+8));
		header.OffsetToselectionPrimitive=*((int*)(data+offset+12));
		header.XFromParent=*((int*)(data+offset+16));
		header.YFromParent=*((int*)(data+offset+20));
		header.ZFromParent=*((int*)(data+offset+24));
		header.OffsetToObjectName=*((int*)(data+offset+28));
		header.Always_0=*((int*)(data+offset+32));
		header.OffsetToVertexArray=*((int*)(data+offset+36));
		header.OffsetToPrimitiveArray=*((int*)(data+offset+40));
		header.OffsetToSiblingObject=*((int*)(data+offset+44));
		header.OffsetToChildObject=*((int*)(data+offset+48));

		nb_vtx=header.NumberOfVertexes;
		nb_prim=header.NumberOfPrimitives;
		name=strdup((char*)(data+header.OffsetToObjectName));
		int i;
#ifdef DEBUG_MODE
/*		for(i=0;i<dec;i++)
			printf("  ");
		printf("%s",name);
		for(i=0;i<20-2*dec-strlen(name);i++)
			printf(" ");
		printf("-> nb_vtx=%d | nb_prim=%d\n",nb_vtx,nb_prim);*/
#endif
		if(header.OffsetToChildObject) {					// Charge récursivement les différents objets du modèle
			child=(OBJECT*) malloc(sizeof(OBJECT));
			child->init();
			child->load_obj(data,header.OffsetToChildObject,dec+1);
			}
		if(header.OffsetToSiblingObject) {					// Charge récursivement les différents objets du modèle
			next=(OBJECT*) malloc(sizeof(OBJECT));
			next->init();
			next->load_obj(data,header.OffsetToSiblingObject,dec);
			}
		points=(POINTF*) malloc(sizeof(POINTF)*nb_vtx);		// Alloue la mémoire nécessaire pour stocker les points
		int f_pos;
		float div=1.0f/65536.0f*0.5f;
		pos_from_parent.x=header.XFromParent*div;
		pos_from_parent.y=header.YFromParent*div;
		pos_from_parent.z=-header.ZFromParent*div;
		f_pos=header.OffsetToVertexArray;
		for(i=0;i<nb_vtx;i++) {				// Lit le tableau de points stocké dans le fichier
			tagVertex vertex;
			vertex.x=*((int*)(data+f_pos));		f_pos+=4;
			vertex.y=*((int*)(data+f_pos));		f_pos+=4;
			vertex.z=*((int*)(data+f_pos));		f_pos+=4;
			points[i].x=vertex.x*div;
			points[i].y=vertex.y*div;
			points[i].z=-vertex.z*div;
			}
		f_pos=header.OffsetToPrimitiveArray;
		int n_index=0;
		selprim=header.OffsetToselectionPrimitive;
		for(i=0;i<nb_prim;i++) {					// Compte le nombre de primitive de chaque sorte
			tagPrimitive primitive;
			primitive.ColorIndex=*((int*)(data+f_pos));						f_pos+=4;
			primitive.NumberOfVertexIndexes=*((int*)(data+f_pos));			f_pos+=4;
			primitive.Always_0=*((int*)(data+f_pos));						f_pos+=4;
			primitive.OffsetToVertexIndexArray=*((int*)(data+f_pos));		f_pos+=4;
			primitive.OffsetToTextureName=*((int*)(data+f_pos));			f_pos+=4;
			primitive.Unknown_1=*((int*)(data+f_pos));						f_pos+=4;
			primitive.Unknown_2=*((int*)(data+f_pos));						f_pos+=4;
			primitive.Unknown_3=*((int*)(data+f_pos));						f_pos+=4;

			switch(primitive.NumberOfVertexIndexes)
			{
			case 1:		nb_p_index++;	break;
			case 2:		nb_l_index+=2;	break;
			default:
				n_index+=primitive.NumberOfVertexIndexes;
				nb_t_index++;
			};
			}
#ifdef DEBUG_MODE
//		printf("(%d,%d,%d)\n",nb_p_index,nb_l_index,nb_t_index);
#endif

		if(nb_p_index>0)				// Alloue la mémoire nécessaire pour stocker les primitives
			p_index=(GLuint*) malloc(sizeof(GLuint)*nb_p_index);
		if(nb_l_index>0)
			l_index=(GLuint*) malloc(sizeof(GLuint)*nb_l_index);
		if(nb_t_index>0) {
			tex=(GLuint*) malloc(sizeof(GLuint)*nb_t_index);
			usetex=(byte*) malloc(sizeof(byte)*nb_t_index);
			nb_index=(short*) malloc(sizeof(short)*nb_t_index);
			t_index=(GLuint*) malloc(sizeof(GLuint)*n_index);
			}

		f_pos=header.OffsetToPrimitiveArray;
		int pos_p=0,pos_l=0,pos_t=0,cur=0;
		int nb_diff_tex=0;
		int index_tex[nb_prim];
		for(i=0;i<nb_prim;i++) {					// Compte le nombre de primitive de chaque sorte
			tagPrimitive primitive;
			primitive.ColorIndex=*((int*)(data+f_pos));						f_pos+=4;
			primitive.NumberOfVertexIndexes=*((int*)(data+f_pos));			f_pos+=4;
			primitive.Always_0=*((int*)(data+f_pos));						f_pos+=4;
			primitive.OffsetToVertexIndexArray=*((int*)(data+f_pos));		f_pos+=4;
			primitive.OffsetToTextureName=*((int*)(data+f_pos));			f_pos+=4;
			primitive.Unknown_1=*((int*)(data+f_pos));						f_pos+=4;
			primitive.Unknown_2=*((int*)(data+f_pos));						f_pos+=4;
			primitive.Unknown_3=*((int*)(data+f_pos));						f_pos+=4;

			switch(primitive.NumberOfVertexIndexes)
			{
			case 1:
				p_index[pos_p++]=*((short*)(data+primitive.OffsetToVertexIndexArray));
				break;
			case 2:
				l_index[pos_l++]=*((short*)(data+primitive.OffsetToVertexIndexArray));
				l_index[pos_l++]=*((short*)(data+primitive.OffsetToVertexIndexArray+2));
				break;
			default:
				nb_index[cur]=primitive.NumberOfVertexIndexes;
				tex[cur]=texture_manager.get_texture_index((char*)(data+primitive.OffsetToTextureName));
				usetex[cur]=1;
				if(texture_manager.get_texture_index((char*)(data+primitive.OffsetToTextureName))==-1)
					usetex[cur]=0;
				else {														// Code pour la création d'une texture propre à chaque modèle
					bool al_in=false;
					int indx=texture_manager.get_texture_index((char*)(data+primitive.OffsetToTextureName));
					for(int e=0;e<nb_diff_tex;e++)
						if(index_tex[e]==indx) {
							al_in=true;
							break;
							}
					if(!al_in)
						index_tex[nb_diff_tex++]=indx;
				}
				for(int e=0;e<nb_index[cur];e++)
					t_index[pos_t++]=*((short*)(data+primitive.OffsetToVertexIndexArray+e*2));
				cur++;
			};
			}
/*------------------------------Création de la texture unique pour l'unité--------------*/
		int px[nb_diff_tex],py[nb_diff_tex];			// Pour placer les différentes mini-textures sur une grande texture
		int mx=0,my=0;
		for(i=0;i<nb_diff_tex;i++) {
			int dx=texture_manager.bmptex[index_tex[i]]->w,dy=texture_manager.bmptex[index_tex[i]]->h;
			px[i]=py[i]=0;
			if(i!=0)
				for(int e=0;e<i;e++) {
					int fx=texture_manager.bmptex[index_tex[e]]->w,fy=texture_manager.bmptex[index_tex[e]]->h;
					bool found[3];
					found[0]=found[1]=found[2]=true;
					int j;

					px[i]=px[e]+fx;		py[i]=py[e];
					for(j=0;j<i;j++) {
						int gx=texture_manager.bmptex[index_tex[j]]->w,gy=texture_manager.bmptex[index_tex[j]]->h;
						if(coupe(px[i],py[i],dx,dy,px[j],py[j],gx,gy)) {
							found[0]=false;
							break;
							}
						}

					px[i]=px[e];		py[i]=py[e]+fy;
					for(j=0;j<i;j++) {
						int gx=texture_manager.bmptex[index_tex[j]]->w,gy=texture_manager.bmptex[index_tex[j]]->h;
						if(coupe(px[i],py[i],dx,dy,px[j],py[j],gx,gy)) {
							found[2]=false;
							break;
							}
						}
					px[i]=px[e]+fx;		py[i]=0;
					for(j=0;j<i;j++) {
						int gx=texture_manager.bmptex[index_tex[j]]->w,gy=texture_manager.bmptex[index_tex[j]]->h;
						if(coupe(px[i],py[i],dx,dy,px[j],py[j],gx,gy)) {
							found[1]=false;
							break;
							}
						}
					bool deborde=false;
					bool found_one=false;
					int deb=0;
					if(found[1]) {
						px[i]=px[e]+fx;
						py[i]=0;
						deborde=false;
						if(px[i]+dx>mx || py[i]+dy>my) deborde=true;
						deb=max(mx,px[i]+dx)*max(py[i]+dy,my)-mx*my;
						found_one=true;
						}
					if(found[0] && (!found_one || deborde)) {
						px[i]=px[e]+fx;
						py[i]=py[e];
						deborde=false;
						if(px[i]+dx>mx || py[i]+dy>my) deborde=true;
						deb=max(mx,px[i]+dx)*max(py[i]+dy,my)-mx*my;
						found_one=true;
						}
					if(found[2] && deborde) {
						int ax=px[i],ay=py[i];
						px[i]=px[e];
						py[i]=py[e]+fy;
						deborde=false;
						if(px[i]+dx>mx || py[i]+dy>my) deborde=true;
						int deb2=max(mx,px[i]+dx)*max(py[i]+dy,my)-mx*my;
						if(found_one && deb<deb2) {
							px[i]=ax;	py[i]=ay;
							}
						else
							found_one=true;
						}
					if(found_one)			// On a trouvé une position qui convient
						break;
					}
			if(px[i]+dx>mx) mx=px[i]+dx;
			if(py[i]+dy>my) my=py[i]+dy;
			}
		BITMAP *bmp=create_bitmap(mx,my);
		clear(bmp);
		for(i=0;i<nb_diff_tex;i++)
			blit(texture_manager.bmptex[index_tex[i]],bmp,0,0,px[i],py[i],texture_manager.bmptex[index_tex[i]]->w,texture_manager.bmptex[index_tex[i]]->h);
		if(g_useTextureCompression)
			allegro_gl_set_texture_format(GL_COMPRESSED_RGB_ARB);
		else
			allegro_gl_set_texture_format(GL_RGB5);
		gltex=allegro_gl_make_texture(bmp);	dtex=true;
		glBindTexture(GL_TEXTURE_2D,gltex);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
		destroy_bitmap(bmp);
		int nb_total_point=0;
		for(i=0;i<nb_t_index;i++)
			nb_total_point+=nb_index[i];
		nb_total_point+=nb_l_index;
		POINTF *p=(POINTF*) malloc(sizeof(POINTF)*nb_total_point*2);			// *2 pour le volume d'ombre
		for(i=0;i<nb_total_point-nb_l_index;i++) {
			p[i]=points[t_index[i]];
			t_index[i]=i;
			}
		for(i=nb_total_point-nb_l_index;i<nb_total_point;i++) {
			int e=i-nb_total_point+nb_l_index;
			p[i]=points[l_index[e]];
			l_index[e]=i;
			}
		free(points);
		points=p;
		nb_vtx=nb_total_point;
		free(usetex);
		usetex=NULL;

		int nb_triangle=0;
		for(i=0;i<nb_t_index;i++)
			if(i!=selprim)
				nb_triangle+=nb_index[i]-2;
		GLuint *index=(GLuint*) malloc(sizeof(GLuint)*nb_triangle*3);
		tcoord=(float*) malloc(sizeof(float)*nb_vtx*2);
		cur=0;
		int curt=0;
		pos_t=0;
		for(i=0;i<nb_t_index;i++)
			for(int e=0;e<nb_index[i];e++) {
				if(i==selprim) {
					if(e==0) {
						sel[0]=t_index[cur];
						sel[1]=t_index[cur+1];
						sel[2]=t_index[cur+2];
						sel[3]=t_index[cur+3];
						cur+=nb_index[i];
						}
					continue;
					}
				if(e<3)
					index[pos_t++]=t_index[cur];
				else {
					index[pos_t]=index[pos_t-3];	pos_t++;
					index[pos_t]=index[pos_t-2];	pos_t++;
					index[pos_t++]=t_index[cur];
					}
				tcoord[curt]=0.0f;
				tcoord[curt+1]=0.0f;
				if(tex[i]!=-1) {
					switch(e&3)
					{
					case 0:						break;
					case 1:
						tcoord[curt]+=((float)texture_manager.bmptex[tex[i]]->w-1)/(mx-1);
						break;
					case 2:
						tcoord[curt]+=((float)texture_manager.bmptex[tex[i]]->w-1)/(mx-1);
						tcoord[curt+1]+=((float)texture_manager.bmptex[tex[i]]->h-1)/(my-1);
						break;
					case 3:
						tcoord[curt+1]+=((float)texture_manager.bmptex[tex[i]]->h-1)/(my-1);
						break;
					};
					int indx=0;
					for(int f=0;f<nb_diff_tex;f++)
						if(tex[i]==index_tex[f]) {
							indx=f;
							break;
							}
					tcoord[curt]+=(float)px[indx]/(mx-1);
					tcoord[curt+1]+=(float)py[indx]/(my-1);
					}
				cur++;
				curt+=2;
				}
		for(cur=0;cur<pos_t;cur+=3) {			// Petite inversion pour avoir un affichage correct
			GLuint t=index[cur+1];
			index[cur+1]=index[cur+2];
			index[cur+2]=t;
			}
		nb_t_index=nb_triangle*3;
		free(t_index);
		t_index=index;
/*--------------------------------------------------------------------------------------*/

		if(nb_t_index>0) {			// Calcule les normales pour l'éclairage
			N=(VECTOR*) malloc(sizeof(VECTOR)*nb_vtx);
			for(i=0;i<nb_vtx;i++)
				N[i].x=N[i].z=N[i].y=0.0f;
			for(i=0;i<nb_t_index;i+=3) {
				VECTOR AB,AC,Normal;
				AB=points[t_index[i]]>>points[t_index[i+1]];
				AC=points[t_index[i]]>>points[t_index[i+2]];
				Normal=AB*AC;	Normal.Unit();
				for(int e=0;e<3;e++)
					N[t_index[i+e]]=N[t_index[i+e]]+Normal;
				}
			for(i=0;i<nb_vtx;i++)
				N[i].Unit();
			}
		return 0;
	}

	void OBJECT::create_from_2d(BITMAP *bmp,float w,float h,float max_h)
	{
		destroy();					// Au cas où l'objet ne serait pas vierge

		pos_from_parent.x=0.0f;
		pos_from_parent.y=0.0f;
		pos_from_parent.z=0.0f;
		selprim=-1;
		child=NULL;
		next=NULL;
		nb_l_index=0;
		nb_p_index=0;
		l_index=NULL;
		p_index=NULL;

		nb_vtx=256;
		nb_t_index=1350;
		points=(POINTF*) malloc(sizeof(POINTF)*nb_vtx);
		tcoord=(float*) malloc(sizeof(float)*nb_vtx*2);
		t_index=(GLuint*) malloc(sizeof(GLuint)*nb_t_index);
		if(points==NULL || tcoord==NULL || t_index==NULL) {
			printf("ERREUR: plus de mémoire disponible!!\n");
			exit(1);
			}

		int i,x,y;

		for(y=0;y<16;y++)						// Maillage (sommets)
			for(x=0;x<16;x++) {
				points[y*16+x].x=(x-7.5f)*w/7.5f*0.5f;
				points[y*16+x].z=(y-7.5f)*h/7.5f*0.5f;
				tcoord[(y*16+x<<1)]=x/15.0f;
				tcoord[(y*16+x<<1)+1]=y/15.0f;
				}
		for(y=0;y<15;y++)						// Maillage (triangles)
			for(x=0;x<15;x++) {
				t_index[(y*15+x)*6]=y*16+x;
				t_index[(y*15+x)*6+1]=(y+1)*16+x;
				t_index[(y*15+x)*6+2]=y*16+x+1;
				t_index[(y*15+x)*6+3]=y*16+x+1;
				t_index[(y*15+x)*6+4]=(y+1)*16+x;
				t_index[(y*15+x)*6+5]=(y+1)*16+x+1;
				}

		int tmp[16][16];

		int med=0;
		int div=0;
		for(y=0;y<16;y++)						// Carte miniature en nuances de gris
			for(x=0;x<16;x++) {
				int c=0;
				int n=0;
				bool zero=false;
				for(int py=y*bmp->h/16;py<(y+1)*bmp->h/16;py++)
					for(int px=x*bmp->w/16;px<(x+1)*bmp->w/16;px++) {
						int pc=getpixel(bmp,px,py);
						c+=getr(pc)+getg(pc)+getb(pc);
						if(geta(pc)<128 || pc==0xFF00FF)
							zero=true;
						n+=3;
						}
				if(zero)	{	c=-0xFFFFFF;	n=1;	}
				tmp[y][x]=c/n;
				if(!zero) {
					med+=tmp[y][x];
					div++;
					}
				}
		if(div==0)	div=1;						// Il y a des trucs bizarres des fois!
		med=(med+(div>>1))/div;
		for(y=0;y<16;y++)						// Carte miniature en nuances de gris
			for(x=0;x<16;x++)
				if(tmp[y][x]==-0xFFFFFF)
					tmp[y][x]=med;

		points[0].y=0.0f;
		for(y=1;y<16;y++)			// x=0
			points[y*16].y=0.0f;
		for(x=1;x<16;x++)			// y=0
			points[x].y=0.0f;
		for(y=1;y<16;y++)
			for(x=1;x<16;x++) {
				int d_h0=tmp[y][x-1]-med;
				int d_h1=tmp[y-1][x]-med;
				int d_h=tmp[y][x]-med;
				float l=sqrt(d_h0*d_h0+d_h1*d_h1);
				float dhx,dhy;
				if(l==0.0f)
					dhx=dhy=0.0f;
				else {
					dhx=d_h*fabs(d_h0)/l;
					dhy=d_h*fabs(d_h1)/l;
					}
				points[y*16+x].y=(points[y*16+x-1].y+dhx+points[(y-1)*16+x].y+dhy)*0.5f;
				}
		float maxh=0.0f,minh=0.0f;
		for(i=0;i<256;i++) {
			if(minh>points[i].y)	minh=points[i].y;
			if(maxh<points[i].y)	maxh=points[i].y;
			}
		for(y=1;y<16;y++)			// x=15
			points[y*16+15].y=minh;
		for(x=1;x<16;x++)			// y=15
			points[15*16+x].y=minh;
		if(maxh==minh)
			for(i=0;i<256;i++)
				points[i].y=0.0f;
		else
			for(i=0;i<256;i++)
				points[i].y=(points[i].y-minh)*max_h/(maxh-minh);

		if(g_useTextureCompression)
			allegro_gl_set_texture_format(GL_COMPRESSED_RGBA_ARB);
		else
			allegro_gl_set_texture_format(GL_RGB5_A1);
		gltex=allegro_gl_make_texture(bmp);	dtex=true;
		glBindTexture(GL_TEXTURE_2D,gltex);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);

/*--------------------------------------------------------------------------------------*/

		N=(VECTOR*) malloc(sizeof(VECTOR)*nb_vtx);
		for(i=0;i<nb_vtx;i++)
			N[i].x=N[i].z=N[i].y=0.0f;
		for(i=0;i<nb_t_index;i+=3) {
			VECTOR AB,AC,Normal;
			AB=points[t_index[i]]>>points[t_index[i+1]];
			AC=points[t_index[i]]>>points[t_index[i+2]];
			Normal=AB*AC;	Normal.Unit();
			for(int e=0;e<3;e++)
				N[t_index[i+e]]=N[t_index[i+e]]+Normal;
			}
		for(i=0;i<nb_vtx;i++)
			N[i].Unit();
	}

	bool OBJECT::draw(SCRIPT_DATA *data_s,bool sel_primitive,bool alset,bool notex)
	{
		glPushMatrix();
		glTranslatef(pos_from_parent.x,pos_from_parent.y,pos_from_parent.z);
		bool hide=false;
		if(script_index>=0 && data_s) {
			glTranslatef(data_s->axe[0][script_index].pos, data_s->axe[1][script_index].pos, data_s->axe[2][script_index].pos);
			glRotatef(data_s->axe[0][script_index].angle, 1.0f, 0.0f, 0.0f);
			glRotatef(data_s->axe[1][script_index].angle, 0.0f, 1.0f, 0.0f);
			glRotatef(data_s->axe[2][script_index].angle, 0.0f, 0.0f, 1.0f);
			hide=data_s->flag[script_index]&FLAG_HIDE;
			}
		bool set=false;
		if(nb_t_index>0 && nb_vtx>0 && !hide && t_index!=NULL) {
			if(!alset) {
				glEnableClientState(GL_VERTEX_ARRAY);		// Les sommets
				glEnableClientState(GL_NORMAL_ARRAY);
				if(notex)
					glDisableClientState(GL_TEXTURE_COORD_ARRAY);
				else
					glEnableClientState(GL_TEXTURE_COORD_ARRAY);
				glEnable(GL_LIGHTING);
				if(notex)
					glDisable(GL_TEXTURE_2D);
				else
					glEnable(GL_TEXTURE_2D);
				alset=true;
				}
			set=true;
			glVertexPointer( 3, GL_FLOAT, 0, points);
			glNormalPointer( GL_FLOAT, 0, N);
			if(!notex) {
				glBindTexture(GL_TEXTURE_2D,gltex);
				glTexCoordPointer(2, GL_FLOAT, 0, tcoord);
				}
			glDrawElements(GL_TRIANGLES, nb_t_index,GL_UNSIGNED_INT,t_index);		// dessine le tout
			}
#ifdef DEBUG_MODE_3DO
		if(nb_l_index>0 && nb_vtx>0) {
			glEnableClientState(GL_VERTEX_ARRAY);		// Les sommets
			glDisableClientState(GL_NORMAL_ARRAY);
			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
			glDisable(GL_LIGHTING);
			glDisable(GL_TEXTURE_2D);
			alset=false;
			if(!set)
				glVertexPointer( 3, GL_FLOAT, 0, points);
			set=true;
			glDrawElements(GL_LINES, nb_l_index,GL_UNSIGNED_INT,l_index);		// dessine le tout
			}
#endif
		if(sel_primitive && selprim>=0 && nb_vtx>0) {
			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
			glDisableClientState(GL_NORMAL_ARRAY);
			glDisable(GL_LIGHTING);
			glDisable(GL_TEXTURE_2D);
			glDisable(GL_CULL_FACE);
			if(!set)
				glVertexPointer( 3, GL_FLOAT, 0, points);
			glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
			glColor3f(1.0f,1.0f,0.0f);
			glDrawElements(GL_QUADS, 4,GL_UNSIGNED_INT,sel);		// dessine la primitive de sélection
			if(notex) {
				float var=fabs(1.0f-(Atimer%MSEC_TO_TIMER(1000))/(float)MSEC_TO_TIMER(500));
				glColor3f(0.0f,var,0.0f);
				}
			else
				glColor3f(1.0f,1.0f,1.0f);
			glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
			glEnable(GL_CULL_FACE);
			alset=false;
			}
		if(child) {
			glPushMatrix();
			alset=child->draw(data_s,sel_primitive,alset,notex);
			glPopMatrix();
			}
		if(next) {
			glPopMatrix();
			glPushMatrix();
			alset=next->draw(data_s,sel_primitive,alset,notex);
			glPopMatrix();
			}
		else
			glPopMatrix();
		return alset;
	}

	void OBJECT::compute_coord(SCRIPT_DATA *data_s,VECTOR *pos,bool c_part,int p_tex,VECTOR *target,POINTF *upos,MATRIX_4x4 *M,float size,VECTOR *center)
	{
		VECTOR opos=*pos;
		MATRIX_4x4 OM;
		if(M)
			OM=*M;
		if(script_index>=0 && data_s) {
			if(M) {
				VECTOR ipos;
				ipos.x=data_s->axe[0][script_index].pos;
				ipos.y=data_s->axe[1][script_index].pos;
				ipos.z=data_s->axe[2][script_index].pos;
				*pos=*pos+(pos_from_parent+ipos)*(*M);
				*M=RotateZ(data_s->axe[2][script_index].angle*DEG2RAD)*RotateY(data_s->axe[1][script_index].angle*DEG2RAD)*RotateX(data_s->axe[0][script_index].angle*DEG2RAD)*(*M);
				if(nb_l_index==2) {
					data_s->dir[script_index]=(points[l_index[0]]>>points[l_index[1]])*(*M);
					data_s->dir[script_index].Unit();
					ipos.x=points[l_index[0]].x;
					ipos.y=points[l_index[0]].y;
					ipos.z=points[l_index[0]].z;
					data_s->pos[script_index]=*pos+ipos*(*M);
					}
				else
					data_s->pos[script_index]=*pos;
				}
			}
		if((nb_t_index==0 || nb_vtx==0) && c_part && child==NULL && next==NULL) {			// Emission d'une particule
			VECTOR Dir;
			float life=1.0f;
			int nb=(rand()%30)+1;
			for(int i=0;i<nb;i++) {
				VECTOR t_mod;
				t_mod.x=((rand()%2001)-1000)*0.001f;
				t_mod.y=((rand()%2001)-1000)*0.001f;
				t_mod.z=((rand()%2001)-1000)*0.001f;
				t_mod.Unit();
				t_mod=(rand()%1001)*0.001f*size*t_mod;
				if(center)
					t_mod=t_mod+(*center);
				Dir=t_mod+*target-*pos;
				Dir.x-=upos->x;
				Dir.y-=upos->y;
				Dir.z-=upos->z;
				float dist=Dir.Norm();
				float speed=1.0f;
				particle_engine.emit_part(*upos+*pos,Dir,p_tex,1,speed,life,2.0f,true);
				}
			}
		if(child)
			child->compute_coord(data_s,pos,c_part,p_tex,target,upos,M,size,center);
		*pos=opos;
		if(M)
			*M=OM;
		if(next)
			next->compute_coord(data_s,pos,c_part,p_tex,target,upos,M,size,center);
	}

	bool OBJECT::draw_shadow(VECTOR Dir,SCRIPT_DATA *data_s,bool alset)
	{
		glPushMatrix();
		glTranslatef(pos_from_parent.x,pos_from_parent.y,pos_from_parent.z);
		bool hide=false;
		VECTOR ODir=Dir;
		if(script_index>=0 && data_s) {
			glTranslatef(data_s->axe[0][script_index].pos, data_s->axe[1][script_index].pos, data_s->axe[2][script_index].pos);
			glRotatef(data_s->axe[0][script_index].angle, 1.0f, 0.0f, 0.0f);
			glRotatef(data_s->axe[1][script_index].angle, 0.0f, 1.0f, 0.0f);
			glRotatef(data_s->axe[2][script_index].angle, 0.0f, 0.0f, 1.0f);
			Dir=Dir*RotateZ(-data_s->axe[2][script_index].angle*DEG2RAD)*RotateY(-data_s->axe[1][script_index].angle*DEG2RAD)*RotateX(-data_s->axe[0][script_index].angle*DEG2RAD);
			hide=data_s->flag[script_index]&FLAG_HIDE;
			}
			if(nb_t_index>0 && !hide) {
				if(!alset) {
					glDisable(GL_LIGHTING);
					glDisable(GL_TEXTURE_2D);
					glEnableClientState(GL_VERTEX_ARRAY);		// Les sommets
					glDisableClientState(GL_NORMAL_ARRAY);
					glDisableClientState(GL_TEXTURE_COORD_ARRAY);
					alset=true;
					}
				POINTF AP,BP,CP;
				POINTF A,B,C;
				int posA=0,posB=0,posC=0;
/*-----------------Code de calcul du cone d'ombre-------------------------*/
				if(shadow_index==NULL)
					shadow_index=(GLuint*) malloc(sizeof(GLuint)*nb_t_index*12);
				int nb_idx=0;

				if(t_line==NULL) {									// Repère les arêtes
					t_line=(int*) malloc(sizeof(int)*nb_t_index);
					line_v_idx[0]=(int*) malloc(sizeof(int)*nb_t_index);
					line_v_idx[1]=(int*) malloc(sizeof(int)*nb_t_index);
					nb_line=0;
					for(int i=0;i<nb_t_index;i+=3)
						for(int a=0;a<3;a++) {
							int idx=-1;
							for(int e=0;e<nb_line;e++)
								if((line_v_idx[0][e]==t_index[i+a] && line_v_idx[1][e]==t_index[i+((a+1)%3)]) || (line_v_idx[0][e]==t_index[i+((a+1)%3)] && line_v_idx[1][e]==t_index[i+a])) {
									idx=e;
									break;
									}
							if(idx==-1) {
								line_v_idx[0][nb_line]=t_index[i+a];
								line_v_idx[1][nb_line]=t_index[i+((a+1)%3)];
								idx=nb_line;	nb_line++;
								}
							t_line[i+a]=idx;
							}
					}

				bool line_on[nb_line];
				for(int i=0;i<nb_line;i++)	line_on[i]=false;

				for(int i=0;i<nb_t_index;i+=3) {
					VECTOR N=this->N[t_index[i]];
					if((N%Dir)>=0.0f) continue;
					line_on[t_line[i]]^=true;
					line_on[t_line[i+1]]^=true;
					line_on[t_line[i+2]]^=true;
					}
				for(int i=0;i<nb_line;i++) {
					if(!line_on[i])	continue;
					A=points[line_v_idx[0][i]];
					B=points[line_v_idx[1][i]];
					AP=A+Dir;				// Calculs de projection
					BP=B+Dir;
					points[line_v_idx[0][i]+nb_vtx]=AP;
					points[line_v_idx[1][i]+nb_vtx]=BP;

					shadow_index[nb_idx++]=line_v_idx[0][i];			shadow_index[nb_idx++]=line_v_idx[1][i];
					shadow_index[nb_idx++]=line_v_idx[1][i]+nb_vtx;		shadow_index[nb_idx++]=line_v_idx[0][i]+nb_vtx;
					}
				glVertexPointer( 3, GL_FLOAT, 0, points);
				glDrawElements(GL_QUADS, nb_idx,GL_UNSIGNED_INT,shadow_index);		// dessine le tout
				}
		if(child) {
			glPushMatrix();
			alset=child->draw_shadow(Dir,data_s,alset);
			glPopMatrix();
			}
		if(next) {
			glPopMatrix();
			glPushMatrix();
			alset=next->draw_shadow(ODir,data_s,alset);
			glPopMatrix();
			}
		else
			glPopMatrix();
		return alset;
	}

	bool OBJECT::hit(VECTOR Pos,VECTOR Dir,SCRIPT_DATA *data_s,VECTOR *I,MATRIX_4x4 M)
	{
		MATRIX_4x4 OM=M;
		bool hide=false;
		VECTOR ODir=Dir;
		bool is_hit=false;

		if(script_index>=0 && data_s) {
			VECTOR T=pos_from_parent;
			T.x+=data_s->axe[0][script_index].pos;
			T.y+=data_s->axe[1][script_index].pos;
			T.z+=data_s->axe[2][script_index].pos;
			M=RotateZ(data_s->axe[2][script_index].angle*DEG2RAD)*RotateY(data_s->axe[1][script_index].angle*DEG2RAD)*RotateX(data_s->axe[0][script_index].angle*DEG2RAD)*Translate(T)*M;
			hide=data_s->flag[script_index]&FLAG_HIDE;
			}
		VECTOR MP;
			if(nb_t_index>0 && !hide) {
				POINTF A,B,C,O;
				O.x=O.y=O.z=0.0f;
//-----------------Code de calcul d'intersection--------------------------
				for(int i=0;i<nb_t_index;i+=3) {
					A=points[t_index[i]]*M;
					B=points[t_index[i+1]]*M;
					C=points[t_index[i+2]]*M;
					VECTOR AB=A>>B;
					VECTOR AC=A>>C;
					VECTOR N=AB*AC;
					if(N%Dir==0.0f)	continue;
					N.Unit();
					float dist=((Pos-(O>>A))%N)/(N%Dir);
					VECTOR P_p=Pos+dist*Dir;

					float a,b,c;		// Coefficients pour que P soit le barycentre de A,B,C
					VECTOR AP=P_p-(O>>A);
					if(AC.y!=0.0f && AB.x*AC.y!=AB.y*AC.x) {
						b=(AP.x-AP.y*AC.x/AC.y)/(AB.x-AB.y*AC.x/AC.y);
						a=(AP.y-b*AB.y)/AC.y;
						}
					else if(AC.z!=0.0f && AB.x*AC.z!=AB.z*AC.x) {
						b=(AP.x-AP.z*AC.x/AC.z)/(AB.x-AB.z*AC.x/AC.z);
						a=(AP.z-b*AB.z)/AC.z;
						}
					else if(AC.x!=0.0f && AB.y*AC.x!=AB.x*AC.y) {
						b=(AP.y-AP.x*AC.y/AC.x)/(AB.y-AB.x*AC.y/AC.x);
						a=(AP.x-b*AB.x)/AC.x;
						}
					else if(AB.y!=0.0f && AC.x*AB.y!=AC.y*AB.x) {
						a=(AP.x-AP.y*AB.x/AB.y)/(AC.x-AC.y*AB.x/AB.y);
						b=(AP.y-a*AC.y)/AB.y;
						}
					else if(AB.z!=0.0f && AC.x*AB.z!=AC.z*AB.x) {
						a=(AP.x-AP.z*AB.x/AB.z)/(AC.x-AC.z*AB.x/AB.z);
						b=(AP.z-a*AC.z)/AB.z;
						}
					else if(AB.x!=0.0f && AC.y*AB.x!=AC.x*AB.y) {
						a=(AP.y-AP.x*AB.y/AB.x)/(AC.y-AC.x*AB.y/AB.x);
						b=(AP.x-a*AC.x)/AB.x;
						}
					else continue;		// Saute le point s'il n'est pas positionnable
					c=1.0f-a-b;
					if(a<0.0f || b<0.0f || c<0.0f) continue;		// Le point n'appartient pas au triangle
					if(!is_hit)
						MP=P_p;
					else
						if((MP-Pos)%Dir>(P_p-Pos)%Dir)
							MP=P_p;
					is_hit=true;
					}
				}
		if(child) {
			VECTOR MP2;
			bool nhit=child->hit(Pos,Dir,data_s,&MP2,M);
			if(nhit && !is_hit)
				MP=MP2;
			else if(nhit && is_hit)
				if((MP2-Pos)%Dir<(MP-Pos)%Dir)
					MP=MP2;
			is_hit|=nhit;
			}
		if(next) {
			VECTOR MP2;
			bool nhit=next->hit(Pos,Dir,data_s,&MP2,OM);
			if(nhit && !is_hit)
				MP=MP2;
			else if(nhit && is_hit)
				if((MP2-Pos)%Dir<(MP-Pos)%Dir)
					MP=MP2;
			is_hit|=nhit;
			}
		if(is_hit)
			*I=MP;
		return is_hit;
	}

/*	bool OBJECT::hit(VECTOR Pos,VECTOR Dir,SCRIPT_DATA *data_s,VECTOR *I)
	{
		VECTOR P=Pos-pos_from_parent;
		bool hide=false;
		VECTOR ODir=Dir;
		bool is_hit=false;

		MATRIX_4x4 IM=RotateX(data_s->axe[0][script_index].angle*DEG2RAD)*RotateY(data_s->axe[1][script_index].angle*DEG2RAD)*RotateZ(data_s->axe[2][script_index].angle*DEG2RAD);
		MATRIX_4x4 M=RotateZ(-data_s->axe[2][script_index].angle*DEG2RAD)*RotateY(-data_s->axe[1][script_index].angle*DEG2RAD)*RotateX(-data_s->axe[0][script_index].angle*DEG2RAD);

		if(script_index>=0 && data_s) {
			P.x-=data_s->axe[0][script_index].pos;
			P.y-=data_s->axe[1][script_index].pos;
			P.z-=data_s->axe[2][script_index].pos;
			P=P*M;
			Dir=Dir*M;
			hide=data_s->flag[script_index]&FLAG_HIDE;
			}
		VECTOR MP;
			if(nb_t_index>0 && !hide) {
				POINTF A,B,C,O;
				O.x=O.y=O.z=0.0f;
//-----------------Code de calcul d'intersection--------------------------
				for(int i=0;i<nb_t_index;i+=3) {
					A=points[t_index[i]];
					B=points[t_index[i+1]];
					C=points[t_index[i+2]];
					VECTOR AB=A>>B;
					VECTOR AC=A>>C;
					VECTOR N=AB*AC;
					if(N%Dir>=0.0f)	continue;
					N.Unit();
					float dist=(P-(O>>A))%N;
					if(dist<0.0f)	continue;
					VECTOR P_p=P+(dist/(N%Dir))*Dir;

					float a,b,c;		// Coefficients pour que P soit le barycentre de A,B,C
					VECTOR AP=P_p-(O>>A);
					if(AC.y!=0.0f && AB.x*AC.y!=AB.y*AC.x) {
						b=(AP.x-AP.y*AC.x/AC.y)/(AB.x-AB.y*AC.x/AC.y);
						a=(AP.y-b*AB.y)/AC.y;
						}
					else if(AC.z!=0.0f && AB.x*AC.z!=AB.z*AC.x) {
						b=(AP.x-AP.z*AC.x/AC.z)/(AB.x-AB.z*AC.x/AC.z);
						a=(AP.z-b*AB.z)/AC.z;
						}
					else if(AC.x!=0.0f && AB.y*AC.x!=AB.x*AC.y) {
						b=(AP.y-AP.x*AC.y/AC.x)/(AB.y-AB.x*AC.y/AC.x);
						a=(AP.x-b*AB.x)/AC.x;
						}
					else if(AB.y!=0.0f && AC.x*AB.y!=AC.y*AB.x) {
						a=(AP.x-AP.y*AB.x/AB.y)/(AC.x-AC.y*AB.x/AB.y);
						b=(AP.y-a*AC.y)/AB.y;
						}
					else if(AB.z!=0.0f && AC.x*AB.z!=AC.z*AB.x) {
						a=(AP.x-AP.z*AB.x/AB.z)/(AC.x-AC.z*AB.x/AB.z);
						b=(AP.z-a*AC.z)/AB.z;
						}
					else if(AB.x!=0.0f && AC.y*AB.x!=AC.x*AB.y) {
						a=(AP.y-AP.x*AB.y/AB.x)/(AC.y-AC.x*AB.y/AB.x);
						b=(AP.x-a*AC.x)/AB.x;
						}
					else continue;		// Saute le point s'il n'est pas positionnable
					c=1.0f-a-b;
					if(a<0.0f || b<0.0f || c<0.0f) continue;		// Le point n'appartient pas au triangle
					if(!is_hit)
						MP=P_p;
					else
						if((MP-P)%Dir>(P_p-P)%Dir)
							MP=P_p;
					is_hit=true;
					}
				}
		if(child) {
			VECTOR MP2;
			bool nhit=child->hit(P,Dir,data_s,&MP2);
			if(nhit && !is_hit)
				MP=MP2;
			else if(nhit && is_hit)
				if((MP2-P)%Dir<(MP-P)%Dir)
					MP=MP2;
			is_hit|=nhit;
			}
		if(is_hit) {
			if(script_index>=0 && data_s) {
				MP=MP*IM;
				MP.x+=data_s->axe[0][script_index].pos;
				MP.y+=data_s->axe[1][script_index].pos;
				MP.z+=data_s->axe[2][script_index].pos;
				}
			MP=MP+pos_from_parent;
			}
		if(next) {
			VECTOR MP2;
			bool nhit=next->hit(Pos,ODir,data_s,&MP2);
			if(nhit && !is_hit)
				MP=MP2;
			else if(nhit && is_hit)
				if((MP2-P)%ODir<(MP-P)%ODir)
					MP=MP2;
			is_hit|=nhit;
			}
		if(is_hit)
			*I=MP;
		return is_hit;
	}*/

float OBJECT::print_struct(float Y,float X,FONT *fnt)
{
//	if(nb_vtx==0 || nb_prim==0 || nb_p_index>0 || nb_l_index>0)
		allegro_gl_printf(fnt,X,Y,0.0f,0xFFFFFF,"%s",name);
		allegro_gl_printf(fnt,320.0f,Y,0.0f,0xFFFFFF,"(v:%d",nb_vtx);
		allegro_gl_printf(fnt,368.0f,Y,0.0f,0xFFFFFF,",p:%d",nb_prim);
		allegro_gl_printf(fnt,416.0f,Y,0.0f,0xFFFFFF,",t:%d",nb_t_index);
		allegro_gl_printf(fnt,464.0f,Y,0.0f,0xFFFFFF,",l:%d",nb_l_index);
		allegro_gl_printf(fnt,512.0f,Y,0.0f,0xFFFFFF,",p:%d)",nb_p_index);
//		allegro_gl_printf(fnt,320.0f,Y,0.0f,0xFFFFFF,"(v:%d,p:%d,t:%d,l:%d,p:%d)",nb_vtx,nb_prim,nb_t_index,nb_l_index,nb_p_index);
	Y+=8.0f;
	if(child)
		Y=child->print_struct(Y,X+8.0f,fnt);
	if(next)
		Y=next->print_struct(Y,X,fnt);
	return Y;
}
