Code Source
/************************************************************/
/* terrain2.c */
/************************************************************/
/* Generation de terrain correctement illumine 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>
#include <math.h>
#define NB_SUBDIV_INIT 32
#define NB_SUBDIV_MAX 64
#define ECHELLE_VERT_INIT 0.1
#define ECHELLE_VERT_MAX 1.0
#define ECHELLE_NORMALES 0.1
#define DISTANCE_INIT 4.0
#define DISTANCE_MAX 15.0
/* Definition du type sommet */
typedef struct {
float x;
float y;
float z;
float nx;
float ny;
float nz;
char nb;
} vertex;
/* 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 */
unsigned char afficheNormales=FALSE; /* Affichage des normales */
unsigned char afficheLampes=TRUE; /* Affichage des lampes */
unsigned char modePlein=TRUE; /* Affichage en mode plein ou fil de fer */
int repere=0,terrain=0,normales=0,lampes=0; /* 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 */
/* Parametres d'éclairage */
GLfloat L0pos[]={ 0.0,2.0,1.0};
GLfloat L0dif[]={ 0.3,0.3,0.8};
GLfloat L1pos[]={ 2.0,2.0,2.0};
GLfloat L1dif[]={ 0.5,0.5,0.5};
GLfloat Mspec[]={0.5,0.5,0.5};
GLfloat Mshiny=50;
/* 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();
void creeNormales(vertex *T);
void creeLampes();
void drawVertex(int i,int j,vertex *T);
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 | GLUT_DEPTH);
glutInitWindowSize(500,500);
glutCreateWindow(argv[0]);
/*Initialisation d'OpenGL */
init();
loadJpegImage(argv[1]);
/* Creation des objets */
creeRepere();
creeLampes();
creeTerrain();
glutMainLoop();
return 0;
}
/* Initialisation d'openGL */
void init()
{
glClearColor(0.8,0.8,0.8,1.0);
glEnable(GL_DEPTH_TEST);
if (modePlein)
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
else
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);
/* Parametrage de l'éclairage */
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,GL_TRUE);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHT1);
glLightfv(GL_LIGHT0,GL_DIFFUSE,L0dif);
glLightfv(GL_LIGHT0,GL_SPECULAR,L0dif);
glLightfv(GL_LIGHT1,GL_DIFFUSE,L1dif);
glLightfv(GL_LIGHT1,GL_SPECULAR,L1dif);
/* Paramétrage du matériau */
glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,Mspec);
glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,Mshiny);
/* 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 | GL_DEPTH_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);
glLightfv(GL_LIGHT0,GL_POSITION,L0pos);
glLightfv(GL_LIGHT1,GL_POSITION,L1pos);
if (afficheLampes)
glCallList(lampes);
glCallList(terrain);
if (afficheNormales)
glCallList(normales);
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 'f':
modePlein=1-modePlein;
if (modePlein)
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
else
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
glutPostRedisplay();
break;
case 'n': /* Affichage des normales ON/OFF */
afficheNormales=1-afficheNormales;
glutPostRedisplay();
break;
case 'r': /* Affichage du repere ON/OFF */
afficheRepere=1-afficheRepere;
glutPostRedisplay();
break;
case 'l' : /* Affichage des lampes ON/OFF */
afficheLampes=1-afficheLampes;
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 droit est presse */
if (b_droit) {
distance+=((float)(y-yprec))/50.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);
glDisable(GL_LIGHTING);
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();
glEnable(GL_LIGHTING);
glEndList();
}
/* Creation de la liste d'afffichage pour le terrain */
void creeTerrain()
{
int i,j;
float pas=2.0/nbSubdiv;
vertex *T;
vertex *P1,*P2,*P3,*P4;
vertex V1,V2,V3;
float incx,incy,incz,norme;
T=(vertex *)malloc((nbSubdiv+1)*(nbSubdiv+1)*sizeof(vertex));
/* Initialisation des normales */
for (i=0;i<=nbSubdiv;i++)
for (j=0;j<=nbSubdiv;j++) {
T[i*(nbSubdiv+1)+j].nx=0.0;
T[i*(nbSubdiv+1)+j].ny=0.0;
T[i*(nbSubdiv+1)+j].nz=0.0;
}
/* remplissage du tableau Tvertex */
for (i=0;i<=nbSubdiv;i++)
for (j=0;j<=nbSubdiv;j++) {
T[i*(nbSubdiv+1)+j].x=-1.0+i*pas;
T[i*(nbSubdiv+1)+j].y=-1.0+j*pas;
T[i*(nbSubdiv+1)+j].z=elevation(i,j);
}
for (i=0;i<nbSubdiv;i++)
for (j=0;j<nbSubdiv;j++) {
P1=&T[i*(nbSubdiv+1)+j];
P2=&T[(i+1)*(nbSubdiv+1)+j];
P3=&T[(i+1)*(nbSubdiv+1)+j+1];
P4=&T[i*(nbSubdiv+1)+j+1];
V1.x=P2->x-P1->x; V1.y=P2->y-P1->y; V1.z=P2->z-P1->z;
V2.x=P3->x-P1->x; V2.y=P3->y-P1->y; V2.z=P3->z-P1->z;
V3.x=P4->x-P1->x; V3.y=P4->y-P1->y; V3.z=P4->z-P1->z;
incx=V2.y*V1.z-V1.y*V2.z;
incy=V2.z*V1.x-V1.z*V2.x;
incz=V2.x*V1.y-V1.x*V2.y;
norme=sqrt(incx*incx+incy*incy+incz*incz);
incx/=norme; incy/=norme; incz/=norme;
P1->nx-=incx; P1->ny-=incy; P1->nz-=incz;
P2->nx-=incx; P2->ny-=incy; P2->nz-=incz;
P3->nx-=incx; P3->ny-=incy; P3->nz-=incz;
incx=V3.y*V2.z-V2.y*V3.z;
incy=V3.z*V2.x-V2.z*V3.x;
incz=V3.x-V2.y-V2.x*V3.y;
P1->nx-=incx; P1->ny-=incy; P1->nz-=incz;
P3->nx-=incx; P3->ny-=incy; P3->nz-=incz;
P4->nx-=incx; P4->ny-=incy; P4->nz-=incz;
}
/* normalisation des normales */
for (i=0;i<=nbSubdiv;i++)
for (j=0;j<=nbSubdiv;j++) {
P1=&T[i*(nbSubdiv+1)+j];
norme=sqrt(P1->nx*P1->nx+P1->ny*P1->ny+P1->nz*P1->nz);
P1->nx/=norme;
P1->ny/=norme;
P1->nz/=norme;
}
/* 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++) {
glBegin(GL_TRIANGLES);
/* triangle 1 */
glEdgeFlag(TRUE);
drawVertex(i,j,T);
drawVertex(i+1,j,T);
if (!areteTransv)
glEdgeFlag(FALSE);
drawVertex(i+1,j+1,T);
/*triangle 2 */
drawVertex(i,j,T);
if (!areteTransv)
glEdgeFlag(TRUE);
drawVertex(i+1,j+1,T);
drawVertex(i,j+1,T);
glEnd();
}
glEndList();
/* Generation de la liste d'affichage des normales*/
creeNormales(T);
/* Liberation de Tvertex */
free(T);
}
/* Cree la liste d'affichage pour les normales */
void creeNormales(vertex *T)
{
int i,j;
vertex *P;
if (glIsList(normales))
glDeleteLists(normales,1);
normales=glGenLists(1);
glNewList(normales,GL_COMPILE);
glDisable(GL_LIGHTING);
glLineWidth(1.0);
glColor3f(1.0,1.0,1.0);
glBegin(GL_LINES);
for (i=0;i<=nbSubdiv;i++)
for (j=0;j<=nbSubdiv;j++) {
P=&T[i*(nbSubdiv+1)+j];
glVertex3fv(&P->x);
glVertex3f(P->x+ECHELLE_NORMALES*P->nx,
P->y+ECHELLE_NORMALES*P->ny,
P->z+ECHELLE_NORMALES*P->nz);
}
glEnd();
glEnable(GL_LIGHTING);
glEndList();
}
/* Creation de la Liste d'affichage pour les lampes */
void creeLampes()
{
lampes=glGenLists(1);
glNewList(lampes,GL_COMPILE);
glDisable(GL_LIGHTING);
glColor3f(1.0,1.0,1.0);
glPointSize(6.0);
glBegin(GL_POINTS);
glVertex3fv(L0pos);
glVertex3fv(L1pos);
glEnd();
glLineWidth(1.0);
glBegin(GL_LINES);
glVertex3fv(L0pos);
glVertex3f(0.0,0.0,0.0);
glVertex3fv(L1pos);
glVertex3f(0.0,0.0,0.0);
glEnd();
glEnable(GL_LIGHTING);
glEndList();
}
/* Affiche le sommet (i,j) du maillage */
void drawVertex(int i,int j,vertex *T)
{
glNormal3fv(&(T[i*(nbSubdiv+1)+j].nx));
glVertex3fv(&(T[i*(nbSubdiv+1)+j].x));
}
/* 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);
}