
#include "Renderer.h"
#include "BloodGenerator.h"
#include "SparkGenerator.h"
#include "DustParticle.h"

#include "MemoryManager/DebugNew.h"

using namespace RenderSystem;
using namespace ground;
using namespace ParticleSystem;

Renderer* Renderer::renderer = NULL;

Renderer::Renderer()
{
	this->mapIsLoaded = false;
	this->vertex = NULL;
	this->color = NULL;
	this->texCoord = NULL;
	this->normal = NULL;
	this->elapsedTime = 0.0f;

	this->trWater = 0.0f;

	this->skipFrustumUpdate = false;
	this->showQuadTree = false;
	this->showParticleAABB = false;
	this->showModeleWorldAABB = false;
	this->showModeleLocalAABB = false;
	this->showWireGround = false;
	this->showGraph = false;
	this->showUnitLife = true;
	this->useGLFog = true;
	this->useGLlighting = true;
	this->useGroundTexture = true;
	this->renderWaterZone = true;
	this->renderTreeLeaf = true;
	this->renderTreeTrunk = true;
	this->renderParticles = true;
	this->renderHiddenUnits = true;
	this->renderDecors = true;
	this->showDangerZoneIntensity = false;
	this->showStrongZoneIntensity = false;
	this->showNaturelAxeIntensity = false;
	this->showBlockCase = false;
	this->timeFactor = 1.0f;
	this->showStrongZoneCases = -1;
	this->showNaturalAxeCases = -1;
	this->showForestZoneCases = -1;

	this->groundSeenColorIntensity = 1.0f;
	this->groundWarFogColorIntensity = 0.2f;

	this->texLuciole = NULL;
	this->texMeadow = NULL;
	this->texForest = NULL;
	this->texOreeForest = NULL;
	this->texRoad = NULL;
	this->texWater = NULL;
	this->texUnderWater = NULL;
	this->texTreeTrunk = NULL;
	this->texTreeLeaf = NULL;
	this->texFire = NULL;
	this->texFish = NULL;
	this->texSpring = NULL;
	this->texBlood = NULL;
	this->texSpark = NULL;
	this->texDust = NULL;

	this->army1 = NULL;
	this->army2 = NULL;
	this->currentArmy = NULL;

	this->visibilityUpdateTime = 300.0f;
	this->visibilityElapsedTime = 0.0f;

	renderableEntities.clear();
	decors.clear();
}

Renderer::~Renderer()
{
	this->unloadGroundMap();
}

Renderer& Renderer::getInstance()
{
	if(renderer==NULL) {
		renderer = new Renderer();
	}
	return *renderer;
}

void Renderer::destroy()
{
	delete renderer;
	renderer = NULL;
}

void Renderer::unloadGroundMap()
{
	delete [] this->vertex;
	this->vertex = NULL;
	delete [] this->color;
	this->color = NULL;
	delete [] this->normal;
	this->normal = NULL;
	delete [] this->texCoord;
	this->texCoord = NULL;
	delete [] this->mapPlanes;
	this->mapPlanes = NULL;

	rec_deleteOpenGLDisplayList(qtRoot);
	rec_deleteQuadTree(qtRoot);
	delete this->qtRoot;
	this->qtRoot = NULL;

	delete this->treeTrunkModel;
	this->treeTrunkModel = NULL;
	delete this->treeLeafModel;
	this->treeLeafModel = NULL;
	delete this->treeTrunkInstance;
	this->treeTrunkInstance = NULL;
	delete this->treeLeafInstance;
	this->treeLeafInstance = NULL;

	delete this->rain;
	this->rain = NULL;
	delete this->snow;
	this->snow = NULL;

	for (map<string,Model*>::iterator it = modelsMap.begin(); it != modelsMap.end(); ++it){
		delete it->second;
	}
	modelsMap.clear();

	/*for(int i=0;i<lucioleEngine.size();i++)	//suprimer : c'est le manager qui s'en occupe
		delete lucioleEngine[i];
	this->lucioleEngine.clear();*/
	ParticleManager::destroyGlobal();
	// on vide les vecteur de moteurs de particules
	lucioleEngine.clear();
	fishEngine.clear();
	fireEngine.clear();
	springEngine.clear();
	bloodEngine.clear();

	renderableEntities.clear();
	decors.clear();

	this->mapIsLoaded = false;
}

void Renderer::loadGroundMap(ground::GroundMap& t, char* tileSet, Army& a1, Army& a2)
{
	if(this->mapIsLoaded == true ) this->unloadGroundMap();

	army1 = &a1;
	army2 = &a2;
	currentArmy = army1;

	gc = t.getCases();
	gw = (int)t.getWidth();
	gh = (int)t.getHeight();
	gvw = gw+1;
	gvh = gh+1;

	ModelMD2::setLimitTimeForAnim(MD2_DEFAULT_LIMIT_TIME_FOR_ANIM);

	mapPlanes = new MapCasePlanes[gw*gh];

	vertex = new float[gvw*gvh*3];
	color = new float[gvw*gvh*3];
	texCoord = new float[gvw*gvh*2];
	normal = new float[gvw*gvh*3];
	for(unsigned int i=0;i<(unsigned int)(gvw);i++) {
		for(unsigned int j=0;j<(unsigned int)(gvh);j++) {
			vertex[(i*gvh+j)*3  ] = i*CASE_SIDE_SIZE;
			vertex[(i*gvh+j)*3+1] = computeHauteur(i,j);
			vertex[(i*gvh+j)*3+2] = j*CASE_SIDE_SIZE;

			color[(i*gvh+j)*3  ] = this->groundWarFogColorIntensity;
			color[(i*gvh+j)*3+1] = this->groundWarFogColorIntensity;
			color[(i*gvh+j)*3+2] = this->groundWarFogColorIntensity;

			normal[(i*gvh+j)*3  ] = 0.0f;
			normal[(i*gvh+j)*3+1] = 0.0f;
			normal[(i*gvh+j)*3+2] = 0.0f;

			texCoord[(i*gvh+j)*2  ] = i;
			texCoord[(i*gvh+j)*2+1] = j;
		}
	}

	//calcul des normal des vertices
	float l;
	float v01x,v01y,v01z,v02x,v02y,v02z;
	float fx,fy,fz;
	int tmpid00,tmpid01,tmpid11,tmpid10;
	for(unsigned int i=0;i<(unsigned int)(gw);i++) {
		for(unsigned int j=0;j<(unsigned int)(gh);j++) {
			tmpid00 = (i)*(gvh) + (j);
			tmpid01 = (i+1)*(gvh) + (j);
			tmpid11 = (i+1)*(gvh) + (j+1);
			tmpid10 = (i)*(gvh) + (j+1);

			//vecteur allant du point 1 au point 0
			v01x = this->vertex[tmpid11*3+0] - this->vertex[tmpid10*3+0];
			v01y = (this->vertex[tmpid11*3+1]) - (this->vertex[tmpid10*3+1]);
			v01z = this->vertex[tmpid11*3+2] - this->vertex[tmpid10*3+2];
			//vecteur allant du point 2 au point 0
			v02x = this->vertex[tmpid01*3+0] - this->vertex[tmpid10*3+0];
			v02y = (this->vertex[tmpid01*3+1]) - (this->vertex[tmpid10*3+1]);
			v02z = this->vertex[tmpid01*3+2] - this->vertex[tmpid10*3+2];
			//produit vectoriel pour obtenir la normal
			fx= v01y*v02z - v01z*v02y;
			fy= v01z*v02x - v01x*v02z;
			fz= v01x*v02y - v01y*v02x;
			//normalisation de la normal
			l = sqrt(fx*fx + fy*fy + fz*fz);
			fx/=l;
			fy/=l;
			fz/=l;
			//affectation
			this->normal[tmpid11*3+0] += fx;
			this->normal[tmpid11*3+1] += fy;
			this->normal[tmpid11*3+2] += fz;
			this->normal[tmpid10*3+0] += fx;
			this->normal[tmpid10*3+1] += fy;
			this->normal[tmpid10*3+2] += fz;
			this->normal[tmpid01*3+0] += fx;
			this->normal[tmpid01*3+1] += fy;
			this->normal[tmpid01*3+2] += fz;

			//calcul du plan haut-droit de la case (on recalcul sans les modification de hauteur de l'eau)
			//vecteur allant du point 1 au point 0
			v01x = this->vertex[tmpid11*3+0] - this->vertex[tmpid10*3+0];
			v01y = this->vertex[tmpid11*3+1] - this->vertex[tmpid10*3+1];
			v01z = this->vertex[tmpid11*3+2] - this->vertex[tmpid10*3+2];
			//vecteur allant du point 2 au point 0
			v02x = this->vertex[tmpid01*3+0] - this->vertex[tmpid10*3+0];
			v02y = this->vertex[tmpid01*3+1] - this->vertex[tmpid10*3+1];
			v02z = this->vertex[tmpid01*3+2] - this->vertex[tmpid10*3+2];
			//produit vectoriel pour obtenir la normal
			fx= v01y*v02z - v01z*v02y;
			fy= v01z*v02x - v01x*v02z;
			fz= v01x*v02y - v01y*v02x;
			//normalisation de la normal
			l = sqrt(fx*fx + fy*fy + fz*fz);
			fx/=l;
			fy/=l;
			fz/=l;
			mapPlanes[i*gh+j].p1.n[0] = fx;
			mapPlanes[i*gh+j].p1.n[1] = fy;
			mapPlanes[i*gh+j].p1.n[2] = fz;
			mapPlanes[i*gh+j].p1.d    = - ( this->vertex[tmpid11*3+0]*fx + this->vertex[tmpid11*3+1]*fy + this->vertex[tmpid11*3+2]*fz );

			
			//vecteur allant du point 1 au point 0
			v01x = this->vertex[tmpid01*3+0] - this->vertex[tmpid10*3+0];
			v01y = (this->vertex[tmpid01*3+1]) - (this->vertex[tmpid10*3+1]);
			v01z = this->vertex[tmpid01*3+2] - this->vertex[tmpid10*3+2];
			//vecteur allant du point 2 au point 0
			v02x = this->vertex[tmpid00*3+0] - this->vertex[tmpid10*3+0];
			v02y = (this->vertex[tmpid00*3+1]) - (this->vertex[tmpid10*3+1]);
			v02z = this->vertex[tmpid00*3+2] - this->vertex[tmpid10*3+2];
			//produit vectoriel pour obtenir la normal
			fx= v01y*v02z - v01z*v02y;
			fy= v01z*v02x - v01x*v02z;
			fz= v01x*v02y - v01y*v02x;
			//normalisation de la normal
			l = sqrt(fx*fx + fy*fy + fz*fz);
			fx/=l;
			fy/=l;
			fz/=l;
			//affectation
			this->normal[tmpid10*3+0] += fx;
			this->normal[tmpid10*3+1] += fy;
			this->normal[tmpid10*3+2] += fz;
			this->normal[tmpid00*3+0] += fx;
			this->normal[tmpid00*3+1] += fy;
			this->normal[tmpid00*3+2] += fz;
			this->normal[tmpid01*3+0] += fx;
			this->normal[tmpid01*3+1] += fy;
			this->normal[tmpid01*3+2] += fz;

			//calcul du plan haut-gauche de la case (on recalcul sans les modification de hauteur de l'eau)
			//vecteur allant du point 1 au point 0
			v01x = this->vertex[tmpid01*3+0] - this->vertex[tmpid10*3+0];
			v01y = this->vertex[tmpid01*3+1] - this->vertex[tmpid10*3+1];
			v01z = this->vertex[tmpid01*3+2] - this->vertex[tmpid10*3+2];
			//vecteur allant du point 2 au point 0
			v02x = this->vertex[tmpid00*3+0] - this->vertex[tmpid10*3+0];
			v02y = this->vertex[tmpid00*3+1] - this->vertex[tmpid10*3+1];
			v02z = this->vertex[tmpid00*3+2] - this->vertex[tmpid10*3+2];
			//produit vectoriel pour obtenir la normal
			fx= v01y*v02z - v01z*v02y;
			fy= v01z*v02x - v01x*v02z;
			fz= v01x*v02y - v01y*v02x;
			//normalisation de la normal
			l = sqrt(fx*fx + fy*fy + fz*fz);
			fx/=l;
			fy/=l;
			fz/=l;
			mapPlanes[i*gh+j].p2.n[0] = fx;
			mapPlanes[i*gh+j].p2.n[1] = fy;
			mapPlanes[i*gh+j].p2.n[2] = fz;
			mapPlanes[i*gh+j].p2.d    = - ( this->vertex[tmpid10*3+0]*fx + this->vertex[tmpid10*3+1]*fy + this->vertex[tmpid10*3+2]*fz );
		}
	}
	//normalisation des normals
	for(int i=0;i<gvw*gvh;i++)
	{
		l = sqrt( this->normal[i*3+0]*this->normal[i*3+0] +
				  this->normal[i*3+1]*this->normal[i*3+1] +
				  this->normal[i*3+2]*this->normal[i*3+2] );
		this->normal[i*3+0]/=l;
		this->normal[i*3+1]/=l;
		this->normal[i*3+2]/=l;
	}

	//on déforme les vertices du terrain suivant les zones d'eau
	//ainsi, on a les équations de plan avec la surface de l'eau et les coord de vertex du terrain au final.
	for(unsigned int i=0;i<(unsigned int)(gvw);i++) {
		for(unsigned int j=0;j<(unsigned int)(gvh);j++) {
			this->vertex[(i*gvh+j)*3+1] -= vertexWithWaterLevelOffset(i,j);
		}
	}

	//on charge les models
	this->treeTrunkModel = new ModelStatic();
	this->treeLeafModel = new ModelStatic();
	this->treeTrunkInstance = new ModelInstance();
	this->treeLeafInstance = new ModelInstance();
	this->treeTrunkModel->load("./modeles/obj/tronc.obj","tronc");
	this->treeLeafModel->load("./modeles/obj/feuilles.obj","feuilles");
	this->treeTrunkModel->initInstance(treeTrunkInstance);
	this->treeLeafModel->initInstance(treeLeafInstance);


	if (!TextureManager::getInstance()->addTexture2D("./textures/common/fantassinBleu.bmp",NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,false,0,0,0,0,0)){
		fprintf(stderr,"Impossible de charger la texture fantassinBleu.bmp\n");
	}
	if(!TextureManager::getInstance()->addTexture2D("./textures/common/fantassinRouge.bmp",NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,false,0,0,0,0,0)){
		fprintf(stderr,"Impossible de charger la texture fantassinRouge.bmp\n");
	}
	if (!TextureManager::getInstance()->addTexture2D("./textures/common/piquierBleu.bmp",NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,false,0,0,0,0,0)){
		fprintf(stderr,"Impossible de charger la texture piquierBleu.bmp\n");
	}
	if(!TextureManager::getInstance()->addTexture2D("./textures/common/piquierRouge.bmp",NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,false,0,0,0,0,0)){
		fprintf(stderr,"Impossible de charger la texture piquierRouge.bmp\n");
	}
	if (!TextureManager::getInstance()->addTexture2D("./textures/common/archerBleu.bmp",NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,false,0,0,0,0,0)){
		fprintf(stderr,"Impossible de charger la texture archerBleu.bmp\n");
	}
	if(!TextureManager::getInstance()->addTexture2D("./textures/common/archerRouge.bmp",NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,false,0,0,0,0,0)){
		fprintf(stderr,"Impossible de charger la texture archerRouge.bmp\n");
	}
	if (!TextureManager::getInstance()->addTexture2D("./textures/common/cheval.bmp",NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,false,0,0,0,0,0)){
		fprintf(stderr,"Impossible de charger la texture cheval.bmp\n");
	}


	// chargement des models des unitée
	Model* fantassin = new ModelMD2();
	if (!fantassin->load("./modeles/md2/fantassin.md2","Fantassin")){
		fprintf(stderr,"Impossible de charger le modele\n");
	}
	fantassin->scale(0.02f);
	modelsMap["Infantry"] = fantassin;
	// le piquier
	Model* piquier = new ModelMD2();
	piquier->load("./modeles/md2/piquier.md2","Piquier");
	modelsMap["Lancer"] = piquier;
	piquier->scale(0.02f);
	// l'archer
	Model* archer = new ModelMD2();
	archer->load("./modeles/md2/archer.md2","Archer");
	modelsMap["Archer"] = archer;
	archer->scale(0.02f);
	// le cheval
	Model* cheval = new ModelMD2();//new ModelMD2();
	cheval->load("./modeles/md2/cheval.md2","Cheval");
	//cheval->scale(0.02f);
	cheval->scale(0.02f);
	modelsMap["Horse"] = cheval;

	//on regle les aniamtion qui ne boucle pas
	((ModelMD2*)archer)->animationLoop(5,false);
	((ModelMD2*)archer)->animationLoop(10,false);
	((ModelMD2*)fantassin)->animationLoop(5,false);
	((ModelMD2*)fantassin)->animationLoop(10,false);
	((ModelMD2*)piquier)->animationLoop(5,false);
	((ModelMD2*)piquier)->animationLoop(10,false);
	((ModelMD2*)cheval)->animationLoop(5,false);

	rain = new ParticleEngine();
	snow = new ParticleEngine();

	this->reloadTexture(tileSet);

	//couleur
	this->waterColor[0] = 0.41f;
	this->waterColor[1] = 1.0f;
	this->waterColor[2] = 1.0f;
	this->fogcolor[0] = 0.5f;
	this->fogcolor[1] = 0.5;
	this->fogcolor[2] = 0.6;
	this->fogcolor[3] = 1.0f;
	glClearColor(this->fogcolor[0],this->fogcolor[1],this->fogcolor[2],this->fogcolor[3]);
	glClearDepth(FOG_DISTANCE);

	//construction du QuadTree
	qtRoot = new QT_Node;
	this->rec_buildQuadTree(0,0,gw,gh,qtRoot);
	this->rec_buildOpenGLDisplayList(qtRoot);

	visibleLeaf.clear();
	visibleLeaf.reserve((gw/QT_MIN_SIDE_SIZE)*(gh/QT_MIN_SIDE_SIZE)+1);

	//////////////////
	/// PARTICULES ///
	//////////////////
	windTime = 0.0f;
	////////////
	//particule de pluie
	// creation du gestionnaire de physique
	ParticleEngine::ParticlePhysique * phys = new ParticleEngine::ParticlePhysique();
	phys->setPhysique(0,0,0);
	vent.push_back(phys);
	//PLUIE
	rain->setPhysique(phys);
	// ici aussi la destruction du generator est prise en charge par le moteur de particule
	RainGenerator* rainGenerator = new RainGenerator( CASE_SIDE_SIZE*(max(gw,gh)/2) );
	rainGenerator->pos[0] = CASE_SIDE_SIZE*(max(gw,gh)/2);
	rainGenerator->pos[1] = 100;
	rainGenerator->pos[2] = CASE_SIDE_SIZE*(max(gw,gh)/2);
	rain->setGenerator(rainGenerator);
	rain->setSortType(NO_SORT);
	rain->setDrawMode(GL_LINES);
	RainParticle * particles = new RainParticle[1000];
	rain->setParticles(particles,1000,sizeof(RainParticle));
	rain->setFramePerSecondes(60);
	rain->setBlendFunc(GL_SRC_ALPHA,GL_ONE);
	rain->start();
	//NEIGE
	ParticleEngine::ParticlePhysique * phys2 = new ParticleEngine::ParticlePhysique();
	phys2->setPhysique(0,0,0);
	vent.push_back(phys2);
	snow->setPhysique(phys2);
	// ici aussi la destruction du generator est prise en charge par le moteur de particule
	SnowGenerator* snowGenerator = new SnowGenerator( CASE_SIDE_SIZE*(max(gw,gh)/2) );
	snowGenerator->pos[0] = CASE_SIDE_SIZE*(max(gw,gh)/2);
	snowGenerator->pos[1] = 100;
	snowGenerator->pos[2] = CASE_SIDE_SIZE*(max(gw,gh)/2);
	snow->setGenerator(snowGenerator);
	snow->setSortType(NO_SORT);
	snow->createParticles(1000);
	snow->setFramePerSecondes(60);
	snow->setBlendFunc(GL_ONE,GL_ONE);
	snow->start();

	if(strcmp(tileSet,"textures/tileset/snow/")==0) {
		this->curEnvPE = snow;
	}
	else {
		this->curEnvPE = rain;
	}
	////////////
	//particule luciole
	for (int i = 8; i < gw; i+= 8){
		for (int j = i%3; j < gh; j+= 8){
			ParticleEngine * particleEngine = new ParticleEngine();
			lucioleEngine.push_back(particleEngine);
			// ici aussi la destruction du generator est prise en charge par le moteur de particule
			LuciolGenerator* luciolGenerator = new LuciolGenerator(CASE_SIDE_SIZE);
			luciolGenerator->pos[0] = CASE_SIDE_SIZE*i;
			luciolGenerator->pos[1] = this->getElevation(i+0.5f,j+0.5f)+(CASE_SIDE_SIZE*1.3f);
			luciolGenerator->pos[2] = CASE_SIDE_SIZE*j;
			particleEngine->setGenerator(luciolGenerator);
			particleEngine->setSortType(NO_SORT);
			particleEngine->setTextureId(texLuciole->getId());
			LuciolParticle* luciolParticle = new LuciolParticle[7];
			particleEngine->setParticles(luciolParticle,7,sizeof(LuciolParticle));
			particleEngine->setBlendFunc(GL_SRC_ALPHA,GL_ONE);
			// ici il ne faut pas oublier de demarrer le moteur de particule, sinon au premier appel de update, le 
			// manager va trouver le moteur arreter et va le supprimer.
			particleEngine->start();
			ParticleManager::getGlobal().addEngine(particleEngine);
		}
	}

	for (int i = 5; i < gw; i+= 5){
		for (int j = i%3; j < gh; j+= 5){
			if (gc[i][j]->getGroundType() == ground::WATER){
				ParticleEngine * particleEngine = new ParticleEngine();
				fishEngine.push_back(particleEngine);
				// ici aussi la destruction du generator est prise en charge par le moteur de particule
				FishGenerator* fishGenerator = new FishGenerator(CASE_SIDE_SIZE/2.0f);
				fishGenerator->pos[0] = CASE_SIDE_SIZE*(i+0.5);
				fishGenerator->pos[1] = this->getElevation(i+0.5f,j+0.5f)-1;
				fishGenerator->pos[2] = CASE_SIDE_SIZE*(j+0.5);
				particleEngine->setGenerator(fishGenerator);
				particleEngine->setSortType(NO_SORT);
				particleEngine->setTextureId(texFish->getId());
				DirectionalParticle* fishParticle = new DirectionalParticle[3];
				particleEngine->setParticles(fishParticle,3,sizeof(DirectionalParticle));
				particleEngine->setBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
				// ici il ne faut pas oublier de demarrer le moteur de particule, sinon au premier appel de update, le 
				// manager va trouver le moteur arreter et va le supprimer.
				particleEngine->start();
				ParticleManager::getGlobal().addEngine(particleEngine);
			}
		}
	}

	this->miniMap.mapChanged();
	this->mapIsLoaded = true;
}

float Renderer::computeHauteur(int x, int y)
{
	//moyenne des cases adjacentes
	/*float res = 0.0f;
	float coeff = 0.0f;

	if(x-1>=0 && y-1>=0)
	{
		res += gc[x-1][y-1]->getElevation();
		coeff++;
	}
	if(x<(int)gw && y-1>=0)
	{
		res += gc[x][y-1]->getElevation();
		coeff++;
	}
	if(x<(int)gw && y<(int)gh)
	{
		res += gc[x][y]->getElevation();
		coeff++;
	}
	if(x-1>=0 && y<(int)gh)
	{
		res += gc[x-1][y]->getElevation();
		coeff++;
	}

	res /= coeff;
	return res;*/
	
	//maximum des cases adjacentes
	float res = 0.0f;
	if(x-1>=0 && y-1>=0)
	{
		res = (res>gc[x-1][y-1]->getElevation()) ? res : gc[x-1][y-1]->getElevation();
	}
	if(x<(int)gw && y-1>=0)
	{
		res = (res>gc[x][y-1]->getElevation()) ? res : gc[x][y-1]->getElevation();
	}
	if(x<(int)gw && y<(int)gh)
	{
		res = (res>gc[x][y]->getElevation()) ? res : gc[x][y]->getElevation();
	}
	if(x-1>=0 && y<(int)gh)
	{
		res = (res>gc[x-1][y]->getElevation()) ? res : gc[x-1][y]->getElevation();
	}
	return res;
}


void Renderer::beginFrame(float time_ms)
{
	if(mapIsLoaded)
	{
		int tmp;
		//enregistrement du temp écoulé
		this->elapsedTime = this->timeFactor*time_ms;
		this->visibilityElapsedTime += this->elapsedTime;

		if(this->elapsedTime>0) {	//on update seulement si le temp est > a 0

			if(this->visibilityElapsedTime>this->visibilityUpdateTime)
			{
				for(int i=0;i<this->gvw;i++) {
					for(int j=0;j<this->gvh;j++) {
							tmp = (i*gvh+j)*3;
							this->color[tmp]=this->groundWarFogColorIntensity;
							this->color[tmp+1]=this->groundWarFogColorIntensity;
							this->color[tmp+2]=this->groundWarFogColorIntensity;
					}
				}

				//update de la matrice de brouillard de guerre
				if(currentArmy!=NULL)
				{
					this->visibilityElapsedTime = 0;//pas de report : sa sert à rien ici
					bool** visibilityMatrix = currentArmy->getGroundArmyInfo().getVisibilityMatrix();
					for(int i=0;i<this->gw;i++) {
						for(int j=0;j<this->gh;j++) {
							if(visibilityMatrix[i][j]) {
								tmp = (i*gvh+j)*3;
								this->color[tmp]=this->groundSeenColorIntensity;
								this->color[tmp+1]=this->groundSeenColorIntensity;
								this->color[tmp+2]=this->groundSeenColorIntensity;
								tmp = ((i+1)*gvh+j)*3;
								this->color[tmp]=this->groundSeenColorIntensity;
								this->color[tmp+1]=this->groundSeenColorIntensity;
								this->color[tmp+2]=this->groundSeenColorIntensity;
								tmp = (i*gvh+(j+1))*3;
								this->color[tmp]=this->groundSeenColorIntensity;
								this->color[tmp+1]=this->groundSeenColorIntensity;
								this->color[tmp+2]=this->groundSeenColorIntensity;
								tmp = ((i+1)*gvh+(j+1))*3;
								this->color[tmp]=this->groundSeenColorIntensity;
								this->color[tmp+1]=this->groundSeenColorIntensity;
								this->color[tmp+2]=this->groundSeenColorIntensity;
							}
						}
					}
				}
				else{
					this->visibilityElapsedTime = 0;//pas de report : sa sert à rien ici
					bool** visibilityMatrix1 = army1->getGroundArmyInfo().getVisibilityMatrix();
					bool** visibilityMatrix2 = army2->getGroundArmyInfo().getVisibilityMatrix();
					
					for(int i=0;i<this->gw;i++) {
						for(int j=0;j<this->gh;j++) {
							if(visibilityMatrix1[i][j] || visibilityMatrix2[i][j]) {
								tmp = (i*gvh+j)*3;
								this->color[tmp]=this->groundSeenColorIntensity;
								this->color[tmp+1]=this->groundSeenColorIntensity;
								this->color[tmp+2]=this->groundSeenColorIntensity;
								tmp = ((i+1)*gvh+j)*3;
								this->color[tmp]=this->groundSeenColorIntensity;
								this->color[tmp+1]=this->groundSeenColorIntensity;
								this->color[tmp+2]=this->groundSeenColorIntensity;
								tmp = (i*gvh+(j+1))*3;
								this->color[tmp]=this->groundSeenColorIntensity;
								this->color[tmp+1]=this->groundSeenColorIntensity;
								this->color[tmp+2]=this->groundSeenColorIntensity;
								tmp = ((i+1)*gvh+(j+1))*3;
								this->color[tmp]=this->groundSeenColorIntensity;
								this->color[tmp+1]=this->groundSeenColorIntensity;
								this->color[tmp+2]=this->groundSeenColorIntensity;
							}
						}
					}
				}
			}

			//update animation de l'eau
			this->trWater += 0.001f*(float)this->elapsedTime;
			
			// on update les particules
			curEnvPE->update(elapsedTime*0.001f);
			ParticleManager::getGlobal().update(this->elapsedTime*0.001f);
			windTime += this->elapsedTime*0.001f;
			for (std::vector<ParticleEngine::ParticlePhysique*>::iterator it = vent.begin(); it != vent.end(); ++it){
				(*it)->physique[0] = cos(windTime/30)*0.3f;
			}

			//on update les modeles enregistrés
			ModelInstance* mInstance;
			RenderableEntity* rEntity;
			float eX;
			float eY;
			float x;
			float z;
			float fx;
			float fz;
			float minx;
			float minz;
			float maxx;
			float maxz;
			float rotMatrix[2][2];
			std::map<RenderableEntity*,RenderableEntity*>::iterator itm;
			for( itm=renderableEntities.begin() ; itm!=renderableEntities.end() ; itm++) {
				rEntity = itm->second;
				mInstance = &rEntity->modelInstance;
				eX = rEntity->getRenderableEntityPosition().getX();
				eY = rEntity->getRenderableEntityPosition().getY();

				// on doit aussi mettre a jour la bounding box des models statiques mais bougé par les unitée (chevaux)
				/*if(!mInstance->isGeometricalyStatic || !mInstance->isStatic)*/ {

					mInstance->updateInstance_aabbAndAnimState(this->elapsedTime);		//on update la AABB.
					
					//calcul de la AABB global du model
					rotMatrix[0][0] = cos(rEntity->getRenderableEntityAngle()*DEG2RAD);
					rotMatrix[0][1] = sin(rEntity->getRenderableEntityAngle()*DEG2RAD);
					rotMatrix[1][0] = -rotMatrix[0][1];
					rotMatrix[1][1] = rotMatrix[0][0];
					minx=9999.0f;
					minz=9999.0f;
					maxx=-9999.0f;
					maxz=-9999.0f;
					for(float i=mInstance->animState.aabb.minx; i<=mInstance->animState.aabb.maxx; i+=(mInstance->animState.aabb.maxx-mInstance->animState.aabb.minx)) {
						x = i;
						for(float j=mInstance->animState.aabb.minz; j<=mInstance->animState.aabb.maxz; j+=(mInstance->animState.aabb.maxz-mInstance->animState.aabb.minz)) {
							z = j;

							fx = x*rotMatrix[0][0] + z*rotMatrix[0][1];
							fz = x*rotMatrix[1][0] + z*rotMatrix[1][1];

							if(fx<minx) minx = fx;
							if(fz<minz) minz = fz;
							if(fx>maxx) maxx = fx;
							if(fz>maxz) maxz = fz;
						}
					}
					float elev = this->getElevation(rEntity->getRenderableEntityPosition().getX()+0.5f,rEntity->getRenderableEntityPosition().getY()+0.5f);
					rEntity->aabb.minx = minx + eX*CASE_SIDE_SIZE;
					rEntity->aabb.miny = mInstance->animState.aabb.miny + elev;
					rEntity->aabb.minz = minz + eY*CASE_SIDE_SIZE;
					rEntity->aabb.maxx = maxx + eX*CASE_SIDE_SIZE;
					rEntity->aabb.maxy = mInstance->animState.aabb.maxy + elev;
					rEntity->aabb.maxz = maxz + eY*CASE_SIDE_SIZE;
					rEntity->aabb.computeLimitInverse();
				}
			}
			// on met a jour les decors
			for( itm=decors.begin() ; itm!=decors.end() ; itm++) {
				rEntity = itm->second;
				mInstance = &rEntity->modelInstance;
				eX = rEntity->getRenderableEntityPosition().getX();
				eY = rEntity->getRenderableEntityPosition().getY();

				if(!mInstance->isGeometricalyStatic || !mInstance->isStatic) {

					mInstance->updateInstance_aabbAndAnimState(this->elapsedTime);		//on update la AABB.
					
					//calcul de la AABB global du model
					rotMatrix[0][0] = cos(rEntity->getRenderableEntityAngle()*DEG2RAD);
					rotMatrix[0][1] = sin(rEntity->getRenderableEntityAngle()*DEG2RAD);
					rotMatrix[1][0] = -rotMatrix[0][1];
					rotMatrix[1][1] = rotMatrix[0][0];
					minx=9999.0f;
					minz=9999.0f;
					maxx=-9999.0f;
					maxz=-9999.0f;
					for(float i=mInstance->animState.aabb.minx; i<=mInstance->animState.aabb.maxx; i+=(mInstance->animState.aabb.maxx-mInstance->animState.aabb.minx)) {
						x = i;
						for(float j=mInstance->animState.aabb.minz; j<=mInstance->animState.aabb.maxz; j+=(mInstance->animState.aabb.maxz-mInstance->animState.aabb.minz)) {
							z = j;

							fx = x*rotMatrix[0][0] + z*rotMatrix[0][1];
							fz = x*rotMatrix[1][0] + z*rotMatrix[1][1];

							if(fx<minx) minx = fx;
							if(fz<minz) minz = fz;
							if(fx>maxx) maxx = fx;
							if(fz>maxz) maxz = fz;
						}
					}
					float elev = this->getElevation(rEntity->getRenderableEntityPosition().getX()+0.5f,rEntity->getRenderableEntityPosition().getY()+0.5f);
					rEntity->aabb.minx = minx + eX*CASE_SIDE_SIZE;
					rEntity->aabb.miny = mInstance->animState.aabb.miny + elev;
					rEntity->aabb.minz = minz + eY*CASE_SIDE_SIZE;
					rEntity->aabb.maxx = maxx + eX*CASE_SIDE_SIZE;
					rEntity->aabb.maxy = mInstance->animState.aabb.maxy + elev;
					rEntity->aabb.maxz = maxz + eY*CASE_SIDE_SIZE;
					rEntity->aabb.computeLimitInverse();
				}
			}
		}
	}
}
	
void Renderer::endFrame()
{
	if(mapIsLoaded)
	{
	}
}

void Renderer::render(float *pos, Frustum* frust)
{
	if(mapIsLoaded)
	{
		//copie des paramètres
		if(!this->skipFrustumUpdate)
		{
			this->f = *frust;
			this->pos[0] = pos[0];
			this->pos[1] = pos[1];
			this->pos[2] = pos[2];
		}

		//////////////////
		/// VISIBILITé ///
		//////////////////
		//on calcul quel sont les feuilles du quad-tree qui sont visible
		this->visibleLeaf.clear();
		this->rec_ComputeVisMap(qtRoot);

		/////////////
		/// RENDU ///
		/////////////
		/////////////
		//activation du brouillard
		if(this->useGLFog) {
			glEnable(GL_FOG);
			glFogfv(GL_FOG_COLOR, fogcolor);
			glFogi(GL_FOG_MODE, GL_LINEAR);
			glFogf(GL_FOG_START, 2.0f*CASE_SIDE_SIZE);
			glFogf(GL_FOG_END, FOG_DISTANCE);
		}
		/////////////
		//activation de l'éclairage OpenGL
		if(this->useGLlighting) {
			static int LightPos[4] = {1,1,1,0};
			glLightiv(GL_LIGHT0,GL_POSITION,LightPos);
			glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 1.0f);
			glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.0f);
			glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.0f);
			static float MatDif[4] = {1.0f, 1.0f, 1.0f, 1.0f};
			static float MatSpec[4] = {0.0f, 0.0f, 0.0f, 0.0f};
			static float Light1Dif[4] = {1.0f, 1.0f, 1.0f, 1.0f};
			static float Light1Spec[4] = {0.0f, 0.0f, 0.0f, 0.0f};
			static float Light1Amb[4] = {1.0f, 1.0f, 1.0f, 1.0f};
			glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,MatSpec);
			glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,MatDif);
			static float MatAmbModel[4] = {0.3f, 0.3f, 0.3f, 1.0f};
			glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,MatAmbModel);
			glMateriali(GL_FRONT_AND_BACK,GL_SHININESS,1);
			glLightfv(GL_LIGHT0, GL_DIFFUSE, Light1Dif);
			glLightfv(GL_LIGHT0, GL_SPECULAR, Light1Spec);
			glLightfv(GL_LIGHT0, GL_AMBIENT, Light1Amb);
			static GLfloat lmodel_ambient[] = { 0.1f, 0.1f, 0.1f, 0.1f};
			glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
			glEnable(GL_LIGHT0);
			glEnable(GL_LIGHTING);
			glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
		}
		else {
			glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);
		}

		glEnable(GL_CULL_FACE);
		glCullFace(GL_CCW);

		//On affiche les arbre visibles du quad-tree
		this->renderTree();
		
		// on affiche les decors visible
		this->renderDecor();

		//On affiche les modeles visibles du frustum
		this->renderModel();

		//on affiche les sols visibles du quad-tree
		static float GroundAmb[4] = {0.08f, 0.08f, 0.08f, 1.0f};
		glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,GroundAmb);
		this->renderGround();

		glDisable(GL_CULL_FACE);
		glDisable(GL_FOG);
		glDisable(GL_LIGHTING);
		glDisable(GL_LIGHT0);

		//affichage de l'eau visibles du quad-tree
		//(après le terrain car sinon l'eau pourrai cacher le terrain d'une feuille adjacente à celle-ci)
		this->renderWater();

		//affichage des moteurs de particules visibles du frustum
		this->renderParticle();

		//On affiche la vie des entités
		if(this->showUnitLife)
			this->renderEntitiesLife();

		////////////////
		/// DEBUGAGE ///
		////////////////
		// debuggage des zones&axes
		this->debugZone();

		miniMap.draw();
	}
}


void Renderer::rec_deleteQuadTree(QT_Node *qtnode)
{
	if(qtnode->isLeaf) {
		if(qtnode->groundForestElement!=NULL){
			delete [] qtnode->groundForestElement;
			qtnode->groundForestElement = NULL;
		}
		if(qtnode->groundMeadowElement!=NULL){
			delete [] qtnode->groundMeadowElement;
			qtnode->groundForestElement = NULL;
		}
		if(qtnode->groundOreeElement!=NULL){
			delete [] qtnode->groundOreeElement;
			qtnode->groundForestElement = NULL;
		}
		if(qtnode->groundRoadElement!=NULL){
			delete [] qtnode->groundRoadElement;
			qtnode->groundForestElement = NULL;
		}
		if(qtnode->groundWaterElement!=NULL){
			delete [] qtnode->groundWaterElement;
			qtnode->groundForestElement = NULL;
		}
		glDeleteLists(qtnode->waterDL,1);
		glDeleteLists(qtnode->tree_leafDL,1);
		glDeleteLists(qtnode->tree_trunkDL,1);
	}
	else {
		//on nettoie les fils puis on les suprime de la mémoire
		if(qtnode->sons_node[0]!=NULL) {
			rec_deleteQuadTree(qtnode->sons_node[0]);
			delete qtnode->sons_node[0];
			qtnode->sons_node[0] = NULL;
		}
			if(qtnode->sons_node[1]!=NULL) {
			rec_deleteQuadTree(qtnode->sons_node[1]);
			delete qtnode->sons_node[1];
			qtnode->sons_node[1] = NULL;
		}
		if(qtnode->sons_node[2]!=NULL) {
			rec_deleteQuadTree(qtnode->sons_node[2]);
			delete qtnode->sons_node[2];
			qtnode->sons_node[2] = NULL;
		}
		if(qtnode->sons_node[3]!=NULL) {
			rec_deleteQuadTree(qtnode->sons_node[3]);
			delete qtnode->sons_node[3];
			qtnode->sons_node[3] = NULL;
		}
	}
}


void Renderer::rec_buildQuadTree(int oX, int oZ, int sX, int sZ, QT_Node *qtnode)
{
	//// !!! Cet algorithme ne marche que pour les carte rectangulaire de taille de ^2 
	//// avec une taille minimum de QT_MIN_SIDE_SIZE !!!

	if( sX==QT_MIN_SIDE_SIZE && sZ==QT_MIN_SIDE_SIZE )
	{
		//On est dans le cas d'une feuille de l'arbre
		qtnode->isLeaf = true;
		qtnode->oX = oX;
		qtnode->oZ = oZ;
		qtnode->sX = sX;
		qtnode->sZ = sZ;

		qtnode->groundMeadowElement = NULL;
		qtnode->groundForestElement = NULL;
		qtnode->groundOreeElement = NULL;
		qtnode->groundRoadElement = NULL;
		qtnode->groundWaterElement = NULL;
		qtnode->waterDL = 0;
		qtnode->tree_leafDL = 0;
		qtnode->tree_trunkDL = 0;

		//pas de fils
		qtnode->sons_node[0]=NULL;
		qtnode->sons_node[1]=NULL;
		qtnode->sons_node[2]=NULL;
		qtnode->sons_node[3]=NULL;

		//calcul de la bounding box
		float minX=99999.0f;
		float maxX=-99999.0f;
		float minY=99999.0f;
		float maxY=-99999.0f;
		float minZ=99999.0f;
		float maxZ=-99999.0f;
		const float treeHeight = 2.5f*CASE_SIDE_SIZE;	//la hauteur d'un arbre en plus sur la carte pour la AABB de la feuille
		float tmp;
		int tmpid;
		for(int i=0;i<=(sX);i++)	//todo, je croit que je ne prend pas en compte les vertices du bord extrem!!!!
		{
			for(int j=0;j<=(sZ);j++)
			{
				tmpid = (oX+i)*(gvh) + (oZ+j);

				tmp = vertex[tmpid*3];
				if(tmp<minX)
					minX=tmp;
				if(tmp>maxX)
					maxX=tmp;

				tmp = vertex[tmpid*3 +1];
				if(tmp<minY)
					minY=tmp;
				if(tmp>maxY)
					maxY=tmp;

				tmp = vertex[tmpid*3 +2];
				if(tmp<minZ)
					minZ=tmp;
				if(tmp>maxZ)
					maxZ=tmp;
				}
		}
		//prise en compte des arbres
		for(int i=oX;i<(oX+sX);i++){
			for(int j=oZ;j<(oZ+sZ);j++){
				if(gc[i][j]->getGroundType()==OREE || gc[i][j]->getGroundType()==FOREST) {
					tmp = this->getElevation(i+0.5f,j+0.5f) + treeHeight;
					if(tmp<minY)
						minY=tmp;
					if(tmp>maxY)
						maxY=tmp;
				}
			}
		}
		qtnode->bb.minx = minX;
		qtnode->bb.miny = minY;
		qtnode->bb.minz = minZ;
		qtnode->bb.maxx = maxX;
		qtnode->bb.maxy = maxY;
		qtnode->bb.maxz = maxZ;
		qtnode->bb.computeLimitInverse();
		return;
	}
	else
	{
		//On est dans le cas d'une noeud interne de l'arbre
		qtnode->isLeaf = false;

		//TODO : vérifier mais peut provoquer des bug si la carte n'est pas puissance de 2
		int sX2=sX/2;
		if(sX2<QT_MIN_SIDE_SIZE)
			sX2=QT_MIN_SIDE_SIZE;
		int sZ2=sZ/2;
		if(sZ2<QT_MIN_SIDE_SIZE)
			sZ2=QT_MIN_SIDE_SIZE;

		//on doit subdivisé
		qtnode->sons_node[0]=NULL;
		qtnode->sons_node[1]=NULL;
		qtnode->sons_node[2]=NULL;
		qtnode->sons_node[3]=NULL;

		//demande le calcule des noeuds fils avec la bonnes zone
		if(sX>sZ)
		{
			//le cas d'un noeud plus grand sur l'axe X
			qtnode->sons_node[0]=new QT_Node;
			qtnode->sons_node[1]=new QT_Node;
			
			rec_buildQuadTree(oX,     oZ, sX2, sZ, qtnode->sons_node[0] );
			rec_buildQuadTree(oX+sX2, oZ, sX2, sZ, qtnode->sons_node[1] );

			//calcul de la AABB suivant les fils
			qtnode->bb.minx = qtnode->sons_node[0]->bb.minx<qtnode->sons_node[1]->bb.minx ?
														qtnode->sons_node[0]->bb.minx : qtnode->sons_node[1]->bb.minx;
			qtnode->bb.miny = qtnode->sons_node[0]->bb.miny<qtnode->sons_node[1]->bb.miny ?
														qtnode->sons_node[0]->bb.miny : qtnode->sons_node[1]->bb.miny;
			qtnode->bb.minz = qtnode->sons_node[0]->bb.minz<qtnode->sons_node[1]->bb.minz ?
														qtnode->sons_node[0]->bb.minz : qtnode->sons_node[1]->bb.minz;
			qtnode->bb.maxx = qtnode->sons_node[0]->bb.maxx>qtnode->sons_node[1]->bb.maxx ?
														qtnode->sons_node[0]->bb.maxx : qtnode->sons_node[1]->bb.maxx;
			qtnode->bb.maxy = qtnode->sons_node[0]->bb.maxy>qtnode->sons_node[1]->bb.maxy ?
														qtnode->sons_node[0]->bb.maxy : qtnode->sons_node[1]->bb.maxy;
			qtnode->bb.maxz = qtnode->sons_node[0]->bb.maxz>qtnode->sons_node[1]->bb.maxz ?
														qtnode->sons_node[0]->bb.maxz : qtnode->sons_node[1]->bb.maxz;
			qtnode->bb.computeLimitInverse();
		}
		else if(sZ>sX)
		{
			//le cas d'un noeud plus grand sur l'axe Z
			qtnode->sons_node[0]=new QT_Node;
			qtnode->sons_node[1]=new QT_Node;
			
			rec_buildQuadTree(oX, oZ    , sX, sZ2, qtnode->sons_node[0] );
			rec_buildQuadTree(oX, oZ+sZ2, sX, sZ2, qtnode->sons_node[1] );

			//calcul de la AABB suivant les fils
			qtnode->bb.minx = qtnode->sons_node[0]->bb.minx<qtnode->sons_node[1]->bb.minx ?
														qtnode->sons_node[0]->bb.minx : qtnode->sons_node[1]->bb.minx;
			qtnode->bb.miny = qtnode->sons_node[0]->bb.miny<qtnode->sons_node[1]->bb.miny ?
														qtnode->sons_node[0]->bb.miny : qtnode->sons_node[1]->bb.miny;
			qtnode->bb.minz = qtnode->sons_node[0]->bb.minz<qtnode->sons_node[1]->bb.minz ?
														qtnode->sons_node[0]->bb.minz : qtnode->sons_node[1]->bb.minz;
			qtnode->bb.maxx = qtnode->sons_node[0]->bb.maxx>qtnode->sons_node[1]->bb.maxx ?
														qtnode->sons_node[0]->bb.maxx : qtnode->sons_node[1]->bb.maxx;
			qtnode->bb.maxy = qtnode->sons_node[0]->bb.maxy>qtnode->sons_node[1]->bb.maxy ?
														qtnode->sons_node[0]->bb.maxy : qtnode->sons_node[1]->bb.maxy;
			qtnode->bb.maxz = qtnode->sons_node[0]->bb.maxz>qtnode->sons_node[1]->bb.maxz ?
														qtnode->sons_node[0]->bb.maxz : qtnode->sons_node[1]->bb.maxz;
			qtnode->bb.computeLimitInverse();
		}
		else
		{
			//le cas d'un noeud carré sZ==sX
			qtnode->sons_node[0]=new QT_Node;
			qtnode->sons_node[1]=new QT_Node;
			qtnode->sons_node[2]=new QT_Node;
			qtnode->sons_node[3]=new QT_Node;

			rec_buildQuadTree(oX,     oZ    , sX2, sZ2, qtnode->sons_node[0] );
			rec_buildQuadTree(oX+sX2, oZ    , sX2, sZ2, qtnode->sons_node[1] );
			rec_buildQuadTree(oX+sX2, oZ+sZ2, sX2, sZ2, qtnode->sons_node[2] );
			rec_buildQuadTree(oX,     oZ+sZ2, sX2, sZ2, qtnode->sons_node[3] );
			
			//calcul de la AABB suivant les fils
			qtnode->bb.minx = qtnode->sons_node[0]->bb.minx;
			qtnode->bb.minx = qtnode->sons_node[1]->bb.minx<qtnode->bb.minx ?
														qtnode->sons_node[1]->bb.minx : qtnode->bb.minx;
			qtnode->bb.minx = qtnode->sons_node[2]->bb.minx<qtnode->bb.minx ?
														qtnode->sons_node[2]->bb.minx : qtnode->bb.minx;
			qtnode->bb.minx = qtnode->sons_node[3]->bb.minx<qtnode->bb.minx ?
														qtnode->sons_node[3]->bb.minx : qtnode->bb.minx;
			qtnode->bb.miny = qtnode->sons_node[0]->bb.miny;
			qtnode->bb.miny = qtnode->sons_node[1]->bb.miny<qtnode->bb.miny ?
														qtnode->sons_node[1]->bb.miny : qtnode->bb.miny;
			qtnode->bb.miny = qtnode->sons_node[2]->bb.miny<qtnode->bb.miny ?
														qtnode->sons_node[2]->bb.miny : qtnode->bb.miny;
			qtnode->bb.miny = qtnode->sons_node[3]->bb.miny<qtnode->bb.miny ?
														qtnode->sons_node[3]->bb.miny : qtnode->bb.miny;
			qtnode->bb.minz = qtnode->sons_node[0]->bb.minz;
			qtnode->bb.minz = qtnode->sons_node[1]->bb.minz<qtnode->bb.minz ?
														qtnode->sons_node[1]->bb.minz : qtnode->bb.minz;
			qtnode->bb.minz = qtnode->sons_node[2]->bb.minz<qtnode->bb.minz ?
														qtnode->sons_node[2]->bb.minz : qtnode->bb.minz;
			qtnode->bb.minz = qtnode->sons_node[3]->bb.minz<qtnode->bb.minz ?
														qtnode->sons_node[3]->bb.minz : qtnode->bb.minz;
			qtnode->bb.maxx = qtnode->sons_node[0]->bb.maxx;
			qtnode->bb.maxx = qtnode->sons_node[1]->bb.maxx>qtnode->bb.maxx ?
														qtnode->sons_node[1]->bb.maxx : qtnode->bb.maxx;
			qtnode->bb.maxx = qtnode->sons_node[2]->bb.maxx>qtnode->bb.maxx ?
														qtnode->sons_node[2]->bb.maxx : qtnode->bb.maxx;
			qtnode->bb.maxx = qtnode->sons_node[3]->bb.maxx>qtnode->bb.maxx ?
														qtnode->sons_node[3]->bb.maxx : qtnode->bb.maxx;
			qtnode->bb.maxy = qtnode->sons_node[0]->bb.maxy;
			qtnode->bb.maxy = qtnode->sons_node[1]->bb.maxy>qtnode->bb.maxy ?
														qtnode->sons_node[1]->bb.maxy : qtnode->bb.maxy;
			qtnode->bb.maxy = qtnode->sons_node[2]->bb.maxy>qtnode->bb.maxy ?
														qtnode->sons_node[2]->bb.maxy : qtnode->bb.maxy;
			qtnode->bb.maxy = qtnode->sons_node[3]->bb.maxy>qtnode->bb.maxy ?
														qtnode->sons_node[3]->bb.maxy : qtnode->bb.maxy;
			qtnode->bb.maxz = qtnode->sons_node[0]->bb.maxz;
			qtnode->bb.maxz = qtnode->sons_node[1]->bb.maxz>qtnode->bb.maxz ?
														qtnode->sons_node[1]->bb.maxz : qtnode->bb.maxz;
			qtnode->bb.maxz = qtnode->sons_node[2]->bb.maxz>qtnode->bb.maxz ?
														qtnode->sons_node[2]->bb.maxz : qtnode->bb.maxz;
			qtnode->bb.maxz = qtnode->sons_node[3]->bb.maxz>qtnode->bb.maxz ?
														qtnode->sons_node[3]->bb.maxz : qtnode->bb.maxz;
			qtnode->bb.computeLimitInverse();
		}

		return;
	}
}

void Renderer::rec_ComputeVisMap(QT_Node* qt, bool forceVisible)
{
	int nodeVisibility=FRUSTUM_OUT;
	if(!forceVisible) {
		nodeVisibility = f.paveVisibility(&qt->bb.pos.x,qt->bb.sizex,qt->bb.sizey,qt->bb.sizez);
	}

	if( forceVisible || nodeVisibility==FRUSTUM_IN || nodeVisibility==FRUSTUM_INTERSECT )
	{
		//on met seulement les feuilles
		if(qt->isLeaf)
			visibleLeaf.push_back(qt);

		//affichage du noeud du QT pour debuggage
		if(this->showQuadTree)
		{
			if(qt->isLeaf)
				glColor3f(0.0f,1.0f,0.0f);
			else
				glColor3f(1.0f,0.0f,1.0f);

			if(forceVisible || (nodeVisibility==FRUSTUM_IN))
				glColor3f(1.0f,1.0f,0.0f);

			glLineWidth(3.0f);
			glBegin(GL_LINES);
			glVertex3f(qt->bb.pos.x-qt->bb.sizex, qt->bb.pos.y-qt->bb.sizey, qt->bb.pos.z-qt->bb.sizez);
			glVertex3f(qt->bb.pos.x+qt->bb.sizex, qt->bb.pos.y-qt->bb.sizey, qt->bb.pos.z-qt->bb.sizez);
			
			glVertex3f(qt->bb.pos.x+qt->bb.sizex, qt->bb.pos.y-qt->bb.sizey, qt->bb.pos.z-qt->bb.sizez);
			glVertex3f(qt->bb.pos.x+qt->bb.sizex, qt->bb.pos.y-qt->bb.sizey, qt->bb.pos.z+qt->bb.sizez);
			
			glVertex3f(qt->bb.pos.x+qt->bb.sizex, qt->bb.pos.y-qt->bb.sizey, qt->bb.pos.z+qt->bb.sizez);
			glVertex3f(qt->bb.pos.x-qt->bb.sizex, qt->bb.pos.y-qt->bb.sizey, qt->bb.pos.z+qt->bb.sizez);
			
			glVertex3f(qt->bb.pos.x-qt->bb.sizex, qt->bb.pos.y-qt->bb.sizey, qt->bb.pos.z+qt->bb.sizez);
			glVertex3f(qt->bb.pos.x-qt->bb.sizex, qt->bb.pos.y-qt->bb.sizey, qt->bb.pos.z-qt->bb.sizez);
			
			glVertex3f(qt->bb.pos.x-qt->bb.sizex, qt->bb.pos.y+qt->bb.sizey, qt->bb.pos.z-qt->bb.sizez);
			glVertex3f(qt->bb.pos.x+qt->bb.sizex, qt->bb.pos.y+qt->bb.sizey, qt->bb.pos.z-qt->bb.sizez);
			
			glVertex3f(qt->bb.pos.x+qt->bb.sizex, qt->bb.pos.y+qt->bb.sizey, qt->bb.pos.z-qt->bb.sizez);
			glVertex3f(qt->bb.pos.x+qt->bb.sizex, qt->bb.pos.y+qt->bb.sizey, qt->bb.pos.z+qt->bb.sizez);
			
			glVertex3f(qt->bb.pos.x+qt->bb.sizex, qt->bb.pos.y+qt->bb.sizey, qt->bb.pos.z+qt->bb.sizez);
			glVertex3f(qt->bb.pos.x-qt->bb.sizex, qt->bb.pos.y+qt->bb.sizey, qt->bb.pos.z+qt->bb.sizez);
			
			glVertex3f(qt->bb.pos.x-qt->bb.sizex, qt->bb.pos.y+qt->bb.sizey, qt->bb.pos.z+qt->bb.sizez);
			glVertex3f(qt->bb.pos.x-qt->bb.sizex, qt->bb.pos.y+qt->bb.sizey, qt->bb.pos.z-qt->bb.sizez);
			
			
			glVertex3f(qt->bb.pos.x-qt->bb.sizex, qt->bb.pos.y+qt->bb.sizey, qt->bb.pos.z-qt->bb.sizez);
			glVertex3f(qt->bb.pos.x-qt->bb.sizex, qt->bb.pos.y-qt->bb.sizey, qt->bb.pos.z-qt->bb.sizez);

			glVertex3f(qt->bb.pos.x+qt->bb.sizex, qt->bb.pos.y+qt->bb.sizey, qt->bb.pos.z-qt->bb.sizez);
			glVertex3f(qt->bb.pos.x+qt->bb.sizex, qt->bb.pos.y-qt->bb.sizey, qt->bb.pos.z-qt->bb.sizez);

			glVertex3f(qt->bb.pos.x+qt->bb.sizex, qt->bb.pos.y-qt->bb.sizey, qt->bb.pos.z+qt->bb.sizez);
			glVertex3f(qt->bb.pos.x+qt->bb.sizex, qt->bb.pos.y+qt->bb.sizey, qt->bb.pos.z+qt->bb.sizez);
			
			glVertex3f(qt->bb.pos.x-qt->bb.sizex, qt->bb.pos.y-qt->bb.sizey, qt->bb.pos.z+qt->bb.sizez);
			glVertex3f(qt->bb.pos.x-qt->bb.sizex, qt->bb.pos.y+qt->bb.sizey, qt->bb.pos.z+qt->bb.sizez);
			glEnd();
			glLineWidth(1.0f);
		}
		
		//poursuite récursive du quadTree...
		if(qt->sons_node[0]!=NULL) rec_ComputeVisMap(qt->sons_node[0],forceVisible || (nodeVisibility==FRUSTUM_IN));
		if(qt->sons_node[1]!=NULL) rec_ComputeVisMap(qt->sons_node[1],forceVisible || (nodeVisibility==FRUSTUM_IN));
		if(qt->sons_node[2]!=NULL) rec_ComputeVisMap(qt->sons_node[2],forceVisible || (nodeVisibility==FRUSTUM_IN));
		if(qt->sons_node[3]!=NULL) rec_ComputeVisMap(qt->sons_node[3],forceVisible || (nodeVisibility==FRUSTUM_IN));
	}
}

void Renderer::renderGroundForDL(QT_Node* qtnode)
{
	qtnode->groundMeadowElement = NULL;
	qtnode->groundMeadowElement = NULL;
	qtnode->groundForestElement = NULL;
	qtnode->groundOreeElement = NULL;
	qtnode->groundRoadElement = NULL;
	qtnode->groundWaterElement = NULL;
	qtnode->nbGroundMeadowElement = 0;
	qtnode->nbGroundMeadowElement = 0;
	qtnode->nbGroundForestElement = 0;
	qtnode->nbGroundOreeElement = 0;
	qtnode->nbGroundRoadElement = 0;
	qtnode->nbGroundWaterElement = 0;

	//prairie
	renderGroundWithTexture(MEADOW,&qtnode->nbGroundMeadowElement,&qtnode->groundMeadowElement,qtnode->oX,qtnode->oZ,qtnode->sX,qtnode->sZ);

	//sol de l'eau
	renderGroundWithTexture(WATER,&qtnode->nbGroundWaterElement,&qtnode->groundWaterElement,qtnode->oX,qtnode->oZ,qtnode->sX,qtnode->sZ);

	//foret
	renderGroundWithTexture(FOREST,&qtnode->nbGroundForestElement,&qtnode->groundForestElement,qtnode->oX,qtnode->oZ,qtnode->sX,qtnode->sZ);

	//oree de la foret
	renderGroundWithTexture(OREE,&qtnode->nbGroundOreeElement,&qtnode->groundOreeElement,qtnode->oX,qtnode->oZ,qtnode->sX,qtnode->sZ);

	//chemin
	renderGroundWithTexture(ROAD,&qtnode->nbGroundRoadElement,&qtnode->groundRoadElement,qtnode->oX,qtnode->oZ,qtnode->sX,qtnode->sZ);
}


void Renderer::renderWaterForDL(int oX, int oZ, int sX, int sZ)
{
	int tmpid00;
	int tmpid01;
	int tmpid11;
	int tmpid10;

	if(this->containsGroundWithTexture(WATER,oX,oZ,sX,sZ)) {
		glBegin(GL_TRIANGLES);
		for(int i=oX;i<oX+sX;i++){
			for(int j=oZ;j<oZ+sZ;j++){
				if(gc[i][j]->getGroundType()==WATER) {
					tmpid00 = (i)*(gvh) + (j);
					tmpid01 = (i+1)*(gvh) + (j);
					tmpid11 = (i+1)*(gvh) + (j+1);
					tmpid10 = (i)*(gvh) + (j+1);

					//T1
					glTexCoord2fv(&this->texCoord[tmpid10*2]);
					glVertex3f(	this->vertex[tmpid10*3+0],
								this->vertex[tmpid10*3+1] + this->vertexWithWaterLevelOffset(i,j+1),
								this->vertex[tmpid10*3+2]);
					glTexCoord2fv(&this->texCoord[tmpid11*2]);
					glVertex3f(	this->vertex[tmpid11*3+0],
								this->vertex[tmpid11*3+1] + this->vertexWithWaterLevelOffset(i+1,j+1),
								this->vertex[tmpid11*3+2]);
					glTexCoord2fv(&this->texCoord[tmpid01*2]);
					glVertex3f(	this->vertex[tmpid01*3+0],
								this->vertex[tmpid01*3+1] + this->vertexWithWaterLevelOffset(i+1,j),
								this->vertex[tmpid01*3+2]);
					//T2
					glTexCoord2fv(&this->texCoord[tmpid01*2]);
					glVertex3f(	this->vertex[tmpid01*3+0],
								this->vertex[tmpid01*3+1] + this->vertexWithWaterLevelOffset(i+1,j),
								this->vertex[tmpid01*3+2]);
					glTexCoord2fv(&this->texCoord[tmpid00*2]);
					glVertex3f(	this->vertex[tmpid00*3+0],
								this->vertex[tmpid00*3+1] + this->vertexWithWaterLevelOffset(i,j),
								this->vertex[tmpid00*3+2]);
					glTexCoord2fv(&this->texCoord[tmpid10*2]);
					glVertex3f(	this->vertex[tmpid10*3+0],
								this->vertex[tmpid10*3+1] + this->vertexWithWaterLevelOffset(i,j+1),
								this->vertex[tmpid10*3+2]);
				}
			}
		}
		glEnd();
	}
}

void Renderer::renderGroundWithTexture(GroundType t,unsigned int* nbEl, unsigned short** elem,int oX, int oZ, int sX, int sZ)
{
	int tmpid00;
	int tmpid01;
	int tmpid11;
	int tmpid10;
	int nbCase=0;
	unsigned short* elem2;
	unsigned int curit = 0;

	//on compte les cases contenant le type désirer
	for(int i=oX;i<oX+sX;i++)
		for(int j=oZ;j<oZ+sZ;j++)
			if(gc[i][j]->getGroundType()==t) nbCase++;

	//reglage de qtnode
	(*nbEl) = 6*nbCase;

	if(nbCase>0) {

		elem2 = new unsigned short[*nbEl];
		*elem = elem2;

		for(int i=oX;i<oX+sX;i++){
			for(int j=oZ;j<oZ+sZ;j++){
				if(gc[i][j]->getGroundType()==t) {
					tmpid00 = (i)*(gvh) + (j);
					tmpid01 = (i+1)*(gvh) + (j);
					tmpid11 = (i+1)*(gvh) + (j+1);
					tmpid10 = (i)*(gvh) + (j+1);

					elem2[curit] = tmpid10;
					curit++;
					elem2[curit] = tmpid11;
					curit++;
					elem2[curit] = tmpid01;
					curit++;
					elem2[curit] = tmpid01;
					curit++;
					elem2[curit] = tmpid00;
					curit++;
					elem2[curit] = tmpid10;
					curit++;
				}
			}
		}
	}
}


bool Renderer::containsGroundWithTexture(GroundType t,int oX, int oZ, int sX, int sZ)
{

	for(int i=oX;i<oX+sX;i++){
		for(int j=oZ;j<oZ+sZ;j++){
			if(gc[i][j]->getGroundType()==t)
				return true;
		}
	}
	return false;
}


float Renderer::vertexWithWaterLevelOffset(int i, int j)
{
	float coeff = 0.0f;
	if( (j)>=0 && (j)<gw && (i)>=0   &&( i)<gw   && gc[i][j]->getGroundType()==WATER)   coeff++;
	if( (j)>=0 && (j)<gw && (i-1)>=0 && (i-1)<gw && gc[i-1][j]->getGroundType()==WATER) coeff++;
	if( (i-1)>=0 && (i-1)<gw && (j-1)>=0   && (j-1)<gw   && gc[i-1][j-1]->getGroundType()==WATER)   coeff++;
	if( (i)>=0 && (i)<gw && (j-1)>=0 && (j-1)<gw && gc[i][j-1]->getGroundType()==WATER) coeff++;

	return (coeff==4.0f ? 12.0f : 0.0f);
}

void Renderer::renderModel(ModelInstance* mi)
{	
	glVertexPointer( 3, GL_FLOAT, 0, mi->vertex );//activation tab de vertice en mémoire cliente
	glTexCoordPointer( 2, GL_FLOAT, 0, mi->texCoord );//activation tab de texcoord en mémoire cliente
	glNormalPointer( GL_FLOAT, 0, mi->normal );//activation tab de normal en mémoire cliente

	//dessin du model
	glDrawElements(GL_TRIANGLES,mi->model->nbTris*3,GL_UNSIGNED_SHORT,mi->triangles);
}

void Renderer::renderQTLeafTreeDL(QT_Node* qt)
{
	int limitX = qt->oX+qt->sX;
	int limitZ = qt->oZ+qt->sZ;

	bool containsOree = this->containsGroundWithTexture(OREE,qt->oX,qt->oZ,qt->sX,qt->sZ);
	bool contiansForest = this->containsGroundWithTexture(FOREST,qt->oX,qt->oZ,qt->sX,qt->sZ);
	
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glEnableClientState(GL_NORMAL_ARRAY);
	//les feuilles avant
	if(containsOree||contiansForest) {
		glNewList(qt->tree_leafDL,GL_COMPILE);
		for(int i=qt->oX;i<limitX;i++){
			for(int j=qt->oZ;j<limitZ;j++){
				if(gc[i][j]->getGroundType()==FOREST || gc[i][j]->getGroundType()==OREE){
					glPushMatrix();
					//glTranslatef(i*CASE_SIDE_SIZE+CASE_SIDE_SIZE/2, gc[i][j]->getElevation()+(-1+1*cos(50.0f*(float)(i*gw+j))), j*CASE_SIDE_SIZE+CASE_SIDE_SIZE/2);
					glTranslatef(i*CASE_SIDE_SIZE+CASE_SIDE_SIZE/2, this->getElevation((float)i+0.5f,(float)j+0.5f)+(-1+1*cos(50.0f*(float)(i*gw+j))), j*CASE_SIDE_SIZE+CASE_SIDE_SIZE/2);
					glRotatef((i*gw+j)*50.0f,0.0f,1.0f,0.0f);
					glRotatef(-90.0f,1.0f,0.0f,0.0f);
					this->renderModel(this->treeLeafInstance);
					glPopMatrix();
				}
			}
		}
		glEndList();
	}
	//les tronc après
	if(containsOree) {
		glNewList(qt->tree_trunkDL,GL_COMPILE);
		for(int i=qt->oX;i<limitX;i++){
			for(int j=qt->oZ;j<limitZ;j++){
				if(gc[i][j]->getGroundType()==OREE){
					glPushMatrix();
					//glTranslatef(i*CASE_SIDE_SIZE+CASE_SIDE_SIZE/2, gc[i][j]->getElevation(), j*CASE_SIDE_SIZE+CASE_SIDE_SIZE/2);
					glTranslatef(i*CASE_SIDE_SIZE+CASE_SIDE_SIZE/2, this->getElevation((float)i+0.5f,(float)j+0.5f), j*CASE_SIDE_SIZE+CASE_SIDE_SIZE/2);
					glRotatef((i*gw+j)*50.0f,0.0f,1.0f,0.0f);
					glRotatef(-90.0f,1.0f,0.0f,0.0f);
					this->renderModel(this->treeTrunkInstance);
					glPopMatrix();
				}
			}
		}
		glEndList();
	}
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);
}

void Renderer::renderGroundZone(GroundZone* z)
{
	std::vector<Case*>* c = (std::vector<Case*>*)z->getCases();
	
	// dessin de la zone
	glDepthFunc(GL_LEQUAL);
	glBegin(GL_TRIANGLES);
	for (int i=0; i<(int)c->size(); i++){
		glVertex3fv(&vertex[ ( ((*c)[i]->getCoord()[0]+1)*gvh	+(*c)[i]->getCoord()[1])	*3]);
		glVertex3fv(&vertex[ ( ((*c)[i]->getCoord()[0]+1)*gvh	+(*c)[i]->getCoord()[1]+1)	*3]);
		glVertex3fv(&vertex[ ( (*c)[i]->getCoord()[0]*gvh		+(*c)[i]->getCoord()[1]+1)  *3]);
		glVertex3fv(&vertex[ ( (*c)[i]->getCoord()[0]*gvh		+(*c)[i]->getCoord()[1]+1)  *3]);
		glVertex3fv(&vertex[ ( (*c)[i]->getCoord()[0]*gvh		+(*c)[i]->getCoord()[1])    *3]);
		glVertex3fv(&vertex[ ( ((*c)[i]->getCoord()[0]+1)*gvh	+(*c)[i]->getCoord()[1])	*3]);
	}
	glEnd();
	glDepthFunc(GL_LESS);
	z->renderDebug();
}

void Renderer::debugZone()
{
	std::vector<QT_Node*>::iterator it;
	//affichage de l'intensité des case bloquante
	if(this->showBlockCase) {
		glDepthFunc(GL_LEQUAL);
		glBegin(GL_TRIANGLES);
		glColor3f(0.0f,0.0f,0.0f);
		for(it=visibleLeaf.begin(); it!=visibleLeaf.end(); it++) {
			for(int i=(*it)->oX;i<((*it)->oX+(*it)->sX);i++)
			{
				for(int j=(*it)->oZ;j<((*it)->oZ+(*it)->sZ);j++)
				{
					if(gc[i][j]->isPassable()) continue;
					glVertex3fv(&vertex[ ( (i+1)*gvh+j)  *3]);
					glVertex3fv(&vertex[ ( (i+1)*gvh+j+1)*3]);
					glVertex3fv(&vertex[ ( i*gvh+j+1)    *3]);
					glVertex3fv(&vertex[ ( i*gvh+j+1)    *3]);
					glVertex3fv(&vertex[ ( i*gvh+j)      *3]);
					glVertex3fv(&vertex[ ( (i+1)*gvh+j)  *3]);
				}
			
			}
		}
		glEnd();
		glDepthFunc(GL_LESS);
	}
	//affichage de l'intensité des zones de danger
	if(this->showDangerZoneIntensity) {
		glDepthFunc(GL_LEQUAL);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glBegin(GL_TRIANGLES);
		for(it=visibleLeaf.begin(); it!=visibleLeaf.end(); it++) {
			for(int i=(*it)->oX;i<((*it)->oX+(*it)->sX);i++)
			{
				for(int j=(*it)->oZ;j<((*it)->oZ+(*it)->sZ);j++)
				{
					if(gc[i][j]->getDangerZoneIntensity()<=0.0f) continue;
					glColor4f(1.0f,0.0f,0.0f,gc[i][j]->getDangerZoneIntensity());
					glVertex3fv(&vertex[ ( (i+1)*gvh+j)  *3]);
					glVertex3fv(&vertex[ ( (i+1)*gvh+j+1)*3]);
					glVertex3fv(&vertex[ ( i*gvh+j+1)    *3]);
					glVertex3fv(&vertex[ ( i*gvh+j+1)    *3]);
					glVertex3fv(&vertex[ ( i*gvh+j)      *3]);
					glVertex3fv(&vertex[ ( (i+1)*gvh+j)  *3]);
				}
			
			}
		}
		glEnd();
		glDisable(GL_BLEND);
		glDepthFunc(GL_LESS);
	}
	//affichage de l'intensité des zones fortes
	if(this->showStrongZoneIntensity) {
		glDepthFunc(GL_LEQUAL);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glBegin(GL_TRIANGLES);
		for(it=visibleLeaf.begin(); it!=visibleLeaf.end(); it++) {
			for(int i=(*it)->oX;i<((*it)->oX+(*it)->sX);i++)
			{
				for(int j=(*it)->oZ;j<((*it)->oZ+(*it)->sZ);j++)
				{
					if(gc[i][j]->getStrongZoneIntensity()<=0.0f) continue;
					glColor4f(0.0f,0.0f,1.0f,gc[i][j]->getStrongZoneIntensity());
					glVertex3fv(&vertex[ ( (i+1)*gvh+j)  *3]);
					glVertex3fv(&vertex[ ( (i+1)*gvh+j+1)*3]);
					glVertex3fv(&vertex[ ( i*gvh+j+1)    *3]);
					glVertex3fv(&vertex[ ( i*gvh+j+1)    *3]);
					glVertex3fv(&vertex[ ( i*gvh+j)      *3]);
					glVertex3fv(&vertex[ ( (i+1)*gvh+j)  *3]);
				}
			
			}
		}
		glEnd();
		glDisable(GL_BLEND);
		glDepthFunc(GL_LESS);
	}
	//affichage de l'intensité des axes naturels
	if(this->showNaturelAxeIntensity) {
		glDepthFunc(GL_LEQUAL);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glBegin(GL_TRIANGLES);
		for(it=visibleLeaf.begin(); it!=visibleLeaf.end(); it++) {
			for(int i=(*it)->oX;i<((*it)->oX+(*it)->sX);i++)
			{
				for(int j=(*it)->oZ;j<((*it)->oZ+(*it)->sZ);j++)
				{
					if(gc[i][j]->getNaturalAxeIntensity()<=0.0f) continue;
					glColor4f(1.0f,1.0f,0.0f,gc[i][j]->getNaturalAxeIntensity());
					glVertex3fv(&vertex[ ( (i+1)*gvh+j)  *3]);
					glVertex3fv(&vertex[ ( (i+1)*gvh+j+1)*3]);
					glVertex3fv(&vertex[ ( i*gvh+j+1)    *3]);
					glVertex3fv(&vertex[ ( i*gvh+j+1)    *3]);
					glVertex3fv(&vertex[ ( i*gvh+j)      *3]);
					glVertex3fv(&vertex[ ( (i+1)*gvh+j)  *3]);
				}
			
			}
		}
		glEnd();
		glDisable(GL_BLEND);
		glDepthFunc(GL_LESS);
	}
	if(this->showStrongZoneCases!=-1) {
		if(ZoneManager::getInstance().getZoneStrong().find(this->showStrongZoneCases) != ZoneManager::getInstance().getZoneStrong().end()) {
			glColor3f(0.0f,0.0f,1.0f);
			this->renderGroundZone(ZoneManager::getInstance().getZoneStrong().find(this->showStrongZoneCases)->second);
		}
	}
	if(this->showNaturalAxeCases!=-1) {
		if(ZoneManager::getInstance().getAxeNatural().find(this->showNaturalAxeCases) != ZoneManager::getInstance().getAxeNatural().end()) {
			glColor3f(1.0f,1.0f,0.0f);
			this->renderGroundZone(ZoneManager::getInstance().getAxeNatural().find(this->showNaturalAxeCases)->second);
		}
	}
	if(this->showForestZoneCases!=-1) {
		if(ZoneManager::getInstance().getZoneForest().find(this->showForestZoneCases) != ZoneManager::getInstance().getZoneForest().end()) {
			glColor3f(0.0f,1.0f,0.0f);
			this->renderGroundZone(ZoneManager::getInstance().getZoneForest().find(this->showForestZoneCases)->second);
		}
	}

	if (this->showGraph){
		ground::GroundMap::getInstance().drawDebugGraph(CASE_SIDE_SIZE);
	}
}

void Renderer::rec_buildOpenGLDisplayList(QT_Node *qtnode)
{
	if(qtnode->isLeaf) {
		//le sol
		this->renderGroundForDL(qtnode);
		//l'eau
		if(this->containsGroundWithTexture(WATER,qtnode->oX,qtnode->oZ,qtnode->sX,qtnode->sZ)) {
			qtnode->waterDL = glGenLists(1);
			glNewList(qtnode->waterDL,GL_COMPILE);
			this->renderWaterForDL(qtnode->oX,qtnode->oZ,qtnode->sX,qtnode->sZ);
			glEndList();
		}
		//le tronc et le feuillage des arbres
		qtnode->tree_leafDL = glGenLists(1);
		qtnode->tree_trunkDL = glGenLists(1);
		this->renderQTLeafTreeDL(qtnode);
	}
	else
	{
		if(qtnode->sons_node[0]!=NULL) {
			rec_buildOpenGLDisplayList(qtnode->sons_node[0]);
		}
		if(qtnode->sons_node[1]!=NULL) {
			rec_buildOpenGLDisplayList(qtnode->sons_node[1]);
		}
		if(qtnode->sons_node[2]!=NULL) {
			rec_buildOpenGLDisplayList(qtnode->sons_node[2]);
		}
		if(qtnode->sons_node[3]!=NULL) {
			rec_buildOpenGLDisplayList(qtnode->sons_node[3]);
		}
	}
}

void Renderer::rec_deleteOpenGLDisplayList(QT_Node *qtnode)
{
	if(qtnode->isLeaf) {
		//l'eau
		glDeleteLists(qtnode->waterDL,1);
		qtnode->waterDL = 0;
		//les arbres
		glDeleteLists(qtnode->tree_leafDL,1);
		qtnode->tree_leafDL = 0;
		glDeleteLists(qtnode->tree_trunkDL,1);
		qtnode->tree_trunkDL = 0;

	}
	else
	{
		if(qtnode->sons_node[0]!=NULL) {
			rec_deleteOpenGLDisplayList(qtnode->sons_node[0]);
		}
		if(qtnode->sons_node[1]!=NULL) {
			rec_deleteOpenGLDisplayList(qtnode->sons_node[1]);
		}
		if(qtnode->sons_node[2]!=NULL) {
			rec_deleteOpenGLDisplayList(qtnode->sons_node[2]);
		}
		if(qtnode->sons_node[3]!=NULL) {
			rec_deleteOpenGLDisplayList(qtnode->sons_node[3]);
		}
	}
}

void Renderer::reloadTexture(char* tileSet) {
	char tmpc[128];

	//TextureManager::getInstance()->unloadAllTexture();
	if(texLuciole!=NULL && texLuciole->isInVideoMem()){
		this->texLuciole->unloadFromVideoMem();
	}
	if(texMeadow!=NULL && texMeadow->isInVideoMem()){
		this->texMeadow->unloadFromVideoMem();
	}
	if(texForest!=NULL && texForest->isInVideoMem()){
		this->texForest->unloadFromVideoMem();
	}
	if(texOreeForest!=NULL && texOreeForest->isInVideoMem()){
		this->texOreeForest->unloadFromVideoMem();
	}
	if(texRoad!=NULL && texRoad->isInVideoMem()){
		this->texRoad->unloadFromVideoMem();
	}
	if(texWater!=NULL && texWater->isInVideoMem()){
		this->texWater->unloadFromVideoMem();
	}
	if(texUnderWater!=NULL && texUnderWater->isInVideoMem()){
		this->texUnderWater->unloadFromVideoMem();
	}
	if(texTreeTrunk!=NULL && texTreeTrunk->isInVideoMem()){
		this->texTreeTrunk->unloadFromVideoMem();
	}
	if(texTreeLeaf!=NULL && texTreeLeaf->isInVideoMem()){
		this->texTreeLeaf->unloadFromVideoMem();
	}
	if(texFire!=NULL && texFire->isInVideoMem()){
		this->texFire->unloadFromVideoMem();
	}
	if(texFish!=NULL && texFish->isInVideoMem()){
		this->texFish->unloadFromVideoMem();
	}
	if(texSpring!=NULL && texSpring->isInVideoMem()){
		this->texSpring->unloadFromVideoMem();
	}
	if(texBlood!=NULL && texBlood->isInVideoMem()){
		this->texBlood->unloadFromVideoMem();
	}
	if(texSpark!=NULL && texSpark->isInVideoMem()){
		this->texSpark->unloadFromVideoMem();
	}
	if(texDust!=NULL && texDust->isInVideoMem()){
		this->texDust->unloadFromVideoMem();
	}
	
	//enregistrement auprès du manager de texture.
	sprintf(tmpc,"%s%s\0",tileSet,"luciole.bmp");
	if(!TextureManager::getInstance()->addTexture2D(tmpc,NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,false,0,0,0,0,0)){
		std::cout<<"impossible de charger la texture luciole!";
		std::cout<<tmpc;
		exit(0);
	}
	this->texLuciole = TextureManager::getInstance()->getTextureByName(tmpc);
	//
	sprintf(tmpc,"%s%s\0",tileSet,"fire.bmp");
	if(!TextureManager::getInstance()->addTexture2D(tmpc,NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,false,0,0,0,0,0)){
		std::cout<<"impossible de charger la texture fire!";
		std::cout<<tmpc;
		exit(0);
	}
	this->texFire = TextureManager::getInstance()->getTextureByName(tmpc);
	//
	sprintf(tmpc,"%s%s\0",tileSet,"fish.bmp");
	if(!TextureManager::getInstance()->addTexture2D(tmpc,NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,true,0,0,0,0,255)){
		std::cout<<"impossible de charger la texture fish!";
		std::cout<<tmpc;
		exit(0);
	}
	this->texFish = TextureManager::getInstance()->getTextureByName(tmpc);
	//
	sprintf(tmpc,"%s%s\0",tileSet,"spring.bmp");
	if(!TextureManager::getInstance()->addTexture2D(tmpc,NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,true,0,0,0,0,255)){
		std::cout<<"impossible de charger la texture spring!";
		std::cout<<tmpc;
		exit(0);
	}
	this->texSpring = TextureManager::getInstance()->getTextureByName(tmpc);
	//
	sprintf(tmpc,"%s%s\0",tileSet,"blood.bmp");
	if(!TextureManager::getInstance()->addTexture2D(tmpc,NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,true,255,0,0,0,255)){
		std::cout<<"impossible de charger la texture blood!";
		std::cout<<tmpc;
		exit(0);
	}
	this->texBlood = TextureManager::getInstance()->getTextureByName(tmpc);
	//
	sprintf(tmpc,"%s%s\0",tileSet,"spark.bmp");
	if(!TextureManager::getInstance()->addTexture2D(tmpc,NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,true,255,0,0,0,255)){
		std::cout<<"impossible de charger la texture spark!";
		std::cout<<tmpc;
		exit(0);
	}
	this->texSpark = TextureManager::getInstance()->getTextureByName(tmpc);
	//
	sprintf(tmpc,"%s%s\0",tileSet,"dust.bmp");
	if(!TextureManager::getInstance()->addTexture2D(tmpc,NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,true,255,0,0,0,255)){
		std::cout<<"impossible de charger la texture dust!";
		std::cout<<tmpc;
		exit(0);
	}
	this->texDust = TextureManager::getInstance()->getTextureByName(tmpc);
	//
	sprintf(tmpc,"%s%s\0",tileSet,"meadow.bmp");
	if(!TextureManager::getInstance()->addTexture2D(tmpc,NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,false,0,0,0,0,0)){
		std::cout<<"impossible de charger la texture meadow!";
		exit(0);
	}
	this->texMeadow = TextureManager::getInstance()->getTextureByName(tmpc);
	//
	sprintf(tmpc,"%s%s\0",tileSet,"forest.bmp");
	if(!TextureManager::getInstance()->addTexture2D(tmpc,NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,false,0,0,0,0,0)){
		std::cout<<"impossible de charger la texture forest!";
		exit(0);
	}
	this->texForest = TextureManager::getInstance()->getTextureByName(tmpc);
	//
	sprintf(tmpc,"%s%s\0",tileSet,"oree_forest.bmp");
	if(!TextureManager::getInstance()->addTexture2D(tmpc,NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,false,0,0,0,0,0)){
		std::cout<<"impossible de charger la texture oree_forest!";
		exit(0);
	}
	this->texOreeForest = TextureManager::getInstance()->getTextureByName(tmpc);
	//
	sprintf(tmpc,"%s%s\0",tileSet,"road.bmp");
	if(!TextureManager::getInstance()->addTexture2D(tmpc,NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,false,0,0,0,0,0)){
		std::cout<<"impossible de charger la texture road!";
		exit(0);
	}
	this->texRoad = TextureManager::getInstance()->getTextureByName(tmpc);
	//
	sprintf(tmpc,"%s%s\0",tileSet,"water.bmp");
	if(!TextureManager::getInstance()->addTexture2D(tmpc,NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,false,0,0,0,0,0)){
		std::cout<<"impossible de charger la texture water!";
		exit(0);
	}
	this->texWater = TextureManager::getInstance()->getTextureByName(tmpc);
	//
	sprintf(tmpc,"%s%s\0",tileSet,"under_water.bmp");
	if(!TextureManager::getInstance()->addTexture2D(tmpc,NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,false,0,0,0,0,0)){
		std::cout<<"impossible de charger la texture under_water!";
		exit(0);
	}
	this->texUnderWater = TextureManager::getInstance()->getTextureByName(tmpc);
	//
	sprintf(tmpc,"%s%s\0",tileSet,"tronc.bmp");
	if(!TextureManager::getInstance()->addTexture2D(tmpc,NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,false,0,0,0,0,0)){
		std::cout<<"impossible de charger la texture tronc!";
		exit(0);
	}
	this->texTreeTrunk = TextureManager::getInstance()->getTextureByName(tmpc);
	//
	sprintf(tmpc,"%s%s\0",tileSet,"feuilles.bmp");
	if(!TextureManager::getInstance()->addTexture2D(tmpc,NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,false,0,0,0,0,0)){
		std::cout<<"impossible de charger la texture feuilles!";
		exit(0);
	}
	this->texTreeLeaf = TextureManager::getInstance()->getTextureByName(tmpc);
	//

	//on recharge la texture de neige qui doit toujour être charger et on la réassocie au particleEngine de snow
	if(!TextureManager::getInstance()->addTexture2D("textures/tileset/snow/snow.bmp",NULL,0,GL_LINEAR,GL_LINEAR_MIPMAP_LINEAR,1,GL_REPEAT,GL_REPEAT,GL_REPEAT,false,0,0,0,0,0)){
		std::cout<<"impossible de charger la texture textures/tileset/snow/snow.bmp!";
		exit(0);
	} 
	this->texSnow=TextureManager::getInstance()->getTextureByName("textures/tileset/snow/snow.bmp");
	snow->setTextureId(texSnow->getId());

	//on réassocie les texture au modeles
	treeTrunkInstance->rtex = this->texTreeTrunk;
	treeLeafInstance->rtex = this->texTreeLeaf;

	//on redonne l'identifiant au particuleEngine
	for(std::vector<ParticleSystem::ParticleEngine*>::iterator it=lucioleEngine.begin();it!=lucioleEngine.end();it++){
		(*it)->setTextureId(texLuciole->getId());
	}
	for(std::vector<ParticleSystem::ParticleEngine*>::iterator it=fireEngine.begin();it!=fireEngine.end();it++){
		(*it)->setTextureId(texFire->getId());
	}
	for(std::vector<ParticleSystem::ParticleEngine*>::iterator it=fishEngine.begin();it!=fishEngine.end();it++){
		(*it)->setTextureId(texFish->getId());
	}
	for(std::vector<ParticleSystem::ParticleEngine*>::iterator it=springEngine.begin();it!=springEngine.end();it++){
		(*it)->setTextureId(texSpring->getId());
	}
	for(std::vector<ParticleSystem::ParticleEngine*>::iterator it=bloodEngine.begin();it!=bloodEngine.end();it++){
		(*it)->setTextureId(texBlood->getId());
	}
	for(std::vector<ParticleSystem::ParticleEngine*>::iterator it=sparkEngine.begin();it!=sparkEngine.end();it++){
		(*it)->setTextureId(texSpark->getId());
	}
	for(std::vector<ParticleSystem::ParticleEngine*>::iterator it=dustEngine.begin();it!=dustEngine.end();it++){
		(*it)->setTextureId(texDust->getId());
	}
}

void Renderer::changeTileSet(char* tileSet)
{
	this->rec_deleteOpenGLDisplayList(qtRoot);

	this->reloadTexture(tileSet);
	if(strcmp(tileSet,"textures/tileset/snow/")==0) {
		this->curEnvPE = snow;
	}
	else {
		this->curEnvPE = rain;
	}

	this->rec_buildOpenGLDisplayList(qtRoot);
}

float Renderer::getElevation(Point2D& pos) {
	Plane* p;
	float x = pos.getX();
	float y = pos.getY();
	if(x<0.0f || x>=(float)this->gw || y<0.0f || y>=(float)this->gh)
		return 0.0f;

	int i = (int)pos.getX();
	int j = (int)pos.getY();

	x = x-(float)i;
	y = y-(float)j;
	if(y>(1.0f-x)) {
		//plan du triangle haut-droit de la case
		p = &this->mapPlanes[i*gh+j].p1;
	}
	else {
		//plan du triangle bas_gauche de la case
		p = &this->mapPlanes[i*gh+j].p2;
	}
	//ax+by+cz+d=0 <=> y=(-d-ax-cz)/b
	return (-p->d - p->n[0]*(pos.getX()*CASE_SIDE_SIZE) - p->n[2]*(pos.getY()*CASE_SIDE_SIZE))/p->n[1];
}
float Renderer::getElevation(float _x, float _y) {
	Plane* p;
	float x = _x;
	float y = _y;
	if(x<0.0f || x>=(float)this->gw || y<0.0f || y>=(float)this->gh)
		return 0.0f;

	int i = (int)_x;
	int j = (int)_y;

	x = x-(float)i;
	y = y-(float)j;
	if(y>=(1.0f-x)) {
		//plan du triangle haut-droit de la case
		p = &this->mapPlanes[i*gh+j].p1;
	}
	else {
		//plan du triangle bas_gauche de la case
		p = &this->mapPlanes[i*gh+j].p2;
	}
	//ax+by+cz+d=0 <=> y=(-d-ax-cz)/b
	return (-p->d - p->n[0]*(_x*CASE_SIDE_SIZE) - p->n[2]*(_y*CASE_SIDE_SIZE))/p->n[1];
}

float Renderer::getElevationWorldCoord(Point2D& pos) {
	Plane* p;
	float x = pos.getX()/CASE_SIDE_SIZE;
	float y = pos.getY()/CASE_SIDE_SIZE;
	if(x<0.0f || x>=(float)this->gw || y<0.0f || y>=(float)this->gh)
		return 0.0f;

	int i = (int)x;
	int j = (int)y;

	x = x-(float)i;
	y = y-(float)j;
	if(y>(1.0f-x)) {
		//plan du triangle haut-droit de la case
		p = &this->mapPlanes[i*gh+j].p1;
	}
	else {
		//plan du triangle bas_gauche de la case
		p = &this->mapPlanes[i*gh+j].p2;
	}
	//ax+by+cz+d=0 <=> y=(-d-ax-cz)/b
	return (-p->d - p->n[0]*(pos.getX()) - p->n[2]*(pos.getY()))/p->n[1];
}
float Renderer::getElevationWorldCoord(float _x, float _y) {
	Plane* p;
	float x = _x/CASE_SIDE_SIZE;
	float y = _y/CASE_SIDE_SIZE;
	if(x<0.0f || x>=(float)this->gw || y<0.0f || y>=(float)this->gh)
		return 0.0f;

	int i = (int)x;
	int j = (int)y;

	x = x-(float)i;
	y = y-(float)j;
	if(y>=(1.0f-x)) {
		//plan du triangle haut-droit de la case
		p = &this->mapPlanes[i*gh+j].p1;
	}
	else {
		//plan du triangle bas_gauche de la case
		p = &this->mapPlanes[i*gh+j].p2;
	}
	//ax+by+cz+d=0 <=> y=(-d-ax-cz)/b
	return (-p->d - p->n[0]*_x - p->n[2]*_y)/p->n[1];
}


void Renderer::addRenderableEntity(RenderableEntity& entity)
{
	float eX;
	float eY;
	float x;
	float z;
	float fx;
	float fz;
	float minx;
	float minz;
	float maxx;
	float maxz;
	float elev;
	float rotMatrix[2][2];

	//on ajoute l'entite auprès des model du renderer
	renderableEntities[&entity] = &entity;

	//on initialise la AA-bounding-box
	ModelInstance* mInstance = &entity.modelInstance;
	eX = entity.getRenderableEntityPosition().getX();
	eY = entity.getRenderableEntityPosition().getY();

	//calcul de la AABB global du model
	rotMatrix[0][0] = cos(entity.getRenderableEntityAngle()*DEG2RAD);
	rotMatrix[0][1] = sin(entity.getRenderableEntityAngle()*DEG2RAD);
	rotMatrix[1][0] = -rotMatrix[0][1];
	rotMatrix[1][1] = rotMatrix[0][0];
	minx=9999.0f;
	minz=9999.0f;
	maxx=-9999.0f;
	maxz=-9999.0f;
	for(float i=mInstance->animState.aabb.minx; i<=mInstance->animState.aabb.maxx; i+=(mInstance->animState.aabb.maxx-mInstance->animState.aabb.minx)) {
		x = i;
		for(float j=mInstance->animState.aabb.minz; j<=mInstance->animState.aabb.maxz; j+=(mInstance->animState.aabb.maxz-mInstance->animState.aabb.minz)) {
			z = j;

			fx = x*rotMatrix[0][0] + z*rotMatrix[0][1];
			fz = x*rotMatrix[1][0] + z*rotMatrix[1][1];

			if(fx<minx) minx = fx;
			if(fz<minz) minz = fz;
			if(fx>maxx) maxx = fx;
			if(fz>maxz) maxz = fz;
		}
	}
	elev = this->getElevation(entity.getRenderableEntityPosition().getX(),entity.getRenderableEntityPosition().getY());
	entity.aabb.minx = minx + eX*CASE_SIDE_SIZE;
	entity.aabb.miny = mInstance->animState.aabb.miny + elev;
	entity.aabb.minz = minz + eY*CASE_SIDE_SIZE;
	entity.aabb.maxx = maxx + eX*CASE_SIDE_SIZE;
	entity.aabb.maxy = mInstance->animState.aabb.maxy + elev;
	entity.aabb.maxz = maxz + eY*CASE_SIDE_SIZE;
	entity.aabb.computeLimitInverse();
}

void Renderer::removeRenderableEntity(RenderableEntity& entity)
{
	if(renderableEntities.find(&entity)!=renderableEntities.end()) {
		renderableEntities.erase(&entity);
	}
}
void Renderer::addDecor(RenderableEntity& entity)
{
	float eX;
	float eY;
	float x;
	float z;
	float fx;
	float fz;
	float minx;
	float minz;
	float maxx;
	float maxz;
	float elev;
	float rotMatrix[2][2];

	//on ajoute l'entite auprès des model du renderer
	decors[&entity] = &entity;

	//on initialise la AA-bounding-box
	ModelInstance* mInstance = &entity.modelInstance;
	eX = entity.getRenderableEntityPosition().getX();
	eY = entity.getRenderableEntityPosition().getY();

	//calcul de la AABB global du model
	rotMatrix[0][0] = cos(entity.getRenderableEntityAngle()*DEG2RAD);
	rotMatrix[0][1] = sin(entity.getRenderableEntityAngle()*DEG2RAD);
	rotMatrix[1][0] = -rotMatrix[0][1];
	rotMatrix[1][1] = rotMatrix[0][0];
	minx=9999.0f;
	minz=9999.0f;
	maxx=-9999.0f;
	maxz=-9999.0f;
	for(float i=mInstance->animState.aabb.minx; i<=mInstance->animState.aabb.maxx; i+=(mInstance->animState.aabb.maxx-mInstance->animState.aabb.minx)) {
		x = i;
		for(float j=mInstance->animState.aabb.minz; j<=mInstance->animState.aabb.maxz; j+=(mInstance->animState.aabb.maxz-mInstance->animState.aabb.minz)) {
			z = j;

			fx = x*rotMatrix[0][0] + z*rotMatrix[0][1];
			fz = x*rotMatrix[1][0] + z*rotMatrix[1][1];

			if(fx<minx) minx = fx;
			if(fz<minz) minz = fz;
			if(fx>maxx) maxx = fx;
			if(fz>maxz) maxz = fz;
		}
	}
	elev = this->getElevation(entity.getRenderableEntityPosition().getX(),entity.getRenderableEntityPosition().getY());
	entity.aabb.minx = minx + eX*CASE_SIDE_SIZE;
	entity.aabb.miny = mInstance->animState.aabb.miny + elev;
	entity.aabb.minz = minz + eY*CASE_SIDE_SIZE;
	entity.aabb.maxx = maxx + eX*CASE_SIDE_SIZE;
	entity.aabb.maxy = mInstance->animState.aabb.maxy + elev;
	entity.aabb.maxz = maxz + eY*CASE_SIDE_SIZE;
	entity.aabb.computeLimitInverse();
}


void Renderer::removeDecor(RenderableEntity& entity)
{
	if(renderableEntities.find(&entity)!=renderableEntities.end()) {
		renderableEntities.erase(&entity);
	}
}

void Renderer::setupModelInstance(ModelInstance& modelInstance,const std::string& type,const std::string& camp){
	map<string,Model*>::iterator it =  modelsMap.find(type);
	if (it == modelsMap.end()){
		printf("On a pas trouver l'entite %s.\n",type.c_str());
		return;
	}
	Model* model = it->second;
	model->initInstance(&modelInstance);
	if (camp == "Bleu"){
		if (type == "Infantry"){
			modelInstance.rtex = TextureManager::getInstance()->getTextureByName("./textures/common/fantassinBleu.bmp");
		}else if (type == "Lancer"){
			modelInstance.rtex = TextureManager::getInstance()->getTextureByName("./textures/common/piquierBleu.bmp");
		}else if (type == "Archer"){
			modelInstance.rtex = TextureManager::getInstance()->getTextureByName("./textures/common/archerBleu.bmp");
		}else if (type == "Horse"){
			modelInstance.rtex = TextureManager::getInstance()->getTextureByName("./textures/common/cheval.bmp");
		}
	}else if (camp == "Rouge"){
		if (type == "Infantry"){
			modelInstance.rtex = TextureManager::getInstance()->getTextureByName("./textures/common/fantassinRouge.bmp");
		}else if (type == "Lancer"){
			modelInstance.rtex = TextureManager::getInstance()->getTextureByName("./textures/common/piquierRouge.bmp");
		}else if (type == "Archer"){
			modelInstance.rtex = TextureManager::getInstance()->getTextureByName("./textures/common/archerRouge.bmp");
		}else if (type == "Horse"){
			modelInstance.rtex = TextureManager::getInstance()->getTextureByName("./textures/common/cheval.bmp");
		}
	}else{
		if (type == "Horse"){
			modelInstance.rtex = TextureManager::getInstance()->getTextureByName("./textures/common/cheval.bmp");
		}
	}
}


void Renderer::renderGround()
{
		glEnableClientState(GL_VERTEX_ARRAY);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glEnableClientState(GL_COLOR_ARRAY);
		glVertexPointer( 3, GL_FLOAT, 0, this->vertex );//activation tab de vertice en mémoire cliente
		glTexCoordPointer( 2, GL_FLOAT, 0, this->texCoord );//activation tab de texcoord en mémoire cliente
		glColorPointer( 3, GL_FLOAT, 0, this->color );

		if(this->showWireGround)
		{
			glDisable(GL_LIGHTING);
			glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
			glColor3f(0.0f,0.0f,1.0f);
			glBegin(GL_QUADS);
			for(int i=0;i<gvw-1;i++)
			{
				for(int j=0;j<gvh-1;j++)
				{
					glArrayElement((i)*gvh+(j));
					glArrayElement((i+1)*gvh+(j));
					glArrayElement((i+1)*gvh+(j+1));
					glArrayElement((i)*gvh+(j+1));
				}
			}
			glEnd();
			glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
			glColor3f(0.0f,1.0f,0.0f);
			glBegin(GL_LINES);
			for(int i=0;i<gvw;i++)
			{
				for(int j=0;j<gvh;j++)
				{
					glVertex3f(
						this->vertex[(i*gvh+j)*3  ],
						this->vertex[(i*gvh+j)*3+1],
						this->vertex[(i*gvh+j)*3+2]
						);
					glVertex3f(
						this->vertex[(i*gvh+j)*3  ] + 3.0f*this->normal[(i*gvh+j)*3  ],
						this->vertex[(i*gvh+j)*3+1] + 3.0f*this->normal[(i*gvh+j)*3+1],
						this->vertex[(i*gvh+j)*3+2] + 3.0f*this->normal[(i*gvh+j)*3+2]
						);
				}
			}
			glEnd();
			if(this->useGLlighting)
				glEnable(GL_LIGHTING);
		}

	glColor3f(1.0f,1.0f,1.0f);
	std::vector<QT_Node*>::iterator it;
	if(useGroundTexture)
	{
		glEnable(GL_TEXTURE_2D);
		glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
	}
	if(this->useGLlighting) {
		glEnableClientState(GL_NORMAL_ARRAY);
		glNormalPointer( GL_FLOAT, 0, this->normal );
		glEnable(GL_COLOR_MATERIAL);
		glColorMaterial(GL_FRONT, GL_DIFFUSE);
	}

	this->texMeadow->bindTexture();
	for(it=visibleLeaf.begin(); it!=visibleLeaf.end(); it++)
	{
		if((*it)->nbGroundMeadowElement==0) continue;
		//affichage
		glDrawElements(GL_TRIANGLES,(*it)->nbGroundMeadowElement,GL_UNSIGNED_SHORT,(*it)->groundMeadowElement);
	}
	this->texForest->bindTexture();
	for(it=visibleLeaf.begin(); it!=visibleLeaf.end(); it++)
	{
		if((*it)->nbGroundForestElement==0) continue;
		//affichage
		glDrawElements(GL_TRIANGLES,(*it)->nbGroundForestElement,GL_UNSIGNED_SHORT,(*it)->groundForestElement);
	}
	this->texOreeForest->bindTexture();
	for(it=visibleLeaf.begin(); it!=visibleLeaf.end(); it++)
	{
		if((*it)->nbGroundOreeElement==0) continue;
		//affichage
		glDrawElements(GL_TRIANGLES,(*it)->nbGroundOreeElement,GL_UNSIGNED_SHORT,(*it)->groundOreeElement);
	}
	this->texRoad->bindTexture();
	for(it=visibleLeaf.begin(); it!=visibleLeaf.end(); it++)
	{
		if((*it)->nbGroundRoadElement==0) continue;
		//affichage
		glDrawElements(GL_TRIANGLES,(*it)->nbGroundRoadElement,GL_UNSIGNED_SHORT,(*it)->groundRoadElement);
	}
	this->texUnderWater->bindTexture();
	for(it=visibleLeaf.begin(); it!=visibleLeaf.end(); it++)
	{
		if((*it)->nbGroundWaterElement==0) continue;
		//affichage
		glDrawElements(GL_TRIANGLES,(*it)->nbGroundWaterElement,GL_UNSIGNED_SHORT,(*it)->groundWaterElement);
	}

	glDisable(GL_COLOR_MATERIAL);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);
	glDisableClientState(GL_COLOR_ARRAY);
	glDisable(GL_TEXTURE_2D);
}

void Renderer::renderWater()
{
	glColor3f(1.0f,1.0f,1.0f);
	std::vector<QT_Node*>::iterator it;
	if(this->renderWaterZone) {
		glEnable(GL_BLEND);
		glBlendFunc(GL_ONE,GL_ONE);
		texWater->glEnableTexture();
		texWater->bindTexture();
		glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
		glColor3fv(this->waterColor);
		glMatrixMode(GL_TEXTURE);
		glPushMatrix();
		glTranslatef(0.07f*cos(trWater), 0.07f*sin(trWater), 0.0f );
		glMatrixMode(GL_MODELVIEW);
		glDepthFunc(GL_LEQUAL);
		for(it=visibleLeaf.begin(); it!=visibleLeaf.end(); it++) {
			if((*it)->waterDL==0) continue;
			glCallList((*it)->waterDL);
		}
		glMatrixMode(GL_TEXTURE);
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
		glDepthFunc(GL_LESS);
		texWater->glDisableTexture();
		glDisable(GL_BLEND);
	}
}

void Renderer::renderTree()
{
	glColor3f(1.0f,1.0f,1.0f);
	std::vector<QT_Node*>::iterator it;
	glEnable(GL_TEXTURE_2D);	//on active les texture pour les arbres au cas ou on aurai afficher le terrain sans textures
	if(this->renderTreeLeaf) {
		this->treeLeafInstance->rtex->bindTexture();
		for(it=visibleLeaf.begin(); it!=visibleLeaf.end(); it++)
		{
			if((*it)->tree_leafDL==0) continue;
			//affichage des feuilles des arbres
			glCallList((*it)->tree_leafDL);
		}
	}
	if(this->renderTreeTrunk) {
		this->treeTrunkInstance->rtex->bindTexture();
		for(it=visibleLeaf.begin(); it!=visibleLeaf.end(); it++)
		{
			if((*it)->tree_trunkDL==0) continue;
			//affichage du tronc des arbres
			glCallList((*it)->tree_trunkDL);
		}
	}
	glDisable(GL_TEXTURE_2D);
}

void Renderer::renderModel()
{
	std::multimap<float,RenderableEntity*>::iterator itm;

	bool** visibilityMatrix = NULL;
	if(currentArmy!=NULL)	//dans le cas ou l'on suit les deux armé
		visibilityMatrix = currentArmy->getGroundArmyInfo().getVisibilityMatrix();

	//on creer un multimap pour ordonnée les model suivant leur distance par rapport à la caméra
	//Pour afficher les modèles à travers les arbres sans superposition
	std::map<RenderableEntity*,RenderableEntity*>::iterator itRe;
	std::multimap<float,RenderableEntity*> sortedModele;
	float dist;
	float x,y;
	sortedModele.clear();
	for( itRe=renderableEntities.begin() ; itRe!=renderableEntities.end() ; itRe++) {
			x = (this->pos[0]-itRe->second->getRenderableEntityPosition().getX()+0.5f)*CASE_SIDE_SIZE;
			y = (this->pos[2]-itRe->second->getRenderableEntityPosition().getY()+0.5f)*CASE_SIDE_SIZE;
			dist = x*x + y*y;
			dist = -dist;
			sortedModele.insert(std::make_pair<float,RenderableEntity*>(dist,itRe->second));
	}

	//on test la visibilité et on update la géometrie des modeles visibles
	for( itm=sortedModele.begin() ; itm!=sortedModele.end() ; itm++) {
		float pos[3] = {itm->second->aabb.pos.x + (0.5f*CASE_SIDE_SIZE),
						itm->second->aabb.pos.y,
						itm->second->aabb.pos.z + (0.5f*CASE_SIDE_SIZE)
						};

		//test la visibilité avec le fogOfWar
		if(currentArmy!=NULL)	//dans le cas ou l'on suit les deux armé
			itm->second->setFogOfWarVisibility(visibilityMatrix[(int)(itm->second->getRenderableEntityPosition().getX()+0.5f)][(int)(itm->second->getRenderableEntityPosition().getY()+0.5f)]);
		else
			itm->second->setFogOfWarVisibility(true);	//l'unité est visible

		//test la visibilité avec le frustum
		if(	f.paveInFrustum(pos,itm->second->aabb.sizex,itm->second->aabb.sizey,itm->second->aabb.sizez)) 
		{
			itm->second->isVisible = true;
			if(!itm->second->modelInstance.isGeometricalyStatic){
				itm->second->modelInstance.updateInstance_geometry();
			}
		}
		else {
			itm->second->isVisible = false;
		}
	}

	//On affiche les unité avec un zTest GL_GREATER de la couleur de l'armé avec écriture dans le zBuffer désactivé
	// TODO : pas les unité neutre ou autre?
	glEnableClientState(GL_VERTEX_ARRAY);
	if(this->renderHiddenUnits) {
		glDepthFunc(GL_GREATER);
		glDepthMask(GL_FALSE);
		glDisable(GL_LIGHTING);
		for( itm=sortedModele.begin() ; itm!=sortedModele.end() ; itm++) {
			if(itm->second->isVisible && itm->second->isFogOfWarVisible() && itm->second->isAlive()) {
					x = (itm->second->getRenderableEntityPosition().getX()+0.5f)*CASE_SIDE_SIZE;
					y = (itm->second->getRenderableEntityPosition().getY()+0.5f)*CASE_SIDE_SIZE;
					glColor3fv(itm->second->armyColor);
					glVertexPointer( 3, GL_FLOAT, 0, itm->second->modelInstance.vertex );//activation tab de vertice en mémoire cliente
					glPushMatrix();
					glTranslatef((itm->second->getRenderableEntityPosition().getX()+0.5f)*CASE_SIDE_SIZE
								,this->getElevationWorldCoord(x,y)
								,(itm->second->getRenderableEntityPosition().getY()+0.5f)*CASE_SIDE_SIZE);
					glRotatef(itm->second->getRenderableEntityAngle(),0.0f,1.0f,0.0f);
					glDrawElements(GL_TRIANGLES,itm->second->modelInstance.model->nbTris*3,GL_UNSIGNED_SHORT,itm->second->modelInstance.model->triangles);
					glPopMatrix();
			}
		}
		if(this->useGLlighting) {
			glEnable(GL_LIGHTING);
		}
		else {
			//au cas ou on désactive GL_LIGHTING, on aura la texture de base
			glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);
		}
		glDepthFunc(GL_LESS);
		glDepthMask(GL_TRUE);
	}

	//On affiche les unité avec leur texture en GL_LEQUAL
	glEnable(GL_TEXTURE_2D);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glEnableClientState(GL_NORMAL_ARRAY);

	unsigned int previousTextureId = 0;

	for( itm=sortedModele.begin() ; itm!=sortedModele.end() ; itm++) {
		if((itm->second->isVisible && itm->second->isFogOfWarVisible()) ||  !itm->second->isAlive()) {
			if (itm->second->modelInstance.rtex->getId() != previousTextureId){
				previousTextureId = itm->second->modelInstance.rtex->getId();
				itm->second->modelInstance.rtex->bindTexture();
			}
			x = (itm->second->getRenderableEntityPosition().getX()+0.5f)*CASE_SIDE_SIZE;
			y = (itm->second->getRenderableEntityPosition().getY()+0.5f)*CASE_SIDE_SIZE;
			glPushMatrix();
			glTranslatef((itm->second->getRenderableEntityPosition().getX()+0.5f)*CASE_SIDE_SIZE
						,this->getElevationWorldCoord(x,y)
						,(itm->second->getRenderableEntityPosition().getY()+0.5f)*CASE_SIDE_SIZE);
			glRotatef(itm->second->getRenderableEntityAngle(),0.0f,1.0f,0.0f);
			this->renderModel(&itm->second->modelInstance);
			glPopMatrix();
		}
	}
	glDisable(GL_TEXTURE_2D);
	if(this->showModeleWorldAABB) {
		glPushMatrix();
		glTranslatef(CASE_SIDE_SIZE/2.0f,0,CASE_SIDE_SIZE/2.0f);
		glDisable(GL_LIGHTING);
		glDisable(GL_CULL_FACE);
		glDisableClientState(GL_NORMAL_ARRAY);
		glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
		glLineWidth(2.0f);

		for( itm=sortedModele.begin() ; itm!=sortedModele.end() ; itm++) {
			if((itm->second->isVisible && itm->second->isFogOfWarVisible()) ||  !itm->second->isAlive()) {
				glColor3f(0.0f,0.0f,0.0f);	//global en noir
				itm->second->aabb.render();
			}
		}

		if(this->useGLlighting)
			glEnable(GL_LIGHTING);
		glEnable(GL_CULL_FACE);
		glEnableClientState(GL_NORMAL_ARRAY);
		glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
		glLineWidth(1.0f);
		glPopMatrix();
	}
	if(this->showModeleLocalAABB) {
		glDisable(GL_LIGHTING);
		glDisable(GL_CULL_FACE);
		glDisableClientState(GL_NORMAL_ARRAY);
		glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
		glLineWidth(2.0f);

		for( itm=sortedModele.begin() ; itm!=sortedModele.end() ; itm++) {
			if((itm->second->isVisible && itm->second->isFogOfWarVisible()) ||  !itm->second->isAlive()) {
				glColor3f(0.5f,0.5f,0.5f);	//local en gris
				Point2D p = itm->second->getRenderableEntityPosition();
				p.setCoord(p.getX()+0.5f,p.getY()+0.5f);
				glPushMatrix();
				glTranslatef(p.getX()*CASE_SIDE_SIZE,this->getElevation(p),p.getY()*CASE_SIDE_SIZE);
				glRotatef(itm->second->getRenderableEntityAngle(),0.0f,1.0f,0.0f);
				itm->second->modelInstance.animState.aabb.render();
				glPopMatrix();
			}
		}

		if(this->useGLlighting)
			glEnable(GL_LIGHTING);
		glEnable(GL_CULL_FACE);
		glEnableClientState(GL_NORMAL_ARRAY);
		glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
		glLineWidth(1.0f);
	}

	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
}

void Renderer::renderDecor()
{
	if (!renderDecors){
		return;
	}
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glEnableClientState(GL_NORMAL_ARRAY);
	glEnable(GL_TEXTURE_2D);
	unsigned int previousTextureId = 0;
	//on test la visibilité et on update la géometrie des modeles visibles
	for( map<RenderableEntity*,RenderableEntity*>::iterator it =decors.begin() ; it!=decors.end() ; it++) {
		float pos[3] = {it->second->aabb.pos.x + (0.5f*CASE_SIDE_SIZE),
						it->second->aabb.pos.y,
						it->second->aabb.pos.z + (0.5f*CASE_SIDE_SIZE)
						};
		if(f.paveInFrustum(pos,it->second->aabb.sizex,it->second->aabb.sizey,it->second->aabb.sizez)) {
			it->second->isVisible = true;
			if(!it->second->modelInstance.isGeometricalyStatic){
				it->second->modelInstance.updateInstance_geometry();
			}
			if (it->second->modelInstance.rtex->getId() != previousTextureId){
				previousTextureId = it->second->modelInstance.rtex->getId();
				it->second->modelInstance.rtex->bindTexture();
			}
			int x = (it->second->getRenderableEntityPosition().getX()+0.5f)*CASE_SIDE_SIZE;
			int y = (it->second->getRenderableEntityPosition().getY()+0.5f)*CASE_SIDE_SIZE;
			glPushMatrix();
			glTranslatef((it->second->getRenderableEntityPosition().getX()+0.5f)*CASE_SIDE_SIZE
						,this->getElevationWorldCoord(x,y)
						,(it->second->getRenderableEntityPosition().getY()+0.5f)*CASE_SIDE_SIZE);
			glRotatef(it->second->getRenderableEntityAngle(),0.0f,1.0f,0.0f);
			this->renderModel(&it->second->modelInstance);
			glPopMatrix();
		}else{
			it->second->isVisible = false;
		}
	}

	glDisable(GL_TEXTURE_2D);
	if(this->showModeleWorldAABB) {
		glPushMatrix();
		glTranslatef(CASE_SIDE_SIZE/2.0f,0,CASE_SIDE_SIZE/2.0f);
		glDisable(GL_LIGHTING);
		glDisable(GL_CULL_FACE);
		glDisableClientState(GL_NORMAL_ARRAY);
		glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
		glLineWidth(2.0f);

		for( map<RenderableEntity*,RenderableEntity*>::iterator it=decors.begin() ; it!=decors.end() ; it++) {
			if(it->second->isVisible) {
				glColor3f(0.0f,0.0f,0.0f);	//global en noir
				it->second->aabb.render();
			}
		}

		if(this->useGLlighting){
			glEnable(GL_LIGHTING);
		}
		glEnable(GL_CULL_FACE);
		glEnableClientState(GL_NORMAL_ARRAY);
		glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
		glLineWidth(1.0f);
		glPopMatrix();
	}
	if(this->showModeleLocalAABB) {
		glDisable(GL_LIGHTING);
		glDisable(GL_CULL_FACE);
		glDisableClientState(GL_NORMAL_ARRAY);
		glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
		glLineWidth(2.0f);

		for( map<RenderableEntity*,RenderableEntity*>::iterator it=decors.begin() ; it!=decors.end() ; it++) {
			if(it->second->isVisible) {
				glColor3f(0.5f,0.5f,0.5f);	//local en gris
				Point2D p = it->second->getRenderableEntityPosition();
				p.setCoord(p.getX()+0.5f,p.getY()+0.5f);
				glPushMatrix();
				glTranslatef(p.getX()*CASE_SIDE_SIZE,this->getElevation(p),p.getY()*CASE_SIDE_SIZE);
				glRotatef(it->second->getRenderableEntityAngle(),0.0f,1.0f,0.0f);
				it->second->modelInstance.animState.aabb.render();
				glPopMatrix();
			}
		}

		if(this->useGLlighting)
			glEnable(GL_LIGHTING);
		glEnable(GL_CULL_FACE);
		glEnableClientState(GL_NORMAL_ARRAY);
		glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
		glLineWidth(1.0f);
	}

	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}


void Renderer::renderParticle()
{
	if (renderParticles){
		glEnable(GL_TEXTURE_2D);
		ParticleEngine::setCameraPos(pos[0],pos[1],pos[2]);
		// on nettoie l'afficheur de particules
		particleDrawer.clear();
		particleDrawer.addManager(&ParticleManager::getGlobal());
		particleDrawer.addEngine(curEnvPE);
		BoundingBox aabb;	//pour le clipping des particules
		for (particleDrawer.startIteration(); !particleDrawer.iterationFinished(); particleDrawer.next())
		{
			aabb.maxx = particleDrawer.current().getBoundingBox()[0];
			aabb.maxy = particleDrawer.current().getBoundingBox()[1];
			aabb.maxz = particleDrawer.current().getBoundingBox()[2];
			aabb.minx = particleDrawer.current().getBoundingBox()[3];
			aabb.miny = particleDrawer.current().getBoundingBox()[4];
			aabb.minz = particleDrawer.current().getBoundingBox()[5];
			aabb.computeLimitInverse();
			if(f.paveInFrustum(&aabb.pos.x,aabb.sizex,aabb.sizey,aabb.sizez)) {	//si on voit le moteur de particule, on l'affiche
				particleDrawer.current().draw();
			}
		}
		glDisable(GL_TEXTURE_2D);
		if(this->showParticleAABB) {
			glColor3f(0.0f,0.0f,0.0f);
			glLineWidth(2.0f);
			for (particleDrawer.startIteration(); !particleDrawer.iterationFinished(); particleDrawer.next()){
				aabb.maxx = particleDrawer.current().getBoundingBox()[0];
				aabb.maxy = particleDrawer.current().getBoundingBox()[1];
				aabb.maxz = particleDrawer.current().getBoundingBox()[2];
				aabb.minx = particleDrawer.current().getBoundingBox()[3];
				aabb.miny = particleDrawer.current().getBoundingBox()[4];
				aabb.minz = particleDrawer.current().getBoundingBox()[5];
				aabb.computeLimitInverse();
				if(f.paveInFrustum(&aabb.pos.x,aabb.sizex,aabb.sizey,aabb.sizez)) {
					particleDrawer.current().drawBoundingBox();
				}
			}
			glLineWidth(1.0f);
		}
	}
}

void Renderer::setVisibilityUpdateTime(float time_ms)
{
	this->visibilityUpdateTime = time_ms;

	if(this->visibilityUpdateTime>2000.0f)	this->visibilityUpdateTime=2000.0f;
	else if(this->visibilityUpdateTime<0.0f)		this->visibilityUpdateTime=0.0f;
}

float Renderer::getVisibilityUpdateTime()
{
	return this->visibilityUpdateTime;
}

void Renderer::setFogOfWarAttributes(float fogIntensity,float visibleIntensity){
	this->groundSeenColorIntensity = visibleIntensity;
	this->groundWarFogColorIntensity = fogIntensity;
}

void Renderer::drawPath(std::vector<Point2D>& path){
	glDisable(GL_TEXTURE_2D);
	glColor3f(1.0f,1.0f,1.0f);
	for (vector<Point2D>::iterator it = path.begin(); it != path.end(); ++it){
		glPushMatrix();
		Point2D p;
		p.setCoord((*it).getX() + 0.5f,(*it).getY() +0.5f);
		glTranslatef(p.getX()*CASE_SIDE_SIZE,getElevation(p),p.getY()*CASE_SIDE_SIZE);
		glutSolidSphere(1,6,6);
		glPopMatrix();
	}
}

void Renderer::addFireParticle(ParticleSystem::ParticleEngine* fire,ParticleSystem::ParticleEngine* smoke){
	this->fireEngine.push_back(fire);
	this->fireEngine.push_back(smoke);
	fire->setTextureId(texFire->getId());
	smoke->setTextureId(texFire->getId());
}

void Renderer::addSpringParticle(ParticleSystem::ParticleEngine* spring){
	this->springEngine.push_back(spring);
	spring->setTextureId(texSpring->getId());
}

void Renderer::addBloodParticle(Point2D& pos){
	ParticleEngine * particleEngine = new ParticleEngine();
	bloodEngine.push_back(particleEngine);
	// ici aussi la destruction du generator est prise en charge par le moteur de particule
	BloodGenerator* bloodGenerator = new BloodGenerator(0.0f,10.0f,0.0f);
	bloodGenerator->pos[0] = CASE_SIDE_SIZE*(pos.getX()+0.5);
	bloodGenerator->pos[1] = this->getElevation(pos)+3;
	bloodGenerator->pos[2] = CASE_SIDE_SIZE*(pos.getY()+0.5);
	particleEngine->setGenerator(bloodGenerator);
	particleEngine->setSortType(NO_SORT);
	particleEngine->setTextureId(texBlood->getId());
	particleEngine->createParticles(50);
	particleEngine->setBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	// ici il ne faut pas oublier de demarrer le moteur de particule, sinon au premier appel de update, le 
	// manager va trouver le moteur arreter et va le supprimer.
	particleEngine->start(false);
	ParticleManager::getGlobal().addEngine(particleEngine);
}

void Renderer::addSparkParticle(Point2D& pos,bool fatal,float radius){
	ParticleEngine * particleEngine = new ParticleEngine();
	sparkEngine.push_back(particleEngine);
	// ici aussi la destruction du generator est prise en charge par le moteur de particule
	SparkGenerator* sparkGenerator = new SparkGenerator(fatal,radius);
	particleEngine->setGenerator(sparkGenerator);
	particleEngine->setSortType(NO_SORT);
	particleEngine->setTextureId(texSpark->getId());
	if (fatal){
		particleEngine->createParticles(600);
		sparkGenerator->pos[0] = CASE_SIDE_SIZE*(pos.getX()+0.5);
		sparkGenerator->pos[1] = this->getElevation(pos);
		sparkGenerator->pos[2] = CASE_SIDE_SIZE*(pos.getY()+0.5);
	}else{
		particleEngine->createParticles(100);
		sparkGenerator->pos[0] = CASE_SIDE_SIZE*(pos.getX()+0.5);
		sparkGenerator->pos[1] = this->getElevation(pos)+3;
		sparkGenerator->pos[2] = CASE_SIDE_SIZE*(pos.getY()+0.5);
	}
	particleEngine->setBlendFunc(GL_SRC_ALPHA,GL_ONE);
	// ici il ne faut pas oublier de demarrer le moteur de particule, sinon au premier appel de update, le 
	// manager va trouver le moteur arreter et va le supprimer.
	particleEngine->start(false);
	ParticleManager::getGlobal().addEngine(particleEngine);
}

void Renderer::addDustParticle(DustGenerator* generator){
	ParticleEngine * particleEngine = new ParticleEngine();
	dustEngine.push_back(particleEngine);
	// ici aussi la destruction du generator est prise en charge par le moteur de particule
	particleEngine->setGenerator(generator);
	particleEngine->getPhysique()->setPhysique(0,0,0);
	particleEngine->setSortType(SORTED);
	particleEngine->setTextureId(texDust->getId());
	ParticleSystem::DustParticle * particles = new ParticleSystem::DustParticle[100];
	particleEngine->setParticles(particles,100,sizeof(ParticleSystem::DustParticle));
	particleEngine->setBlendFunc(GL_SRC_COLOR,GL_ONE_MINUS_SRC_COLOR);
	// ici il ne faut pas oublier de demarrer le moteur de particule, sinon au premier appel de update, le 
	// manager va trouver le moteur arreter et va le supprimer.
	particleEngine->start();
	ParticleManager::getGlobal().addEngine(particleEngine);
}

void Renderer::setGLFont(glFont2D& glf){
	this->glfont = glf;
}

void Renderer::renderEntitiesLife()
{
	std::multimap<float,RenderableEntity*>::iterator itm;

	//TODO en faire une calculer une fois puis réutilisé
	//on creer un multimap pour ordonnée les model suivant leur distance par rapport à la caméra
	//Pour afficher les modèles à travers les arbres sans superposition
	std::map<RenderableEntity*,RenderableEntity*>::iterator itRe;
	std::multimap<float,RenderableEntity*> sortedModele;
	float dist;
	float x,y;
	sortedModele.clear();
	for( itRe=renderableEntities.begin() ; itRe!=renderableEntities.end() ; itRe++) {
			x = (this->pos[0]-itRe->second->getRenderableEntityPosition().getX()+0.5f)*CASE_SIDE_SIZE;
			y = (this->pos[2]-itRe->second->getRenderableEntityPosition().getY()+0.5f)*CASE_SIDE_SIZE;
			dist = x*x + y*y;
			dist = -dist;
			sortedModele.insert(std::make_pair<float,RenderableEntity*>(dist,itRe->second));
	}
	////////////////////////////////////////////////////////////////////////////////////////////////

	//glDepthFunc(GL_ALWAYS);
	glDepthMask(GL_FALSE);

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D,glfont.getIdTexFont());
	glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);

	glColor4f(1.0f,1.0f,1.0f,1.0f);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_ALPHA_TEST);
	glAlphaFunc( GL_GREATER, 0);

	glfont.setHeight(1);
	glfont.setWidth(1);

	glPushMatrix();
	glLoadIdentity();
	for( itm=sortedModele.begin() ; itm!=sortedModele.end() ; itm++) {
		if(itm->second->isVisible && itm->second->isFogOfWarVisible() && itm->second->haveLife()) {

			float x = (itm->second->getRenderableEntityPosition().getX()+0.5f)*CASE_SIDE_SIZE - this->pos[0];
			float y = this->getElevation( itm->second->getRenderableEntityPosition().getX()+0.5f , itm->second->getRenderableEntityPosition().getY()+0.5f ) - this->pos[1];
			float z = (itm->second->getRenderableEntityPosition().getY()+0.5f)*CASE_SIDE_SIZE - this->pos[2];

			float xf = f.getModelViewMat()[0]*x + f.getModelViewMat()[4]*y + f.getModelViewMat()[8]*z;
			float yf = f.getModelViewMat()[1]*x + f.getModelViewMat()[5]*y + f.getModelViewMat()[9]*z;
			float zf = f.getModelViewMat()[2]*x + f.getModelViewMat()[6]*y + f.getModelViewMat()[10]*z;

			glPushMatrix();
			glTranslatef(xf,yf+6.0f,zf);
			glfont.renderText2D( -1.5f, 0, "%d", (int)itm->second->getCurrentLife() );
			glPopMatrix();
		}
	}
	glPopMatrix();

	glDisable(GL_BLEND);
	glDisable(GL_ALPHA_TEST);
	
	glDisable(GL_TEXTURE_2D);

	glDepthFunc(GL_LESS);
	glDepthMask(GL_TRUE);
}

void Renderer::followArmy(int which)
{
	switch(which)
	{
	case 0:
		this->currentArmy = this->army1;
		break;
	case 1:
		this->currentArmy = this->army2;
		break;
	case 2:
		this->currentArmy = NULL;
		break;
	default:
		//on ne change rien
		break;
	}
}

