Travaux Pratiques OpenGL
TP7
-
Le but de ce TP est d'apprendre à charger un maillage depuis un fichier et de le visualiser avec des tableaux de sommets. A la fin de ce TP vous obtiendrez le musée suivant :
-
Dans un premier temps, nous allons modéliser les pièces du musée. La pièce où se trouve le robo est déjà faite. Ajoutez un couloir de côté 5 et la pièce principale de largeur 30 et de profondeur 40.
Modifiez la position initiale de la caméra _pos ainsi que celle du point visé _vise pour avoir une vue initiale de la scène proche de celle de l'image ci-dessous (pour cette image, seule la lampe GL_LIGHT0 est allumée).
-
Nous allons maintenant ajouter un spot rouge (GL_LIGHT3) qui tourne autour du centre de la pièce principale. Il a une position initiale static float light3_position []= {30.,4.,0.,1.0} par rapport au centre de la pièce principale et une orientation de static float light3_direction []= {-5.,-1.,0.,0.}. Son angle d'ouverture est de 70 degrés et son coefficient d'atténuation est de 3. Le spot commence/arrête de tourner en même temps que le spot GL_LIGHT1 quand on presse la touche s. De la même façon que le spot GL_LIGHT1, ce spot est matérialisé par une sphère de la couleur de la source de lumière (rouge). Quand les spots sont allumés et tournent, vous devez obtenir le résultat suivant :
-
Passons maintenant aux maillages. Rappelez à l'aide de votre cours comment on visualise un maillage avec des tableaux de sommets (Vertex Array). Expliquez l'utilisation d'un tableau d'index (pour le TP, vous utiliserez des index codé avec des entiers non signé : GLuint, GL_UNSIGNED_INT). Si l'on souhaite visualiser un maillage avec ses normales, donnez les tableaux dont il faut disposer ainsi que la séquence de fonctions OpenGL qui va permettre le tracé du maillage. Dans la suite du TP, nous allons regrouper les tableaux dans une structure et nous allons voir comment les remplir à partir d'un fichier.
Les fichiers de maillage que nous allons utiliser sont au format .off et ils se présentent de la façon suivante :
OFF
8 12 0
0.256 0.365 0.569
2.365 2.654 8.145
...
...
3 0 1 2
3 2 5 9
3 6 0 4
...
...
où :
-
tout d'abord, il s'agit de fichiers ASCII.
-
Les trois premiers caractères rappellent l'extension du fichier. Ici : OFF
-
Le premier entier est le nombre de sommets du maillage. Ici : 8
-
Le deuxième entier est le nombre de facettes du maillage. Ici : 12
-
Le troisième entier est le nombre d'arêtes et bien souvent cette valeur est à 0 : elle n'a pas été mise à jour lors de la création du fichier.
-
Ensuite vient la liste des coordonnées des sommets du maillage sous forme de flottants. Les trois premiers flottants sont les coordonnées x, y, z du premier sommet (numéro 0), les trois suivants sont les coordonnées x, y, z du deuxième sommet (noméro 1) et ainsi de suite.
-
Une fois tous les sommets stockés, vient la liste des facettes sous la forme suivante : La première valeur est le nombre de sommets de la facette (pour nous, il n'y aura que des facettes triangulaires, donc cette valeur sera 3). Cette valeur est suivie du numéro des sommets composant la facette. Ici, la première facette triangulaire est composée du sommet numéro 0, du sommet 1 et du sommet 2. La deuxième facette triangulaire est composée des sommets numéro 2, 5 et 9, et ainsi de suite.
Téléchargez le fichier suivant : buddha.off
Editez le fichier et vérifiez qu'il est bien conforme au format précédant.
-
Pour stocker un maillage, nous allons utiliser la structure et les types suivants :
typedef struct sMaillage {
int nbSommets; // nombre de sommets du maillage
int nbIndex; // nombre d'index du maillage
float *sommets; // tableau des coordonnées x,y,z des sommets du maillage
float *normales; // tableau des coordonnées x,y,z des normales aux sommets du maillage
GLuint *index; // tableau des index des sommets pour les facettes triangulaires
float minY; // coordonnée Y de sommet la plus petite : pour pouvoir poser le maillage sur un autre objet
} tMaillage, *pMaillage;
Le type tMaillage est une structure sMaillage et le type pMaillage est un pointeur sur une structure sMaillage.
-
Dans un premier temps, nous n'allons pas nous occuper des normales.
-
Ecrivez la fonction pMaillage chargeMaillage (char *nomFich) {...} qui alloue l'espace mémoire et affecte les champs nbSommets, nbIndex, sommets et index à partir du fichier dont le nom est passé en paramètre, et retourne le maillage stocké dans un pointeur de type pMaillage.
Rappels / aides :
-
FILE *f; // déclaration d'une variable de type fichier
char c[3]; // tableau de 3 carctères
int i; // compteur
pMaillage maillage; // variable de type pointuer sur une structure de maillage
...
f = fopen(nomFich, "r"); // ouverture du fichier en lecture
...
maillage = (pMaillage)malloc(sizeof(tMaillage)); // allocation du pointeur sur la structure de maillage
...
fread(c, sizeof(char), 3, f); // lecture des trois premiers caractères du fichier
fscanf(f,"%d",&maillage->nbSommets); // lecture du nombre de sommets
...
maillage->sommets = (float*)malloc(sizeof(float)*3*maillage->nbSommets); // allocation mémoire pour le tableau de sommets
...
for (i=0;inbSommets*3;i++) fscanf (f,"%f",maillage->sommets+i); // lecture des coordonnées x,y,z des sommets du maillage
...
fclose(f); // fermeture du fichier
return maillage; // renvoi du pointeur sur la structure de maillage
-
Ecrivez la fonction void traceMaillage (pMaillage maillage) {...} qui trace un maillage en utilisant les Vertex Array (pour l'instant, sans les normales).
-
Chargez le maillage du buddha, puis appelez la fonction pour tracer le maillage une fois le repère objet placé au centre de la pièce principale, en rouge. Vous devez obtenir le résultat illustré ci-dessous : sur l'image de gauche, c'est le petit point rouge que l'on voir au milieu de la pièce principale ! A droite, c'est encore lui après s'être rapproché.
NB : Une façon simple d'appeler cette fonction de chargement d'un maillage est la suivante : soit la variable globale static pMaillage _buddha, l'appel de la fonction peut être _buddha = chargeMaillage ("Maillages/buddha.off") (si le fichier buddha.off est dans le répertoire Maillages).
-
En fait, quand on charge un maillage à partir d'un fichier .off, on ne sait jamais quelle va être la taille de l'objet, ni si il va être centré. Pour éviter ce problème, dans la fonction chargeMaillage (...), vous allez calculer le centre de gravité du maillage (la somme des sommets divisée par leur nombre) et vous allez centrer le maillage en (0,0,0) (en soustrayant le centre de gravité à chaque sommet). Ainsi, tous les maillages chargés seront centrés.
Ensuite, vous allez calculer la plus grande coordonnée en valeur absolue (pour la valeur absolue, vous pouvez utiliser la fonction float fabs (float f) qui est dans la librairie math.h) et vous allez diviser toutes les coordonnées par cette valeur positive. Ainsi, le maillage aura toutes ses coordonnées comprises entre -1 et 1. Tous les maillages chargés auront grossièrement la même taille. Voici comment vous devez voir le buddha maintenant :
Expliquez pouquoi le buddha apparait tel qu'il est sur l'image de droite (avec le sol en plein milieu).
-
Toujours dans la fonction chargeMaillage (...), ajoutez le calcul de la coordonnée en Y la plus petite et stockez la dans le champs minY de la structure de maillage.
Ajoutez une translation dans la fonction traceMaillage (...) pour que le bas de l'objet se trace toujours en y=0 dans le repère objet. Vous devez maintenant obtenir le résultat qui suit. Dans la fonction traceMaillage (...), n'oubliez pas d'entourer le positionnement du maillage ainsi que son tracé d'un glPushMatrix() et un glPopMatrix(). Pourquoi est-ce nécessaire ?
-
Comment augmenter ou réduire la taille de l'objet sans modifier la fonction traceMaillage (...) ?
Affichez le bouddha avec une taille de 5 afin d'obtenir :
-
Allumez la lumière GL_LIGHT0. L'éclairage du buddha est-il correct ? Pourquoi ?
Comment calcule-t-on la normale d'une facette triangulaire ? Donnez la formule pour normer un vecteur. Les normales aux sommets du maillage se calculent de la façon suivante :
Dans la fonction chargeMaillage (...), après avoir alloué l'espace mémoire pour le tableau normales de la structure de maillage, on initialise les normales en chaque sommet à (0,0,0) : chaque normale a 3 coordonnées x,y,z. Puis, à chaque fois qu'une facette est chargée dans le tableau d'index, on calcule sa normale, on la norme et on l'ajoute à la normale de chaque sommet de la facette. Quand toutes les facettes du fichier ont été parcourues, le tableau des normales est complet et correct.
Il ne reste plus qu'à ajouter les fonctions nécessaire à la prise en compte du tableau de normales dans la fonction traceMaillage (...) pour obtenir le résultat ci-dessous quand on affecte le matériau suivant au maillage (lumière GL_LIGHT0 et spot GL_LIGHT2 allumés) :
static float laiton_ambient [] = {0.4,0.2,0.08,1.0};
static float laiton_diffuse [] = {0.4,0.2,0.08,1.0};
static float laiton_specular [] = {1.,0.5,0.2,1.0};
static float laiton_shininess = 90.;
Pour que toutes les composantes du matériau soient prises en compte, affectez les valeurs au début de la fonction traceMaillage (...) et ajoutez : en première ligne glDisable (GL_COLOR_MATERIAL) et en dernière ligne de la fonction glEnable (GL_COLOR_MATERIAL). Expliquez pourquoi il est nécessaire de faire ceci.
-
Maintenant vous avez tout ce qu'il vous faut pour remplir le musée. Téléchargez les maillages suivant et placez les objets dans le musée pour obtenir le résultat ci-dessous (dans ce musée, le bunny est de taille 2, la tête de Max Plank est de taille 3, le triceratops est de taille 15 et le buddha est toujours de taille 5). La couleur des socles est glColor3f (0.9,0.2,0.2) et le matériau est identique à celui du robo.
bunny.off
max.off
triceratops.off
Fin du TP7