(* ============================================================================== *) (* === Projet ocamlpilot - CG version du 13/05/2005 === *) (* === === *) (* === Ce programme montre comment on peut a la fois deplacer la camera et === *) (* === les objets qui s'affichent. Afin que le programme corresponde dans son === *) (* === principe au client OcamlPilot, chaque fois que l'on veut afficher les === *) (* === objets 3D, on commence par appeler une fonction qui renvoie la liste === *) (* === des objets a afficher avec leur position (comme si le serveur === *) (* === ocamlpilotd nous envoyait cette liste), puis on effectue l'affichage. === *) (* === Les deplacements des objets (des theieres et des beignets) sont de === *) (* === petits sauts, qui montrent que les objets sont satisfaits de la === *) (* === maniere dont ocaml/lablGL/openGL les affiche. Quant a la camera, elle === *) (* === tourne lentement autour des objets. Enfin, si l'utilisateur appuie === *) (* === sur la fleche droite, la rotation de la camera s'accelere et s'il === *) (* === appuie sur la fleche gauche, elle diminue. Les touches sont prises en === *) (* === compte comme dans clavier.ml et une fonction lancee avec un timer, et === *) (* === reveillee regulierement, se charge de mettre a jour la position === *) (* === effective de la camera (comme si elle envoyait les directives de === *) (* === l'utilisateur au serveur OcamlPilotd). Le programme se termine quand === *) (* === on appuie sur la touche ESC. === *) (* ============================================================================== *) (* ============================================================================== *) (* === 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");; (* ============================================================================== *) (* === var globales tres moches pour simuler l'interaction avec le serveur === *) (* ============================================================================== *) type couleur = RED | BLUE | YELLOW;; type objets = { x : float; y : float; z : float; angle : float; couleur : couleur; temps : float; };; (* liste des objets a afficher *) let liste_objets = ref [ {x = 0.5; y = 0.0; z = 0.0; angle = 45.0; temps = 0.; couleur = RED}; {x = 0.0; y = 2.0; z = 3.0; angle = 0.0; temps = 3.; couleur = BLUE}; {x = 2.0; y = 1.0; z = 1.0; angle = 70.0; temps = 1.; couleur = YELLOW}; ];; (* angle/position de la camera sur le cercle qui lui sert de trajectoire *) let angle_camera = ref 0.;; (* le pas dont on increment l'angle de la camera 10 fois par seconde *) let angle_camera_step = ref 0.05;; (* de quel angle on incremente/decremente le pas lorsque l'utilisateur appuie sur une fleche *) let angle_camera_step_incr = 0.01;; (* tableau de booleens qui indique les actions a mener : touche.(0) = touche escape touche.(1) = fleche gauche touche.(2) = fleche droite *) let touche = Array.make 3 false;; (* ============================================================================== *) (* === gestion du clavier : ESC = sortie du programme === *) (* === fleche gauche = ralentit la rotation === *) (* === fleche droite = accelere la rotation === *) (* ============================================================================== *) (* on recupere les touches sur lesquelles l'utilisateur a appuye *) let bind_keyboard () = (* si on appuie sur ESC *) Glut.keyboardFunc ~cb:(fun ~key ~x ~y -> if key = 27 then touche.(0) <- true); (* gestion des fleches gauche et droite *) Glut.specialFunc ~cb:(fun ~key ~x ~y -> match key with | Glut.KEY_LEFT -> touche.(1) <- true | Glut.KEY_RIGHT -> touche.(2) <- true | _ -> ()); Glut.specialUpFunc ~cb:(fun ~key ~x ~y -> match key with | Glut.KEY_LEFT -> touche.(1) <- false | Glut.KEY_RIGHT -> touche.(2) <- false | _ -> ());; (* ============================================================================== *) (* === recuperation des objets a afficher et affichage === *) (* ============================================================================== *) (* function qui simule le fait que l'on recupere de la part du serveur les listes des objets a afficher *) let get_theieres () = let rec get_update = function | {x=x; y=y; z=z; angle=angle; couleur=couleur; temps=t} :: reste -> let new_t = if t +. 1. > 4. then t -. 7. else t +. 1. in let new_y = (16. -. new_t *. new_t) /. 6. -. 2. in {x=x; y=new_y; z=z; angle=angle; couleur=couleur; temps=new_t} :: get_update reste | [] -> [] in liste_objets := get_update !liste_objets; !liste_objets;; (* la fonction qui, etant donnee une liste d'objets, realise les affichages *) let dessine_theieres liste = (* fonction pour afficher chaque objet *) let rec dessine = function | {x = x; y = y; z = z; angle = angle; couleur = couleur} :: reste -> (* on sauvegarde la matrice courante dans la pile de maniere a pouvoir la reutiliser pour la deuxieme theiere. *) GlMat.push (); (* on selectionne la couleur de la theiere *) (match couleur with | RED -> GlLight.material ~face:`front (`ambient_and_diffuse (0.49,0.,0.16,1.0)) | BLUE -> GlLight.material ~face:`front (`ambient_and_diffuse (0.10,0.10,0.50,1.0)) | YELLOW -> GlLight.material ~face:`front (`ambient_and_diffuse (0.61,0.35,0.10,1.0))); GlLight.material ~face:`front (`shininess 100.0); GlLight.material ~face:`front (`specular (1.,1.,0.,10.)); (* on place la theiere sur l'ecran *) GlMat.translate ~x ~y ~z (); GlMat.rotate ~angle ~x:(1.0) (); (* theiere de taille 0.5 *) Glut.wireTeapot 0.5; (* on peut maintenant faire un pop et se retrouver avec la matrice qui indique ou se trouve la camera. *) GlMat.pop (); (* affichage des autres theieres *) dessine reste | [] -> () in (* 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, l'oeil se trouve sur un cercle de centre (0,0,0) et de rayon 10. On regarde toujours dans la direction du point (0,0,0) *) GlMat.mode `modelview; GlMat.load_identity(); GluMat.look_at ~eye:(10. *. cos !angle_camera,0.,10. *. sin !angle_camera) ~center:(0.,0.,0.) ~up:(0.,1.,0.); (* affichage des theieres *) dessine liste; (* on effectue la synchronisation du double buffering *) Gl.flush (); Glut.swapBuffers ();; (* ============================================================================== *) (* === realisation des actions du joueur + affichage === *) (* ============================================================================== *) (* on effectue la mise a jour demandee par l'utilisateur (30 fois par seconde ici, tout se passe comme si l'utilisateur envoyait via la socket les actions qu'il veut effectuer au serveur. puis on affiche l'espace *) let rec update_jeu ~value = (* traitement des touches *) if touche.(0) then exit 0; if touche.(1) then angle_camera_step := !angle_camera_step -. angle_camera_step_incr; if touche.(2) then angle_camera_step := !angle_camera_step +. angle_camera_step_incr; (* mise a jour de la position de la camera *) angle_camera := !angle_camera +. !angle_camera_step; (* affichage *) (let liste_objets = get_theieres () in dessine_theieres liste_objets); (* temporisation d'un 30eme de seconde *) Glut.timerFunc ~ms:33 ~cb:update_jeu ~value:0;; (* ============================================================================== *) (* === creation de la lumiere === *) (* ============================================================================== *) 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 *) bind_keyboard (); (* initialisation de la lumiere *) manage_light (); (* creation du timer pour effectuer les affichages regulierement *) Glut.timerFunc ~ms:30 ~cb:update_jeu ~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 ();;