gutter_runner/game/assets/assets.odin

182 lines
3.8 KiB
Odin

package assets
import "core:c"
import "core:log"
import "core:math"
import lg "core:math/linalg"
import "game:physics/bvh"
import "libs:tracy"
import rl "vendor:raylib"
Loaded_Texture :: struct {
texture: rl.Texture2D,
modtime: c.long,
}
Loaded_Model :: struct {
model: rl.Model,
modtime: c.long,
}
Loaded_BVH :: struct {
// AABB of all bvhs
aabb: bvh.AABB,
// BVH for each mesh in a model
bvhs: []bvh.BVH,
modtime: c.long,
}
destroy_loaded_bvh :: proc(loaded_bvh: Loaded_BVH) {
tracy.Zone()
for &mesh_bvh in loaded_bvh.bvhs {
bvh.destroy_bvh(&mesh_bvh)
}
delete(loaded_bvh.bvhs)
}
Asset_Manager :: struct {
textures: map[cstring]Loaded_Texture,
models: map[cstring]Loaded_Model,
bvhs: map[cstring]Loaded_BVH,
}
get_texture :: proc(assetman: ^Asset_Manager, path: cstring) -> rl.Texture2D {
tracy.Zone()
modtime := rl.GetFileModTime(path)
existing, ok := assetman.textures[path]
if ok && existing.modtime == modtime {
return existing.texture
}
if ok {
rl.UnloadTexture(existing.texture)
delete_key(&assetman.textures, path)
log.infof("deleted texture %s. New textures len: %d", path, len(assetman.textures))
}
loaded := rl.LoadTexture(path)
if rl.IsTextureValid(loaded) {
assetman.textures[path] = {
texture = loaded,
modtime = modtime,
}
return loaded
} else {
return rl.Texture2D{}
}
}
get_model_ex :: proc(
assetman: ^Asset_Manager,
path: cstring,
ref_modtime: c.long = 0, // will check reload status using reference load time. When 0 reloaded will be true only if this call triggered reload
) -> (
model: rl.Model,
modtime: c.long,
reloaded: bool,
) {
tracy.Zone()
new_modtime := rl.GetFileModTime(path)
existing, ok := assetman.models[path]
if ok && existing.modtime == new_modtime {
return existing.model,
existing.modtime,
ref_modtime == 0 ? false : existing.modtime != ref_modtime
}
if ok {
rl.UnloadModel(existing.model)
delete_key(&assetman.textures, path)
log.infof("deleted model %s. New models len: %d", path, len(assetman.textures))
}
loaded := rl.LoadModel(path)
if rl.IsModelValid(loaded) {
assetman.models[path] = {
model = loaded,
modtime = new_modtime,
}
return loaded, new_modtime, true
} else {
return rl.Model{}, 0, true
}
}
get_model :: proc(assetman: ^Asset_Manager, path: cstring) -> rl.Model {
model, _, _ := get_model_ex(assetman, path)
return model
}
null_bvhs: []bvh.BVH
get_bvh :: proc(assetman: ^Asset_Manager, path: cstring) -> Loaded_BVH {
tracy.Zone()
loaded_bvh, ok := assetman.bvhs[path]
model, modtime, reloaded := get_model_ex(assetman, path, loaded_bvh.modtime)
should_recreate := reloaded || !ok
if ok && should_recreate {
destroy_loaded_bvh(loaded_bvh)
delete_key(&assetman.bvhs, path)
}
if should_recreate {
new_bvhs := make([]bvh.BVH, model.meshCount)
outer_aabb := bvh.AABB {
min = math.F32_MAX,
max = -math.F32_MAX,
}
for i in 0 ..< model.meshCount {
mesh := model.meshes[i]
vertices := (cast([^]rl.Vector3)mesh.vertices)[:mesh.vertexCount]
indices := mesh.indices[:mesh.triangleCount * 3]
mesh_bvh := bvh.build_bvh_from_mesh(
{vertices = vertices, indices = indices},
context.allocator,
)
root_aabb := mesh_bvh.bvh.nodes[0].aabb
outer_aabb.min = lg.min(outer_aabb.min, root_aabb.min)
outer_aabb.max = lg.max(outer_aabb.max, root_aabb.max)
new_bvhs[i] = mesh_bvh.bvh
}
assetman.bvhs[path] = Loaded_BVH {
aabb = outer_aabb,
bvhs = new_bvhs,
modtime = modtime,
}
}
return assetman.bvhs[path]
}
shutdown :: proc(assetman: ^Asset_Manager) {
tracy.Zone()
for _, texture in assetman.textures {
rl.UnloadTexture(texture.texture)
}
for _, model in assetman.models {
rl.UnloadModel(model.model)
}
for _, loaded_bvh in assetman.bvhs {
destroy_loaded_bvh(loaded_bvh)
}
delete(assetman.textures)
delete(assetman.models)
delete(assetman.bvhs)
}