Compare commits
2 Commits
5f4fa12040
...
7da986970e
Author | SHA1 | Date | |
---|---|---|---|
7da986970e | |||
a81e81c52c |
1
.gitignore
vendored
1
.gitignore
vendored
@ -19,3 +19,4 @@ game_web/
|
||||
.venv
|
||||
build/
|
||||
bin/
|
||||
.direnv/
|
||||
|
1
.tool-versions
Normal file
1
.tool-versions
Normal file
@ -0,0 +1 @@
|
||||
python 3.11.13
|
BIN
assets/blender/ice_cream_truck_blend/Split.glb
(Stored with Git LFS)
Normal file
BIN
assets/blender/ice_cream_truck_blend/Split.glb
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/blender/test_level_blend/NurbsPath.glb
(Stored with Git LFS)
Normal file
BIN
assets/blender/test_level_blend/NurbsPath.glb
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/blender/test_level_blend/Plane.glb
(Stored with Git LFS)
Normal file
BIN
assets/blender/test_level_blend/Plane.glb
(Stored with Git LFS)
Normal file
Binary file not shown.
45
assets/blender/test_level_blend/Scene.scn
Normal file
45
assets/blender/test_level_blend/Scene.scn
Normal file
@ -0,0 +1,45 @@
|
||||
(inst
|
||||
:model "assets/blender/testblend_blend/Bakery.glb"
|
||||
:pos (0.000000 0.000000 12.000000)
|
||||
:rot (0.000000 0.000000 0.000000 1.000000)
|
||||
:scale (1.000000 1.000000 1.000000))
|
||||
(inst
|
||||
:model "assets/blender/test_level_blend/Plane.glb"
|
||||
:pos (0.000000 -1.000000 0.000000)
|
||||
:rot (0.000000 0.000000 0.000000 1.000000)
|
||||
:scale (22.469545 22.469545 22.469545))
|
||||
(inst
|
||||
:model "assets/blender/testblend_blend/Fire_Station.glb"
|
||||
:pos (9.000000 0.000000 13.000000)
|
||||
:rot (0.000000 0.000000 0.000000 1.000000)
|
||||
:scale (1.000000 1.000000 1.000000))
|
||||
(inst
|
||||
:model "assets/blender/testblend_blend/Gas_Station_Shop.glb"
|
||||
:pos (-8.000000 0.000000 11.000000)
|
||||
:rot (0.000000 0.000000 0.000000 1.000000)
|
||||
:scale (1.000000 1.000000 1.000000))
|
||||
(inst
|
||||
:model "assets/blender/testblend_blend/Gas_Station_Shop.glb"
|
||||
:pos (-16.000000 0.000000 11.000000)
|
||||
:rot (0.000000 0.000000 0.000000 1.000000)
|
||||
:scale (1.000000 1.000000 1.000000))
|
||||
(inst
|
||||
:model "assets/blender/testblend_blend/Gas_Station_Shop.glb"
|
||||
:pos (-15.559284 1.609015 0.259853)
|
||||
:rot (0.000000 0.000000 0.000000 1.000000)
|
||||
:scale (1.000000 1.000000 1.000000))
|
||||
(inst
|
||||
:model "assets/blender/testblend_blend/Green_House.glb"
|
||||
:pos (14.000000 0.000000 -7.000000)
|
||||
:rot (0.000000 0.000000 0.000000 1.000000)
|
||||
:scale (1.000000 1.000000 1.000000))
|
||||
(inst
|
||||
:model "assets/blender/testblend_blend/Hotel.glb"
|
||||
:pos (8.000000 0.000000 -18.000000)
|
||||
:rot (0.000000 0.000000 0.000000 1.000000)
|
||||
:scale (1.000000 1.000000 1.000000))
|
||||
(inst
|
||||
:model "assets/blender/test_level_blend/NurbsPath.glb"
|
||||
:pos (0.000000 0.000000 0.000000)
|
||||
:rot (0.000000 0.000000 0.000000 1.000000)
|
||||
:scale (1.000000 1.000000 1.000000))
|
BIN
assets/blender/testblend_blend/Bakery.glb
(Stored with Git LFS)
Normal file
BIN
assets/blender/testblend_blend/Bakery.glb
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/blender/testblend_blend/Fire_Station.glb
(Stored with Git LFS)
Normal file
BIN
assets/blender/testblend_blend/Fire_Station.glb
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/blender/testblend_blend/Gas_Station_Shop.glb
(Stored with Git LFS)
Normal file
BIN
assets/blender/testblend_blend/Gas_Station_Shop.glb
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/blender/testblend_blend/Green_House.glb
(Stored with Git LFS)
Normal file
BIN
assets/blender/testblend_blend/Green_House.glb
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/blender/testblend_blend/Hotel.glb
(Stored with Git LFS)
Normal file
BIN
assets/blender/testblend_blend/Hotel.glb
(Stored with Git LFS)
Normal file
Binary file not shown.
87
blender/batch_export.py
Normal file
87
blender/batch_export.py
Normal file
@ -0,0 +1,87 @@
|
||||
import bpy
|
||||
import os
|
||||
import pathlib
|
||||
|
||||
# Ensure blend file is saved
|
||||
blend_filepath = bpy.data.filepath
|
||||
if not blend_filepath:
|
||||
raise Exception("Blend file is not saved")
|
||||
|
||||
assert bpy.context.scene is not None
|
||||
|
||||
def clean_blend_path(blend_path: str) -> str:
|
||||
return os.path.join(os.path.dirname(blend_path), bpy.path.clean_name(os.path.basename(blend_path)))
|
||||
|
||||
# Directory setup
|
||||
abs_filepath = bpy.path.abspath(blend_filepath)
|
||||
src_assets_path = os.path.dirname(abs_filepath)
|
||||
project_path = os.path.dirname(src_assets_path)
|
||||
assets_path = os.path.join(project_path, "assets")
|
||||
rel_blend_path = os.path.relpath(abs_filepath, src_assets_path)
|
||||
clean_rel_path = clean_blend_path(rel_blend_path)
|
||||
scene_name = bpy.context.scene.name
|
||||
instance_output_path = os.path.join(assets_path, "blender", clean_rel_path, f"{scene_name}.scn")
|
||||
|
||||
# Utility: returns path to store the glb
|
||||
def get_model_export_path(obj: bpy.types.Object) -> str:
|
||||
if obj.instance_type == 'COLLECTION' and obj.instance_collection:
|
||||
lib_path = obj.instance_collection.library.filepath
|
||||
collection_name = bpy.path.clean_name(obj.instance_collection.name)
|
||||
rel_lib_path = os.path.relpath(bpy.path.abspath(lib_path), src_assets_path)
|
||||
rel_lib_path = clean_blend_path(rel_lib_path)
|
||||
return os.path.join("blender", rel_lib_path, f"{collection_name}.glb")
|
||||
else:
|
||||
obj_name = bpy.path.clean_name(obj.name)
|
||||
return os.path.join("blender", clean_rel_path, f"{obj_name}.glb")
|
||||
|
||||
# Utility: writes an object to glb
|
||||
def export_object_as_glb(obj: bpy.types.Object, export_path: str):
|
||||
full_export_path = os.path.join(assets_path, export_path)
|
||||
pathlib.Path(os.path.dirname(full_export_path)).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
obj.select_set(True)
|
||||
|
||||
assert bpy.context.view_layer is not None
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
|
||||
bpy.ops.export_scene.gltf(filepath=full_export_path, use_selection=True, export_apply=True)
|
||||
print(f"Exported {obj.name} -> {export_path}")
|
||||
|
||||
# Collect all visible, non-hidden objects in the scene
|
||||
visible_objects = [
|
||||
obj for obj in bpy.context.scene.objects
|
||||
if obj.visible_get()
|
||||
]
|
||||
|
||||
exported = set()
|
||||
instances = []
|
||||
|
||||
for obj in visible_objects:
|
||||
model_path = get_model_export_path(obj)
|
||||
|
||||
if model_path not in exported:
|
||||
export_object_as_glb(obj, model_path)
|
||||
exported.add(model_path)
|
||||
|
||||
loc = obj.location
|
||||
rot = obj.rotation_quaternion
|
||||
scale = obj.scale
|
||||
instance_sexpr = (
|
||||
'(inst'
|
||||
f'\n\t:model "assets/{model_path}"'
|
||||
f'\n\t:pos ({loc.x:.6f} {loc.z:.6f} {loc.y:.6f})'
|
||||
f'\n\t:rot ({rot.x:.6f} {rot.y:.6f} {rot.z:.6f} {rot.w:.6f})'
|
||||
f'\n\t:scale ({scale.x:.6f} {scale.y:.6f} {scale.z:.6f}))'
|
||||
)
|
||||
instances.append(instance_sexpr)
|
||||
|
||||
# Save instances to scene file
|
||||
instance_output_abs = os.path.join(project_path, instance_output_path)
|
||||
pathlib.Path(os.path.dirname(instance_output_abs)).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(instance_output_abs, "w", encoding="utf-8") as f:
|
||||
for line in instances:
|
||||
f.write(line + "\n")
|
||||
|
||||
print(f"Written instances to {instance_output_abs}")
|
@ -4,6 +4,7 @@ import "common:name"
|
||||
import "core:container/intrusive/list"
|
||||
import "core:fmt"
|
||||
import "core:io"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
import "core:testing"
|
||||
import "core:unicode"
|
||||
@ -151,7 +152,13 @@ parse_atom :: proc(ctx: ^SEXP_Parser) -> (atom: Atom, error: Error) {
|
||||
result := ctx.data[start:ctx.pos]
|
||||
ctx.pos = next
|
||||
return result, nil
|
||||
|
||||
case '0' ..= '9', '-', '+':
|
||||
value, n, ok := strconv.parse_f64_prefix(ctx.data[ctx.pos:])
|
||||
if !ok {
|
||||
return nil, make_error(ctx, "failed to parse number")
|
||||
}
|
||||
ctx.pos += n
|
||||
return value, nil
|
||||
}
|
||||
|
||||
return nil, make_error(ctx, fmt.tprintf("unknown atom {}", c))
|
||||
@ -233,6 +240,43 @@ iterator_next_checked :: proc(it: ^List_Iterator) -> Sexp {
|
||||
return node.expr
|
||||
}
|
||||
|
||||
iterator_has_more :: proc(it: List_Iterator) -> bool {
|
||||
return it.curr != nil
|
||||
}
|
||||
|
||||
iterator_expect_list :: proc(it: ^List_Iterator) -> (Sexp_List, bool) {
|
||||
next, ok := iterator_next(it)
|
||||
|
||||
if !ok {
|
||||
return {}, false
|
||||
}
|
||||
|
||||
list_expr: Sexp_List
|
||||
list_expr, ok = next.expr.(Sexp_List)
|
||||
|
||||
return list_expr, ok
|
||||
}
|
||||
|
||||
iterator_expect_atom :: proc(it: ^List_Iterator, $T: typeid) -> (T, bool) {
|
||||
next, ok := iterator_next(it)
|
||||
|
||||
if !ok {
|
||||
return {}, false
|
||||
}
|
||||
|
||||
atom_expr: Atom
|
||||
atom_expr, ok = next.expr.(Atom)
|
||||
|
||||
if !ok {
|
||||
return {}, false
|
||||
}
|
||||
|
||||
result: T
|
||||
result, ok = atom_expr.(T)
|
||||
|
||||
return result, ok
|
||||
}
|
||||
|
||||
print_list :: proc(it: List_Iterator, w: io.Writer) -> io.Error {
|
||||
it := it
|
||||
io.write_byte(w, '(') or_return
|
||||
@ -402,6 +446,16 @@ print_pretty_error :: proc(w: io.Writer, data: string, error: Error) -> io.Error
|
||||
return nil
|
||||
}
|
||||
|
||||
// Prints pretty error to temp string
|
||||
temp_pretty_error :: proc(data: string, error: Error) -> string {
|
||||
builder: strings.Builder
|
||||
strings.builder_init(&builder, context.temp_allocator)
|
||||
|
||||
writer := strings.to_writer(&builder)
|
||||
print_pretty_error(writer, data, error)
|
||||
return strings.to_string(builder)
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_parse :: proc(t: ^testing.T) {
|
||||
ctx: SEXP_Parser
|
||||
|
@ -81,3 +81,7 @@ to_string :: proc(name: Name) -> string {
|
||||
sync.atomic_rw_mutex_shared_guard(&global_container.lock)
|
||||
return global_container.names_array[name]
|
||||
}
|
||||
|
||||
to_cstring :: proc(name: Name) -> cstring {
|
||||
return strings.unsafe_string_to_cstring(to_string(name))
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package assets
|
||||
|
||||
import "common:encoding/sexp"
|
||||
import "common:name"
|
||||
import "core:log"
|
||||
import "core:math"
|
||||
import lg "core:math/linalg"
|
||||
@ -15,11 +17,24 @@ import "libs:tracy"
|
||||
|
||||
_ :: math
|
||||
|
||||
@(private = "file")
|
||||
g_assetman_instance: ^Asset_Manager
|
||||
|
||||
init :: proc(assetman: ^Asset_Manager) {
|
||||
g_assetman_instance = assetman
|
||||
}
|
||||
|
||||
manager :: #force_inline proc() -> ^Asset_Manager {
|
||||
return g_assetman_instance
|
||||
}
|
||||
|
||||
Loaded_BVH :: struct {
|
||||
// AABB of all bvhs
|
||||
aabb: bvh.AABB,
|
||||
// BVH for each mesh in a model
|
||||
bvhs: []bvh.BVH,
|
||||
bvh: bvh.BVH,
|
||||
vertices: []rl.Vector3,
|
||||
indices: []u16,
|
||||
modtime: physfs.sint64,
|
||||
}
|
||||
|
||||
@ -34,17 +49,28 @@ Loaded_Curve_2D :: struct {
|
||||
points: [][2]f32,
|
||||
}
|
||||
|
||||
destroy_loaded_bvh :: proc(loaded_bvh: Loaded_BVH) {
|
||||
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)
|
||||
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_Manager :: struct {
|
||||
textures: Asset_Cache(rl.Texture2D),
|
||||
models: Asset_Cache(rl.Model),
|
||||
@ -53,6 +79,7 @@ Asset_Manager :: struct {
|
||||
music: Asset_Cache(rl.Music),
|
||||
curves_1d: Asset_Cache([]f32),
|
||||
curves_2d: Asset_Cache(Curve_2D),
|
||||
scenes: Asset_Cache(Scene_Desc),
|
||||
bvhs: map[cstring]Loaded_BVH,
|
||||
curves: map[cstring]Loaded_Curve_2D,
|
||||
}
|
||||
@ -191,6 +218,104 @@ CURVE_2D_CSV_LOADER :: Asset_Cache_Loader(Curve_2D) {
|
||||
},
|
||||
}
|
||||
|
||||
SCENE_DESC_LOADER :: Asset_Cache_Loader(Scene_Desc) {
|
||||
load = proc(path: cstring, payload: Asset_Cache_Loader_Payload) -> (Scene_Desc, 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(scene_desc: Scene_Desc) {
|
||||
delete(scene_desc.instances)
|
||||
},
|
||||
}
|
||||
|
||||
assetman_init :: proc(assetman: ^Asset_Manager) {
|
||||
assetman.models = {
|
||||
loader = MODEL_LOADER,
|
||||
@ -213,6 +338,9 @@ assetman_init :: proc(assetman: ^Asset_Manager) {
|
||||
assetman.curves_2d = {
|
||||
loader = CURVE_2D_CSV_LOADER,
|
||||
}
|
||||
assetman.scenes = {
|
||||
loader = SCENE_DESC_LOADER,
|
||||
}
|
||||
}
|
||||
|
||||
Asset_Cache_Result :: enum {
|
||||
@ -226,6 +354,7 @@ assetcache_fetch_or_load :: proc(
|
||||
ac: ^$T/Asset_Cache($E),
|
||||
path: cstring,
|
||||
payload: Asset_Cache_Loader_Payload = nil,
|
||||
force_no_reload := false,
|
||||
) -> (
|
||||
value: E,
|
||||
modtime: physfs.sint64,
|
||||
@ -238,7 +367,7 @@ assetcache_fetch_or_load :: proc(
|
||||
if has_existing {
|
||||
new_modtime := physfs.getLastModTime(path)
|
||||
|
||||
if existing.modtime == new_modtime {
|
||||
if force_no_reload || existing.modtime == new_modtime {
|
||||
result = .Cached
|
||||
return existing.value, new_modtime, result
|
||||
} else {
|
||||
@ -361,7 +490,7 @@ get_music :: proc(assetman: ^Asset_Manager, path: cstring) -> rl.Music {
|
||||
|
||||
null_bvhs: []bvh.BVH
|
||||
|
||||
get_bvh :: proc(assetman: ^Asset_Manager, path: cstring) -> Loaded_BVH {
|
||||
get_bvh :: proc(assetman: ^Asset_Manager, path: cstring) -> (Loaded_BVH, bool) {
|
||||
tracy.Zone()
|
||||
|
||||
loaded_bvh, ok := assetman.bvhs[path]
|
||||
@ -370,22 +499,41 @@ get_bvh :: proc(assetman: ^Asset_Manager, path: cstring) -> Loaded_BVH {
|
||||
should_recreate := reloaded || !ok
|
||||
|
||||
if ok && should_recreate {
|
||||
destroy_loaded_bvh(loaded_bvh)
|
||||
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 = max(f32),
|
||||
max = min(f32),
|
||||
}
|
||||
vert_count := 0
|
||||
indices_count := 0
|
||||
|
||||
for i in 0 ..< model.meshCount {
|
||||
mesh := model.meshes[i]
|
||||
vertices := (cast([^]rl.Vector3)mesh.vertices)[:mesh.vertexCount]
|
||||
indices := mesh.indices[:mesh.triangleCount * 3]
|
||||
vert_count += int(mesh.vertexCount)
|
||||
indices_count += int(mesh.triangleCount * 3)
|
||||
}
|
||||
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])
|
||||
assert(index <= int(max(u16)))
|
||||
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},
|
||||
@ -393,20 +541,17 @@ get_bvh :: proc(assetman: ^Asset_Manager, path: cstring) -> Loaded_BVH {
|
||||
)
|
||||
|
||||
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,
|
||||
aabb = root_aabb,
|
||||
bvh = mesh_bvh.bvh,
|
||||
vertices = vertices,
|
||||
indices = indices,
|
||||
modtime = modtime,
|
||||
}
|
||||
}
|
||||
|
||||
return assetman.bvhs[path]
|
||||
return assetman.bvhs[path], should_recreate
|
||||
}
|
||||
|
||||
get_curve_1d :: proc(assetman: ^Asset_Manager, path: cstring) -> (curve: []f32) {
|
||||
@ -420,6 +565,20 @@ get_curve_2d :: proc(assetman: ^Asset_Manager, path: cstring) -> (curve: Curve_2
|
||||
return
|
||||
}
|
||||
|
||||
// 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,
|
||||
reloaded: bool,
|
||||
) {
|
||||
res: Asset_Cache_Result
|
||||
scene, _, res = assetcache_fetch_or_load(&assetman.scenes, name.to_cstring(path))
|
||||
return scene, (res == .Loaded || res == .Reloaded)
|
||||
}
|
||||
|
||||
get_convex :: proc(assetman: ^Asset_Manager, path: cstring) -> (result: Loaded_Convex) {
|
||||
bytes, err := physfs.read_entire_file(string(path), context.temp_allocator)
|
||||
if err != nil {
|
||||
@ -826,9 +985,10 @@ shutdown :: proc(assetman: ^Asset_Manager) {
|
||||
assetcache_destroy(&assetman.music)
|
||||
assetcache_destroy(&assetman.curves_1d)
|
||||
assetcache_destroy(&assetman.curves_2d)
|
||||
assetcache_destroy(&assetman.scenes)
|
||||
|
||||
for _, loaded_bvh in assetman.bvhs {
|
||||
destroy_loaded_bvh(loaded_bvh)
|
||||
for _, &loaded_bvh in assetman.bvhs {
|
||||
destroy_loaded_bvh(&loaded_bvh)
|
||||
}
|
||||
delete(assetman.bvhs)
|
||||
}
|
||||
|
158
game/game.odin
158
game/game.odin
@ -17,12 +17,10 @@ package game
|
||||
import "assets"
|
||||
import "common:name"
|
||||
import "core:fmt"
|
||||
import "core:hash"
|
||||
import "core:log"
|
||||
import "core:math"
|
||||
import "core:math/linalg"
|
||||
import "game:physics"
|
||||
import "game:physics/bvh"
|
||||
import "game:render"
|
||||
import rl "libs:raylib"
|
||||
import "libs:raylib/rlgl"
|
||||
@ -49,6 +47,65 @@ Debug_Draw_State :: struct {
|
||||
draw_physics_scene: bool,
|
||||
}
|
||||
|
||||
Scene :: struct {
|
||||
scene_desc_path: name.Name,
|
||||
level_geoms: []physics.Level_Geom_Handle,
|
||||
}
|
||||
|
||||
scene_destroy :: proc(world: ^World, scene: ^Scene) {
|
||||
scene.scene_desc_path = name.NONE
|
||||
sim_state := physics.get_sim_state(&world.physics_scene)
|
||||
for handle in scene.level_geoms {
|
||||
physics.remove_level_geom(sim_state, handle)
|
||||
}
|
||||
delete(scene.level_geoms)
|
||||
scene.level_geoms = nil
|
||||
}
|
||||
|
||||
immediate_scene :: proc(world: ^World, scene: ^Scene, path: cstring) {
|
||||
path_name := name.from_string(string(path))
|
||||
|
||||
desc, reloaded := assets.get_scene_desc(&g_mem.assetman, path_name)
|
||||
|
||||
if reloaded || scene.scene_desc_path != path_name {
|
||||
scene_destroy(world, scene)
|
||||
|
||||
scene.scene_desc_path = path_name
|
||||
|
||||
sim_state := physics.get_sim_state(&world.physics_scene)
|
||||
for inst in desc.instances {
|
||||
physics.add_level_geom(
|
||||
sim_state,
|
||||
physics.Level_Geom_Config {
|
||||
position = inst.pos,
|
||||
rotation = inst.rot,
|
||||
source = physics.Level_Geometry_Asset(name.to_string(inst.model)),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scene_copy :: proc(dst, src: ^Scene) {
|
||||
dst.scene_desc_path = src.scene_desc_path
|
||||
if len(dst.level_geoms) != len(src.level_geoms) {
|
||||
delete(dst.level_geoms)
|
||||
dst.level_geoms = make([]physics.Level_Geom_Handle, len(src.level_geoms))
|
||||
}
|
||||
copy(dst.level_geoms, src.level_geoms)
|
||||
}
|
||||
|
||||
scene_draw :: proc(scene: ^Scene) {
|
||||
desc, _ := assets.get_scene_desc(&g_mem.assetman, scene.scene_desc_path, true)
|
||||
for geo in desc.instances {
|
||||
render.draw_model(
|
||||
assets.get_model(&g_mem.assetman, name.to_cstring(geo.model)),
|
||||
{},
|
||||
auto_cast linalg.matrix4_from_trs(geo.pos, geo.rot, geo.scale),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
World :: struct {
|
||||
player_pos: rl.Vector3,
|
||||
track: Track,
|
||||
@ -58,10 +115,15 @@ World :: struct {
|
||||
car_handle: physics.Body_Handle,
|
||||
engine_handle: physics.Engine_Handle,
|
||||
debug_state: Debug_Draw_State,
|
||||
main_scene: Scene,
|
||||
}
|
||||
world_init :: proc(world: ^World) {
|
||||
physics.scene_init(&world.physics_scene, &g_mem.assetman)
|
||||
}
|
||||
copy_world :: proc(dst, src: ^World) {
|
||||
copy_track(&dst.track, &src.track)
|
||||
physics.copy_physics_scene(&dst.physics_scene, &src.physics_scene)
|
||||
scene_copy(&dst.main_scene, &src.main_scene)
|
||||
dst.player_pos = src.player_pos
|
||||
dst.pause = src.pause
|
||||
dst.car_com = src.car_com
|
||||
@ -71,7 +133,8 @@ copy_world :: proc(dst, src: ^World) {
|
||||
}
|
||||
destroy_world :: proc(world: ^World) {
|
||||
destroy_track(&world.track)
|
||||
physics.destroy_physics_scene(&world.physics_scene)
|
||||
scene_destroy(world, &world.main_scene)
|
||||
physics.scene_destroy(&world.physics_scene)
|
||||
}
|
||||
|
||||
Runtime_World :: struct {
|
||||
@ -91,6 +154,7 @@ Runtime_World :: struct {
|
||||
runtime_world_init :: proc(runtime_world: ^Runtime_World, num_snapshots: int = 2) {
|
||||
runtime_world.world_snapshots = make([]World, num_snapshots)
|
||||
world := runtime_world_current_world(runtime_world)
|
||||
physics.scene_init(&world.physics_scene, &g_mem.assetman)
|
||||
world.debug_state.show_menu = true
|
||||
world.debug_state.draw_physics_scene = true
|
||||
}
|
||||
@ -125,7 +189,6 @@ Game_Memory :: struct {
|
||||
name_container: name.Container,
|
||||
assetman: assets.Asset_Manager,
|
||||
runtime_world: Runtime_World,
|
||||
fluid_scene: Fluid_Scene,
|
||||
es: Editor_State,
|
||||
ui_context: ui.Context,
|
||||
default_font: rl.Font,
|
||||
@ -169,6 +232,8 @@ world_stack_init :: proc(stack: ^World_Stack, num_snapshots: int, allocator := c
|
||||
assert(num_snapshots > 0)
|
||||
stack.worlds = make([]World, num_snapshots, allocator)
|
||||
world_stack_push(stack)
|
||||
|
||||
world_init(world_stack_current(stack))
|
||||
}
|
||||
|
||||
world_stack_destroy :: proc(stack: ^World_Stack, allocator := context.allocator) {
|
||||
@ -695,30 +760,7 @@ update_world :: proc(world: ^World, dt: f32, config: World_Update_Config) {
|
||||
wheel.turn_angle = TURN_ANGLE * turn_vel_correction * turn_input
|
||||
}
|
||||
|
||||
{
|
||||
level_model := assets.get_model(&g_mem.assetman, "assets/testblend.glb")
|
||||
|
||||
for i in 0 ..< level_model.meshCount {
|
||||
mesh := &level_model.meshes[i]
|
||||
|
||||
if mesh.triangleCount > 0 {
|
||||
assert(mesh.vertexCount <= i32(max(u16)))
|
||||
m := level_model.transform
|
||||
pos := physics.Vec3{m[3][0], m[3][1], m[3][2]}
|
||||
rotation := linalg.quaternion_from_matrix4_f32(auto_cast level_model.transform)
|
||||
physics.immediate_level_geom(
|
||||
&world.physics_scene,
|
||||
hash.fnv32a(transmute([]byte)(fmt.tprintf("level mesh {}", i))),
|
||||
{
|
||||
position = pos,
|
||||
rotation = rotation,
|
||||
vertices = (cast([^]rl.Vector3)mesh.vertices)[:mesh.vertexCount],
|
||||
indices = mesh.indices[:mesh.triangleCount * 3],
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
immediate_scene(world, &world.main_scene, "assets/blender/test_level_blend/Scene.scn")
|
||||
|
||||
if len(world.track.points) > 1 {
|
||||
interpolated_points := calculate_spline_interpolated_points(
|
||||
@ -734,9 +776,11 @@ update_world :: proc(world: ^World, dt: f32, config: World_Update_Config) {
|
||||
#hash("track", "fnv32a"),
|
||||
{
|
||||
rotation = linalg.QUATERNIONF32_IDENTITY,
|
||||
source = physics.Level_Geometry_Mesh {
|
||||
vertices = track_verts,
|
||||
indices = track_inds,
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@ -973,33 +1017,33 @@ update :: proc() {
|
||||
dt := rl.GetFrameTime()
|
||||
|
||||
// Debug BVH traversal
|
||||
mesh_bvh := assets.get_bvh(&g_mem.assetman, "assets/toyota_corolla_ae86_trueno.glb")
|
||||
// mesh_bvh := assets.get_bvh(&g_mem.assetman, "assets/toyota_corolla_ae86_trueno.glb")
|
||||
|
||||
if rl.IsKeyDown(.LEFT_SHIFT) {
|
||||
if g_mem.preview_bvh >= 0 && g_mem.preview_bvh < len(mesh_bvh.bvhs) {
|
||||
b := mesh_bvh.bvhs[g_mem.preview_bvh]
|
||||
node := &b.nodes[g_mem.preview_node]
|
||||
// if rl.IsKeyDown(.LEFT_SHIFT) {
|
||||
// if g_mem.preview_bvh >= 0 && g_mem.preview_bvh < len(mesh_bvh.bvhs) {
|
||||
// b := mesh_bvh.bvhs[g_mem.preview_bvh]
|
||||
// node := &b.nodes[g_mem.preview_node]
|
||||
|
||||
if !bvh.is_leaf_node(node^) {
|
||||
if rl.IsKeyPressed(.LEFT_BRACKET) {
|
||||
g_mem.preview_node = int(node.child_or_prim_start)
|
||||
} else if rl.IsKeyPressed(.RIGHT_BRACKET) {
|
||||
g_mem.preview_node = int(node.child_or_prim_start + 1)
|
||||
} else if rl.IsKeyPressed(.P) {
|
||||
g_mem.preview_node = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if rl.IsKeyPressed(.LEFT_BRACKET) {
|
||||
g_mem.preview_bvh -= 1
|
||||
g_mem.preview_node = 0
|
||||
}
|
||||
if rl.IsKeyPressed(.RIGHT_BRACKET) {
|
||||
g_mem.preview_bvh += 1
|
||||
g_mem.preview_node = 0
|
||||
}
|
||||
}
|
||||
// if !bvh.is_leaf_node(node^) {
|
||||
// if rl.IsKeyPressed(.LEFT_BRACKET) {
|
||||
// g_mem.preview_node = int(node.child_or_prim_start)
|
||||
// } else if rl.IsKeyPressed(.RIGHT_BRACKET) {
|
||||
// g_mem.preview_node = int(node.child_or_prim_start + 1)
|
||||
// } else if rl.IsKeyPressed(.P) {
|
||||
// g_mem.preview_node = 0
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// if rl.IsKeyPressed(.LEFT_BRACKET) {
|
||||
// g_mem.preview_bvh -= 1
|
||||
// g_mem.preview_node = 0
|
||||
// }
|
||||
// if rl.IsKeyPressed(.RIGHT_BRACKET) {
|
||||
// g_mem.preview_bvh += 1
|
||||
// g_mem.preview_node = 0
|
||||
// }
|
||||
// }
|
||||
|
||||
if rl.IsKeyPressed(.SPACE) {
|
||||
g_mem.physics_pause = !g_mem.physics_pause
|
||||
@ -1072,7 +1116,6 @@ draw_world :: proc(world: ^World) {
|
||||
|
||||
sim_state := physics.get_sim_state(&world.physics_scene)
|
||||
|
||||
level_model := assets.get_model(&g_mem.assetman, "assets/testblend.glb")
|
||||
car_model := assets.get_model(&g_mem.assetman, "assets/ice_cream_truck.glb")
|
||||
|
||||
phys_debug_state: physics.Debug_State
|
||||
@ -1161,7 +1204,7 @@ draw_world :: proc(world: ^World) {
|
||||
// .VEC3,
|
||||
// )
|
||||
|
||||
render.draw_model(level_model, {}, 1)
|
||||
scene_draw(&world.main_scene)
|
||||
|
||||
render.draw_model(car_model, {}, car_matrix)
|
||||
|
||||
@ -1429,9 +1472,9 @@ game_init :: proc() {
|
||||
name.init(&g_mem.name_container)
|
||||
name.setup_global_container(&g_mem.name_container)
|
||||
init_physifs_raylib_callbacks()
|
||||
assets.assetman_init(&g_mem.assetman)
|
||||
|
||||
fluid_scene_init(&g_mem.fluid_scene)
|
||||
assets.assetman_init(&g_mem.assetman)
|
||||
assets.init(&g_mem.assetman)
|
||||
|
||||
editor_state_init(&g_mem.es, 100)
|
||||
runtime_world_init(&g_mem.runtime_world, DEV_BUILD ? 100 : 2)
|
||||
@ -1454,7 +1497,6 @@ game_shutdown :: proc() {
|
||||
editor_state_destroy(&g_mem.es)
|
||||
delete(g_mem.es.point_selection)
|
||||
runtime_world_destroy(&g_mem.runtime_world)
|
||||
fluid_scene_destroy(&g_mem.fluid_scene)
|
||||
|
||||
free(g_mem)
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package physics
|
||||
|
||||
import "bvh"
|
||||
import "collision"
|
||||
import "core:log"
|
||||
import "core:math"
|
||||
@ -299,13 +300,14 @@ body_get_convex_shapes_world :: proc(
|
||||
|
||||
level_geom_get_convex_shape :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
level_geom: Level_Geom_Ptr,
|
||||
level_geom_handle: Level_Geom_Handle,
|
||||
tri_idx: int,
|
||||
allocator := context.temp_allocator,
|
||||
) -> (
|
||||
mesh: collision.Convex,
|
||||
) {
|
||||
vertices, indices := get_level_geom_data(sim_state, level_geom.geometry)
|
||||
level_geom := get_level_geom(sim_state, level_geom_handle)
|
||||
vertices, indices := get_level_geom_data(sim_state, level_geom_handle)
|
||||
return collision.double_sided_triangle_to_convex(
|
||||
get_transformed_triangle(vertices, indices, tri_idx, level_geom.x, level_geom.q),
|
||||
allocator,
|
||||
@ -396,3 +398,8 @@ rpm_to_angular_velocity :: proc(rpm: f32) -> f32 {
|
||||
angular_velocity_to_rpm :: proc(w: f32) -> f32 {
|
||||
return (w / (2.0 * math.PI)) * 60.0
|
||||
}
|
||||
|
||||
// TODO: use one format everywhere...
|
||||
bvh_aabb_to_phys_aabb :: proc(aabb: bvh.AABB) -> AABB {
|
||||
return AABB{center = (aabb.max + aabb.min) * 0.5, extent = (aabb.max - aabb.min) * 0.5}
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ import "bvh"
|
||||
import "collision"
|
||||
import "common:name"
|
||||
import lg "core:math/linalg"
|
||||
import "core:strings"
|
||||
import "game:assets"
|
||||
import "game:container/spanpool"
|
||||
import "libs:tracy"
|
||||
|
||||
@ -78,6 +80,7 @@ contact_container_copy :: proc(dst: ^Contact_Container, src: Contact_Container)
|
||||
}
|
||||
|
||||
Sim_State :: struct {
|
||||
scene: ^Scene,
|
||||
bodies: #soa[dynamic]Body,
|
||||
suspension_constraints: #soa[dynamic]Suspension_Constraint,
|
||||
engines: [dynamic]Engine,
|
||||
@ -120,6 +123,7 @@ Sim_State :: struct {
|
||||
}
|
||||
|
||||
Scene :: struct {
|
||||
assetman: ^assets.Asset_Manager,
|
||||
simulation_states: [2]Sim_State,
|
||||
simulation_state_index: i32,
|
||||
solver_state: Solver_State,
|
||||
@ -130,6 +134,7 @@ copy_sim_state :: proc(dst: ^Sim_State, src: ^Sim_State) {
|
||||
tracy.Zone()
|
||||
convex_container_reconcile(&src.convex_container)
|
||||
|
||||
dst.scene = src.scene
|
||||
dst.num_bodies = src.num_bodies
|
||||
dst.first_free_body_plus_one = src.first_free_body_plus_one
|
||||
dst.first_free_suspension_constraint_plus_one = src.first_free_suspension_constraint_plus_one
|
||||
@ -168,12 +173,12 @@ copy_sim_state :: proc(dst: ^Sim_State, src: ^Sim_State) {
|
||||
copy_physics_scene :: proc(dst, src: ^Scene) {
|
||||
tracy.Zone()
|
||||
|
||||
dst.assetman = src.assetman
|
||||
dst.simulation_state_index = src.simulation_state_index
|
||||
src_sim_state := get_sim_state(src)
|
||||
dst_sim_state := get_sim_state(dst)
|
||||
|
||||
copy_sim_state(dst_sim_state, src_sim_state)
|
||||
|
||||
copy_solver_state(&dst.solver_state, &src.solver_state)
|
||||
}
|
||||
|
||||
@ -391,11 +396,24 @@ BLAS_Handle :: struct {
|
||||
primitives: spanpool.Handle,
|
||||
}
|
||||
|
||||
Level_Geom_Source_Local :: struct {
|
||||
geometry: Geometry_Handle,
|
||||
blas: BLAS_Handle,
|
||||
}
|
||||
|
||||
Level_Geom_Source_Asset :: struct {
|
||||
assetpath: name.Name,
|
||||
}
|
||||
|
||||
Level_Geom_Source :: union #no_nil {
|
||||
Level_Geom_Source_Local,
|
||||
Level_Geom_Source_Asset,
|
||||
}
|
||||
|
||||
Level_Geom :: struct {
|
||||
alive: bool,
|
||||
aabb: AABB,
|
||||
geometry: Geometry_Handle,
|
||||
blas: BLAS_Handle,
|
||||
source: Level_Geom_Source,
|
||||
x: Vec3,
|
||||
q: Quat,
|
||||
next_plus_one: i32,
|
||||
@ -584,11 +602,23 @@ Engine_Config :: struct {
|
||||
axle: Drive_Axle_Config,
|
||||
}
|
||||
|
||||
Level_Geometry_Asset :: distinct string
|
||||
|
||||
Level_Geometry_Mesh :: struct {
|
||||
vertices: []Vec3,
|
||||
indices: []u16,
|
||||
}
|
||||
|
||||
// Either runtime mesh or asset reference which will be fetched from assetmanager
|
||||
Level_Geometry_Source :: union #no_nil {
|
||||
Level_Geometry_Mesh,
|
||||
Level_Geometry_Asset,
|
||||
}
|
||||
|
||||
Level_Geom_Config :: struct {
|
||||
position: Vec3,
|
||||
rotation: Quat,
|
||||
vertices: []Vec3,
|
||||
indices: []u16,
|
||||
source: Level_Geometry_Source,
|
||||
}
|
||||
|
||||
calculate_body_params_from_config :: proc(
|
||||
@ -919,20 +949,52 @@ get_level_geom :: proc(sim_state: ^Sim_State, handle: Level_Geom_Handle) -> Leve
|
||||
|
||||
get_level_geom_data :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
handle: Geometry_Handle,
|
||||
handle: Level_Geom_Handle,
|
||||
) -> (
|
||||
vertices: []Vec3,
|
||||
indices: []u16,
|
||||
) {
|
||||
vertices = spanpool.resolve_slice(&sim_state.geometry_vertices_pool, handle.vertices)
|
||||
indices = spanpool.resolve_slice(&sim_state.geometry_indices_pool, handle.indices)
|
||||
level_geom := get_level_geom(sim_state, handle)
|
||||
|
||||
switch s in level_geom.source {
|
||||
case Level_Geom_Source_Local:
|
||||
vertices = spanpool.resolve_slice(&sim_state.geometry_vertices_pool, s.geometry.vertices)
|
||||
indices = spanpool.resolve_slice(&sim_state.geometry_indices_pool, s.geometry.indices)
|
||||
case Level_Geom_Source_Asset:
|
||||
loaded_bvh, reloaded := assets.get_bvh(
|
||||
sim_state.scene.assetman,
|
||||
strings.unsafe_string_to_cstring(name.to_string(s.assetpath)),
|
||||
)
|
||||
if reloaded {
|
||||
level_geom.aabb = bvh_aabb_to_phys_aabb(loaded_bvh.aabb)
|
||||
level_geom.aabb = aabb_transform(level_geom.aabb, level_geom.x, level_geom.q)
|
||||
}
|
||||
vertices = loaded_bvh.vertices
|
||||
indices = loaded_bvh.indices
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
get_level_geom_blas :: proc(sim_state: ^Sim_State, handle: BLAS_Handle) -> (bvh: bvh.BVH) {
|
||||
bvh.nodes = spanpool.resolve_slice(&sim_state.blas_nodes_pool, handle.nodes)
|
||||
bvh.primitives = spanpool.resolve_slice(&sim_state.blas_primitives_pool, handle.primitives)
|
||||
get_level_geom_blas :: proc(sim_state: ^Sim_State, handle: Level_Geom_Handle) -> (bvh: bvh.BVH) {
|
||||
level_geom := get_level_geom(sim_state, handle)
|
||||
switch s in level_geom.source {
|
||||
case Level_Geom_Source_Local:
|
||||
bvh.nodes = spanpool.resolve_slice(&sim_state.blas_nodes_pool, s.blas.nodes)
|
||||
bvh.primitives = spanpool.resolve_slice(&sim_state.blas_primitives_pool, s.blas.primitives)
|
||||
bvh.nodes_used = i32(len(bvh.nodes))
|
||||
case Level_Geom_Source_Asset:
|
||||
loaded_bvh, reloaded := assets.get_bvh(
|
||||
sim_state.scene.assetman,
|
||||
strings.unsafe_string_to_cstring(name.to_string(s.assetpath)),
|
||||
)
|
||||
if reloaded {
|
||||
level_geom.aabb = bvh_aabb_to_phys_aabb(loaded_bvh.aabb)
|
||||
level_geom.aabb = aabb_transform(level_geom.aabb, level_geom.x, level_geom.q)
|
||||
}
|
||||
|
||||
bvh = loaded_bvh.bvh
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -945,32 +1007,36 @@ update_level_geom_from_config :: proc(
|
||||
level_geom.q = config.rotation
|
||||
|
||||
// TODO: figure out if asset changed and rebuild only then
|
||||
|
||||
|
||||
}
|
||||
|
||||
add_level_geom :: proc(sim_state: ^Sim_State, config: Level_Geom_Config) -> Level_Geom_Handle {
|
||||
assert(len(config.vertices) > 0)
|
||||
assert(len(config.vertices) <= int(max(u16)))
|
||||
|
||||
sim_state.num_level_geoms += 1
|
||||
|
||||
level_geom: Level_Geom
|
||||
level_geom.alive = true
|
||||
update_level_geom_from_config(sim_state, &level_geom, config)
|
||||
|
||||
switch s in config.source {
|
||||
case Level_Geometry_Mesh:
|
||||
assert(len(s.vertices) > 0)
|
||||
assert(len(s.vertices) <= int(max(u16)))
|
||||
blas :=
|
||||
bvh.build_bvh_from_mesh(bvh.Mesh{vertices = config.vertices, indices = config.indices}, context.temp_allocator).bvh
|
||||
bvh.build_bvh_from_mesh(bvh.Mesh{vertices = s.vertices, indices = s.indices}, context.temp_allocator).bvh
|
||||
|
||||
level_geom.blas.nodes = spanpool.allocate_elems(
|
||||
source: Level_Geom_Source_Local
|
||||
source.blas.nodes = spanpool.allocate_elems(
|
||||
&sim_state.blas_nodes_pool,
|
||||
..blas.nodes[:blas.nodes_used],
|
||||
)
|
||||
level_geom.blas.primitives = spanpool.allocate_elems(
|
||||
source.blas.primitives = spanpool.allocate_elems(
|
||||
&sim_state.blas_primitives_pool,
|
||||
..blas.primitives,
|
||||
)
|
||||
|
||||
aabb_min, aabb_max: Vec3 = max(f32), min(f32)
|
||||
for v in config.vertices {
|
||||
for v in s.vertices {
|
||||
aabb_min = lg.min(aabb_min, v)
|
||||
aabb_max = lg.max(aabb_max, v)
|
||||
}
|
||||
@ -978,19 +1044,39 @@ add_level_geom :: proc(sim_state: ^Sim_State, config: Level_Geom_Config) -> Leve
|
||||
level_geom.aabb.center = (aabb_max + aabb_min) * 0.5
|
||||
level_geom.aabb.extent = (aabb_max - aabb_min) * 0.5
|
||||
|
||||
if spanpool.is_handle_valid(&sim_state.geometry_vertices_pool, level_geom.geometry.vertices) {
|
||||
spanpool.free(&sim_state.geometry_vertices_pool, level_geom.geometry.vertices)
|
||||
spanpool.free(&sim_state.geometry_indices_pool, level_geom.geometry.indices)
|
||||
// if spanpool.is_handle_valid(
|
||||
// &sim_state.geometry_vertices_pool,
|
||||
// level_geom.geometry.vertices,
|
||||
// ) {
|
||||
// spanpool.free(&sim_state.geometry_vertices_pool, level_geom.geometry.vertices)
|
||||
// spanpool.free(&sim_state.geometry_indices_pool, level_geom.geometry.indices)
|
||||
// }
|
||||
|
||||
source.geometry.vertices = spanpool.allocate_elems(
|
||||
&sim_state.geometry_vertices_pool,
|
||||
..s.vertices,
|
||||
)
|
||||
source.geometry.indices = spanpool.allocate_elems(
|
||||
&sim_state.geometry_indices_pool,
|
||||
..s.indices,
|
||||
)
|
||||
level_geom.source = source
|
||||
case Level_Geometry_Asset:
|
||||
level_geom.source = Level_Geom_Source_Asset {
|
||||
assetpath = name.from_string(string(s)),
|
||||
}
|
||||
|
||||
level_geom.geometry.vertices = spanpool.allocate_elems(
|
||||
&sim_state.geometry_vertices_pool,
|
||||
..config.vertices,
|
||||
)
|
||||
level_geom.geometry.indices = spanpool.allocate_elems(
|
||||
&sim_state.geometry_indices_pool,
|
||||
..config.indices,
|
||||
bvh, _ := assets.get_bvh(
|
||||
sim_state.scene.assetman,
|
||||
strings.unsafe_string_to_cstring(string(s)),
|
||||
)
|
||||
level_geom.aabb = AABB {
|
||||
center = (bvh.aabb.max + bvh.aabb.min) * 0.5,
|
||||
extent = (bvh.aabb.max - bvh.aabb.min) * 0.5,
|
||||
}
|
||||
}
|
||||
|
||||
level_geom.aabb = aabb_transform(level_geom.aabb, level_geom.x, level_geom.q)
|
||||
|
||||
if sim_state.first_free_level_geom_plus_one > 0 {
|
||||
index := sim_state.first_free_level_geom_plus_one
|
||||
@ -1009,11 +1095,15 @@ add_level_geom :: proc(sim_state: ^Sim_State, config: Level_Geom_Config) -> Leve
|
||||
remove_level_geom :: proc(sim_state: ^Sim_State, handle: Level_Geom_Handle) {
|
||||
level_geom := get_level_geom(sim_state, handle)
|
||||
|
||||
spanpool.free(&sim_state.geometry_vertices_pool, level_geom.geometry.vertices)
|
||||
spanpool.free(&sim_state.geometry_indices_pool, level_geom.geometry.indices)
|
||||
switch s in level_geom.source {
|
||||
case Level_Geom_Source_Local:
|
||||
spanpool.free(&sim_state.geometry_vertices_pool, s.geometry.vertices)
|
||||
spanpool.free(&sim_state.geometry_indices_pool, s.geometry.indices)
|
||||
|
||||
spanpool.free(&sim_state.blas_nodes_pool, level_geom.blas.nodes)
|
||||
spanpool.free(&sim_state.blas_primitives_pool, level_geom.blas.primitives)
|
||||
spanpool.free(&sim_state.blas_nodes_pool, s.blas.nodes)
|
||||
spanpool.free(&sim_state.blas_primitives_pool, s.blas.primitives)
|
||||
case Level_Geom_Source_Asset:
|
||||
}
|
||||
}
|
||||
|
||||
_get_first_free_body :: proc(sim_state: ^Sim_State) -> i32 {
|
||||
@ -1039,7 +1129,14 @@ destry_sim_state :: proc(sim_state: ^Sim_State) {
|
||||
dynamic_tlas_destroy(&sim_state.dynamic_tlas)
|
||||
}
|
||||
|
||||
destroy_physics_scene :: proc(scene: ^Scene) {
|
||||
scene_init :: proc(scene: ^Scene, assetman: ^assets.Asset_Manager) {
|
||||
scene.assetman = assetman
|
||||
for &sim_state in scene.simulation_states {
|
||||
sim_state.scene = scene
|
||||
}
|
||||
}
|
||||
|
||||
scene_destroy :: proc(scene: ^Scene) {
|
||||
for &sim_state in scene.simulation_states {
|
||||
destry_sim_state(&sim_state)
|
||||
}
|
||||
|
@ -201,8 +201,8 @@ raycasts_level :: proc(
|
||||
int(tlas.bvh_tree.primitives[leaf_node.child_or_prim_start + j]),
|
||||
)
|
||||
level_geom := get_level_geom(sim_state, level_geom_handle)
|
||||
blas := get_level_geom_blas(sim_state, level_geom.blas)
|
||||
vertices, indices := get_level_geom_data(sim_state, level_geom.geometry)
|
||||
blas := get_level_geom_blas(sim_state, level_geom_handle)
|
||||
vertices, indices := get_level_geom_data(sim_state, level_geom_handle)
|
||||
|
||||
// TODO: transform ray into blas space and back
|
||||
|
||||
@ -211,7 +211,13 @@ raycasts_level :: proc(
|
||||
for k in 0 ..< blas_leaf_node.prim_len {
|
||||
tri_idx := int(blas.primitives[blas_leaf_node.child_or_prim_start + k])
|
||||
|
||||
tri := get_triangle(vertices, indices, tri_idx)
|
||||
tri := get_transformed_triangle(
|
||||
vertices,
|
||||
indices,
|
||||
tri_idx,
|
||||
level_geom.x,
|
||||
level_geom.q,
|
||||
)
|
||||
|
||||
hit_t, tmp_normal, _, ok := collision.intersect_ray_triangle(
|
||||
{origin, origin + dir},
|
||||
@ -388,12 +394,18 @@ remove_invalid_contacts :: proc(
|
||||
|
||||
if !should_remove {
|
||||
tri_idx := int(contact.local_tri_idx)
|
||||
vertices, indices := get_level_geom_data(sim_state, level_geom.geometry)
|
||||
vertices, indices := get_level_geom_data(sim_state, level_geom_handle)
|
||||
should_remove |= tri_idx * 3 >= len(indices)
|
||||
|
||||
if !should_remove {
|
||||
aabb_a := dyn_tlas.body_aabbs[body_handle_to_index(contact.a)]
|
||||
tri := get_triangle(vertices, indices, tri_idx)
|
||||
tri := get_transformed_triangle(
|
||||
vertices,
|
||||
indices,
|
||||
tri_idx,
|
||||
level_geom.x,
|
||||
level_geom.q,
|
||||
)
|
||||
aabb_b := get_triangle_aabb(tri)
|
||||
|
||||
should_remove |= !bvh.test_aabb_vs_aabb(aabb_a, aabb_b)
|
||||
@ -547,11 +559,8 @@ find_new_contacts :: proc(
|
||||
)
|
||||
level_geom := get_level_geom(sim_state, level_geom_handle)
|
||||
if level_geom.alive {
|
||||
blas := get_level_geom_blas(sim_state, level_geom.blas)
|
||||
vertices, indices := get_level_geom_data(
|
||||
sim_state,
|
||||
level_geom.geometry,
|
||||
)
|
||||
blas := get_level_geom_blas(sim_state, level_geom_handle)
|
||||
vertices, indices := get_level_geom_data(sim_state, level_geom_handle)
|
||||
|
||||
blas_it := bvh.iterator_intersect_leaf_aabb(&blas, body_aabb)
|
||||
for blas_leaf_node in bvh.iterator_intersect_leaf_next(&blas_it) {
|
||||
@ -559,7 +568,13 @@ find_new_contacts :: proc(
|
||||
tri_idx := int(
|
||||
blas.primitives[blas_leaf_node.child_or_prim_start + k],
|
||||
)
|
||||
tri := get_triangle(vertices, indices, tri_idx)
|
||||
tri := get_transformed_triangle(
|
||||
vertices,
|
||||
indices,
|
||||
tri_idx,
|
||||
level_geom.x,
|
||||
level_geom.q,
|
||||
)
|
||||
prim_aabb := get_triangle_aabb(tri)
|
||||
|
||||
if bvh.test_aabb_vs_aabb(body_aabb, prim_aabb) {
|
||||
@ -809,8 +824,14 @@ update_contacts :: proc(sim_state: ^Sim_State, static_tlas: ^Static_TLAS) {
|
||||
case .Body_vs_Level:
|
||||
level_geom_handle := Level_Geom_Handle(contact.b)
|
||||
level_geom := get_level_geom(sim_state, level_geom_handle)
|
||||
vertices, indices := get_level_geom_data(sim_state, level_geom.geometry)
|
||||
tri := get_triangle(vertices, indices, int(contact.local_tri_idx))
|
||||
vertices, indices := get_level_geom_data(sim_state, level_geom_handle)
|
||||
tri := get_transformed_triangle(
|
||||
vertices,
|
||||
indices,
|
||||
int(contact.local_tri_idx),
|
||||
level_geom.x,
|
||||
level_geom.q,
|
||||
)
|
||||
|
||||
m2 = collision.double_sided_triangle_to_convex(tri, context.temp_allocator)
|
||||
}
|
||||
|
9
src_assets/blender_assets.cats.txt
Normal file
9
src_assets/blender_assets.cats.txt
Normal file
@ -0,0 +1,9 @@
|
||||
# This is an Asset Catalog Definition file for Blender.
|
||||
#
|
||||
# Empty lines and lines starting with `#` will be ignored.
|
||||
# The first non-ignored line should be the version indicator.
|
||||
# Other lines are of the format "UUID:catalog/path/for/assets:simple catalog name"
|
||||
|
||||
VERSION 1
|
||||
|
||||
65988f84-4b5f-463a-bcae-896eba4975c5:Buildings:Buildings
|
9
src_assets/blender_assets.cats.txt~
Normal file
9
src_assets/blender_assets.cats.txt~
Normal file
@ -0,0 +1,9 @@
|
||||
# This is an Asset Catalog Definition file for Blender.
|
||||
#
|
||||
# Empty lines and lines starting with `#` will be ignored.
|
||||
# The first non-ignored line should be the version indicator.
|
||||
# Other lines are of the format "UUID:catalog/path/for/assets:simple catalog name"
|
||||
|
||||
VERSION 1
|
||||
|
||||
65988f84-4b5f-463a-bcae-896eba4975c5:Buildings:Buildings
|
BIN
src_assets/ice_cream_truck.blend
(Stored with Git LFS)
BIN
src_assets/ice_cream_truck.blend
(Stored with Git LFS)
Binary file not shown.
BIN
src_assets/ice_cream_truck.blend1
(Stored with Git LFS)
BIN
src_assets/ice_cream_truck.blend1
(Stored with Git LFS)
Binary file not shown.
BIN
src_assets/test_level.blend
(Stored with Git LFS)
Normal file
BIN
src_assets/test_level.blend
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src_assets/test_level.blend1
(Stored with Git LFS)
Normal file
BIN
src_assets/test_level.blend1
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src_assets/testblend.blend
(Stored with Git LFS)
BIN
src_assets/testblend.blend
(Stored with Git LFS)
Binary file not shown.
BIN
src_assets/testblend.blend1
(Stored with Git LFS)
BIN
src_assets/testblend.blend1
(Stored with Git LFS)
Binary file not shown.
BIN
src_assets/tools.blend
(Stored with Git LFS)
Normal file
BIN
src_assets/tools.blend
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src_assets/tools.blend1
(Stored with Git LFS)
Normal file
BIN
src_assets/tools.blend1
(Stored with Git LFS)
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user