[C++/OpenGL] Génération d'un terrain 3D à partir d'un bitmap
* Général *
Pour lire une image BMP, il faut l'ouvrir en lecture binaire, puis tout simplement récupérer les structures de données. Pour cela nous allons donc utiliser les outils fournis de base pour vraiment comprendre le fonctionnement des structures windows et celui des fichiers binaires (et de l'application des textures par la même occasion). Ensuite une fois notre image ouverte, on va récupérer les données des pixels dans un tableau à 2 dimensions.
* Quelques variables globales *
Le but ici est de faire quelque chose de simple pour comprendre le fonctionnement global et ensuite le perfectionner. On va donc coder ce générateur dans un optique d'avoir une application fonctionnelle pour maintenant mais pas forcément ayant une évolution future.
On proposera à l'utilisateur plusieurs options d'affichage :
- Rotation de la scène
- Affichage en mode texturé ou filaire
- Nombre de polygones pour le terrain
- Taille d'un polygone
- Hauteur max du terrain
bool CONFIG_filaire = true; // Filaire ou non int CONFIG_hautmax = 12; // Hauteur max en coordonnées int CONFIG_max_points = 40; // Nombre de poygones par côté double CONFIG_taille_carre = 40.0; // Taille d'un poygone bool CONFIG_rotate = false; // Terrain qui tourne ou non |
* Lecture de l'image et récupération des informations *
FILE *BMPFile; BITMAPFILEHEADER bfh; BITMAPINFOHEADER bih; unsigned char *bitmapImage; |
On déclare simplement notre variable de traitement du fichier et de stockage des entêtes (cf page précédente). La variable bitmapImage est un pointeur vers des caractères non signés (donc une valeur comprise entre 0 et 255). Les données d'une image sont renvoyées comme un tableau d'octet. Pour y accéder il suffira de faire : bitmapImage [index_octet] et on aura la valeur de la couleur comprise entre 0 et 255 bien entendu.
BMPFile = fopen(filename, "rb"); |
On ouvre le fichier en mode "rb", lecture binaire pour pouvoir y récupérer les données brutes.
fread (&bfh, sizeof (BITMAPFILEHEADER), 1, BMPFile); fread (&bih, sizeof (BITMAPINFOHEADER), 1, BMPFile); fread (bitmapImage, 1, bih.biSizeImage, BMPFile); fclose(BMPFile); |
Cette étape consiste à stocker dans nos variables bfh et bih tous les (sizeof) octets lus. Sizeof (type) renvoyant donc la taille des structures. fread se déplace dans le fichier au fur et à mesure que la lecture se déroule. Les 2 entêtes se suivant, il est initule de se déplacer manuellement dans le fichier.
Les entêtes étant stockées, nous avons accès maintenant aux informations des structures, dont une vitale pour la récupération des données images : biSizeImage, la taille en octets des données.
La récupération étant terminée, on peut fermer le fichier.
* Exploitation des résultats *
Nous allons donc stocker nos valeurs dans un tableau de pixel de dimensions précisée plus haut dans les configurations. Ce tableau sera un tableau d'entiers appelé "map". Notre image fait 256x256 alors que notre tableau fera 40x40 de base (ce qui fait déjà 1600 points à placer, sachant qu'un point est lui même composé d'un rectangle composé de 2 triangles ... ). Il va donc falloir tout remettre à l'échelle. Voyons un peu ce que cela donne :
di = (di / CONFIG_max_points) * h; dj = (dj / CONFIG_max_points) * w; i = (int)di; j = (int)dj; return (int) ((bitmapImage[i*w*3+j*3]/256.0)*CONFIG_hautmax); |
Avec di et dj, deux variables flottantes pour avoir une mise à l'échelle. On les convertit ensuite en entier et on va chercher la position du pixel en faisant le cacul suivant :
ligne x largeur x 3 + colonne x 3
On multiplie par 3 car nous sommes dans une image en 24 bits, donc 1 octet par composant de couleur, et en N&B les 3 valeurs sont égales (R=V=B), on va donc ici récupérer la première valeur. Stockons maintenant les résultats.
Cette étape est relativement simple étant donné que nous avons tout ce qu'il nous faut : les pixels de l'image, la taille de notre image ... Une boucle imbriquée dans une autre fera l'affaire :
for(i=0;i < CONFIG_max_points;i++) for(j=0;j < CONFIG_max_points;j++) map[i][j] = GetHauteur(i,j, bih.biWidth, bih.biHeight); |
Voilà, nous avons donc une fonction prête à l'emploi qui lit un fichier Bitmap, récupère les pixels, et en extrait la valeur de la première couleur et qui met tout à l'échelle. Il ne reste maintenant plus qu'à afficher tout ça.