karts/model/obj.odin
2025-02-16 11:28:18 +13:00

182 lines
5.2 KiB
Odin

package model
import "../image"
import "../texture"
import "../vertex"
import "core:fmt"
import glm "core:math/linalg/glsl"
import "core:os"
import "core:strconv"
import "core:strings"
Error :: union {
os.Error,
string,
}
Index_Range :: struct {
first: u32,
count: i32,
}
Material :: struct {
name: string,
texture: texture.Texture,
ranges: [dynamic]Index_Range,
// first_index: u32,
// index_count: i32,
}
load_mtl_string :: proc(text: string, texture_map: map[string]string) -> (materials: map[string]Material) {
lines := strings.split_lines(text)
default, _ := texture.load(image.default_image)
current_material: string
for line in lines {
parts := strings.split(line, " ")
switch parts[0] {
case "newmtl":
current_material = strings.join(parts[1:], " ")
materials[current_material] = Material {
name = current_material,
texture = default,
}
case "map_Kd":
filename := strings.join(parts[1:], " ")
img_data := texture_map[filename]
fmt.printfln("\"{}\" = #load(\"{}\")", filename, filename)
tex, err := texture.load(image.load_string(img_data))
mat := materials[current_material]
mat.texture = tex
materials[current_material] = mat
}
}
return
}
load_mtl :: proc(filename: string, texture_map: map[string]string) -> (materials: map[string]Material) {
bytes, _ := os.read_entire_file_or_err(filename)
lines := strings.split_lines(string(bytes))
default, _ := texture.load(image.default_image)
current_material: string
for line in lines {
parts := strings.split(line, " ")
switch parts[0] {
case "newmtl":
current_material = strings.join(parts[1:], " ")
materials[current_material] = Material {
name = current_material,
texture = default,
}
case "map_Kd":
filename := strings.join(parts[1:], " ")
// img_data := texture_map[filename]
// fmt.printfln("\"{}\" = #load(\"{}\")", filename, filename)
tex, err := texture.load(image.load(filename))
mat := materials[current_material]
mat.texture = tex
materials[current_material] = mat
}
}
return
}
load_obj_string :: proc(
text: string,
mtl: string,
textures_map: map[string]string,
) -> (
Maybe(Model),
Error,
) {
vertices: [dynamic]vertex.Vertex
texcoords: [dynamic]glm.vec2
indices: [dynamic]u32
materials: map[string]Material
current_material: string
current_range := 0
lines := strings.split_lines(text)
for line in lines {
parts := strings.split(line, " ")
switch parts[0] {
case "v":
vert: vertex.Vertex
vert.pos = glm.vec4(1)
for part, i in parts[1:] {
val, ok := strconv.parse_f32(part)
if !ok {
return nil, "Failed to parse f32"
}
vert.pos[i] = val
}
append(&vertices, vert)
case "vt":
texcoord: glm.vec2
for part, i in parts[1:] {
val, ok := strconv.parse_f32(part)
if !ok {
return nil, "Failed to parse f32"
}
texcoord[i] = val
}
append(&texcoords, texcoord)
case "f":
for part in parts[1:] {
slashes := strings.split(part, "/")
v, ok := strconv.parse_u64(slashes[0])
if !ok {
return nil, "Failed to parse u64"
}
v -= 1
if len(slashes) > 1 {
vt: u64
vt, ok = strconv.parse_u64(slashes[1])
if !ok {
return nil, "Failed to parse u64"
}
vt -= 1
vertices[v].texcoord = texcoords[vt]
}
append(&indices, u32(v))
if current_material != "" {
mtl := materials[current_material]
mtl.ranges[len(mtl.ranges) - 1].count += 1
materials[current_material] = mtl
}
}
case "mtllib":
filename := strings.join(parts[1:], " ")
mtl_materials := load_mtl_string(mtl, textures_map)
for material_name, material in mtl_materials {
materials[material_name] = material
}
case "usemtl":
current_material = strings.join(parts[1:], " ")
mtl := materials[current_material]
append(&mtl.ranges, Index_Range{first = u32(len(indices))})
materials[current_material] = mtl
}
}
model := create(vertices[:], indices[:], materials)
return model, nil
}
// load_obj :: proc(filename: string) -> (Maybe(Model), Error) {
// bytes, err := os.read_entire_file_or_err(filename)
// if err != nil {
// return nil, err
// }
//
// return load_obj_string(string(bytes))
// }