diff --git a/.gitignore b/.gitignore index 3e57027..a353719 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,4 @@ *.cmxa _build .vscode/* -*.mtl .ocamlformat diff --git a/README.md b/README.md index 47ffc4f..77ecec2 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ OBJS="objs/uv_sphere.obj objs/torus.obj objs/star_destroyer.obj objs/pyramid.obj ### Blender -If you're using Blender to create 3D models for ZENITH, ensure that, in the export window under `Geometry`, the options "Write Normals," "Include UVs," and "Write Materials" are deactivated. These options generate vernacular in the output `.obj` file that my loader does not support. +If you want to color your Blender models, the only shader compatible with the `Kd` (diffuse color) field is Principled BSDF. ### Supported .OBJ Vernacular diff --git a/bin/argparse.ml b/bin/argparse.ml index 8bb59a7..4861c38 100644 --- a/bin/argparse.ml +++ b/bin/argparse.ml @@ -9,7 +9,7 @@ let copies l n = let parse_arguments () = let obj_files = ref [] in - let objs = ref [ cube ] in + let objs : mesh list ref = ref [ cube ] in let num_each = ref 1 in let speclist = diff --git a/lib/logging/logging.ml b/lib/logging/logging.ml index 71d985a..ce55273 100644 --- a/lib/logging/logging.ml +++ b/lib/logging/logging.ml @@ -27,6 +27,7 @@ let rc_Ok = (0, "OK") and rc_Error = (1, "ERROR") and rc_OOB = (2, "OUT OF BOUNDS ERROR") and rc_MatchError = (3, "MATCH ERROR") +and rc_MaterialError = (4, "MATERIAL ERROR") (** ANSI encoding for bold text *) let ansi_bold = "\x1b[1m" diff --git a/lib/mesh.ml b/lib/mesh.ml index 6608a2c..d1f03c4 100644 --- a/lib/mesh.ml +++ b/lib/mesh.ml @@ -1,24 +1,34 @@ +open Graphics open Math.Vector -type mesh = vec list * (int * int) list +(* Vertices, edges, edge colors *) +type mesh = vec list * ((int * int) * color option) list -let print_mesh mesh = +let print_mesh (mesh : mesh) = let vertices, edges = mesh in Printf.printf "Vertices:\n"; List.iteri (fun i v -> Printf.printf "%d: %s\n" i (string_of_vec v)) vertices; Printf.printf "\nEdges:\n"; - List.iter (fun (a, b) -> Printf.printf "(%d, %d)\n" a b) edges + List.iter + (fun ((a, b), c) -> + Printf.printf "(%d, %d) Color: %s\n" a b + (match c with None -> "None" | Some x -> string_of_int x)) + edges -let map_verts mesh f = (List.map f (fst mesh), snd mesh) +let map_verts (mesh : mesh) (f : vec -> vec) : mesh = + let verts, edges = mesh in + (List.map f verts, edges) -let triangle = - ([ v3 0.5 0. 0.; v3 1. 0. 0.; v3 1. 1. 0. ], [ (0, 1); (1, 2); (2, 0) ]) +let edge a b = ((a, b), None) -let square = +let triangle : mesh = + ([ v3 0.5 0. 0.; v3 1. 0. 0.; v3 1. 1. 0. ], [ edge 0 1; edge 1 2; edge 2 0 ]) + +let square : mesh = ( [ v3 0. 0. 0.; v3 0. 1. 0.; v3 1. 0. 0.; v3 1. 1. 0. ], - [ (0, 1); (0, 2); (3, 1); (3, 2) ] ) + [ edge 0 1; edge 0 2; edge 3 1; edge 3 2 ] ) -let cube = +let cube : mesh = ( [ v3 0. 0. 0.; v3 0. 0. 1.; @@ -30,23 +40,32 @@ let cube = v3 1. 1. 1.; ], [ - (0, 1); - (0, 2); - (0, 4); - (1, 3); - (1, 5); - (2, 3); - (2, 6); - (3, 7); - (4, 5); - (4, 6); - (5, 7); - (6, 7); + edge 0 1; + edge 0 2; + edge 0 4; + edge 1 3; + edge 1 5; + edge 2 3; + edge 2 6; + edge 3 7; + edge 4 5; + edge 4 6; + edge 5 7; + edge 6 7; ] ) -let pyramid = +let pyramid : mesh = ( [ 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) ] ) + [ + edge 0 1; + edge 1 2; + edge 2 3; + edge 3 0; + edge 0 4; + edge 1 4; + edge 2 4; + edge 3 4; + ] ) let filter_duplicates meshes = let rec aux acc seen = function diff --git a/lib/objloader.ml b/lib/objloader.ml index f3a5957..d66e6e3 100644 --- a/lib/objloader.ml +++ b/lib/objloader.ml @@ -1,7 +1,15 @@ +open Graphics open Mesh open Math.Vector +open Logging -type vernacular = Vertex of vec | Edge of (int * int) +type vernacular = + | Vertex of vec + | Edge of ((int * int) * color option) + | MaterialFilename of string + | UseMaterial of string + +type material = { name : string; color : color } let pairs_of_consecutive_elements lst = let rec aux first = function @@ -13,52 +21,120 @@ let pairs_of_consecutive_elements lst = in match lst with [] | [ _ ] -> [] | h :: _ :: _ -> aux h lst -let parse_obj_line line : vernacular list option = +let parse_obj_line line current_mtl : vernacular list option = match String.split_on_char ' ' line with - | [] -> None - | [ "#" ] -> None (* Comment line *) | "v" :: xs -> let v = List.map float_of_string xs in Some [ Vertex (v3 (List.nth v 0) (List.nth v 1) (List.nth v 2)) ] | "l" :: xs -> - let edge = List.map (fun s -> int_of_string s - 1) xs in - Some [ Edge (List.nth edge 0, List.nth edge 1) ] + let parsed_edge = List.map (fun s -> int_of_string s - 1) xs in + Some + [ + Edge + ( (List.nth parsed_edge 0, List.nth parsed_edge 1), + Some current_mtl.color ); + ] | "f" :: xs -> - let vertices = List.map (fun s -> int_of_string s - 1) xs in + let vertices = + List.map + (fun s -> + int_of_string + (if String.contains s '/' then String.sub s 0 (String.index s '/') + else s) + - 1) + xs + in let edges = pairs_of_consecutive_elements vertices in - Some (List.map (fun x -> Edge x) edges) + Some (List.map (fun x -> Edge (x, Some current_mtl.color)) edges) + | [ "mtllib"; name ] -> Some [ MaterialFilename name ] + | [ "usemtl"; name ] -> Some [ UseMaterial name ] | _ -> None -let rec parse_obj_lines lines vertices edges = +let scan_file filename = + let lines = ref [] in + try + let chan = open_in filename in + try + while true do + lines := input_line chan :: !lines + done; + !lines + with End_of_file -> + close_in chan; + List.rev !lines + with + | Sys_error s when String.ends_with ~suffix:"No such file or directory" s -> + [] + +let parse_mtls obj_fn mtl_fn : material list = + let lines = scan_file (Filename.concat (Filename.dirname obj_fn) mtl_fn) in + let found_materials : material list ref = ref [] in + let _ = + List.fold_left + (fun (name, color) line -> + if name != None && color != None then + match (name, color) with + | Some n, Some c -> + found_materials := { name = n; color = c } :: !found_materials; + (None, None) + | _ -> failwith "This never happens" + else + match String.split_on_char ' ' line with + | "newmtl" :: n -> (Some (String.concat " " n), color) + | [ "Kd"; r; g; b ] -> + let kd_to_comp kd = int_of_float (float_of_string kd *. 255.) in + (name, Some (rgb (kd_to_comp r) (kd_to_comp g) (kd_to_comp b))) + | _ -> (name, color)) + (None, None) lines + in + if List.length !found_materials = 0 then + fatal rc_MaterialError + ("Material file " ^ mtl_fn + ^ " did not contain both a material name and diffuse color") + else !found_materials + +let get_material l name obj_fn = + match + List.fold_left + (fun s i -> + if s != None then s else if i.name = name then Some i else None) + None l + with + | Some m -> m + | _ -> + fatal rc_MaterialError + ("Object file " ^ obj_fn ^ " tried to use material \"" ^ name + ^ "\" that has not been declared yet") + +let rec parse_obj_lines obj_fn lines (materials : material list) current_mtl + vertices edges = match lines with - | [] -> (vertices, edges) + | [] -> (List.rev vertices, edges) | line :: rest -> ( - match parse_obj_line line with - | Some [ Vertex v ] -> parse_obj_lines rest (v :: vertices) edges - | Some [ Edge e ] -> parse_obj_lines rest vertices (e :: edges) + match parse_obj_line line current_mtl with + | Some [ Vertex v ] -> + parse_obj_lines obj_fn rest materials current_mtl (v :: vertices) + edges + | Some [ Edge e ] -> + parse_obj_lines obj_fn rest materials current_mtl vertices (e :: edges) | Some (Edge e :: t) -> - parse_obj_lines rest vertices + parse_obj_lines obj_fn rest materials current_mtl vertices (List.map - (fun x -> match x with Edge _e -> _e | _ -> (0, 0)) + (fun x -> match x with Edge _e -> _e | _ -> edge 0 0) (Edge e :: t) @ edges) - | _ -> parse_obj_lines rest vertices edges) - -let parse_obj_file filename = - let lines = ref [] in - let chan = open_in filename in - try - while true do - lines := input_line chan :: !lines - done; - !lines - with End_of_file -> - close_in chan; - List.rev !lines + | Some [ MaterialFilename mtl_fn ] -> + parse_obj_lines obj_fn rest + (parse_mtls obj_fn mtl_fn @ materials) + current_mtl vertices edges + | Some [ UseMaterial mtl_name ] -> + parse_obj_lines obj_fn rest materials + (get_material materials mtl_name obj_fn) + vertices edges + | _ -> parse_obj_lines obj_fn rest materials current_mtl vertices edges) -let load_obj filename : mesh = - let lines = parse_obj_file filename in - let vertices, edges = parse_obj_lines lines [] [] in - let out = (List.rev vertices, List.rev edges) in - (* print_mesh out; *) - out +let load_obj obj_fn : mesh = + let obj_lines = scan_file obj_fn in + parse_obj_lines obj_fn obj_lines [] + { name = "Default"; color = Config.edge_color } + [] [] diff --git a/lib/renderer.ml b/lib/renderer.ml index 1303a84..9b5f450 100644 --- a/lib/renderer.ml +++ b/lib/renderer.ml @@ -182,7 +182,7 @@ let projection_matrix fov aspect_ratio z_near z_far = dim = (4, 4); } -let project_mesh mesh euler_rot translation = +let project_mesh (m : mesh) euler_rot translation : mesh = let mvp = matmul (matmul @@ -195,7 +195,7 @@ let project_mesh mesh euler_rot translation = ( (Config.z_far -. Config.z_near) /. 2., (Config.z_far +. Config.z_near) /. 2. ) in - map_verts mesh (fun v -> + map_verts m (fun v -> v |> fun x -> (* Projection *) vecmatmul x mvp |> fun x -> @@ -209,14 +209,16 @@ let project_mesh mesh euler_rot translation = w = x.w; }) -let draw_mesh mesh euler_rot translation = +let draw_mesh m euler_rot translation = let c = foreground in set_color Config.edge_color; - let verts, edges = project_mesh mesh euler_rot translation in + let verts, edges = project_mesh m euler_rot translation in let _ = List.fold_left - (fun visited_verts edge -> - let v1, v2 = (List.nth verts (fst edge), List.nth verts (snd edge)) in + (fun (index, visited_verts) e -> + let (e1, e2), ecolor = e in + let v1, v2 = (List.nth verts e1, List.nth verts e2) in + let _ = match ecolor with Some c -> set_color c | _ -> () in draw_line v1 v2; if Config.draw_verts then ( set_color Config.vert_color; @@ -227,7 +229,7 @@ let draw_mesh mesh euler_rot translation = fill_circle (int_of_float v2.x) (int_of_float v2.y) Config.vert_radius; set_color Config.edge_color); - v1 :: v2 :: visited_verts) - [] edges + (index + 1, v1 :: v2 :: visited_verts)) + (0, []) edges in set_color c diff --git a/media/logo.png b/media/logo.png index 7c4bcb8..95b4f15 100644 Binary files a/media/logo.png and b/media/logo.png differ diff --git a/objs/teapot.obj b/objs/teapot.obj index 9b194f3..3914788 100644 --- a/objs/teapot.obj +++ b/objs/teapot.obj @@ -1,6 +1,5 @@ # Blender v3.0.0 OBJ File: '' # www.blender.org -mtllib teapot.mtl o teapot v 1.368074 2.435437 -0.227403 v 1.381968 2.400000 -0.229712 diff --git a/objs/zenith.mtl b/objs/zenith.mtl new file mode 100644 index 0000000..f79cc13 --- /dev/null +++ b/objs/zenith.mtl @@ -0,0 +1,72 @@ +# Blender MTL File: 'untitled.blend' +# Material Count: 7 + +newmtl Blue +Ns 225.000000 +Ka 1.000000 1.000000 1.000000 +Kd 0.000000 0.000000 1.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.450000 +d 1.000000 +illum 2 + +newmtl Green +Ns 225.000000 +Ka 1.000000 1.000000 1.000000 +Kd 0.000000 1.000000 0.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.450000 +d 1.000000 +illum 2 + +newmtl Indigo +Ns 225.000000 +Ka 1.000000 1.000000 1.000000 +Kd 0.131147 0.000000 0.416085 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.450000 +d 1.000000 +illum 2 + +newmtl Orange +Ns 225.000000 +Ka 1.000000 1.000000 1.000000 +Kd 1.000000 0.376262 0.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.450000 +d 1.000000 +illum 2 + +newmtl Red +Ns 225.000000 +Ka 1.000000 1.000000 1.000000 +Kd 1.000000 0.000000 0.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.450000 +d 1.000000 +illum 2 + +newmtl Violet +Ns 225.000000 +Ka 1.000000 1.000000 1.000000 +Kd 0.212231 0.000000 1.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.450000 +d 1.000000 +illum 2 + +newmtl Yellow +Ns 225.000000 +Ka 1.000000 1.000000 1.000000 +Kd 1.000000 1.000000 0.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.450000 +d 1.000000 +illum 2 diff --git a/objs/zenith.obj b/objs/zenith.obj index 4752d2e..ff47bce 100644 --- a/objs/zenith.obj +++ b/objs/zenith.obj @@ -1,5 +1,6 @@ -# Blender v3.0.1 OBJ File: '' +# Blender v3.0.1 OBJ File: 'untitled.blend' # www.blender.org +mtllib zenith.mtl o Text v 6.430280 -1.504543 -0.625003 v 6.430280 1.402433 -0.625002 @@ -137,184 +138,337 @@ v -7.980018 -0.786105 0.624998 v -7.980018 -0.902384 0.624998 v -6.219220 -0.902384 0.624998 v -6.219220 -1.504543 0.624998 +vt 0.363636 0.000000 +vt 0.454545 0.000000 +vt 0.545455 0.000000 +vt 0.272727 0.000000 +vt 0.636364 0.000000 +vt 0.181818 0.000000 +vt 0.090909 0.000000 +vt 0.727273 0.000000 +vt 0.000000 0.000000 +vt 0.818182 0.000000 +vt 0.909091 0.000000 +vt 1.000000 0.000000 +vt 0.363636 0.000000 +vt 0.545455 0.000000 +vt 0.454545 0.000000 +vt 0.272727 0.000000 +vt 0.636364 0.000000 +vt 0.181818 0.000000 +vt 0.090909 0.000000 +vt 0.727273 0.000000 +vt 0.000000 0.000000 +vt 0.818182 0.000000 +vt 0.909091 0.000000 +vt 1.000000 0.000000 +vt 0.000000 0.000000 +vt 0.090909 0.000000 +vt 0.363636 0.000000 +vt 0.181818 0.000000 +vt 0.272727 0.000000 +vt 0.454545 0.000000 +vt 0.727273 0.000000 +vt 0.545455 0.000000 +vt 0.636364 0.000000 +vt 0.818182 0.000000 +vt 0.909091 0.000000 +vt 1.000000 0.000000 +vt 0.000000 0.000000 +vt 0.363636 0.000000 +vt 0.090909 0.000000 +vt 0.181818 0.000000 +vt 0.272727 0.000000 +vt 0.454545 0.000000 +vt 0.727273 0.000000 +vt 0.545455 0.000000 +vt 0.636364 0.000000 +vt 0.818182 0.000000 +vt 0.909091 0.000000 +vt 1.000000 0.000000 +vt 0.000000 0.000000 +vt 0.090909 0.000000 +vt 0.909091 0.000000 +vt 0.181818 0.000000 +vt 0.818182 0.000000 +vt 0.272727 0.000000 +vt 0.363636 0.000000 +vt 0.454545 0.000000 +vt 0.545455 0.000000 +vt 0.636364 0.000000 +vt 1.000000 0.000000 +vt 0.727273 0.000000 +vt 0.000000 0.000000 +vt 0.909091 0.000000 +vt 0.090909 0.000000 +vt 0.181818 0.000000 +vt 0.818182 0.000000 +vt 0.272727 0.000000 +vt 0.363636 0.000000 +vt 0.545455 0.000000 +vt 0.454545 0.000000 +vt 0.636364 0.000000 +vt 1.000000 0.000000 +vt 0.727273 0.000000 +vt 0.363636 0.000000 +vt 0.454545 0.000000 +vt 0.545455 0.000000 +vt 0.636364 0.000000 +vt 0.272727 0.000000 +vt 0.181818 0.000000 +vt 0.727273 0.000000 +vt 0.818182 0.000000 +vt 0.000000 0.000000 +vt 0.090909 0.000000 +vt 0.909091 0.000000 +vt 1.000000 0.000000 +vt 0.363636 0.000000 +vt 0.545455 0.000000 +vt 0.454545 0.000000 +vt 0.636364 0.000000 +vt 0.272727 0.000000 +vt 0.181818 0.000000 +vt 0.727273 0.000000 +vt 0.818182 0.000000 +vt 0.000000 0.000000 +vt 0.090909 0.000000 +vt 0.909091 0.000000 +vt 1.000000 0.000000 +vt 0.285714 0.000000 +vt 0.428571 0.000000 +vt 0.571429 0.000000 +vt 0.714286 0.000000 +vt 0.142857 0.000000 +vt 0.000000 0.000000 +vt 0.857143 0.000000 +vt 1.000000 0.000000 +vt 0.285714 0.000000 +vt 0.571429 0.000000 +vt 0.428571 0.000000 +vt 0.714286 0.000000 +vt 0.142857 0.000000 +vt 0.000000 0.000000 +vt 0.857143 0.000000 +vt 1.000000 0.000000 +vt 0.000000 0.000000 +vt 0.090909 0.000000 +vt 0.181818 0.000000 +vt 0.272727 0.000000 +vt 0.909091 0.000000 +vt 0.363636 0.000000 +vt 1.000000 0.000000 +vt 0.000000 0.000000 +vt 0.181818 0.000000 +vt 0.090909 0.000000 +vt 0.272727 0.000000 +vt 0.909091 0.000000 +vt 0.363636 0.000000 +vt 1.000000 0.000000 +vt 0.454545 0.000000 +vt 0.545455 0.000000 +vt 0.636364 0.000000 +vt 0.818182 0.000000 +vt 0.727273 0.000000 +vt 0.545455 0.000000 +vt 0.454545 0.000000 +vt 0.636364 0.000000 +vt 0.818182 0.000000 +vt 0.727273 0.000000 +vn 0.0000 0.0000 -1.0000 +vn 0.0000 -0.0000 1.0000 +vn 0.0000 -1.0000 0.0000 +vn 1.0000 0.0000 0.0000 +vn -1.0000 0.0000 0.0000 +vn -0.6192 0.7852 0.0000 +vn 0.6192 -0.7852 -0.0000 +vn 0.0000 1.0000 0.0000 +vn 0.8889 0.4582 0.0000 +vn -0.8889 -0.4582 0.0000 +usemtl Red s off -f 1 2 3 -f 1 3 4 -f 5 6 7 -f 5 7 8 -f 1 4 11 -f 11 4 5 -f 11 5 10 -f 10 5 8 -f 1 11 12 -f 9 10 8 -f 15 16 17 -f 15 17 18 -f 14 15 18 -f 13 14 19 -f 19 14 18 -f 13 19 20 -f 25 26 27 -f 25 27 28 -f 24 25 28 -f 23 24 29 -f 29 24 28 -f 23 29 30 -f 21 22 23 -f 21 23 30 -f 21 30 31 -f 21 31 32 -f 33 34 43 -f 43 34 35 -f 43 35 42 -f 42 35 36 -f 37 38 39 -f 37 39 40 -f 33 43 44 -f 41 42 36 -f 41 36 37 -f 41 37 40 -f 45 46 49 -f 49 46 47 -f 49 47 48 -f 45 49 50 -f 45 50 53 -f 53 50 51 -f 53 51 52 -f 45 53 54 -f 45 54 55 -f 45 55 56 -f 61 62 63 -f 61 63 60 -f 60 63 64 -f 59 60 64 -f 58 59 64 -f 58 64 65 -f 57 58 65 -f 57 65 66 -f 57 66 67 -f 57 67 68 -f 69 71 70 -f 69 72 71 -f 73 75 74 -f 73 76 75 -f 69 79 72 -f 79 73 72 -f 79 78 73 -f 78 76 73 -f 69 80 79 -f 77 76 78 -f 83 85 84 -f 83 86 85 -f 82 86 83 -f 81 87 82 -f 87 86 82 -f 81 88 87 -f 93 95 94 -f 93 96 95 -f 92 96 93 -f 91 97 92 -f 97 96 92 -f 91 98 97 -f 89 91 90 -f 89 98 91 -f 89 99 98 -f 89 100 99 -f 101 111 102 -f 111 103 102 -f 111 110 103 -f 110 104 103 -f 105 107 106 -f 105 108 107 -f 101 112 111 -f 109 104 110 -f 109 105 104 -f 109 108 105 -f 113 117 114 -f 117 115 114 -f 117 116 115 -f 113 118 117 -f 113 121 118 -f 121 119 118 -f 121 120 119 -f 113 122 121 -f 113 123 122 -f 113 124 123 -f 129 131 130 -f 129 128 131 -f 128 132 131 -f 127 132 128 -f 126 132 127 -f 126 133 132 -f 125 133 126 -f 125 134 133 -f 125 135 134 -f 125 136 135 -f 61 60 128 129 -f 37 36 104 105 -f 41 40 108 109 -f 23 22 90 91 -f 17 16 84 85 -f 46 45 113 114 -f 64 63 131 132 -f 18 17 85 86 -f 60 59 127 128 -f 31 30 98 99 -f 15 14 82 83 -f 2 1 69 70 -f 21 32 100 89 -f 59 58 126 127 -f 47 46 114 115 -f 32 31 99 100 -f 14 13 81 82 -f 3 2 70 71 -f 49 48 116 117 -f 34 33 101 102 -f 48 47 115 116 -f 4 3 71 72 -f 65 64 132 133 -f 19 18 86 87 -f 6 5 73 74 -f 58 57 125 126 -f 50 49 117 118 -f 13 20 88 81 -f 35 34 102 103 -f 20 19 87 88 -f 7 6 74 75 -f 43 42 110 111 -f 26 25 93 94 -f 66 65 133 134 -f 8 7 75 76 -f 51 50 118 119 -f 27 26 94 95 -f 67 66 134 135 -f 53 52 120 121 -f 36 35 103 104 -f 57 68 136 125 -f 52 51 119 120 -f 38 37 105 106 -f 28 27 95 96 -f 68 67 135 136 -f 25 24 92 93 -f 5 4 72 73 -f 54 53 121 122 -f 39 38 106 107 -f 11 10 78 79 -f 24 23 91 92 -f 55 54 122 123 -f 40 39 107 108 -f 45 56 124 113 -f 33 44 112 101 -f 1 12 80 69 -f 56 55 123 124 -f 44 43 111 112 -f 29 28 96 97 -f 12 11 79 80 -f 62 61 129 130 -f 42 41 109 110 -f 10 9 77 78 -f 30 29 97 98 -f 9 8 76 77 -f 63 62 130 131 -f 22 21 89 90 -f 16 15 83 84 +f 61/1/1 62/2/1 63/3/1 +f 61/1/1 63/3/1 60/4/1 +f 60/4/1 63/3/1 64/5/1 +f 59/6/1 60/4/1 64/5/1 +f 58/7/1 59/6/1 64/5/1 +f 58/7/1 64/5/1 65/8/1 +f 57/9/1 58/7/1 65/8/1 +f 57/9/1 65/8/1 66/10/1 +f 57/9/1 66/10/1 67/11/1 +f 57/9/1 67/11/1 68/12/1 +f 129/13/2 131/14/2 130/15/2 +f 129/13/2 128/16/2 131/14/2 +f 128/16/2 132/17/2 131/14/2 +f 127/18/2 132/17/2 128/16/2 +f 126/19/2 132/17/2 127/18/2 +f 126/19/2 133/20/2 132/17/2 +f 125/21/2 133/20/2 126/19/2 +f 125/21/2 134/22/2 133/20/2 +f 125/21/2 135/23/2 134/22/2 +f 125/21/2 136/24/2 135/23/2 +f 61/1/3 60/4/3 128/16/3 129/13/3 +f 64/5/4 63/3/4 131/14/4 132/17/4 +f 60/4/5 59/6/5 127/18/5 128/16/5 +f 59/6/6 58/7/6 126/19/6 127/18/6 +f 65/8/7 64/5/7 132/17/7 133/20/7 +f 58/7/5 57/9/5 125/21/5 126/19/5 +f 66/10/4 65/8/4 133/20/4 134/22/4 +f 67/11/8 66/10/8 134/22/8 135/23/8 +f 57/9/3 68/12/3 136/24/3 125/21/3 +f 68/12/4 67/11/4 135/23/4 136/24/4 +f 62/2/5 61/1/5 129/13/5 130/15/5 +f 63/3/8 62/2/8 130/15/8 131/14/8 +usemtl Orange +f 45/25/1 46/26/1 49/27/1 +f 49/27/1 46/26/1 47/28/1 +f 49/27/1 47/28/1 48/29/1 +f 45/25/1 49/27/1 50/30/1 +f 45/25/1 50/30/1 53/31/1 +f 53/31/1 50/30/1 51/32/1 +f 53/31/1 51/32/1 52/33/1 +f 45/25/1 53/31/1 54/34/1 +f 45/25/1 54/34/1 55/35/1 +f 45/25/1 55/35/1 56/36/1 +f 113/37/2 117/38/2 114/39/2 +f 117/38/2 115/40/2 114/39/2 +f 117/38/2 116/41/2 115/40/2 +f 113/37/2 118/42/2 117/38/2 +f 113/37/2 121/43/2 118/42/2 +f 121/43/2 119/44/2 118/42/2 +f 121/43/2 120/45/2 119/44/2 +f 113/37/2 122/46/2 121/43/2 +f 113/37/2 123/47/2 122/46/2 +f 113/37/2 124/48/2 123/47/2 +f 46/26/5 45/25/5 113/37/5 114/39/5 +f 47/28/8 46/26/8 114/39/8 115/40/8 +f 49/27/3 48/29/3 116/41/3 117/38/3 +f 48/29/4 47/28/4 115/40/4 116/41/4 +f 50/30/4 49/27/4 117/38/4 118/42/4 +f 51/32/8 50/30/8 118/42/8 119/44/8 +f 53/31/3 52/33/3 120/45/3 121/43/3 +f 52/33/4 51/32/4 119/44/4 120/45/4 +f 54/34/4 53/31/4 121/43/4 122/46/4 +f 55/35/8 54/34/8 122/46/8 123/47/8 +f 45/25/3 56/36/3 124/48/3 113/37/3 +f 56/36/4 55/35/4 123/47/4 124/48/4 +usemtl Yellow +f 33/49/1 34/50/1 43/51/1 +f 43/51/1 34/50/1 35/52/1 +f 43/51/1 35/52/1 42/53/1 +f 42/53/1 35/52/1 36/54/1 +f 37/55/1 38/56/1 39/57/1 +f 37/55/1 39/57/1 40/58/1 +f 33/49/1 43/51/1 44/59/1 +f 41/60/1 42/53/1 36/54/1 +f 41/60/1 36/54/1 37/55/1 +f 41/60/1 37/55/1 40/58/1 +f 101/61/2 111/62/2 102/63/2 +f 111/62/2 103/64/2 102/63/2 +f 111/62/2 110/65/2 103/64/2 +f 110/65/2 104/66/2 103/64/2 +f 105/67/2 107/68/2 106/69/2 +f 105/67/2 108/70/2 107/68/2 +f 101/61/2 112/71/2 111/62/2 +f 109/72/2 104/66/2 110/65/2 +f 109/72/2 105/67/2 104/66/2 +f 109/72/2 108/70/2 105/67/2 +f 37/55/8 36/54/8 104/66/8 105/67/8 +f 41/60/3 40/58/3 108/70/3 109/72/3 +f 34/50/5 33/49/5 101/61/5 102/63/5 +f 35/52/8 34/50/8 102/63/8 103/64/8 +f 43/51/3 42/53/3 110/65/3 111/62/3 +f 36/54/9 35/52/9 103/64/9 104/66/9 +f 38/56/5 37/55/5 105/67/5 106/69/5 +f 39/57/8 38/56/8 106/69/8 107/68/8 +f 40/58/4 39/57/4 107/68/4 108/70/4 +f 33/49/3 44/59/3 112/71/3 101/61/3 +f 44/59/4 43/51/4 111/62/4 112/71/4 +f 42/53/10 41/60/10 109/72/10 110/65/10 +usemtl Green +f 25/73/1 26/74/1 27/75/1 +f 25/73/1 27/75/1 28/76/1 +f 24/77/1 25/73/1 28/76/1 +f 23/78/1 24/77/1 29/79/1 +f 29/79/2 24/77/2 28/76/2 +f 23/78/1 29/79/1 30/80/1 +f 21/81/1 22/82/1 23/78/1 +f 21/81/1 23/78/1 30/80/1 +f 21/81/1 30/80/1 31/83/1 +f 21/81/1 31/83/1 32/84/1 +f 93/85/2 95/86/2 94/87/2 +f 93/85/2 96/88/2 95/86/2 +f 92/89/2 96/88/2 93/85/2 +f 91/90/2 97/91/2 92/89/2 +f 97/91/2 96/88/2 92/89/2 +f 91/90/2 98/92/2 97/91/2 +f 89/93/2 91/90/2 90/94/2 +f 89/93/2 98/92/2 91/90/2 +f 89/93/2 99/95/2 98/92/2 +f 89/93/2 100/96/2 99/95/2 +f 23/78/8 22/82/8 90/94/8 91/90/8 +f 31/83/8 30/80/8 98/92/8 99/95/8 +f 21/81/3 32/84/3 100/96/3 89/93/3 +f 32/84/4 31/83/4 99/95/4 100/96/4 +f 26/74/5 25/73/5 93/85/5 94/87/5 +f 27/75/8 26/74/8 94/87/8 95/86/8 +f 28/76/4 27/75/4 95/86/4 96/88/4 +f 25/73/3 24/77/3 92/89/3 93/85/3 +f 24/77/5 23/78/5 91/90/5 92/89/5 +f 29/79/3 28/76/3 96/88/3 97/91/3 +f 30/80/4 29/79/4 97/91/4 98/92/4 +f 22/82/5 21/81/5 89/93/5 90/94/5 +usemtl Blue +f 15/97/1 16/98/1 17/99/1 +f 15/97/1 17/99/1 18/100/1 +f 14/101/2 15/97/2 18/100/2 +f 13/102/1 14/101/1 19/103/1 +f 19/103/2 14/101/2 18/100/2 +f 13/102/1 19/103/1 20/104/1 +f 83/105/2 85/106/2 84/107/2 +f 83/105/2 86/108/2 85/106/2 +f 82/109/2 86/108/2 83/105/2 +f 81/110/2 87/111/2 82/109/2 +f 87/111/1 86/108/1 82/109/1 +f 81/110/2 88/112/2 87/111/2 +f 17/99/8 16/98/8 84/107/8 85/106/8 +f 18/100/4 17/99/4 85/106/4 86/108/4 +f 15/97/3 14/101/3 82/109/3 83/105/3 +f 14/101/5 13/102/5 81/110/5 82/109/5 +f 19/103/3 18/100/3 86/108/3 87/111/3 +f 13/102/3 20/104/3 88/112/3 81/110/3 +f 20/104/4 19/103/4 87/111/4 88/112/4 +f 16/98/5 15/97/5 83/105/5 84/107/5 +usemtl Indigo +f 1/113/1 2/114/1 3/115/1 +f 1/113/1 3/115/1 4/116/1 +f 1/113/1 4/116/1 11/117/1 +f 11/117/1 4/116/1 5/118/1 +f 1/113/1 11/117/1 12/119/1 +f 69/120/2 71/121/2 70/122/2 +f 69/120/2 72/123/2 71/121/2 +f 69/120/2 79/124/2 72/123/2 +f 79/124/2 73/125/2 72/123/2 +f 69/120/2 80/126/2 79/124/2 +f 2/114/5 1/113/5 69/120/5 70/122/5 +f 3/115/8 2/114/8 70/122/8 71/121/8 +f 4/116/4 3/115/4 71/121/4 72/123/4 +f 5/118/8 4/116/8 72/123/8 73/125/8 +f 1/113/3 12/119/3 80/126/3 69/120/3 +f 12/119/4 11/117/4 79/124/4 80/126/4 +usemtl Violet +f 5/118/1 6/127/1 7/128/1 +f 5/118/1 7/128/1 8/129/1 +f 11/117/1 5/118/1 10/130/1 +f 10/130/1 5/118/1 8/129/1 +f 9/131/1 10/130/1 8/129/1 +f 73/125/2 75/132/2 74/133/2 +f 73/125/2 76/134/2 75/132/2 +f 79/124/2 78/135/2 73/125/2 +f 78/135/2 76/134/2 73/125/2 +f 77/136/2 76/134/2 78/135/2 +f 6/127/5 5/118/5 73/125/5 74/133/5 +f 7/128/8 6/127/8 74/133/8 75/132/8 +f 8/129/4 7/128/4 75/132/4 76/134/4 +f 11/117/3 10/130/3 78/135/3 79/124/3 +f 10/130/5 9/131/5 77/136/5 78/135/5 +f 9/131/3 8/129/3 76/134/3 77/136/3