Misc refactoring
[dormin.git] / nmo.ml
bloba0fc197d8180b069746fdf0e699e44cb922af327
1 open Format;;
3 type subhdr =
4 { off : int
5 ; count : int
8 type surf =
9 { tricount : int
10 ; strcount : int
11 ; name : string
12 ; hdr1 : (int array * float * int32 * int32) array
13 ; hdr2 : (int32 * float * float * int32 array)
14 ; hdr3 : int32 array
17 type surf1 =
18 { size : int
19 ; surf : int
20 ; offs : int
21 ; tri_count : int
22 ; strip_count : int
25 type tex =
26 { texname : string
27 ; nto : (int * int * string) array
28 ; int5 : int32 array
29 ; half1 : int array
30 ; w : int
31 ; h : int
34 type geom =
35 { vertexa : float array
36 ; skin : Skin.skin
37 ; normala : float array
38 ; uva : float array
39 ; colora : string
40 ; surfaces : (int list * surf * GlTex.texture_id lazy_t) list
43 let rsurf1 sbuf =
44 { size = Xff.rint sbuf 0
45 ; surf = Xff.rint sbuf 8
46 ; offs = Xff.rint sbuf 16
47 ; tri_count = Xff.rint sbuf 20
48 ; strip_count = Xff.rint sbuf 24
52 let mag i j k s = sqrt (i*.i +. j*.j +. k*.k +. s*.s);;
54 let verts_in_surf1 surf1 sbuf =
55 let sbuf = Xff.sbufplus sbuf surf1.offs in
56 let r8 = Xff.r8 sbuf in
57 let rec r1 verts pos =
58 let a, b, c, d = r8 pos, r8 (pos+1), r8 (pos+2), r8 (pos+3) in
59 let pos = pos + 4 in
60 let skip pos = r verts pos in
61 match d with
62 | 0x05 when c = 0 -> skip pos
63 | 0x17 when c = 0 -> skip pos
64 | 0x65 -> r verts (pos + 4*c)
65 | 0x68 when a = 1 -> r (verts + c) (pos + 12*c)
66 | 0x68 -> skip (pos + 12*c)
67 | 0x6c -> skip (pos + 16*c)
68 | 0x6d -> skip (pos + 8*c)
69 | 0x6e -> skip (pos + 4*c)
70 | 0x00 when a = 0 && b = 0 && c = 0 -> verts
71 | _ ->
72 let msg = sprintf "geom (a=%x b=%x c=%x d=%x)" a b c d in
73 Xff.sbuferr sbuf pos msg
74 and r verts pos =
75 if pos = surf1.size then verts
76 else r1 verts pos
78 r 0 12
81 let app count pos index pos_incr index_incr f =
82 let rec g p i count = if count = 0 then () else (
83 f p i;
84 g (p + pos_incr) (i + index_incr) (count -1)
86 in g pos (index*index_incr) count
89 let rgeom1 start_index surf1 geom sbuf =
90 let sbuf = Xff.sbufplus sbuf surf1.offs in
91 let r8, r16s, r16, rfloat =
92 Xff.r8 sbuf, Xff.r16s sbuf, Xff.r16 sbuf, Xff.rfloat sbuf
94 let rec r1 counts index prev_count pos =
95 let a, b, c, d = r8 pos, r8 (pos+1), r8 (pos+2), r8 (pos+3) in
96 let pos = pos + 4 in
97 let skip pos = r counts index prev_count pos in
98 let skip2 n = skip (pos+c*n) in
99 match d with
100 | 0x05 when c = 0 -> skip pos
101 | 0x17 when c = 0 -> skip pos
103 | 0x65 ->
104 app c pos index 4 2 (fun pos index ->
105 let u = r16s (pos + 0)
106 and v = r16s (pos + 2) in
107 let u = float u /. 4096.0
108 and v = float v /. 4096.0 in
109 geom.uva.(index + 0) <- u;
110 geom.uva.(index + 1) <- v;
112 skip2 4
114 | 0x68 when a = 1 ->
115 let index = index + prev_count in
116 for i = 0 to pred c do
117 let vi = index*3 + i*3
118 and pi = pos + i*12 in
119 geom.vertexa.(vi + 0) <- rfloat (pi + 0);
120 geom.vertexa.(vi + 1) <- rfloat (pi + 4);
121 geom.vertexa.(vi + 2) <- rfloat (pi + 8);
122 done;
123 r (c :: counts) index c (pos + c*12)
125 | 0x68 when a = 2 ->
126 for i = 0 to pred c do
127 let vi = index*3 + i*3 in
128 let x = rfloat (pos + i*12 + 0) in
129 let y = rfloat (pos + i*12 + 4) in
130 let z = rfloat (pos + i*12 + 8) in
131 geom.normala.(vi + 0) <- x;
132 geom.normala.(vi + 1) <- y;
133 geom.normala.(vi + 2) <- z;
134 done;
135 skip2 12
137 | 0x6c when a = 0 -> skip (pos + 16*c)
138 | 0x6c ->
139 for i = 0 to pred c do
140 let pi = pos + i*16 in
141 let a = rfloat (pi + 0) in
142 let b = rfloat (pi + 4) in
143 let c = rfloat (pi + 8) in
144 let d = Xff.rint sbuf (pi + 12) in
145 geom.skin.(index + i) <- (a,b,c,d);
146 done;
147 skip2 16
149 | 0x6d when a = 2 ->
150 for i = 0 to pred c do
151 let vi = index*3 + i*3 in
152 let x = r16s (pos + i*8 + 0) in
153 let y = r16s (pos + i*8 + 2) in
154 let z = r16s (pos + i*8 + 4) in
155 geom.normala.(vi + 0) <- float x /. 4096.;
156 geom.normala.(vi + 1) <- float y /. 4096.;
157 geom.normala.(vi + 2) <- float z /. 4096.;
158 done;
159 skip2 8
161 | 0x6d when a = 3 ->
162 for i = 0 to pred c do
163 let a = r16s (pos + i*8 + 0) in
164 let b = r16s (pos + i*8 + 2) in
165 let c = r16s (pos + i*8 + 4) in
166 let d = r16s (pos + i*8 + 6) in
167 let i = float a /. 4096. in
168 let j = float b /. 4096. in
169 let k = float c /. 4096. in
170 let s = float d /. 4096. in
171 let _ = i,j,k,s in ()
172 done;
173 skip2 8
175 | 0x6d ->
176 skip2 8
178 | 0x6e ->
179 for i = 0 to pred c do
180 let vi = index*4 + i*4 in
181 Xff.sbufblt sbuf
182 ~dst:geom.colora
183 ~src_pos:(pos + i*4)
184 ~dst_pos:vi
185 ~len:4
187 done;
188 skip2 4
190 | 0x00 when a = 0 && b = 0 && c = 0 ->
191 index + prev_count, counts
193 | _ ->
194 let msg = sprintf "geom (a=%x b=%x c=%x d=%x)" a b c d in
195 Xff.sbuferr sbuf pos msg
197 and r counts index prev_count pos =
198 if pos = surf1.size
199 then index + prev_count, counts
200 else r1 counts index prev_count pos
202 r [] start_index 0 12
205 let rtext n sectbuf sbuf =
206 if not (Xff.cmp sbuf (`chars "TEX\000"))
207 then
208 Xff.sbuferr sbuf 0 "invalid TEX signature"
210 let int5 = Array.init 5 (fun n -> Xff.r32 sbuf (4+n*4)) in
211 let half2_1 = Array.init 2 (fun n -> Xff.r16 sbuf (24+n*2)) in
212 let w = Xff.r16 sbuf 28
213 and h = Xff.r16 sbuf 30 in
214 let nameoff = int5.(0) in
215 let name = (Xff.rcstrtabent sectbuf (Int32.to_int nameoff) 0) in
216 let nto =
217 let xff, sbuf =
218 if true then Xff.test2 (name ^ ".nto")
219 else Xff.test2 ("scee_logo_uk.nto")
221 let dim = (w, h) in
222 Nto.r xff sbuf ~dim ()
224 { texname = name
225 ; nto = nto
226 ; int5 = int5
227 ; half1 = half2_1
228 ; w = w
229 ; h = h
233 let rsrf n sectbuf sbuf =
234 if not (Xff.cmp sbuf (`chars "SRF\000"))
235 then
236 Xff.sbuferr sbuf 0 "invalid SRF signature"
238 let tricount = Xff.rint sbuf 4
239 and stripcount = Xff.rint sbuf 8
240 and nameoff = Xff.rint sbuf 12 in
241 let hdr1 =
242 Array.init 3
243 (fun n ->
244 let sbuf = Xff.sbufplus sbuf (16 + n*16) in
245 let _0 = Array.init 4 (fun n -> Xff.r8 sbuf n) in
246 let _1 = Xff.rfloat sbuf 4 in
247 let _2 = Xff.r32 sbuf 8 in
248 let _3 = Xff.r32 sbuf 12 in
249 (_0, _1, _2, _3)
252 let hdr2 =
253 let sbuf = Xff.sbufplus sbuf (16 + 3*16) in
254 let _0 = Xff.r32 sbuf 0 in
255 let _1 = Xff.rfloat sbuf 4 in
256 let _2 = Xff.rfloat sbuf 8 in
257 let _3 = Array.init 5 (fun n -> Xff.r32 sbuf (12 + n*4)) in
258 (_0, _1, _2, _3)
260 let hdr3 =
261 let sbuf = Xff.sbufplus sbuf (16 + 3*16 + 32) in
262 Array.init 48 (fun n -> Xff.r32 sbuf (n*4))
264 let name = Xff.rcstrtabent sectbuf nameoff 0 in
265 { tricount = tricount
266 ; strcount = stripcount
267 ; name = name
268 ; hdr1 = hdr1
269 ; hdr2 = hdr2
270 ; hdr3 = hdr3
274 let r xff sbufxff =
275 if Array.length xff.Xff.sections != 2
276 then
277 Xff.sbuferr sbufxff 0 "number of xff sections is not 2"
279 let sectpos = xff.Xff.sections.(1).Xff.off in
280 let sectbuf = Xff.sbufplus sbufxff sectpos in
281 let nmobuf = Xff.sbufplus sectbuf xff.Xff.entry in
282 if not (Xff.cmp nmobuf (`chars "NMO\000"))
283 then
284 Xff.sbuferr nmobuf 0 "invalid NMO signature"
286 let hdrs = Array.init 5 (fun n ->
287 let pos = 0x30 + n*16 in
288 let off = Xff.rint nmobuf pos
289 and count = Xff.rint nmobuf (pos+4) in
290 { off = off
291 ; count = count
295 let texts =
296 Array.init hdrs.(1).count (fun n ->
297 let sbuf = Xff.sbufplus sectbuf (hdrs.(1).off + (n*32)) in
298 rtext n sectbuf sbuf
302 let surfs =
303 Array.init hdrs.(2).count (fun n ->
304 let sbuf = Xff.sbufplus sectbuf (hdrs.(2).off + (n*288)) in
305 rsrf n sectbuf sbuf
309 let num_vertices, num_strips =
310 let rec calc num_vertices n =
311 if n = hdrs.(3).count then num_vertices
312 else
313 let sbuf = Xff.sbufplus sectbuf (hdrs.(3).off + (n*32)) in
314 let surf1 = rsurf1 sbuf in
315 let here_verts = verts_in_surf1 surf1 sectbuf in
316 calc (num_vertices + here_verts) (n + 1)
318 let num_vertices = calc 0 0 in
319 let num_strips =
320 Array.fold_left
321 (fun num_strips surf -> num_strips + surf.strcount) 0 surfs
323 (num_vertices, num_strips)
326 let surf1s = Array.init hdrs.(3).count (fun n ->
327 let sbuf = Xff.sbufplus sectbuf (hdrs.(3).off + (n*32)) in
328 rsurf1 sbuf
332 let texts =
333 Array.map
334 (fun text ->
335 lazy
337 let nto = text.nto in
338 let id = GlTex.gen_texture () in
339 GlTex.bind_texture `texture_2d id;
340 GlTex.parameter `texture_2d (`mag_filter `linear);
341 if !Rend.mipmaps then (
342 GlTex.parameter `texture_2d (`min_filter `linear_mipmap_linear);
343 GlTex.parameter `texture_2d (`generate_mipmap true);
345 else (
346 GlTex.parameter `texture_2d (`min_filter `linear);
348 GlTex.parameter `texture_2d (`wrap_s `repeat);
349 GlTex.parameter `texture_2d (`wrap_t `repeat);
350 let image2d level (w, h, data) =
351 let raw = Raw.of_string data `ubyte in
352 let pix = GlPix.of_raw raw `rgba w h in
353 GlTex.image2d ~level pix
355 if !Rend.mipmaps then Array.iteri image2d nto else image2d 0 nto.(0);
358 ) texts
361 let geom =
362 { vertexa = Array.make (num_vertices*3) 0.0
363 ; normala = Array.create (num_vertices*3) 0.0
364 ; colora = String.create (num_vertices*4)
365 ; uva = Array.make (num_vertices*2) 0.0
366 ; skin = Array.create num_vertices (0., 0., 0., 0)
367 ; surfaces = []
370 let _, surfaces =
371 Array.fold_left
372 (fun (last_index, countss) surf1 ->
373 let index, counts = rgeom1 last_index surf1 geom sectbuf in
374 let surf = surfs.(surf1.surf) in
375 let _, _, _, texindex = surf.hdr1.(1) in
376 let text = texts.(Int32.to_int texindex) in
377 (index, (List.rev counts, surf, text) :: countss)
378 ) (0, []) surf1s
380 { geom with surfaces = List.rev surfaces }
383 let draw geom =
384 let l = lazy
386 Skin.init (geom.vertexa, geom.normala, geom.uva, geom.skin, geom.colora)
389 fun ~textures ~lighting ~solid ~colormaterial () ->
390 let () = Lazy.force l in
391 if true then (
392 if textures then (
393 Gl.enable `texture_2d;
395 else (
396 GlDraw.line_width 1.0;
397 GlDraw.color (1., 1., 1.);
399 if colormaterial then (
400 let a = Rend.view.Rend.ambient
401 and d = Rend.view.Rend.diffuse in
402 GlLight.light 0 (`ambient (a,a,a,1.));
403 GlLight.light 0 (`diffuse (d,d,d,1.));
404 Gl.enable `color_material;
406 else (
407 GlLight.light 0 (`ambient (let c = 0.0 in (c,c,c,1.)));
408 GlLight.light 0 (`diffuse (let c = 1.0 in (c,c,c,1.)));
409 GlLight.material `both (`ambient (0.2, 0.2, 0.2, 1.0));
410 GlLight.material `both (`diffuse (0.8, 0.8, 0.8, 1.0));
412 if lighting then (
413 Gl.enable `lighting;
414 Gl.enable `light0;
415 Gl.enable `normalize;
416 GlTex.env (`mode `modulate);
417 GlLight.light_model (`two_side false);
419 else (
420 GlTex.env (`mode `replace);
422 GlDraw.polygon_mode `both (if solid then `fill else `line);
424 let rec f last_index surf = function
425 | [] -> last_index
426 | count :: rest ->
427 GlArray.draw_arrays `triangle_strip last_index count;
428 f (last_index + count) surf rest
429 and g last_index = function
430 | [] -> ()
431 | (counts, surf, id) :: rest ->
432 let texid = Lazy.force id in
433 GlTex.bind_texture `texture_2d texid;
434 let last_index = f last_index surf counts in
435 g last_index rest
438 Skin.draw_begin ();
440 g 0 geom.surfaces;
442 Skin.draw_end ();
444 Gl.disable `texture_2d;
445 Gl.disable `lighting;
446 Gl.disable `light0;
447 Gl.disable `color_material;
451 let obj geom =
452 let draw = draw geom in
453 let onoff c s b = c, "toggle " ^ s, if b then "on" else "off" in
454 (object (self)
455 val dodraw = true
456 val textures = false
457 val lighting = false
458 val solid = true
459 val colormaterial = false
461 method help =
462 [onoff "t""textures" textures
463 ;onoff "l" "lighting" lighting
464 ;onoff "w" "wireframe" (not solid)
465 ;onoff "c" "color material" colormaterial
466 ;onoff "m" "model" dodraw
469 method draw =
470 if dodraw
471 then
472 draw ~textures ~lighting ~solid ~colormaterial ()
474 method char c =
475 match c with
476 | 't' -> {< textures = not textures >}
477 | 'l' -> {< lighting = not lighting >}
478 | 'w' -> {< solid = not solid >}
479 | 'c' -> {< colormaterial = not colormaterial >}
480 | 'm' -> {< dodraw = not dodraw >}
481 | _ -> self
482 end)
485 let _ =
486 let name =
487 match !Rend.nmo_name with
488 | None -> failwith "must supply model name"
489 | Some s -> Filename.basename s
491 let x, sbuf = Xff.test2 name in
492 let geom = r x sbuf in
493 let minmax =
494 let rec f ((minx, maxx, miny, maxy, minz, maxz) as minmax) i =
495 if i >= Array.length geom.vertexa then minmax
496 else
497 let x = geom.vertexa.(i+0) in
498 let y = geom.vertexa.(i+1) in
499 let z = geom.vertexa.(i+2) in
500 let minmax =
501 min minx x, max maxx x,
502 min miny y, max maxy y,
503 min minz z, max maxz z
505 f minmax (i + 3)
507 let x = geom.vertexa.(0) in
508 let y = geom.vertexa.(1) in
509 let z = geom.vertexa.(2) in
510 f (x, x, y, y, z, z) 3
512 Skb.main name;
513 Rend.add_obj (obj geom);
514 Rend.init minmax;
515 Rend.main ()