Home Index Search Links About Us
[LinuxFocus Image]
[Navegation Bar]
  Nouvelles   Archives

La Programmation GLUT : Fenêtres et Animations

par Miguel Angel Sepúlveda


Introduction

Initialisations

Traitement des  Evènements

Exemple d'Animation



Le site GLUT
Les Auteurs de GLUT
Les sources de GLUT-3.6

Introduction

Pour des raisons de conception, la spécification d'OpenGL a été écartée de toutes dépendance à un système de fenêtrage. L'interface qui en résulte est portable, cohérente et efficace en terme de bibliothèque de tracés 2 et 3D.  Il appartient au gestionnaire de fenêtres du système d'ouvrir et de dessiner les fenêtres. La bibliothèque OpenGL communique avec le système au travers de bibliothèque aditionelles auxiliaires. Par exemple, la bibliothèque auxiliaire GLX décrit les interactions entre OpenGL et le système X windows. 

La Boite à Outils Utilitaire OpenGL (GLUT) (OpenGL Utility Toolkit) est une interface de programmation entre le C ANSI, le FORTRAN et OpenGL. Elle a été écrite par Mark J. Kilgard et comble un grand vide laissé par la spécification OpenGL. Grace au développeurs de GLUT, nous pouvons maintenant utiliser une interface commune avec les gestionnaires de fenêtres et ce indépendamment de la plateforme utilisée. Les applications OpenGL qui utilisent GLUT, sont facilement portables sans modification du code source. GLUT simplifie assurement la production de code OpenGL et elle en complémente la bibliothèque. 

Glut est relativement petite et facile à apprendre. Elle est bien conçue et son auteur lui a déjà écrit une merveilleuse documention. En conséquence,commencer une série d'article dans LinuxFocus qui lui est consacrée peut paraître redondant. Nous recommandons à tout développeur sérieux de lire la documentation de Mark. Notre objectif en écrivant ces colonnes régulières est d'indroduire pas à pas la bibliothèque GLUT et son usage avec des exemples en parallèle de la série OpenGLde ce magazine. Nous espérons que ceci contribuera utilement à motiver d'autres programmeur à rejoindre le wagon OpenGL-Linux. Dans tous les cas, procurez vous la documentation de Mark comme référence. 

L'Interface de Programmation d'Application (API) de GLUT est un automate à états tout comme OpenGL. Cela signifie que GLUT a un nombre d'états variables qui vivent pendant l'exécution d'une application. L'état initial de la machine GLUT a été choisi pour s'adapter raisonnablement à la plupart des applications. Le programme peut modifier les valeurs des variables d'état à sa guise. Chaque fois qu'une fonction de GLUT est appelée, son action est modifiée selon les valeurs des variables d'état. Les fonctions GLUT sont simples et prennent peu de paramètres. Aucun pointeur n'est jamais retourné et les seuls pointeurs passés aux fonctions GLUT sont des pointeurs vers des chaînes de caractères et des descripteurs de fontes opaques. 

Les fonctions GLUT peuvent être classées en sous-APIs selon leur fonctionnalités: 

  • Initialisation 
  • Démarrage du processeur d'évènement
  • Gestion de fenêtres
  • Gestion de recouvrement
  • Gestion de Menus
  • Enregistrement de fonctions de rappel
  • Gestion d'index de couleurs 
  • Récupération d'états 
  • Tracés de Fontes
  • Tracés de Formes Géometriques 
Dans cet article nous décrirons une partie de l'initialisation, les fonctions de gestion des évènements et des fenêtres qui sont necessaires pour démarrer un programme OpenGL simple. 

Initialisations

Tout programme OpenGL qui utilise GLUT, doit commencer par initialiser la machine d'état GLUT. Les fonctions d'initialisation GLUT sont préfixées par  glutInit-.  La routine principale d'initialisation est glutInit

Utilisation 
  glutInit(int **argcp, char **argv); 
  argcp est un pointeur vers la variable non  modifiée de main. Lors du retour, la valeur pointée par argcp est mise à jour car glutInit extrait de la ligne de commande les options adaptées à la bibliothèque GLUT, par exemple : sous l'environnement du système X Windows, toute option adaptée pour X windows et associée à la fenêtre GLUT.
  argv est la variable argv de main non modifiée.  

glutInit se charge d'initialiser les variables d'état GLUT et de négotier une session avec le gestionnaire de fenêtres. Il y a quelques routines qui peuvent apparaître avant glutInit; seulement des routines préfixées par  glutInit-. Ces routines peuvent être utilisées pour définir l'état d'initialisation de la fenêtre par defaut. Par exemple :

Utilisation 
  glutInitWindowPosition(int x, int **y); 
  glutInitWindowSize(int width, int **height); 
  x,y  = position écran en pixels dans la fenêtre window (coin supérieur gauche) 
  width,height  en pixels de la fenêtre. 

Il y a une autre routine d'initialisation omniprésente dans toute application OpenGL,  glutInitDisplayMode()

Utilisation 
  glutInitDisplayMode(unsigned int mode); 
  mode est le mode d'affichage, un OU logique bit à bit du masque des modes d'affichage GLUT. Les valeur possibles du masque sont: 
GLUT_RGBA  Sélectionne une fenêtre en mode RGBA. Ceci est le défaut si ni GLUT_RGBA ni GLUT_INDEX ne sont specifiés. 
GLUT_RGB  comme GLUT_RGBA. 
GLUT_INDEX  Sélectionne une fenêtre en mode index de couleur. Ceci est prioritaire sur GLUT_RGBA. 
GLUT_SINGLE  Sélectionne une fenêtre simple tampon. Ceci est le défaut.
GLUT_DOUBLE  Sélectionne une fenêtre double tampon . Ceci est prioritaire sur GLUT_SINGLE. 
GLUT_ACCUM  Sélectionne une fenêtre avec un tampon à accumulation. 
GLUT_ALPHA  Sélectionne une fenêtre avec  une composante alpha sur le(s) tampon(s) de couleur. 
GLUT_DEPTH  Sélectionne une fenêtre avec un tampon de profondeur. 
GLUT_STENCIL  Sélectionne une fenêtre avec un tampon pochoir (stencil). 
GLUT_MULTISAMPLE  Sélectionne une fenêtre avec possibilité d'échantillonage multiple. 
GLUT_STEREO Sélectionne une fenêtre stereo. 
GLUT_LUMINANCE Sélectionne une fenêtre stereo avec un modèle de couleur "luminance". 
Si vous ne connaissez pas certaines de ces options, ne vous inquiétez pas, nous en parlerons un jour ou l'autre. Examinons deux examples. D'abord une initialisation simple pour une application comportant un dessin à une vue:

#include <GL/glut.h> 

void main(int argcp, char **argv){ 

/* Set window size and location */ 
glutInitWindowSize(640, 480); 
glutInitWindowPosition(0, 0); 

/* Select type of Display mode:  
   Single buffer & RGBA color */ 
glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE); 

/* Initialize GLUT state */ 
glutInit(&argcp, argv);  

.....encore du code 

}; 

Ensuite un exemple pour un programme d'animation: 

#include <GL/glut.h> 

void main(int argcp, char **argv){ 

/* Set window size and location */ 
glutInitWindowSize(640, 480); 
glutInitWindowPosition(0, 0); 

/* Select type of Display mode: 
   Double buffer & RGBA color */ 
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); 

/* Initialize GLUT state */ 
glutInit(&argcp, argv);  

.....encore du code

}; 

Nous reviendrons sur ces deux exemple au fur et à mesure que nous en saurons plus sur GLUT. La principale différence réside dans le fait que dans le second cas, l'affichage est initialisé en mode double tampon, idéal pour les animations parce qu'il élimine les effets de clignotements pendant les changements d'image dans la séquence d'animation.

Traitement des  Evènements

Comme mentionné auparavant, GLUT est une machine d'état. Nous allons maintenant apprendre que c'est aussi un processeur d'évènements. Cela signifie, qu'il existe une "horloge" ou une boucle continue qui démarre après des initialisations correctes et qui traite, un par un, tous les évènement déclarés à GLUT pendant l'initialisation. Les évènements sont : un clic souris, une fenêtre fermée ou dimensionnée, un curseur déplacé, un frappe sur une touche du clavier et plus curieux, l'évènement "idle" c'est à dire que rien ne se passe ! Chacun de ces évènements doit être enregistré dans une des variables d'état de GLUT pour que la boucle du processeur de GLUT puisse les interroger périodiquement et déterminer si l'utilisateur les a activés.

Par exemple, nous pourrions enregistrer "cliquez un bouton de la souris" comme évènement à surveiller par GLUT. Les évènement sont enregistrés au moyens de routines de rappel (callback en anglais). Toutes suivent la syntaxe  glut[unEvenement]Func, dans le cas du clic de souris,se serait  glutMouseFunc. L'enregistrement d'un rappel dit au processeur GLUT quelle est la fonction définie par l'utilisateur qui doit être appellée quand l'évènement se produit. Ainsi si j'écris ma propre fonction  MyMouse  qui définit quoi faire si le bouton gauche de la souris est cliqué,(ou le droit, etc.) alors, je peux enregistrer ma fonction de rappel après glutInit() dans main() en utilisant l'instruction "glutMouseFunc(MyMouse);" . 

Terminons en décrivant quels fonctions de rappel et évènements sont permis par GLUT. La chose importante maintenant, est, après avoir enregistré tous les évènements importants de notre application, d'appeller le processeur d'évènement GLUT, soit la fonction glutMainLoop()

La fonction ne se termine jamais, autrement dit, notre programme entre dans une boucle infinie. Elle appellera tant que nécessaire, toutes les fonctions de rappel que nous avons précédement enregistrées. Chaque fonction  main() dans une application OpenGL doit donc se terminer par une instruction glutMainLoop(). Ainsi dans le cas de notre modèle d'animation : 

#include <GL/glut.h> 

void main(int argcp, char **argv){ 

/* Initialize GLUT state */ 
glutInit(&argcp, argv);  
glutInitWindowSize(640, 480); 
glutInitWindowPosition(0, 0); 

/* Open a window */ 
glutCreateWindow("My OpenGL Application"); 

/* Select type of Display mode:  
   Double buffer & RGBA color */ 
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); 
 

/* Register Callback Functions */ 
..... 

/* Start Event Processing Engine */ 
glutMainLoop(); 
}; 

Remarquez que j'ai ajouté du code supplémentaire dont nous n'avons pas parlé auparavant. C'est une des fonctions de gestion de fenêtre de GLUT, glutCreateWindow(char **name). C'est ce que j'aime tant dans OpenGL et GLUT, la philosophie de conception, il est très clair de voir ce que fait la fonction simplement en regardant son nom!  Elle s'occupe aussi de transmettre les ordres d'ouverture d'une fenêtre de notre application OpenGL au gestionnaire de fenêtres sous jascent. Le nom de la fenêtre "name" est transmit comme chaine de caracteres. Dans l'environnement X Windows, ce nom est écrit dans le coin supérieur gauche de la fenêtre. La section de gestion des fenêtres de GLUT, offre beaucoup d'autres fonctions dont nous parlerons. Pour l'instant, celle ci est suffisante. J'ai aussi réarrangé les routines d'initialisation pour montrer qu'elles peuvent être placées après glutInit(). 

Pour revenir aux évènements... Je voudrais maintenant introduire deux fonctions de rappel qui sont fondamentales pour tout programme d'animation. glutDisplayFunc qui défini la fonction d'affichage pour la fenêtre courante et glutIdleFunc qui définit le rappel d'attente (idle). Ces deux routines d'enregistrement attendent une fonction de type
void *(void).  Disons que nous écrivons deux autres fonctions de rappel pour notre modèle d'animation, void MyDisplay(void)qui s'occupe d'appeller les instructions OpenGL qui tracent réellement notre scène dans la fenêtre, et  void MyIdle(void)qui est une fonction qui est appelée chaque fois qu'il n'y a pas d'autre entrée de la part de l'utilisateur. Autrement dit, chaque fois que le processeur d'évènement GLUT fait un tour dans la boucle infinie  (glutMainLoop())  et ne trouve aucun nouvel évènement, il execute  MyIdle.  Pourquoi enregistrer une fonction de rappel d'attente dans un programme d'animation ? Parce que si nous voulons modifier chacune des images qui apparait au cours de l'animation, indépendemment de toute entrée de l'utilisateur, il doit y avoir une fonction (la fonction de rappel d'attente) qui est appelée suffisamment souvent dans la vie du programme OpenGL pour changer les images avnt qu'elles ne soient dessinées par  Mydisplay()

Exemple d'Animation

Pour terminer, voici un exemple simple de modèle pour un programme d'animation: 
#include <GL/glut.h> 

void MyIdle(void){ 
/* Some code to modify the variables defining next frame */ 
.... 
}; 

void MyDisplay(void){ 
/* Some OpenGL code that draws a frame */ 
.... 
/* After drawing the frame we swap the buffers */ 
glutSwapBuffers(); 
}; 

void main(int argcp, char **argv){ 

/* Initialize GLUT state */ 
glutInit(&argcp, argv);  
glutInitWindowSize(640, 480); 
glutInitWindowPosition(0, 0); 

/* Open a window */ 
glutCreateWindow("My OpenGL Application"); 

/* Select type of Display mode:  
   Double buffer & RGBA color */ 
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); 

/* Register Callback Functions */ 
glutDisplayFunc(MyDisplay) 
glutIdleFunc(MyIdle) 

/* Start Event Processing Engine */ 
glutMainLoop(); 
}; 

Remarquez la nouvelle fonction GLUT à la fin de  MyDisplay, glutSwapBuffers().  Elle est très utile pour les animations. Nous utilisons une fenêtre en mode DOUBLE tampon, un affiché et l'autre caché. Les instructions de tracé OpenGL dessinent toujours dans ce cas dans le tampon caché. L'appel à  glutSwapBuffers, échange les tampons, affichant d'un coup dans la fenêtre ce qui a été dessiné. Cette technique est habituelle pour les animations sur ordinateur parce qu'elle empêche l'oeil humain de voir l'image se construire ligne par ligne.

Il y a déjà suffisamment de matière pour démarrer des applications OpenGL. Il manque tout de même les instructions OpenGL dans  MyDisplay qui tracent réellement le dessin...mais ceci est une autre histoire ;-). 

Dans le prochain article sur la programmation GLUT nous explorerons plus en détail les fonctionnalités disponibles dans la section de gestion des fenêtres de GLUT, et nous verrons comment ouvrir des scènes multiples dans la même fenêtre. Nous parlerons aussi des menus et du pour et du contre pour leur portabilité.


Traduit par John Perr

Pour en savoir plus:
© 1998 Miguel Angel Sepúlveda
Ce site web est maintenu par Miguel A Sepulveda.