(* ============================================================================== *) (* === Projet ocamlpilot - CG version du 15/05/2005 === *) (* === === *) (* === Ce programme montre comment plaquer une texture sur un objet === *) (* ============================================================================== *) open Pixmap;; (* fichier qui permet de lire une image au format X11 pixmap *) (* ============================================================================== *) (* === 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 texture === *) (* ============================================================================== *) (* [make_texture ficname id_texture] lit le fichier [ficname] contenant un xpm et associe ce pixmap à une texture. La fonction retourne l'identifiant de la texture generee. *) let make_texture ficname mag_filter min_filter = (* lecture du pixmap, cf le fichier pixmap.ml. Vous pouvez generer le pixmap en partant d'un gif ou d'un jpeg et en le sauvagardant sous gimp au format X11 pixmap. *) let (nCols,nRows,pixels) = read_pixmap ficname in let raw_t = Raw.of_array pixels ~kind:`ubyte in let pixmap = GlPix.of_raw raw_t ~format:`rgb ~width:nRows ~height:nCols in (* on demande a openGL de nous donner un identifiant de texture non encore utilise *) let id_texture = GlTex.gen_texture () in (* sauvegarde du pixmap sous openGL *) GlPix.store (`unpack_alignment 1); GlTex.bind_texture ~target:`texture_2d id_texture; (* ici on indique comment cacluler les pixels a afficher en fonction des pixels de la texture. Grossierement, il faut utiliser des `nearest sur de mauvaises cartes graphiques et `linear sur de bonnes cartes *) GlTex.parameter ~target:`texture_2d (`mag_filter mag_filter); GlTex.parameter ~target:`texture_2d (`min_filter min_filter); GlTex.parameter ~target:`texture_2d (`wrap_s `repeat); GlTex.parameter ~target:`texture_2d (`wrap_t `repeat); (* creation de la texture proprement dite : on utilise des mipmaps (bitmaps de differentes tailles) pour ameliorer l'affichage *) GluMisc.build_2d_mipmaps pixmap; (* on retourne l'identifiant de la texture, identifiant qui sera utilise par la fonction d'affichage pour plaquer la texture sur les objets *) id_texture;; (* ============================================================================== *) (* === affichage d'un cube texture === *) (* ============================================================================== *) let affiche_cube id_texture = (* on indique que l'on veut utiliser le Z-buffer pour supprimer les parties cachees. De plus, cull_face va accelerer ces suppressions en ne tenant pas compte des faces du cube dont seule la partie interne est orientee vers la camera. *) Gl.enable `depth_test; Gl.enable `cull_face; (* on indique que l'on va plaquer une texture *) Gl.enable `texture_2d; GlTex.env (`mode `decal); (* on recupere la texture des cubes *) GlTex.bind_texture ~target:`texture_2d id_texture; (* affichage du cube. Les GlTex.coord2 plaquent la texture sur chaque face *) GlDraw.begins `quads; GlDraw.polygon_mode `front `fill; (* le dessous *) GlTex.coord2(0.,0.); GlDraw.vertex3(-0.5, -0.5, -0.5); GlTex.coord2(0.,1.); GlDraw.vertex3( 0.5, -0.5, -0.5); GlTex.coord2(1.,1.); GlDraw.vertex3( 0.5, -0.5, 0.5); GlTex.coord2(1.,0.); GlDraw.vertex3(-0.5, -0.5, 0.5); (* le dessus *) GlTex.coord2(0.,0.); GlDraw.vertex3(-0.5, 0.5, -0.5); GlTex.coord2(0.,1.); GlDraw.vertex3(-0.5, 0.5, 0.5); GlTex.coord2(1.,1.); GlDraw.vertex3( 0.5, 0.5, 0.5); GlTex.coord2(1.,0.); GlDraw.vertex3( 0.5, 0.5, -0.5); (* le devant *) (* notez que les parametres de coord2 sont entre 0 et 2 alors qu'avant ils etaient entre 0 et 1. Consequence : les briques sont 2 fois plus petites. C'est top moumoute non ? *) GlTex.coord2(0.,0.); GlDraw.vertex3(-0.5, -0.5, -0.5); GlTex.coord2(0.,2.); GlDraw.vertex3(-0.5, 0.5, -0.5); GlTex.coord2(2.,2.); GlDraw.vertex3( 0.5, 0.5, -0.5); GlTex.coord2(2.,0.); GlDraw.vertex3( 0.5, -0.5, -0.5); (* l'arriere *) GlTex.coord2(0.,0.); GlDraw.vertex3(-0.5, -0.5, 0.5); GlTex.coord2(0.,2.); GlDraw.vertex3( 0.5, -0.5, 0.5); GlTex.coord2(2.,2.); GlDraw.vertex3( 0.5, 0.5, 0.5); GlTex.coord2(2.,0.); GlDraw.vertex3(-0.5, 0.5, 0.5); (* le cote droit *) GlTex.coord2(0.,0.); GlDraw.vertex3( 0.5, -0.5, -0.5); GlTex.coord2(0.,2.); GlDraw.vertex3( 0.5, 0.5, -0.5); GlTex.coord2(2.,2.); GlDraw.vertex3( 0.5, 0.5, 0.5); GlTex.coord2(2.,0.); GlDraw.vertex3( 0.5, -0.5, 0.5); (* le cote gauche *) GlTex.coord2(0.,0.); GlDraw.vertex3(-0.5, -0.5, -0.5); GlTex.coord2(0.,2.); GlDraw.vertex3(-0.5, -0.5, 0.5); GlTex.coord2(2.,2.); GlDraw.vertex3(-0.5, 0.5, 0.5); GlTex.coord2(2.,0.); GlDraw.vertex3(-0.5, 0.5, -0.5); GlDraw.ends ();; (* ============================================================================== *) (* === affichage === *) (* ============================================================================== *) let angle = ref 0.;; let dessine_cube id_texture = (* 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:(0.,0.,4.) ~center:(0.,0.,0.) ~up:(0.,1.,0.); (* affichage du cube *) GlMat.rotate ~angle:(!angle) ~x:(1.0) ~y:(1.0) ~z:(1.0) (); affiche_cube id_texture; (* 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 id_texture = make_texture "brick.xpm" `nearest `nearest in fun ~value -> dessine_cube id_texture; 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:(3.,50.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 ();;