(* ============================================================================== *) (* === Projet ocamlpilot - CG version du 15/05/2005 === *) (* === === *) (* === Ce programme montre comment utiliser les listes pour accelerer vos === *) (* === affichages : lors de sa construction, la liste est transmise a la === *) (* === carte graphique. Lors d'un appel de la liste, celle-ci residant dans === *) (* === la carte, il n'y a plus besoin de transferer quoi que ce soit sur le === *) (* === bus AGP/PCI-E. La liste utilisee dans cette exemple consiste en 1 cube === *) (* === dont les faces ont des couleurs differentes. Cette liste sera appelee === *) (* === de maniere a afficher 10 cubes en rotation. === *) (* ============================================================================== *) (* ============================================================================== *) (* === 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);; (* ============================================================================== *) (* === creation de la liste === *) (* ============================================================================== *) let cree_cube () = (* on cree une liste openGL mais on n'execute pas les instructions d'affichage tout de suite (= compile) *) let liste = GlList.create `compile in (* lumiere *) GlDraw.polygon_mode `front `fill; GlLight.material ~face:`both (`shininess 120.0); GlLight.material ~face:`both (`specular (0.7,0.7,0.7,0.1)); (* on indique que l'on veut utiliser le Z-buffer pour supprimer les parties cachees *) Gl.enable `depth_test; (* affichage du cube. Comme pour la theiere, le cube est oriente dans selon un axe fixe. Simeplement, par lasuite, avant d'appeler la liste pour effectuer les affichages, on modifie la matrice du modelView (cf plus loin) *) GlDraw.begins `quads; (* le dessous en rouge *) GlLight.material ~face:`front (`ambient_and_diffuse (0.49,0.,0.16,1.0)); GlDraw.vertex3(-0.5, -0.5, -0.5); GlDraw.vertex3(-0.5, -0.5, 0.5); GlDraw.vertex3( 0.5, -0.5, 0.5); GlDraw.vertex3( 0.5, -0.5, -0.5); (* le dessus en bleu *) GlLight.material ~face:`front (`ambient_and_diffuse (0.10,0.10,0.40,1.0)); GlDraw.vertex3(-0.5, 0.5, -0.5); GlDraw.vertex3( 0.5, 0.5, -0.5); GlDraw.vertex3( 0.5, 0.5, 0.5); GlDraw.vertex3(-0.5, 0.5, 0.5); (* le devant en jaune *) GlLight.material ~face:`front (`ambient_and_diffuse (0.61,0.35,0.10,1.0)); GlDraw.vertex3(-0.5, -0.5, -0.5); GlDraw.vertex3( 0.5, -0.5, -0.5); GlDraw.vertex3( 0.5, 0.5, -0.5); GlDraw.vertex3(-0.5, 0.5, -0.5); (* l'arriere en cyan *) GlLight.material ~face:`front (`ambient_and_diffuse (0.10,0.35,0.40,1.0)); GlDraw.vertex3(-0.5, -0.5, 0.5); GlDraw.vertex3(-0.5, 0.5, 0.5); GlDraw.vertex3( 0.5, 0.5, 0.5); GlDraw.vertex3( 0.5, -0.5, 0.5); (* le cote droit en vert *) GlLight.material ~face:`front (`ambient_and_diffuse (0.10,0.40,0.01,1.0)); GlDraw.vertex3( 0.5, -0.5, -0.5); GlDraw.vertex3( 0.5, -0.5, 0.5); GlDraw.vertex3( 0.5, 0.5, 0.5); GlDraw.vertex3( 0.5, 0.5, -0.5); (* le cote gauche en magenta *) GlLight.material ~face:`front (`ambient_and_diffuse (0.40,0.10,0.40,1.0)); GlDraw.vertex3(-0.5, -0.5, -0.5); GlDraw.vertex3(-0.5, 0.5, -0.5); GlDraw.vertex3(-0.5, 0.5, 0.5); GlDraw.vertex3(-0.5, -0.5, 0.5); GlDraw.ends (); (* on termine la liste et on l'envoie a la carte graphique *) GlList.ends (); liste;; (* ============================================================================== *) (* === affichage === *) (* ============================================================================== *) let angle = ref 0.;; (* on parametre la fonction d'affichage par la liste openGL creee ci-dessus *) let dessine_theieres liste_gl = (* on efface le buffer dans lequel on dessine *) GlClear.color (0.0, 0.0, 0.0); (* <- couleur de fond = noir *) GlClear.clear [`color;`depth]; (* lumiere *) Gl.enable `lighting; GlDraw.shade_model `smooth; (* on place la camera *) GlMat.mode `modelview; GlMat.load_identity(); GluMat.look_at ~eye:(10.,5.,10.) ~center:(0.,0.,0.) ~up:(0.,1.,0.); (* affichage des 10 cubes *) for i = 1 to 10 do (* on sauvegarde la matrice courante dans la pile de maniere a pouvoir la reutiliser pour le deuxieme cube. *) GlMat.push (); (* positionnement du cube *) GlMat.rotate ~angle:(float i *. 36.0) ~y:(1.0) (); GlMat.translate ~x:4. ~y:0. ~z:0. (); GlMat.rotate ~angle:(!angle) ~x:(1.0) ~y:(1.0) ~z:(1.0) (); (* on affiche d'un cube = appel a la liste *) GlList.call liste_gl; (* on recupere la matrice d'origine du modelView *) GlMat.pop () done; (* on effectue la synchronisation du double buffering *) Gl.flush (); Glut.swapBuffers (); (* on rajoute un delta a l'angle *) angle := !angle +. 2.0;; (* fonction qui reaffiche tout toutes les 3 secondes *) let rec affichage = let liste_gl = cree_cube () in fun ~value -> dessine_theieres liste_gl; Glut.timerFunc ~ms:100 ~cb:affichage ~value:0;; let manage_light () = (* caracteristiques de la lumiere numero 0 (le spot 0) *) GlLight.light ~num:0 (`ambient (1.0, 1.0, 1.0, 1.0)); GlLight.light ~num:0 (`diffuse (1.0, 1.0, 1.0, 1.0)); GlLight.light ~num:0 (`specular (1.0, 1.0, 1.0, 1.0)); GlLight.light ~num:0 (`position ((0.0), 5.0, (-10.0), 2.0)); (* on allume le spot numero 0 *) Gl.enable `light0;; (* ============================================================================== *) (* === 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 = déformation 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:45.0 ~aspect:(float w /. float h) ~z:(7.,1500.0);; (* ============================================================================== *) (* ============================================================================== *) let main () = (* on binde le clavier *) manage_keyboard (); (* initialisation de la lumiere *) manage_light (); (* 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 ();;