/****************************************************************************

   Copyright (C) 2002-2008 Gilles Debunne. All rights reserved.

   This file is part of the QGLViewer library version 2.3.5.

   http://www.libqglviewer.com - contact@libqglviewer.com

   This file may be used under the terms of the GNU General Public License
   versions 2.0 or 3.0 as published by the Free Software Foundation and
   appearing in the LICENSE file included in the packaging of this file.
   In addition, as a special exception, Gilles Debunne gives you certain
   additional rights, described in the file GPL_EXCEPTION in this package.

   libQGLViewer uses dual licensing. Commercial/proprietary software must
   purchase a libQGLViewer Commercial License.

   This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
   WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

 *****************************************************************************/

// TP OpenGL: Joerg Liebelt, Serigne Sow
#include <stdio.h>
#include "quadtree.h"

#define SQR(number) (number * number)
#define CUBE(number) (number * number * number)
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) < (b)) ? (b) : (a))

// codes determinant le type de suite partielle de triangles a creer, selon la position dans le quadtree
static char suiteCode[] = { 10, 8, 8, 12, 8, 0, 12, 14, 8, 12, 0, 14, 12, 14, 14, 0 };
static char suiteStart[] = { 3, 3, 0, 3, 1, 0, 0, 3, 2, 2, 0, 2, 1, 1, 0, 0 };

static bool debugger = false;

bool QUADTREE::Init(void)
{
  int x, z;

  quadMatrix = new unsigned char[SQR(sizeHeightMap)];

  for(z = 0; z < sizeHeightMap; z++) {
    for(x = 0; x < sizeHeightMap; x++) {
      quadMatrix[GetMatrixIndex(x, z)] = 1;
    }
  }

  // repartir les triangles pour que les regions detailles/moins lisses obtiennent plus de triangles
  PropagateRoughness();
  return true;
}

void QUADTREE::Shutdown(void)
{
  if(quadMatrix)
    delete[] quadMatrix;
}

void QUADTREE::Update(float x, float y, float z)
{
  float center;
  setCameraPosition(x, y, z);
  // centre de la carte
  center = (sizeHeightMap) / 2.0f;

  // reconstruire le "grid" par traversee top-down du quadtree
  RefineNode(center, center, sizeHeightMap);
}

// rendering d'un seul vertex
// params u,v deja normalises par rapport a sizeHeightMap
void QUADTREE::RenderVertex(float x, float z, float u, float v, bool multiTextures)
{
  unsigned char color = GetBrightnessAtPoint((int)x, (int)z);
  if(debugger)
    printf("RenderVertex %f,%f - %f,%f\n", x, z, u, v);
  if(paintLighting) {
    glColor3ub((unsigned char)(color * rLight), (unsigned char)(color * gLight), (unsigned char)(color * bLight));
  } else
    glColor3ub(255, 255, 255);
  glMultiTexCoord2f(GL_TEXTURE0, u, v);
  if(multiTextures)
    glMultiTexCoord2f(GL_TEXTURE1, u * repeatDetailMap, v * repeatDetailMap);
  glVertex3f(x / sizeHeightMap * scaleSize, GetScaledHeightAtPoint((int)x, (int)z) / sizeHeightMap,
             z / sizeHeightMap * scaleSize);
}

// debut du rendering recursif
void QUADTREE::Render(void)
{
  float center;
  if(debugger)
    printf("Render\n");
  // centre de la carte
  center = (sizeHeightMap) / 2.0f;

  // on fait le culling a travers le quadtree, pas avec le hardware
  glDisable(GL_CULL_FACE);

  // on a multitextures et on souhaite les afficher
  if(haveMultitexture && paintTextures) {
    glDisable(GL_BLEND);     // pas de combinaison de couleurs avec MULTITEXTURES,
    //.. la combi des deux textures se fait automatiquement en hardware

    // selectionner comme premiere unite de texture la texture de base (couleur selon hauteur)
    glActiveTexture(GL_TEXTURE0);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, textureColorID);

    // selectionner commer deuxieme unite de texture la texture de detail (structure)
    glActiveTexture(GL_TEXTURE1);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, textureDetailID);

    // definir la maniere dont les couleurs des triangles sont crees en fct. des couleurs des textures
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);     // mode de combination des deux
    glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, 2);                     // augmenter la luminosite des couleurs

    // render a partir du noeud principal au milieu de la carte (recursif a partir d'ici)
    RenderNode(center, center, sizeHeightMap, true, true);
  }

  // on a pas de multitextures mais on souhaite des textures
  else if(paintTextures) {
    // PREMIER PARCOURS: couleurs
    // selectionner comme unite de texture la texture de base (couleur selon hauteur)
    glActiveTexture(GL_TEXTURE0);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, textureColorID);

    RenderNode(center, center, sizeHeightMap, false, false);

    // DEUXIEME PARCOURS: DETAIL
    // preparer la texture de detail
    glActiveTexture(GL_TEXTURE0);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, textureDetailID);

    // activer la combinaison entre couleur existant (premier parcours) et couleur ajoutee (detail)
    glEnable(GL_BLEND);
    glBlendFunc(GL_ZERO, GL_SRC_COLOR);

    RenderNode(center, center, sizeHeightMap, false, true);

    glDisable(GL_BLEND);
  }

  else   // pas de textures du tout
  {
    RenderNode(center, center, sizeHeightMap, false, false);
    // sinon rendering brute force?
  }

  // liberer la deuxieme texture
  glActiveTexture(GL_TEXTURE1);
  glDisable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, 0);

  // liberer la premiere texture
  glActiveTexture(GL_TEXTURE0);
  glDisable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, 0);
}

// gerer la repartition des triangles sur les parties moins lisses / plus detaillees
void QUADTREE::PropagateRoughness(void)
{
  float upperBound;
  int dH, d2, localD2, localH;
  int edgeLength, edgeOffset;
  int childOffset;
  int x, z;
  if(debugger)
    printf("PropagateRoughness\n");
  // commencer au niveau de detail le plus elevee (taille de bloc la plus petite)
  edgeLength = 2;
  // jusqu'au niveau de detail le moins elevee (taille de bloc la plus petite)
  // un bloc est defini par edgeLength*edgeLength
  //.. ==> bottom-up!
  while(edgeLength <= sizeHeightMap) {
    edgeOffset = (edgeLength) >> 1;

    childOffset = (edgeLength) >> 2;
    // D2: valeur qui indique la difference maximale entre l'hauteur reelle et
    //.. l'hauteur moyenne autour du point (calculee comme moyenne de deux points)
    // pour chaque noeud, calculer D2 a 9 endroits (milieu + 8 autour, sur un rectangle) pour 5 valeurs
    for(z = edgeOffset; z < sizeHeightMap - edgeOffset; z += (edgeLength)) {
      for(x = edgeOffset; x < sizeHeightMap - edgeOffset; x += (edgeLength)) {
        // haut, milieu
        localD2 = (int)ceil((int)(abs(((GetTrueHeightAtPoint(x - edgeOffset, z + edgeOffset)
                                        + GetTrueHeightAtPoint(x + edgeOffset, z + edgeOffset)) >> 1)
                                      - GetTrueHeightAtPoint(x, z + edgeOffset))));
        // a droite, milieu
        dH = (int)ceil(abs(((GetTrueHeightAtPoint(x + edgeOffset, z + edgeOffset)
                             + GetTrueHeightAtPoint(x + edgeOffset, z - edgeOffset)) >> 1)
                           - GetTrueHeightAtPoint(x + edgeOffset, z)));
        localD2 = MAX(localD2, dH);

        // en bas, milieu
        dH = (int)ceil(abs(((GetTrueHeightAtPoint(x - edgeOffset, z - edgeOffset)
                             + GetTrueHeightAtPoint(x + edgeOffset, z - edgeOffset)) >> 1)
                           - GetTrueHeightAtPoint(x, z - edgeOffset)));
        localD2 = MAX(localD2, dH);

        // a gauche, milieu
        dH = (int)ceil(abs(((GetTrueHeightAtPoint(x - edgeOffset, z + edgeOffset)
                             + GetTrueHeightAtPoint(x - edgeOffset, z - edgeOffset)) >> 1)
                           - GetTrueHeightAtPoint(x - edgeOffset, z)));
        localD2 = MAX(localD2, dH);

        // deux points sur la diagonale de bas gauche a haut droite
        dH = (int)ceil(
            abs(((GetTrueHeightAtPoint(x - edgeOffset, z - edgeOffset)
                  + GetTrueHeightAtPoint(x + edgeOffset, z + edgeOffset)) >> 1) - GetTrueHeightAtPoint(x, z)));
        localD2 = MAX(localD2, dH);

        // deux points sur la diagonale de bas droite a haut gauche
        dH = (int)ceil(
            abs(((GetTrueHeightAtPoint(x + edgeOffset, z - edgeOffset)
                  + GetTrueHeightAtPoint(x - edgeOffset, z + edgeOffset)) >> 1) - GetTrueHeightAtPoint(x, z)));
        localD2 = MAX(localD2, dH);
        // localD2 de 0 a 255, normaliser par la taille du bloc actuelle (de 3 a sizeHeightMap)
        // ... donc multiplier par 3 pour obtenir une precision maximale de 0 a 255 pour d2
        localD2 = (int)ceil((localD2 * 3.0f) / edgeLength);
        // sur le niveau le plus bas, on calcule l'hauteur reelle maximale a la main
        if(edgeLength == 2) {
          d2 = localD2;
          // haut, droite
          localH = GetTrueHeightAtPoint(x + edgeOffset, z + edgeOffset);
          // milieu, droite
          localH = MAX(localH, GetTrueHeightAtPoint(x + edgeOffset, z));
          // bas, droite
          localH = MAX(localH, GetTrueHeightAtPoint(x + edgeOffset, z - edgeOffset));
          // bas milieu
          localH = MAX(localH, GetTrueHeightAtPoint(x, z - edgeOffset));
          // bas gauche
          localH = MAX(localH, GetTrueHeightAtPoint(x - edgeOffset, z - edgeOffset));
          // gauche milieu
          localH = MAX(localH, GetTrueHeightAtPoint(x - edgeOffset, z));
          // gauche haut
          localH = MAX(localH, GetTrueHeightAtPoint(x - edgeOffset, z + edgeOffset));
          // haut milieu
          localH = MAX(localH, GetTrueHeightAtPoint(x, z + edgeOffset));
          // centre
          localH = MAX(localH, GetTrueHeightAtPoint(x, z));
          // sauvegarder ce valeur dans la matrice (pour les pas recursifs suivants)
          quadMatrix[GetMatrixIndex(x + 1, z)] = localH;
        }
        // sur les autres niveaux, on utilise la valeur sauvegarde dans la matrice
        else {
          upperBound = 1.0f * minResolution / (2.0f * (minResolution - 1.0f));

          d2 = (int)ceil(MAX(upperBound * (float)GetQuadMatrixData(x, z), (float)localD2));
          d2 = (int)ceil(MAX(upperBound * (float)GetQuadMatrixData(x - edgeOffset, z), (float)d2));
          d2 = (int)ceil(MAX(upperBound * (float)GetQuadMatrixData(x + edgeOffset, z), (float)d2));
          d2 = (int)ceil(MAX(upperBound * (float)GetQuadMatrixData(x, z + edgeOffset), (float)d2));
          d2 = (int)ceil(MAX(upperBound * (float)GetQuadMatrixData(x, z - edgeOffset), (float)d2));

          localH = GetTrueHeightAtPoint(x + childOffset, z + childOffset);
          localH = MAX(localH, GetTrueHeightAtPoint(x + childOffset, z - childOffset));
          localH = MAX(localH, GetTrueHeightAtPoint(x - childOffset, z - childOffset));
          localH = MAX(localH, GetTrueHeightAtPoint(x - childOffset, z + childOffset));

          quadMatrix[GetMatrixIndex(x + 1, z)] = localH;
        }

        // sauvegarder les valeurs d2 dans la matrice
        quadMatrix[GetMatrixIndex(x, z)] = d2;
        quadMatrix[GetMatrixIndex(x - 1, z)] = d2;

        // la valeur se propage vers les noeuds voisins
        quadMatrix[GetMatrixIndex(x - edgeOffset, z - edgeOffset)]
          = MAX(GetQuadMatrixData(x - edgeOffset, z - edgeOffset), d2);
        quadMatrix[GetMatrixIndex(x - edgeOffset, z + edgeOffset)]
          = MAX(GetQuadMatrixData(x - edgeOffset, z + edgeOffset), d2);
        quadMatrix[GetMatrixIndex(x + edgeOffset, z + edgeOffset)]
          = MAX(GetQuadMatrixData(x + edgeOffset, z + edgeOffset), d2);
        quadMatrix[GetMatrixIndex(x + edgeOffset, z - edgeOffset)]
          = MAX(GetQuadMatrixData(x + edgeOffset, z - edgeOffset), d2);
      }
    }
    // monter un niveau (elargir la taille d'un bloc, reduire le niveau de detail)
    edgeLength = (edgeLength << 1);
  }
}

// on determine la region capturee par la camera (en fct. de modelview et projection)
// idee pour le code: Chris Cookson, gamersdev.net
void QUADTREE::ComputeView(void)
{
  float projMatrix[16];   // projection matrice
  float modMatrix[16];    // modelview matrice
  float clip[16];
  float norm;
  if(debugger)
    printf("ComputeView\n");
  /* opengl: matrice de proj.*/
  glGetFloatv(GL_PROJECTION_MATRIX, projMatrix);

  /* opengl: matrice de vue*/
  glGetFloatv(GL_MODELVIEW_MATRIX, modMatrix);

  /* multiplication vue*proj*/
  clip[0] = modMatrix[0] * projMatrix[0] + modMatrix[1] * projMatrix[4] + modMatrix[2] * projMatrix[8]
    + modMatrix[3] * projMatrix[12];
  clip[1] = modMatrix[0] * projMatrix[1] + modMatrix[1] * projMatrix[5] + modMatrix[2] * projMatrix[9]
    + modMatrix[3] * projMatrix[13];
  clip[2] = modMatrix[0] * projMatrix[2] + modMatrix[1] * projMatrix[6] + modMatrix[2] * projMatrix[10]
    + modMatrix[3] * projMatrix[14];
  clip[3] = modMatrix[0] * projMatrix[3] + modMatrix[1] * projMatrix[7] + modMatrix[2] * projMatrix[11]
    + modMatrix[3] * projMatrix[15];

  clip[4] = modMatrix[4] * projMatrix[0] + modMatrix[5] * projMatrix[4] + modMatrix[6] * projMatrix[8]
    + modMatrix[7] * projMatrix[12];
  clip[5] = modMatrix[4] * projMatrix[1] + modMatrix[5] * projMatrix[5] + modMatrix[6] * projMatrix[9]
    + modMatrix[7] * projMatrix[13];
  clip[6] = modMatrix[4] * projMatrix[2] + modMatrix[5] * projMatrix[6] + modMatrix[6] * projMatrix[10]
    + modMatrix[7] * projMatrix[14];
  clip[7] = modMatrix[4] * projMatrix[3] + modMatrix[5] * projMatrix[7] + modMatrix[6] * projMatrix[11]
    + modMatrix[7] * projMatrix[15];

  clip[8] = modMatrix[8] * projMatrix[0] + modMatrix[9] * projMatrix[4] + modMatrix[10] * projMatrix[8]
    + modMatrix[11] * projMatrix[12];
  clip[9] = modMatrix[8] * projMatrix[1] + modMatrix[9] * projMatrix[5] + modMatrix[10] * projMatrix[9]
    + modMatrix[11] * projMatrix[13];
  clip[10] = modMatrix[8] * projMatrix[2] + modMatrix[9] * projMatrix[6] + modMatrix[10] * projMatrix[10]
    + modMatrix[11] * projMatrix[14];
  clip[11] = modMatrix[8] * projMatrix[3] + modMatrix[9] * projMatrix[7] + modMatrix[10] * projMatrix[11]
    + modMatrix[11] * projMatrix[15];

  clip[12] = modMatrix[12] * projMatrix[0] + modMatrix[13] * projMatrix[4] + modMatrix[14] * projMatrix[8]
    + modMatrix[15] * projMatrix[12];
  clip[13] = modMatrix[12] * projMatrix[1] + modMatrix[13] * projMatrix[5] + modMatrix[14] * projMatrix[9]
    + modMatrix[15] * projMatrix[13];
  clip[14] = modMatrix[12] * projMatrix[2] + modMatrix[13] * projMatrix[6] + modMatrix[14] * projMatrix[10]
    + modMatrix[15] * projMatrix[14];
  clip[15] = modMatrix[12] * projMatrix[3] + modMatrix[13] * projMatrix[7] + modMatrix[14] * projMatrix[11]
    + modMatrix[15] * projMatrix[15];

  /* plan droite*/
  viewMatrix[VIEW_RIGHT][0] = clip[3] - clip[0];
  viewMatrix[VIEW_RIGHT][1] = clip[7] - clip[4];
  viewMatrix[VIEW_RIGHT][2] = clip[11] - clip[8];
  viewMatrix[VIEW_RIGHT][3] = clip[15] - clip[12];

  // normaliser
  norm = sqrtf(SQR(viewMatrix[VIEW_RIGHT][0]) + SQR(viewMatrix[VIEW_RIGHT][1]) + SQR(viewMatrix[VIEW_RIGHT][2]));
  viewMatrix[VIEW_RIGHT][0] /= norm;
  viewMatrix[VIEW_RIGHT][1] /= norm;
  viewMatrix[VIEW_RIGHT][2] /= norm;
  viewMatrix[VIEW_RIGHT][3] /= norm;

  /* plan gauche*/
  viewMatrix[VIEW_LEFT][0] = clip[3] + clip[0];
  viewMatrix[VIEW_LEFT][1] = clip[7] + clip[4];
  viewMatrix[VIEW_LEFT][2] = clip[11] + clip[8];
  viewMatrix[VIEW_LEFT][3] = clip[15] + clip[12];

  // normaliser
  norm = sqrtf(SQR(viewMatrix[VIEW_LEFT][0]) + SQR(viewMatrix[VIEW_LEFT][1]) + SQR(viewMatrix[VIEW_LEFT][2]));
  viewMatrix[VIEW_LEFT][0] /= norm;
  viewMatrix[VIEW_LEFT][1] /= norm;
  viewMatrix[VIEW_LEFT][2] /= norm;
  viewMatrix[VIEW_LEFT][3] /= norm;

  /* plan bas*/
  viewMatrix[VIEW_BOTTOM][0] = clip[3] + clip[1];
  viewMatrix[VIEW_BOTTOM][1] = clip[7] + clip[5];
  viewMatrix[VIEW_BOTTOM][2] = clip[11] + clip[9];
  viewMatrix[VIEW_BOTTOM][3] = clip[15] + clip[13];

  // normaliser
  norm = sqrtf(SQR(viewMatrix[VIEW_BOTTOM][0]) + SQR(viewMatrix[VIEW_BOTTOM][1]) + SQR(viewMatrix[VIEW_BOTTOM][2]));
  viewMatrix[VIEW_BOTTOM][0] /= norm;
  viewMatrix[VIEW_BOTTOM][1] /= norm;
  viewMatrix[VIEW_BOTTOM][2] /= norm;
  viewMatrix[VIEW_BOTTOM][3] /= norm;

  /* plan haut*/
  viewMatrix[VIEW_TOP][0] = clip[3] - clip[1];
  viewMatrix[VIEW_TOP][1] = clip[7] - clip[5];
  viewMatrix[VIEW_TOP][2] = clip[11] - clip[9];
  viewMatrix[VIEW_TOP][3] = clip[15] - clip[13];

  // normaliser
  norm = sqrtf(SQR(viewMatrix[VIEW_TOP][0]) + SQR(viewMatrix[VIEW_TOP][1]) + SQR(viewMatrix[VIEW_TOP][2]));
  viewMatrix[VIEW_TOP][0] /= norm;
  viewMatrix[VIEW_TOP][1] /= norm;
  viewMatrix[VIEW_TOP][2] /= norm;
  viewMatrix[VIEW_TOP][3] /= norm;

  /* plan loin*/
  viewMatrix[VIEW_FAR][0] = clip[3] - clip[2];
  viewMatrix[VIEW_FAR][1] = clip[7] - clip[6];
  viewMatrix[VIEW_FAR][2] = clip[11] - clip[10];
  viewMatrix[VIEW_FAR][3] = clip[15] - clip[14];

  // normaliser
  norm = sqrtf(SQR(viewMatrix[VIEW_FAR][0]) + SQR(viewMatrix[VIEW_FAR][1]) + SQR(viewMatrix[VIEW_FAR][2]));
  viewMatrix[VIEW_FAR][0] /= norm;
  viewMatrix[VIEW_FAR][1] /= norm;
  viewMatrix[VIEW_FAR][2] /= norm;
  viewMatrix[VIEW_FAR][3] /= norm;

  /* plan proche*/
  viewMatrix[VIEW_NEAR][0] = clip[3] + clip[2];
  viewMatrix[VIEW_NEAR][1] = clip[7] + clip[6];
  viewMatrix[VIEW_NEAR][2] = clip[11] + clip[10];
  viewMatrix[VIEW_NEAR][3] = clip[15] + clip[14];

  // normaliser
  norm = sqrtf(SQR(viewMatrix[VIEW_NEAR][0]) + SQR(viewMatrix[VIEW_NEAR][1]) + SQR(viewMatrix[VIEW_NEAR][2]));
  viewMatrix[VIEW_NEAR][0] /= norm;
  viewMatrix[VIEW_NEAR][1] /= norm;
  viewMatrix[VIEW_NEAR][2] /= norm;
  viewMatrix[VIEW_NEAR][3] /= norm;
}

// on teste si un cube est visible (entieremen/partiellement) par la camera
bool QUADTREE::CubeViewTest(float x, float y, float z, float size)
{
  int i;
  if(debugger)
    printf("CubeViewTest\n");
  // tester les six bords du cube contre l'intersections avec les plans de vue
  for(i = 0; i < 6; i++) {
    if(viewMatrix[i][0] * (x - size) + viewMatrix[i][1] * (y - size) + viewMatrix[i][2] * (z - size)
       + viewMatrix[i][3] > 0)
      continue;
    if(viewMatrix[i][0] * (x + size) + viewMatrix[i][1] * (y - size) + viewMatrix[i][2] * (z - size)
       + viewMatrix[i][3] > 0)
      continue;
    if(viewMatrix[i][0] * (x - size) + viewMatrix[i][1] * (y + size) + viewMatrix[i][2] * (z - size)
       + viewMatrix[i][3] > 0)
      continue;
    if(viewMatrix[i][0] * (x + size) + viewMatrix[i][1] * (y + size) + viewMatrix[i][2] * (z - size)
       + viewMatrix[i][3] > 0)
      continue;
    if(viewMatrix[i][0] * (x - size) + viewMatrix[i][1] * (y - size) + viewMatrix[i][2] * (z + size)
       + viewMatrix[i][3] > 0)
      continue;
    if(viewMatrix[i][0] * (x + size) + viewMatrix[i][1] * (y - size) + viewMatrix[i][2] * (z + size)
       + viewMatrix[i][3] > 0)
      continue;
    if(viewMatrix[i][0] * (x - size) + viewMatrix[i][1] * (y + size) + viewMatrix[i][2] * (z + size)
       + viewMatrix[i][3] > 0)
      continue;
    if(viewMatrix[i][0] * (x + size) + viewMatrix[i][1] * (y + size) + viewMatrix[i][2] * (z + size)
       + viewMatrix[i][3] > 0)
      continue;

    return false;
  }
  return true;
}

// decider si on sous-divise un noeud (ameliorer l'affichage si pret de la camera)
void QUADTREE::RefineNode(float x, float z, int edgeLength)
{
  float viewDistance, f;
  float childOffset;
  int childEdgeLength;
  int blend;
  if(debugger)
    printf("RefineNode: %f,%f:%d\n", x, z, edgeLength);

  // tester les bords d'un cube contenant le vertex actuel contre l'intersection avec la vue
  if(!CubeViewTest(x * scaleSize / sizeHeightMap, GetScaledHeightAtPoint((int)x, (int)z) / sizeHeightMap,
                   z * scaleSize / sizeHeightMap,
                   edgeLength * scaleSize / sizeHeightMap))   //*2
  {
    // desactiver ce noeud et ne plus continuer la recursion
    quadMatrix[GetMatrixIndex((int)x, (int)z)] = 0;
    return;
  }

  // calculer la distance entre le vertex et la camera, norme L1

  viewDistance = (float)(fabs(pX - (x * scaleSize / sizeHeightMap))
                         + fabs(pY - GetScaledHeightAtPoint((int)x + 1, (int)z) / sizeHeightMap)
                         + fabs(pZ - (z * scaleSize / sizeHeightMap)));

  // f: valeur qui decide si on subdivise un noeud ou non (selon l'article de Stefan Röttger sur le quadtree algo)
  f = viewDistance / ((float)edgeLength * minResolution *   /*/sizeHeightMap* */
                      MAX((float)GetQuadMatrixData((int)x - 1, (int)z) / 3 * detailLevel, 1.0f));
  if(debugger)
    printf("f: %f\n", f);
  if(f < 1.0f)
    blend = 255;
  else
    blend = 0;

  quadMatrix[GetMatrixIndex((int)x, (int)z)] = blend;

  if(blend != 0) {
    // si on a deja atteint le niveau de taille de noeud le plus petit: plus de sousdivision possible
    if(edgeLength <= 2)
      return;

    // sinon, continuer la recursion du quadtree
    else {
      childOffset = (float)((edgeLength) >> 2);
      childEdgeLength = (edgeLength) >> 1;

      // effectuer du "refinement" recursivement
      // bas gauche
      RefineNode(x - childOffset, z - childOffset, childEdgeLength);
      // bas droite
      RefineNode(x + childOffset, z - childOffset, childEdgeLength);
      // haut gauche
      RefineNode(x - childOffset, z + childOffset, childEdgeLength);
      // haut droite
      RefineNode(x + childOffset, z + childOffset, childEdgeLength);
      return;
    }
  }
}

// rendering (pas final!) du noeud (feuille) a la position x,z
void QUADTREE::RenderNode(float x, float z, int edgeLength, bool multiTextures, bool detail)
{
  float texLeft, texBottom, midX, midZ, texRight, texTop;
  float childOffset;
  float edgeOffset;
  int start, code;
  int childEdgeLength;
  int ichildOffset;
  int iedgeOffset;
  int adjOffset;
  int suiteLength;
  int suitePosition;
  int blend;
  int iX, iZ;

  iX = (int)x;
  iZ = (int)z;
  if(debugger)
    printf("RenderNode: %f,%f:%d\n", x, z, edgeLength);
  // noeud marque "desactive": ne pas render
  if(GetQuadMatrixData(iX, iZ) == 0)
    return;

  // offset: puissances de 2; la position dans le Quadtree
  iedgeOffset = (edgeLength) / 2;
  edgeOffset = (edgeLength) / 2.0f;

  // offset vers voisins
  adjOffset = edgeLength;   //-1;

  if(detail && !multiTextures) {
    // coord. des textures
    texLeft = ((float)fabs(x - edgeOffset) / sizeHeightMap) * repeatDetailMap;
    texBottom = ((float)fabs(z - edgeOffset) / sizeHeightMap) * repeatDetailMap;
    texRight = ((float)fabs(x + edgeOffset) / sizeHeightMap) * repeatDetailMap;
    texTop = ((float)fabs(z + edgeOffset) / sizeHeightMap) * repeatDetailMap;

    midX = ((texLeft + texRight) / 2.0f);
    midZ = ((texBottom + texTop) / 2.0f);
  }

  else   // multitextures disponibles (un seul parcours) ou pas de detail souhaite
  {
    texLeft = ((float)fabs(x - edgeOffset) / sizeHeightMap);
    texBottom = ((float)fabs(z - edgeOffset) / sizeHeightMap);
    texRight = ((float)fabs(x + edgeOffset) / sizeHeightMap);
    texTop = ((float)fabs(z + edgeOffset) / sizeHeightMap);

    midX = ((texLeft + texRight) / 2.0f);
    midZ = ((texBottom + texTop) / 2.0f);
  }

  blend = GetQuadMatrixData(iX, iZ);

  if(blend > 0) {
    // noeud sur le niveau le plus petit
    if(edgeLength <= 2) {
      glBegin(GL_TRIANGLE_FAN);
      // vertex au milieu
      RenderVertex(x, z, midX, midZ, multiTextures);

      //..bas gauche
      RenderVertex(x - edgeOffset, z - edgeOffset, texLeft, texBottom, multiTextures);

      // bas milieu, on n'affiche pas si voisin moins detaille (eviter "CRACKS")
      if(((iZ - adjOffset) <= 0) || GetQuadMatrixData(iX, iZ - adjOffset) != 0) {
        RenderVertex(x, z - edgeOffset, midX, texBottom, multiTextures);
      }

      // bas droite
      RenderVertex(x + edgeOffset, z - edgeOffset, texRight, texBottom, multiTextures);

      // milieu droite, on n'affiche pas si voisin moins detaille (eviter "CRACKS")
      if(((iX + adjOffset) >= sizeHeightMap) || GetQuadMatrixData(iX + adjOffset, iZ) != 0) {
        RenderVertex(x + edgeOffset, z, texRight, midZ, multiTextures);
      }

      // haut droite
      RenderVertex(x + edgeOffset, z + edgeOffset, texRight, texTop, multiTextures);

      // haut milieu,on n'affiche pas si voisin moins detaille (eviter "CRACKS")
      if(((iZ + adjOffset) >= sizeHeightMap) || GetQuadMatrixData(iX, iZ + adjOffset) != 0) {
        RenderVertex(x, z + edgeOffset, midX, texTop, multiTextures);
      }

      // haut gauche
      RenderVertex(x - edgeOffset, z + edgeOffset, texLeft, texTop, multiTextures);

      // gauche milieu, on n'affiche pas si voisin moins detaille (eviter "CRACKS")
      if(((iX - adjOffset) <= 0) || GetQuadMatrixData(iX - adjOffset, iZ) != 0) {
        RenderVertex(x - edgeOffset, z, texLeft, midZ, multiTextures);
      }

      // bas gauche
      RenderVertex(x - edgeOffset, z - edgeOffset, texLeft, texBottom, multiTextures);
      glEnd();
      return;
    }

    else     // sur les autres niveaux: recursion
    {
      // position dans le quadtree
      ichildOffset = (edgeLength) / 4;
      childOffset = (float)ichildOffset;

      childEdgeLength = (edgeLength) / 2;

      // on calcule un code indiquant le type de suite de triangles a creer
      //.. en fonction des valeurs dans notre quadtree-matrice
      // haut droite
      code = (GetQuadMatrixData(iX + ichildOffset, iZ + ichildOffset) != 0) * 8;

      // haut gauche
      code |= (GetQuadMatrixData(iX - ichildOffset, iZ + ichildOffset) != 0) * 4;

      // bas gauche
      code |= (GetQuadMatrixData(iX - ichildOffset, iZ - ichildOffset) != 0) * 2;

      // bas droite
      code |= (GetQuadMatrixData(iX + ichildOffset, iZ - ichildOffset) != 0);

      // pas d'affichage de ce noeud, car il est compose de 4 "enfants" qui seront affiches recursivement
      if(code == QT_NO_FAN) {
        // bas gauche
        RenderNode(x - childOffset, z - childOffset, childEdgeLength);
        // bas droite
        RenderNode(x + childOffset, z - childOffset, childEdgeLength);
        // haut gauche
        RenderNode(x - childOffset, z + childOffset, childEdgeLength);
        // haut droite
        RenderNode(x + childOffset, z + childOffset, childEdgeLength);
        return;
      }

      // a afficher: bas gauche, haut droite; les autres sont des enfants
      if(code == QT_LL_UR) {
        // suite de triangles en haut droite
        glBegin(GL_TRIANGLE_FAN);
        // milieu
        RenderVertex(x, z, midX, midZ, multiTextures);

        // droite milieu
        RenderVertex(x + edgeOffset, z, texRight, midZ, multiTextures);

        // haut droite
        RenderVertex(x + edgeOffset, z + edgeOffset, texRight, texTop, multiTextures);

        // haut milieu
        RenderVertex(x, z + edgeOffset, midX, texTop, multiTextures);
        glEnd();

        // suite en bas gauche
        glBegin(GL_TRIANGLE_FAN);
        // milieu
        RenderVertex(x, z, midX, midZ, multiTextures);

        // gauche milieu
        RenderVertex(x - edgeOffset, z, texLeft, midZ, multiTextures);

        // bas gauche
        RenderVertex(x - edgeOffset, z - edgeOffset, texLeft, texBottom, multiTextures);

        // bas milieu
        RenderVertex(x, z - edgeOffset, midX, texBottom, multiTextures);
        glEnd();

        // recursion haut gauche, bas droite
        RenderNode(x - childOffset, z + childOffset, childEdgeLength);
        RenderNode(x + childOffset, z - childOffset, childEdgeLength);
        return;
      }

      // a afficher: triangles bas-droite, haut-gauche; les autres: enfants
      if(code == QT_LR_UL) {
        // haut gauche
        glBegin(GL_TRIANGLE_FAN);
        // milieu
        RenderVertex(x, z, midX, midZ, multiTextures);

        // haut milieu
        RenderVertex(x, z + edgeOffset, midX, texTop, multiTextures);

        // haut gauche
        RenderVertex(x - edgeOffset, z + edgeOffset, texLeft, texTop, multiTextures);

        // milieu gauche
        RenderVertex(x - edgeOffset, z, texLeft, midZ, multiTextures);
        glEnd();

        // bas droite
        glBegin(GL_TRIANGLE_FAN);
        // milieu
        RenderVertex(x, z, midX, midZ, multiTextures);

        // bas milieu
        RenderVertex(x, z - edgeOffset, midX, texBottom, multiTextures);

        // bas droite
        RenderVertex(x + edgeOffset, z - edgeOffset, texRight, texBottom, multiTextures);

        // droite milieu
        RenderVertex(x + edgeOffset, z, texRight, midZ, multiTextures);
        glEnd();

        // recursion haut droite, bas gauche: enfants
        RenderNode(x + childOffset, z + childOffset, childEdgeLength);
        RenderNode(x - childOffset, z - childOffset, childEdgeLength);
        return;
      }

      // feuille: pas d'enfants, rendering complet a faire
      if(code == QT_COMPLETE_FAN) {
        glBegin(GL_TRIANGLE_FAN);
        // vertex au milieu
        RenderVertex(x, z, midX, midZ, multiTextures);

        //..bas gauche
        RenderVertex(x - edgeOffset, z - edgeOffset, texLeft, texBottom, multiTextures);

        // bas milieu, on n'affiche pas si voisin moins detaille (eviter "CRACKS")
        if(((iZ - adjOffset) <= 0) || GetQuadMatrixData(iX, iZ - adjOffset) != 0) {
          RenderVertex(x, z - edgeOffset, midX, texBottom, multiTextures);
        }

        // bas droite
        RenderVertex(x + edgeOffset, z - edgeOffset, texRight, texBottom, multiTextures);

        // milieu droite, on n'affiche pas si voisin moins detaille (eviter "CRACKS")
        if(((iX + adjOffset) >= sizeHeightMap) || GetQuadMatrixData(iX + adjOffset, iZ) != 0) {
          RenderVertex(x + edgeOffset, z, texRight, midZ, multiTextures);
        }

        // haut droite
        RenderVertex(x + edgeOffset, z + edgeOffset, texRight, texTop, multiTextures);

        // haut milieu,on n'affiche pas si voisin moins detaille (eviter "CRACKS")
        if(((iZ + adjOffset) >= sizeHeightMap) || GetQuadMatrixData(iX, iZ + adjOffset) != 0) {
          RenderVertex(x, z + edgeOffset, midX, texTop, multiTextures);
        }

        // haut gauche
        RenderVertex(x - edgeOffset, z + edgeOffset, texLeft, texTop, multiTextures);

        // gauche milieu, on n'affiche pas si voisin moins detaille (eviter "CRACKS")
        if(((iX - adjOffset) <= 0) || GetQuadMatrixData(iX - adjOffset, iZ) != 0) {
          RenderVertex(x - edgeOffset, z, texLeft, midZ, multiTextures);
        }

        // bas gauche
        RenderVertex(x - edgeOffset, z - edgeOffset, texLeft, texBottom, multiTextures);
        glEnd();
        return;
      }

      // il reste les suites partielles a afficher; on utilise les codes pour faciliter le choix de triang.
      // cette partie est quasiment directement copie de l'article de Stefan Röttger;
      //.. les codes des suites partielles sont pas evidents.. (code binaire pour representer un quadtree)
      // suite partielle = repartition non-symmetrique entre enfants recursifs et feuilles
      start = suiteStart[code];
      suiteLength = 0;

      // l'indice du permier bit non-null du code indique la taille de la suite a creer
      while(!(((long)suiteCode[code]) & (1 << suiteLength)) && suiteLength < 8)
        suiteLength++;

      // rendering
      glBegin(GL_TRIANGLE_FAN);
      // milieu recursif
      RenderVertex(x, z, midX, midZ, multiTextures);

      for(suitePosition = suiteLength; suitePosition > 0; suitePosition--) {
        switch(start) {
        // bas droite
        case QT_LR_NODE:
          // bas milieu, ne pas afficher si voisin moins detaille
          if(((iZ - adjOffset) <= 0) || GetQuadMatrixData(iX, iZ - adjOffset) != 0
             || suitePosition == suiteLength) {
            RenderVertex(x, z - edgeOffset, midX, texBottom, multiTextures);
          }

          // bas droite
          RenderVertex(x + edgeOffset, z - edgeOffset, texRight, texBottom, multiTextures);

          // droit milieu
          if(suitePosition == 1) {
            RenderVertex(x + edgeOffset, z, texRight, midZ, multiTextures);
          }
          break;

        // bas gauche
        case QT_LL_NODE:
          // gauche milieu, ne pas afficher si voisin moins detaille
          if(((x - adjOffset) <= 0) || GetQuadMatrixData(iX - adjOffset, iZ) != 0
             || suitePosition == suiteLength) {
            RenderVertex(x - edgeOffset, z, texLeft, midZ, multiTextures);
          }

          // bas gauche
          RenderVertex(x - edgeOffset, z - edgeOffset, texLeft, texBottom, multiTextures);

          // bas milieu
          if(suitePosition == 1) {
            RenderVertex(x, z - edgeOffset, midX, texBottom, multiTextures);
          }
          break;

        // haut gauche
        case QT_UL_NODE:
          // haut milieu, ne pas afficher si voisin moins detaille
          if(((iZ + adjOffset) >= sizeHeightMap) || GetQuadMatrixData(iX, iZ + adjOffset) != 0
             || suitePosition == suiteLength) {
            RenderVertex(x, z + edgeOffset, midX, texTop, multiTextures);
          }

          // haut gauche
          RenderVertex(x - edgeOffset, z + edgeOffset, texLeft, texTop, multiTextures);

          // gauche milieu
          if(suitePosition == 1) {
            RenderVertex(x - edgeOffset, z, texLeft, midZ, multiTextures);
          }
          break;

        // haut droite
        case QT_UR_NODE:
          // droit milieu, ne pas afficher si voisin moins detaille
          if(((iX + adjOffset) >= sizeHeightMap) || GetQuadMatrixData(iX + adjOffset, iZ) != 0
             || suitePosition == suiteLength) {
            RenderVertex(x + edgeOffset, z, texRight, midZ, multiTextures);
          }

          // haut droite
          RenderVertex(x + edgeOffset, z + edgeOffset, texRight, texTop, multiTextures);

          // haut milieu
          if(suitePosition == 1) {
            RenderVertex(x, z + edgeOffset, midX, texTop, multiTextures);
          }
          break;
        }
        start--;
        start &= 3;
      }
      glEnd();

      // pour les cas toujours pas traites, on continue la recursion
      for(suitePosition = (4 - suiteLength); suitePosition > 0; suitePosition--) {
        switch(start) {
        // bas droite
        case QT_LR_NODE:
          RenderNode(x + childOffset, z - childOffset, childEdgeLength);
          break;

        // bas gauche
        case QT_LL_NODE:
          RenderNode(x - childOffset, z - childOffset, childEdgeLength);
          break;

        // haut gauche
        case QT_UL_NODE:
          RenderNode(x - childOffset, z + childOffset, childEdgeLength);
          break;

        // haut droite
        case QT_UR_NODE:
          RenderNode(x + childOffset, z + childOffset, childEdgeLength);
          break;
        }
        start--;
        start &= 3;
      }
      return;
    }
  }
}

/*
   void bruteForce::Render(void)
   {
   float texLeft, texBottom, texTop;
   int	x, z;
   unsigned char color, shadeLow, shadeHigh;

   // optimiser en ne pas affichant terrain hors camera
   glEnable( GL_CULL_FACE );

   //la machine dispose de multitextures (dessiner plusieurs textures sur le meme triangle en meme temps)
   //.. et l'utilisateur veut les textures
   if( haveMultitexture && paintTextures )
   {
   glDisable( GL_BLEND );	//pas de combinaison de couleurs avec MULTITEXTURES,
   //.. la combi des deux textures se fait automatiquement en hardware

   //selectionner comme premiere unite de texture la texture de base (couleur selon hauteur)
   glActiveTexture( GL_TEXTURE0 );
   glEnable( GL_TEXTURE_2D );
   glBindTexture( GL_TEXTURE_2D, textureColorID );

   //selectionner commer deuxieme unite de texture la texture de detail (structure)
   glActiveTexture( GL_TEXTURE1 );
   glEnable( GL_TEXTURE_2D );
   glBindTexture( GL_TEXTURE_2D, textureDetailID );

   //definir la maniere dont les couleurs des triangles sont crees en fct. des couleurs des textures
   glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );		//mode de combination des deux
   glTexEnvi( GL_TEXTURE_ENV, GL_RGB_SCALE, 2 );		//augmenter la luminosite des couleurs

   //axe z
   for( z=0; z<sizeHeightMap-1; z++ )
   {
   //suite de triangles
   glBegin( GL_TRIANGLE_STRIP );
   //axe x
   for( x=0; x<sizeHeightMap-1; x++ )
   {
   //calculer les coordonnees sur la texture qui seront "mappees" sur le terrain
   //.. entre ces valeurs, opengl interpole
   texLeft  = ( float )x/sizeHeightMap;
   texBottom= ( float )z/sizeHeightMap;
   texTop	  = ( float )( z+1 )/sizeHeightMap;

   //creation d'ombrages en modifiant la couleur de base
   shadeLow = GetBrightnessAtPoint( x, z );
   shadeHigh= GetBrightnessAtPoint( x, z+1 );

   if (paintLighting)
   glColor3ub( ( unsigned char )( shadeLow*rLight ),
   ( unsigned char )( shadeLow*gLight ),
   ( unsigned char )( shadeLow*bLight ) );
   else
   glColor3ub( 255, 255, 255 );


   glMultiTexCoord2f( GL_TEXTURE0, texLeft, texBottom );
   glMultiTexCoord2f( GL_TEXTURE1, texLeft*repeatDetailMap,
   texBottom*repeatDetailMap );
   glVertex3f( ( float )x/sizeHeightMap,
   GetScaledHeightAtPoint( x, z )/sizeHeightMap,
   ( float )z/sizeHeightMap );

   //meme chose pour deuxieme vertex
   if (paintLighting)
   glColor3ub( ( unsigned char )( shadeHigh*rLight ),
   ( unsigned char )( shadeHigh*gLight ),
   ( unsigned char )( shadeHigh*bLight ) );
   else
   glColor3ub( 255, 255, 255 );


   glMultiTexCoord2f( GL_TEXTURE0, texLeft, texTop );
   glMultiTexCoord2f( GL_TEXTURE1, texLeft*repeatDetailMap,
   texTop*repeatDetailMap );
   glVertex3f( ( float )x/sizeHeightMap,
   GetScaledHeightAtPoint( x, z+1 )/sizeHeightMap,
   ( float )(z+1)/sizeHeightMap );
   }
   glEnd( );
   }

   //liberer la deuxieme texture
   glActiveTexture( GL_TEXTURE1 );
   glDisable( GL_TEXTURE_2D );
   glBindTexture( GL_TEXTURE_2D, 0 );

   //liberer la premiere texture
   glActiveTexture( GL_TEXTURE0 );
   glDisable( GL_TEXTURE_2D );
   glBindTexture( GL_TEXTURE_2D, 0 );
   }

   //la machine ne dispose pas de fonction multitextures, il faut alors faire deux parcours du terrain,
   //.. un premier parcours pour les textures de base (couleurs en fct. d'hauteur), le deuxieme pour le detail
   // .. mais seulement SI l'utilisateur souhaite des textures
   else if( paintTextures )
   {
   //PREMIER PARCOURS: couleurs
   //selectionner comme unite de texture la texture de base (couleur selon hauteur)
   glActiveTexture( GL_TEXTURE0 );
   glEnable( GL_TEXTURE_2D );
   glBindTexture( GL_TEXTURE_2D, textureColorID );

   for( z=0; z<sizeHeightMap-1; z++ )
   {
   glBegin( GL_TRIANGLE_STRIP );
   for( x=0; x<sizeHeightMap-1; x++ )
   {
   //calculer la position sur la texture qui correspond a la pos. ds la carte
   // .. et afficher premier vertex
   texLeft  = ( float )x/sizeHeightMap;
   texBottom= ( float )z/sizeHeightMap;
   texTop	  = ( float )( z+1 )/sizeHeightMap;

   //creation d'ombrages en modifiant la couleur de base
   shadeLow = GetBrightnessAtPoint( x, z );
   shadeHigh= GetBrightnessAtPoint( x, z+1 );

   if (paintLighting)
   glColor3ub( ( unsigned char )( shadeLow*rLight ),
   ( unsigned char )( shadeLow*gLight ),
   ( unsigned char )( shadeLow*bLight ) );
   else
   glColor3ub( 255, 255, 255 );

   glMultiTexCoord2f( GL_TEXTURE0, texLeft, texBottom );
   glVertex3f( ( float )x/sizeHeightMap,
   GetScaledHeightAtPoint( x, z )/sizeHeightMap,
   ( float )z/sizeHeightMap );

   //..deuxieme vertex
   if (paintLighting)
   glColor3ub( ( unsigned char )( shadeHigh*rLight ),
   ( unsigned char )( shadeHigh*gLight ),
   ( unsigned char )( shadeHigh*bLight ) );
   else
   glColor3ub( 255, 255, 255 );

   glMultiTexCoord2f( GL_TEXTURE0, texLeft, texTop );
   glVertex3f( ( float )x/sizeHeightMap,
   GetScaledHeightAtPoint( x, z+1 )/sizeHeightMap,
   ( float )(z+1)/sizeHeightMap );
   }
   glEnd( );
   }

   //DEUXIEME PARCOURS: DETAIL
   //preparer la texture de detail
   glActiveTexture( GL_TEXTURE0 );
   glEnable( GL_TEXTURE_2D );
   glBindTexture( GL_TEXTURE_2D, textureDetailID );

   //activer la combinaison entre couleur existant (premier parcours) et couleur ajoutee (detail)
   glEnable( GL_BLEND );
   glBlendFunc( GL_ZERO, GL_SRC_COLOR );

   for( z=0; z<sizeHeightMap-1; z++ )
   {
   glBegin( GL_TRIANGLE_STRIP );
   for( x=0; x<sizeHeightMap-1; x++ )
   {
   //calcul de coord. texture de detail
   texLeft  = ( float )x/sizeHeightMap;
   texBottom= ( float )z/sizeHeightMap;
   texTop	  = ( float )( z+1 )/sizeHeightMap;

   //premier vertex, la texture de detail est repetee (ca ne se voit quasiment pas)
   //creation d'ombrages en modifiant la couleur de base
   shadeLow = GetBrightnessAtPoint( x, z );
   shadeHigh= GetBrightnessAtPoint( x, z+1 );

   if (paintLighting)
   glColor3ub( ( unsigned char )( shadeLow*rLight ),
   ( unsigned char )( shadeLow*gLight ),
   ( unsigned char )( shadeLow*bLight ) );
   else
   glColor3ub( 255, 255, 255 );

   glMultiTexCoord2f( GL_TEXTURE0, texLeft*repeatDetailMap, texBottom*repeatDetailMap );
   glVertex3f( ( float )x/sizeHeightMap,
   GetScaledHeightAtPoint( x, z )/sizeHeightMap,
   ( float )z/sizeHeightMap );

   //deuxieme vertex
   if (paintLighting)
   glColor3ub( ( unsigned char )( shadeHigh*rLight ),
   ( unsigned char )( shadeHigh*gLight ),
   ( unsigned char )( shadeHigh*bLight ) );
   else
   glColor3ub( 255, 255, 255 );

   glMultiTexCoord2f( GL_TEXTURE0, texLeft*repeatDetailMap, texTop*repeatDetailMap );
   glVertex3f( ( float )x/sizeHeightMap,
   GetScaledHeightAtPoint( x, z+1 )/sizeHeightMap,
   ( float )(z+1)/sizeHeightMap );
   }
   glEnd( );
   }
   glDisable( GL_BLEND );
   //liberer l'unite de texture
   glActiveTexture( GL_TEXTURE0 );
   glDisable( GL_TEXTURE_2D );
   glBindTexture( GL_TEXTURE_2D, 0 );
   }
   //l'utilisateur ne souhaite pas de textures: on fait un parcours de rendering sans textures
   else
   {
   //axe z
   for( z=0; z<sizeHeightMap-1; z++ )
   {
   glBegin( GL_TRIANGLE_STRIP );
   //x-axe
   for( x=0; x<sizeHeightMap-1; x++ )
   {
   //premier coord vertex
   //choisir couleur en fct. de heightmap-valeur
   color= GetTrueHeightAtPoint( x, z );
   //ici, pas d'ombrages, mais colorisation en fct. de l'hauteur
   glColor3ub( color, color, color );

   glVertex3f(  ( float )x/sizeHeightMap,
   GetScaledHeightAtPoint( x, z )/sizeHeightMap,
   ( float )z/sizeHeightMap );

   //deuxieme coord vertex
   color= GetTrueHeightAtPoint( x, z+1 );
   glColor3ub( color, color, color );

   glVertex3f(  ( float )x/sizeHeightMap,
   GetScaledHeightAtPoint( x, z+1 )/sizeHeightMap,
   ( float )(z+1)/sizeHeightMap );
   }
   glEnd( );
   }
   }
   }

 */
