Skip to content

Instantly share code, notes, and snippets.

@vassvik
Last active October 17, 2022 03:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vassvik/bde472fa9daf436c620cf4b693aa93ab to your computer and use it in GitHub Desktop.
Save vassvik/bde472fa9daf436c620cf4b693aa93ab to your computer and use it in GitHub Desktop.
odin obj loader
package main
import "core:os";
import "core:fmt";
import "core:strconv";
// model data stuff
Model_Data :: struct {
vertices: [][3]f32,
indices: []i32,
}
free_model_data :: proc(using model_data: Model_Data) {
delete(vertices);
delete(indices);
}
// parsing stuff
stream: string;
is_whitespace :: inline proc(c: u8) -> bool {
switch c {
case ' ', '\t', '\n', '\v', '\f', '\r': return true;
}
return false;
}
skip_whitespace :: inline proc() #no_bounds_check {
for stream != "" && is_whitespace(stream[0]) do stream = stream[1:];
}
skip_line :: proc() #no_bounds_check {
N := len(stream);
for i in 0..N-1 {
if stream[0] == '\r' || stream[0] == '\n' {
skip_whitespace();
return;
}
stream = stream[1:];
}
}
next_word :: proc() -> string #no_bounds_check {
skip_whitespace();
N := len(stream);
for i in 0..N-1 {
if is_whitespace(stream[i]) || i == len(stream)-1 {
current_word := stream[0:i];
stream = stream[i:];
return current_word;
}
}
return "";
}
next_word_slash :: proc() -> string #no_bounds_check {
skip_whitespace();
if stream[0] == '/' do stream = stream[1:]; // i/j or i/j/k
if stream[0] == '/' do stream = stream[1:]; // i//k
N := len(stream);
for i in 0..N-1 {
if is_whitespace(stream[i]) || stream[i] == '/' || i == len(stream)-1 {
current_word := stream[0:i];
stream = stream[i:];
return current_word;
}
}
return "";
}
// loader, assumes a well formed file...
read_obj :: proc(filename: string) -> (Model_Data, bool) #no_bounds_check {
to_f32 :: strconv.parse_f32;
to_i32 :: proc(str: string) -> i32 do return cast(i32)strconv.parse_int(str);
data, status := os.read_entire_file(filename);
if !status do return Model_Data{}, false;
defer delete(data);
vertices: [dynamic][3]f32;
indices: [dynamic]i32;
first_face := true;
face_type: int;
stream = string(data);
for stream != "" {
current_word := next_word();
switch current_word {
case "v":
v := append(&vertices, [3]f32{to_f32(next_word()), to_f32(next_word()), to_f32(next_word())});
case "f":
if first_face {
old_stream := stream;
num_slashes := 0;
slashes: [2]int;
word := next_word();
for c, i in word {
if c == '/' {
slashes[num_slashes] = i;
num_slashes += 1;
}
}
if num_slashes == 0 do face_type = 0;
if num_slashes == 1 do face_type = 1;
if num_slashes == 2 do face_type = slashes[1] == slashes[0]+1 ? 2 : 3;
type_desc := [?]string{"i", "i/j", "i//k", "i/j/k"};
fmt.printf("Identified face formatting of type '%s'\n", type_desc[face_type]);
// reset stream to start
stream = old_stream;
first_face = false;
}
switch (face_type) {
case 0: // i: 0
append(&indices, to_i32(next_word())-1, to_i32(next_word())-1, to_i32(next_word())-1);
case 1: // i/j: 1
case 2: // i//k: 2
append(&indices, to_i32(next_word_slash())-1); next_word_slash();
append(&indices, to_i32(next_word_slash())-1); next_word_slash();
append(&indices, to_i32(next_word_slash())-1); next_word_slash();
case 3: // i/j/k: 3
append(&indices, to_i32(next_word_slash())-1); next_word_slash(); next_word_slash();
append(&indices, to_i32(next_word_slash())-1); next_word_slash(); next_word_slash();
append(&indices, to_i32(next_word_slash())-1); next_word_slash(); next_word_slash();
case:
fmt.printf("ERROR: should not have reached here. current_word = %v\n", current_word);
}
}
skip_line();
}
return Model_Data{vertices[:], indices[:]}, true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment