diff --git a/Makefile b/Makefile index 978bb65..5c9dc75 100644 --- a/Makefile +++ b/Makefile @@ -26,10 +26,11 @@ test: fmt testf: fmt opam exec -- dune runtest -f -OBJ ?= objs/teapot.obj +OBJS ?= objs/teapot.obj +N ?= 1 run: build - opam exec -- dune exec -- zenith -obj $(OBJ) + opam exec -- dune exec -- zenith $(OBJS) -n $(N) raw_run: build clear diff --git a/README.md b/README.md index b39a66b..7537900 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # ZENITH Zen Engine for Navigating wIreframes In Three-dimensional Holographic space -![teapot](media/success2/teapot.gif) +![demo](media/demo.gif) ## Usage diff --git a/bin/argparse.ml b/bin/argparse.ml index 8485bdf..8bb59a7 100644 --- a/bin/argparse.ml +++ b/bin/argparse.ml @@ -1,20 +1,30 @@ open Zenith.Mesh open Zenith.Objloader -type arguments = { obj_mesh : mesh } +type arguments = { obj_meshes : mesh list } + +let copies l n = + if n <= 0 then [] + else List.concat (List.map (fun x -> List.init n (fun _ -> x)) l) let parse_arguments () = - let obj_file = ref "" in - let obj = ref cube in + let obj_files = ref [] in + let objs = ref [ cube ] in + let num_each = ref 1 in let speclist = - [ ("-obj", Arg.String (fun s -> obj_file := s), "An .OBJ file to render") ] + [ + ( "-n", + Arg.Int (fun n -> num_each := n), + "How many of each object to render" ); + ] in + let usage_msg = "Usage: zenith [OBJ]+" in - let usage_msg = "Usage: zenith [OBJ]" in + Arg.parse speclist (fun n -> obj_files := n :: !obj_files) usage_msg; - Arg.parse speclist (fun _ -> ()) usage_msg; + if !obj_files != [] then objs := List.map load_obj !obj_files; - if !obj_file != "" then obj := load_obj !obj_file; + if !num_each != 1 then objs := copies !objs !num_each; - { obj_mesh = !obj } + { obj_meshes = !objs } diff --git a/bin/driver.ml b/bin/driver.ml new file mode 100644 index 0000000..e129157 --- /dev/null +++ b/bin/driver.ml @@ -0,0 +1,113 @@ +open Graphics +open Logging +open Zenith.Config +open Math.Vector +open Zenith.Mesh +open Zenith.Renderer + +let clear_window color = + let fg = foreground in + set_color color; + fill_rect 0 0 (size_x ()) (size_y ()); + set_color fg + +let evenly_spaced_positions meshes = + let n = List.length meshes in + let radius = if n > 1 then 1. +. largest_distance meshes else 0. in + let delta_theta = 2.0 *. Float.pi /. float_of_int n in + + let rec generate_positions theta count acc = + if count <= 0 then acc + else + let x = radius *. Float.cos theta in + let y = radius *. Float.sin theta in + generate_positions (theta +. delta_theta) (count - 1) (v3 x y 0. :: acc) + in + + generate_positions 0.0 n [] + +let to_draw = ref [] +let break_mainloop = ref false +let euler = ref zvec +let translation = ref zvec + +let update_euler n d = + let eulerx, eulery, eulerz = + match !euler with { x; y; z; w = __ } -> (x, y, z) + in + match n with + | 0 -> euler := { x = eulerx +. d; y = eulery; z = eulerz; w = 0. } + | 1 -> euler := { x = eulerx; y = eulery +. d; z = eulerz; w = 0. } + | 2 -> euler := { x = eulerx; y = eulery; z = eulerz +. d; w = 0. } + | _ -> + fatal rc_MatchError + ("Tried to update euler angle " ^ string_of_int n + ^ ", but expected [0:2]") + +let update_translation n d = + let tx, ty, tz = match !translation with { x; y; z; w = __ } -> (x, y, z) in + match n with + | 0 -> translation := { x = tx +. d; y = ty; z = tz; w = 0. } + | 1 -> translation := { x = tx; y = ty +. d; z = tz; w = 0. } + | 2 -> translation := { x = tx; y = ty; z = tz +. d; w = 0. } + | _ -> + fatal rc_MatchError + ("Tried to update translation " ^ string_of_int n + ^ ", but expected [0:2]") + +let input_dispatch key = + match key with + (* X Translation *) + | 'a' -> update_translation 0 (-.translation_incr) + | 'd' -> update_translation 0 translation_incr + (* Y Translation *) + | 'w' -> update_translation 1 translation_incr + | 's' -> update_translation 1 (-.translation_incr) + (*Z Translation *) + | 'q' -> update_translation 2 (-.translation_incr) + | 'e' -> update_translation 2 translation_incr + (* Pitch *) + | 'i' -> update_euler 0 (-.rotation_incr) + | 'k' -> update_euler 0 rotation_incr + (* Yaw *) + | 'j' -> update_euler 1 (-.rotation_incr) + | 'l' -> update_euler 1 rotation_incr + (* Roll *) + | 'u' -> update_euler 2 (-.rotation_incr) + | 'o' -> update_euler 2 rotation_incr + (* Reset scene *) + | 'r' -> + euler := zvec; + translation := zvec + (* Escape *) + | x when x = char_of_int 27 -> break_mainloop := true + | _ -> + print_int (int_of_char key); + print_endline "" + +let draw_scene () = + display_mode false; + clear_window black; + (* Draw each mesh at their offset *) + (* print_endline (string_of_int (List.length !to_draw - 1)); *) + for i = 0 to List.length !to_draw - 1 do + let mesh, offset = List.nth !to_draw i in + draw_mesh mesh !euler (v3_add !translation offset) + done; + let input = wait_next_event [ Poll ] in + synchronize (); + display_mode true; + if input.keypressed then input_dispatch (read_key ()) + +let rec main_loop () = + draw_scene (); + if !break_mainloop then () else main_loop () + +let start meshes = + (* Evenly-space meshes *) + _log Log_Debug (string_of_float (largest_distance meshes)); + let mesh_positions = evenly_spaced_positions meshes in + to_draw := List.combine meshes mesh_positions; + auto_synchronize false; + open_graph (" " ^ string_of_int viewportw ^ "x" ^ string_of_int viewporth); + main_loop () diff --git a/bin/main.ml b/bin/main.ml index de95806..7eddf41 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -1,63 +1,6 @@ -open Graphics -open Logging -open Zenith.Config -open Zenith.Mesh -open Zenith.Renderer - -let clear_window color = - let fg = foreground in - set_color color; - fill_rect 0 0 (size_x ()) (size_y ()); - set_color fg - -let to_draw = ref cube -let break_mainloop = ref false -let euler = ref (0., 0., 0.) - -let update_euler n d = - let eulerx, eulery, eulerz = !euler in - match n with - | 0 -> euler := (eulerx +. d, eulery, eulerz) - | 1 -> euler := (eulerx, eulery +. d, eulerz) - | 2 -> euler := (eulerx, eulery, eulerz +. d) - | _ -> - fatal rc_MatchError - ("Tried to update euler angle " ^ string_of_int n - ^ ", but expected [0:2]") - -let input_dispatch key = - match key with - (* Pitch *) - | 'w' -> update_euler 0 (-.rotation_incr) - | 's' -> update_euler 0 rotation_incr - (* Yaw *) - | 'a' -> update_euler 1 (-.rotation_incr) - | 'd' -> update_euler 1 rotation_incr - (* Roll *) - | 'q' -> update_euler 2 (-.rotation_incr) - | 'e' -> update_euler 2 rotation_incr - (* Reset scene *) - | 'r' -> euler := (0., 0., 0.) - (* Escape *) - | x when x = char_of_int 27 -> break_mainloop := true - | _ -> - print_int (int_of_char key); - print_endline "" - -let draw_scene () = - clear_window black; - draw_mesh !to_draw !euler; - let input = wait_next_event [ Poll ] in - synchronize (); - if input.keypressed then input_dispatch (read_key ()) - -let rec main_loop () = - draw_scene (); - if !break_mainloop then () else main_loop () +open Argparse +open Driver let () = let args = Argparse.parse_arguments () in - to_draw := args.obj_mesh; - open_graph (" " ^ string_of_int viewportw ^ "x" ^ string_of_int viewporth); - auto_synchronize false; - main_loop () + start args.obj_meshes diff --git a/lib/config.ml b/lib/config.ml index d1bc9d8..a05f5d7 100644 --- a/lib/config.ml +++ b/lib/config.ml @@ -1,14 +1,15 @@ open Math.Vector open Graphics -let viewportw, viewporth = (700, 700) -let viewpoint = v3 0. 0. 10. +let viewportw, viewporth = (1000, 1000) +let viewpoint = v3 0. 0. 30. let fov = 45.0 let aspect_ratio = 1. let z_near = 0.1 let z_far = 50. let rotation_incr = 7.5 +let translation_incr = 0.5 let draw_verts = false -let vert_radius = 5 +let vert_radius = 3 let vert_color = red let edge_color = green diff --git a/lib/math/vector.ml b/lib/math/vector.ml index 164f776..56efa59 100644 --- a/lib/math/vector.ml +++ b/lib/math/vector.ml @@ -11,5 +11,14 @@ let vec_unit vec = let vec_dot a b = (a.x *. b.x) +. (a.y *. b.y) +. (a.z *. b.z) +. (a.w *. b.w) let vec_div v n = { x = v.x /. n; y = v.y /. n; z = v.y /. n; w = v.w } +let v3_add v1 v2 = + { x = v1.x +. v2.x; y = v1.y +. v2.y; z = v1.z +. v2.z; w = v1.w } + +let v3_sub v1 v2 = + { x = v1.x -. v2.x; y = v1.y -. v2.y; z = v1.z -. v2.z; w = v1.w } + +let min_vec a b = v3 (min a.x b.x) (min a.y b.y) (min a.z b.z) +let max_vec a b = v3 (max a.x b.x) (max a.y b.y) (max a.z b.z) + let string_of_vec vec = Printf.sprintf "<%f, %f, %f, %f>" vec.x vec.y vec.z vec.w diff --git a/lib/mesh.ml b/lib/mesh.ml index ce054b0..6608a2c 100644 --- a/lib/mesh.ml +++ b/lib/mesh.ml @@ -47,3 +47,30 @@ let cube = let pyramid = ( [ v3 0. 0. 0.; v3 1. 0. 0.; v3 1. 0. 1.; v3 0. 0. 1.; v3 0.5 1. 0.5 ], [ (0, 1); (1, 2); (2, 3); (3, 0); (0, 4); (1, 4); (2, 4); (3, 4) ] ) + +let filter_duplicates meshes = + let rec aux acc seen = function + | [] -> List.rev acc + | mesh :: rest -> + if List.exists (fun m -> m = mesh) seen then aux acc seen rest + else aux (mesh :: acc) (mesh :: seen) rest + in + aux [] [] meshes + +let largest_distance (meshes : mesh list) = + let max_distance = + List.fold_left + (fun acc (verts, _) -> + List.fold_left + (fun acc_vert_i vert_i -> + List.fold_left + (fun acc_vert_j vert_j -> + let dist = v3_sub vert_i vert_j in + let max_dist = max (max dist.x dist.y) dist.z in + max acc_vert_j max_dist) + acc_vert_i verts) + acc verts) + 0.0 (filter_duplicates meshes) + in + + max_distance diff --git a/lib/renderer.ml b/lib/renderer.ml index a6a1f39..1303a84 100644 --- a/lib/renderer.ml +++ b/lib/renderer.ml @@ -44,12 +44,21 @@ let view_matrix eye_pos = dim = (4, 4); } -let model_matrix euler_rot = +let model_matrix euler_rot translation_vec = let radx, rady, radz = match euler_rot with - | rotx, roty, rotz -> (deg_to_rad rotx, deg_to_rad roty, deg_to_rad rotz) + | { x = rotx; y = roty; z = rotz; w = _ } -> + (deg_to_rad rotx, deg_to_rad roty, deg_to_rad rotz) + and tx, ty, tz = + match translation_vec with + | { x = tx; y = ty; z = tz; w = _ } -> (tx, ty, tz) + in + let translation = + { + arr = [| 1.; 0.; 0.; tx; 0.; 1.; 0.; ty; 0.; 0.; 1.; tz; 0.; 0.; 0.; 1. |]; + dim = (4, 4); + } in - let translation = id_matrix 4 4 in let rotation_x, rotation_y, rotation_z = ( { arr = @@ -173,14 +182,14 @@ let projection_matrix fov aspect_ratio z_near z_far = dim = (4, 4); } -let project_mesh mesh euler_rot = +let project_mesh mesh euler_rot translation = let mvp = matmul (matmul (projection_matrix Config.fov Config.aspect_ratio Config.z_near Config.z_far) (view_matrix Config.viewpoint)) - (model_matrix euler_rot) + (model_matrix euler_rot translation) in let f1, f2 = ( (Config.z_far -. Config.z_near) /. 2., @@ -200,10 +209,10 @@ let project_mesh mesh euler_rot = w = x.w; }) -let draw_mesh mesh euler_rot = +let draw_mesh mesh euler_rot translation = let c = foreground in set_color Config.edge_color; - let verts, edges = project_mesh mesh euler_rot in + let verts, edges = project_mesh mesh euler_rot translation in let _ = List.fold_left (fun visited_verts edge -> diff --git a/media/demo.gif b/media/demo.gif new file mode 100644 index 0000000..f67fa39 Binary files /dev/null and b/media/demo.gif differ diff --git a/objs/star_destroyer.mtl b/objs/star_destroyer.mtl new file mode 100644 index 0000000..5b165d4 --- /dev/null +++ b/objs/star_destroyer.mtl @@ -0,0 +1,2 @@ +# Blender MTL File: 'None' +# Material Count: 0 diff --git a/objs/star_destroyer.obj b/objs/star_destroyer.obj new file mode 100644 index 0000000..5d45106 --- /dev/null +++ b/objs/star_destroyer.obj @@ -0,0 +1,677 @@ +# Blender v3.0.0 OBJ File: '' +# www.blender.org +mtllib star_destroyer.mtl +o jet1 +v -0.864172 -0.213484 -3.160349 +v -1.272828 -0.518522 -3.160349 +v -1.396513 -0.333755 -3.160349 +v -1.272828 -0.518522 -3.160349 +v -1.065679 -0.559828 -3.160349 +v -0.896408 -0.433477 -3.160349 +v -0.987855 -0.028717 -3.160349 +v -1.195006 0.012590 -3.160349 +v -1.195006 0.012590 -3.160349 +v -1.364277 -0.113762 -3.160349 +v -1.364277 -0.113762 -3.160349 +v -1.396513 -0.333755 -3.160349 +v -1.065679 -0.559828 -3.160349 +v -0.896408 -0.433477 -3.160349 +v -0.987855 -0.028717 -3.160349 +v -0.864172 -0.213484 -3.160349 +v -0.896408 -0.433477 -3.160349 +v -1.195006 0.012590 -2.541738 +v -1.364276 -0.113762 -2.541738 +v -0.864172 -0.213484 -2.541738 +v -0.896408 -0.433477 -2.541738 +v -1.272828 -0.518522 -2.541738 +v -0.987855 -0.028717 -2.541738 +v -1.065679 -0.559828 -2.541738 +v -0.896408 -0.433477 -2.541738 +v -1.396512 -0.333755 -2.541738 +v 1.328938 -0.213484 -3.160350 +v 0.920282 -0.518522 -3.160350 +v 0.796598 -0.333755 -3.160349 +v 0.920282 -0.518522 -3.160350 +v 1.127431 -0.559828 -3.160350 +v 1.296702 -0.433477 -3.160350 +v 1.205255 -0.028717 -3.160350 +v 0.998105 0.012590 -3.160350 +v 0.998105 0.012590 -3.160350 +v 0.828834 -0.113762 -3.160349 +v 0.828834 -0.113762 -3.160349 +v 0.796598 -0.333755 -3.160349 +v 1.127431 -0.559828 -3.160350 +v 1.296702 -0.433477 -3.160350 +v 1.205255 -0.028717 -3.160350 +v 1.328938 -0.213484 -3.160350 +v 1.296702 -0.433477 -3.160350 +v 0.998105 0.012590 -2.541739 +v 0.828834 -0.113762 -2.541739 +v 1.328938 -0.213484 -2.541739 +v 1.296702 -0.433477 -2.541739 +v 0.920282 -0.518522 -2.541739 +v 1.205255 -0.028717 -2.541739 +v 1.127432 -0.559828 -2.541739 +v 1.296702 -0.433477 -2.541739 +v 0.796599 -0.333755 -2.541738 +v 0.239388 0.058458 -2.006492 +v -0.249223 0.058458 -2.006492 +v 0.239388 0.058458 -2.006492 +v -0.249223 0.058458 -2.006492 +v 0.239388 0.103613 -3.063107 +v -0.249224 0.103613 -3.063107 +v 0.239388 0.103613 -3.063107 +v -0.249224 0.103613 -3.063107 +v -0.249224 0.479816 -3.171151 +v 0.239388 0.479816 -3.171152 +v -0.249224 0.479816 -3.171151 +v 0.239388 0.479816 -3.171152 +v 0.239388 1.184997 -2.742976 +v 0.239388 1.184997 -2.742976 +v -0.249224 1.184997 -2.742976 +v -0.249224 1.184997 -2.742976 +v 0.239388 1.229205 -2.206426 +v -0.249223 1.229205 -2.206426 +v -0.249223 1.229205 -2.206426 +v -0.249223 1.229205 -2.206426 +v 0.239388 1.229205 -2.206426 +v 0.239388 1.229205 -2.206426 +v -0.005839 0.709384 -3.001557 +v -0.005839 0.709384 -3.001557 +v -0.005839 0.709384 -3.001557 +v 0.592566 0.581780 -2.812641 +v 0.592566 0.279520 -2.812641 +v 0.592566 0.581780 -2.812641 +v 0.592566 0.279520 -2.812641 +v -0.575962 0.279520 -2.812640 +v -0.575962 0.279520 -2.812640 +v -0.575962 0.581780 -2.812640 +v 0.592566 0.581780 -2.812641 +v -0.575962 0.581780 -2.812640 +v -0.575962 0.279520 -2.812640 +v -0.575962 0.581780 -2.812640 +v 0.592566 0.279520 -2.812641 +v -0.537512 0.495087 -0.811310 +v -0.537512 0.279520 -0.811310 +v -0.537512 0.495087 -0.811310 +v 0.554117 0.495087 -0.811310 +v 0.554117 0.279520 -0.811310 +v -0.537512 0.279520 -0.811310 +v 0.554117 0.495087 -0.811310 +v 0.554117 0.279520 -0.811310 +v -0.537512 0.495087 -0.811310 +v -0.537512 0.279520 -0.811310 +v 0.554117 0.495087 -0.811310 +v 0.554117 0.279520 -0.811310 +v -0.005839 0.622692 -0.801208 +v -0.005839 0.622692 -0.801208 +v 0.409072 1.214869 -2.259876 +v 0.436800 1.214869 -2.083375 +v 0.613229 1.214869 -2.055206 +v 0.613229 1.214869 -2.055206 +v 0.568365 1.214869 -2.340787 +v 0.409072 1.214869 -2.259876 +v 0.436800 1.214869 -2.083375 +v 0.694540 1.214869 -2.214293 +v 0.694540 1.214869 -2.214293 +v 0.568365 1.214869 -2.340787 +v 0.568365 1.506600 -2.340787 +v 0.694540 1.506600 -2.214293 +v 0.409072 1.506600 -2.259876 +v 0.436800 1.506600 -2.083375 +v 0.613229 1.506600 -2.055206 +v 0.568365 1.506600 -2.340787 +v 0.694540 1.506600 -2.214293 +v 0.409072 1.506600 -2.259876 +v 0.409072 1.506600 -2.259876 +v 0.613229 1.506600 -2.055206 +v 0.436800 1.506600 -2.083375 +v 0.613229 1.506600 -2.055206 +v 0.694540 1.506600 -2.214293 +v 0.544401 1.594347 -2.190707 +v -0.669462 1.214869 -2.197314 +v -0.564446 1.214869 -2.052771 +v -0.394527 1.214869 -2.107981 +v -0.394527 1.214869 -2.107981 +v -0.564446 1.214869 -2.341854 +v -0.669462 1.214869 -2.197314 +v -0.564446 1.214869 -2.052771 +v -0.394527 1.214869 -2.286644 +v -0.394527 1.214869 -2.286644 +v -0.564446 1.214869 -2.341854 +v -0.564446 1.506600 -2.341854 +v -0.394527 1.506600 -2.286644 +v -0.669462 1.506600 -2.197314 +v -0.564446 1.506600 -2.052771 +v -0.394527 1.506600 -2.107981 +v -0.564446 1.506600 -2.341854 +v -0.394527 1.506600 -2.286644 +v -0.669462 1.506600 -2.197314 +v -0.669462 1.506600 -2.197314 +v -0.394527 1.506600 -2.107981 +v -0.564446 1.506600 -2.052771 +v -0.394527 1.506600 -2.107981 +v -0.394527 1.506600 -2.286644 +v -0.517481 1.594347 -2.197314 +v -0.629293 -0.022872 -0.399975 +v -0.629293 0.168090 -0.399975 +v -0.629293 0.168090 -0.399975 +v -0.455675 0.154719 0.201314 +v -0.455675 -0.015692 0.311094 +v -0.455675 -0.015692 0.311094 +v -0.455675 0.154719 0.201314 +v -0.455675 0.154719 0.201314 +v -0.418303 -0.015520 0.311825 +v -0.005603 0.235435 0.315125 +v -0.005603 0.235435 0.315125 +v 0.000922 0.270744 -0.413890 +v 0.000922 0.270744 -0.413890 +v 0.449024 0.158887 0.219011 +v 0.449024 -0.011524 0.328794 +v 0.449024 -0.011524 0.328794 +v 0.449024 0.158887 0.219011 +v 0.449024 0.158887 0.219011 +v 0.584624 -0.017570 -0.388196 +v 0.584624 0.169450 -0.388196 +v 0.584624 0.169450 -0.388196 +v -0.005840 0.471613 -3.024554 +v -0.005840 0.471613 -3.024554 +v -1.027191 0.275395 -2.694585 +v -1.027191 0.275395 -2.694585 +v -1.027191 -0.042617 -2.694585 +v -1.027191 -0.042617 -2.694585 +v -1.027191 0.275395 -2.694585 +v 1.103258 -0.106808 -2.669804 +v 1.103258 0.257514 -2.669804 +v 1.103258 0.257514 -2.669804 +v 1.103258 0.257514 -2.669804 +v 1.103258 -0.106808 -2.669804 +v 0.790574 0.323128 -1.945204 +v 0.790574 0.323128 -1.945204 +v 0.790574 0.323128 -1.945204 +v 0.790574 -0.041194 -1.945202 +v 0.790574 -0.041194 -1.945202 +v -1.014917 0.276818 -1.936474 +v -0.781778 0.323128 -1.936474 +v -0.781778 0.323128 -1.936474 +v -0.781778 0.323128 -1.936474 +v -1.014917 0.276818 -1.936474 +v -1.014917 0.276818 -1.936474 +v -1.014917 -0.041194 -1.936470 +v -0.781778 -0.041194 -1.936470 +v -1.014917 -0.041194 -1.936470 +v -0.781778 -0.041194 -1.936470 +v 1.090984 0.258936 -1.931485 +v 1.090984 0.258936 -1.931485 +v 1.090984 0.258936 -1.931485 +v 1.090984 -0.105384 -1.931483 +v 1.090984 -0.105384 -1.931483 +v 0.737289 -0.042617 -0.343144 +v 0.737289 -0.042617 -0.343144 +v -0.005839 0.435457 -0.341601 +v -0.005839 0.435457 -0.341601 +v 0.737289 0.285549 -0.340639 +v 0.737289 0.285549 -0.340639 +v 0.737289 0.285549 -0.340639 +v -0.737212 -0.042617 -0.320679 +v -0.737212 -0.042617 -0.320679 +v -0.737212 0.285549 -0.318174 +v -0.737212 0.285549 -0.318174 +v -0.737212 0.285549 -0.318174 +v -0.787678 1.024871 -2.482593 +v -0.787678 1.248174 -2.482593 +v -0.787678 1.248174 -2.482593 +v -0.787678 1.024871 -1.945426 +v -0.787678 1.024871 -2.482593 +v -0.787678 1.248174 -1.945426 +v -0.787678 1.248174 -1.945426 +v -0.787678 1.024871 -1.945426 +v -0.787678 1.024871 -2.482593 +v -0.787678 1.024871 -1.945426 +v -0.787678 1.248174 -1.945426 +v -0.787678 1.248174 -2.482593 +v -0.010656 0.914503 -2.590056 +v -0.010656 0.914503 -1.945426 +v -0.010656 0.914503 -1.945426 +v -0.010656 0.914503 -2.590056 +v -0.010656 1.358541 -1.945426 +v -0.010656 1.358541 -1.945426 +v -0.010656 1.358541 -2.590056 +v -0.010656 1.358541 -2.590056 +v 0.766366 1.024871 -1.945426 +v 0.766366 1.248174 -1.945426 +v 0.766366 1.248174 -1.945426 +v 0.766366 1.248174 -2.482594 +v 0.766366 1.248174 -2.482594 +v 0.766366 1.024871 -1.945426 +v 0.766366 1.024871 -2.482594 +v 0.766366 1.024871 -2.482594 +v 0.766366 1.248174 -2.482594 +v 0.766366 1.024871 -1.945426 +v 0.766366 1.248174 -1.945426 +v 0.766366 1.024871 -2.482594 +v -0.006044 0.218547 -3.244982 +v -0.006044 0.218547 -3.244982 +v -0.006044 -0.837909 -3.244982 +v -0.006044 -0.837909 -3.244982 +v -0.006044 0.218547 -3.244982 +v -0.006044 -0.837909 -3.244982 +v 2.049723 -0.130907 -2.609800 +v -2.021690 -0.408216 -2.609799 +v 2.049723 -0.408216 -2.609800 +v -2.021690 -0.130907 -2.609799 +v -2.021690 -0.130907 -2.609799 +v 2.049723 -0.130907 -2.609800 +v 2.049723 -0.408216 -2.609800 +v 2.049723 -0.408216 -2.609800 +v 2.049723 -0.130907 -2.609800 +v -2.021690 -0.408216 -2.609799 +v -2.021690 -0.130907 -2.609799 +v -2.021690 -0.408216 -2.609799 +v -0.006042 -0.051060 3.952354 +v -0.006042 -0.207223 3.952354 +v -0.006042 -0.051060 3.952354 +v -0.006042 -0.051060 3.952354 +v -0.006042 -0.207223 3.952354 +v -0.006042 -0.051060 3.952354 +v -0.006042 -0.207223 3.952354 +l 10 12 +l 1 2 +l 4 22 +l 3 4 +l 2 5 +l 1 6 +l 7 8 +l 9 10 +l 3 11 +l 1 12 +l 4 13 +l 13 14 +l 1 15 +l 7 16 +l 16 17 +l 7 18 +l 8 19 +l 16 20 +l 17 21 +l 3 22 +l 7 23 +l 4 24 +l 13 25 +l 3 26 +l 22 24 +l 12 15 +l 2 6 +l 22 26 +l 20 23 +l 11 19 +l 24 25 +l 5 6 +l 9 15 +l 2 12 +l 8 18 +l 19 26 +l 11 26 +l 13 24 +l 17 20 +l 14 25 +l 18 19 +l 16 23 +l 8 11 +l 18 23 +l 9 12 +l 20 21 +l 36 38 +l 27 28 +l 30 48 +l 29 30 +l 28 31 +l 27 32 +l 35 41 +l 27 35 +l 35 36 +l 29 37 +l 27 38 +l 30 39 +l 39 40 +l 27 41 +l 33 42 +l 42 43 +l 33 44 +l 34 45 +l 42 46 +l 43 47 +l 29 48 +l 33 49 +l 30 50 +l 39 51 +l 29 52 +l 48 50 +l 28 32 +l 48 52 +l 46 49 +l 37 45 +l 50 51 +l 31 32 +l 33 34 +l 28 38 +l 34 44 +l 45 52 +l 37 52 +l 39 50 +l 43 46 +l 40 51 +l 44 45 +l 42 49 +l 34 37 +l 44 49 +l 35 38 +l 46 47 +l 55 56 +l 53 57 +l 54 58 +l 59 60 +l 54 61 +l 53 62 +l 60 63 +l 59 64 +l 62 65 +l 63 66 +l 57 62 +l 65 74 +l 66 69 +l 58 61 +l 54 71 +l 56 72 +l 55 73 +l 53 74 +l 69 70 +l 64 66 +l 61 71 +l 63 68 +l 61 67 +l 63 64 +l 60 64 +l 68 69 +l 62 74 +l 68 70 +l 66 68 +l 72 73 +l 67 71 +l 56 73 +l 94 96 +l 78 79 +l 77 80 +l 77 81 +l 81 82 +l 77 84 +l 76 85 +l 86 87 +l 75 88 +l 83 89 +l 86 90 +l 87 91 +l 88 92 +l 85 93 +l 94 95 +l 92 103 +l 79 97 +l 95 98 +l 83 99 +l 78 100 +l 99 101 +l 95 102 +l 76 103 +l 93 103 +l 79 100 +l 87 90 +l 97 100 +l 96 102 +l 77 82 +l 95 96 +l 98 102 +l 75 103 +l 80 81 +l 90 91 +l 85 103 +l 82 84 +l 89 99 +l 89 101 +l 88 103 +l 104 105 +l 108 109 +l 107 110 +l 106 111 +l 114 115 +l 112 113 +l 108 114 +l 113 115 +l 116 117 +l 117 118 +l 116 119 +l 118 120 +l 109 121 +l 104 122 +l 110 123 +l 105 124 +l 106 125 +l 111 126 +l 125 126 +l 111 125 +l 114 121 +l 119 120 +l 116 127 +l 107 123 +l 109 114 +l 117 127 +l 105 122 +l 118 127 +l 110 124 +l 119 127 +l 120 127 +l 122 124 +l 112 115 +l 113 114 +l 123 124 +l 128 129 +l 132 133 +l 131 134 +l 130 135 +l 138 139 +l 136 137 +l 132 138 +l 137 139 +l 140 141 +l 141 142 +l 140 143 +l 142 144 +l 133 145 +l 128 146 +l 134 147 +l 129 148 +l 130 149 +l 135 150 +l 149 150 +l 135 149 +l 138 145 +l 143 144 +l 140 151 +l 131 147 +l 133 138 +l 141 151 +l 129 146 +l 142 151 +l 134 148 +l 143 151 +l 144 151 +l 146 148 +l 136 139 +l 137 138 +l 147 148 +l 161 163 +l 152 154 +l 153 155 +l 152 156 +l 157 158 +l 154 159 +l 157 160 +l 155 161 +l 157 162 +l 153 163 +l 161 164 +l 162 165 +l 160 166 +l 155 163 +l 161 169 +l 167 170 +l 164 171 +l 168 172 +l 156 159 +l 162 166 +l 168 170 +l 158 162 +l 154 156 +l 169 171 +l 165 166 +l 167 168 +l 160 162 +l 170 172 +l 164 169 +l 188 200 +l 173 175 +l 174 176 +l 174 177 +l 178 179 +l 174 180 +l 174 181 +l 173 182 +l 182 186 +l 183 184 +l 173 186 +l 191 198 +l 185 188 +l 187 189 +l 212 216 +l 190 191 +l 197 216 +l 173 193 +l 173 194 +l 179 195 +l 178 196 +l 192 197 +l 190 198 +l 191 199 +l 185 200 +l 182 201 +l 183 202 +l 186 209 +l 188 204 +l 189 205 +l 173 207 +l 173 209 +l 187 210 +l 206 211 +l 197 212 +l 206 213 +l 208 214 +l 193 215 +l 192 216 +l 176 177 +l 205 210 +l 179 196 +l 193 194 +l 195 196 +l 189 210 +l 177 180 +l 207 209 +l 198 199 +l 193 207 +l 213 214 +l 208 211 +l 180 181 +l 208 213 +l 207 215 +l 184 202 +l 211 213 +l 184 203 +l 200 204 +l 186 201 +l 175 194 +l 202 203 +l 226 228 +l 217 218 +l 235 240 +l 220 222 +l 219 223 +l 221 224 +l 227 228 +l 225 226 +l 226 227 +l 223 235 +l 217 229 +l 221 230 +l 220 231 +l 221 232 +l 220 233 +l 223 234 +l 219 235 +l 218 236 +l 237 238 +l 234 239 +l 233 247 +l 238 241 +l 230 242 +l 232 243 +l 229 244 +l 236 245 +l 231 246 +l 231 247 +l 237 248 +l 236 244 +l 239 240 +l 230 232 +l 229 236 +l 231 233 +l 238 248 +l 218 229 +l 242 243 +l 241 248 +l 234 235 +l 244 245 +l 232 242 +l 225 228 +l 222 233 +l 224 230 +l 234 240 +l 246 247 +l 252 257 +l 249 252 +l 253 255 +l 252 256 +l 249 257 +l 250 258 +l 249 260 +l 251 261 +l 262 263 +l 261 271 +l 249 265 +l 254 266 +l 263 267 +l 264 268 +l 259 269 +l 250 270 +l 251 271 +l 253 272 +l 262 273 +l 259 264 +l 264 269 +l 255 272 +l 252 265 +l 263 273 +l 256 265 +l 258 270 +l 254 271 +l 266 271 +l 257 260 +l 267 273 +l 268 269