/*  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*/

/*-----------------------------------------------------------------------------------\
|                                      particles.cpp                                 |
|  Ce fichier contient les structures, classes et fonctions nécessaires aux effets   |
| graphiques utilisants des particules comme la poussière, les effets de feu,        |
| d'explosion, de fumée ... Ce fichier est conçu pour fonctionner avec la librairie  |
| Allegro et l'addon AllegroGL pour l'utilisation d'OpenGl avec Allegro.             |
|                                                                                    |
\-----------------------------------------------------------------------------------*/

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

PARTICLE_ENGINE	particle_engine;

	int PARTICLE_ENGINE::addtex(char *file,char *filealpha)
	{
		dsmoke=true;
		if(partbmp==NULL)
			partbmp=create_bitmap_ex(32,256,256);
		BITMAP *bmp;
		if(filealpha)
			bmp=LoadMaskedTexBmp(file,filealpha);		// Avec canal alpha séparé
		else
			bmp=load_bitmap(file,NULL);					// Avec canal alpha intégré ou Sans canal alpha
		stretch_blit(bmp,partbmp,0,0,bmp->w,bmp->h,64*(ntex&3),64*(ntex>>2),64,64);
		ntex++;
		destroy_bitmap(bmp);
		if(ntex>1)
			glDeleteTextures(1,&parttex);
		allegro_gl_use_alpha_channel(true);
		allegro_gl_set_texture_format(GL_RGBA8);
		parttex=allegro_gl_make_texture(partbmp);
		allegro_gl_use_alpha_channel(false);
		glBindTexture(GL_TEXTURE_2D, parttex);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);

		return (ntex-1);
	}

	void PARTICLE_ENGINE::more_memory()			// Alloue de la mémoire supplémentaire
	{
		size+=100;
		PARTICLE *tmp=(PARTICLE*) malloc(sizeof(PARTICLE)*size);
		if(nb_part>0 && part)
			memcpy(tmp,part,sizeof(PARTICLE)*nb_part);
		if(part)
			free(part);
		part=tmp;

		if(point)				// Agrandi les tableaux pour le dessin des particules
			free(point);
		if(texcoord)
			free(texcoord);
		if(index)
			free(index);
		if(color)
			free(color);
		point=(VECTOR*) malloc(sizeof(VECTOR)*size<<2);
		texcoord=(GLfloat*) malloc(sizeof(GLfloat)*size<<3);
		index=(GLuint*) malloc(sizeof(GLuint)*size<<2);
		color=(GLfloat*) malloc(sizeof(GLfloat)*size<<4);
		for(int i=0;i<(size<<2);i++)
			index[i]=i;
	}

	void PARTICLE_ENGINE::emit_part(POINTF pos,VECTOR Dir,int tex,int nb,float speed,float life,float psize,bool white)
	{
		while(nb_part+nb>size)			// Si besoin alloue de la mémoire
			more_memory();
		POINTF O;
		O.x=O.y=O.z=0.0f;
		for(int i=0;i<nb;i++) {
			part[nb_part].px=-1;
			part[nb_part].Pos=O>>pos;
			part[nb_part].V=speed*Dir;
			part[nb_part].life=life;
			part[nb_part].mass=0.0f;
			part[nb_part].smoking=-1.0f;
			part[nb_part].gltex=tex;
			if(white) {
				part[nb_part].col[0]=1.0f;
				part[nb_part].col[1]=1.0f;
				part[nb_part].col[2]=1.0f;
				part[nb_part].col[3]=1.0f;
				}
			else {
				part[nb_part].col[0]=0.5f;
				part[nb_part].col[1]=0.5f;
				part[nb_part].col[2]=1.0f;
				part[nb_part].col[3]=1.0f;
				}
			part[nb_part].dcol[0]=0.0f;
			part[nb_part].dcol[1]=0.0f;
			part[nb_part].dcol[2]=0.0f;
			if(life>0.0f)
				part[nb_part].dcol[3]=-1.0f/life;
			else
				part[nb_part].dcol[3]=-0.1f;
			part[nb_part].angle=0.0f;
			part[nb_part].v_rot=(rand()%200)*0.01f-0.1f;
			part[nb_part].size=psize;
			part[nb_part++].dsize=0.0f;
			}
	}

	void PARTICLE_ENGINE::emit_lava(POINTF pos,VECTOR Dir,int tex,int nb,float speed,float life)
	{
		while(nb_part+nb>size)			// Si besoin alloue de la mémoire
			more_memory();
		POINTF O;
		O.x=O.y=O.z=0.0f;
		for(int i=0;i<nb;i++) {
			part[nb_part].px=-1;
			part[nb_part].Pos=O>>pos;
			float speed_mul=((rand()%100)*0.01f+0.01f);
			part[nb_part].V=speed_mul*speed*Dir;
			part[nb_part].life=life;
			part[nb_part].mass=1.0f;
			part[nb_part].smoking=-1.0f;
			part[nb_part].gltex=tex;
			part[nb_part].col[0]=1.0f;
			part[nb_part].col[1]=0.5f;
			part[nb_part].col[2]=0.5f;
			part[nb_part].col[3]=1.0f;
			part[nb_part].dcol[0]=0.0f;
			part[nb_part].dcol[1]=0.0f;
			part[nb_part].dcol[2]=0.0f;
			part[nb_part].dcol[3]=-1.0f/life;
			part[nb_part].angle=0.0f;
			part[nb_part].v_rot=(rand()%200)*0.01f-0.1f;
			part[nb_part].size=10.0f*(1.0f-speed_mul*0.9f);
			part[nb_part++].dsize=0.0f;
			}
	}

	void PARTICLE_ENGINE::make_smoke(POINTF pos,int tex,int nb,float speed,float mass)
	{
		while(nb_part+nb>size)			// Si besoin alloue de la mémoire
			more_memory();
		float pre=speed*0.01f;
		POINTF O;
		O.x=O.y=O.z=0.0f;
		for(int i=0;i<nb;i++) {
			part[nb_part].px=-1;
			part[nb_part].Pos=O>>pos;
			part[nb_part].V.y=((rand()%1000)+1)*0.001f;
			part[nb_part].V.x=((rand()%2001)-1000)*0.001f;
			part[nb_part].V.z=((rand()%2001)-1000)*0.001f;
			part[nb_part].V.Unit();
			part[nb_part].V=((rand()%100)+1)*pre*part[nb_part].V;
			part[nb_part].life=5.0f;
			part[nb_part].mass=mass;
			part[nb_part].smoking=-1.0f;
			part[nb_part].gltex=tex;
			part[nb_part].col[0]=1.0f;
			part[nb_part].col[1]=1.0f;
			part[nb_part].col[2]=1.0f;
			part[nb_part].col[3]=1.0f;
			part[nb_part].dcol[0]=0.0f;
			part[nb_part].dcol[1]=0.0f;
			part[nb_part].dcol[2]=0.0f;
			part[nb_part].dcol[3]=-0.2f;
			part[nb_part].angle=0.0f;
			part[nb_part].v_rot=(rand()%200)*0.01f-0.1f;
			part[nb_part].size=1.0f;
			part[nb_part++].dsize=10.0f;
			}
	}

	void PARTICLE_ENGINE::make_fire(POINTF pos,int tex,int nb,float speed)
	{
		while(nb_part+nb>size)			// Si besoin alloue de la mémoire
			more_memory();
		POINTF O;
		O.x=O.y=O.z=0.0f;
		for(int i=0;i<nb;i++) {
			part[nb_part].px=-1;
			part[nb_part].Pos=O>>pos;
			part[nb_part].V.y=((rand()%1000)+1)*0.001f;
			part[nb_part].V.x=((rand()%2001)-1000)*0.001f;
			part[nb_part].V.z=((rand()%2001)-1000)*0.001f;
			part[nb_part].V.Unit();
			part[nb_part].V=((rand()%100)+1)*0.01f*speed*part[nb_part].V;
			part[nb_part].life=1.0f;
			part[nb_part].mass=1.0f;
			part[nb_part].smoking=(rand()%100)*0.01f;
			part[nb_part].gltex=tex;
			part[nb_part].col[0]=1.0f;
			part[nb_part].col[1]=1.0f;
			part[nb_part].col[2]=1.0f;
			part[nb_part].col[3]=1.0f;
			part[nb_part].dcol[0]=0.0f;
			part[nb_part].dcol[1]=0.0f;
			part[nb_part].dcol[2]=0.0f;
			part[nb_part].dcol[3]=1.0f;
			part[nb_part].angle=0.0f;
			part[nb_part].v_rot=(rand()%200)*0.01f-0.1f;
			part[nb_part].size=15.0f;
			part[nb_part++].dsize=-15.0f;
			}
	}

	void PARTICLE_ENGINE::move(float dt,float g)
	{
		if(part==NULL || nb_part==0 || dt==0.0f) return;

		VECTOR G;
		POINTF O;
		O.x=O.y=O.z=0.0f;
		G.x=G.y=G.z=0.0f;
		G.y=dt*g;

		int i;
		float factor=exp(-0.1f*dt);
		float factor2=exp(-dt);
		bool dead=false;
		for(i=0;i<nb_part;i++) {
			VECTOR RAND;
			RAND.x=((rand()%2001)-1000)*0.01f;
			RAND.y=((rand()%2001)-1000)*0.01f;
			RAND.z=((rand()%2001)-1000)*0.01f;
			part[i].V=part[i].V-part[i].mass*G+dt*RAND;
			if(part[i].mass>0.0f)
				part[i].V=factor*part[i].V;
			else
				part[i].V=factor2*part[i].V;
			part[i].Pos=part[i].Pos+dt*part[i].V;
			part[i].size+=dt*part[i].dsize;
			part[i].angle+=dt*part[i].v_rot;
			part[i].col[0]+=dt*part[i].dcol[0];
			part[i].col[1]+=dt*part[i].dcol[1];
			part[i].col[2]+=dt*part[i].dcol[2];
			part[i].col[3]+=dt*part[i].dcol[3];
			part[i].life-=dt;
			if(part[i].smoking>0.0f && part[i].life<part[i].smoking) {
				part[i].life=part[i].col[3];
				part[i].mass=-1.0f;
				part[i].gltex=0;
				part[i].dcol[3]=-1.0f;
				part[i].dsize=15.0f;
				part[i].smoking=-1.0f;
				}
			dead|=part[i].life<0.0f;
			}
		if(dead) {
			int e=0;
			for(i=0;e+i<nb_part;) {				// Supprime les particules "mortes"
				part[i]=part[i+e];
				if(part[i].life<0.0f)
					e++;
				else
					i++;
				}
			nb_part-=e;
			if(nb_part<0)
				nb_part=0;
			}
	}

	void PARTICLE_ENGINE::draw(CAMERA *cam,int map_w,int map_h,int bloc_w,int bloc_h,byte **bmap)
	{
		if(part==NULL || nb_part==0) return;		// Pas la peinne d'éxecuter le code s'il n'y a rien à afficher

		cam->SetView();

		int i,j=-1;
		
		VECTOR A;
		VECTOR B;
		float oangle;
		for(i=0;i<nb_part;i++) {					// Calcule la position des points
			if(part[i].px==-1) {
				part[i].px=((int)(part[i].Pos.x+map_w*0.5f))>>4;
				part[i].py=((int)(part[i].Pos.z+map_h*0.5f))>>4;
				}
			if(part[i].px>=0 && part[i].px<bloc_w && part[i].py>=0 && part[i].py<bloc_h) {
				if(!bmap[part[i].py][part[i].px])	continue;
				}
			else
				continue;	// Particule en dehors de la carte donc hors champ
			j++;
			if(j==0 || oangle!=part[i].angle) {
				oangle=part[i].angle;
				float cosinus=cos(part[i].angle);
				float sinus=sin(part[i].angle);
				A=(cosinus-sinus)*cam->Side+(sinus+cosinus)*cam->Up;
				B=(cosinus+sinus)*cam->Side+(sinus-cosinus)*cam->Up;
				}
			int i_bis=j<<2;
			int i_qat=j<<4;
			point[i_bis++]=part[i].Pos-part[i].size*B;
			point[i_bis++]=part[i].Pos+part[i].size*A;
			point[i_bis++]=part[i].Pos+part[i].size*B;
			point[i_bis]=part[i].Pos-part[i].size*A;

			int i_ter=j<<3;
			float px=0.25f*(part[i].gltex&3);
			float py=0.25f*(part[i].gltex>>2);
			texcoord[i_ter++]=px;			texcoord[i_ter++]=py;
			texcoord[i_ter++]=px+0.25f;		texcoord[i_ter++]=py;
			texcoord[i_ter++]=px+0.25f;		texcoord[i_ter++]=py+0.25f;
			texcoord[i_ter++]=px;			texcoord[i_ter]=py+0.25f;

			for(int e=i_qat;e<i_qat+16;) {
				color[e++]=part[i].col[0];
				color[e++]=part[i].col[1];
				color[e++]=part[i].col[2];
				color[e++]=part[i].col[3];
				}
			}

		glAlphaFunc(GL_GREATER,0.1);
		glEnable(GL_ALPHA_TEST);

		glEnable(GL_TEXTURE_2D);
		glDisable(GL_LIGHTING);
		glDisable(GL_CULL_FACE);
		glDepthMask(GL_FALSE);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glEnable(GL_BLEND);

		glDisableClientState(GL_NORMAL_ARRAY);
		glEnableClientState(GL_VERTEX_ARRAY);		// Les sommets
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glEnableClientState(GL_COLOR_ARRAY);
		glVertexPointer( 3, GL_FLOAT, 0, point);
		glBindTexture(GL_TEXTURE_2D,parttex);
		glTexCoordPointer(2, GL_FLOAT, 0, texcoord);
		glColorPointer(4,GL_FLOAT,0,color);

		glDrawElements(GL_QUADS, (j<<2),GL_UNSIGNED_INT,index);		// dessine le tout

		glDisableClientState(GL_COLOR_ARRAY);
		glDisable(GL_BLEND);
		glDisable(GL_ALPHA_TEST);
		glDepthMask(GL_TRUE);
		glEnable(GL_CULL_FACE);
	}
