806 lines
18 KiB
Odin
806 lines
18 KiB
Odin
package assets
|
|
|
|
import "common:encoding/sexp"
|
|
import "common:name"
|
|
import "core:log"
|
|
import "core:math"
|
|
import lg "core:math/linalg"
|
|
import "game:physics/bvh"
|
|
import "game:physics/collision"
|
|
import "libs:physfs"
|
|
import rl "libs:raylib"
|
|
import "libs:tracy"
|
|
|
|
|
|
_ :: math
|
|
|
|
DEV_BUILD :: #config(DEV, false)
|
|
|
|
Loaded_BVH :: struct {
|
|
// AABB of all bvhs
|
|
aabb: bvh.AABB,
|
|
// BVH for each mesh in a model
|
|
bvh: bvh.BVH,
|
|
vertices: []rl.Vector3,
|
|
indices: []u16,
|
|
}
|
|
|
|
Loaded_Convex :: struct {
|
|
mesh: collision.Convex,
|
|
center_of_mass: rl.Vector3,
|
|
inertia_tensor: lg.Matrix3f32,
|
|
total_volume: f32,
|
|
}
|
|
|
|
destroy_loaded_bvh :: proc(loaded_bvh: ^Loaded_BVH) {
|
|
tracy.Zone()
|
|
|
|
bvh.destroy_bvh(&loaded_bvh.bvh)
|
|
delete(loaded_bvh.vertices)
|
|
delete(loaded_bvh.indices)
|
|
}
|
|
|
|
Curve_2D :: [][2]f32
|
|
|
|
Scene_Instance :: struct {
|
|
model: name.Name,
|
|
// TODO: common format definitions
|
|
pos: rl.Vector3,
|
|
rot: rl.Quaternion,
|
|
scale: rl.Vector3,
|
|
}
|
|
|
|
Scene_Desc :: struct {
|
|
instances: []Scene_Instance,
|
|
}
|
|
|
|
Asset_Type :: enum {
|
|
Texture,
|
|
Model,
|
|
Shader,
|
|
Sound,
|
|
Music,
|
|
Curve_1D,
|
|
Curve_2D,
|
|
Scene,
|
|
BVH,
|
|
Convex,
|
|
}
|
|
|
|
Curve_1D :: []f32
|
|
|
|
Asset :: union {
|
|
rl.Texture2D,
|
|
rl.Model,
|
|
Loaded_Shader,
|
|
rl.Sound,
|
|
rl.Music,
|
|
Curve_1D,
|
|
Curve_2D,
|
|
Scene_Desc,
|
|
Loaded_BVH,
|
|
Loaded_Convex,
|
|
}
|
|
|
|
Asset_Key :: struct {
|
|
path: name.Name,
|
|
type: Asset_Type,
|
|
}
|
|
|
|
Asset_Entry :: struct {
|
|
asset: Asset,
|
|
modtime: physfs.sint64,
|
|
|
|
// Current modtime on file system, used to detect when asset changes
|
|
fs_modtime: physfs.sint64,
|
|
}
|
|
|
|
Asset_Manager :: struct {
|
|
assets: map[Asset_Key]Asset_Entry,
|
|
watcher: Asset_Modtime_Watcher,
|
|
}
|
|
|
|
Asset_Cache_Entry :: struct($E: typeid) {
|
|
value: E,
|
|
modtime: i64,
|
|
fs_modtime: i64,
|
|
}
|
|
|
|
Asset_Cache_Loader_Payload :: union {
|
|
cstring,
|
|
}
|
|
|
|
Asset_Cache_Loader :: struct {
|
|
load: proc(
|
|
assetman: ^Asset_Manager,
|
|
path: cstring,
|
|
payload: Asset_Cache_Loader_Payload,
|
|
) -> (
|
|
Asset,
|
|
bool,
|
|
),
|
|
unload: proc(value: Asset),
|
|
should_reload: proc(assetman: ^Asset_Manager, key: Asset_Key, entry: Asset_Entry) -> bool,
|
|
}
|
|
|
|
Asset_Cache :: struct($E: typeid) {
|
|
cache: map[cstring]Asset_Cache_Entry(E),
|
|
loader: Asset_Cache_Loader(E),
|
|
}
|
|
|
|
Shader_Location :: enum {
|
|
Ambient,
|
|
LightDir,
|
|
LightColor,
|
|
Heightmap,
|
|
}
|
|
Shader_Location_Set :: bit_set[Shader_Location]
|
|
Shader_Location_Array :: [Shader_Location]i32
|
|
|
|
SHADER_LOCATION_NAMES := [Shader_Location]cstring {
|
|
.Ambient = "ambient",
|
|
.LightDir = "lightDir",
|
|
.LightColor = "lightColor",
|
|
.Heightmap = "heightmap",
|
|
}
|
|
|
|
Loaded_Shader :: struct {
|
|
shader: rl.Shader,
|
|
location_set: Shader_Location_Set,
|
|
locations: Shader_Location_Array,
|
|
}
|
|
|
|
SHADER_LOADER :: Asset_Cache_Loader {
|
|
load = proc(
|
|
assetman: ^Asset_Manager,
|
|
path: cstring,
|
|
payload: Asset_Cache_Loader_Payload,
|
|
) -> (
|
|
Asset,
|
|
bool,
|
|
) {
|
|
shader := rl.LoadShader(path, payload.(cstring))
|
|
return Loaded_Shader{shader = shader}, rl.IsShaderValid(shader)
|
|
},
|
|
unload = proc(asset: Asset) {
|
|
rl.UnloadShader(asset.(Loaded_Shader).shader)
|
|
},
|
|
}
|
|
|
|
SOUND_LOADER :: Asset_Cache_Loader {
|
|
load = proc(
|
|
assetman: ^Asset_Manager,
|
|
path: cstring,
|
|
payload: Asset_Cache_Loader_Payload,
|
|
) -> (
|
|
Asset,
|
|
bool,
|
|
) {
|
|
sound := rl.LoadSound(path)
|
|
return sound, rl.IsSoundValid(sound)
|
|
},
|
|
unload = proc(asset: Asset) {
|
|
rl.UnloadSound(asset.(rl.Sound))
|
|
},
|
|
}
|
|
|
|
MUSIC_LOADER :: Asset_Cache_Loader {
|
|
load = proc(
|
|
assetman: ^Asset_Manager,
|
|
path: cstring,
|
|
payload: Asset_Cache_Loader_Payload,
|
|
) -> (
|
|
Asset,
|
|
bool,
|
|
) {
|
|
music := rl.LoadMusicStream(path)
|
|
return music, rl.IsMusicValid(music)
|
|
},
|
|
unload = proc(asset: Asset) {
|
|
rl.UnloadMusicStream(asset.(rl.Music))
|
|
},
|
|
}
|
|
|
|
MODEL_LOADER :: Asset_Cache_Loader {
|
|
load = proc(
|
|
assetman: ^Asset_Manager,
|
|
path: cstring,
|
|
payload: Asset_Cache_Loader_Payload,
|
|
) -> (
|
|
Asset,
|
|
bool,
|
|
) {
|
|
model := rl.LoadModel(path)
|
|
return model, rl.IsModelValid(model)
|
|
},
|
|
unload = proc(asset: Asset) {
|
|
rl.UnloadModel(asset.(rl.Model))
|
|
},
|
|
}
|
|
|
|
TEXTURE_LOADER :: Asset_Cache_Loader {
|
|
load = proc(
|
|
assetman: ^Asset_Manager,
|
|
path: cstring,
|
|
payload: Asset_Cache_Loader_Payload,
|
|
) -> (
|
|
Asset,
|
|
bool,
|
|
) {
|
|
texture := rl.LoadTexture(path)
|
|
return texture, rl.IsTextureValid(texture)
|
|
},
|
|
unload = proc(asset: Asset) {
|
|
rl.UnloadTexture(asset.(rl.Texture2D))
|
|
},
|
|
}
|
|
|
|
CURVE_1D_CSV_LOADER :: Asset_Cache_Loader {
|
|
load = proc(
|
|
assetman: ^Asset_Manager,
|
|
path: cstring,
|
|
payload: Asset_Cache_Loader_Payload,
|
|
) -> (
|
|
Asset,
|
|
bool,
|
|
) {
|
|
data, err := physfs.read_entire_file(string(path), context.temp_allocator)
|
|
if err != nil {
|
|
log.errorf("Failed to read curve: %s, %v", path, err)
|
|
return nil, false
|
|
}
|
|
|
|
values, err2 := parse_csv_1d(data)
|
|
|
|
if err2 != nil {
|
|
log.errorf("Failed to parse curve: %s, %v", path, err2)
|
|
}
|
|
|
|
return values, true
|
|
},
|
|
unload = proc(asset: Asset) {
|
|
delete(asset.(Curve_1D))
|
|
},
|
|
}
|
|
|
|
CURVE_2D_CSV_LOADER :: Asset_Cache_Loader {
|
|
load = proc(
|
|
assetman: ^Asset_Manager,
|
|
path: cstring,
|
|
payload: Asset_Cache_Loader_Payload,
|
|
) -> (
|
|
Asset,
|
|
bool,
|
|
) {
|
|
data, err := physfs.read_entire_file(string(path), context.temp_allocator)
|
|
if err != nil {
|
|
log.errorf("Failed to read curve: %s, %v", path, err)
|
|
return nil, false
|
|
}
|
|
|
|
curve, err2 := parse_csv_2d(data)
|
|
|
|
if err2 != nil {
|
|
log.errorf("Failed to parse curve: %s, %v", path, err2)
|
|
}
|
|
|
|
return curve, true
|
|
},
|
|
unload = proc(asset: Asset) {
|
|
delete(asset.(Curve_2D))
|
|
},
|
|
}
|
|
|
|
SCENE_DESC_LOADER :: Asset_Cache_Loader {
|
|
load = proc(
|
|
assetman: ^Asset_Manager,
|
|
path: cstring,
|
|
payload: Asset_Cache_Loader_Payload,
|
|
) -> (
|
|
Asset,
|
|
bool,
|
|
) {
|
|
data, err := physfs.read_entire_file(string(path), context.temp_allocator)
|
|
if err != nil {
|
|
log.errorf("Failed to read curve: %s, %v", path, err)
|
|
return {}, false
|
|
}
|
|
|
|
parser: sexp.SEXP_Parser
|
|
parser.data = string(data)
|
|
parsed, err2 := sexp.parse(&parser, context.temp_allocator)
|
|
|
|
if err2 != nil {
|
|
log.errorf(
|
|
"Failed to parse curve: %s\n%s",
|
|
path,
|
|
sexp.temp_pretty_error(parser.data, err2),
|
|
)
|
|
}
|
|
|
|
inst_identifier := sexp.Ident(name.from_string("inst"))
|
|
|
|
num_instances: int
|
|
|
|
top_it := sexp.iterator_list(parsed)
|
|
for top_level_expr in sexp.iterator_next(&top_it) {
|
|
list_expr := top_level_expr.expr.(sexp.Sexp_List) or_continue
|
|
|
|
it := sexp.iterator_list(list_expr)
|
|
ident := sexp.iterator_expect_atom(&it, sexp.Ident) or_continue
|
|
|
|
if ident != inst_identifier {
|
|
continue
|
|
}
|
|
|
|
num_instances += 1
|
|
}
|
|
|
|
instances := make([]Scene_Instance, num_instances)
|
|
num_instances = 0
|
|
|
|
top_it = sexp.iterator_list(parsed)
|
|
for top_level_expr in sexp.iterator_next(&top_it) {
|
|
list_expr := top_level_expr.expr.(sexp.Sexp_List) or_continue
|
|
|
|
it := sexp.iterator_list(list_expr)
|
|
ident := sexp.iterator_expect_atom(&it, sexp.Ident) or_continue
|
|
|
|
if ident != inst_identifier {
|
|
continue
|
|
}
|
|
|
|
inst: Scene_Instance
|
|
|
|
for sexp.iterator_has_more(it) {
|
|
key := sexp.iterator_expect_atom(&it, sexp.Tag) or_continue
|
|
|
|
switch name.Name(key) {
|
|
case name.from_string("model"):
|
|
model_path := sexp.iterator_expect_atom(&it, string) or_continue
|
|
inst.model = name.from_string(model_path)
|
|
case name.from_string("pos"):
|
|
pos_list := sexp.iterator_expect_list(&it) or_continue
|
|
pos_it := sexp.iterator_list(pos_list)
|
|
x := sexp.iterator_expect_atom(&pos_it, f64) or_continue
|
|
y := sexp.iterator_expect_atom(&pos_it, f64) or_continue
|
|
z := sexp.iterator_expect_atom(&pos_it, f64) or_continue
|
|
|
|
inst.pos = {f32(x), f32(y), f32(z)}
|
|
case name.from_string("rot"):
|
|
rot_list := sexp.iterator_expect_list(&it) or_continue
|
|
rot_it := sexp.iterator_list(rot_list)
|
|
inst.rot.x = f32(sexp.iterator_expect_atom(&rot_it, f64) or_continue)
|
|
inst.rot.y = f32(sexp.iterator_expect_atom(&rot_it, f64) or_continue)
|
|
inst.rot.z = f32(sexp.iterator_expect_atom(&rot_it, f64) or_continue)
|
|
inst.rot.w = f32(sexp.iterator_expect_atom(&rot_it, f64) or_continue)
|
|
case name.from_string("scale"):
|
|
scale_list := sexp.iterator_expect_list(&it) or_continue
|
|
scale_it := sexp.iterator_list(scale_list)
|
|
x := sexp.iterator_expect_atom(&scale_it, f64) or_continue
|
|
y := sexp.iterator_expect_atom(&scale_it, f64) or_continue
|
|
z := sexp.iterator_expect_atom(&scale_it, f64) or_continue
|
|
|
|
inst.scale = {f32(x), f32(y), f32(z)}
|
|
}
|
|
}
|
|
|
|
instances[num_instances] = inst
|
|
num_instances += 1
|
|
}
|
|
|
|
return Scene_Desc{instances = instances[:num_instances]}, true
|
|
},
|
|
unload = proc(asset: Asset) {
|
|
delete(asset.(Scene_Desc).instances)
|
|
},
|
|
}
|
|
|
|
BVH_LOADER :: Asset_Cache_Loader {
|
|
load = proc(
|
|
assetman: ^Asset_Manager,
|
|
path: cstring,
|
|
payload: Asset_Cache_Loader_Payload,
|
|
) -> (
|
|
Asset,
|
|
bool,
|
|
) {
|
|
model_asset, _, model_res := assetman_fetch_or_load(
|
|
assetman,
|
|
Asset_Key{path = name.from_cstring(path), type = .Model},
|
|
)
|
|
|
|
if model_res == .Error {
|
|
return nil, false
|
|
}
|
|
|
|
model := model_asset.(rl.Model)
|
|
|
|
vert_count := 0
|
|
indices_count := 0
|
|
|
|
for i in 0 ..< model.meshCount {
|
|
mesh := model.meshes[i]
|
|
vert_count += int(mesh.vertexCount)
|
|
indices_count += int(mesh.triangleCount * 3)
|
|
}
|
|
assert(vert_count < int(max(u16)))
|
|
|
|
vertices := make([]bvh.Vec3, vert_count)
|
|
indices := make([]u16, indices_count)
|
|
|
|
vert_count = 0
|
|
indices_count = 0
|
|
for i in 0 ..< model.meshCount {
|
|
mesh := model.meshes[i]
|
|
mesh_vertices := (cast([^]rl.Vector3)mesh.vertices)[:mesh.vertexCount]
|
|
mesh_indices := mesh.indices[:mesh.triangleCount * 3]
|
|
|
|
copy(vertices[vert_count:], mesh_vertices)
|
|
|
|
for j in 0 ..< len(mesh_indices) {
|
|
index := vert_count + int(mesh_indices[j])
|
|
indices[indices_count + j] = u16(index)
|
|
}
|
|
|
|
vert_count += len(mesh_vertices)
|
|
indices_count += len(mesh_indices)
|
|
}
|
|
|
|
|
|
mesh_bvh := bvh.build_bvh_from_mesh(
|
|
{vertices = vertices, indices = indices},
|
|
context.allocator,
|
|
)
|
|
|
|
root_aabb := mesh_bvh.bvh.nodes[0].aabb
|
|
|
|
return Loaded_BVH {
|
|
aabb = root_aabb,
|
|
bvh = mesh_bvh.bvh,
|
|
vertices = vertices,
|
|
indices = indices,
|
|
},
|
|
true
|
|
},
|
|
unload = proc(asset: Asset) {
|
|
loaded_bvh := asset.(Loaded_BVH)
|
|
|
|
bvh.destroy_bvh(&loaded_bvh.bvh)
|
|
delete(loaded_bvh.vertices)
|
|
delete(loaded_bvh.indices)
|
|
},
|
|
should_reload = proc(assetman: ^Asset_Manager, key: Asset_Key, entry: Asset_Entry) -> bool {
|
|
_, modtime, _ := assetman_fetch_or_load(
|
|
assetman,
|
|
Asset_Key{path = key.path, type = .Model},
|
|
)
|
|
return entry.modtime != modtime
|
|
},
|
|
}
|
|
|
|
CONVEX_LOADER :: Asset_Cache_Loader {
|
|
load = proc(
|
|
assetman: ^Asset_Manager,
|
|
path: cstring,
|
|
payload: Asset_Cache_Loader_Payload,
|
|
) -> (
|
|
Asset,
|
|
bool,
|
|
) {
|
|
tracy.Zone()
|
|
bytes, err := physfs.read_entire_file(string(path), context.temp_allocator)
|
|
|
|
if err != nil {
|
|
log.errorf("error reading file %v %s", err)
|
|
return {}, false
|
|
}
|
|
|
|
return parse_convex(bytes)
|
|
},
|
|
unload = proc(asset: Asset) {
|
|
convex := asset.(Loaded_Convex)
|
|
|
|
delete(convex.mesh.vertices)
|
|
delete(convex.mesh.edges)
|
|
delete(convex.mesh.faces)
|
|
},
|
|
}
|
|
|
|
ASSET_LOADERS := [Asset_Type]Asset_Cache_Loader {
|
|
.Texture = TEXTURE_LOADER,
|
|
.Model = MODEL_LOADER,
|
|
.Shader = SHADER_LOADER,
|
|
.Sound = SOUND_LOADER,
|
|
.Music = MUSIC_LOADER,
|
|
.Curve_1D = CURVE_1D_CSV_LOADER,
|
|
.Curve_2D = CURVE_2D_CSV_LOADER,
|
|
.Scene = SCENE_DESC_LOADER,
|
|
.BVH = BVH_LOADER,
|
|
.Convex = CONVEX_LOADER,
|
|
}
|
|
|
|
assetman_init :: proc(assetman: ^Asset_Manager) {
|
|
modtime_watcher_init(&assetman.watcher)
|
|
}
|
|
|
|
assetman_tick :: proc(assetman: ^Asset_Manager) {
|
|
tracy.Zone()
|
|
|
|
for asset in modtime_watcher_next(&assetman.watcher) {
|
|
key := Asset_Key {
|
|
path = asset.path,
|
|
type = asset.type,
|
|
}
|
|
|
|
entry, ok := &assetman.assets[key]
|
|
|
|
if ok {
|
|
log.debugf(
|
|
"asset changed {} {} {} {}",
|
|
name.to_string(asset.path),
|
|
asset,
|
|
entry.modtime,
|
|
asset.modtime,
|
|
)
|
|
entry.fs_modtime = asset.modtime
|
|
}
|
|
}
|
|
}
|
|
|
|
Asset_Cache_Result :: enum {
|
|
Cached,
|
|
Loaded,
|
|
Reloaded,
|
|
Error,
|
|
}
|
|
|
|
assetman_fetch_or_load_internal :: proc(
|
|
assetman: ^Asset_Manager,
|
|
key: Asset_Key,
|
|
payload: Asset_Cache_Loader_Payload = nil,
|
|
force_no_reload := false,
|
|
) -> (
|
|
value: Asset,
|
|
modtime: physfs.sint64,
|
|
result: Asset_Cache_Result,
|
|
) {
|
|
existing, has_existing := assetman.assets[key]
|
|
|
|
if has_existing {
|
|
wants_reload :=
|
|
ASSET_LOADERS[key.type].should_reload(assetman, key, existing) if ASSET_LOADERS[key.type].should_reload != nil else false
|
|
|
|
if force_no_reload || (existing.modtime == existing.fs_modtime && !wants_reload) {
|
|
result = .Cached
|
|
return existing.asset, existing.modtime, result
|
|
} else {
|
|
path := name.to_cstring(key.path)
|
|
new_modtime := physfs.getLastModTime(path)
|
|
// Try to load the new version
|
|
new_value, ok := ASSET_LOADERS[key.type].load(assetman, path, payload)
|
|
|
|
if ok {
|
|
result = .Reloaded
|
|
ASSET_LOADERS[key.type].unload(existing.asset)
|
|
assetman.assets[key] = {
|
|
asset = new_value,
|
|
modtime = new_modtime,
|
|
fs_modtime = new_modtime,
|
|
}
|
|
|
|
log.debugf("reloaded asset: %s", path)
|
|
|
|
return new_value, new_modtime, result
|
|
} else {
|
|
log.warnf("failed to reload asset after modification %s", path)
|
|
result = .Cached
|
|
|
|
return existing.asset, existing.modtime, result
|
|
}
|
|
}
|
|
} else {
|
|
path := name.to_cstring(key.path)
|
|
modtime = physfs.getLastModTime(path)
|
|
ok: bool
|
|
value, ok = ASSET_LOADERS[key.type].load(assetman, path, payload)
|
|
|
|
if ok {
|
|
assetman.assets[key] = {
|
|
asset = value,
|
|
modtime = modtime,
|
|
fs_modtime = modtime,
|
|
}
|
|
result = .Loaded
|
|
|
|
log.debugf("loaded asset: %s", path)
|
|
|
|
return value, modtime, result
|
|
} else {
|
|
log.errorf("failed to load asset %s", path)
|
|
result = .Error
|
|
return {}, 0, .Error
|
|
}
|
|
}
|
|
}
|
|
|
|
assetman_fetch_or_load :: proc(
|
|
assetman: ^Asset_Manager,
|
|
key: Asset_Key,
|
|
payload: Asset_Cache_Loader_Payload = nil,
|
|
force_no_reload := false,
|
|
) -> (
|
|
value: Asset,
|
|
modtime: physfs.sint64,
|
|
result: Asset_Cache_Result,
|
|
) {
|
|
value, modtime, result = assetman_fetch_or_load_internal(
|
|
assetman,
|
|
key,
|
|
payload,
|
|
force_no_reload,
|
|
)
|
|
if result != .Cached {
|
|
log.debugf("asset {}: [{}] {}", key.type, name.to_string(key.path), result)
|
|
}
|
|
|
|
if result == .Loaded {
|
|
modtime_watcher_add_asset(&assetman.watcher, key.type, key.path, modtime)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
assetcache_destroy :: proc(ac: ^$T/Asset_Cache($E)) {
|
|
for _, v in ac.cache {
|
|
ac.loader.unload(v.value)
|
|
}
|
|
delete(ac.cache)
|
|
}
|
|
|
|
get_texture :: proc(assetman: ^Asset_Manager, path: cstring) -> rl.Texture2D {
|
|
tracy.Zone()
|
|
|
|
texture, _, _ := assetman_fetch_or_load(
|
|
assetman,
|
|
Asset_Key{path = name.from_cstring(path), type = .Texture},
|
|
)
|
|
|
|
return texture.(rl.Texture2D)
|
|
}
|
|
|
|
get_model_ex :: proc(
|
|
assetman: ^Asset_Manager,
|
|
path: cstring,
|
|
ref_modtime: physfs.sint64 = 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: physfs.sint64,
|
|
reloaded: bool,
|
|
) {
|
|
tracy.Zone()
|
|
|
|
asset: Asset
|
|
result: Asset_Cache_Result
|
|
asset, modtime, result = assetman_fetch_or_load(
|
|
assetman,
|
|
Asset_Key{path = name.from_cstring(path), type = .Model},
|
|
)
|
|
model = asset.(rl.Model)
|
|
reloaded = result == .Reloaded || ref_modtime != modtime
|
|
|
|
return
|
|
}
|
|
|
|
get_model :: proc(assetman: ^Asset_Manager, path: cstring) -> rl.Model {
|
|
model, _, _ := get_model_ex(assetman, path)
|
|
|
|
return model
|
|
}
|
|
|
|
get_shader :: proc(
|
|
assetman: ^Asset_Manager,
|
|
vs_path: cstring,
|
|
ps_path: cstring,
|
|
location_set: Shader_Location_Set,
|
|
) -> Loaded_Shader {
|
|
asset, _, result := assetman_fetch_or_load(
|
|
assetman,
|
|
Asset_Key{path = name.from_cstring(vs_path), type = .Shader},
|
|
ps_path,
|
|
)
|
|
loaded_shader := asset.(Loaded_Shader)
|
|
|
|
if location_set > loaded_shader.location_set || result == .Loaded || result == .Reloaded {
|
|
loaded_shader.location_set = location_set
|
|
loaded_shader.locations = {}
|
|
|
|
for location in location_set {
|
|
loaded_shader.locations[location] = rl.GetShaderLocation(
|
|
loaded_shader.shader,
|
|
SHADER_LOCATION_NAMES[location],
|
|
)
|
|
}
|
|
}
|
|
|
|
return loaded_shader
|
|
}
|
|
|
|
get_sound :: proc(assetman: ^Asset_Manager, path: cstring) -> rl.Sound {
|
|
sound, _, _ := assetman_fetch_or_load(
|
|
assetman,
|
|
Asset_Key{path = name.from_cstring(path), type = .Sound},
|
|
)
|
|
return sound.(rl.Sound)
|
|
}
|
|
|
|
get_music :: proc(assetman: ^Asset_Manager, path: cstring) -> rl.Music {
|
|
music, _, _ := assetman_fetch_or_load(
|
|
assetman,
|
|
Asset_Key{path = name.from_cstring(path), type = .Music},
|
|
)
|
|
return music.(rl.Music)
|
|
}
|
|
|
|
get_bvh :: proc(assetman: ^Asset_Manager, path: name.Name) -> (Loaded_BVH, bool) {
|
|
tracy.Zone()
|
|
|
|
asset, _, res := assetman_fetch_or_load(assetman, Asset_Key{path = path, type = .BVH})
|
|
|
|
return asset.(Loaded_BVH), res != Asset_Cache_Result.Error
|
|
}
|
|
|
|
get_curve_1d :: proc(assetman: ^Asset_Manager, path: cstring) -> (curve: Curve_1D) {
|
|
asset, _, _ := assetman_fetch_or_load(
|
|
assetman,
|
|
Asset_Key{path = name.from_cstring(path), type = .Curve_1D},
|
|
)
|
|
return asset.(Curve_1D)
|
|
}
|
|
|
|
// Reads a two column comma separated csv file as a curve
|
|
get_curve_2d :: proc(assetman: ^Asset_Manager, path: cstring) -> (curve: Curve_2D) {
|
|
asset, _, _ := assetman_fetch_or_load(
|
|
assetman,
|
|
Asset_Key{path = name.from_cstring(path), type = .Curve_2D},
|
|
)
|
|
return asset.(Curve_2D)
|
|
}
|
|
|
|
// Reads a two column comma separated csv file as a curve
|
|
get_scene_desc :: proc(
|
|
assetman: ^Asset_Manager,
|
|
path: name.Name,
|
|
force_no_reload := false,
|
|
) -> (
|
|
scene: Scene_Desc,
|
|
modtime: i64,
|
|
) {
|
|
asset, mtime, _ := assetman_fetch_or_load(
|
|
assetman,
|
|
Asset_Key{path = path, type = .Scene},
|
|
nil,
|
|
force_no_reload,
|
|
)
|
|
return asset.(Scene_Desc), mtime
|
|
}
|
|
|
|
get_convex :: proc(assetman: ^Asset_Manager, path: cstring) -> (result: Loaded_Convex) {
|
|
asset, _, _ := assetman_fetch_or_load(
|
|
assetman,
|
|
Asset_Key{path = name.from_cstring(path), type = .Convex},
|
|
)
|
|
return asset.(Loaded_Convex)
|
|
}
|
|
|
|
shutdown :: proc(assetman: ^Asset_Manager) {
|
|
tracy.Zone()
|
|
|
|
modtime_watcher_deinit(&assetman.watcher)
|
|
|
|
for k, v in assetman.assets {
|
|
ASSET_LOADERS[k.type].unload(v.asset)
|
|
}
|
|
delete_map(assetman.assets)
|
|
}
|