diff --git a/example/book.odin b/example/book.odin index 7bb8e69..c44d35b 100644 --- a/example/book.odin +++ b/example/book.odin @@ -2,7 +2,6 @@ package example import glm "core:math/linalg/glsl" -import "core:strings" import "core:slice" import gl "../wasm/webgl" import "../obj" @@ -22,17 +21,12 @@ setup_book :: proc(s: ^State_Book, program: gl.Program) { gl.Enable(gl.CULL_FACE) // don't draw back faces gl.Enable(gl.DEPTH_TEST) // draw only closest faces - data := obj.data_make(context.temp_allocator) - obj_file := #load("./public/book.obj", string) - - for line in strings.split_lines_iterator(&obj_file) { - obj.parse_line(&data, line) - } + objects := obj.parse_file(#load("./public/book.obj", string), context.temp_allocator) s.vao = gl.CreateVertexArray() - object := &data.objects[0] + object := &objects[0] s.positions = slice.clone(object.vertices.position[:len(object.vertices)]) extent_min, extent_max := get_extents(s.positions) diff --git a/example/chair.odin b/example/chair.odin index 173042e..6dd8983 100644 --- a/example/chair.odin +++ b/example/chair.odin @@ -2,7 +2,6 @@ package example import glm "core:math/linalg/glsl" -import "core:strings" import "core:slice" import gl "../wasm/webgl" import "../obj" @@ -10,10 +9,10 @@ import "../obj" @private State_Chair :: struct { rotation: mat4, - objects: []Object, + shapes: []Shape, } -Object :: struct { +Shape :: struct { using locations: Input_Locations_Boxes, vao : VAO, positions: []vec3, @@ -28,38 +27,34 @@ setup_chair :: proc(s: ^State_Chair, program: gl.Program) { gl.Enable(gl.CULL_FACE) // don't draw back faces gl.Enable(gl.DEPTH_TEST) // draw only closest faces - data := obj.data_make(context.temp_allocator) - it := chair_obj_bytes - for line in strings.split_lines_iterator(&it) { - obj.parse_line(&data, line) - } + objects := obj.parse_file(#load("./public/chair.obj", string), context.temp_allocator) - extent_min, extent_max := get_extents(data.positions[:]) + extent_min, extent_max := get_extents(objects[0].vertices.position[:len(objects[0].vertices)]) + for o in objects[1:] { + extend_extents(&extent_min, &extent_max, o.vertices.position[:len(o.vertices)]) + } - objects: [dynamic]Object + s.shapes = make([]Shape, len(objects)) - for object in data.objects { - append(&objects, Object{}) - o := last(&objects) + for &shape, i in s.shapes { + o := objects[i] - o.vao = gl.CreateVertexArray() + shape.vao = gl.CreateVertexArray() - o.positions = slice.clone(object.vertices.position[:len(object.vertices)]) - correct_extents(o.positions, extent_min, extent_max, -200, 200) + shape.positions = slice.clone(o.vertices.position[:len(o.vertices)]) + correct_extents(shape.positions, extent_min, extent_max, -200, 200) - o.colors = make([]rgba, len(o.positions)) - slice.fill(o.colors, rand_color()) + shape.colors = make([]rgba, len(shape.positions)) + slice.fill(shape.colors, rand_color()) - gl.BindVertexArray(o.vao) + gl.BindVertexArray(shape.vao) - input_locations_boxes(&o.locations, program) + input_locations_boxes(&shape.locations, program) - attribute(o.a_position, gl.CreateBuffer(), o.positions) - attribute(o.a_color , gl.CreateBuffer(), o.colors) + attribute(shape.a_position, gl.CreateBuffer(), shape.positions) + attribute(shape.a_color, gl.CreateBuffer(), shape.colors) } - s.objects = objects[:] - /* Init rotation */ s.rotation = 1 } @@ -86,7 +81,7 @@ frame_chair :: proc(s: ^State_Chair, delta: f32) { - for &o in s.objects { + for &o in s.shapes { gl.BindVertexArray(o.vao) diff --git a/example/setup.odin b/example/setup.odin index 9034fbd..0d14075 100644 --- a/example/setup.odin +++ b/example/setup.odin @@ -1,6 +1,5 @@ package example -import "core:mem" import "core:math/rand" import "core:crypto" @@ -167,17 +166,11 @@ demo_state: struct #raw_union { book: State_Book, } - -temp_arena_buffer: [mem.Megabyte]byte -temp_arena: mem.Arena = {data = temp_arena_buffer[:]} -temp_arena_allocator := mem.arena_allocator(&temp_arena) - @export start :: proc (example_kind: Example_Kind) -> (ok: bool) { example = example_kind demo := demos[example] - context.temp_allocator = temp_arena_allocator defer free_all(context.temp_allocator) program: gl.Program @@ -231,7 +224,7 @@ start :: proc (example_kind: Example_Kind) -> (ok: bool) { @export frame :: proc (delta: f32) { - context.temp_allocator = temp_arena_allocator + defer free_all(context.temp_allocator) defer mouse_down_frame = false diff --git a/example/suzanne.odin b/example/suzanne.odin index 453c502..a84ad3d 100644 --- a/example/suzanne.odin +++ b/example/suzanne.odin @@ -2,8 +2,8 @@ package example import glm "core:math/linalg/glsl" -import "core:strings" import "core:slice" +import "core:fmt" import gl "../wasm/webgl" import "../obj" @@ -15,18 +15,14 @@ State_Suzanne :: struct { vertices: Vertices, } -suzanne_obj_bytes := #load("./public/suzanne.obj", string) - @private setup_suzanne :: proc(s: ^State_Suzanne, program: gl.Program) { - data := obj.data_make(context.temp_allocator) - it := suzanne_obj_bytes - for line in strings.split_lines_iterator(&it) { - obj.parse_line(&data, line) + objects, parse_err := obj.parse_file(#load("./public/suzanne.obj", string), context.temp_allocator) + if parse_err != nil { + fmt.eprintf("Parse error: %v", parse_err) } - - object := &data.objects[0] + object := &objects[0] vertices := make(Vertices, len(object.vertices), context.temp_allocator) diff --git a/example/utils.odin b/example/utils.odin index d881293..ade3906 100644 --- a/example/utils.odin +++ b/example/utils.odin @@ -392,7 +392,7 @@ normals_from_positions :: proc (dst, src: []vec3) { } } -get_extents :: proc (positions: []$T) -> (v_min, v_max: T) { +get_extents :: proc (positions: []vec3) -> (v_min, v_max: vec3) { if len(positions) == 0 { return @@ -413,6 +413,13 @@ get_extents :: proc (positions: []$T) -> (v_min, v_max: T) { return } +extend_extents :: proc (v_min, v_max: ^vec3, positions: []vec3) { + a_min, a_max := get_extents(positions) + b_min, b_max := get_extents({v_min^, v_max^, a_min, a_max}) + v_min ^= b_min + v_max ^= b_max +} + correct_extents :: proc ( positions: []vec3, in_min: vec3, in_max: vec3, diff --git a/obj/parser.odin b/obj/parser.odin index 3757222..574adc4 100644 --- a/obj/parser.odin +++ b/obj/parser.odin @@ -6,6 +6,9 @@ https://github.com/thisistherk/fast_obj package obj +import "base:runtime" +import "core:strings" + vec3 :: [3]f32 vec2 :: [2]f32 @@ -92,23 +95,6 @@ Data :: struct { // } // } -init_data :: proc (data: ^Data, allocator := context.allocator) { - - // indices are base-1, add zero index to be able to index it normally - data.positions = make([dynamic]vec3, 1, 32, allocator) - data.texcoords = make([dynamic]vec2, 1, 32, allocator) - data.normals = make([dynamic]vec3, 1, 32, allocator) - data.colors = make([dynamic]vec3, 0, 32, allocator) - data.objects = make([dynamic]Object, 1, 4, allocator) - data.objects[0] = object_make(data) -} -data_init :: init_data - -data_make :: proc (allocator := context.allocator) -> (data: Data) { - init_data(&data) - return -} - object_make :: proc (data: ^Data) -> (g: Object) { g.vertices = make(#soa[dynamic]Vertex, 0, 32, data.objects.allocator) return @@ -264,32 +250,38 @@ parse_vec2 :: proc(ptr: ^[^]byte) -> vec2 { return {parse_float(ptr), parse_float(ptr)} } -parse_vertex :: proc(data: ^Data, ptr: ^[^]byte) { +parse_vertex :: proc(data: ^Data, ptr: ^[^]byte) -> (err: runtime.Allocator_Error) { - append(&data.positions, parse_vec3(ptr)) + append(&data.positions, parse_vec3(ptr)) or_return skip_whitespace(ptr) if is_newline(ptr[0]) do return /* Fill the colors array until it matches the size of the positions array */ for _ in len(data.colors) ..< len(data.positions)-1 { - append(&data.colors, vec3{1, 1, 1}) + append(&data.colors, vec3{1, 1, 1}) or_return } - append(&data.colors, parse_vec3(ptr)) + append(&data.colors, parse_vec3(ptr)) or_return + + return } -parse_texcoord :: proc (data: ^Data, ptr: ^[^]byte) { +parse_texcoord :: proc (data: ^Data, ptr: ^[^]byte) -> (err: runtime.Allocator_Error) { - append(&data.texcoords, parse_vec2(ptr)) + append(&data.texcoords, parse_vec2(ptr)) or_return + + return } -parse_normal :: proc (data: ^Data, ptr: ^[^]byte) { +parse_normal :: proc (data: ^Data, ptr: ^[^]byte) -> (err: runtime.Allocator_Error) { + + append(&data.normals, parse_vec3(ptr)) or_return - append(&data.normals, parse_vec3(ptr)) + return } -parse_face :: proc (data: ^Data, ptr: ^[^]byte) { +parse_face :: proc (data: ^Data, ptr: ^[^]byte) -> (err: runtime.Allocator_Error) { g := object_last(data) @@ -315,33 +307,37 @@ parse_face :: proc (data: ^Data, ptr: ^[^]byte) { skip_whitespace(ptr) if is_newline(ptr[0]) do break - { - indices[0], indices[1] = indices[1], indices[2] - idx := &indices[2] - - idx.position = parse_int(ptr) - - if ptr[0] == '/' { - move(ptr) - - if ptr[0] != '/' { - idx.texcoord = parse_int(ptr) - } - - if (ptr[0] == '/') { - move(ptr) - idx.normal = parse_int(ptr) - } + index: Index + + index.position = parse_int(ptr) + + if ptr[0] == '/' { + move(ptr) + + if ptr[0] != '/' { + index.texcoord = parse_int(ptr) } - - if idx.position == 0 { - return /* Skip lines with no valid vertex idx */ + + if (ptr[0] == '/') { + move(ptr) + index.normal = parse_int(ptr) } - if idx.position < 0 do idx.position += len(data.positions) - if idx.texcoord < 0 do idx.texcoord += len(data.texcoords) - if idx.normal < 0 do idx.normal += len(data.normals) } + if index.position == 0 { + return /* Skip lines with no valid vertex idx */ + } + if index.position < 0 do index.position += len(data.positions) + if index.texcoord < 0 do index.texcoord += len(data.texcoords) + if index.normal < 0 do index.normal += len(data.normals) + + if i == 0 { + indices[0] = index + } else { + indices[1] = indices[2] + indices[2] = index + } + if i >= 2 { vertices: [3]Vertex for &v, i in vertices { @@ -350,7 +346,7 @@ parse_face :: proc (data: ^Data, ptr: ^[^]byte) { // colors array is only filled if the colors data is in the file v.color = data.colors[idx.position] if idx.position < len(data.colors) else 1 } - append(&g.vertices, ..vertices[:]) + append(&g.vertices, ..vertices[:]) or_return } } @@ -361,6 +357,7 @@ parse_face :: proc (data: ^Data, ptr: ^[^]byte) { // data.group.face_count += 1 // data.object.face_count += 1 + return } parse_object :: proc (data: ^Data, ptr: ^[^]byte) @@ -392,7 +389,7 @@ parse_usemtl :: proc (data: ^Data, ptr: ^[^]byte) { g.material = parse_name(ptr) } -parse_line :: proc (data: ^Data, str: string) +parse_line :: proc (data: ^Data, str: string) -> (err: runtime.Allocator_Error) { ptr := raw_data(str) @@ -466,4 +463,33 @@ parse_line :: proc (data: ^Data, str: string) parse_usemtl(data, &ptr) } } + + return +} + +parse_file :: proc ( + src: string, + allocator := context.allocator, +) -> ( + objects: []Object, + err: runtime.Allocator_Error, +) #optional_allocator_error { + + data: Data + + // indices are base-1, add zero index to be able to index it normally + data.positions = make([dynamic]vec3, 1, 1024, context.temp_allocator) + data.colors = make([dynamic]vec3, 1, 1024, context.temp_allocator) + data.texcoords = make([dynamic]vec2, 1, 1024, context.temp_allocator) + data.normals = make([dynamic]vec3, 1, 1024, context.temp_allocator) + + data.objects = make([dynamic]Object, 1, 4, allocator) + data.objects[0] = object_make(&data) + + it := src + for line in strings.split_lines_iterator(&it) { + parse_line(&data, line) or_return + } + + return data.objects[:], nil }