///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// ZParticule File
///
///		- Emitter Class, Particule Class
///		- Manage the Particule properties of the 3D scene
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////





#include "ZooParticle.h"
#include "../Basic/ZooScene.h"
//$LBDEBUG
#include "../basic/zoodetect.h"




///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///		ZEmitter Class
///
///	
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define _2PI	2*MY_PI

//#define PCL_EXTENT
///////////////////////////////////////////////////////////////////////////
/// Constructor															///
///////////////////////////////////////////////////////////////////////////
ZEmitter::ZEmitter(int s3d, int _mask, float _life, T_VOLUME _volume) : ZNodeGraph(s3d)
{
	type		= PCL_TYPE_ID;
	age			= 0.0f;
	dt			= 0.01f;
	hasEffects	= false;
	state		= PCL_DISABLE;
	pFather		= NULL;
	

	// ------ Init the Emitter -------
	life			= fabs(_life);
	mask			= _mask;
	nbFrame			= 0;

	if( (_volume.type>=VOLUME_CONE) && (_volume.type<=VOLUME_LINE) )
		volume.type		= _volume.type;
	else
		volume.type		= VOLUME_SPHERE;
		


	volume.vectorA	= _volume.vectorA;
	volume.vectorB	= _volume.vectorB;

	effectlist.resize(0);
	particlelist.resize(0);

	ZScene *scene = (ZScene*)s3d;
	scene->pclList.Add(this);
}



///////////////////////////////////////////////////////////////////////////
/// Destructor															///
///////////////////////////////////////////////////////////////////////////
ZEmitter::~ZEmitter()
{
	ZScene *scene = (ZScene*)scene3D;
	int n = scene->pclList.Size();
	scene->pclList.Delete(this);
	n = scene->pclList.Size();


	effectlist.resize(0);
	std::vector<T_PARTICLE*>::iterator	pit;
	std::vector<T_PCL_LIST>::iterator	it = particlelist.begin();
	for(;it!=particlelist.end();it++)
	{
		pit = it->list.begin();
		for(;pit!=it->list.end();pit++)
			delete (*pit);
		it->list.resize(0);
	}
	particlelist.resize(0);
	ZNodeGraph::~ZNodeGraph();
}


//////////////////////////////////////////////////////////////////////////
/// Set the emitter parameters											///
///////////////////////////////////////////////////////////////////////////
int ZEmitter::SetEmitter(int _mask, float _life, T_VOLUME _volume)
{
	life			= fabs(_life);
	mask			= _mask;
	
	if( (_volume.type>=VOLUME_CONE) && (_volume.type<=VOLUME_LINE) )
		volume.type		= _volume.type;
	else
		volume.type		= VOLUME_SPHERE;

	volume.vectorA	= _volume.vectorA;
	volume.vectorB	= _volume.vectorB;

	return 0;
}


///////////////////////////////////////////////////////////////////////////
/// Set the emitter's particle parameters								///
///////////////////////////////////////////////////////////////////////////
int ZEmitter::LinkParticle(ZParticle *newType)
{
	if(newType!=NULL)
	{
		T_PCL_LIST pcl;
		pcl.nbpart = 0;//newType->rate*age/dt;
		pcl.nbNew  = 0;//
		pcl.parttype = newType;
		pcl.list.resize(0);
		particlelist.push_back(pcl);
	}
	return particlelist.size();
}



int ZEmitter::LinkEffect(ZEffect *newEffect)
{
	if(newEffect!=NULL) 
	{
		effectlist.push_back((ZEffect* const &)newEffect);
		hasEffects = true;
	}
	return effectlist.size();
}


///////////////////////////////////////////////////////////////////////////
/// Render the particles of this emitter - Camera necessary				///
///////////////////////////////////////////////////////////////////////////
int ZEmitter::Render(ZVector3 camRot, ZMatrix matCam)
{
	if(state&PCL_ACTIVE)
	{
		ZMatrix		matBILL = RotateYXZMatrix(0.0174532925f * camRot);

		ZMatrix		matx;

		for(int i=0;i<particlelist.size();i++)
		{
			T_PARTICLE			*part;
			ZParticle			*type	= particlelist[i].parttype;

			if(type->mask&PCL_BILLBOARD)
			{
				for(int j=0; j<particlelist[i].list.size(); j++)
				{
					part = particlelist[i].list[j];								// Current particle

					// Application de la matrice
					glLoadIdentity();

					matx = matCam * TranslateMatrix(part->position.x, part->position.y, part->position.z);
					matx *= matBILL;
					matx *= RotateZMatrix(part->spin);

					//$BLG: v4.6a5 - Removed the next line to check impact: No impact
					//matx *= ScaleMatrix(part->size);
					//$BLG: v4.6a5 - No idea about the purpose of the next line, especially after the previous one.
					//cf basic/zoomatrix.cpp - Next line seems to reset the matrix to zeros ...
					//Didn't see any changes when I removed the line, except sparing 5% CPU ...
					//Not sure at all if the above line really has an effect or not in fact ...
					//matx *= ScaleMatrix (0.0);

					glLoadMatrixf(matx.matxArray);

					type->GetMesh()->CallDisplayList1();
				}
			}
			else
			{
				if(type->mask&PCL_JITTER)
				{
					for(int j=0; j<particlelist[i].list.size(); j++)
					{
						part  = particlelist[i].list[j];						// Current particle

						matx  = matCam * TranslateMatrix(part->position.x, part->position.y, part->position.z);
						matx *= RotateYXZMatrix(part->rot);
						matx *= ScaleMatrix(part->size);
						glLoadMatrixf(matx.matxArray);

						type->GetMesh()->CallDisplayList1();
					}
				}
				else {
					for(int j=0; j<particlelist[i].list.size(); j++)
					{
						part = particlelist[i].list[j];							// Current particle

						matx = matCam * TranslateMatrix(part->position.x, part->position.y, part->position.z);
						matx *= ScaleMatrix(part->size);
						glLoadMatrixf(matx.matxArray);

						type->GetMesh()->CallDisplayList1();
					}
				}
			}
		}
		return 0;
	}
	return 1;
}



///////////////////////////////////////////////////////////////////////////
/// Render the additive particles of this emitter - Camera necessary	///
///////////////////////////////////////////////////////////////////////////
int ZEmitter::RenderAdditive(ZVector3 camRot, ZMatrix matCam)
{
	if(state&PCL_ACTIVE)
	{
		ZMatrix		matBILL = RotateYXZMatrix(0.0174532925f * camRot);

		ZMatrix		matx;

		ZUV			*curUV;
		ZMesh		*curMesh;
		ZFace		*curFace;
		ZVector3	*point0;
		ZVector3	*point1;
		ZVector3	*point2;
		ZTexture	*curTex;
		ZMaterial	*curMat;

		vector<T_PCL_LIST>::iterator	l_it = particlelist.begin();
		vector<T_PARTICLE*>::iterator	p_it;
		vector<T_PARTICLE*>::iterator	p_end;
		vector<ZFace*>::iterator		f_it;
		vector<ZFace*>::iterator		fend_it;

		ZTexture	*lastTex;
		bool		curClampU;
		bool		curClampV;

		lastTex		= NULL;

		curClampU	= curClampV = true;
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);


		for(; l_it!= particlelist.end(); l_it++)
		{
			T_PARTICLE			*part;
			ZParticle			*type	= l_it->parttype;

			curMesh	= type->GetMesh();
			FaceList			FacesTransp;

			p_it	= l_it->list.begin();
			p_end	= l_it->list.end();

			// -------------------- RENDU D'UN TYPE DE PARTICULE --------------------
			if(type->mask&PCL_BILLBOARD)
			{
				for(; p_it!=p_end; p_it++)
				{
					part = (*p_it);								// Current particle

					// ------------- RENDU D'UNE PARTICULE TRANSPARENTE -------------

					matx = matCam * TranslateMatrix(part->position.x, part->position.y, part->position.z);
					matx *= matBILL;
					matx *= RotateZMatrix(part->spin);
					//$BLG: v4.6a5 - Removed the next line to verify a point in previous function: No impact
					//matx *= ScaleMatrix(part->size);
					//$BLG: v4.6a5 - Added (and removed) the next line to verify a point in previous function: No impact
					//matx *= ScaleMatrix (0.0);
					
					glLoadMatrixf(matx.matxArray);

					f_it = curMesh->FacesTransp.begin();
					fend_it = curMesh->FacesTransp.end();
					for(;f_it!=fend_it;f_it++)
					{
						curFace		= (*f_it);
						point0		= &curMesh->Verts[curFace->VertRef[0]];
						point1		= &curMesh->Verts[curFace->VertRef[1]];
						point2		= &curMesh->Verts[curFace->VertRef[2]];

						curMat = curFace->GetMaterial();
						curTex = curMat->GetTexture1();


						// ---> TEXTURE <---
						if(curMat->IsOnMode(RENDER_TEXTURED) && curTex && curTex->activated)
						{
							glEnable(GL_TEXTURE_2D);

							if(curTex!=lastTex)		curTex->PutGLMultiparams0();
							lastTex = curTex;

							if(curFace->clampU1 != curClampU)
							{
								curClampU = curFace->clampU1;
								if(curClampU)		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
								else				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
							}

							if(curFace->clampV1 != curClampV)
							{
								curClampV = curFace->clampV1;
								if(curClampV)		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
								else				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
							}


							glBegin(GL_TRIANGLES);
								
								curUV = curFace->UV1;
								glColor4fv(part->color.vals);

								// Vertex number 0
								glTexCoord2fv(curUV->vals);
								glVertex3fv(point0->vals);
								curUV++;

								// Vertex number 1
								glTexCoord2fv(curUV->vals);
								glVertex3fv(point1->vals);
								curUV++;

								// Vertex number 2
								glTexCoord2fv(curUV->vals);
								glVertex3fv(point2->vals);
							
							glEnd();
						}
						else
						// ---> NO TEXTURE <---
						{
							glDisable(GL_TEXTURE_2D);

							glBegin(GL_TRIANGLES);
								
								glColor4fv(part->color.vals);

								// Vertex number 0
								glVertex3fv(point0->vals);

								// Vertex number 1
								glVertex3fv(point1->vals);

								// Vertex number 2
								glVertex3fv(point2->vals);

							glEnd();
						}

					}
				}
			}
			else
			{
				if(type->mask&PCL_JITTER)
				{
					for(; p_it!=p_end; p_it++)
					{
						part = (*p_it);								// Current particle

						matx = matCam * TranslateMatrix(part->position.x, part->position.y, part->position.z);
						matx *= RotateYXZMatrix(part->rot);
						matx *= ScaleMatrix(part->size);

						glLoadMatrixf(matx.matxArray);

						f_it = curMesh->FacesTransp.begin();
						fend_it = curMesh->FacesTransp.end();
						for(;f_it!=fend_it;f_it++)
						{
							curFace		= (*f_it);
							point0		= &curMesh->Verts[curFace->VertRef[0]];
							point1		= &curMesh->Verts[curFace->VertRef[1]];
							point2		= &curMesh->Verts[curFace->VertRef[2]];

							curMat = curFace->GetMaterial();
							curTex = curMat->GetTexture1();


							// ---> TEXTURE <---
							if(curMat->IsOnMode(RENDER_TEXTURED) && curTex && curTex->activated)
							{
								glEnable(GL_TEXTURE_2D);

								if(curTex!=lastTex)		curTex->PutGLMultiparams0();
								lastTex = curTex;

								if(curFace->clampU1 != curClampU)
								{
									curClampU = curFace->clampU1;
									if(curClampU)		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
									else				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
								}

								if(curFace->clampV1 != curClampV)
								{
									curClampV = curFace->clampV1;
									if(curClampV)		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
									else				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
								}


								glBegin(GL_TRIANGLES);
									
									curUV = curFace->UV1;
									glColor4fv(part->color.vals);

									// Vertex number 0
									glTexCoord2fv(curUV->vals);
									glVertex3fv(point0->vals);
									curUV++;

									// Vertex number 1
									glTexCoord2fv(curUV->vals);
									glVertex3fv(point1->vals);
									curUV++;

									// Vertex number 2
									glTexCoord2fv(curUV->vals);
									glVertex3fv(point2->vals);
								
								glEnd();
							}
							else
							// ---> NO TEXTURE <---
							{
								glDisable(GL_TEXTURE_2D);

								glBegin(GL_TRIANGLES);
									
									glColor4fv(part->color.vals);

									// Vertex number 0
									glVertex3fv(point0->vals);

									// Vertex number 1
									glVertex3fv(point1->vals);

									// Vertex number 2
									glVertex3fv(point2->vals);

								glEnd();
							}

						}
					}
				}
				else
				{
					for(; p_it!=p_end; p_it++)
					{
						part = (*p_it);								// Current particle

						matx = matCam * TranslateMatrix(part->position.x, part->position.y, part->position.z);
						matx *= ScaleMatrix(part->size);

						glLoadMatrixf(matx.matxArray);

						f_it = curMesh->FacesTransp.begin();
						fend_it = curMesh->FacesTransp.end();
						for(;f_it!=fend_it;f_it++)
						{
							curFace		= (*f_it);
							point0		= &curMesh->Verts[curFace->VertRef[0]];
							point1		= &curMesh->Verts[curFace->VertRef[1]];
							point2		= &curMesh->Verts[curFace->VertRef[2]];

							curMat = curFace->GetMaterial();
							curTex = curMat->GetTexture1();


							// ---> TEXTURE <---
							if(curMat->IsOnMode(RENDER_TEXTURED) && curTex && curTex->activated)
							{
								glEnable(GL_TEXTURE_2D);

								if(curTex!=lastTex)		curTex->PutGLMultiparams0();
								lastTex = curTex;

								if(curFace->clampU1 != curClampU)
								{
									curClampU = curFace->clampU1;
									if(curClampU)		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
									else				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
								}

								if(curFace->clampV1 != curClampV)
								{
									curClampV = curFace->clampV1;
									if(curClampV)		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
									else				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
								}


								glBegin(GL_TRIANGLES);
									
									curUV = curFace->UV1;
									glColor4fv(part->color.vals);

									// Vertex number 0
									glTexCoord2fv(curUV->vals);
									glVertex3fv(point0->vals);
									curUV++;

									// Vertex number 1
									glTexCoord2fv(curUV->vals);
									glVertex3fv(point1->vals);
									curUV++;

									// Vertex number 2
									glTexCoord2fv(curUV->vals);
									glVertex3fv(point2->vals);
								
								glEnd();
							}
							else
							// ---> NO TEXTURE <---
							{
								glDisable(GL_TEXTURE_2D);

								glBegin(GL_TRIANGLES);
									
									glColor4fv(part->color.vals);

									// Vertex number 0
									glVertex3fv(point0->vals);

									// Vertex number 1
									glVertex3fv(point1->vals);

									// Vertex number 2
									glVertex3fv(point2->vals);

								glEnd();
							}

						}
					}
				}
			}
		}
	}
	return 1;
}


///////////////////////////////////////////////////////////////////////////
/// Add a particle in the list											///
///////////////////////////////////////////////////////////////////////////
///	The particle data are randomly generated according	to their mean values
///
void ZEmitter::AddParticle(T_PCL_LIST *pcl)
{
	T_PARTICLE		*newpart = new T_PARTICLE;
	ZParticle		*type = pcl->parttype;

	float			x;
	ZVector3		v;

	x					= 0.1f*(float)rand()/RAND_MAX;					// Set x ranged from 0 to 0.1 (<10%)

	newpart->age		= x*type->life;
	newpart->weight		= type->weight;
	
	newpart->size		= type->size[0].value;
	newpart->spin		= type->spin[0].value;

	if(type->mask&PCL_RAINBOW)
	{
		 int choice		= rand() % type->color.size();
		 newpart->color = type->color[choice].value;
	}
	else newpart->color	= type->color[0].value;

	if(type->mask&PCL_JITTER)
		newpart->rot.SetCoord((float)_2PI*rand()/RAND_MAX, (float)_2PI*rand()/RAND_MAX, (float)_2PI*rand()/RAND_MAX);	// Random axis
	

	/* The emitter Volume is a line */
	if(volume.type==VOLUME_LINE)
	{
			newpart->position.x	= volume.vectorA.x * ( 2*(float)rand()/RAND_MAX-1 );
			newpart->position.z	= 0.0f;
			newpart->position.y	= 0.0f;

			newpart->speed.SetNull();
	}
	/* The emitter Volume is a plan */
	else if(volume.type==VOLUME_PLAN)
	{
			newpart->position.x	= volume.vectorA.x * ( 2*(float)rand()/RAND_MAX-1 );
			newpart->position.z	= volume.vectorA.z * ( 2*(float)rand()/RAND_MAX-1 );

			newpart->position.y	= 0.0f;

			newpart->speed.SetNull();
	}
	else if(volume.type==VOLUME_SPHERE)
	{
		/* The particles spawn in the center of the volume */
		newpart->position.SetNull();

		newpart->speed.x	= 2*(float)rand()/RAND_MAX-1;
		newpart->speed.y	= 2*(float)rand()/RAND_MAX-1;
		newpart->speed.z	= 2*(float)rand()/RAND_MAX-1;
		
		newpart->speed.Normalize();
		newpart->speed *= volume.vectorA.x/type->life;
	}
	/* The emitter Volume is a cone */
	else if(volume.type==VOLUME_CONE)
	{
		newpart->position.SetNull();

		float r2			= (float)rand()/RAND_MAX;							
		float r1			= (float)rand()/RAND_MAX;							
		float r0			= (float)rand()/RAND_MAX;							
		x					= ((float)rand()/RAND_MAX)*6.28319;

		newpart->speed.x	= (float)(volume.vectorA.x*cos(x));
		newpart->speed.z	= (float)(volume.vectorA.z*sin(x));
		newpart->speed.y	= volume.vectorA.y;

		newpart->speed.Normalize();
		newpart->speed *= volume.vectorA.y/type->life;
	}
	
	newpart->position	*= RealAngleMat;
	newpart->position	+= RealPos;
	newpart->speed		*= RealAngleMat;
	

	if(pFather)
	{
		newpart->speed		*= RealFatherMat;
		newpart->position	*= RealFatherMat;
	}

	pcl->list.push_back(newpart);
	pcl->nbpart++;
	pcl->nbNew++;
}


int	ZEmitter::ApplyEffect(T_PARTICLE *part, float polarity)
{
	float		q1, cf, cr;
	ZVector3	p, bp, vn, vt;
	ZEffect*	effect;

	for(int j=0; j<effectlist.size(); j++)
	{
		effect = effectlist[j];

		if(effect->etype==EFFECT_CONSTANT)	part->speed += dt*effect->vectorA;

		else if(effect->etype==EFFECT_ELECTRIC && part->weight>0)
		{
			q1	 = effect->vectorB.x;							// Strenght of the electric force
								
			p	 = part->position-effect->vectorA-RealPos;
			p.Normalize();
			
			part->speed += ( dt*q1*polarity/part->weight ) * p;
		}

		else if(effect->etype==EFFECT_MAGNETIC && part->weight>0)		// Magnetic field force
		{
			q1	 = effect->vectorB.x;							// Strenght of the magnetic force
			part->speed += (q1/part->weight)*(effect->vectorA ^ part->speed);
		}

		else if(effect->etype==EFFECT_HELICOID)							// Elliptic effect
		{
			ZVector3 v0 = effect->vectorA;

			float teta	= (2*PI*part->age)/fabs(effect->vectorB.z);
			float x		= fabs(effect->vectorB.x) * cos(teta);
			float y		= fabs(effect->vectorB.y) * sin(teta);
			
			ZVector3 v1(v0.y+v0.z, -v0.x+v0.z, -v0.x-v0.y);
			ZVector3 v2 = v0^v1;
			
			v0.Normalize();
			v1.Normalize();
			v2.Normalize();

			part->speed		+= (RealAngleMat * effect->vectorA);
			part->position	 = RealAngleMat * (x*v1 + y*v2) + RealPos;
//			part->position	 = (RealAngleMat * (x*v1 + y*v2 + RealPos));
		}
		else if(effect->etype==EFFECT_PLANCOLL)								// Collision with a plan
		{
			p = part->position + part->speed*dt;
			bp = p-effect->vectorB;

			q1 = bp*effect->vectorA;

			if(fabs(q1)<effect->vectorC.z)
			{
				cf = effect->vectorC.x;						// dumping factor
				cr = effect->vectorC.y;						// rebound factor

				vn = (part->speed*effect->vectorA)*effect->vectorA;
				vt = part->speed-vn;

				part->speed += (cf*vt-cr*vn - part->speed);
			}
		}
		else if(effect->etype==EFFECT_CHAOTIC0)								// Noise effect
		{
			part->speed		+=  ((float)rand()/RAND_MAX) * (effect->vectorB-effect->vectorA) + effect->vectorA;
		}
		else if(effect->etype==EFFECT_CHAOTIC1)								// Noise effect
		{
			part->speed		+=  effect->vectorC;

		}
	}
	return 0;
}

///////////////////////////////////////////////////////////////////////////
/// Do all calculation													///
///////////////////////////////////////////////////////////////////////////
///	Called each time (frame) you want to update the emitter
///
int ZEmitter::NextStep()
{
//	float rate;
	int nbP;
	nbMaxPart = 0;

	ZScene*		sc	= (ZScene*)scene3D;
	ZCamera*	cam = sc->activeCam;
	
	RealWorldMat	= cam->RealWorldMat * worldMat;
	RealPos.SetCoord(RealWorldMat._14, RealWorldMat._24, RealWorldMat._34);
	RealAngleMat	= RotateYXZMatrix(0.0174532925f * RealWorldMat.GetAnglesYXZ() );

	if(pFather)		RealFatherMat = cam->RealWorldMat * pFather->worldMat;

	/* Initialize the CHAOTIC1 type effects */
	if(hasEffects)
	{
		ZVector3 rnd;
		std::vector<ZEffect*>::iterator it1 = effectlist.begin();
		for(;it1!=effectlist.end();it1++)
			if((*it1)->etype==EFFECT_CHAOTIC1)
			{
				rnd					= ((float)rand()/RAND_MAX) * ((*it1)->vectorB-(*it1)->vectorA) + (*it1)->vectorA;
				(*it1)->vectorC		=  0.01f * rnd + 0.99f * (*it1)->vectorC;
			}
	}

	/* See if the emitter is still alive */
	if(age>life && !(mask&PCL_INFINIT) )		state &= (0xfff - PCL_ENABLE);
	else if(state&PCL_ENABLE)					
	{
		age += dt;
		if(nbFrame>100)		nbFrame = 1;
		else				nbFrame++;
	}


	
	T_PARTICLE	*curPart;
	ZParticle	*type;
	std::vector<T_PARTICLE*>::iterator it0;
	std::vector<T_PCL_LIST>::iterator it2 = particlelist.begin();


	/* Process all the emitter particle types */
	for(;it2!=particlelist.end();it2++)
	{
		type	= it2->parttype;

		it0		= it2->list.begin();

		/* Process all the particle of each type */
		for(; it0!=it2->list.end();)
		{
			curPart			 = (*it0);

			/* If the particle is still alive */
			if( curPart->age < type->life)
			{
				nbMaxPart++;
				curPart->age	+= dt;

				if(hasEffects)		ApplyEffect(curPart, type->polarity);

				/* Update the color, size and spin of the particle */
				type->SetParam(curPart);

				curPart->position	+= dt*curPart->speed;
				it0++;
			}
			else
			{
				it2->list.erase(it0);
				delete curPart;
				curPart = NULL;
				it2->nbpart--;
			}
		}

		if(state&PCL_ENABLE)
		{
			if(nbFrame==1)		it2->nbNew = 0;

//			rate = (float)it2->nbNew/nbFrame;
//			while(rate<type->rate)

			nbP = nbFrame*type->rate - it2->nbNew;
			for(int i=0;i<nbP;i++)
			{
				AddParticle(it2);
//				rate = (float)it2->nbNew/nbFrame;
				nbMaxPart++;
			}
		}

	}


	if(nbMaxPart==0 && particlelist.size())			state &= (0xfff - PCL_ACTIVE);
	else if(nbMaxPart)								state = state|PCL_ACTIVE;

	return nbMaxPart;
}





















///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///		ZParticle Class
///
///	
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

ZParticle::ZParticle(int s3d, ZMesh *_mesh, int _mask, float _life, float _rate, float _weight, float _size,
					 float _spin, float _polarity) : ZData()
{
	type			= PCLDEF_TYPE_ID;

	life		= _life;
	rate		= _rate;
	weight		= _weight;
	
	T_PARAM p;
	p.pos		= 0;
	p.value		= _size;
	size.push_back(p);

	p.value		= _spin;
	spin.push_back(p);


	polarity	= _polarity;
	mask		= _mask;

	mesh = NULL;
	SetMesh(_mesh);

	// ------- Init the Colors -------
	color.resize(2);
	color[0].value.SetCoord(1.0f,1.0f,1.0f,1.0f);
	color[1].value.SetCoord(0.0f,0.0f,0.0f,1.0f);
	
	color[0].pos = 0.0f;
	color[1].pos = 100.0f;
}

ZParticle::~ZParticle()
{
	color.resize(0);
	size.resize(0);
	spin.resize(0);
	mesh->DecRef();
}


int	ZParticle::	SetParticle(ZMesh *_mesh, int _mask, float _life, float _rate, float _weight, float _size, float _spin, float _polarity)
{
	SetMesh(_mesh);

	life		= _life;
	rate		= _rate;
	weight		= _weight;
	
	if(size.size()==1) size[0].value = _size;
	if(spin.size()==1) spin[0].value = _spin;
	
	polarity	= _polarity;
	mask		= _mask;

	return 0;
}


//$LBDEBUG //$LB (24/11/2004)
extern int LBDEBUG_VENDOR_ATI;


void ZParticle::SetParam(T_PARTICLE *part)
{
//$LB (08/02/2004)
int maxsize = color.size();


	//$LBDEBUG
	//MMechostr(0, "\n\nPARTICLE maxsize %d\n", maxsize);



	/* Mise à jour de la couleur */
    //$LB (08/02/2004)
	if( maxsize!=1 && !(mask&PCL_RAINBOW) )
	{
		float curPos = 100.0 * part->age/life;
		int io = 1;

		//$LB
		while ((color[io].pos<curPos) && (io < maxsize))
			io++;

		float	coef  = (color[io].pos - curPos)/(color[io].pos-color[io-1].pos);
		
		part->color	  = color[io-1].value*coef + color[io].value*(1-coef);
	}


	//$LBDEBUG
	//MMechostr(0, "\nPARTICLE size.size() %d\n", size.size());

 

	/* Mise à jour de la taille */
	if(size.size()!=1)
	{
		int n = size.size();
		float	curPos = 100.0 * part->age/life;
		int		io	  = 1;

		float pos0 = size[0].pos;
		float pos1 = size[1].pos;
		float pos2 = size[2].pos;

		float pos = size[io].pos;

		//$LB (24/11/2004)
		if (LBDEBUG_VENDOR_ATI)
		{
			while ((pos<curPos) && (io <n))
			{
				io++;
				pos = size[io].pos;
			}
		}
		else
		{
			while(pos<curPos)
			{
				io++;
				pos = size[io].pos;
			}

		}

		
		float	coef  = (size[io].pos - curPos)/(size[io].pos-size[io-1].pos);
	
		part->size	  = size[io-1].value*coef + size[io].value*(1-coef);
	} 


	/* Mise à jour du spin */
	if( spin.size()==1 )
	{
		part->spin += spin[0].value;

		if( (part->spin!=0.f) && (mask&PCL_JITTER) && !(mask&PCL_BILLBOARD))
		{
			part->rot.x += spin[0].value;
			part->rot.y += spin[0].value;
			part->rot.z += spin[0].value;
		}
	}

	else
	{
		float	curPos = 100.0 * part->age/life;
		int		io	  = 1;
		int n = spin.size();

		//$LB
		while ((spin[io].pos<curPos) && (io < n))
			io++;

		float	coef  = (spin[io].pos - curPos)/(spin[io].pos-spin[io-1].pos);
		float	delta = spin[io-1].value*coef + spin[io].value*(1-coef);
		part->spin	  += delta;

		if( (part->spin!=0.f) && (mask&PCL_JITTER) && !(mask&PCL_BILLBOARD) )
		{
			part->rot.x += delta;
			part->rot.y += delta; 
			part->rot.z += delta;
		}
	}
}


///////////////////////////////////////////////////////////////////////////
/// Set a texture for the face (PASS 1)									///
///////////////////////////////////////////////////////////////////////////
void ZParticle::SetMesh(ZMesh *newMesh)
{
	if(newMesh!=mesh)
	{
		if(mesh!=NULL)			mesh->DecRef();

		mesh = newMesh;
		if(mesh!=NULL)			mesh->IncRef();
	}
}











///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///		ZEffect Class
///
///	
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

ZEffect::ZEffect(int s3d, int _type, ZVector3 _vA, ZVector3 _vB, ZVector3 _vC) : ZData()
{
	type			= PCLEFF_TYPE_ID;


	etype	= _type;
	vectorA	= _vA;
	vectorB	= _vB;
	vectorC	= _vC;
}

ZEffect::~ZEffect()
{

}


int ZEffect::SetEffect(int _type, ZVector3 _vA, ZVector3 _vB, ZVector3 _vC)
{
	etype	= _type;
	vectorA	= _vA;
	vectorB	= _vB;
	vectorC	= _vC;

	return 0;
}

void ZParticle::SortLists()
{
	// Order SIZE table
	if(size.size()>1)
	{
		std::vector<T_PARAM>::iterator itmin;
		std::vector<T_PARAM>::iterator it;

		std::vector<T_PARAM> list;
		list.resize(0);
		
		while(size.size()>0)
		{
			it		= size.begin();
			itmin	= it;

			for(;it!=size.end();it++)
				if(it->pos<itmin->pos)
					itmin = it;

			T_PARAM p;
			p.pos	= itmin->pos;
			p.value	= itmin->value;
			list.push_back(p);
			size.erase(itmin);
		}

		size.assign(list.begin(), list.end());
	}

	// Order SPIN table
	if(spin.size()>1)
	{
		std::vector<T_PARAM>::iterator itmin;
		std::vector<T_PARAM>::iterator it;

		std::vector<T_PARAM> list;
		list.resize(0);
		
		while(spin.size()>0)
		{
			it		= spin.begin();
			itmin	= it;

			for(;it!=spin.end();it++)
				if(it->pos<itmin->pos)
					itmin = it;

			T_PARAM p;
			p.pos	= itmin->pos;
			p.value	= itmin->value;
			list.push_back(p);
			spin.erase(itmin);
		}

		spin.assign(list.begin(), list.end());
	}

	// Order COLOR table
	if(color.size()>1)
	{
		std::vector<T_COLOR>::iterator itmin;
		std::vector<T_COLOR>::iterator it;

		std::vector<T_COLOR> list;
		list.resize(0);
		
		while(color.size()>0)
		{
			it		= color.begin();
			itmin	= it;

			for(;it!=color.end();it++)
				if(it->pos<itmin->pos)
					itmin = it;

			T_COLOR p;
			p.pos	= itmin->pos;
			p.value	= itmin->value;
			list.push_back(p);
			color.erase(itmin);
		}

		color.assign(list.begin(), list.end());
	}
}
