(* ============================================================================== *) (* === Projet ocamlpilot - CG version du 16/05/2005 === *) (* === === *) (* === Ce programme montre comment afficher du texte en openGL. Il existe une === *) (* === maniere assez simple en glut mais celle-ci plaque des bitmaps sur === *) (* === l'ecran. Consequence : on ne peut afficher un texte en biais ou === *) (* === incline. Or, si l'on veut afficher un compas gyroscopique, on a besoin === *) (* === de tourner le texte. On va donc utiliser ici une texture contenant === *) (* === toute la police de caractere. L'exemple ci-dessous fait defiler un === *) (* === texte a la maniere de l'intro de la guerre des etoiles. === *) (* ============================================================================== *) (* ============================================================================== *) (* === initialisation d'openGL a faire pour chaque programme === *) (* ============================================================================== *) (* on recupere les arguments specifiques a OpenGL transmis sur la ligne de commande *) ignore(Glut.init Sys.argv);; (* initialisation du mode d'affichage : alpha : permet de jouer sur la luminosite des objets double_buffer : on travaille en double buffering, comme en 2D depth : on utilise le Z-buffer pour supprimer les parties cachees *) Glut.initDisplayMode ~alpha:true ~double_buffer:true ~depth:true ();; (* taille de la fenetre a l'ouverture *) Glut.initWindowSize ~w:800 ~h:600;; (* creation de la fenetre et titre de la fenetre *) ignore(Glut.createWindow "OcamlPilot");; (* gestion du clavier : ESC = sortie du programme *) let manage_keyboard () = Glut.keyboardFunc ~cb:(fun ~key ~x ~y -> if key = 27 then exit 0);; (* ============================================================================== *) (* ============================================================================== *) (* on recupere la police de caractere permettant les affichages. notez que l'on a bsoin pour cela de recuperer le module Mltexfont, qui est simplement une interface ocaml que j'ai realisee pour pouvoir appeler les fonctions de texfont. *) (* "courier12.txf" = nom de la police de caractere sur le systeme de fichier *) let txf = Mltexfont.loadFont "courier12.txf";; (* generation de la texture sous openGL. Le 2000 est l'identifiant que l'on aimerait avoir pour cette texture: mettre n'importe quel nombre, de toutes facons, texfont en generera un compatible avec les textures deja existantes. *) let id_texture = Mltexfont.establishTexture txf 2000 true;; (* ============================================================================== *) (* === affichage du texte === *) (* ============================================================================== *) let texte = [ "Les jeux en reseau sont en"; "principe realises autour"; "d'une architecture client-"; "serveur. Dans ce type"; "d'architecture, le serveur"; "a pour mission de gerer le"; "bon deroulement du jeu, de"; "permettre aux utilisateurs"; "de se connecter, et d'ech-"; "anger avec ceux-ci toutes"; "les informations utiles"; "pour le bon fonctionnement"; "du jeu. Le client, quant a"; "lui est le programme lance"; "par l'utilisateur. Son"; "role est d'agir comme une"; "interface entre l'utili-"; "sateur et le serveur, il"; "envoie en particulier a ce"; "dernier toutes les com-"; "mandes tapees par l'utili-"; "sateur et affiche sur"; "l'ecran du joueur l'etat"; "du jeu." ];; (* fonction qui, pour un texte donne, realise l'affichage de ce dernier *) let rec affiche_texte = function | str :: reste -> (* on recupere la taille d'une ligne de maniere a pouvoir se decaler pour la ligne suivante *) let (width,haut,bas) = Mltexfont.getStringMetrics txf str in (* affichage de la ligne *) (* on fait une translation pour centrer le texte *) GlMat.translate ~x:(-. float width /. 2.) (); Mltexfont.renderString txf str; (* decalage pour la ligne suivante. 0.0.3 = l'interligne *) GlMat.translate ~y:(3. -. float (haut + bas)) ~x:(-. float width /. 2.) (); (* affochage des lignes suivantes *) affiche_texte reste | [] -> ();; (* la fonction dessine raffraichit l'ecran et s'occupe de gerer le deplacement *) let deplacement = ref (-50.);; let dessine () = (* on efface le buffer dans lequel on dessine *) GlClear.color (0.0, 0.0, 0.0); (* <- couleur de fond = noir *) GlClear.clear [`color;`depth]; (* on place la camera, l'oeil au centre des axes, on regarde selon l'axe des z, vers les z croissants, et le haut de notre tete est vers le haut. *) GlMat.mode `modelview; GlMat.load_identity(); GluMat.look_at ~eye:(0.,2.,10.) ~center:(0.,0.,0.) ~up:(0.,1.,0.); (* on affiche du texte plaque sur un rectangle horizontal *) GlDraw.color (1.0, 1.0, 0.0); (* <- jaune *) (* comme on a choisi la couleur du texte, on supprime les effets de lumiere *) Gl.disable `lighting; Gl.disable `light0; (* la police de caractere est une texture, on doit donc autoriser les textures si l'on veut qu'elle s'affiche. Le mode modulate doit imperativement etre choisi si l'on veut un affichage correct. *) Gl.enable `texture_2d; GlTex.env (`mode `modulate); Gl.disable `depth_test; (* ici, on change l'echelle en z de maniere a obtenir un effet de profondeur *) GlMat.scale ~x:1.0 ~y:1.0 ~z:(0.2) (); (* on indique que l'on va utiliser la police txf *) Mltexfont.bindFontTexture txf; (* on effectue une translation du texte de maniere a produire un effet de defilement et, comme ce dernier est par defaut vertical, on lui fait operer une rotation pour que le texte ait l'air d'tre sur un plan horizontal *) GlMat.rotate ~x:(-1.) ~angle:80. (); GlMat.translate ~y:(!deplacement) (); (* maintenant que les matrices du modelView sont au point, on peut realiser l'affichage du texte *) affiche_texte texte; (* on effectue la synchronisation du double buffering *) Gl.flush (); Glut.swapBuffers (); (* on change la position de la camera *) deplacement := !deplacement +. 0.5;; (* fonction qui reaffiche tout toutes les 3 secondes *) let rec affichage ~value = dessine (); Glut.timerFunc ~ms:100 ~cb:affichage ~value:0;; (* ============================================================================== *) (* === changement de taille de la fenetre === *) (* ============================================================================== *) (* en cas de modification de la taille de la fenetre, il ne faut pas aplatir ou alonger l'image mais plutot diminuer ou augmenter le champ de vision. Ceci est calcule en utilisant une matrice de projection/perspective *) let reshape_window ~w ~h = (* w = nouvelle largeur de la fenetre h = nouvelle hauteur de la fenetre *) (* remise a jour de la matrice de projection *) GlMat.mode `projection; GlMat.load_identity(); (* changement de la matrice de perspective : fovy = l'angle de vision aspect = deformation aplatissement/alongement z = la coordonnee en Z du plan le plus proche et du plus eloigne ces parametres definissent le cone de ce qui est visible *) GluMat.perspective ~fovy:140.0 ~aspect:(5. *. float w /. float h) ~z:(1.,500.0);; (* ============================================================================== *) (* ============================================================================== *) let main () = (* on binde le clavier *) manage_keyboard (); (* creation du timer pour effectuer les affichages regulierement *) Glut.timerFunc ~ms:100 ~cb:affichage ~value:0; (* au cas ou la taille de la fenetre aurait change *) Glut.reshapeFunc ~cb:reshape_window; (* on appelle reshape de maniere a mettre en place la matrice de perspective *) reshape_window ~w:800 ~h:600; (* lancement de la boucle principale d'openGL. Ceci doit etre effectue en dernier car mainLoop ne rendra la main qu'a la fin de l'execution du programme. *) Glut.mainLoop ();; main ();;