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é.
|