/*====================ÉCOLE POLYTECHNIQUE DE MONTRÉAL============================
  
	NOM DU FICHIER :CTerrain.cpp
	
	DESCRIPTION :	Déclaration des fonctions de la classe Terrain
	
	AUTEUR : Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
	
	DATE DE CRÉATION : 2004-3-16
	
	MISES À JOUR :
	
===============================================================================*/
#include "CTerrain.h"
#include <GL/glut.h>
#include <cstdlib>

#define LARGEURPHARE	1.0f

CPngTexture CTerrain::TexGazon;
CPngTexture CTerrain::TexBordures;
CPngTexture CTerrain::TexPhare;
CPngTexture CTerrain::TexFloor;

/*------------------------------------------------------------------
  Fonction :	CTerrain::~CTerrain
  
  Description :	Destructeur
  
  Paramètres :
  
  Valeur retournée :	 : 
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
CTerrain::~CTerrain()
{
    if (this->ListeAffichage != 0)
    {
	glDeleteLists(ListeAffichage,1);
	ListeAffichage = 0;
	AngleRotPhare = 0.0f;
    }
    if (ListLumierePhare != 0)
    {
	glDeleteLists(ListLumierePhare, 1);
	ListLumierePhare = 0;
    }
    delete Balle;
}


/*------------------------------------------------------------------
  Fonction :	CTerrain::CTerrain
  
  Description :	Constructeur
  
  Paramètres :
  
  Valeur retournée :	 : 
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
CTerrain::CTerrain()
{
    int i,j;
    for (i = 0; i < GRILLE_W+2; i++)
    {
	for (j = 0; j < GRILLE_H+2; j++)
	{
	    Grille[i][j] = JEUTUILE_NONE;
	}
    }
    RempliCoins();
    Balle = new CBalle;
    //Balle->setPos(CVector3f(2.3, 2.5, 0.04267));
    Balle->setDirection(CVector3f(0,0,0));
    this->ListeAffichage = 0;
    ListLumierePhare = 0;
}

// Macro utilisée dans ChargerTerrain
#define VALIDE               \
if (Fic.fail() || Fic.eof()) \
{                            \
			     Fic.close();              \
			     return false;             \
			 }

//hauteur des objets dans le jeu en 3D
#define ZOBJET3D 0.0f


/*------------------------------------------------------------------
  Fonction :	CTerrain::ChargerTerrain
  
  Description :	Charge un terrain à partir du nom de fichier du terrain
  
  Paramètres :
	 const char* NomFic : Nom du fichier du terrain
	 
  Valeur retournée :	bool : Si le chargement à réussi
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
bool CTerrain::ChargerTerrain(const char* NomFic)
{
    
    char EnTete[] = "carmonit";
    std::list<CObjet *>::iterator it;
    int NbObjets, Type;
    float X, Y;
    CObjet* NouvelObjet;
    
    //On s'assure que la liste des objets du terrain est vide -> on la vide!
    for (it = LesObjets.begin(); it != LesObjets.end();)
    {
	delete (*it);
	it = LesObjets.erase(it);
    }
    
    //ouverture du fichier
    std::ifstream Fic(NomFic, std::ios::binary);
    VALIDE
	    
	    // Lecture de l'en-tête de fichier
	    Fic.read( (char *) EnTete, 8*sizeof(char));
    VALIDE
	    
	    //vérification de l'en-tête du fichier ouvert
	    if (strcmp(EnTete, "carmonit") != 0)
    {
	Fic.close();
	return false;
    }
    
    // Lecture de la grille de tuiles
    Fic.read( (char *) Grille, (GRILLE_W+2)*(GRILLE_H+2)*sizeof(type_jeutuile));
    VALIDE
	    
	    //lecture du nombre d'objets
	    Fic.read( (char *) &NbObjets, sizeof(int));
    VALIDE
	    
	    // Lecture des objets
	    for (int i = 0; i < NbObjets; i++)
    {
	Fic.read( (char *) &Type, sizeof(int));
	VALIDE
		Fic.read( (char *) &X, sizeof(int));
	VALIDE
		Fic.read( (char *) &Y, sizeof(int));
	VALIDE
		NouvelObjet = CObjet::CreerObjet( (type_jeuobjet) Type, CVector3f(X, Y, ZOBJET3D));
	if (NouvelObjet == 0)
	{
	    Fic.close();
	    return false;
	}
	LesObjets.push_back(NouvelObjet);
    }
    
    Fic.close();
    
    RempliCoins();
    
    CentreBalleDepart();
    
    return true;
    
}


/*------------------------------------------------------------------
  Fonction :	CTerrain::CentreBalleDepart
  
  Description :	Fonction qui place la balle initialement au centre de départ
  
  Paramètres :
  
  Valeur retournée :	void : 
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
void CTerrain::CentreBalleDepart()
{
    Balle->setPos(CVector3f(getDepart()->getPos().GetPosX(),
			    getDepart()->getPos().GetPosY(),
			    Balle->getRayon()/TAILLE_TUILE));
    this->getTrou()->isCollision(this->Balle);
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::AfficheGL
  
  Description :	Fonction qui affiche le terrain en OpenGL
  
  Paramètres :
  
  Valeur retournée :	void : 
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
void CTerrain::AfficheGL()
{
    //Valeurs des sources de lumieres
    GLfloat mat_diffuse[] = {0.8, 0.8, 0.8, 1.0};
    GLfloat mat_specular[] = {0.2, 0.2, 0.2, 1.0};
    GLfloat mat_shininess[] = {5.0};
    GLfloat mat_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
    
    //Listes d'affichage pour le phare et le terrain
    if (ListLumierePhare == 0)
    {
	ListLumierePhare = glGenLists(1);
	glNewList(ListLumierePhare, GL_COMPILE);
	ConstruitLumierePhare();
	glEndList();
    }
    
    if (this->ListeAffichage == 0)
    {
	ListeAffichage = glGenLists(1);
	getTrou()->setStencil();
	glNewList(ListeAffichage,GL_COMPILE);
	{
	    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);
	    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
	    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
	    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
	    this->AfficheTuiles();
	    this->AfficheBordures();
	    this->AfficheEnv();
	}
	glEndList();
    }
    
    //Afficher le phare
    glPushMatrix();
    glTranslatef(PhareX, PhareY, PhareZ + 6*LARGEURPHARE);
    glScalef(LARGEURPHARE / 2.0f, LARGEURPHARE / 2.0f, LARGEURPHARE / 2.0f);
    glRotatef(AngleRotPhare, 0, 0, 1);
    glCallList(ListLumierePhare);
    glPopMatrix();
    
    //Afficher le terrain
    glCallList(ListeAffichage);
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
    this->AfficheObjets();
    Balle->AfficheGL();
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::CCM
  
  Description :	Fonction qui gère les collisions de la balle avec les bordures
  
  Paramètres :
	 CBalle* balle : 
	 
  Valeur retournée :	bool : Si il y a eu collision
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
bool CTerrain::CCM(CBalle* balle)
{
    bool ATrouveCollision = false;
    CVector3f Normale, Vcolli, Vconst;
    bool JeuB[8]; //0 = HAUT 1 = DROIT 2 = BAS 3 = GAUCHE 4 = UR 5 = DR 6 = DL 7 = UL
    //Initialiser le tableau à FALSE
    for(int i = 0; i<8; i++)
	JeuB[i] = false;
    //Vitesse de la balle en i et j
    float NVitesseX = balle->getDirection().GetPosX();
    float NVitesseY = balle->getDirection().GetPosY();
    //Coordonnees du coin le plus proche de la balle
    int CoinX = int(balle->getPos().GetPosX()+1.5);
    int CoinY = int(balle->getPos().GetPosY()+1.5);
    //Coordonnees du coin inferieur gauche de la tuile sur laquelle se situe la balle
    int BaseX = int(balle->getPos().GetPosX()+1);
    int BaseY = int(balle->getPos().GetPosY()+1);
    
    //Collision avec le coin le plus proche
    if((CoinGrille[CoinX][CoinY]) &&
       (sqrt(pow(CoinX-balle->getPos().GetPosX()-1,2) +
	     pow(CoinY-balle->getPos().GetPosY()-1,2)) < balle->getRayon()/TAILLE_TUILE))
    {
	// Collision avec un coin
	ATrouveCollision = true;
	Vcolli = balle->getDirection() >>
		 (balle->getPos() - CVector3f(CoinX - 1, CoinY - 1, balle->getRayon()/TAILLE_TUILE));//getPos().GetPosZ()
	Vconst = (balle->getDirection() - Vcolli);
	balle->Recule();
	balle->setDirection(balle->getCoefRestitution() * (Vconst-Vcolli));
    }
    
    else
    {
	//Determiner quelle bordure va etre frappée
	switch(Grille[BaseX][BaseY])
	{
			case(JEUTUILE_SQUARE):
			    {
				if(NVitesseY > 0)
				    if(Grille[BaseX][BaseY+1]==JEUTUILE_NONE
				       || Grille[BaseX][BaseY+1]==JEUTUILE_UL
				       || Grille[BaseX][BaseY+1]==JEUTUILE_UR)
					JeuB[0] = true; //HAUT
				if(NVitesseX > 0)
				    if(Grille[BaseX+1][BaseY]==JEUTUILE_NONE
				       || Grille[BaseX+1][BaseY]==JEUTUILE_UR
				       || Grille[BaseX+1][BaseY]==JEUTUILE_DR)
					JeuB[1] = true; //DROIT
				if(NVitesseY < 0)
				    if(Grille[BaseX][BaseY-1]==JEUTUILE_NONE
				       || Grille[BaseX][BaseY-1]==JEUTUILE_DL
				       || Grille[BaseX][BaseY-1]==JEUTUILE_DR)
					JeuB[2] = true; //BAS
				if(NVitesseX < 0)
				    if(Grille[BaseX-1][BaseY]==JEUTUILE_NONE
				       || Grille[BaseX-1][BaseY]==JEUTUILE_DL
				       || Grille[BaseX-1][BaseY]==JEUTUILE_UL)
					JeuB[3] = true; //GAUCHE
			    }
			    break;
			case(JEUTUILE_UR):
			    {
				if(NVitesseY > 0)
				    if(Grille[BaseX][BaseY+1]==JEUTUILE_NONE
				       || Grille[BaseX][BaseY+1]==JEUTUILE_UL
				       || Grille[BaseX][BaseY+1]==JEUTUILE_UR)
					JeuB[0] = true; //HAUT
				if(NVitesseX > 0)
				    if(Grille[BaseX+1][BaseY]==JEUTUILE_NONE
				       || Grille[BaseX+1][BaseY]==JEUTUILE_UR
				       || Grille[BaseX+1][BaseY]==JEUTUILE_DR)
					JeuB[1] = true; //DROIT
				JeuB[4] = true;
			    }
			    break;
			case(JEUTUILE_DR):
			    {
				if(NVitesseY < 0)
				    if(Grille[BaseX][BaseY-1]==JEUTUILE_NONE
				       || Grille[BaseX][BaseY-1]==JEUTUILE_DL
				       || Grille[BaseX][BaseY-1]==JEUTUILE_DR)
					JeuB[2] = true; //BAS
				if(NVitesseX > 0)
				    if(Grille[BaseX+1][BaseY]==JEUTUILE_NONE
				       || Grille[BaseX+1][BaseY]==JEUTUILE_UR
				       || Grille[BaseX+1][BaseY]==JEUTUILE_DR)
					JeuB[1] = true; //DROIT
				JeuB[5] = true;
			    }
			    break;
			case(JEUTUILE_DL):
			    {
				if(NVitesseY < 0)
				    if(Grille[BaseX][BaseY-1]==JEUTUILE_NONE
				       || Grille[BaseX][BaseY-1]==JEUTUILE_DL
				       || Grille[BaseX][BaseY-1]==JEUTUILE_DR)
					JeuB[2] = true; //BAS
				if(NVitesseX < 0)
				    if(Grille[BaseX-1][BaseY]==JEUTUILE_NONE
				       || Grille[BaseX-1][BaseY]==JEUTUILE_DL
				       || Grille[BaseX-1][BaseY]==JEUTUILE_UL)
					JeuB[3] = true; //GAUCHE
				JeuB[6] = true;
			    }
			    break;
			case(JEUTUILE_UL):
			    {
				if(NVitesseY > 0)
				    if(Grille[BaseX][BaseY+1]==JEUTUILE_NONE
				       || Grille[BaseX][BaseY+1]==JEUTUILE_UL
				       || Grille[BaseX][BaseY+1]==JEUTUILE_UR)
					JeuB[0] = true; //HAUT
				if(NVitesseX < 0)
				    if(Grille[BaseX-1][BaseY]==JEUTUILE_NONE
				       || Grille[BaseX-1][BaseY]==JEUTUILE_DL
				       || Grille[BaseX-1][BaseY]==JEUTUILE_UL)
					JeuB[3] = true; //GAUCHE
				JeuB[7] = true;
			    }
			    break;
			case(JEUTUILE_NONE):
			case(JEUTUILE_END):
			    balle->setDirection(CVector3f(0,0,0));
			    break;
			}
	//0 = HAUT 1 = DROIT 2 = BAS 3 = GAUCHE 4 = UR 5 = DR 6 = DL 7 = UL
	if(JeuB[0])
	{	//Normale de la bordure
	    Normale = CVector3f(0.0, -1.0, 0.0);
	    if (ColliBordu(Normale, balle))
		ATrouveCollision = true;
	}
	if(JeuB[1])
	{
	    Normale = CVector3f(-1.0, 0.0, 0.0);
	    if (ColliBordu(Normale, balle))
		ATrouveCollision = true;
	}
	if(JeuB[2])
	{
	    Normale = CVector3f(0.0, 1.0, 0.0);
	    if(ColliBordu2(Normale, balle))
		ATrouveCollision = true;
	}
	if(JeuB[3])
	{
	    Normale = CVector3f(1.0, 0.0, 0.0);
	    if(ColliBordu2(Normale, balle))
		ATrouveCollision = true;
	}
	if(JeuB[4])
	{
	    Normale = CVector3f(1/sqrt(2.0), 1/sqrt(2.0), 0.0);
	    if(ColliBordu3(Normale, balle))
		ATrouveCollision = true;
	}
	if(JeuB[5])
	{
	    Normale = CVector3f(1/sqrt(2.0), -1/sqrt(2.0), 0.0);
	    if(ColliBordu(Normale, balle))
		ATrouveCollision = true;
	}
	if(JeuB[6])
	{
	    Normale = CVector3f(-1/sqrt(2.0), -1/sqrt(2.0), 0.0);
	    if(ColliBordu3(Normale, balle))
		ATrouveCollision = true;
	}
	if(JeuB[7])
	{
	    Normale = CVector3f(-1/sqrt(2.0), 1/sqrt(2.0), 0.0);
	    if(ColliBordu(Normale, balle))
		ATrouveCollision = true;
	}		
	if(ColliDiago(balle, BaseX, BaseY, Normale))
	    ATrouveCollision = true;
    }
    // Ralentissement de la balle a cause de la friction du terrain
    float Norme = balle->getDirection().GetNorm();
    if(Norme >= balle->getCoefAmortissement() * DT)
    {
	balle->setDirection(balle->getDirection()/Norme);
	Norme -= balle->getCoefAmortissement() * DT;
	balle->setDirection(balle->getDirection()*Norme);
    }
    else
	balle->setDirection(CVector3f(0,0,0));
    return ATrouveCollision;
}


/*------------------------------------------------------------------
  Fonction :	CTerrain::ColliBordu
  
  Description :	Fonction qui vérifie si la collision va se produire avec une bordure
  
  Paramètres :
	 const CVector3f& Normale : Normale de la bordure
	 CBalle* balle : 
	 
  Valeur retournée :	bool : Si la collision va se produire
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
bool CTerrain::ColliBordu(const CVector3f& Normale, CBalle* balle)
{
    CVector3f Rayon, Base, Vcolli, Vconst;
    float C;
    //Coordonnees du coin inferieur gauche de la tuile sur laquelle se situe la balle
    int BaseX = int(balle->getPos().GetPosX()+1);
    int BaseY = int(balle->getPos().GetPosY()+1);
    
    Base = CVector3f(BaseX,BaseY, balle->getRayon()/TAILLE_TUILE);
    //Vecteur de la droite critique
    Rayon = balle->getRayon()/TAILLE_TUILE * Normale;
    Base = Base + Rayon;
    //Constante de l'equation de la droite
    C = -Base*Normale;
    if(Normale*balle->getPos()+C < 0)
    {
	Vcolli = balle->getDirection() >> Normale;
	Vconst = (balle->getDirection() - Vcolli);
	balle->Recule();
	balle->setDirection(balle->getCoefRestitution() * (Vconst-Vcolli));
	return true;
    }
    return false;
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::ColliBordu2
  
  Description :	Fonction qui vérifie si la collision va se produire avec une bordure
  
  Paramètres :
	 const CVector3f& Normale : Normale de la bordure
	 CBalle* balle : 
	 
  Valeur retournée :	bool : Si la collision va se produire
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
bool CTerrain::ColliBordu2(const CVector3f& Normale, CBalle* balle)
{
    CVector3f Rayon, Base, Vcolli, Vconst;
    float C;
    //Coordonnees du coin inferieur gauche de la tuile sur laquelle se situe la balle
    int BaseX = int(balle->getPos().GetPosX());
    int BaseY = int(balle->getPos().GetPosY());
    
    Base = CVector3f(BaseX,BaseY, balle->getRayon()/TAILLE_TUILE);
    //Vecteur de la droite critique
    Rayon = balle->getRayon()/TAILLE_TUILE * Normale;
    Base = Base + Rayon;
    //Constante de l'equation de la droite
    C = -Base*Normale;
    if(Normale*balle->getPos()+C < 0)
    {
	Vcolli = balle->getDirection() >> Normale;
	Vconst = (balle->getDirection() - Vcolli);
	balle->Recule();
	balle->setDirection(balle->getCoefRestitution() * (Vconst-Vcolli));
	return true;
    }
    return false;
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::ColliBordu3
  
  Description :	Fonction qui vérifie si la collision va se produire avec une bordure diagonale
  
  Paramètres :
	 const CVector3f& Normale : Normale de la bordure
	 CBalle* balle : 
	 
  Valeur retournée :	bool : Si la collision va se produire
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
bool CTerrain::ColliBordu3(const CVector3f& Normale, CBalle* balle)
{
    CVector3f Rayon, Base, Vcolli, Vconst;
    float C;
    //Coordonnees du coin inferieur gauche de la tuile sur laquelle se situe la balle
    float BaseX = int(balle->getPos().GetPosX())+1;
    float BaseY = int(balle->getPos().GetPosY());
    
    Base = CVector3f(BaseX,BaseY, balle->getRayon()/TAILLE_TUILE);
    //Vecteur de la droite critique
    Rayon = balle->getRayon()/TAILLE_TUILE * Normale;
    Base = Base + Rayon;
    //Constante de l'equation de la droite
    C = -Base*Normale;
    if(Normale*balle->getPos()+C < 0)
    {
	Vcolli = balle->getDirection() >> Normale;
	Vconst = (balle->getDirection() - Vcolli);
	balle->Recule();
	balle->setDirection(balle->getCoefRestitution() * (Vconst-Vcolli));
	return true;
    }
    return false;
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::ColliBordu4
  
  Description :	Fonction qui vérifie si la collision va se produire avec une bordure diagonale
  
  Paramètres :
	 const CVector3f& Normale : Normale de la bordure
	 CBalle* balle : 
	 
  Valeur retournée :	bool : Si la collision va se produire
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/

bool CTerrain::ColliBordu4(const CVector3f& Normale, CBalle* balle)
{
    CVector3f Rayon, Base, Vcolli, Vconst;
    float C;
    //Coordonnees du coin inferieur gauche de la tuile sur laquelle se situe la balle
    float BaseX = int(balle->getPos().GetPosX());
    float BaseY = int(balle->getPos().GetPosY())+1;
    
    Base = CVector3f(BaseX,BaseY, balle->getRayon()/TAILLE_TUILE);
    //Vecteur de la droite critique
    Rayon = balle->getRayon()/TAILLE_TUILE * Normale;
    Base = Base + Rayon;
    //Constante de l'equation de la droite
    C = -Base*Normale;
    if(Normale*balle->getPos()+C < 0)
    {
	Vcolli = balle->getDirection() >> Normale;
	Vconst = (balle->getDirection() - Vcolli);
	balle->Recule();
	balle->setDirection(balle->getCoefRestitution() * (Vconst-Vcolli));
	return true;
    }
    return false;
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::ColliDiago
  
  Description :	Vérifie si la balle va collisionner avec un coin
  
  Paramètres :
	 CBalle* UneBalle : 
	 BaseX : position en x de la tuile actuelle
	 BaseY : position en y de la tuile actuelle
	 CVector3f Normale : normale de la tuile
	 
  Valeur retournée :	bool : Si une collision se produit
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
bool CTerrain::ColliDiago(CBalle* balle, int BaseX, int BaseY, CVector3f Normale)
{
    bool YAUneCollision = false;
    //Deux tuiles UR une a la suite de l'autre
    if(Grille[BaseX-1][BaseY]==JEUTUILE_UR
       && Grille[BaseX][BaseY-1]==JEUTUILE_UR)
    {
	Normale = 1/sqrt(2.0f)*CVector3f(1.0f, 1.0f, 0.0f);
	YAUneCollision = (ColliBordu2(Normale, balle) || YAUneCollision);
    }
    //Deux tuiles DR une a la suite de l'autre
    if(Grille[BaseX-1][BaseY]==JEUTUILE_DR
       && Grille[BaseX][BaseY+1]==JEUTUILE_DR)
    {
	Normale = 1/sqrt(2.0f)*CVector3f(1.0f, -1.0f, 0.0f);
	YAUneCollision = (ColliBordu4(Normale, balle) || YAUneCollision);
    }
    //Etc...
    if(Grille[BaseX+1][BaseY]==JEUTUILE_DL
       && Grille[BaseX][BaseY+1]==JEUTUILE_DL)
    {
	Normale = 1/sqrt(2.0f)*CVector3f(-1.0f, -1.0f, 0.0f);
	YAUneCollision = (ColliBordu(Normale, balle) || YAUneCollision);
    }
    
    if(Grille[BaseX+1][BaseY]==JEUTUILE_UL
       && Grille[BaseX][BaseY-1]==JEUTUILE_UL)
    {
	Normale = 1/sqrt(2.0f)*CVector3f(-1.0f, 1.0f, 0.0f);
	YAUneCollision = (ColliBordu3(Normale, balle) || YAUneCollision);
    }
    return YAUneCollision;
}
/*------------------------------------------------------------------
  Fonction :	CTerrain::CollisionObjets
  
  Description :	Vérifie si la balle va collisionner avec la liste d'objets
  
  Paramètres :
	 CBalle* UneBalle : 
	 
  Valeur retournée :	bool : Si une collision se produit
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
bool CTerrain::CollisionObjets(CBalle* UneBalle)
{
    for (std::list<CObjet*>::const_iterator it = LesObjets.begin();
    it != LesObjets.end();
    it++)
    {
	if ((*it)->isCollision(UneBalle))
	    return true;
    }
    return false;
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::CollisionObjetsLigne
  
  Description :	Vérifie si la ligne directrice va collisionner avec un objet, 
					sauf la trappe, le trou d'eau et le trou
					
  Paramètres :
	 CBalle* UneBalle : 
	 
  Valeur retournée :	bool : Si une collision se produit
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
bool CTerrain::CollisionObjetsLigne(CBalle* UneBalle)
{
    for (std::list<CObjet*>::const_iterator it = LesObjets.begin();
    it != LesObjets.end();
    it++)
    {
	if ((*it)->GetType() != JEUOBJET_TROU &&
	    (*it)->GetType() != JEUOBJET_TRAPPE &&
	    (*it)->GetType() != JEUOBJET_TROUDEAU &&
	    (*it)->isCollision(UneBalle))
	    return true;
    }
    return false;
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::ChargeTextures
  
  Description :	Charge la texture du gazon et des bordures
  
  Paramètres :
  
  Valeur retournée :	bool : Vrai
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
bool CTerrain::ChargeTextures()
{
    TexGazon.LoadPNG("editeur/images/Gazon.png");
    TexBordures.LoadPNG("editeur/images/Bordures.png");
    TexPhare.LoadPNG("editeur/images/Phare.png");
    TexFloor.LoadPNG("editeur/images/floor.png");
    return true;
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::DechargeTextures
  
  Description :	Décharge la texture du gazon et des bordures
  
  Paramètres :
  
  Valeur retournée :	bool : Vrai
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
bool CTerrain::DechargeTextures()
{
    TexGazon.Unload();
    TexBordures.Unload();
    TexPhare.Unload();
    TexFloor.Unload();
    //On décharge toute l'animation
    return true;
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::AfficheBordures
  
  Description :	Affiche les bordures en OpenGL
  
  Paramètres :
	 CVector3f Position : 
	 
  Valeur retournée :	void : 
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
void CTerrain::AfficheBordures() const
{
    int i, PosX, PosY;
    float Tmp;    
    CVector3f Points[4];
    
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D,TexBordures.GetID());
    glEnable(GL_TEXTURE_GEN_S);
    glEnable(GL_TEXTURE_GEN_T);
    GLfloat stable[4] = {5.0,0.0,0.0,0.0};
    GLfloat ttable[4] = {0.0,5.0,0.0,0.0};
    glTexGenfv(GL_S,GL_OBJECT_PLANE,stable);
    glTexGenfv(GL_T,GL_OBJECT_PLANE,ttable);
    glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
    glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    for (PosY = 0; PosY < GRILLE_H; PosY++)
    {
	for (PosX = 0; PosX < GRILLE_W; PosX++)
	{
	    CVector3f Position(PosX, PosY, 0);
	    //HAUT (Dir = (0,1))
	    if ((Grille[PosX+1][PosY+1] == JEUTUILE_SQUARE ||
		 Grille[PosX+1][PosY+1] == JEUTUILE_UL ||
		 Grille[PosX+1][PosY+1] == JEUTUILE_UR) && BordureATracer(Position,CVector3f(0,1)))
	    {
		CVector3f Dir(0,1);
		CVector3f Perp = CVector3f(Dir.GetPosY(),-Dir.GetPosX());
		Points[0] = CVector3f(PosX,PosY+1);
		Points[1] = CVector3f(PosX+1,PosY+1);
		Points[2] = CVector3f(PosX+1+DepPourDroite(Position,Dir,Perp),PosY+1+BORDER_WIDTH);
		Points[3] = CVector3f(PosX-DepPourDroite(Position,Dir,-Perp),PosY+1+BORDER_WIDTH);	
		glBegin(GL_QUADS);
		{
		    for (i = 0; i < 4; i++)
		    {
			glNormal3f(0,0,1);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
		    }
		    for (i = 0; i < 3; i++)
		    {
			CVector3f Norm = ((Points[i]-Points[i+1]).SideProductV(CVector3f(0,0,-1))).GetNormalVector();
			glNormal3f(Norm.GetPosX(), Norm.GetPosY(), Norm.GetPosZ());
			glVertex3f(Points[i+1].GetPosX(),Points[i+1].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),-2.0f*SAFE_DISP);
			glVertex3f(Points[i+1].GetPosX(),Points[i+1].GetPosY(),-2.0f*SAFE_DISP);
		    }	    
		}
		glEnd();
	    }
	    //HAUT_DROITE (Dir = (1,1))
	    if (Grille[PosX+1][PosY+1] == JEUTUILE_DL)
	    {
		CVector3f Dir(1,1);
		CVector3f Perp = CVector3f(Dir.GetPosY(),-Dir.GetPosX());
		Points[0] = CVector3f(PosX,PosY+1);
		Points[1] = CVector3f(PosX+1,PosY);
		Tmp = DepPourDiago(Position,Dir,Perp);
		Points[2] = CVector3f(PosX+1,PosY)+Tmp*Perp.GetNormalVector()+BORDER_WIDTH*Dir.GetNormalVector();
		Tmp = DepPourDiago(Position,Dir,-Perp);
		Points[3] = CVector3f(PosX,PosY+1)-Tmp*Perp.GetNormalVector()+BORDER_WIDTH*Dir.GetNormalVector();
		glBegin(GL_QUADS);
		{
		    for (i = 0; i < 4; i++)
		    {
			glNormal3f(0,0,1);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
		    }
		    for (i = 0; i < 3; i++)
		    {
			CVector3f Norm = ((Points[i]-Points[i+1]).SideProductV(CVector3f(0,0,-1))).GetNormalVector();
			glNormal3f(Norm.GetPosX(), Norm.GetPosY(), Norm.GetPosZ());
			glVertex3f(Points[i+1].GetPosX(),Points[i+1].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),-2.0f*SAFE_DISP);
			glVertex3f(Points[i+1].GetPosX(),Points[i+1].GetPosY(),-2.0f*SAFE_DISP);
		    }	    
		}
		glEnd();
	    }
	    
	    //DROITE (Dir = (1,0))
	    if ((Grille[PosX+1][PosY+1] == JEUTUILE_SQUARE ||
		 Grille[PosX+1][PosY+1] == JEUTUILE_UR ||
		 Grille[PosX+1][PosY+1] == JEUTUILE_DR) && BordureATracer(Position,CVector3f(1,0)))
	    {
		CVector3f Dir(1,0);
		CVector3f Perp = CVector3f(Dir.GetPosY(),-Dir.GetPosX());
		Points[0] = CVector3f(PosX+1,PosY+1);
		Points[1] = CVector3f(PosX+1,PosY);
		Points[2] = CVector3f(PosX+1+BORDER_WIDTH,PosY-DepPourDroite(Position,Dir,Perp));
		Points[3] = CVector3f(PosX+1+BORDER_WIDTH,PosY+1+DepPourDroite(Position,Dir,-Perp));
		glBegin(GL_QUADS);
		{
		    for (i = 0; i < 4; i++)
		    {
			glNormal3f(0,0,1);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
		    }
		    for (i = 0; i < 3; i++)
		    {
			CVector3f Norm = ((Points[i]-Points[i+1]).SideProductV(CVector3f(0,0,-1))).GetNormalVector();
			glNormal3f(Norm.GetPosX(), Norm.GetPosY(), Norm.GetPosZ());
			glVertex3f(Points[i+1].GetPosX(),Points[i+1].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),-2.0f*SAFE_DISP);
			glVertex3f(Points[i+1].GetPosX(),Points[i+1].GetPosY(),-2.0f*SAFE_DISP);
		    }	    
		}
		glEnd();
	    }
	    
	    //BAS_DROITE (Dir = (1,-1))
	    if (Grille[PosX+1][PosY+1] == JEUTUILE_UL)
	    {
		CVector3f Dir(1,-1);
		CVector3f Perp = CVector3f(Dir.GetPosY(),-Dir.GetPosX());
		Points[0] = CVector3f(PosX+1,PosY+1);
		Points[1] = CVector3f(PosX,PosY);
		Tmp = DepPourDiago(Position,Dir,Perp);
		Points[2] = CVector3f(PosX,PosY)+Tmp*Perp.GetNormalVector()+BORDER_WIDTH*Dir.GetNormalVector();
		Tmp = DepPourDiago(Position,Dir,-Perp);
		Points[3] = CVector3f(PosX+1,PosY+1)-Tmp*Perp.GetNormalVector()+BORDER_WIDTH*Dir.GetNormalVector();
		glBegin(GL_QUADS);
		{
		    for (i = 0; i < 4; i++)
		    {
			glNormal3f(0,0,1);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
		    }
		    for (i = 0; i < 3; i++)
		    {
			CVector3f Norm = ((Points[i]-Points[i+1]).SideProductV(CVector3f(0,0,-1))).GetNormalVector();
			glNormal3f(Norm.GetPosX(), Norm.GetPosY(), Norm.GetPosZ());
			glVertex3f(Points[i+1].GetPosX(),Points[i+1].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),-2.0f*SAFE_DISP);
			glVertex3f(Points[i+1].GetPosX(),Points[i+1].GetPosY(),-2.0f*SAFE_DISP);
		    }	    
		}
		glEnd();
	    }
	    
	    //BAS (Dir = (0,-1))
	    if ((Grille[PosX+1][PosY+1] == JEUTUILE_SQUARE ||
		 Grille[PosX+1][PosY+1] == JEUTUILE_DL ||
		 Grille[PosX+1][PosY+1] == JEUTUILE_DR) && BordureATracer(Position,CVector3f(0,-1)))
	    {
		CVector3f Dir(0,-1);
		CVector3f Perp = CVector3f(Dir.GetPosY(),-Dir.GetPosX());
		Points[0] = CVector3f(PosX+1,PosY);
		Points[1] = CVector3f(PosX,PosY);
		Points[2] = CVector3f(PosX-DepPourDroite(Position,Dir,Perp),PosY-BORDER_WIDTH);
		Points[3] = CVector3f(PosX+1+DepPourDroite(Position,Dir,-Perp),PosY-BORDER_WIDTH);
		glBegin(GL_QUADS);
		{
		    for (i = 0; i < 4; i++)
		    {
			glNormal3f(0,0,1);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
		    }
		    for (i = 0; i < 3; i++)
		    {
			CVector3f Norm = ((Points[i]-Points[i+1]).SideProductV(CVector3f(0,0,-1))).GetNormalVector();
			glNormal3f(Norm.GetPosX(), Norm.GetPosY(), Norm.GetPosZ());
			glVertex3f(Points[i+1].GetPosX(),Points[i+1].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),-2.0f*SAFE_DISP);
			glVertex3f(Points[i+1].GetPosX(),Points[i+1].GetPosY(),-2.0f*SAFE_DISP);
		    }	    
		}
		glEnd();
	    }
	    
	    //BAS_GAUCHE (Dir = (-1,-1))
	    if (Grille[PosX+1][PosY+1] == JEUTUILE_UR)
	    {
		CVector3f Dir(-1,-1);
		CVector3f Perp = CVector3f(Dir.GetPosY(),-Dir.GetPosX());
		Points[0] = CVector3f(PosX+1,PosY);
		Points[1] = CVector3f(PosX,PosY+1);
		Tmp = DepPourDiago(Position,Dir,Perp);
		Points[2] = CVector3f(PosX,PosY+1)+Tmp*Perp.GetNormalVector()+BORDER_WIDTH*Dir.GetNormalVector();
		Tmp = DepPourDiago(Position,Dir,-Perp);
		Points[3] = CVector3f(PosX+1,PosY)-Tmp*Perp.GetNormalVector()+BORDER_WIDTH*Dir.GetNormalVector();
		glBegin(GL_QUADS);
		{
		    for (i = 0; i < 4; i++)
		    {
			glNormal3f(0,0,1);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
		    }
		    for (i = 0; i < 3; i++)
		    {
			CVector3f Norm = ((Points[i]-Points[i+1]).SideProductV(CVector3f(0,0,-1))).GetNormalVector();
			glNormal3f(Norm.GetPosX(), Norm.GetPosY(), Norm.GetPosZ());
			glVertex3f(Points[i+1].GetPosX(),Points[i+1].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),-2.0f*SAFE_DISP);
			glVertex3f(Points[i+1].GetPosX(),Points[i+1].GetPosY(),-2.0f*SAFE_DISP);
		    }	    
		}
		glEnd();
	    }
	    
	    //GAUCHE (Dir = (-1,0))
	    if ((Grille[PosX+1][PosY+1] == JEUTUILE_SQUARE ||
		 Grille[PosX+1][PosY+1] == JEUTUILE_UL ||
		 Grille[PosX+1][PosY+1] == JEUTUILE_DL) && BordureATracer(Position,CVector3f(-1,0)))
	    {
		CVector3f Dir(-1,0);
		CVector3f Perp = CVector3f(Dir.GetPosY(),-Dir.GetPosX());
		Points[0] = CVector3f(PosX,PosY);
		Points[1] = CVector3f(PosX,PosY+1);
		Points[2] = CVector3f(PosX-BORDER_WIDTH,PosY+1+DepPourDroite(Position,Dir,Perp));
		Points[3] = CVector3f(PosX-BORDER_WIDTH,PosY-DepPourDroite(Position,Dir,-Perp));
		glBegin(GL_QUADS);
		{
		    for (i = 0; i < 4; i++)
		    {
			glNormal3f(0,0,1);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
		    }
		    for (i = 0; i < 3; i++)
		    {
			CVector3f Norm = ((Points[i]-Points[i+1]).SideProductV(CVector3f(0,0,-1))).GetNormalVector();
			glNormal3f(Norm.GetPosX(), Norm.GetPosY(), Norm.GetPosZ());
			glVertex3f(Points[i+1].GetPosX(),Points[i+1].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),-2.0f*SAFE_DISP);
			glVertex3f(Points[i+1].GetPosX(),Points[i+1].GetPosY(),-2.0f*SAFE_DISP);
		    }	    
		}
		glEnd();
	    }
	    
	    //HAUT_GAUCHE (Dir = (-1,1))
	    if (Grille[PosX+1][PosY+1] == JEUTUILE_DR)
	    {
		CVector3f Dir(-1,1);
		CVector3f Perp = CVector3f(Dir.GetPosY(),-Dir.GetPosX());
		Points[0] = CVector3f(PosX,PosY);
		Points[1] = CVector3f(PosX+1,PosY+1);
		Tmp = DepPourDiago(Position,Dir,Perp);
		Points[2] = CVector3f(PosX+1,PosY+1)+Tmp*Perp.GetNormalVector()+BORDER_WIDTH*Dir.GetNormalVector();
		Tmp = DepPourDiago(Position,Dir,-Perp);
		Points[3] = CVector3f(PosX,PosY)-Tmp*Perp.GetNormalVector()+BORDER_WIDTH*Dir.GetNormalVector();
		glBegin(GL_QUADS);
		{
		    for (i = 0; i < 4; i++)
		    {
			glNormal3f(0,0,1);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
		    }
		    for (i = 0; i < 3; i++)
		    {
			CVector3f Norm = ((Points[i]-Points[i+1]).SideProductV(CVector3f(0,0,-1))).GetNormalVector();
			glNormal3f(Norm.GetPosX(), Norm.GetPosY(), Norm.GetPosZ());
			glVertex3f(Points[i+1].GetPosX(),Points[i+1].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),BORDER_HEIGHT-2.0f*SAFE_DISP);
			glVertex3f(Points[i].GetPosX(),Points[i].GetPosY(),-2.0f*SAFE_DISP);
			glVertex3f(Points[i+1].GetPosX(),Points[i+1].GetPosY(),-2.0f*SAFE_DISP);
		    }	    
		}
		glEnd();
	    }
	}
    }
    
    glDisable(GL_TEXTURE_GEN_S);
    glDisable(GL_TEXTURE_GEN_T);
    glDisable(GL_TEXTURE_2D);
}


/*------------------------------------------------------------------
  Fonction :	CTerrain::BordureATracer
  
  Description :	Détermine si une bordure doit être tracée pour les côtés "droits"
  
  Paramètres :
	 CVector3f Position : 
	 CVector3f Direction : 
	 
  Valeur retournée :	bool : 
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
bool CTerrain::BordureATracer(CVector3f Position, CVector3f Direction) const
{
    CVector3f PosToCheck = Position+Direction;
    CVector3f TileToCheck = AssocieTuile(PosToCheck);
    CVector3f SideTile = CVector3f(Direction.GetPosY(),-Direction.GetPosX());
    if (TileToCheck == CVector3f(0,0) 
	|| TileToCheck == Direction+SideTile 
		|| TileToCheck == Direction-SideTile)
	return false;
    return true;
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::AfficheTuiles
  
  Description :	Affiche toutes les tuiles de la grille
  
  Paramètres :
  
  Valeur retournée :	void : 
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
void CTerrain::AfficheTuiles() const
{
    int i,j;
    glDepthFunc(GL_ALWAYS);
    for (i = 0; i < GRILLE_H; i++)
    {
	for (j = 0; j < GRILLE_W; j++)
	{
	    DessineTuile(j,i,Grille[j+1][i+1]);
	}
    }
    glDepthFunc(GL_LEQUAL);
    //Affiche les coins en rouge si on est en debug des coins
    //Aide à trouver les nouveaux bugs
#ifdef DEBUG_CORNERS
    for (i = 0; i < GRILLE_W; i++)
    {
	for (j = 0; j < GRILLE_H; j++)
	{
	    glColor3f(1,0,0);
	    if (CoinGrille[i+1][j+1])
	    {
		glBegin(GL_QUADS);
		{
		    glVertex3f(i,j);
		    glVertex3f(i,j+0.03);
		    glVertex3f(i+0.03,j+0.03);
		    glVertex3f(i+0.03,j);
		}
		glEnd();
	    }
	    glColor3f(1,1,1);
	}
    }
#endif
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::AfficheObjets
  
  Description :	Fonction qui affiche les AfficheGL de chaque objet de la liste
  
  Paramètres :
  
  Valeur retournée :	void : 
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
void CTerrain::AfficheObjets() const
{
    std::list<CObjet*>::const_iterator it;
    glDepthFunc(GL_LEQUAL);
    for (it = LesObjets.begin(); it != LesObjets.end(); ++it)
    {
	GLfloat mat_diffuse[] = {0.8, 0.8, 0.8, 1.0};
	GLfloat mat_specular[] = {0.2, 0.2, 0.2, 1.0};
	GLfloat mat_shininess[] = {5.0};
	GLfloat mat_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
	(*it)->AfficheGL();
    }
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::DiviseTuileTriangle
  
  Description :	Cette fonction divise un triangle en sous parties récursivement
						et les dessinent une fois le Depth = 0;
						
  Paramètres :
	 int Depth : profondeur de la récursivité
	 int pt1, pt2, pt3 : points du triangle
	 
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
void CTerrain::DiviseTuileTriangle(int Depth, CVector3f pt1, CVector3f pt2, CVector3f pt3) const
{
    if (Depth == 0)
    {
	glBegin(GL_TRIANGLES);
	{
	    glNormal3f(0,0,1);
	    glVertex3f(pt1.GetPosX(),pt1.GetPosY(),-SAFE_DISP);
	    glVertex3f(pt2.GetPosX(),pt2.GetPosY(),-SAFE_DISP);
	    glVertex3f(pt3.GetPosX(),pt3.GetPosY(),-SAFE_DISP);
	}
	glEnd();	
    } else {
	CVector3f pt1p = 0.5f*(pt1 + pt2);
	CVector3f pt2p = 0.5f*(pt2 + pt3);
	CVector3f pt3p = 0.5f*(pt3 + pt1);
	DiviseTuileTriangle(Depth-1,pt1,pt1p,pt3p);
	DiviseTuileTriangle(Depth-1,pt1p,pt2,pt2p);
	DiviseTuileTriangle(Depth-1,pt1p,pt2p,pt3p);
	DiviseTuileTriangle(Depth-1,pt3p,pt2p,pt3);
    }
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::DessineTuile
  
  Description :	Dessine une tuile en OpenGL
  
  Paramètres :
	 int PosX : Position en x du coin gauche de la tuile
	 int PosY : Position en y du coin gauche de la tuile
	 type_jeutuile Type : Type de tuile
	 
  Valeur retournée :	void : 
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
void CTerrain::DessineTuile(int PosX, int PosY, type_jeutuile Type) const
{
    int i,j;
    bool TuileTrou = (PosX >= int(getTrou()->getPos().GetPosX() - getTrou()->getRayon()/TAILLE_TUILE) &&
		      PosX <= int(getTrou()->getPos().GetPosX() + getTrou()->getRayon()/TAILLE_TUILE) &&
		      PosY >= int(getTrou()->getPos().GetPosY() - getTrou()->getRayon()/TAILLE_TUILE) &&
		      PosY <= int(getTrou()->getPos().GetPosY() + getTrou()->getRayon()/TAILLE_TUILE));
    if (TuileTrou)
    {
	glEnable(GL_STENCIL_TEST);
	glStencilFunc(GL_NOTEQUAL,0x1,0x1);
	glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
    }   
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D,TexGazon.GetID());
    glEnable(GL_TEXTURE_GEN_S);
    glEnable(GL_TEXTURE_GEN_T);
    GLfloat stable[4] = {1.5,0.0,0.0,0.0};
    GLfloat ttable[4] = {0.0,1.5,0.0,0.0};
    glTexGenfv(GL_S,GL_OBJECT_PLANE,stable);
    glTexGenfv(GL_T,GL_OBJECT_PLANE,ttable);
    glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
    glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    
    float DivDist = 1/(float)SUBDIV;
    switch (Type)
    {
    case JEUTUILE_SQUARE:
	for (i = 0; i < SUBDIV; i++)
	{
	    for (j = 0; j < SUBDIV; j++)
	    {
		glBegin(GL_QUADS);
		{
		    glNormal3f(0,0,1);
		    glVertex3f(PosX+i*DivDist,PosY+j*DivDist,-SAFE_DISP);
		    glVertex3f(PosX+i*DivDist,PosY+(j+1)*DivDist,-SAFE_DISP);
		    glVertex3f(PosX+(i+1)*DivDist,PosY+(j+1)*DivDist,-SAFE_DISP);
		    glVertex3f(PosX+(i+1)*DivDist,PosY+j*DivDist,-SAFE_DISP); 
		}
		glEnd();
	    }
	}
	break;
    case JEUTUILE_DL:
	DiviseTuileTriangle(SUBTRI,
			    CVector3f(PosX, PosY, 0),
			    CVector3f(PosX+1, PosY, 0),
			    CVector3f(PosX, PosY+1, 0));
	break;
    case JEUTUILE_DR:
	DiviseTuileTriangle(SUBTRI,
			    CVector3f(PosX, PosY, 0),
			    CVector3f(PosX+1, PosY, 0),
			    CVector3f(PosX+1, PosY+1, 0));
	break;
    case JEUTUILE_UR:
	DiviseTuileTriangle(SUBTRI,
			    CVector3f(PosX, PosY+1, 0),
			    CVector3f(PosX+1, PosY, 0),
			    CVector3f(PosX+1, PosY+1, 0));
	break;
    case JEUTUILE_UL:
	DiviseTuileTriangle(SUBTRI,
			    CVector3f(PosX, PosY, 0),
			    CVector3f(PosX+1, PosY+1, 0),
			    CVector3f(PosX, PosY+1, 0));
	break;
    case JEUTUILE_NONE:
	break;
    case JEUTUILE_END:
	break;
    }
    
    glDisable(GL_TEXTURE_GEN_S);
    glDisable(GL_TEXTURE_GEN_T);
    glDisable(GL_TEXTURE_2D);
    
    if (TuileTrou)
    {
	glDisable(GL_STENCIL_TEST);
    }
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::AssocieTuile
  
  Description :	Associe un vecteur à une tuile
		Utilisée dans l'algorithme des bordures automatiques
				
  Paramètres :
		CVector3f Position : Position de la tuile
		
  Valeur retournée :	CVector3f : Vecteur associé à la tuiles
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
CVector3f CTerrain::AssocieTuile(CVector3f Position) const
{
    switch (Grille[(int)Position.GetPosX()+1][(int)Position.GetPosY()+1])
    {
		case JEUTUILE_NONE:
		    return CVector3f(-2,-2);
		    break;
		case JEUTUILE_SQUARE:
		    return CVector3f(0,0);
		    break;
		case JEUTUILE_UL:
		    return CVector3f(1,-1);
		    break;
		case JEUTUILE_UR:
		    return CVector3f(-1,-1);
		    break;
		case JEUTUILE_DR:
		    return CVector3f(-1,1);
		    break;
		case JEUTUILE_DL:
		    return CVector3f(1,1);
		    break;
		}
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::DepPourDiago
  
  Description :	Détermine l'inconnue du 3e ou 4e point d'une bordure
				diagonale
				
  Paramètres :
		CVector3f Position : Position de la tuile
		CVector3f Direction : Bordure à tester
		CVector3f A : 3e ou 4e point (orientation par rapport
										à la direction)
										
  Valeur retournée :	float : l'inconnu correspondant à l'angle
								formé par la bordure à ce point
								
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
float CTerrain::DepPourDroite(CVector3f Position, CVector3f Direction, CVector3f A) const
{
    CVector3f VerifTuile;
    //En face
    {
	VerifTuile = AssocieTuile(Position+Direction);
	//45 degrés
	if (VerifTuile == (-Direction-A))
	    return -(BORDER_WIDTH/tan(22.5*M_PI/180.0));
    }
    //En coin
    {
	VerifTuile = AssocieTuile(Position+Direction+A);
	//90 degrés
	if (VerifTuile == CVector3f(0,0) ||
	    VerifTuile == (Direction+A) ||
	    VerifTuile == (-Direction+A))
	    return -BORDER_WIDTH;
	//135 degrés
	if (VerifTuile == (Direction-A))
	    return -BORDER_WIDTH*tan(22.5*M_PI/180.0);
    }
    //A côté
    {
	VerifTuile = AssocieTuile(Position+A);
	//180 degrés
	if (VerifTuile == CVector3f(0,0) ||
	    VerifTuile == (-Direction+A) ||
	    VerifTuile == (-Direction-A))
	    return 0;
	//225 degrés
	if (VerifTuile == (Direction+A))
	    return BORDER_WIDTH*tan(22.5*M_PI/180.0);
    }
    //Autre
    {
	VerifTuile = AssocieTuile(Position);
	//270 degrés
	if (VerifTuile == CVector3f(0,0) ||
	    VerifTuile == (-Direction-A))
	    return BORDER_WIDTH;
	//315 degrés
	if (VerifTuile == (-Direction+A))
	    return BORDER_WIDTH/tan(22.5*M_PI/180.0);
	//return 0.5;
    }
    return 0;
}


/*------------------------------------------------------------------
  Fonction :	CTerrain::DepPourDiago
  
  Description :	Retourne le déplacement nécessaire de l'un des deux points
				inconnus d'une bordure qui est diagonale
				
  Paramètres :
	 CVector3f Position : Position de la tuile testée
	 CVector3f Direction : Orientation de la bordure
	 CVector3f A : Point de la bordure qui est cherché
	 
  Valeur retournée :	float : Déplacement correspondant à l'angle
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
float CTerrain::DepPourDiago(CVector3f Position, CVector3f Direction, CVector3f A) const
{
    CVector3f VerifTuile;
    //À côté
    {
	VerifTuile = AssocieTuile(Position+(Direction+A)/2.0f);
	//45 degrés
	if (VerifTuile == CVector3f(0,0) ||
	    VerifTuile == Direction ||
	    VerifTuile == A)
	    return -(BORDER_WIDTH/tan(22.5*M_PI/180.0));
	//90 degrés
	if (VerifTuile == -A)
	    return -BORDER_WIDTH;
    }
    //Diago
    {
	VerifTuile = AssocieTuile(Position+A);
	//135 degrés
	if (VerifTuile == CVector3f(0,0) ||
	    VerifTuile == -Direction ||
	    VerifTuile == A)
	    return -BORDER_WIDTH*tan(22.5*M_PI/180.0);
	//180 degrés
	if (VerifTuile == Direction)
	    return 0;
    }
    //"Sous"
    {
	VerifTuile = AssocieTuile(Position-(Direction-A)/2.0f);
	//225 degrés
	if (VerifTuile == CVector3f(0,0) ||
	    VerifTuile == -Direction ||
	    VerifTuile == -A)
	    return BORDER_WIDTH*tan(22.5*M_PI/180.0);
	//270 degrés
	if (VerifTuile == A)
	    return BORDER_WIDTH;
    }
    //Autre
    {
	//315 degrés
	return BORDER_WIDTH/tan(22.5*M_PI/180.0);
    }
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::RempliCoins
  
  Description :	Remplit le tableau de booléens en indiquant s'il y a un coin
				qui doit être considéré pour les collisions
				
  Paramètres :
  
  Valeur retournée :	void : 
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
void CTerrain::RempliCoins()
{
    int i,j;
    //On initialise le tableau des coins à false (pas de coins)
    for (i = 0; i < GRILLE_W+2; i++)
    {
	for (j = 0; j < GRILLE_H+2; j++)
	{
	    CoinGrille[i][j] = false;
	}
    }
    for (i = 1; i < GRILLE_W+1; i++)
    {
	for (j = 1; j < GRILLE_H+1; j++)
	{
	    switch (Grille[i][j])
	    {
	    case JEUTUILE_NONE:
		//4 coins à vérifier
		//UL
		if ((Grille[i-1][j] == JEUTUILE_UL || Grille[i-1][j] == JEUTUILE_UR || Grille[i-1][j] == JEUTUILE_SQUARE) &&
		    (Grille[i-1][j+1] == JEUTUILE_SQUARE || Grille[i-1][j+1] == JEUTUILE_DR) &&
		    (Grille[i][j+1] == JEUTUILE_UL || Grille[i][j+1] == JEUTUILE_DL || Grille[i][j+1] == JEUTUILE_SQUARE) &&
		    (Grille[i-1][j] != Grille[i][j+1] || Grille[i-1][j] == JEUTUILE_SQUARE))
		    CoinGrille[i][j+1]=true;
		//UR
		if ((Grille[i+1][j] == JEUTUILE_UL || Grille[i+1][j] == JEUTUILE_UR || Grille[i+1][j] == JEUTUILE_SQUARE) &&
		    (Grille[i+1][j+1] == JEUTUILE_SQUARE || Grille[i+1][j+1] == JEUTUILE_DL) &&
		    (Grille[i][j+1] == JEUTUILE_DR || Grille[i][j+1] == JEUTUILE_UR || Grille[i][j+1] == JEUTUILE_SQUARE) &&
		    (Grille[i+1][j] != Grille[i][j+1] || Grille[i+1][j] == JEUTUILE_SQUARE))
		    CoinGrille[i+1][j+1]=true;
		//DR
		if ((Grille[i+1][j] == JEUTUILE_DR || Grille[i+1][j] == JEUTUILE_DL || Grille[i+1][j] == JEUTUILE_SQUARE) &&
		    (Grille[i+1][j-1] == JEUTUILE_SQUARE || Grille[i+1][j-1] == JEUTUILE_UL) &&
		    (Grille[i][j-1] == JEUTUILE_DR || Grille[i][j-1] == JEUTUILE_UR || Grille[i][j-1] == JEUTUILE_SQUARE) &&
		    (Grille[i+1][j] != Grille[i][j-1] || Grille[i+1][j] == JEUTUILE_SQUARE))
		    CoinGrille[i+1][j]=true;
		//DL
		if ((Grille[i-1][j] == JEUTUILE_DR || Grille[i-1][j] == JEUTUILE_DL || Grille[i-1][j] == JEUTUILE_SQUARE) &&
		    (Grille[i-1][j-1] == JEUTUILE_SQUARE || Grille[i-1][j-1] == JEUTUILE_UR) &&
		    (Grille[i][j-1] == JEUTUILE_UL || Grille[i][j-1] == JEUTUILE_DL || Grille[i][j-1] == JEUTUILE_SQUARE) &&
		    (Grille[i-1][j] != Grille[i][j-1] || Grille[i-1][j] == JEUTUILE_SQUARE))
		    CoinGrille[i][j]=true;
		break;
	    case JEUTUILE_SQUARE:
		//Ne peut pas causer un coin
		break;
	    case JEUTUILE_UL:
		//DR
		if ((Grille[i+1][j] == JEUTUILE_DR || Grille[i+1][j] == JEUTUILE_DL || Grille[i+1][j] == JEUTUILE_SQUARE) &&
		    (Grille[i+1][j-1] == JEUTUILE_SQUARE || Grille[i+1][j-1] == JEUTUILE_UL) &&
		    (Grille[i][j-1] == JEUTUILE_DR || Grille[i][j-1] == JEUTUILE_UR || Grille[i][j-1] == JEUTUILE_SQUARE) &&
		    (Grille[i+1][j] != Grille[i][j-1] || Grille[i+1][j] == JEUTUILE_SQUARE))
		    CoinGrille[i+1][j]=true;
		//UR POINTE
		if ((Grille[i][j+1] != JEUTUILE_UL && Grille[i][j+1] != JEUTUILE_DL && Grille[i][j+1] != JEUTUILE_NONE) &&
		    (Grille[i+1][j+1] == JEUTUILE_SQUARE || Grille[i+1][j+1] == JEUTUILE_DL) &&
		    ((Grille[i][j+1] == JEUTUILE_SQUARE || Grille[i][j+1] == JEUTUILE_DR) ||
		     (Grille[i+1][j] == JEUTUILE_SQUARE || Grille[i+1][j] == JEUTUILE_UL)))
		    CoinGrille[i+1][j+1]=true;
		//DL POINTE
		if ((Grille[i-1][j] != JEUTUILE_UR && Grille[i-1][j] != JEUTUILE_UL && Grille[i-1][j] != JEUTUILE_NONE) &&
		    (Grille[i-1][j-1] == JEUTUILE_SQUARE || Grille[i-1][j-1] == JEUTUILE_UR) &&
		    ((Grille[i][j-1] == JEUTUILE_SQUARE || Grille[i][j-1] == JEUTUILE_UL) ||
		     (Grille[i-1][j] == JEUTUILE_SQUARE || Grille[i-1][j] == JEUTUILE_DR)))
		    CoinGrille[i][j]=true;
		break;
	    case JEUTUILE_UR:
		//DL
		if ((Grille[i-1][j] == JEUTUILE_DR || Grille[i-1][j] == JEUTUILE_DL || Grille[i-1][j] == JEUTUILE_SQUARE) &&
		    (Grille[i-1][j-1] == JEUTUILE_SQUARE || Grille[i-1][j-1] == JEUTUILE_UR) &&
		    (Grille[i][j-1] == JEUTUILE_UL || Grille[i][j-1] == JEUTUILE_DL || Grille[i][j-1] == JEUTUILE_SQUARE) &&
		    (Grille[i-1][j] != Grille[i][j-1] || Grille[i-1][j] == JEUTUILE_SQUARE))
		    CoinGrille[i][j]=true;
		//UL POINTE
		if ((Grille[i][j+1] != JEUTUILE_DR && Grille[i][j+1] != JEUTUILE_UR && Grille[i][j+1] != JEUTUILE_NONE) &&
		    (Grille[i-1][j+1] == JEUTUILE_SQUARE || Grille[i-1][j+1] == JEUTUILE_DR) &&
		    ((Grille[i][j+1] == JEUTUILE_SQUARE || Grille[i][j+1] == JEUTUILE_DL) ||
		     (Grille[i-1][j] == JEUTUILE_SQUARE || Grille[i-1][j] == JEUTUILE_UR)))
		    CoinGrille[i][j+1]=true;
		//DR POINTE
		if ((Grille[i+1][j] != JEUTUILE_UL && Grille[i+1][j] != JEUTUILE_UR && Grille[i+1][j] != JEUTUILE_NONE) &&
		    (Grille[i+1][j-1] == JEUTUILE_SQUARE || Grille[i+1][j-1] == JEUTUILE_UL) &&
		    ((Grille[i][j-1] == JEUTUILE_SQUARE || Grille[i][j-1] == JEUTUILE_UR) ||
		     (Grille[i+1][j] == JEUTUILE_SQUARE || Grille[i+1][j] == JEUTUILE_DL)))
		    CoinGrille[i+1][j]=true;
		break;
	    case JEUTUILE_DR:
		//UL
		if ((Grille[i-1][j] == JEUTUILE_UL || Grille[i-1][j] == JEUTUILE_UR || Grille[i-1][j] == JEUTUILE_SQUARE) &&
		    (Grille[i-1][j+1] == JEUTUILE_SQUARE || Grille[i-1][j+1] == JEUTUILE_DR) &&
		    (Grille[i][j+1] == JEUTUILE_UL || Grille[i][j+1] == JEUTUILE_DL || Grille[i][j+1] == JEUTUILE_SQUARE) &&
		    (Grille[i-1][j] != Grille[i][j+1] || Grille[i-1][j] == JEUTUILE_SQUARE))
		    CoinGrille[i][j+1]=true;
		//UR POINTE
		if ((Grille[i+1][j] != JEUTUILE_DL && Grille[i+1][j] != JEUTUILE_DR && Grille[i+1][j] != JEUTUILE_NONE) &&
		    (Grille[i+1][j+1] == JEUTUILE_SQUARE || Grille[i+1][j+1] == JEUTUILE_DL) &&
		    ((Grille[i][j+1] == JEUTUILE_SQUARE || Grille[i][j+1] == JEUTUILE_DR) ||
		     (Grille[i+1][j] == JEUTUILE_SQUARE || Grille[i+1][j] == JEUTUILE_UL)))
		    CoinGrille[i+1][j+1]=true;
		//DL POINTE
		if ((Grille[i][j-1] != JEUTUILE_UR && Grille[i][j-1] != JEUTUILE_DR && Grille[i][j-1] != JEUTUILE_NONE) &&
		    (Grille[i-1][j-1] == JEUTUILE_SQUARE || Grille[i-1][j-1] == JEUTUILE_UR) &&
		    ((Grille[i][j-1] == JEUTUILE_SQUARE || Grille[i][j-1] == JEUTUILE_UL) ||
		     (Grille[i-1][j] == JEUTUILE_SQUARE || Grille[i-1][j] == JEUTUILE_DR)))
		    CoinGrille[i][j]=true;
		break;
	    case JEUTUILE_DL:
		//UR
		if ((Grille[i+1][j] == JEUTUILE_UL || Grille[i+1][j] == JEUTUILE_UR || Grille[i+1][j] == JEUTUILE_SQUARE) &&
		    (Grille[i+1][j+1] == JEUTUILE_SQUARE || Grille[i+1][j+1] == JEUTUILE_DL) &&
		    (Grille[i][j+1] == JEUTUILE_DR || Grille[i][j+1] == JEUTUILE_UR || Grille[i][j+1] == JEUTUILE_SQUARE) &&
		    (Grille[i+1][j] != Grille[i][j+1] || Grille[i+1][j] == JEUTUILE_SQUARE))
		    CoinGrille[i+1][j+1]=true;
		//UL POINTE
		if ((Grille[i-1][j] != JEUTUILE_DL && Grille[i-1][j] != JEUTUILE_DR && Grille[i-1][j] != JEUTUILE_NONE) &&
		    (Grille[i-1][j+1] == JEUTUILE_SQUARE || Grille[i-1][j+1] == JEUTUILE_DR) &&
		    ((Grille[i][j+1] == JEUTUILE_SQUARE || Grille[i][j+1] == JEUTUILE_DL) ||
		     (Grille[i-1][j] == JEUTUILE_SQUARE || Grille[i-1][j] == JEUTUILE_UR)))
		    CoinGrille[i][j+1]=true;
		//DR POINTE
		if ((Grille[i][j-1] != JEUTUILE_UL && Grille[i][j-1] != JEUTUILE_DL && Grille[i][j-1] != JEUTUILE_NONE) &&
		    (Grille[i+1][j-1] == JEUTUILE_SQUARE || Grille[i+1][j-1] == JEUTUILE_UL) &&
		    ((Grille[i][j-1] == JEUTUILE_SQUARE || Grille[i][j-1] == JEUTUILE_UR) ||
		     (Grille[i+1][j] == JEUTUILE_SQUARE || Grille[i+1][j] == JEUTUILE_DL)))
		    CoinGrille[i+1][j]=true;
		break;
	    }
	}
    }
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::getDepart
  
  Description :	Retourne le départ du terrain
  
  Paramètres :
  
  Valeur retournée :	CDepart* : Départ du terrain
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
CDepart* CTerrain::getDepart() const
{
    std::list<CObjet*>::const_iterator it;
    for (it = LesObjets.begin(); it != LesObjets.end(); ++it)
    {
	if ((*it)->GetType() == JEUOBJET_DEPART_UP ||
	    (*it)->GetType() == JEUOBJET_DEPART_RIGHT ||
	    (*it)->GetType() == JEUOBJET_DEPART_DOWN ||
	    (*it)->GetType() == JEUOBJET_DEPART_LEFT)
	{
	    return (CDepart*)(*it);
	}
    }
    return 0;
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::getTrou
  
  Description :	Retourne le trou du terrain
  
  Paramètres :
  
  Valeur retournée :	CTrou* : Trou du terrain
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
CTrou* CTerrain::getTrou() const
{
    std::list<CObjet*>::const_iterator it;
    for (it = LesObjets.begin(); it != LesObjets.end(); ++it)
    {
	if ((*it)->GetType() == JEUOBJET_TROU)
	{
	    return (CTrou*)(*it);
	}
    }
    return 0;
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::BallInWater
  
  Description :	Détermine si la balle est dans l'eau
  
  Paramètres :
  
  Valeur retournée :	bool : Si la balle est dans l'eau
  
  Par : 2004-3-16 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
bool CTerrain::BallInWater() const
{
    std::list<CObjet*>::const_iterator it;
    for (it = LesObjets.begin(); it != LesObjets.end(); ++it)
    {
	if ((*it)->GetType() == JEUOBJET_TROUDEAU)
	{
	    float Distance = (Balle->getPos()-(*it)->getPos()).GetNorm();
	    if (Distance <= 
		(((CTroudeau*)(*it))->getRayon()/TAILLE_TUILE))
	    {
		return true;
	    }
	}
    }
    return false;
}

/*------------------------------------------------------------------
  Fonction :	CTerrain::UpdateAnimations()
  
  Description :	Eau qui bouge
  
  Valeur retournée :	Void
  
  Par : 2004-4-6 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
void CTerrain::UpdateAnimations() const
{
    std::list<CObjet*>::const_iterator ItO;
    for (ItO = LesObjets.begin(); ItO != LesObjets.end(); ItO++)
    {
	//Met a jour l'animation du trou d'eau
	if ((*ItO)->GetType() == JEUOBJET_TROUDEAU)
	    (*ItO)->UpdateAnimations();
    }
}


#define TUILE_SUBDIV	2
#define DIV_X	5*GRILLE_W*TUILE_SUBDIV
#define DIV_Y	5*GRILLE_H*TUILE_SUBDIV
#define PROB 	50
#define HAUTEURAMP	10.0f

/*------------------------------------------------------------------
  Fonction :	CTerrain::AfficheEnv()
  
  Description :	Fonction qui affiche l'environnement autour du terrain

  Valeur retournée :	void
  
  Par : 2004-4-9 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
void CTerrain::AfficheEnv()
{
    int i, j;
    
    GLfloat mat_diffuse[] = {1.0, 1.0, 1.0, 1.0};
    GLfloat mat_specular[] = {0.7, 0.7, 0.7, 1.0};
    GLfloat mat_shininess[] = {30.0};
    GLfloat mat_ambient[] = { 0.8, 0.8, 0.8, 1.0 };
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
    
    //Affichage du quad au centre
#define REP_FLOOR_X 3
#define REP_FLOOR_Y 5
    float TmpW = ((float)GRILLE_W+2.0f);
    float TmpH = ((float)GRILLE_H+2.0f);
    
    
    glEnable(GL_STENCIL_TEST);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D,TexFloor.GetID());
    glEnable(GL_TEXTURE_GEN_S);
    glEnable(GL_TEXTURE_GEN_T);
    GLfloat stable[4] = {(1.0/TmpW)*(float)REP_FLOOR_X,0.0,0.0,0.0};
    GLfloat ttable[4] = {0.0,(1.0 / TmpH)*(float)REP_FLOOR_Y,0.0,0.0};
    glTexGenfv(GL_S,GL_OBJECT_PLANE,stable);
    glTexGenfv(GL_T,GL_OBJECT_PLANE,ttable);
    glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
    glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glMatrixMode(GL_TEXTURE);
    glPushMatrix();
    glTranslatef(-0.5,-0.5,0);
    glStencilFunc(GL_NOTEQUAL, 0x1, 0x1);
    for (i = 0; i < SUBDIV*(GRILLE_H+2); i++)
    {
	for (j = 0; j < SUBDIV*(GRILLE_W+2); j++)
	{
	    if (Grille[int(j/SUBDIV)][int(i/SUBDIV)] == JEUTUILE_NONE ||
		Grille[int(j/SUBDIV)][int(i/SUBDIV)] == JEUTUILE_UL ||
		Grille[int(j/SUBDIV)][int(i/SUBDIV)] == JEUTUILE_UR ||
		Grille[int(j/SUBDIV)][int(i/SUBDIV)] == JEUTUILE_DR ||
		Grille[int(j/SUBDIV)][int(i/SUBDIV)] == JEUTUILE_DL)
	    {
		glBegin(GL_QUADS);
		{
		    float PerX = (float)j / ((float)SUBDIV*((float)GRILLE_W+2.0f));
		    float PerY = (float)i / ((float)SUBDIV*((float)GRILLE_H+2.0f));
		    float PerX2 = ((float)j+1.0f) / ((float)SUBDIV*((float)GRILLE_W+2.0f));
		    float PerY2 = ((float)i+1.0f) / ((float)SUBDIV*((float)GRILLE_H+2.0f));
		    
		    glVertex3f(-1.0f+PerX*TmpW,-1.0f+PerY*TmpH,-2*SAFE_DISP);
		    glVertex3f(-1.0f+PerX2*TmpW,-1.0f+PerY*TmpH,-2*SAFE_DISP);
		    glVertex3f(-1.0f+PerX2*TmpW,-1.0f+PerY2*TmpH,-2*SAFE_DISP);
		    glVertex3f(-1.0f+PerX*TmpW,-1.0f+PerY2*TmpH,-2*SAFE_DISP);
		}
		glEnd();
	    }
	}
    }
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glStencilFunc(GL_ALWAYS, 0x1, 0x1);
    glDisable(GL_TEXTURE_GEN_S);
    glDisable(GL_TEXTURE_GEN_T);
    glDisable(GL_TEXTURE_2D);
    glDisable(GL_STENCIL_TEST);
    
    float TmpHauteurs[DIV_X][DIV_Y];
    float Hauteurs[DIV_X][DIV_Y];
    CVector3f Normales[DIV_X][DIV_Y];
    
    for (i = 0; i < DIV_X; i++)
	for (j = 0; j < DIV_Y; j++)
	{
	if (!(rand() % PROB))
	{
	    TmpHauteurs[i][j] =(rand()/ (float)RAND_MAX - 0.5f) * PROB;
	}
	else
	{
	    TmpHauteurs[i][j] = 0;
	}
    }
    
    for (int k = 0; k < 50; k++)
    {
	for (i = 1; i < DIV_X-1; i++)
	    for (j = 1; j < DIV_Y-1; j++)
	    {
	    Hauteurs[i][j] = (TmpHauteurs[i][j]+
			      TmpHauteurs[i-1][j]+
			      TmpHauteurs[i-1][j-1]+
			      TmpHauteurs[i-1][j+1]+
			      TmpHauteurs[i+1][j]+
			      TmpHauteurs[i+1][j-1]+
			      TmpHauteurs[i+1][j+1]+
			      TmpHauteurs[i][j-1]+
			      TmpHauteurs[i][j+1])/9.0f;
	}
	
	for (i = 1; i < DIV_X-1; i++)
	{
	    Hauteurs[0][i] = (TmpHauteurs[0][i]+
			      TmpHauteurs[0][i-1]+
			      TmpHauteurs[0][i+1]+
			      TmpHauteurs[1][i]+
			      TmpHauteurs[1][i-1]+
			      TmpHauteurs[1][i+1])/6.0f;
	    Hauteurs[DIV_X-1][i] = (TmpHauteurs[DIV_X-1][i]+
				    TmpHauteurs[DIV_X-1][i-1]+
				    TmpHauteurs[DIV_X-1][i+1]+
				    TmpHauteurs[DIV_X-2][i]+
				    TmpHauteurs[DIV_X-2][i-1]+
				    TmpHauteurs[DIV_X-2][i+1])/6.0f;
	    Hauteurs[i][0] = (TmpHauteurs[i][0]+
			      TmpHauteurs[i-1][0]+
			      TmpHauteurs[i+1][0]+
			      TmpHauteurs[i][1]+
			      TmpHauteurs[i-1][1]+
			      TmpHauteurs[i+1][1])/6.0f;
	    Hauteurs[i][DIV_Y-1] = (TmpHauteurs[i][DIV_Y-1]+
				    TmpHauteurs[i-1][DIV_Y-1]+
				    TmpHauteurs[i+1][DIV_Y-1]+
				    TmpHauteurs[i][DIV_Y-2]+
				    TmpHauteurs[i-1][DIV_Y-2]+
				    TmpHauteurs[i+1][DIV_Y-2])/6.0f;
	}
	
	Hauteurs[0][0] = (TmpHauteurs[0][0]+
			  TmpHauteurs[1][0]+
			  TmpHauteurs[0][1]+
			  TmpHauteurs[1][1])/4.0f;
	Hauteurs[0][DIV_Y-1] = (TmpHauteurs[0][DIV_Y-1]+
				TmpHauteurs[0][DIV_Y-2]+
				TmpHauteurs[1][DIV_Y-1]+
				TmpHauteurs[1][DIV_Y-2])/4.0f;
	Hauteurs[DIV_X-1][0] = (TmpHauteurs[DIV_X-1][0]+
				TmpHauteurs[DIV_X-1][1]+
				TmpHauteurs[DIV_X-2][0]+
				TmpHauteurs[DIV_X-2][1])/4.0f;
	Hauteurs[DIV_X-1][DIV_Y-1] = (TmpHauteurs[DIV_X-1][DIV_Y-1]+
				      TmpHauteurs[DIV_X-1][DIV_Y-2]+
				      TmpHauteurs[DIV_X-2][DIV_Y-1]+
				      TmpHauteurs[DIV_X-2][DIV_Y-2])/4.0f;
	
	for (i = 0; i < (GRILLE_W + 2)*TUILE_SUBDIV + 1; i++)
	    for (j = 0; j < (GRILLE_H + 2)*TUILE_SUBDIV + 1; j++)
	    {
	    Hauteurs[i+DIV_X / 2 - (GRILLE_W+2)*TUILE_SUBDIV / 2]
		    [j+DIV_Y / 2 - (GRILLE_H+2)*TUILE_SUBDIV / 2] = 0;
	}
	
	for (i = 0; i < DIV_X; i++)
	    for (j = 0; j < DIV_Y; j++)
		TmpHauteurs[i][j] = Hauteurs[i][j];
	
    }
    
    
    for (i = 1; i < DIV_X-1; i++)
	for (j = 1; j < DIV_Y-1; j++)
	{
	Normales[i][j].setX((Hauteurs[i-1][j] - Hauteurs[i+1][j]) * DIV_X / 2);
	Normales[i][j].setY((Hauteurs[i][j-1] - Hauteurs[i][j+1]) * DIV_Y / 2);
	Normales[i][j].setZ(1);
	Normales[i][j] = Normales[i][j].GetNormalVector();
    }
    
    
    float Montagne[] = {0.4,0.4,0.4, 1.0};
    float MontagneAm[] = {0.5,0.5,0.5, 1.0};
    float MontagneSpec[] = {0.2,0.2,0.2, 1.0};
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, MontagneSpec);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, Montagne);
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, MontagneAm);
    
    //Quads au cas ou la caméra passe au travers d'une montagne...
    glPushMatrix();
    glTranslatef(0, 0, -2*SAFE_DISP);
    
    glDisable(GL_LIGHTING);
    glColor4f(0.0f, 0.0f, 0.0f, 1.0f );
    glBegin(GL_QUADS);
    glVertex3f(-2*GRILLE_W, -2*GRILLE_H,-5);
    glVertex3f(-2*GRILLE_W, 3*GRILLE_H,-5);
    glVertex3f(3*GRILLE_W, 3*GRILLE_H,-5);
    glVertex3f(3*GRILLE_W, -2*GRILLE_W,-5);
    glEnd();
    
    float x1, y1, x2, y2;
    
    for (i = 1; i < DIV_X -2; i++)
    {
	x1 = (float) i / (float) TUILE_SUBDIV + GRILLE_W / 2.0f - (float) DIV_X / (float) TUILE_SUBDIV / 2.0f;
	y1 = (float) 1/ (float) TUILE_SUBDIV + GRILLE_H / 2.0f - (float) DIV_Y / (float) TUILE_SUBDIV / 2.0f;
	x2 = x1 + 1 / (float) TUILE_SUBDIV;
	y2 = (float) (DIV_Y - 2) / (float) TUILE_SUBDIV + GRILLE_H / 2.0f - (float) DIV_Y / (float) TUILE_SUBDIV / 2.0f;
	    
	glBegin(GL_QUADS);
	glVertex3f(x1, y1, Hauteurs[i][1]*HAUTEURAMP);
	glVertex3f(x2, y1, Hauteurs[i+1][1]*HAUTEURAMP);
	glVertex3f(x2, y1, -5);
	glVertex3f(x1, y1, -5);
	glEnd();
	glBegin(GL_QUADS);
	glVertex3f(x1, y2, Hauteurs[i][DIV_Y-2]*HAUTEURAMP);
	glVertex3f(x2, y2, Hauteurs[i+1][DIV_Y-2]*HAUTEURAMP);
	glVertex3f(x2, y2, -5);
	glVertex3f(x1, y2, -5);
	glEnd();
    }

    for (j = 1; j < DIV_Y -2; j++)
    {
	x1 = (float) 1 / (float) TUILE_SUBDIV + GRILLE_W / 2.0f - (float) DIV_X / (float) TUILE_SUBDIV / 2.0f;
	y1 = (float) j/ (float) TUILE_SUBDIV + GRILLE_H / 2.0f - (float) DIV_Y / (float) TUILE_SUBDIV / 2.0f;
	x2 = (float) (DIV_X-2) / (float) TUILE_SUBDIV + GRILLE_W / 2.0f - (float) DIV_X / (float) TUILE_SUBDIV / 2.0f;
	y2 = y1 + 1 / (float) TUILE_SUBDIV;
	
	glBegin(GL_QUADS);
	glVertex3f(x1, y1, Hauteurs[1][j]*HAUTEURAMP);
	glVertex3f(x1, y2, Hauteurs[1][j+1]*HAUTEURAMP);
	glVertex3f(x1, y2, -5);
	glVertex3f(x1, y1, -5);
	glEnd();
	glBegin(GL_QUADS);
	glVertex3f(x2, y1, Hauteurs[DIV_X-2][j]*HAUTEURAMP);
	glVertex3f(x2, y2, Hauteurs[DIV_Y-2][j+1]*HAUTEURAMP);
	glVertex3f(x2, y2, -5);
	glVertex3f(x2, y1, -5);
	glEnd();
    }

    
    glEnable(GL_CULL_FACE);
    glEnable(GL_LIGHTING);
    glCullFace(GL_BACK);
    
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D,TexGazon.GetID());
    glEnable(GL_TEXTURE_GEN_S);
    glEnable(GL_TEXTURE_GEN_T);
    GLfloat stable2[4] = {0.5,0.0,0.0,0.0};
    GLfloat ttable2[4] = {0.0,0.5,0.0,0.0};
    glTexGenfv(GL_S,GL_OBJECT_PLANE,stable2);
    glTexGenfv(GL_T,GL_OBJECT_PLANE,ttable2);
    glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
    glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    for (i = 1; i < DIV_X-2; i++)
	for (j = 1; j < DIV_Y-2; j++)
	{
	if ((i < (DIV_X - (GRILLE_W+2)*TUILE_SUBDIV) / 2) ||
	    (i > (DIV_X + (GRILLE_W+2)*TUILE_SUBDIV) / 2 - 1) ||
	    (j < (DIV_Y - (GRILLE_H+2)*TUILE_SUBDIV) / 2) ||
	    (j > (DIV_Y + (GRILLE_H+2)*TUILE_SUBDIV) / 2 - 1) )
	{
	    glBegin(GL_QUADS);
	    glColor3f(1,1,1);
	    
	    x1 = (float) i / (float) TUILE_SUBDIV + GRILLE_W / 2.0f - (float) DIV_X / (float) TUILE_SUBDIV / 2.0f;
	    y1 = (float) j / (float) TUILE_SUBDIV + GRILLE_H / 2.0f - (float) DIV_Y / (float) TUILE_SUBDIV / 2.0f;
	    x2 = x1 + 1 / (float) TUILE_SUBDIV;
	    y2 = y1 + 1 / (float) TUILE_SUBDIV;
	    glNormal3f(Normales[i][j].GetPosX(),
		       Normales[i][j].GetPosY(),
		       Normales[i][j].GetPosZ());
	    glVertex3f(x1, y1, Hauteurs[i][j]*HAUTEURAMP);
	    
	    glNormal3f(Normales[i+1][j].GetPosX(),
		       Normales[i+1][j].GetPosY(),
		       Normales[i+1][j].GetPosZ());
	    glVertex3f(x2, y1, Hauteurs[i+1][j]*HAUTEURAMP);
	    
	    glNormal3f(Normales[i+1][j+1].GetPosX(),
		       Normales[i+1][j+1].GetPosY(),
		       Normales[i+1][j+1].GetPosZ());
	    glVertex3f(x2, y2, Hauteurs[i+1][j+1]*HAUTEURAMP);
	    
	    glNormal3f(Normales[i][j+1].GetPosX(),
		       Normales[i][j+1].GetPosY(),
		       Normales[i][j+1].GetPosZ());
	    glVertex3f(x1, y2, Hauteurs[i][j+1]*HAUTEURAMP);
	    glEnd();
	}
   }
    glDisable(GL_TEXTURE_GEN_T);
    glDisable(GL_TEXTURE_GEN_S);
    glDisable(GL_TEXTURE_2D);
    
    glDisable(GL_CULL_FACE);
    
    //Affichage du phare
    do
    {
	PhareX = (float) (rand() % DIV_X) / (float) TUILE_SUBDIV;
	PhareY = (float) (rand() % DIV_Y) / (float) TUILE_SUBDIV;
    }
    while ( (PhareX <= LARGEURPHARE) ||
	    (PhareX >= DIV_X / TUILE_SUBDIV - LARGEURPHARE) ||
	    (PhareY<= LARGEURPHARE) ||
	    (PhareY>= DIV_Y/ TUILE_SUBDIV - LARGEURPHARE) ||
	    (fabs(PhareX - DIV_X / TUILE_SUBDIV / 2) < GRILLE_W / 2 + 1 + LARGEURPHARE) ||
	    (fabs(PhareY - DIV_Y / TUILE_SUBDIV / 2) < GRILLE_H / 2 + 1 + LARGEURPHARE) );
    
    PhareZ = Hauteurs[(int) (PhareX * TUILE_SUBDIV)]
	     [(int) (PhareY * TUILE_SUBDIV)];
    
    for (i = (int)((PhareX - LARGEURPHARE / 2.0f) * (float)TUILE_SUBDIV)-1;
    i < (int)((PhareX + LARGEURPHARE / 2.0f) * (float)TUILE_SUBDIV)+1; i++)
    {
	for (j = (int)((PhareY - LARGEURPHARE / 2.0f) * (float)TUILE_SUBDIV)-1;
	j < (int)((PhareY + LARGEURPHARE / 2.0f) * (float)TUILE_SUBDIV)+1; j++)
	{
	    if (PhareZ > Hauteurs[i][j])
		PhareZ = Hauteurs[i][j];
	}
    }
    
    PhareZ *= HAUTEURAMP;
        
    PhareX = PhareX + GRILLE_W / 2.0f - (float) DIV_X / (float) TUILE_SUBDIV / 2.0f;
    PhareY = PhareY + GRILLE_H / 2.0f - (float) DIV_Y / (float) TUILE_SUBDIV / 2.0f;
    
    glTranslatef(PhareX, PhareY, PhareZ);
    glScalef(LARGEURPHARE / 2.0f, LARGEURPHARE / 2.0f, LARGEURPHARE / 2.0f);
    
    ConstruitPhare();
    
    glPopMatrix();
    
}

/*------------------------------------------------------------------
  Fonction :	ConstruitLumierePhare
  
  Description :	Lumiere dans le phare qui rotationne
  
  Valeur retournée :	void
  
  Par : 2004-4-9 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
void CTerrain::ConstruitLumierePhare(void) const
{
    GLUquadricObj *Quadobj;
    Quadobj = gluNewQuadric();
    
    gluQuadricDrawStyle(Quadobj, (GLenum) GLU_FILL);
    gluQuadricNormals(Quadobj, (GLenum) GLU_SMOOTH);
    gluQuadricTexture(Quadobj, (GLenum) GL_TRUE);
    
    float Phare[] = { 0.3f, 0.3f, 0.3f, 1.0f };
    float PhareAm[] = { 0.3f, 0.3f, 0.3f, 1.0f };
    float PhareSpec[] = { 1.0f, 1.0f, 1.0f, 1.0f };
    float mat_zero[] = { 0.0f, 0.0f, 0.0f, 1.0f };
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, PhareSpec);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, Phare);
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, PhareAm);
    glColor3f(1.0, 1.0, 1.0);
    
    glDisable(GL_TEXTURE_2D);
    
    gluCylinder(Quadobj, 0.1f, 0.1f, 1.0f, 10, 10);
    
    glPushMatrix();
    glTranslatef(0.0f, 0.0f, 1.0f);
    glRotatef(90, 0, 1, 0);
    gluCylinder(Quadobj, 0.3f, 0.5f, 0.7f, 10, 10);
    glRotatef(180, 0, 1, 0);
    gluCylinder(Quadobj, 0.3f, 0.5f, 0.7f, 10, 10);
    
    Phare[0] = Phare[1] = 0.8f; Phare[2] = 0.0f; Phare[3] = 1.0f;
    PhareAm[0] = PhareAm[1] = 1.0f; PhareAm[2] = 0.0f; PhareAm[3] = 1.0f;
    PhareSpec[0] = PhareSpec[1] = 1.0f; PhareSpec[2] = 0.0f; PhareSpec[3] = 1.0f;
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, Phare);
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, PhareAm);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, PhareSpec);
    glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, PhareAm);
    
    glTranslatef(0.0f, 0.0f, 0.7f);
    gluDisk(Quadobj, 0.0f, 0.5f, 10, 5);
    glTranslatef(0.0f, 0.0f, -1.4f);
    glScalef(1.0f, 1.0f, -1.0f);
    gluDisk(Quadobj, 0.0f, 0.5f, 10, 5);
    
    glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, mat_zero);
    
    glPopMatrix();
    glEnable(GL_TEXTURE_2D);
    
    gluDeleteQuadric(Quadobj);
}

/*------------------------------------------------------------------
  Fonction :	ConstruitPhare
  
  Description :	Fonction qui affiche le phare
  
  Valeur retournée :	void
  
  Par : 2004-4-9 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
void CTerrain::ConstruitPhare(void) const
{
    GLUquadricObj *Quadobj;
    Quadobj = gluNewQuadric();
        
    gluQuadricDrawStyle(Quadobj, (GLenum) GLU_FILL);
    gluQuadricNormals(Quadobj, (GLenum) GLU_SMOOTH);
    gluQuadricTexture(Quadobj, (GLenum) GL_TRUE);
    
    float Phare[] = { 0.8f, 0.8f, 0.8f, 1.0f };
    float PhareAm[] = { 1.0, 1.0f, 1.0f, 1.0f };
    float PhareSpec[] = { 1.0f, 1.0f, 1.0f, 1.0f };
    float mat_zero[] = { 0.0f, 0.0f, 0.0f, 1.0f };
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, PhareSpec);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, Phare);
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, PhareAm);
    glColor3f(1.0, 1.0, 1.0);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, TexPhare.GetID());
    
    glPushMatrix();
    
    // Grande tour
    gluCylinder(Quadobj, 2.0f, 1.5f, 11, 10, 10);
        
    // Petit disque
    glTranslatef(0.0f, 0.0f, 11);
    glScalef(1.0f, 1.0f, -1.0f);
    gluDisk(Quadobj, 0.0f, 2.0f, 10, 5);
    glScalef(1.0f, 1.0f, -1.0f);
    
    glMatrixMode(GL_TEXTURE);
    glPushMatrix();
    glScalef(1, 1.0f/11.0f, 1);
    gluCylinder(Quadobj, 2.0f, 2.0f, 1, 10, 10);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    
    glTranslatef(0.0f, 0.0f, 1);
    gluDisk(Quadobj, 0.0f, 2.0f, 10, 5);
    
     // Cone d'en haut
    glTranslatef(0.0f, 0.0f, 2.0f);
    Phare[0] = 0.8f; Phare[1] = Phare[2] = 0.0f; Phare[3] = 1.0f;
    PhareAm[0] = 1.0f; PhareAm[1] = PhareAm[2] = 0.0f; PhareAm[3] = 1.0f;
    PhareSpec[0] = 1.0f; PhareSpec[1] = PhareSpec[2] = 0.0f; PhareSpec[3] = 1.0f;
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, PhareSpec);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, Phare);
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, PhareAm);
    
    gluCylinder(Quadobj, 2.0f, 0.0f, 1.5f, 10, 10);
    gluDisk(Quadobj, 0.0f, 2.0f, 10, 5);
    
    // Cylindre de vitre
    glDisable(GL_TEXTURE_2D);
    glDisable(GL_LIGHTING);
    
    glColor4f(0.8f, 0.8f, 0.8f, 0.3f);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
    glTranslatef(0.0f, 0.0f, -2.0f);
    
    glEnable(GL_CULL_FACE);
    gluCylinder(Quadobj, 1.5f, 1.5f, 2.0f, 10, 10);
    
    glDisable(GL_CULL_FACE);
    glEnable(GL_LIGHTING);
    glEnable(GL_TEXTURE_2D);
    
    glPopMatrix();
    
    gluDeleteQuadric(Quadobj);
    
}

/*------------------------------------------------------------------
  Fonction :	TournerPhare
  
  Description :	Fonction qui fait tourner la lumiere du phare
  
  Valeur retournée :	void
  
  Par : 2004-4-9 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
void CTerrain::TournerPhare()
{
    AngleRotPhare += 0.54f;
    if (AngleRotPhare > 360.0f) AngleRotPhare -= 360.0f;
}

/*------------------------------------------------------------------
  Fonction :	AfficheLumierePhare
  
  Description :	Fonction qui fait afficher la lumiere du phare
  
  Valeur retournée :	void
  
  Par : 2004-4-9 Martin Bisson, Anne-Marie Buki, Mylene Denis & Jean-Francois Pérusse
  MAJ: 
------------------------------------------------------------------*/
void CTerrain::AfficheLumierePhare()
{
    //position des lumieres du phare
    glPushMatrix();
    GLfloat lumPhare_position[] = {0.0, 0.0, 0.0, 1.0};
    glTranslatef(PhareX, PhareY, PhareZ);
    glScalef(LARGEURPHARE / 2.0f, LARGEURPHARE / 2.0f, LARGEURPHARE / 2.0f);
    glTranslatef(0.0f, 0.0f, 13);
    glLightfv(GL_LIGHT2, GL_POSITION, lumPhare_position);
    glLightfv(GL_LIGHT3, GL_POSITION, lumPhare_position);
    glPopMatrix();
    
    //direction des faisceaux lumineux du phare
    glPushMatrix();
    //direction initiale de la lumiere dans sens axe des x, un peu penchee
    GLfloat lumPhare_spot_dir[] =  { 1.0, 0.0, -0.3};
    glRotatef(AngleRotPhare, 0.0, 0.0, 1.0);
    glLightfv(GL_LIGHT2, GL_SPOT_DIRECTION, lumPhare_spot_dir);
    //la seconde lumiere est decalee de 180 degres par rapport a la premiere
    glRotatef(180.0, 0.0, 0.0, 1.0);	
    glLightfv(GL_LIGHT3, GL_SPOT_DIRECTION, lumPhare_spot_dir);
    glPopMatrix();
}
