Génération de terrain (1/2)
Par Xavier Michelon


 
   
Code Source

/************************************************************/
/*                        terrain .c                        */
/************************************************************/
/* Generation de terrain a partir d'une image JPEG          */
/************************************************************/



#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <jpeglib.h>
#include <jerror.h>

#define NB_SUBDIV_INIT 32
#define NB_SUBDIV_MAX 64
#define ECHELLE_VERT_INIT 0.3
#define ECHELLE_VERT_MAX 1.0
#define DISTANCE_INIT 4.0
#define DISTANCE_MAX 15.0


/* Variables globales */

unsigned char image[256][256]; /* l'image du terrain */
unsigned char afficheRepere=TRUE; /* Affichage du repere */
unsigned char faceArriere=FALSE; /* Affichage des faces arrieres de polygones */
unsigned char areteTransv=FALSE; /* Affichage de l'arete transversale */
int repere,terrain; /* Identifiants des listes d'affichage */
int nbSubdiv=NB_SUBDIV_INIT; /* Nombre de subdivisions du maillage */
float echelleVert=ECHELLE_VERT_INIT; /* echelle verticale du relief */
char b_gauche=0,b_droit=0; /* bouton de souris presse ? */
int theta=-30,phi=300; /* Position de l'observateur */
int xprec,yprec; /* sauvegarde de la position de la souris */
float distance=DISTANCE_INIT; /* distance de l'observateur a l'origine */

/* Prototypes des fonctions */

void init();
void affichage();
void clavier(unsigned char touche,int x,int y);
void souris(int bouton,int etat,int x,int y);
void mouvement(int x,int y);
void redim(int l,int h);
void creeRepere();
void creeTerrain();
float elevation(int i,int j);
void loadJpegImage(char *filename);



int main(int argc,char **argv)
{
  /* Initialisation de glut */
  glutInit(&argc,argv);
  glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
  glutInitWindowSize(500,500);
  glutCreateWindow(argv[0]);

  /*Initialisation d'OpenGL */
  init();
  loadJpegImage(argv[1]);

  /* Creation des objets */
  creeRepere();
  creeTerrain();

  glutMainLoop();
  return 0;
}



void init()
{
  glClearColor(0.8,0.8,0.8,1.0);
  glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
  glCullFace(GL_BACK);
  if (faceArriere)
    glDisable(GL_CULL_FACE);
  else
    glEnable(GL_CULL_FACE);

  /* Mise en place de la perspective */
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();  
  gluPerspective(45.0,1.0,0.1,20.0);
  glMatrixMode(GL_MODELVIEW);  

  /* Mise en place des fonction de rappel */
  glutDisplayFunc(affichage);
  glutKeyboardFunc(clavier);
  glutMouseFunc(souris);
  glutMotionFunc(mouvement);
  glutReshapeFunc(redim);
}



/* Fonction de rappel pour l'affichage */
void affichage()
{
  glClear(GL_COLOR_BUFFER_BIT);
  glLoadIdentity();
  gluLookAt(0.0,0.0,distance,0.0,0.0,0.0,0.0,1.0,0.0);
  glRotatef(phi,1.0,0.0,0.0);
  glRotatef(theta,0.0,0.0,1.0);
  glCallList(terrain);
  if (afficheRepere)
    glCallList(repere);
  glutSwapBuffers();
}



/* Fonction de rappel pour le clavier */
void clavier(unsigned char touche,int x,int y)
{
  switch (touche) {
  case 27: /* touche 'ESC' pour quitter */
    exit(0);
  case '+': /* augmentation du nombre de subdivisions */
    nbSubdiv++;
    if (nbSubdiv>NB_SUBDIV_MAX)
      nbSubdiv=NB_SUBDIV_MAX;
    creeTerrain();
    glutPostRedisplay();
    break;
  case '-': /* diminution du nombre de subdivisions*/
    nbSubdiv--;
    if (nbSubdiv<1)
      nbSubdiv=1;
    creeTerrain();
    glutPostRedisplay();
    break;
  case 'p': /* augmentation de l'echelle verticale */
    echelleVert+=0.02;
    if (echelleVert>ECHELLE_VERT_MAX)
      echelleVert=ECHELLE_VERT_MAX;
    creeTerrain();
    glutPostRedisplay();
    break;
  case 'o': /* diminution de l'echelle verticale */
    echelleVert-=0.02;
    if (echelleVert<-ECHELLE_VERT_MAX)
      echelleVert=-ECHELLE_VERT_MAX;
    creeTerrain();
    glutPostRedisplay();
    break;
  case 'r': /* Affichage du repere ON/OFF */
    afficheRepere=1-afficheRepere;
    glutPostRedisplay();
    break;
  case 'c': /* affichage des faces arrieres ON/OFF */
    faceArriere=1-faceArriere;
  if (faceArriere)
    glDisable(GL_CULL_FACE);
  else
    glEnable(GL_CULL_FACE);
  glutPostRedisplay();
  break;
  case 't': /* affichage des aretes transversales */
    areteTransv=1-areteTransv;
    creeTerrain();
    glutPostRedisplay();
    break;
  }
}



/* fonction de rappel pour l'appui sur les boutons de souris*/
void souris(int bouton,int etat,int x,int y)
{
  
  if (bouton == GLUT_LEFT_BUTTON && etat == GLUT_DOWN) {
    b_gauche = 1;
    xprec = x; 
    yprec=y;
  }
  if (bouton == GLUT_LEFT_BUTTON && etat == GLUT_UP) 
    b_gauche=0; 

  if (bouton == GLUT_RIGHT_BUTTON && etat == GLUT_DOWN) {
    b_droit = 1;
    yprec=y;
  }
  if (bouton == GLUT_RIGHT_BUTTON && etat == GLUT_UP) 
    b_droit=0; 
}



/* Fonction de rappel pour les mouvements de souris */
void mouvement(int x,int y)
{
  /* si le bouton gauche est presse */
  if (b_gauche) {
    theta+=x-xprec;
    if (theta>=360)
      while (theta>=360)
	theta-=360;
    phi+=y-yprec;
    if (phi<0)
      while (phi<0)
	phi+=360;
    xprec=x;
    yprec=y;
    glutPostRedisplay();
  }

  /* si le bouton gauche est presse */
  if (b_droit) {
    distance+=((float)(y-yprec))/10.0;
    if (distance<1.0)
      distance=1.0;
    if (distance>DISTANCE_MAX)
      distance=DISTANCE_MAX;
    glutPostRedisplay();
    yprec=y;
  }
}



/* Fonction de rappel pour le redimensionnement de fenetre */
void redim(int l,int h)
{
  if (l<h)
    glViewport(0,(h-l)/2,l,l);
  else 
    glViewport((l-h)/2,0,h,h);
}



/* Creation de la liste d'affichage pour le repere */
void creeRepere()
{
   repere=glGenLists(1);
  glNewList(repere,GL_COMPILE);
  glLineWidth(2.0);
    glBegin(GL_LINES);
      glColor3f(1.0,0.0,0.0);
      glVertex3f(0.0,0.0,0.0);
      glVertex3f(0.3,0.0,0.0);
      glColor3f(0.0,1.0,0.0);
      glVertex3f(0.0,0.0,0.0);
      glVertex3f(0.0,0.3,0.0);
      glColor3f(0.0,0.0,1.0);
      glVertex3f(0.0,0.0,0.0);
      glVertex3f(0.0,0.0,0.3);
    glEnd();
  glEndList();
} 



/* Creation de la liste d'afffichage pour le terrain */
void creeTerrain()
{
  int i,j;
  float pas=2.0/nbSubdiv;
  float P1[3],P2[3],P3[3],P4[3];
  
  /* Liste pour l'objet terrain */
  if (glIsList(terrain))
    glDeleteLists(terrain,1);
  terrain=glGenLists(1);
  glNewList(terrain,GL_COMPILE);
  glColor3f(0.0,0.0,0.0);
  glLineWidth(1.0);
  for (i=0;i<nbSubdiv;i++)
    for (j=0;j<nbSubdiv;j++) {
      P1[0]=-1.0+i*pas; P1[1]=-1.0+j*pas ; P1[2]=elevation(i,j);
      P2[0]=-1.0+(i+1)*pas; P2[1]=-1.0+j*pas ; P2[2]=elevation(i+1,j);
      P3[0]=-1.0+(i+1)*pas; P3[1]=-1.0+(j+1)*pas ; P3[2]=elevation(i+1,j+1);
      P4[0]=-1.0+i*pas; P4[1]=-1.0+(j+1)*pas ; P4[2]=elevation(i,j+1);
      
      glBegin(GL_TRIANGLES);
      /* triangle 1 */
      glEdgeFlag(TRUE);
      glVertex3fv(P1);
      glVertex3fv(P2);
      if (!areteTransv)
	glEdgeFlag(FALSE);
      glVertex3fv(P3);

      /*triangle 2 */
      glVertex3fv(P1);
      if (!areteTransv)
      glEdgeFlag(TRUE);
      glVertex3fv(P3);
      glVertex3fv(P4);
      glEnd();
    }
  glEndList();
}



/* Calcul de la hauteur d'un point */
float elevation(int i,int j)
{
  int valeur=image[(int)((float)i/nbSubdiv*255)][(int)((float)j/nbSubdiv*255)];
  return ((float)valeur/128.0-1.0)*echelleVert;
}



/* Chargement d'une image jpeg */
void loadJpegImage(char *filename)
{
  FILE *file;
  struct jpeg_decompress_struct cinfo;
  struct jpeg_error_mgr jerr;
  unsigned char *im=(unsigned char *)image,*ligne;
  
  cinfo.err = jpeg_std_error(&jerr);
  jpeg_create_decompress(&cinfo);

  /* On mets en place une image par defaut si filename=NULL*/
  if (filename==NULL){
    filename=(char *)malloc(128);
    strcpy(filename,"terrain.jpg");
  }

  if (!(file=fopen(filename,"rb"))) {
    fprintf(stderr,"Erreur : impossible d'ouvrir %s\n",filename);
    exit(1);
  }
  jpeg_stdio_src(&cinfo, file);
  jpeg_read_header(&cinfo, TRUE);
  if ((cinfo.image_width!=256) || (cinfo.image_height!=256)) {
    fprintf(stderr,"Erreur : l'image doit etre de taille 256x256\n");
    exit(1);
  }
  if (cinfo.out_color_space!=JCS_GRAYSCALE){
    fprintf(stderr,"Error : l'image doit etre en niveaux de gris\n");
    exit(1);
  }
  jpeg_start_decompress(&cinfo);

  while (cinfo.output_scanline>256){

    ligne=im+256*cinfo.output_scanline;
    jpeg_read_scanlines(&cinfo,&ligne,1);
  }

  jpeg_finish_decompress(&cinfo);
  jpeg_destroy_decompress(&cinfo);
}