(* ============================================================================== *) (* === Projet ocamlpilot - CG version du 19/04/2005 === *) (* === === *) (* === Ce programme montre comment deplacer la camera. Celle-ci se deplace === *) (* === sur un cercle en regardant vers le milieu entre un triangle et un cube === *) (* ============================================================================== *) (* ============================================================================== *) (* === 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);; (* ============================================================================== *) (* === affichage / deplacement === *) (* ============================================================================== *) let angle = ref 0.;; let dessine_fenetre () = (* 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 se trouve sur un cercle de centre (0,0,10) et de rayon 10. On regarde toujours dans la direction du point (0,0,10) *) GlMat.mode `modelview; GlMat.load_identity(); GluMat.look_at ~eye:(10. *. cos !angle,0.,10. *. (1. +. sin !angle)) ~center:(0.,0.,10.) ~up:(0.,1.,0.); (* on effectue des tests avec le Z-buffer pour eliminer les parties cachees *) Gl.enable `depth_test; (* on supprime la lumiere => les objets sont dessines avec la couleur specifiee par un GlDraw.color. Ici on affiche un triangle plein rouge *) Gl.disable `lighting; GlDraw.color (1.0, 0.0, 0.0); (* <- rouge *) GlDraw.polygon_mode `both `fill; GlDraw.begins `triangles; GlDraw.vertex ~x:(-3.) ~y:0. ~z:10. (); GlDraw.vertex ~x:(-1.) ~y:0. ~z:10. (); GlDraw.vertex ~x:(-2.) ~y:1. ~z:10. (); GlDraw.ends (); (* on affiche un cube bleu : *) (* lumiere *) Gl.enable `lighting; GlLight.material ~face:`front (`ambient_and_diffuse (0.10,0.10,0.50,1.0)); (* bleu *) GlLight.material ~face:`front (`shininess 100.0); GlLight.material ~face:`front (`specular (1.,1.,0.,10.)); GlDraw.shade_model `smooth; (* le cube est d'abord centre sur l'origine des axes, puis on lui fait operer une rotation et, enfin, on le translate pour que son centre soit en (1.5,0.5,10). Rappelez-vous qu'en OpenGL si vous voulez operer une translation ou une rotation, cela revient a effectuer une multiplication de matrices et que la matrice de translation ou de rotation se trouve a droite. En consequence, si l'on veut effectuer une rotation puis une translation, il faut d'abord effectuer la multiplication avec la matrice de translation puis celle avec la rotation. *) (* translation pour centrer le cube en (1.5,0.5,10) *) GlMat.translate ~x:1.5 ~y:0.5 ~z:10. (); (* rotation de 45° par rapport a l'axe des x puis rotation de 50° par rapport a l'axe des y et enfin rotation de 20° par rapport a l'axe des z *) GlMat.rotate ~angle:(-20.0) ~z:(1.0) (); GlMat.rotate ~angle:(-50.0) ~y:(1.0) (); GlMat.rotate ~angle:(-45.0) ~x:(1.0) (); (* rectangles definissant le cube *) GlDraw.begins `quads; GlDraw.vertex ~x:(-0.5) ~y:(-0.5) ~z:(-0.5) (); GlDraw.vertex ~x:(0.5) ~y:(-0.5) ~z:(-0.5) (); GlDraw.vertex ~x:(0.5) ~y:(0.5) ~z:(-0.5) (); GlDraw.vertex ~x:(-0.5) ~y:(0.5) ~z:(-0.5) (); GlDraw.vertex ~x:(0.5) ~y:(-0.5) ~z:(-0.5) (); GlDraw.vertex ~x:(0.5) ~y:(-0.5) ~z:(0.5) (); GlDraw.vertex ~x:(0.5) ~y:(0.5) ~z:(0.5) (); GlDraw.vertex ~x:(0.5) ~y:(0.5) ~z:(-0.5) (); GlDraw.vertex ~x:(0.5) ~y:(-0.5) ~z:(0.5) (); GlDraw.vertex ~x:(0.5) ~y:(0.5) ~z:(0.5) (); GlDraw.vertex ~x:(-0.5) ~y:(0.5) ~z:(0.5) (); GlDraw.vertex ~x:(-0.5) ~y:(-0.5) ~z:(0.5) (); GlDraw.vertex ~x:(-0.5) ~y:(-0.5) ~z:(0.5) (); GlDraw.vertex ~x:(-0.5) ~y:(-0.5) ~z:(-0.5) (); GlDraw.vertex ~x:(-0.5) ~y:(0.5) ~z:(-0.5) (); GlDraw.vertex ~x:(-0.5) ~y:(0.5) ~z:(0.5) (); GlDraw.vertex ~x:(-0.5) ~y:(0.5) ~z:(-0.5) (); GlDraw.vertex ~x:(0.5) ~y:(0.5) ~z:(-0.5) (); GlDraw.vertex ~x:(0.5) ~y:(0.5) ~z:(0.5) (); GlDraw.vertex ~x:(-0.5) ~y:(0.5) ~z:(0.5) (); GlDraw.vertex ~x:(-0.5) ~y:(0.5) ~z:(0.5) (); GlDraw.vertex ~x:(0.5) ~y:(0.5) ~z:(0.5) (); GlDraw.vertex ~x:(0.5) ~y:(0.5) ~z:(-0.5) (); GlDraw.vertex ~x:(-0.5) ~y:(0.5) ~z:(-0.5) (); GlDraw.ends (); (* on effectue la synchronisation du double buffering *) Gl.flush (); Glut.swapBuffers (); (* on rajoute un delta a l'angle *) angle := !angle +. 0.05;; (* fonction qui reaffiche tout toutes les 3 secondes *) let rec affichage ~value = dessine_fenetre (); 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), 3.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 ();;