Déplacer un bitmap en 3D

Jeff Molofee (NeHe)

Traducteur

Mickaël Le Trocquer

Ce tutoriel couvre quelques demandes que vous m'avez faites. Vous voulez savoir comment faire bouger les objets que vous avez créés dans votre univers 3D. Vous voulez savoir comment dessiner une image à l'écran, sans la partie noire de l'image qui cache ce qu'il y a derrière elle. Vous voulez des animations simples et utilisez plus la transparence. Ce tutoriel va vous expliquer tout ceci. Vous remarquerez qu'il n'y a pas de boîtes qui tournent. Les précédentes leçons introduisaient les bases d'OpenGL. Chaque tutoriel ajoutant de nouvelles notions par rapport au précédent. Ce tutoriel est un mélange de tout ce que vous avez appris jusqu'à présent, avec des informations complémentaires pour savoir comment faire bouger vos objets en 3D. Ce tutoriel est un tout petit peu plus compliqué, donc assurez-vous de bien avoir compris les leçons précédentes avant d'attaquer ce tutoriel. ♪

- Titre : Déplacer un bitmap en 3D

- Auteur : Jeff Molofee (NeHe)

- Traducteur : Mickaël Le Trocquer

- Parution : 23 mars 2007

- Mise à jour : 19 décembre 2012

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2007 Nehe Gamedev.net. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Contributions

- II. Tutoriel

- II-A. Inclusions et déclarations

- II-B. La fonction LoadBMP

- II-C. La fonction LoadGLTextures

- II-D. La fonction InitGL

- II-E. La fonction DrawGLScene

- II-F. La gestion clavier

- II-G. Conclusion

- III. Téléchargements

- IV. Remerciements

- V. Liens

I - Contributions

Nous recherchons toujours des traducteurs pour finir ce projet de traduction des articles de NeHe. Votre aide serait appréciée ! Vous êtes intéressé ? Alors, contactez-nous à l'adresse suivante : nehe@redaction-developpez.com.

II - Tutoriel

Bienvenue dans le tutoriel 9. À partir de maintenant vous devez être en mesure de mieux comprendre OpenGL. Vous avez appris tout ce qu'il faut pour créer et initialiser une fenêtre OpenGL, appliquer une texture sur un objet qui tourne en utilisant des effets d'éclairages et de transparences. Ceci sera le premier tutoriel du niveau supérieur. Vous allez apprendre : à bouger une image dans un univers 3D, enlever les pixels noirs autour de l'image (en utilisant le mélange), ajouter des couleurs à une texture noire et blanche, et finalement vous apprendrez comment créer des couleurs sympathiques et des animations en utilisant plusieurs textures colorées ensemble.

II-A - Inclusions et déclarations

Nous allons modifier le code de la première leçon dans ce tutoriel. Nous allons commencer par ajouter quelques nouvelles variables au début du programme. Je vais réécrire la section entière ainsi cela sera plus facile de voir où il y a eu des changements.

En-têtes et déclarations

#include    <windows.h>                       // En-tête pour Windows
#include    <stdio.h>                         // En-tête pour les entrees/sorties standards (NOUVEAU)
#include    <gl\gl.h>                         // En-tête pour la bibliotheque OpenGL32
#include    <gl\glu.h>                        // En-tête pour la bibliotheque GLu32
#include    <gl\glaux.h>                      // En-tête pour la bibliotheque GLaux

HDC         hDC=NULL;                         // Contexte prive du systeme GDI
HGLRC       hRC=NULL;                         // Contexte de rendu permanent
HWND        hWnd=NULL;                        // Contient notre descripteur de fenêtre
HINSTANCE   hInstance;                        // Sauve l'instance de l'application
bool        keys[256];                        // Tableau utilise pour les routines du clavier
bool        active=TRUE;                      // Drapeau d'activation de la fenetre
bool        fullscreen=TRUE;                  // Drapeau pour le mode plein écran (Vrai par defaut)

Les lignes suivantes sont nouvelles. twinkle et tp sont deux variables booléennes, ce qui signifie qu'elles peuvent contenir soit TRUE ou FALSE. twinkle permet de mémoriser si l'effet de scintillement doit être actif ou non. tp est utilisé pour savoir si la touche 'T' est dans un état pressé ou relâché (touche T pressée : tp = TRUE ; touche T relâchée : tp = FALSE).

Drapeaux d'états

BOOL    twinkle;                         // Etoiles brillantes ?
BOOL    tp;                              // Touche 'T' appuyee ?

num va permettre de mémoriser combien d'étoiles seront dessinées sur l'écran. Cette valeur sera stockée dans une constante. Ce qui signifie qu'elle ne pourra pas être changée dans le code. Nous la mettons dans une constante, car vous ne pouvez pas redéfinir la taille d'un tableau. Donc, si nous avons déclaré un tableau avec seulement 50 étoiles, et que nous décidons d'augmenter num à 51 dans le code, le tableau ne peut pas s'agrandir à 51 étoiles, et donc une erreur apparaîtra. Vous pouvez changer cette valeur pour celle que vous voulez uniquement dans la ligne suivante. N'essayez pas de changer la valeur de num plus loin dans le code, car vous risqueriez d'obtenir une erreur.

Constante représentant le nombre d'étoiles à utiliser

const    num=50;                             // Nombre d'étoiles à dessiner

Maintenant nous allons créer une structure. Le mot structure est quelque peu intimidant, mais en vérité il n'y a rien de bien compliqué. Une structure est un groupe de données simples (variables, etc.) qui, une fois regroupées, permettent de définir un ensemble plus complexe. Nous savons que nous devons garder une trace de nos étoiles. Vous allez voir que la 7e ligne ci-dessous est stars. Nous savons que chaque étoile aura trois valeurs pour définir la couleur, et ces valeurs seront des entiers. La 3e ligne (int r, g, b) définit trois entiers. Un pour le rouge (r), un pour le vert (g), et un pour le bleu (b). Nous savons que les étoiles seront à différentes distances du centre de l'écran, et qu'elles pourront être placées sur l'un des 360 angles différents à partir du centre. Si vous regardez à la 4e ligne ci-dessous, vous remarquerez une valeur réelle nommée dist. Elle permettra de mémoriser la distance entre l'étoile et le centre de l'écran. La 5e ligne crée un réel nommé angle. Cela permet de stocker les angles des étoiles.

Ainsi, maintenant nous avons ce groupement de données qui permet de décrire la couleur, la distance, et l'angle d'une étoile sur l'écran. Par contre nous devons mémoriser les informations de plus d'une étoile. Au lieu de créer 50 valeurs pour le rouge, 50 pour le vert, 50 pour le bleu, 50 pour la distance, et 50 pour l'angle, nous allons juste créer un tableau nommé star. À chaque case dans le tableau star, nous y stockerons une occurrence de la structure stars. Nous créons ce tableau à la 8e ligne ci-dessous. Voici le détail de la 8e ligne : stars star[num]. Le type du tableau est stars. stars est une structure. Ainsi le tableau contiendra toutes les informations des structures. Le nom du tableau est star. Le nombre de cases du tableau est [num]. Or comme num = 50, nous avons maintenant un tableau nommé star. Notre tableau stocke les éléments de la structure stars. C'est beaucoup plus facile ainsi de mémoriser les informations sur les étoiles qu'avec des variables séparées. Ce qui aurait été une chose stupide à faire, et cela ne nous aurait pas permis d'ajouter ou retirer facilement des étoiles en modifiant la valeur constante de num.

Tableau de structures définissant les étoiles

typedef struct                      // On cree une structure pour definir une étoile
{
int r, g, b;                    // Le couleur de l'etoile
GLfloat dist;                   // La distance de l'etoile par rapport au centre
GLfloat angle;                  // L'angle courant de l'étoile
}stars;                             // Le nom de la structure est stars

stars star[num];                    // Cree un tableau 'star' de 'num' case contenant des structures 'stars'

Ensuite, nous allons déclarer des variables pour mémoriser à quelle distance sont les étoiles de l'observateur (zoom), et quel est l'angle à partir duquel on voit les étoiles (tilt). Nous créons également une variable nommée spin qui permettra de faire tourner le scintillement de l'étoile sur l'axe des z, comme si elles tournaient sur elle-même.

loop est une variable qu'on utilisera dans le programme pour dessiner les 50 étoiles, et texture[1] permettra de stocker une texture en noir et blanc que nous aurons chargée. Si vous voulez plus de textures, vous devez modifier la valeur de 1 à autant de texture que vous souhaitez utiliser.

Déclaration des variables globales

GLfloat   zoom=-15.0f;                                    // Distance entre l'observateur et les etoiles
GLfloat   tilt=90.0f;                                     // Inclinaison de l'observateur
GLfloat   spin;                                           // Rotation du scintillement des etoiles
GLuint    loop;                                           // Variable globale pour le parcours des etoiles
GLuint    texture[1];                                     // Tableau pour stocker une texture
LRESULT   CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);   // Declaration de WndProc

Après le code ci-dessus, nous allons maintenant ajouter la fonction permettant de charger notre texture. Je n'ai pas besoin d'expliquer en détail le code. C'est le même code que nous avons utilisé dans les leçons 6, 7 et 8. L'image que nous allons charger ce coup-ci se nomme star.bmp. Nous allons générer uniquement une texture par l'intermédiaire de glGenTextures(1, &texture[0]). La texture utilisera le filtrage linéaire.

II-B - La fonction LoadBMP

Fonction pour charger l'image

AUX_RGBImageRec *LoadBMP(char *Filename)    // Permet de charger une fichier image Bitmap
{
FILE *File=NULL;                        // Descripteur de fichier
if (!Filename)                          // On s'assure qu'un nom de fichier a ete passe en parametre
{
return NULL;                        // Sinon on retourne NULL
}
File=fopen(Filename,"r");               // On verifie que le fichier existe
if (File)                               // Est-ce qu'il existe ?
{
fclose(File);                       // On ferme le descripteur
return auxDIBImageLoad(Filename);   // Charge l'image, et retourne un pointeur
}
return NULL;                            // Si le fichier n'existe pas, on retourne NULL
}

II-C - La fonction LoadGLTextures

C'est la portion de code qui permet de charger l'image (en appelant le code précédent) et qui la convertit en une texture. Status est utilisée pour indiquer si la texture a bien été chargée et créée.

Charge une image, et la convertie en texture

int LoadGLTextures()                                 // Charge une image et la convertie en texture
{
int Status=FALSE;                                // Indique le bon deroulement de l'operation
AUX_RGBImageRec *TextureImage[1];                // Cree un emplacement memoire pour la texture
memset(TextureImage,0,sizeof(void *)*1);         // Reinitialise le pointeur à NULL
// Charge l'image, verifie les erreurs, on quitte si l'image n'est pas trouvee
if (TextureImage[0]=LoadBMP("Data/Star.bmp"))
{
Status=TRUE;                                 // Initialise Status à TRUE
glGenTextures(1, &texture[0]);               // Genere une texture
// Cree une texture filtree lineairement
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX,
TextureImage[0]->sizeY,0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
}
if (TextureImage[0])                            // Si la texture existe
{
if (TextureImage[0]->data)                  // Si l'image de la texture existe
{
free(TextureImage[0]->data);            // On libere la memoire de l'image de la texture
}
free(TextureImage[0]);                      // On libere la structure de la texture
}
return Status;                                  // On retourne le statut
}

II-D - La fonction InitGL

Maintenant nous allons initialiser OpenGL pour qu'il fasse ce qu'on veut. Nous n'utiliserons pas de test de profondeur dans ce projet, donc assurez-vous, si vous utilisez le code de la leçon 1, d'enlever glDepthFunc(GL_LEQUAL); et glEnable(GL_DEPTH_TEST); sinon vous n'obtiendrez pas de résultats concluants. Nous allons utiliser le plaquage de texture dans ce code, donc assurez-vous de ne pas laisser de lignes de code présentes dans la leçon 1.Vous remarquerez également que l'on active le plaquage de texture, ainsi que le mélange.

Initialisation d'OpenGL

int InitGL(GLvoid)                                   // Tous les reglages d'OpenGL se font ici
{
if (!LoadGLTextures())                           // On appelle notre fonction pour charger les textures
{
return FALSE;                                // Si les textures ne se sont pas chargees, on retourne FALSE
}
glEnable(GL_TEXTURE_2D);                         // Activation de l'application de textures
glShadeModel(GL_SMOOTH);                         // Active les ombres douces
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);            // Fond d'ecran noir
glClearDepth(1.0f);                              // Configuration du tampon de profondeur
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);     // Pour avoir des jolis calculs de perspective
glBlendFunc(GL_SRC_ALPHA,GL_ONE);                // Positionne la fonction de melange sur la transparence
glEnable(GL_BLEND);                              // Activation du melange

Le code suivant est nouveau. Il permet d'initialiser l'angle, la distance et la couleur de chaque étoile au démarrage. Remarquez comment il est facile de changer les informations dans une structure. La boucle parcourt les 50 étoiles. Pour changer l'angle de star[1], vous avez juste à écrire star[1].angle = {un nombre}. C'est simple !

Début de la boucle sur les étoiles

for (loop=0; loop<num; loop++)     // Cree une boucle sur les etoiles
{
star[loop].angle=0.0f;         // Initialise l'angle des etoiles à zero

Je calcule la distance en prenant l'index de l'étoile courante (donc la valeur de loop) et en le divisant par le maximum d'étoiles présentes. Ensuite, je le multiplie le résultat par 5.0f. Pour résumer, cela permet de bouger chaque étoile un petit peu plus loin que la précédente. Quand loop sera égale à 50 (la dernière étoile), loop divisé par num sera égale à 1.0f. La raison pour laquelle je multiplie par 5.0f est parce que 1.0f * 5.0f est égal à 5.0f. 5.0f est l'extrémité de l'écran. Je n'ai pas envie que des étoiles soient en dehors de l'écran, donc 5.0f est parfait. Si vous fixez le zoom plus loin dans l'écran, vous feriez mieux d'utiliser une valeur plus grande que 5.0f, mais vos étoiles seront un peu plus petites (à cause de la perspective).

Vous remarquerez que la couleur de chacune des étoiles est attribuée suivant des valeurs aléatoires comprises entre 0 et 255. Vous devez être surpris que l'on puisse utiliser des valeurs aussi grandes, alors qu’habituellement elles sont comprises entre 0.0f et 1.0f. Quand nous fixons la couleur, nous utilisons glColor4ub au lieu de glColor4f. ub signifie Unsigned Byte. Un octet peut contenir n'importe quelle valeur comprise entre 0 et 255. Dans ce programme, c'est plus facile d'utiliser des octets que des nombres réels pour obtenir des valeurs aléatoires.

Initialisation de la position et de la couleur

star[loop].dist=(float(loop)/num)*5.0f;          // Calcul la distance depuis le centre
star[loop].r=rand()%256;                         // Donne a star[loop] une valeur aleatoire pour le rouge
star[loop].g=rand()%256;                         // Donne a star[loop] une valeur aleatoire pour le vert
star[loop].b=rand()%256;                         // Donne a star[loop] une valeur aleatoire pour le bleu
}
return TRUE;                                         // L'initialisation s'est correctement deroulee
}

II-E - La fonction DrawGLScene

Le code pour redimensionner la fenêtre est le même, donc nous allons passer directement à la partie où on dessine. Si vous utilisez le code de la leçon une, supprimer la fonction DrawGLScene, et copiez juste ce qu'il y a en dessous. Il y a uniquement deux lignes de code dans la leçon un de toute façon, donc vous n'avez pas beaucoup de choses à supprimer.

Fonction de dessin

int DrawGLScene(GLvoid)                                    // C'est ici que l'on fera tous les dessins
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    // Efface l'écran et le tampon de profondeur
glBindTexture(GL_TEXTURE_2D, texture[0]);              // Selectionne notre texture
for (loop=0; loop<num; loop++)                         // Boucle sur nos etoiles
{
glLoadIdentity();                                  // Reinitialise la vue avant de dessiner chacune des etoiles
glTranslatef(0.0f,0.0f,zoom);                      // Zoom dans l'écran (En utilisant la valeur de 'zoom')
glRotatef(tilt,1.0f,0.0f,0.0f);                    // Tourne la vue (En utilisant la valeur de 'tilt')

Maintenant nous allons bouger les étoiles. Les étoiles partent du milieu de l'écran. La première chose à faire est de tourner la scène sur l'axe des x. Si on tourne de 90 degrés, l'axe des x n'ira plus de gauche à droite, mais d'avant vers l'arrière de l'écran. Voici un exemple pour clarifier un peu le problème. Imaginez que vous êtes au milieu d'une pièce. Maintenant imaginez qu'il y a écrit -x sur le mur à gauche de vous, -z sur le mur devant vous, +x sur le mur à droite de vous, +z sur le mur derrière vous. Si la pièce tourne de 90 degrés vers la droite, mais que vous ne bougez pas, sur le mur en face de vous il n'y aura plus écrit -z mais -x. Tous les murs auront bougé. -z sera à droite, +z sera à gauche, -x sera devant et +x derrière vous. Vous comprenez ? En appliquant une rotation sur votre scène, vous changez les directions des plans x et z.

La seconde ligne de code permet de bouger vers d'une valeur positive sur le plan des x. Normalement une valeur positive sur x devrait nous faire bouger vers la partie droite de l'écran (où est +x habituellement), mais comme nous avons fait une rotation sur le plan des y, le '+x' peut se trouver n'importe où. Si nous appliquons une rotation de 180 degrés, il sera sur le côté gauche de l'écran au lieu de la partie droite. Donc lorsque vous allez bouger d'une valeur positive sur le plan des x, vous pouvez donc bouger vers la gauche, la droite, l'avant ou l'arrière.

Rotation et déplacement

glRotatef(star[loop].angle,0.0f,1.0f,0.0f);         // Tourne l'étoile selon son angle
glTranslatef(star[loop].dist,0.0f,0.0f);            // Bouge d'une valeur positive sur le plan des X

Maintenant nous allons passer à une partie un peu plus délicate. L'étoile est en fait une texture plate. Maintenant si vous dessinez un rectangle plat au centre de l'écran et que vous le texturez, il sera joli. Il vous fera face. Mais si vous appliquez une rotation sur l'axe des y de 90 degrés, la texture sera face aux côtés droits et gauches de l'écran. La seule chose que vous verrez sera une ligne fine. Nous ne voulons pas que cela arrive. Nous voulons que les étoiles soient face à l'écran en permanence, peu importe si cela nous oblige à faire plus de rotation ou d'inclinaison de l'écran.

On fait ceci en annulant la rotation que nous avons faite précédemment, juste avant de dessiner l'étoile. Vous annulez la rotation dans le sens inverse. Dans la partie précédente, nous avons incliné l'écran, et ensuite nous avons appliqué une rotation aux étoiles en fonction d'un angle. Dans l'ordre inverse, nous devons faire une rotation inverse aux étoiles de l'angle adéquat. Pour ce faire, nous allons utiliser la valeur négative de l'angle, et appliquer la rotation avec ce nouvel angle. Ainsi, si nous avions tourné l'étoile de 10 degrés, en appliquant une rotation de -10 degrés, l'étoile sera de nouveau en face de l'écran. Donc la première ligne ci-dessous annule la rotation sur l'axe des y. Ensuite nous avons besoin d'annuler l'inclinaison de l'écran sur l'axe des x. Pour faire ceci, nous inclinons juste l'écran d'une valeur de -tilt. Après avoir annulé les rotations en x et en y, l'étoile fera face de nouveau complètement à l'écran.

Annulation des rotations précédentes

glRotatef(-star[loop].angle,0.0f,1.0f,0.0f);     // Annule la rotation de l'angle
glRotatef(-tilt,1.0f,0.0f,0.0f);                 // Annule l'inclinaison de l'ecran

Si twinkle est égale à TRUE, nous dessinerons une étoile qui ne tourne pas à l'écran. Pour obtenir une couleur différente, nous prenons le nombre maximum d'étoiles (num) et le soustrayons à l'index courant pour les étoiles (loop), ensuite on soustrait 1 car notre boucle (loop) ne va qu’entre 0 et num-1. Si le résultat est égal à 10, nous utiliserons la couleur de l'étoile numéro 10. De cette manière, les couleurs de deux étoiles seront différentes. Ce n'est pas la meilleure manière de faire, mais elle est efficace. La dernière valeur est la valeur alpha. Plus la valeur sera petite, plus l'étoile sera sombre.

Si twinkle est égale à TRUE, chaque étoile sera dessinée en double. Cela ralentira le programme légèrement, selon l'ordinateur que vous avez. Si twinkle est activé, les couleurs des deux étoiles se mélangeront en créant des couleurs vraiment sympathiques. De plus comme cette étoile ne tournera pas, elle apparaîtra comme si elle était animée lorsque twinkle sera activé (regardez par vous-même si vous ne comprenez pas ce que je dis).

Remarquez également comment il est facile d'ajouter des couleurs à la texture. Même si la texture est blanche et noire, elle prendra sa véritable apparence en fonction de la couleur que l'on sélectionne avant de dessiner la texture. De plus, prenez note que nous utilisons des octets pour les valeurs de la couleur plutôt que des nombres réels. Même la valeur de la composante alpha est un octet.

Dessin du scintillement

if (twinkle)                           // Si le scintillement est active
{
// Applique une couleur en utilisant des octets
glColor4ub(star[(num-loop)-1].r,star[(num-loop)-1].g,star[(num-loop)-1].b,255);
glBegin(GL_QUADS);                 // Commence a dessiner notre carre texture
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,-1.0f, 0.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,-1.0f, 0.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 0.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f);
glEnd();                           // Fin du dessin de notre carre texture
}

Maintenant nous allons dessiner l'étoile principale. La seule différence avec le code précédent est que l'étoile est toujours dessinée et cette étoile tourne autour de l'axe des z.

Dessin de l'étoile principale

glRotatef(spin,0.0f,0.0f,1.0f);          // Applique une rotation a l'etoile sur l'axe des Z
// Applique une couleur en utilisant des octets
glColor4ub(star[loop].r,star[loop].g,star[loop].b,255);
glBegin(GL_QUADS);                       // Commence a dessiner notre carre texture
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,-1.0f, 0.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,-1.0f, 0.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 0.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f);
glEnd();                                // Fin du dessin de notre carre texture

Ici est la partie où nous effectuons tous les mouvements. Nous tournons l'étoile normale en incrémentant la valeur de spin. Ensuite on change l'angle de chacun des angles. L'angle de chaque étoile est incrémenté de loop/num. Ce qui fait que les étoiles tournent plus vite si elles sont éloignées du centre. Les étoiles les plus proches du centre tourneront moins vite. Et enfin, on décrémente la distance entre chaque étoile et le centre de l'écran. Nous aurons l'impression que les étoiles se font aspirer vers le centre de l'écran.

Mouvement des étoiles

spin+=0.01f;                                // Change la valeur d'inclinaison des etoiles
star[loop].angle+=float(loop)/num;          // Change l'angle de l'etoile
star[loop].dist-=0.01f;                     // Change la distance entre l'etoile et le centre

Les lignes suivantes vérifient si les étoiles sont arrivées au centre de l'écran ou non. Quand une étoile touche le centre de l'écran, on lui attribue une nouvelle couleur, et on la bouge de 5 unités du centre, ainsi elle peut reprendre son parcours vers le centre comme si c'était une nouvelle étoile.

Traitement des étoiles arrivées au centre de l'écran

if (star[loop].dist<0.0f)            // Est-ce que l'etoile est arrivee au centre ?
{
star[loop].dist+=5.0f;           // Bouge l'etoile de 5 unités loin du centre
star[loop].r=rand()%256;         // Attribution d'une nouvelle composante rouge
star[loop].g=rand()%256;         // Attribution d'une nouvelle composante verte
star[loop].b=rand()%256;         // Attribution d'une nouvelle composante bleue
}
}
return TRUE;                             // Tout s'est bien deroule
}

Maintenant nous allons ajouter le code qui vient vérifier si une touche a été pressée. Allez plus bas au niveau de la fonction WinMain(). Regardez la ligne SwapBuffers(hDC). Nous allons ajouter le code qui vérifier les touches juste en dessous de cette ligne.

II-F - La gestion clavier

Les lignes suivantes vérifient si la touche 'T' a été pressée. Si elle a été appuyée et qu'elle n'est pas maintenue pressée, ce qui suit sera exécuté. Si twinkle est égale à FALSE, on lui attribuera TRUE, sinon on lui attribuera FALSE. Lorsque 'T' sera pressée, tp prendra la valeur TRUE. Cela permet d'éviter que le code soit exécuté encore et encore durant la période où la touche 'T' est pressée.

Activation du scintillement

SwapBuffers(hDC);               // Echange les tampons (Double Buffering)
if (keys['T'] && !tp)           // Si 'T' est pressee et tp est egal a FALSE
{
tp=TRUE;                    // Si oui, on affecte TRUE à tp
twinkle=!twinkle;           // On affecte a twinkle l'oppose de ce qu'il est
}

Le code qui suit vérifie si vous avez relâché la touche 'T'. Si c'est le cas, tp prend la valeur FALSE. En appuyant sur la touche 'T' cela ne fera rien tant que tp n'est pas égal à FALSE. Donc cette portion de code est très importante.

Relâchement de la touche 'T'

if (!keys['T'])                   // Est-ce que la touche 'T' a ete relachee ?
{
tp=FALSE;                     // Si oui, tp devient egal a FALSE
}

Le reste du code vérifie si les touches flèche haut, flèche bas, page haut ou page bas sont pressées.

Vérification de l'état des touches

if (keys[VK_UP])                // Est-ce que la touche 'fleche haut' est pressee ?
{
tilt-=0.5f;                 // Incline l'ecran vers le haut
}
if (keys[VK_DOWN])              // Est-ce que la touche 'fleche bas' est pressee ?
{
tilt+=0.5f;                 // Incline l'ecran vers le bas
}
if (keys[VK_PRIOR])             // Est-ce que la touche 'page haut' est pressee ?
{
zoom-=0.2f;                 // Zoom vers l'interieur
}
if (keys[VK_NEXT])              // Est-ce que la touche 'page bas' est pressee ?
{
zoom+=0.2f;                 // Zoom vers l'exterieur
}

Comme pour tous les précédents tutoriels, assurez-vous que le titre de la fenêtre est correct.

Modification du titre de la fenêtre

if (keys[VK_F1])                    // Est-ce que la touche 'F1' est pressee ?
{
keys[VK_F1]=FALSE;              // Si oui, on la remet a FALSE
KillGLWindow();                 // On detruit notre fenetre courante
fullscreen=!fullscreen;         // On inverse le mode Plein ecran / Mode fenetre
// On Recree notre fenetre OpenGL
if (!CreateGLWindow("Tutoriel de NeHe sur les textures, l'eclairage et le clavier",640,480,16,fullscreen))
{
return 0;                   // On quitte si la fenetre ne s'est pas creee
}
}
}
}

II-G - Conclusion

Dans ce tutoriel, j'ai essayé de vous expliquer avec le plus de détails possible comment charger une image en noir et blanc, supprimer les espaces noirs autour de l'image (en utilisant la transparence), ajouter des couleurs à l'image, et bouger l'image dans la scène en 3D. Je vous ai également montré comment créer de jolies couleurs et des animations en se faisant chevaucher une seconde copie de l'image au-dessus de l'image originale. Une fois que vous avez bien compris tout ce que j'ai expliqué jusqu'ici, vous ne devriez pas avoir de problème pour réaliser vos propres demos 3D. Toutes les bases d'OpenGL ont été parcourues !

Enfin, voici une petite image de ce que vous devez voir :

Image de l'application

Jeff Molofee ( NeHe )

III - Téléchargements

Compte tenu du nombre de versions de codes sources pour les tutoriels nehe, nous les laissons en anglais. En principe, si vous avez compris le code présenté dans ce tutoriel (et les tutoriels antérieurs), vous n'aurez pas de mal à le comprendre :

- Borland C++ Builder 6(Conversion par Christian Kindahl)

- C#(Conversion par Brian Holley)

- Code Warrior 5.3(Conversion par Scott Lupton)

- CygWin(Conversion par Stephan Ferraro)

- D(Conversion par Familia Pineda Garcia)

- Delphi(Conversion par Michal Tucek)

- Dev C++(Conversion par Dan)

- Euphoria(Conversion par Evan Marshall)

- Game GLUT(Conversion par Milikas Anastasios)

- Irix(Conversion par Lakmal Gunasekara)

- Java(Conversion par Jeff Kirby)

- Jedi-SDL(Conversion par Dominique Louis)

- JOGL(Conversion par Abdul Bezrati)

- LCC Win32(Conversion par Robert Wishlaw)

- Linux(Conversion par Richard Campbell)

- Linux GLX(Conversion par Mihael Vrbanec)

- Linux SDL(Conversion par Ti Leggett)

- LWJGL(Conversion par Mark Bernard)

- Mac OS(Conversion par Anthony Parker)

- Mac OS X/Cocoa(Conversion par Bryan Blackburn)

- MASM(Conversion par Nico (Scalp))

- VC++/OpenIL(Conversion par Denton Woods)

- Pelles C(Conversion par Pelle Orinius)

- Power Basic(Conversion par Angus Law)

- Python(Conversion par Ryan Showalter)

- Solaris(Conversion par Lakmal Gunasekara)

- Visual Basic(Conversion par Peter de Tagyos)

- Visual Fortran(Conversion par Jean-Philippe Perois)

- Visual Studio

- Visual Studio NET(Conversion par Grant James)

IV - Remerciements

Merci à fearyourself et à jc_cornic pour leur relecture.

V - Liens

Sommaire

Tutoriel précédent : Transparence

Tutoriel prochain : Charger et se déplacer dans un univers 3D