Basic lelel import working, collisions for level geo broken

This commit is contained in:
sergeypdev 2025-07-13 00:45:35 +04:00
parent a81e81c52c
commit 7da986970e
11 changed files with 319 additions and 81 deletions

BIN
assets/blender/test_level_blend/Plane.glb (Stored with Git LFS)

Binary file not shown.

View File

@ -1,50 +1,45 @@
(inst (inst
:model "assets/blender/testblend_blend/Bakery.glb" :model "assets/blender/testblend_blend/Bakery.glb"
:pos (0.000000 12.000000 0.000000) :pos (0.000000 0.000000 12.000000)
:rot (0.000000 0.000000 0.000000 1.000000) :rot (0.000000 0.000000 0.000000 1.000000)
:scale (1.000000 1.000000 1.000000)) :scale (1.000000 1.000000 1.000000))
(inst (inst
:model "assets/blender/test_level_blend/Plane.glb" :model "assets/blender/test_level_blend/Plane.glb"
:pos (0.000000 0.000000 0.000000) :pos (0.000000 -1.000000 0.000000)
:rot (0.000000 0.000000 0.000000 1.000000) :rot (0.000000 0.000000 0.000000 1.000000)
:scale (22.469545 22.469545 22.469545)) :scale (22.469545 22.469545 22.469545))
(inst (inst
:model "assets/blender/testblend_blend/Fire_Station.glb" :model "assets/blender/testblend_blend/Fire_Station.glb"
:pos (9.000000 13.000000 0.000000) :pos (9.000000 0.000000 13.000000)
:rot (0.000000 0.000000 0.000000 1.000000) :rot (0.000000 0.000000 0.000000 1.000000)
:scale (1.000000 1.000000 1.000000)) :scale (1.000000 1.000000 1.000000))
(inst (inst
:model "assets/blender/testblend_blend/Gas_Station_Shop.glb" :model "assets/blender/testblend_blend/Gas_Station_Shop.glb"
:pos (-8.000000 11.000000 0.000000) :pos (-8.000000 0.000000 11.000000)
:rot (0.000000 0.000000 0.000000 1.000000) :rot (0.000000 0.000000 0.000000 1.000000)
:scale (1.000000 1.000000 1.000000)) :scale (1.000000 1.000000 1.000000))
(inst (inst
:model "assets/blender/testblend_blend/Gas_Station_Shop.glb" :model "assets/blender/testblend_blend/Gas_Station_Shop.glb"
:pos (-16.000000 11.000000 0.000000) :pos (-16.000000 0.000000 11.000000)
:rot (0.000000 0.000000 0.000000 1.000000) :rot (0.000000 0.000000 0.000000 1.000000)
:scale (1.000000 1.000000 1.000000)) :scale (1.000000 1.000000 1.000000))
(inst (inst
:model "assets/blender/testblend_blend/Gas_Station_Shop.glb" :model "assets/blender/testblend_blend/Gas_Station_Shop.glb"
:pos (-15.559284 0.259853 1.609015) :pos (-15.559284 1.609015 0.259853)
:rot (0.000000 0.000000 0.000000 1.000000) :rot (0.000000 0.000000 0.000000 1.000000)
:scale (1.000000 1.000000 1.000000)) :scale (1.000000 1.000000 1.000000))
(inst (inst
:model "assets/blender/testblend_blend/Green_House.glb" :model "assets/blender/testblend_blend/Green_House.glb"
:pos (14.000000 -7.000000 0.000000) :pos (14.000000 0.000000 -7.000000)
:rot (0.000000 0.000000 0.000000 1.000000) :rot (0.000000 0.000000 0.000000 1.000000)
:scale (1.000000 1.000000 1.000000)) :scale (1.000000 1.000000 1.000000))
(inst (inst
:model "assets/blender/testblend_blend/Hotel.glb" :model "assets/blender/testblend_blend/Hotel.glb"
:pos (8.000000 -18.000000 0.000000) :pos (8.000000 0.000000 -18.000000)
:rot (0.000000 0.000000 0.000000 1.000000) :rot (0.000000 0.000000 0.000000 1.000000)
:scale (1.000000 1.000000 1.000000)) :scale (1.000000 1.000000 1.000000))
(inst (inst
:model "assets/blender/test_level_blend/NurbsPath.glb" :model "assets/blender/test_level_blend/NurbsPath.glb"
:pos (0.000000 0.000000 0.000000) :pos (0.000000 0.000000 0.000000)
:rot (0.000000 0.000000 0.000000 1.000000) :rot (0.000000 0.000000 0.000000 1.000000)
:scale (1.000000 1.000000 1.000000))
(inst
:model "assets/blender/ice_cream_truck_blend/Split.glb"
:pos (-1.000000 3.000000 0.000000)
:rot (0.000000 0.000000 0.000000 1.000000)
:scale (1.000000 1.000000 1.000000)) :scale (1.000000 1.000000 1.000000))

View File

@ -70,8 +70,8 @@ for obj in visible_objects:
instance_sexpr = ( instance_sexpr = (
'(inst' '(inst'
f'\n\t:model "assets/{model_path}"' f'\n\t:model "assets/{model_path}"'
f'\n\t:pos ({loc.x:.6f} {loc.y:.6f} {loc.z:.6f}) ' 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: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}))' f'\n\t:scale ({scale.x:.6f} {scale.y:.6f} {scale.z:.6f}))'
) )
instances.append(instance_sexpr) instances.append(instance_sexpr)

View File

@ -4,6 +4,7 @@ import "common:name"
import "core:container/intrusive/list" import "core:container/intrusive/list"
import "core:fmt" import "core:fmt"
import "core:io" import "core:io"
import "core:strconv"
import "core:strings" import "core:strings"
import "core:testing" import "core:testing"
import "core:unicode" import "core:unicode"
@ -151,7 +152,13 @@ parse_atom :: proc(ctx: ^SEXP_Parser) -> (atom: Atom, error: Error) {
result := ctx.data[start:ctx.pos] result := ctx.data[start:ctx.pos]
ctx.pos = next ctx.pos = next
return result, nil 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)) 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 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 { print_list :: proc(it: List_Iterator, w: io.Writer) -> io.Error {
it := it it := it
io.write_byte(w, '(') or_return 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 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)
test_parse :: proc(t: ^testing.T) { test_parse :: proc(t: ^testing.T) {
ctx: SEXP_Parser ctx: SEXP_Parser

View File

@ -81,3 +81,7 @@ to_string :: proc(name: Name) -> string {
sync.atomic_rw_mutex_shared_guard(&global_container.lock) sync.atomic_rw_mutex_shared_guard(&global_container.lock)
return global_container.names_array[name] return global_container.names_array[name]
} }
to_cstring :: proc(name: Name) -> cstring {
return strings.unsafe_string_to_cstring(to_string(name))
}

View File

@ -1,5 +1,7 @@
package assets package assets
import "common:encoding/sexp"
import "common:name"
import "core:log" import "core:log"
import "core:math" import "core:math"
import lg "core:math/linalg" import lg "core:math/linalg"
@ -57,6 +59,18 @@ destroy_loaded_bvh :: proc(loaded_bvh: ^Loaded_BVH) {
Curve_2D :: [][2]f32 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 { Asset_Manager :: struct {
textures: Asset_Cache(rl.Texture2D), textures: Asset_Cache(rl.Texture2D),
models: Asset_Cache(rl.Model), models: Asset_Cache(rl.Model),
@ -65,6 +79,7 @@ Asset_Manager :: struct {
music: Asset_Cache(rl.Music), music: Asset_Cache(rl.Music),
curves_1d: Asset_Cache([]f32), curves_1d: Asset_Cache([]f32),
curves_2d: Asset_Cache(Curve_2D), curves_2d: Asset_Cache(Curve_2D),
scenes: Asset_Cache(Scene_Desc),
bvhs: map[cstring]Loaded_BVH, bvhs: map[cstring]Loaded_BVH,
curves: map[cstring]Loaded_Curve_2D, curves: map[cstring]Loaded_Curve_2D,
} }
@ -203,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_init :: proc(assetman: ^Asset_Manager) {
assetman.models = { assetman.models = {
loader = MODEL_LOADER, loader = MODEL_LOADER,
@ -225,6 +338,9 @@ assetman_init :: proc(assetman: ^Asset_Manager) {
assetman.curves_2d = { assetman.curves_2d = {
loader = CURVE_2D_CSV_LOADER, loader = CURVE_2D_CSV_LOADER,
} }
assetman.scenes = {
loader = SCENE_DESC_LOADER,
}
} }
Asset_Cache_Result :: enum { Asset_Cache_Result :: enum {
@ -238,6 +354,7 @@ assetcache_fetch_or_load :: proc(
ac: ^$T/Asset_Cache($E), ac: ^$T/Asset_Cache($E),
path: cstring, path: cstring,
payload: Asset_Cache_Loader_Payload = nil, payload: Asset_Cache_Loader_Payload = nil,
force_no_reload := false,
) -> ( ) -> (
value: E, value: E,
modtime: physfs.sint64, modtime: physfs.sint64,
@ -250,7 +367,7 @@ assetcache_fetch_or_load :: proc(
if has_existing { if has_existing {
new_modtime := physfs.getLastModTime(path) new_modtime := physfs.getLastModTime(path)
if existing.modtime == new_modtime { if force_no_reload || existing.modtime == new_modtime {
result = .Cached result = .Cached
return existing.value, new_modtime, result return existing.value, new_modtime, result
} else { } else {
@ -408,7 +525,9 @@ get_bvh :: proc(assetman: ^Asset_Manager, path: cstring) -> (Loaded_BVH, bool) {
copy(vertices[vert_count:], mesh_vertices) copy(vertices[vert_count:], mesh_vertices)
for j in 0 ..< len(mesh_indices) { for j in 0 ..< len(mesh_indices) {
indices[indices_count + j] = u16(vert_count) + mesh_indices[j] index := vert_count + int(mesh_indices[j])
assert(index <= int(max(u16)))
indices[indices_count + j] = u16(index)
} }
vert_count += len(mesh_vertices) vert_count += len(mesh_vertices)
@ -446,6 +565,20 @@ get_curve_2d :: proc(assetman: ^Asset_Manager, path: cstring) -> (curve: Curve_2
return 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) { get_convex :: proc(assetman: ^Asset_Manager, path: cstring) -> (result: Loaded_Convex) {
bytes, err := physfs.read_entire_file(string(path), context.temp_allocator) bytes, err := physfs.read_entire_file(string(path), context.temp_allocator)
if err != nil { if err != nil {
@ -852,6 +985,7 @@ shutdown :: proc(assetman: ^Asset_Manager) {
assetcache_destroy(&assetman.music) assetcache_destroy(&assetman.music)
assetcache_destroy(&assetman.curves_1d) assetcache_destroy(&assetman.curves_1d)
assetcache_destroy(&assetman.curves_2d) assetcache_destroy(&assetman.curves_2d)
assetcache_destroy(&assetman.scenes)
for _, &loaded_bvh in assetman.bvhs { for _, &loaded_bvh in assetman.bvhs {
destroy_loaded_bvh(&loaded_bvh) destroy_loaded_bvh(&loaded_bvh)

View File

@ -17,7 +17,6 @@ package game
import "assets" import "assets"
import "common:name" import "common:name"
import "core:fmt" import "core:fmt"
import "core:hash"
import "core:log" import "core:log"
import "core:math" import "core:math"
import "core:math/linalg" import "core:math/linalg"
@ -48,6 +47,65 @@ Debug_Draw_State :: struct {
draw_physics_scene: bool, 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 { World :: struct {
player_pos: rl.Vector3, player_pos: rl.Vector3,
track: Track, track: Track,
@ -57,6 +115,7 @@ World :: struct {
car_handle: physics.Body_Handle, car_handle: physics.Body_Handle,
engine_handle: physics.Engine_Handle, engine_handle: physics.Engine_Handle,
debug_state: Debug_Draw_State, debug_state: Debug_Draw_State,
main_scene: Scene,
} }
world_init :: proc(world: ^World) { world_init :: proc(world: ^World) {
physics.scene_init(&world.physics_scene, &g_mem.assetman) physics.scene_init(&world.physics_scene, &g_mem.assetman)
@ -64,6 +123,7 @@ world_init :: proc(world: ^World) {
copy_world :: proc(dst, src: ^World) { copy_world :: proc(dst, src: ^World) {
copy_track(&dst.track, &src.track) copy_track(&dst.track, &src.track)
physics.copy_physics_scene(&dst.physics_scene, &src.physics_scene) 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.player_pos = src.player_pos
dst.pause = src.pause dst.pause = src.pause
dst.car_com = src.car_com dst.car_com = src.car_com
@ -73,6 +133,7 @@ copy_world :: proc(dst, src: ^World) {
} }
destroy_world :: proc(world: ^World) { destroy_world :: proc(world: ^World) {
destroy_track(&world.track) destroy_track(&world.track)
scene_destroy(world, &world.main_scene)
physics.scene_destroy(&world.physics_scene) physics.scene_destroy(&world.physics_scene)
} }
@ -699,46 +760,7 @@ update_world :: proc(world: ^World, dt: f32, config: World_Update_Config) {
wheel.turn_angle = TURN_ANGLE * turn_vel_correction * turn_input wheel.turn_angle = TURN_ANGLE * turn_vel_correction * turn_input
} }
{ immediate_scene(world, &world.main_scene, "assets/blender/test_level_blend/Scene.scn")
physics.immediate_level_geom(
&world.physics_scene,
u32(name.from_string("level_geom_from_asset")),
{
position = physics.Vec3{5, 0, 0},
rotation = linalg.QUATERNIONF32_IDENTITY,
source = physics.Level_Geometry_Asset(
"assets/blender/test_level_blend/NurbsPath.glb",
),
},
)
}
{
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,
source = physics.Level_Geometry_Mesh {
vertices = (cast([^]rl.Vector3)mesh.vertices)[:mesh.vertexCount],
indices = mesh.indices[:mesh.triangleCount * 3],
},
},
)
}
}
}
if len(world.track.points) > 1 { if len(world.track.points) > 1 {
interpolated_points := calculate_spline_interpolated_points( interpolated_points := calculate_spline_interpolated_points(
@ -1094,7 +1116,6 @@ draw_world :: proc(world: ^World) {
sim_state := physics.get_sim_state(&world.physics_scene) 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") car_model := assets.get_model(&g_mem.assetman, "assets/ice_cream_truck.glb")
phys_debug_state: physics.Debug_State phys_debug_state: physics.Debug_State
@ -1183,7 +1204,7 @@ draw_world :: proc(world: ^World) {
// .VEC3, // .VEC3,
// ) // )
render.draw_model(level_model, {}, 1) scene_draw(&world.main_scene)
render.draw_model(car_model, {}, car_matrix) render.draw_model(car_model, {}, car_matrix)

View File

@ -967,6 +967,7 @@ get_level_geom_data :: proc(
) )
if reloaded { if reloaded {
level_geom.aabb = bvh_aabb_to_phys_aabb(loaded_bvh.aabb) 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 vertices = loaded_bvh.vertices
indices = loaded_bvh.indices indices = loaded_bvh.indices
@ -989,6 +990,7 @@ get_level_geom_blas :: proc(sim_state: ^Sim_State, handle: Level_Geom_Handle) ->
) )
if reloaded { if reloaded {
level_geom.aabb = bvh_aabb_to_phys_aabb(loaded_bvh.aabb) 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 bvh = loaded_bvh.bvh
@ -1074,6 +1076,8 @@ add_level_geom :: proc(sim_state: ^Sim_State, config: Level_Geom_Config) -> Leve
} }
} }
level_geom.aabb = aabb_transform(level_geom.aabb, level_geom.x, level_geom.q)
if sim_state.first_free_level_geom_plus_one > 0 { if sim_state.first_free_level_geom_plus_one > 0 {
index := sim_state.first_free_level_geom_plus_one index := sim_state.first_free_level_geom_plus_one
new_level_geom := get_level_geom(sim_state, Level_Geom_Handle(index)) new_level_geom := get_level_geom(sim_state, Level_Geom_Handle(index))

View File

@ -200,6 +200,7 @@ raycasts_level :: proc(
level_geom_handle := index_to_level_geom( level_geom_handle := index_to_level_geom(
int(tlas.bvh_tree.primitives[leaf_node.child_or_prim_start + j]), 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_handle) blas := get_level_geom_blas(sim_state, level_geom_handle)
vertices, indices := get_level_geom_data(sim_state, level_geom_handle) vertices, indices := get_level_geom_data(sim_state, level_geom_handle)
@ -210,7 +211,13 @@ raycasts_level :: proc(
for k in 0 ..< blas_leaf_node.prim_len { for k in 0 ..< blas_leaf_node.prim_len {
tri_idx := int(blas.primitives[blas_leaf_node.child_or_prim_start + k]) 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( hit_t, tmp_normal, _, ok := collision.intersect_ray_triangle(
{origin, origin + dir}, {origin, origin + dir},
@ -392,7 +399,13 @@ remove_invalid_contacts :: proc(
if !should_remove { if !should_remove {
aabb_a := dyn_tlas.body_aabbs[body_handle_to_index(contact.a)] 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) aabb_b := get_triangle_aabb(tri)
should_remove |= !bvh.test_aabb_vs_aabb(aabb_a, aabb_b) should_remove |= !bvh.test_aabb_vs_aabb(aabb_a, aabb_b)
@ -555,7 +568,13 @@ find_new_contacts :: proc(
tri_idx := int( tri_idx := int(
blas.primitives[blas_leaf_node.child_or_prim_start + k], 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) prim_aabb := get_triangle_aabb(tri)
if bvh.test_aabb_vs_aabb(body_aabb, prim_aabb) { if bvh.test_aabb_vs_aabb(body_aabb, prim_aabb) {
@ -804,8 +823,15 @@ update_contacts :: proc(sim_state: ^Sim_State, static_tlas: ^Static_TLAS) {
) )
case .Body_vs_Level: case .Body_vs_Level:
level_geom_handle := Level_Geom_Handle(contact.b) 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_handle) vertices, indices := get_level_geom_data(sim_state, level_geom_handle)
tri := get_triangle(vertices, indices, int(contact.local_tri_idx)) 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) m2 = collision.double_sided_triangle_to_convex(tri, context.temp_allocator)
} }

BIN
src_assets/test_level.blend (Stored with Git LFS)

Binary file not shown.

BIN
src_assets/test_level.blend1 (Stored with Git LFS)

Binary file not shown.