Due credit
[dormin.git] / rend.ml
blobb4fd3b2a8d38b4040c8332967473db757e126f15
1 type cmd = | Char of char | Draw;;
2 type func = cmd -> func_ret
3 and helpfunc = (unit -> (string * string * string) list)
4 and func_ret = Func of (func * helpfunc)
6 let nmo_name = ref None
7 let anb_name = ref None
8 let skb_name = ref None
9 let mipmaps = ref false
10 let slerp_step = ref 1.0
12 type view =
13 { mutable w : int
14 ; mutable h : int
15 ; mutable rotx : float
16 ; mutable roty : float
17 ; mutable rotz : float
18 ; mutable zoom : float
19 ; mutable center : (float * float * float)
20 ; mutable radial_scale : float
21 ; mutable func : func list
22 ; mutable helpfunc : helpfunc list
23 ; mutable persp : bool
24 ; mutable last_time : float
25 ; mutable animated : bool
26 ; mutable dumpchan : out_channel Lazy.t
27 ; mutable dodump : bool
28 ; mutable aincr : float
29 ; mutable roteye: bool
30 ; mutable sphere : bool
31 ; mutable help : bool
32 ; mutable x : int
33 ; mutable y : int
34 ; mutable mtype : [`none|`zoom|`rotate|`move]
35 ; mutable transl : (float * float * float)
36 ; mutable alpha : float
39 let view =
40 { w = 0; h = 0
41 ; rotx = 0.0; roty = 0.0; rotz = 0.0
42 ; center = (0.0, 0.0, 0.0)
43 ; radial_scale = 0.0
44 ; zoom = 1.0
45 ; func = []
46 ; helpfunc = []
47 ; persp = true
48 ; last_time = 0.0
49 ; animated = false
50 ; dumpchan = lazy (open_out_bin "dump.rgb")
51 ; dodump = false
52 ; aincr = 3.0
53 ; roteye = false
54 ; sphere = false
55 ; help = false
56 ; x = 0
57 ; y = 0
58 ; mtype = `none
59 ; transl = (0.0, 0.0, 0.0)
60 ; alpha = 0.0
64 let deg2rad deg = deg /. 180.0 *. acos ~-.1.;;
66 let center_and_radial_scale (minx, maxx, miny, maxy, minz, maxz) =
67 let xc = (maxx +. minx) /. 2.0 in
68 let yc = (maxy +. miny) /. 2.0 in
69 let zc = (maxz +. minz) /. 2.0 in
70 let rs =
71 let rs = maxx -. minx in
72 let rs = max rs (maxy -. miny) in
73 let rs = max rs (maxz -. minz) in
76 if false
77 then (
78 Format.eprintf "x (% f, % f)@." minx maxx;
79 Format.eprintf "y (% f, % f)@." miny maxy;
80 Format.eprintf "z (% f, % f)@." minz maxz;
81 Format.eprintf "c (% f, % f, % f)@." xc yc zc;
82 Format.eprintf "rs %f@." rs;
84 ((xc, yc, zc), rs)
87 let help () =
88 let font = Glut.BITMAP_HELVETICA_18 in
89 let draw_string x y s =
90 GlPix.raster_pos ~x ~y ();
91 String.iter (fun c -> Glut.bitmapCharacter ~font ~c:(Char.code c)) s
93 GlMat.mode `projection;
94 GlMat.push ();
95 GlMat.load_identity ();
96 GlMat.mode `modelview;
97 GlMat.push ();
98 GlMat.load_identity ();
99 GlMat.ortho
100 ~x:(0.0, float view.w)
101 ~y:(0.0, float view.h)
102 ~z:(-1., 1.)
105 Gl.disable `depth_test;
106 Gl.disable `alpha_test;
108 GlDraw.polygon_mode `both `fill;
109 Gl.enable `blend;
110 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
111 GlDraw.color (0., 0., 0.) ~alpha:0.3;
112 GlDraw.rect (0., 0.) (float view.w, float view.h);
113 Gl.disable `blend;
115 GlDraw.color (1., 1., 1.);
116 let rec loop row = function
117 | [] -> ()
118 | (s, s2, s3) :: rest ->
119 let y = view.h - row * 18 - 2 in
120 let x = if row = 1 then 0.0 else 5.0 in
121 draw_string (x+.5.0) (float y) s;
122 draw_string (x+.105.0) (float y) s2;
123 draw_string (x+.345.0) (float y) s3;
124 loop (row+1) rest
126 let help =
127 let onoff b = if b then "on" else "off" in
128 let angles =
129 Printf.sprintf "% f, % f, % f" view.rotx view.roty view.rotz
131 [("Keys:", "", "")
132 ;"h", "toggle help", ""
133 ;"e", "toggle eye/model rotation", if view.roteye then "eye" else "model"
134 ;"a", "toggle animation", onoff view.animated
135 ;"o", "toggle bounding sphere", onoff view.sphere
136 ;"d", "dump images to dump.rgb", onoff view.dodump
137 ;"q, ESC", "quit", ""
138 ;"z,x,arrows", "rotate", angles
139 ;"0,9", "zoom", Printf.sprintf "%f" view.zoom
140 ;"1,2", "go to first/last pose", ""
141 ;"< , >", "decrease/increase alpha", Printf.sprintf "%1.2f" view.alpha
142 ;"[ , ]", "decrease/increase slerp step", Printf.sprintf "%2.1f" !slerp_step
143 ;"","",""
146 let help =
147 List.fold_left (fun accu hf -> accu @ hf ()) help view.helpfunc
149 loop 1
150 (help @
151 ["", "", ""
152 ;"Move mouse while holding left button pressed to rotate model", "", ""
153 ;"Move mouse while holding right button pressed to zoom", "", ""
154 ;"Move mouse while holding left button and shift pressed to move model", "", ""
155 ;"",
156 (let tx, ty, tz = view.transl in
157 Printf.sprintf "translation % f, % f, % f" tx ty tz),
162 Gl.enable `depth_test;
163 Gl.enable `alpha_test;
164 GlMat.pop ();
165 GlMat.mode `projection;
166 GlMat.pop ();
169 let display () =
170 GlClear.color (0.5, 0.5, 0.5) ~alpha:1.0;
171 GlClear.clear [`color; `depth];
172 GlDraw.color (0.0, 0.0, 0.0);
173 GlFunc.alpha_func `greater view.alpha;
175 if view.sphere then (
176 let cx, cy, cz = view.center in
177 let cx = -.cx and cy = -.cy and cz = -.cz in
178 GlDraw.line_width 1.0;
179 GlMat.mode `modelview;
180 GlMat.push ();
181 GlMat.translate3 (cx, cy, cz);
182 GlDraw.polygon_mode `back `line;
183 GlDraw.polygon_mode `front `line;
184 Gl.disable `texture_2d;
185 GluQuadric.sphere ~radius:(0.7*.view.radial_scale) ~stacks:25 ~slices:25 ();
186 GlMat.pop ();
189 List.iter (fun f -> ignore (f Draw)) view.func;
190 if view.help then help ();
191 Glut.swapBuffers ();
193 if view.dodump then (
194 let pix = GlPix.read 0 0 view.w view.h `rgb `ubyte in
195 let raw = GlPix.to_raw pix in
196 let pitch = view.w * 3 in
197 let size = view.h * pitch in
198 let s = Raw.gets_string raw 0 size in
199 let dc = Lazy.force view.dumpchan in
200 let rec loop pos =
201 let pos = pos - pitch in
202 if pos < 0 then ()
203 else (
204 output dc s pos pitch;
205 loop pos
208 loop size;
212 let get_eye_and_up () =
213 if not view.roteye
214 then
215 (0.0, 0.0, 2.0), (0.0, 1.0, 0.0)
216 else
217 let q =
218 let rx = deg2rad view.rotx
219 and ry = deg2rad view.roty
220 and rz = deg2rad view.rotz in
221 Qtr.from_euler rz ~-.ry rx
223 let v = Qtr.apply q (Vec.make 0.0 0.0 2.0) in
224 let u = Qtr.apply q (Vec.make 0.0 1.0 0.0) in
225 Vec.elts v, Vec.elts u
228 let setup w h =
229 view.w <- w;
230 view.h <- h;
231 GlDraw.viewport 0 0 w h;
233 let rs = view.zoom /. view.radial_scale in
235 GlMat.mode `projection;
236 GlMat.load_identity ();
237 GlMat.translate3 view.transl;
238 GluMat.perspective
239 ~fovy:45.0
240 ~aspect:(float w /. float h)
241 ~z:(0.1, 10.)
244 GlMat.mode `modelview;
245 GlMat.load_identity ();
247 let eye, up = get_eye_and_up () in
248 GluMat.look_at
249 ~eye
250 ~center:(0.0, 0.0, 0.0)
254 if not view.roteye then (
255 GlMat.rotate ~angle:view.rotx ~x:1.0 ();
256 GlMat.rotate ~angle:view.roty ~y:~-.1.0 ();
257 GlMat.rotate ~angle:view.rotz ~z:1.0 ();
260 GlMat.scale3 (-.rs, rs, rs);
261 GlMat.translate3 view.center;
264 let reshape ~w ~h =
265 setup w h;
268 let allfunc cmd =
269 let f, h =
270 List.split (
271 List.map (fun f -> let Func (fr, hf) = f cmd in fr, hf) view.func
274 view.func <- f;
275 view.helpfunc <-h;
278 let idle () =
279 let deadline = view.last_time +. 0.04 in
280 let currtime = Unix.gettimeofday () in
281 if deadline > currtime
282 then
283 let _ = Unix.select [] [] [] (deadline -. currtime) in
284 view.last_time <- Unix.gettimeofday ()
285 else
286 view.last_time <- view.last_time +. 0.04
288 allfunc (Char 'n');
289 Glut.postRedisplay ();
292 let keyboard ~key ~x ~y =
293 begin match Char.chr key with
294 | '\027' | 'q' -> exit 0
295 | '9' -> view.zoom <- view.zoom +. 0.05
296 | '0' -> view.zoom <- view.zoom -. 0.05
297 | 'z' -> view.roty <- view.roty +. view.aincr
298 | 'x' -> view.roty <- view.roty -. view.aincr
299 | 'd' -> view.dodump <- not view.dodump
300 | 'e' -> view.roteye <- not view.roteye
301 | 'o' -> view.sphere <- not view.sphere;
302 | 'h' -> view.help <- not view.help
303 | 'a' ->
304 if view.animated
305 then (
306 view.animated <- false;
307 Glut.idleFunc None
309 else (
310 view.animated <- true; view.
311 last_time <- Unix.gettimeofday ();
312 Glut.idleFunc (Some idle)
314 | 'f' | 'b' when not view.animated -> allfunc (Char (Char.chr key))
315 | '<' -> view.alpha <- max (view.alpha -. 0.01) 0.0;
316 | '>' -> view.alpha <- min (view.alpha +. 0.01) 1.0;
317 | '[' -> slerp_step := max (!slerp_step -. 0.1) 0.0;
318 | ']' -> slerp_step := min (!slerp_step +. 0.1) 1.0;
319 | c -> allfunc (Char c)
320 end;
321 setup view.w view.h;
322 Glut.postRedisplay ();
325 let special ~key ~x ~y =
326 begin match key with
327 | Glut.KEY_LEFT -> view.rotz <- view.rotz +. view.aincr
328 | Glut.KEY_RIGHT -> view.rotz <- view.rotz -. view.aincr
329 | Glut.KEY_UP -> view.rotx <- view.rotx -. view.aincr
330 | Glut.KEY_DOWN -> view.rotx <- view.rotx +. view.aincr
331 | _ -> ()
332 end;
333 setup view.w view.h;
334 Glut.postRedisplay ();
337 let motion ~x ~y =
338 let dx = (x - view.x) in
339 let dy = (y - view.y) in
340 view.x <- x;
341 view.y <- y;
342 match view.mtype with
343 | `move ->
344 let x, y, z = view.transl in
345 let dx = float dx /. 100.0
346 and dy = float dy /. 100.0 in
347 view.transl <- (x +. dx, y -. dy, z);
348 setup view.w view.h;
349 Glut.postRedisplay ();
350 | `rotate ->
351 view.rotx <- view.rotx +. float dy;
352 view.roty <- view.roty -. float dx;
353 setup view.w view.h;
354 Glut.postRedisplay ();
355 | `zoom ->
356 view.zoom <- view.zoom +. (float dy /. 50.);
357 setup view.w view.h;
358 Glut.postRedisplay ();
359 | `none ->
363 let mouse ~button ~state ~x ~y =
364 if button = Glut.LEFT_BUTTON
365 then (
366 if state = Glut.DOWN
367 then (
368 view.x <- x;
369 view.y <- y;
370 view.mtype <-
371 if Glut.getModifiers () = Glut.active_shift
372 then `move else `rotate;
374 else view.mtype <- `none;
376 else if button = Glut.RIGHT_BUTTON
377 then (
378 if state = Glut.DOWN
379 then (
380 view.x <- x;
381 view.y <- y;
382 view.mtype <- `zoom;
384 else view.mtype <- `none;
388 let main () =
389 let w = 704
390 and h = 576 in
391 let _ = Glut.init Sys.argv in
392 let () = Glut.initDisplayMode ~depth:true ~double_buffer:true () in
393 let () = Glut.initWindowSize w h in
394 let _ = Glut.createWindow "rend (press 'h' to get help)" in
395 Gl.enable `depth_test;
396 Gl.enable `alpha_test;
397 let () = Glut.displayFunc display in
398 let () = Glut.reshapeFunc reshape in
399 let () = Glut.keyboardFunc keyboard in
400 let () = Glut.specialFunc special in
401 let () = Glut.mouseFunc mouse in
402 let () = Glut.motionFunc motion in
403 allfunc (Char '\000');
404 let () = Glut.mainLoop () in
408 let add_func func =
409 view.func <- func :: view.func;
412 let init minmax =
413 let (cx, cy, cz), rs = center_and_radial_scale minmax in
414 view.center <- (-.cx, -.cy, -.cz);
415 view.radial_scale <- rs;
418 let _ =
419 let setsome r s = r := Some s in
420 let spec =
421 ["-slice", Arg.String Slice.openslice, "<path> of file to slice data to"
422 ;"-index", Arg.Set_string Xff.index_path, "<path> of index"
423 ;"-base", Arg.String (setsome Xff.base_path), "<directory> base"
424 ;"-sstep", Arg.Set_float slerp_step, "<float> slerp step"
425 ;("-skb", Arg.String (setsome skb_name),
426 "<name> use specified skb instead of guessing")
427 ;"-mipmaps", Arg.Set mipmaps, " use mipmaps"
430 Arg.parse (Arg.align spec)
431 (fun s ->
432 if !nmo_name != None then (
433 if !anb_name != None
434 then raise (Arg.Bad "invalid usage")
435 else anb_name := Some s;
436 ) else nmo_name := Some s;
438 "Usage: dormin [options] model.nmo [animation.anb]"