Compound shapes support, better debug ui
This commit is contained in:
parent
c83e6831ea
commit
8378b943cb
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@ -5,7 +5,7 @@
|
||||
"type": "lldb",
|
||||
"request": "attach",
|
||||
"name": "Attach Hot Reload (Linux/Max)",
|
||||
"program": "${workspaceFolder}/game_hot_reload.bin"
|
||||
"program": "${workspaceFolder}/bin/hotreload/game.bin"
|
||||
},
|
||||
// Windows configs (only difference from linux/mac is "type" and "program")
|
||||
{
|
||||
@ -53,7 +53,7 @@
|
||||
"name": "Run Hot Reload (Linux / Mac)",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"program": "${workspaceFolder}/build/hotreload/game.bin",
|
||||
"program": "${workspaceFolder}/bin/hotreload/game.bin",
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
|
@ -238,7 +238,7 @@ main :: proc() {
|
||||
opts := Options {
|
||||
tracy = true,
|
||||
debug = true,
|
||||
optimize = true,
|
||||
optimize = false,
|
||||
run = true,
|
||||
}
|
||||
flags.parse_or_exit(&opts, os.args, .Unix, context.temp_allocator)
|
||||
@ -352,7 +352,7 @@ main :: proc() {
|
||||
"-sWASM_BIGINT",
|
||||
"-sWARN_ON_UNDEFINED_SYMBOLS=0",
|
||||
"-sALLOW_MEMORY_GROWTH",
|
||||
"-sASSERTIONS",
|
||||
"-sASSERTIONS=2",
|
||||
"--shell-file",
|
||||
"main_web/index_template.html",
|
||||
"--preload-file",
|
||||
|
@ -27,6 +27,7 @@ Loaded_Convex :: struct {
|
||||
mesh: collision.Convex,
|
||||
center_of_mass: rl.Vector3,
|
||||
inertia_tensor: lg.Matrix3f32,
|
||||
total_volume: f32,
|
||||
}
|
||||
|
||||
Loaded_Curve_2D :: struct {
|
||||
@ -670,9 +671,14 @@ get_convex :: proc(assetman: ^Asset_Manager, path: cstring) -> (result: Loaded_C
|
||||
}
|
||||
}
|
||||
}
|
||||
inertia_tensor = inertia_tensor * lg.Matrix3f32(1.0 / total_volume)
|
||||
inertia_tensor = inertia_tensor
|
||||
|
||||
return {mesh = mesh, center_of_mass = center_of_mass, inertia_tensor = inertia_tensor}
|
||||
return {
|
||||
mesh = mesh,
|
||||
center_of_mass = center_of_mass,
|
||||
inertia_tensor = inertia_tensor,
|
||||
total_volume = total_volume,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move convex stuff out of assets.odin
|
||||
|
@ -39,7 +39,7 @@ init_physfs :: proc(args: []string) {
|
||||
// For non web builds, binary will live 2 subdirs below main content dir, so add it to search path
|
||||
when ODIN_OS != .JS {
|
||||
abs_path, ok := path_abs(args[0], context.temp_allocator)
|
||||
assert(ok)
|
||||
assert(ok)
|
||||
base_dir := path_dir(abs_path, 3)
|
||||
err := physfs.mount(strings.clone_to_cstring(base_dir, context.temp_allocator), "/", 1)
|
||||
|
||||
|
@ -27,6 +27,7 @@ import "game:render"
|
||||
import rl "libs:raylib"
|
||||
import "libs:raylib/rlgl"
|
||||
import "libs:tracy"
|
||||
import "name"
|
||||
import "ui"
|
||||
|
||||
PIXEL_WINDOW_HEIGHT :: 360
|
||||
@ -122,6 +123,7 @@ SOLVER_CONFIG :: physics.Solver_Config {
|
||||
}
|
||||
|
||||
Game_Memory :: struct {
|
||||
name_container: name.Container,
|
||||
assetman: assets.Asset_Manager,
|
||||
runtime_world: Runtime_World,
|
||||
es: Editor_State,
|
||||
@ -369,9 +371,15 @@ update_world :: proc(world: ^World, dt: f32, config: World_Update_Config) {
|
||||
&world.physics_scene,
|
||||
#hash("floor", "fnv32a"),
|
||||
physics.Body_Config {
|
||||
name = name.from_string("Floor"),
|
||||
initial_pos = {0, -0.5, 0},
|
||||
initial_rot = linalg.QUATERNIONF32_IDENTITY,
|
||||
shape = physics.Shape_Box{size = {100, 1, 100}},
|
||||
shapes = {
|
||||
{
|
||||
rel_q = linalg.QUATERNIONF32_IDENTITY,
|
||||
inner_shape = physics.Shape_Box{size = {100, 1, 100}},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
@ -379,9 +387,15 @@ update_world :: proc(world: ^World, dt: f32, config: World_Update_Config) {
|
||||
&world.physics_scene,
|
||||
#hash("ramp", "fnv32a"),
|
||||
physics.Body_Config {
|
||||
name = name.from_string("Ramp"),
|
||||
initial_pos = {0, 0, 0},
|
||||
initial_rot = linalg.quaternion_from_euler_angle_x_f32(-10 * math.RAD_PER_DEG),
|
||||
shape = physics.Shape_Box{size = {5, 1, 100}},
|
||||
shapes = {
|
||||
{
|
||||
rel_q = linalg.QUATERNIONF32_IDENTITY,
|
||||
inner_shape = physics.Shape_Box{size = {5, 1, 100}},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
@ -392,20 +406,33 @@ update_world :: proc(world: ^World, dt: f32, config: World_Update_Config) {
|
||||
&world.physics_scene,
|
||||
#hash("car", "fnv32a"),
|
||||
physics.Body_Config {
|
||||
initial_pos = {0, 4, -10},
|
||||
initial_rot = linalg.QUATERNIONF32_IDENTITY,
|
||||
name = name.from_string("Car"),
|
||||
initial_pos = {0, 4, -10},
|
||||
initial_rot = linalg.QUATERNIONF32_IDENTITY,
|
||||
// initial_rot = linalg.quaternion_angle_axis(
|
||||
// math.RAD_PER_DEG * 180,
|
||||
// rl.Vector3{0, 0, 1},
|
||||
// ) *
|
||||
// linalg.quaternion_angle_axis(math.RAD_PER_DEG * 30, rl.Vector3{1, 0, 0}),
|
||||
initial_ang_vel = {0, 0, 0},
|
||||
shape = physics.Shape_Convex {
|
||||
mesh = car_convex.mesh,
|
||||
center_of_mass = car_convex.center_of_mass,
|
||||
inertia_tensor = auto_cast car_convex.inertia_tensor,
|
||||
shapes = {
|
||||
{
|
||||
rel_q = linalg.QUATERNIONF32_IDENTITY,
|
||||
inner_shape = physics.Shape_Convex {
|
||||
mesh = car_convex.mesh,
|
||||
center_of_mass = car_convex.center_of_mass,
|
||||
inertia_tensor = auto_cast car_convex.inertia_tensor,
|
||||
total_volume = car_convex.total_volume,
|
||||
},
|
||||
},
|
||||
{
|
||||
rel_x = {0, 1, 0},
|
||||
rel_q = linalg.QUATERNIONF32_IDENTITY,
|
||||
density_minus_one = 0.1 - 1,
|
||||
inner_shape = physics.Shape_Box{size = 1},
|
||||
},
|
||||
},
|
||||
mass = 1000,
|
||||
mass = 1000,
|
||||
},
|
||||
)
|
||||
|
||||
@ -416,9 +443,14 @@ update_world :: proc(world: ^World, dt: f32, config: World_Update_Config) {
|
||||
&world.physics_scene,
|
||||
hash.fnv32a(slice.to_bytes([]int{(x | y << 8)})),
|
||||
physics.Body_Config {
|
||||
initial_pos = {5, 0.5 + f32(y) * 1.1, f32(x) * 3 + 10},
|
||||
initial_pos = {-5, 0.5 + f32(y) * 1.1, f32(x) * 3 + 10},
|
||||
initial_rot = linalg.QUATERNIONF32_IDENTITY,
|
||||
shape = physics.Shape_Box{size = 1},
|
||||
shapes = {
|
||||
{
|
||||
rel_q = linalg.QUATERNIONF32_IDENTITY,
|
||||
inner_shape = physics.Shape_Box{size = 1},
|
||||
},
|
||||
},
|
||||
mass = 10,
|
||||
},
|
||||
)
|
||||
@ -986,23 +1018,22 @@ draw_world :: proc(world: ^World) {
|
||||
if .ACTIVE in ui.header(ui_ctx, "Car", {.EXPANDED}) &&
|
||||
car_body.alive &&
|
||||
engine.alive {
|
||||
ui_keyval :: proc(ctx: ^ui.Context, key: string, val: any) {
|
||||
ui.layout_row(ctx, {100, -1}, 0)
|
||||
ui.label(ctx, key)
|
||||
ui.label(ctx, fmt.tprintf("%v", val))
|
||||
}
|
||||
|
||||
gear_ratios := physics.get_gear_ratios(sim_state, engine.gear_ratios)
|
||||
ui.layout_row(ui_ctx, {100, -1}, 0)
|
||||
ui.layout_row(ui_ctx, {100, -1}, 0)
|
||||
ui_keyval(ui_ctx, "p", car_body.x)
|
||||
ui_keyval(ui_ctx, "v", car_body.v)
|
||||
ui_keyval(ui_ctx, "gear", engine.gear)
|
||||
ui_keyval(ui_ctx, "ratio", physics.lookup_gear_ratio(gear_ratios, engine.gear))
|
||||
ui_keyval(ui_ctx, "rpm", physics.angular_velocity_to_rpm(engine.w))
|
||||
ui_keyval(ui_ctx, "clutch", engine.clutch)
|
||||
ui_keyval(ui_ctx, "speed", linalg.length(car_body.v) * 3.6)
|
||||
ui.keyval(ui_ctx, "p", car_body.x)
|
||||
ui.keyval(ui_ctx, "v", car_body.v)
|
||||
ui.keyval(ui_ctx, "gear", engine.gear)
|
||||
ui.keyval(ui_ctx, "ratio", physics.lookup_gear_ratio(gear_ratios, engine.gear))
|
||||
ui.keyval(ui_ctx, "rpm", physics.angular_velocity_to_rpm(engine.w))
|
||||
ui.keyval(ui_ctx, "clutch", engine.clutch)
|
||||
ui.keyval(ui_ctx, "speed", linalg.length(car_body.v) * 3.6)
|
||||
}
|
||||
|
||||
if .ACTIVE in ui.header(ui_ctx, "Physics") {
|
||||
physics.draw_debug_ui(ui_ctx, &world.physics_scene, SOLVER_CONFIG)
|
||||
}
|
||||
} else {
|
||||
log.infof("Window closed")
|
||||
world.debug_state.show_menu = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1291,6 +1322,8 @@ game_init :: proc() {
|
||||
|
||||
g_mem^ = Game_Memory{}
|
||||
|
||||
name.init(&g_mem.name_container)
|
||||
name.setup_global_container(&g_mem.name_container)
|
||||
init_physifs_raylib_callbacks()
|
||||
assets.assetman_init(&g_mem.assetman)
|
||||
|
||||
@ -1310,6 +1343,7 @@ game_init :: proc() {
|
||||
|
||||
@(export)
|
||||
game_shutdown :: proc() {
|
||||
name.destroy()
|
||||
assets.shutdown(&g_mem.assetman)
|
||||
editor_state_destroy(&g_mem.es)
|
||||
delete(g_mem.es.point_selection)
|
||||
@ -1339,6 +1373,7 @@ game_memory_size :: proc() -> int {
|
||||
game_hot_reloaded :: proc(mem: rawptr) {
|
||||
g_mem = (^Game_Memory)(mem)
|
||||
|
||||
name.setup_global_container(&g_mem.name_container)
|
||||
render.init(&g_mem.assetman)
|
||||
ui.rl_init()
|
||||
|
||||
|
@ -4,6 +4,7 @@ import "core:hash/xxhash"
|
||||
import lg "core:math/linalg"
|
||||
import "core:mem"
|
||||
import "core:slice"
|
||||
import "libs:tracy"
|
||||
|
||||
Vec3 :: [3]f32
|
||||
|
||||
@ -43,6 +44,7 @@ mesh_from_vertex_index_list :: proc(
|
||||
vertices_per_face: int = 3,
|
||||
allocator := context.allocator,
|
||||
) -> Half_Edge_Mesh {
|
||||
tracy.Zone()
|
||||
assert(vertices_per_face >= 3)
|
||||
|
||||
num_faces := len(indices) / vertices_per_face
|
||||
@ -277,6 +279,7 @@ copy_mesh :: proc(
|
||||
|
||||
|
||||
transform_mesh :: proc(mesh: ^Half_Edge_Mesh, mat: lg.Matrix4f32) {
|
||||
tracy.Zone()
|
||||
mesh_center_avg_factor := 1.0 / f32(len(mesh.vertices))
|
||||
new_center: Vec3
|
||||
for i in 0 ..< len(mesh.vertices) {
|
||||
|
62
game/name/name.odin
Normal file
62
game/name/name.odin
Normal file
@ -0,0 +1,62 @@
|
||||
// String interning thingy
|
||||
|
||||
package name
|
||||
|
||||
import "core:mem"
|
||||
import "core:strings"
|
||||
import "core:sync"
|
||||
|
||||
Name :: distinct u32
|
||||
|
||||
NONE :: Name(0)
|
||||
|
||||
Container :: struct {
|
||||
lock: sync.Atomic_RW_Mutex,
|
||||
names_lookup: map[string]Name,
|
||||
names_allocator: mem.Dynamic_Arena,
|
||||
names_array: [dynamic]string,
|
||||
}
|
||||
|
||||
@(private = "file")
|
||||
global_container: ^Container
|
||||
|
||||
setup_global_container :: proc(cnt: ^Container) {
|
||||
global_container = cnt
|
||||
}
|
||||
|
||||
init :: proc(cnt: ^Container) {
|
||||
mem.dynamic_arena_init(&cnt.names_allocator)
|
||||
assert(len(cnt.names_array) == 0)
|
||||
append(&cnt.names_array, "None")
|
||||
}
|
||||
|
||||
destroy :: proc() {
|
||||
assert(global_container != nil)
|
||||
|
||||
delete(global_container.names_array)
|
||||
delete(global_container.names_lookup)
|
||||
mem.dynamic_arena_destroy(&global_container.names_allocator)
|
||||
global_container = nil
|
||||
}
|
||||
|
||||
from_string :: proc(str: string) -> Name {
|
||||
sync.atomic_rw_mutex_guard(&global_container.lock)
|
||||
existing, ok := global_container.names_lookup[str]
|
||||
if ok {
|
||||
return existing
|
||||
} else {
|
||||
new_str := strings.clone(
|
||||
str,
|
||||
mem.dynamic_arena_allocator(&global_container.names_allocator),
|
||||
)
|
||||
idx := u32(len(global_container.names_array))
|
||||
append(&global_container.names_array, new_str)
|
||||
global_container.names_lookup[str] = Name(idx)
|
||||
return Name(idx)
|
||||
}
|
||||
}
|
||||
|
||||
to_string :: proc(name: Name) -> string {
|
||||
sync.atomic_rw_mutex_shared_guard(&global_container.lock)
|
||||
return global_container.names_array[name]
|
||||
}
|
@ -6,9 +6,10 @@ import "core:fmt"
|
||||
import "core:log"
|
||||
import lg "core:math/linalg"
|
||||
import "game:debug"
|
||||
import "libs:tracy"
|
||||
import "game:ui"
|
||||
import rl "libs:raylib"
|
||||
import "libs:raylib/rlgl"
|
||||
import "libs:tracy"
|
||||
|
||||
_ :: fmt
|
||||
_ :: log
|
||||
@ -145,6 +146,36 @@ debug_draw_bvh_bounds :: proc(bvh: ^BVH, primitive_bounds: []AABB, node_index: i
|
||||
}
|
||||
}
|
||||
|
||||
debug_ui_bvh_node_recursive :: proc(
|
||||
ctx: ^ui.Context,
|
||||
bvh: BVH,
|
||||
primitive_bounds: []AABB,
|
||||
node_idx: i32,
|
||||
) {
|
||||
if .ACTIVE in ui.treenode(ctx, fmt.tprintf("Node %v", node_idx)) {
|
||||
node := bvh.nodes[node_idx]
|
||||
if !is_leaf_node(node) {
|
||||
debug_ui_bvh_node_recursive(ctx, bvh, primitive_bounds, node.child_or_prim_start)
|
||||
debug_ui_bvh_node_recursive(ctx, bvh, primitive_bounds, node.child_or_prim_start + 1)
|
||||
} else {
|
||||
for i in node.child_or_prim_start ..< node.child_or_prim_start + node.prim_len {
|
||||
prim := bvh.primitives[i]
|
||||
aabb := primitive_bounds[prim]
|
||||
|
||||
ui.layout_row(ctx, {-1})
|
||||
ui.layout_next(ctx)
|
||||
ui.label(ctx, fmt.tprintf("Child: %v", aabb))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug_ui_bvh_tree :: proc(ctx: ^ui.Context, bvh: BVH, primitive_bounds: []AABB) {
|
||||
if .ACTIVE in ui.treenode(ctx, "BVH") {
|
||||
debug_ui_bvh_node_recursive(ctx, bvh, primitive_bounds, 0)
|
||||
}
|
||||
}
|
||||
|
||||
bvh_mesh_from_rl_mesh :: proc(mesh: rl.Mesh) -> Mesh {
|
||||
return Mesh {
|
||||
vertices = (cast([^]Vec3)mesh.vertices)[:mesh.vertexCount],
|
||||
|
@ -16,7 +16,7 @@ _ :: log
|
||||
|
||||
Convex :: halfedge.Half_Edge_Mesh
|
||||
|
||||
BOX_CORNERS_NORM :: [8]Vec3 {
|
||||
box_corners_norm := [8]Vec3 {
|
||||
{-1, 1, 1},
|
||||
{-1, -1, 1},
|
||||
{-1, 1, -1},
|
||||
@ -29,15 +29,28 @@ BOX_CORNERS_NORM :: [8]Vec3 {
|
||||
|
||||
box_indices := [6 * 4]u16{0, 4, 6, 2, 3, 2, 6, 7, 7, 6, 4, 5, 5, 1, 3, 7, 1, 0, 2, 3, 5, 4, 0, 1}
|
||||
|
||||
@(private = "file")
|
||||
box_mesh: Convex
|
||||
|
||||
@(init)
|
||||
init_box_mesh :: proc() {
|
||||
box_mesh = Convex(halfedge.mesh_from_vertex_index_list(box_corners_norm[:], box_indices[:], 4))
|
||||
}
|
||||
|
||||
@(fini)
|
||||
deinit_box_mesh :: proc() {
|
||||
delete(box_mesh.vertices)
|
||||
delete(box_mesh.faces)
|
||||
delete(box_mesh.edges)
|
||||
}
|
||||
|
||||
box_to_convex :: proc(box: Box, allocator := context.allocator) -> (convex: Convex) {
|
||||
vertices := make([]Vec3, 8, context.temp_allocator)
|
||||
convex = halfedge.copy_mesh(box_mesh, allocator)
|
||||
|
||||
for corner, i in BOX_CORNERS_NORM {
|
||||
vertices[i] = box.pos + corner * box.rad
|
||||
for &v in convex.vertices {
|
||||
v.pos = box.pos + v.pos * box.rad
|
||||
}
|
||||
|
||||
convex = Convex(halfedge.mesh_from_vertex_index_list(vertices, box_indices[:], 4, allocator))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@ -649,5 +662,9 @@ ray_vs_convex :: proc(
|
||||
}
|
||||
}
|
||||
|
||||
if hit {
|
||||
normal = lg.normalize0(normal)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -1,16 +1,23 @@
|
||||
package physics
|
||||
|
||||
import "bvh"
|
||||
import "core:fmt"
|
||||
import "core:log"
|
||||
import "core:math"
|
||||
import lg "core:math/linalg"
|
||||
import "core:mem"
|
||||
import "core:strings"
|
||||
import "game:debug"
|
||||
import he "game:halfedge"
|
||||
import "game:name"
|
||||
import "game:ui"
|
||||
import rl "libs:raylib"
|
||||
import "libs:raylib/rlgl"
|
||||
import "libs:tracy"
|
||||
|
||||
_ :: name
|
||||
_ :: mem
|
||||
_ :: fmt
|
||||
_ :: log
|
||||
_ :: math
|
||||
_ :: debug
|
||||
@ -24,6 +31,8 @@ draw_debug_shape :: proc(
|
||||
color: rl.Color,
|
||||
) {
|
||||
mat := lg.matrix4_from_trs(pos, rot, 1)
|
||||
shape_mat := lg.matrix4_translate_f32(shape.rel_x) * lg.matrix4_from_quaternion(shape.rel_q)
|
||||
mat *= auto_cast shape_mat
|
||||
|
||||
rlgl.PushMatrix()
|
||||
defer rlgl.PopMatrix()
|
||||
@ -31,7 +40,7 @@ draw_debug_shape :: proc(
|
||||
rlgl.LoadIdentity()
|
||||
rlgl.MultMatrixf(cast([^]f32)&mat)
|
||||
|
||||
switch s in shape {
|
||||
switch s in shape.inner_shape {
|
||||
case Shape_Box:
|
||||
rl.DrawCubeV(0, s.size, color)
|
||||
case Internal_Shape_Convex:
|
||||
@ -45,6 +54,15 @@ draw_debug_scene :: proc(scene: ^Scene) {
|
||||
|
||||
sim_state := get_sim_state(scene)
|
||||
|
||||
// Dynamic TLAS
|
||||
if true && sim_state.dynamic_tlas.built {
|
||||
bvh.debug_draw_bvh_bounds(
|
||||
&sim_state.dynamic_tlas.bvh_tree,
|
||||
sim_state.dynamic_tlas.body_aabbs,
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
for _, i in sim_state.bodies {
|
||||
body := &sim_state.bodies_slice[i]
|
||||
if body.alive {
|
||||
@ -59,13 +77,25 @@ draw_debug_scene :: proc(scene: ^Scene) {
|
||||
rl.DrawLine3D(pos, pos + y, rl.GREEN)
|
||||
rl.DrawLine3D(pos, pos + z, rl.BLUE)
|
||||
|
||||
draw_debug_shape(
|
||||
sim_state,
|
||||
body.shape,
|
||||
body_get_shape_pos(body),
|
||||
body.q,
|
||||
debug.int_to_color(i32(i + 2)),
|
||||
)
|
||||
it := shapes_iterator(sim_state, body.shape)
|
||||
for shape in shapes_iterator_next(&it) {
|
||||
draw_debug_shape(
|
||||
sim_state,
|
||||
shape^,
|
||||
body_get_shape_pos(body),
|
||||
body.q,
|
||||
debug.int_to_color(i32(i + 2)),
|
||||
)
|
||||
|
||||
shape_aabb := body_transform_shape_aabb(body, shape_get_aabb(shape^))
|
||||
rl.DrawBoundingBox(
|
||||
rl.BoundingBox {
|
||||
min = shape_aabb.center - shape_aabb.extent,
|
||||
max = shape_aabb.center + shape_aabb.extent,
|
||||
},
|
||||
debug.int_to_color(i32(i + 2)),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,133 +198,190 @@ draw_debug_ui :: proc(ctx: ^ui.Context, scene: ^Scene, config: Solver_Config) {
|
||||
|
||||
sim_state := get_sim_state(scene)
|
||||
|
||||
active_wheels := []int{0, 1}
|
||||
|
||||
w, h: i32 = 500, 500
|
||||
|
||||
window_x: i32 = 0
|
||||
|
||||
for i in 0 ..< len(sim_state.suspension_constraints_slice) {
|
||||
s := &sim_state.suspension_constraints_slice[i]
|
||||
|
||||
if s.alive {
|
||||
for idx in active_wheels {
|
||||
if i == idx {
|
||||
if ui.window(
|
||||
ctx,
|
||||
fmt.tprintf("Wheel %v", i),
|
||||
ui.Rect{x = window_x, y = 0, w = w, h = h},
|
||||
ui.Options{},
|
||||
) {
|
||||
NUM_SAMPLES :: 100
|
||||
|
||||
dt := f32(config.timestep) / f32(config.substreps_minus_one + 1)
|
||||
inv_dt := 1.0 / dt
|
||||
|
||||
{
|
||||
ui.layout_row(ctx, {-1}, 300)
|
||||
{
|
||||
ui.begin_line(ctx, ui.Color{255, 0, 0, 255})
|
||||
defer ui.end_line(ctx)
|
||||
|
||||
for j in 0 ..< NUM_SAMPLES {
|
||||
alpha := f32(j) / f32(NUM_SAMPLES - 1)
|
||||
x := alpha * 200.0 - 100.0
|
||||
|
||||
long_friction := abs(
|
||||
pacejka_94_longitudinal(
|
||||
s.pacejka_long,
|
||||
x,
|
||||
max(abs(s.spring_impulse), 0.001) * inv_dt * 0.001,
|
||||
),
|
||||
)
|
||||
|
||||
ui.push_line_point(
|
||||
ctx,
|
||||
ui.Vec2f{alpha, long_friction * -0.5 + 1},
|
||||
)
|
||||
}
|
||||
|
||||
long_friction := abs(
|
||||
pacejka_94_longitudinal(
|
||||
s.pacejka_long,
|
||||
s.slip_ratio,
|
||||
max(abs(s.spring_impulse), 0.001) * inv_dt * 0.001,
|
||||
),
|
||||
)
|
||||
|
||||
rect := ui.get_line(ctx).rect
|
||||
|
||||
cur_point :=
|
||||
Vec2 {
|
||||
(s.slip_ratio + 100.0) / 200.0,
|
||||
long_friction * -0.5 + 1,
|
||||
} *
|
||||
Vec2{f32(rect.w), f32(rect.h)} +
|
||||
Vec2{f32(rect.x), f32(rect.y)}
|
||||
ui.draw_rect(
|
||||
ctx,
|
||||
ui.rect_from_point_extent(
|
||||
ui.Vec2{i32(cur_point.x), i32(cur_point.y)},
|
||||
2,
|
||||
),
|
||||
ui.Color{255, 255, 0, 255},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ui.layout_row(ctx, {-1}, 300)
|
||||
ui.begin_line(ctx, ui.Color{0, 255, 0, 255})
|
||||
defer ui.end_line(ctx)
|
||||
|
||||
for j in 0 ..< NUM_SAMPLES {
|
||||
alpha := f32(j) / f32(NUM_SAMPLES - 1)
|
||||
x := alpha * 180.0 - 90.0
|
||||
|
||||
lat_friction := abs(
|
||||
pacejka_94_lateral(
|
||||
s.pacejka_lat,
|
||||
x,
|
||||
max(abs(s.spring_impulse), 0.001) * inv_dt * 0.001,
|
||||
0.0,
|
||||
),
|
||||
)
|
||||
|
||||
ui.push_line_point(ctx, ui.Vec2f{alpha, lat_friction * -0.5 + 1})
|
||||
}
|
||||
|
||||
lat_friction := abs(
|
||||
pacejka_94_lateral(
|
||||
s.pacejka_lat,
|
||||
s.slip_angle,
|
||||
max(abs(s.spring_impulse), 0.001) * inv_dt * 0.001,
|
||||
0.0,
|
||||
),
|
||||
)
|
||||
|
||||
rect := ui.get_line(ctx).rect
|
||||
|
||||
cur_point :=
|
||||
Vec2{(s.slip_angle + 100.0) / 200.0, lat_friction * -0.5 + 1} *
|
||||
Vec2{f32(rect.w), f32(rect.h)} +
|
||||
Vec2{f32(rect.x), f32(rect.y)}
|
||||
ui.draw_rect(
|
||||
ctx,
|
||||
ui.rect_from_point_extent(
|
||||
ui.Vec2{i32(cur_point.x), i32(cur_point.y)},
|
||||
2,
|
||||
),
|
||||
ui.Color{255, 255, 0, 255},
|
||||
)
|
||||
}
|
||||
|
||||
window_x += w
|
||||
}
|
||||
if .ACTIVE in ui.treenode(ctx, "Bodies") {
|
||||
for _, i in sim_state.bodies_slice {
|
||||
body := &sim_state.bodies_slice[i]
|
||||
if body.alive {
|
||||
if .ACTIVE in
|
||||
ui.treenode(ctx, fmt.tprintf("%v (%v)", name.to_string(body.name), i)) {
|
||||
ui.keyval(ctx, "Pos", body.x)
|
||||
ui.keyval(ctx, "Shape Offset", body.shape_offset)
|
||||
aabb := body_get_aabb(body)
|
||||
ui.keyval(ctx, "AABB", aabb)
|
||||
ui.keyval(ctx, "Inv Inertia", body.inv_inertia_tensor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if .ACTIVE in ui.treenode(ctx, "Contacts") {
|
||||
@(static) search_buf: [1024]u8
|
||||
@(static) search_len: int
|
||||
ui.textbox(ctx, search_buf[:], &search_len)
|
||||
search_str := string(search_buf[0:search_len])
|
||||
for &contact, i in sim_state.contact_container.contacts {
|
||||
title: string
|
||||
|
||||
ui.push_id(ctx, mem.any_to_bytes(i))
|
||||
defer ui.pop_id(ctx)
|
||||
|
||||
if contact.type == .Body_vs_Body {
|
||||
title = fmt.tprintf(
|
||||
"%v v %v",
|
||||
name.to_string(get_body(sim_state, contact.a).name),
|
||||
name.to_string(get_body(sim_state, Body_Handle(contact.b)).name),
|
||||
)
|
||||
} else {
|
||||
title = fmt.tprintf(
|
||||
"%v v lvl",
|
||||
name.to_string(get_body(sim_state, contact.a).name),
|
||||
)
|
||||
}
|
||||
|
||||
if strings.contains(title, search_str) {
|
||||
ui.inspect_value(ctx, title, contact)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if .ACTIVE in ui.treenode(ctx, "Dynamic TLAS") {
|
||||
if sim_state.dynamic_tlas.built {
|
||||
bvh.debug_ui_bvh_node_recursive(
|
||||
ctx,
|
||||
sim_state.dynamic_tlas.bvh_tree,
|
||||
sim_state.dynamic_tlas.body_aabbs,
|
||||
0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// active_wheels := []int{0, 1}
|
||||
|
||||
// w, h: i32 = 500, 500
|
||||
|
||||
// window_x: i32 = 0
|
||||
|
||||
// for i in 0 ..< len(sim_state.suspension_constraints_slice) {
|
||||
// s := &sim_state.suspension_constraints_slice[i]
|
||||
|
||||
// if s.alive {
|
||||
// for idx in active_wheels {
|
||||
// if i == idx {
|
||||
// if ui.window(
|
||||
// ctx,
|
||||
// fmt.tprintf("Wheel %v", i),
|
||||
// ui.Rect{x = window_x, y = 0, w = w, h = h},
|
||||
// ui.Options{},
|
||||
// ) {
|
||||
// NUM_SAMPLES :: 100
|
||||
|
||||
// dt := f32(config.timestep) / f32(config.substreps_minus_one + 1)
|
||||
// inv_dt := 1.0 / dt
|
||||
|
||||
// {
|
||||
// ui.layout_row(ctx, {-1}, 300)
|
||||
// {
|
||||
// ui.begin_line(ctx, ui.Color{255, 0, 0, 255})
|
||||
// defer ui.end_line(ctx)
|
||||
|
||||
// for j in 0 ..< NUM_SAMPLES {
|
||||
// alpha := f32(j) / f32(NUM_SAMPLES - 1)
|
||||
// x := alpha * 200.0 - 100.0
|
||||
|
||||
// long_friction := abs(
|
||||
// pacejka_94_longitudinal(
|
||||
// s.pacejka_long,
|
||||
// x,
|
||||
// max(abs(s.spring_impulse), 0.001) * inv_dt * 0.001,
|
||||
// ),
|
||||
// )
|
||||
|
||||
// ui.push_line_point(
|
||||
// ctx,
|
||||
// ui.Vec2f{alpha, long_friction * -0.5 + 1},
|
||||
// )
|
||||
// }
|
||||
|
||||
// long_friction := abs(
|
||||
// pacejka_94_longitudinal(
|
||||
// s.pacejka_long,
|
||||
// s.slip_ratio,
|
||||
// max(abs(s.spring_impulse), 0.001) * inv_dt * 0.001,
|
||||
// ),
|
||||
// )
|
||||
|
||||
// rect := ui.get_line(ctx).rect
|
||||
|
||||
// cur_point :=
|
||||
// Vec2 {
|
||||
// (s.slip_ratio + 100.0) / 200.0,
|
||||
// long_friction * -0.5 + 1,
|
||||
// } *
|
||||
// Vec2{f32(rect.w), f32(rect.h)} +
|
||||
// Vec2{f32(rect.x), f32(rect.y)}
|
||||
// ui.draw_rect(
|
||||
// ctx,
|
||||
// ui.rect_from_point_extent(
|
||||
// ui.Vec2{i32(cur_point.x), i32(cur_point.y)},
|
||||
// 2,
|
||||
// ),
|
||||
// ui.Color{255, 255, 0, 255},
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
// {
|
||||
// ui.layout_row(ctx, {-1}, 300)
|
||||
// ui.begin_line(ctx, ui.Color{0, 255, 0, 255})
|
||||
// defer ui.end_line(ctx)
|
||||
|
||||
// for j in 0 ..< NUM_SAMPLES {
|
||||
// alpha := f32(j) / f32(NUM_SAMPLES - 1)
|
||||
// x := alpha * 180.0 - 90.0
|
||||
|
||||
// lat_friction := abs(
|
||||
// pacejka_94_lateral(
|
||||
// s.pacejka_lat,
|
||||
// x,
|
||||
// max(abs(s.spring_impulse), 0.001) * inv_dt * 0.001,
|
||||
// 0.0,
|
||||
// ),
|
||||
// )
|
||||
|
||||
// ui.push_line_point(ctx, ui.Vec2f{alpha, lat_friction * -0.5 + 1})
|
||||
// }
|
||||
|
||||
// lat_friction := abs(
|
||||
// pacejka_94_lateral(
|
||||
// s.pacejka_lat,
|
||||
// s.slip_angle,
|
||||
// max(abs(s.spring_impulse), 0.001) * inv_dt * 0.001,
|
||||
// 0.0,
|
||||
// ),
|
||||
// )
|
||||
|
||||
// rect := ui.get_line(ctx).rect
|
||||
|
||||
// cur_point :=
|
||||
// Vec2{(s.slip_angle + 100.0) / 200.0, lat_friction * -0.5 + 1} *
|
||||
// Vec2{f32(rect.w), f32(rect.h)} +
|
||||
// Vec2{f32(rect.x), f32(rect.y)}
|
||||
// ui.draw_rect(
|
||||
// ctx,
|
||||
// ui.rect_from_point_extent(
|
||||
// ui.Vec2{i32(cur_point.x), i32(cur_point.y)},
|
||||
// 2,
|
||||
// ),
|
||||
// ui.Color{255, 255, 0, 255},
|
||||
// )
|
||||
// }
|
||||
|
||||
// window_x += w
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
debug_transform_points_local_to_world :: proc(body: Body_Ptr, points: []Vec3) {
|
||||
|
@ -1,9 +1,14 @@
|
||||
package physics
|
||||
|
||||
import "collision"
|
||||
import "core:log"
|
||||
import "core:math"
|
||||
import lg "core:math/linalg"
|
||||
import "game:halfedge"
|
||||
import "game:name"
|
||||
import "libs:tracy"
|
||||
|
||||
_ :: log
|
||||
|
||||
inertia_tensor_sphere :: proc(radius: f32) -> (tensor: Matrix3) {
|
||||
tensor = radius * radius * (2.0 / 3.0)
|
||||
@ -23,7 +28,7 @@ inertia_tensor_box :: proc(size: Vec3) -> (tensor: Matrix3) {
|
||||
return
|
||||
}
|
||||
|
||||
inertia_tensor_collision_shape :: proc(shape: Input_Shape) -> (tensor: Matrix3) {
|
||||
inertia_tensor_collision_shape :: proc(shape: Input_Shape_Internal) -> (tensor: Matrix3) {
|
||||
switch s in shape {
|
||||
case Shape_Box:
|
||||
tensor = inertia_tensor_box(s.size)
|
||||
@ -35,6 +40,66 @@ inertia_tensor_collision_shape :: proc(shape: Input_Shape) -> (tensor: Matrix3)
|
||||
return
|
||||
}
|
||||
|
||||
center_of_mass_shape :: proc(shape: Input_Shape_Internal) -> (com: Vec3) {
|
||||
switch s in shape {
|
||||
case Shape_Box:
|
||||
com = 0
|
||||
case Shape_Convex:
|
||||
com = s.center_of_mass
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
volume_shape :: proc(shape: Input_Shape_Internal) -> (vol: f32) {
|
||||
switch s in shape {
|
||||
case Shape_Box:
|
||||
vol = s.size.x * s.size.y * s.size.z
|
||||
case Shape_Convex:
|
||||
vol = s.total_volume
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
combined_center_of_mass :: proc(shapes: []Input_Shape) -> (com: Vec3) {
|
||||
total_weight := f32(0)
|
||||
for shape in shapes {
|
||||
inner_com := center_of_mass_shape(shape.inner_shape) + shape.rel_x
|
||||
volume := volume_shape(shape.inner_shape) * (shape.density_minus_one + 1)
|
||||
com += inner_com * volume
|
||||
total_weight += volume
|
||||
}
|
||||
com = total_weight > 0 ? com / total_weight : 0
|
||||
return
|
||||
}
|
||||
|
||||
// Returns normalized inertia
|
||||
// https://pybullet.org/Bullet/phpBB3/viewtopic.php?t=246
|
||||
combined_inertia_tensor :: proc(shapes: []Input_Shape) -> (inertia: Matrix3) {
|
||||
combined_com := combined_center_of_mass(shapes)
|
||||
|
||||
total_volume := f32(0)
|
||||
for shape in shapes {
|
||||
com := center_of_mass_shape(shape.inner_shape) + shape.rel_x
|
||||
r := com - combined_com
|
||||
local_inertia := inertia_tensor_collision_shape(shape.inner_shape)
|
||||
rotation_mat: Matrix3 = auto_cast lg.matrix3_from_quaternion(shape.rel_q)
|
||||
inertia_common_align := lg.transpose(rotation_mat) * local_inertia * rotation_mat
|
||||
volume := volume_shape(shape.inner_shape) * (shape.density_minus_one + 1)
|
||||
dot_r := lg.dot(r, r)
|
||||
// outer_r: Matrix3 = auto_cast lg.outer_product(r, r)
|
||||
#unroll for i in 0 ..< 3 {
|
||||
#unroll for j in 0 ..< 3 {
|
||||
kron: f32 = i == j ? 1 : 0
|
||||
inertia[i][j] += inertia_common_align[i][j] + volume * (dot_r * kron - r[i] * r[j])
|
||||
}
|
||||
}
|
||||
// inertia = inertia + inertia_common_align * (Matrix3(dot_r) - outer_r) * Matrix3(volume)
|
||||
total_volume += volume
|
||||
}
|
||||
inertia *= Matrix3(1.0 / total_volume)
|
||||
return
|
||||
}
|
||||
|
||||
body_local_to_world :: #force_inline proc(body: Body_Ptr, pos: Vec3) -> Vec3 {
|
||||
return body.x + lg.quaternion_mul_vector3(body.q, pos)
|
||||
}
|
||||
@ -88,11 +153,7 @@ wheel_get_forward_vec :: #force_inline proc(
|
||||
}
|
||||
|
||||
body_get_shape_offset_local :: proc(body: Body_Ptr) -> (offset: Vec3) {
|
||||
#partial switch s in body.shape {
|
||||
case Internal_Shape_Convex:
|
||||
offset = -s.center_of_mass
|
||||
}
|
||||
return
|
||||
return body.shape_offset
|
||||
}
|
||||
|
||||
// Returns the shape's world position
|
||||
@ -102,14 +163,14 @@ body_get_shape_pos :: proc(body: Body_Ptr) -> Vec3 {
|
||||
return body_local_to_world(body, offset)
|
||||
}
|
||||
|
||||
body_get_convex_shape_world :: proc(
|
||||
shape_get_convex_local :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
body: Body_Ptr,
|
||||
shape: Collision_Shape_Internal,
|
||||
allocator := context.temp_allocator,
|
||||
) -> (
|
||||
mesh: collision.Convex,
|
||||
) {
|
||||
switch s in body.shape {
|
||||
switch s in shape {
|
||||
case Shape_Box:
|
||||
mesh = collision.box_to_convex(collision.Box{rad = s.size * 0.5}, allocator)
|
||||
case Internal_Shape_Convex:
|
||||
@ -118,10 +179,125 @@ body_get_convex_shape_world :: proc(
|
||||
mesh = halfedge.copy_mesh(mesh, allocator)
|
||||
}
|
||||
|
||||
transform :=
|
||||
lg.matrix4_translate_f32(body_get_shape_pos(body)) * lg.matrix4_from_quaternion_f32(body.q)
|
||||
halfedge.transform_mesh(&mesh, transform)
|
||||
return
|
||||
}
|
||||
|
||||
Shapes_Iterator :: struct {
|
||||
sim_state: ^Sim_State,
|
||||
first_idx: i32,
|
||||
cur_idx: i32,
|
||||
counter: i32,
|
||||
}
|
||||
|
||||
shapes_iterator :: proc(sim_state: ^Sim_State, first_shape: i32) -> (it: Shapes_Iterator) {
|
||||
it.sim_state = sim_state
|
||||
it.first_idx = first_shape
|
||||
it.cur_idx = it.first_idx
|
||||
return
|
||||
}
|
||||
|
||||
shapes_iterator_next :: proc(
|
||||
it: ^Shapes_Iterator,
|
||||
) -> (
|
||||
shape: ^Collision_Shape,
|
||||
idx: i32,
|
||||
ok: bool,
|
||||
) {
|
||||
if it.cur_idx == it.first_idx && it.counter != 0 {
|
||||
return nil, it.counter, false
|
||||
}
|
||||
|
||||
shape = &it.sim_state.shapes[it.cur_idx]
|
||||
idx = it.cur_idx
|
||||
ok = true
|
||||
|
||||
it.cur_idx = shape.next_index
|
||||
it.counter += 1
|
||||
return
|
||||
}
|
||||
|
||||
get_shape_by_index :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
first_shape: i32,
|
||||
index: i32,
|
||||
) -> (
|
||||
shape_idx: i32,
|
||||
) {
|
||||
tracy.Zone()
|
||||
shape_idx = -1
|
||||
i := i32(0)
|
||||
it := shapes_iterator(sim_state, first_shape)
|
||||
for _, idx in shapes_iterator_next(&it) {
|
||||
if i == index {
|
||||
shape_idx = idx
|
||||
break
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
body_get_convex_shape_world :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
body: Body_Ptr,
|
||||
shape: Collision_Shape,
|
||||
allocator := context.temp_allocator,
|
||||
) -> (
|
||||
mesh: collision.Convex,
|
||||
) {
|
||||
tracy.Zone()
|
||||
body_transform := lg.matrix4_from_trs_f32(body_get_shape_pos(body), body.q, 1)
|
||||
shape_transform := lg.matrix4_translate(shape.rel_x) * lg.matrix4_from_quaternion(shape.rel_q)
|
||||
|
||||
transform := body_transform * shape_transform
|
||||
mesh = shape_get_convex_local(sim_state, shape.inner_shape, allocator)
|
||||
halfedge.transform_mesh(&mesh, transform)
|
||||
return
|
||||
}
|
||||
|
||||
body_get_convex_shape_by_linear_index_world :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
body: Body_Ptr,
|
||||
shape_linear_idx: i32,
|
||||
allocator := context.temp_allocator,
|
||||
) -> (
|
||||
mesh: collision.Convex,
|
||||
) {
|
||||
tracy.Zone()
|
||||
if shape_linear_idx == 1 {
|
||||
log.debugf("linear idx: %v, body %v", shape_linear_idx, name.to_string(body.name))
|
||||
}
|
||||
shape_idx := get_shape_by_index(sim_state, body.shape, shape_linear_idx)
|
||||
return body_get_convex_shape_world(sim_state, body, sim_state.shapes[shape_idx], allocator)
|
||||
}
|
||||
|
||||
body_get_convex_shapes_world :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
body: Body_Ptr,
|
||||
allocator := context.temp_allocator,
|
||||
) -> (
|
||||
meshes: []collision.Convex,
|
||||
) {
|
||||
tracy.Zone()
|
||||
body_transform :=
|
||||
lg.matrix4_translate_f32(body_get_shape_pos(body)) * lg.matrix4_from_quaternion_f32(body.q)
|
||||
|
||||
meshes_arr := make([dynamic]collision.Convex, context.temp_allocator)
|
||||
defer delete(meshes_arr)
|
||||
|
||||
it := shapes_iterator(sim_state, body.shape)
|
||||
for shape in shapes_iterator_next(&it) {
|
||||
shape_transform :=
|
||||
lg.matrix4_translate(shape.rel_x) * lg.matrix4_from_quaternion(shape.rel_q)
|
||||
|
||||
transform := body_transform * shape_transform
|
||||
mesh := shape_get_convex_local(sim_state, shape.inner_shape, allocator)
|
||||
halfedge.transform_mesh(&mesh, transform)
|
||||
append(&meshes_arr, mesh)
|
||||
}
|
||||
|
||||
meshes = make([]collision.Convex, len(meshes_arr), allocator)
|
||||
copy(meshes, meshes_arr[:])
|
||||
return
|
||||
}
|
||||
|
||||
@ -140,7 +316,20 @@ level_geom_get_convex_shape :: proc(
|
||||
)
|
||||
}
|
||||
|
||||
shape_get_aabb :: proc(shape: Collision_Shape) -> (aabb: AABB) {
|
||||
input_shape_internal_get_aabb :: proc(shape: Input_Shape_Internal) -> (aabb: AABB) {
|
||||
switch s in shape {
|
||||
case Shape_Box:
|
||||
aabb.center = 0
|
||||
aabb.extent = s.size * 0.5
|
||||
case Shape_Convex:
|
||||
aabb.center = s.mesh.center
|
||||
aabb.extent = s.mesh.extent
|
||||
}
|
||||
|
||||
return aabb
|
||||
}
|
||||
|
||||
internal_shape_get_aabb :: proc(shape: Collision_Shape_Internal) -> (aabb: AABB) {
|
||||
switch s in shape {
|
||||
case Shape_Box:
|
||||
aabb.center = 0
|
||||
@ -149,22 +338,59 @@ shape_get_aabb :: proc(shape: Collision_Shape) -> (aabb: AABB) {
|
||||
aabb.center = s.center
|
||||
aabb.extent = s.extent
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
return aabb
|
||||
shape_get_aabb :: proc(shape: Collision_Shape) -> (aabb: AABB) {
|
||||
local_aabb := internal_shape_get_aabb(shape.inner_shape)
|
||||
aabb = aabb_transform(local_aabb, shape.rel_x, shape.rel_q)
|
||||
return
|
||||
}
|
||||
|
||||
rotate_extent :: proc(extent: Vec3, q: Quat) -> Vec3 {
|
||||
rotation_mat := lg.matrix3_from_quaternion(q)
|
||||
result := lg.abs(extent.xxx * rotation_mat[0])
|
||||
result += lg.abs(extent.yyy * rotation_mat[1])
|
||||
result += lg.abs(extent.zzz * rotation_mat[2])
|
||||
return result
|
||||
}
|
||||
|
||||
aabb_transform :: proc(local_aabb: AABB, x: Vec3, q: Quat) -> (aabb: AABB) {
|
||||
aabb.center = lg.quaternion_mul_vector3(q, local_aabb.center) + x
|
||||
|
||||
aabb.extent = rotate_extent(local_aabb.extent, q)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
input_shape_get_aabb :: proc(shapes: []Input_Shape) -> (aabb: AABB) {
|
||||
min, max: Vec3 = max(f32), min(f32)
|
||||
|
||||
for shape in shapes {
|
||||
local_aabb := input_shape_internal_get_aabb(shape.inner_shape)
|
||||
aabb = aabb_transform(local_aabb, shape.rel_x, shape.rel_q)
|
||||
shape_min := aabb.center - aabb.extent
|
||||
shape_max := aabb.center + aabb.extent
|
||||
|
||||
min, max = lg.min(shape_min, min), lg.max(shape_max, max)
|
||||
}
|
||||
|
||||
aabb.center = (max + min) * 0.5
|
||||
aabb.extent = (max - min) * 0.5
|
||||
return
|
||||
}
|
||||
|
||||
body_transform_shape_aabb :: proc(body: Body_Ptr, local_aabb: AABB) -> (aabb: AABB) {
|
||||
offset_world := body_local_to_world(body, body.shape_offset)
|
||||
aabb = aabb_transform(local_aabb, offset_world, body.q)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
body_get_aabb :: proc(body: Body_Ptr) -> (aabb: AABB) {
|
||||
local_aabb := shape_get_aabb(body.shape)
|
||||
local_aabb := body.shape_aabb
|
||||
|
||||
aabb_center_local_to_body := body_get_shape_offset_local(body) + local_aabb.center
|
||||
aabb.center = body_local_to_world(body, aabb_center_local_to_body)
|
||||
|
||||
rotation_mat := lg.matrix3_from_quaternion(body.q)
|
||||
aabb.extent = lg.abs(local_aabb.extent.xxx * rotation_mat[0])
|
||||
aabb.extent += lg.abs(local_aabb.extent.yyy * rotation_mat[1])
|
||||
aabb.extent += lg.abs(local_aabb.extent.zzz * rotation_mat[2])
|
||||
|
||||
return aabb
|
||||
return body_transform_shape_aabb(body, local_aabb)
|
||||
}
|
||||
|
||||
rpm_to_angular_velocity :: proc(rpm: f32) -> f32 {
|
||||
|
@ -3,6 +3,7 @@ package physics
|
||||
import "collision"
|
||||
import lg "core:math/linalg"
|
||||
import "game:container/spanpool"
|
||||
import "game:name"
|
||||
import "libs:tracy"
|
||||
|
||||
MAX_CONTACTS :: 1024 * 16
|
||||
@ -17,10 +18,12 @@ AABB :: struct {
|
||||
}
|
||||
|
||||
Contact_Pair_Bodies :: struct {
|
||||
a, b: Body_Handle,
|
||||
a, b: Body_Handle,
|
||||
shape_a, shape_b: i32,
|
||||
}
|
||||
Contact_Pair_Body_Level :: struct {
|
||||
a: Body_Handle,
|
||||
shape_a: i32,
|
||||
tri_idx: i32,
|
||||
}
|
||||
|
||||
@ -29,15 +32,26 @@ Contact_Pair :: union {
|
||||
Contact_Pair_Body_Level,
|
||||
}
|
||||
|
||||
make_contact_pair_bodies :: proc(body_a: i32, body_b: i32) -> Contact_Pair_Bodies {
|
||||
make_contact_pair_bodies :: proc(
|
||||
body_a: i32,
|
||||
body_b: i32,
|
||||
shape_a: i32,
|
||||
shape_b: i32,
|
||||
) -> Contact_Pair_Bodies {
|
||||
return {
|
||||
index_to_body_handle(int(min(body_a, body_b))),
|
||||
index_to_body_handle(int(max(body_a, body_b))),
|
||||
shape_a,
|
||||
shape_b,
|
||||
}
|
||||
}
|
||||
|
||||
make_contact_pair_body_level :: proc(body: i32, tri_idx: i32) -> Contact_Pair_Body_Level {
|
||||
return {index_to_body_handle(int(body)), tri_idx}
|
||||
make_contact_pair_body_level :: proc(
|
||||
body: i32,
|
||||
shape_a: i32,
|
||||
tri_idx: i32,
|
||||
) -> Contact_Pair_Body_Level {
|
||||
return {index_to_body_handle(int(body)), shape_a, tri_idx}
|
||||
}
|
||||
|
||||
Contact_Container :: struct {
|
||||
@ -73,10 +87,12 @@ Sim_State :: struct {
|
||||
// Slices. When you call get_body or get_suspension_constraint you will get a pointer to an element in this slice
|
||||
bodies_slice: #soa[]Body,
|
||||
suspension_constraints_slice: #soa[]Suspension_Constraint,
|
||||
shapes: [dynamic]Collision_Shape,
|
||||
first_free_body_plus_one: i32,
|
||||
first_free_suspension_constraint_plus_one: i32,
|
||||
first_free_engine_plus_one: i32,
|
||||
first_free_level_geom_plus_one: i32,
|
||||
first_free_shape_plus_one: i32,
|
||||
|
||||
// Persistent stuff for simulation
|
||||
contact_container: Contact_Container,
|
||||
@ -90,6 +106,10 @@ Sim_State :: struct {
|
||||
// Level geometry
|
||||
geometry_vertices_pool: spanpool.Span_Pool(Vec3),
|
||||
geometry_indices_pool: spanpool.Span_Pool(u16),
|
||||
|
||||
// Temp stuff, kept for one frame, allocated from temp_allocator
|
||||
static_tlas: Static_TLAS,
|
||||
dynamic_tlas: Dynamic_TLAS,
|
||||
}
|
||||
|
||||
Scene :: struct {
|
||||
@ -107,11 +127,13 @@ copy_sim_state :: proc(dst: ^Sim_State, src: ^Sim_State) {
|
||||
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
|
||||
dst.first_free_engine_plus_one = src.first_free_engine_plus_one
|
||||
dst.first_free_shape_plus_one = src.first_free_shape_plus_one
|
||||
|
||||
resize(&dst.bodies, len(src.bodies))
|
||||
resize(&dst.suspension_constraints, len(src.suspension_constraints))
|
||||
resize(&dst.engines, len(src.engines))
|
||||
resize(&dst.level_geoms, len(src.level_geoms))
|
||||
resize(&dst.shapes, len(src.shapes))
|
||||
|
||||
dst.bodies_slice = dst.bodies[:]
|
||||
dst.suspension_constraints_slice = dst.suspension_constraints[:]
|
||||
@ -124,6 +146,7 @@ copy_sim_state :: proc(dst: ^Sim_State, src: ^Sim_State) {
|
||||
}
|
||||
copy(dst.engines[:], src.engines[:])
|
||||
copy(dst.level_geoms[:], src.level_geoms[:])
|
||||
copy(dst.shapes[:], src.shapes[:])
|
||||
|
||||
contact_container_copy(&dst.contact_container, src.contact_container)
|
||||
convex_container_copy(&dst.convex_container, src.convex_container)
|
||||
@ -164,7 +187,11 @@ Body :: struct {
|
||||
inv_mass: f32,
|
||||
// Moment of inertia
|
||||
inv_inertia_tensor: Matrix3,
|
||||
shape: Collision_Shape,
|
||||
shape: i32,
|
||||
// Collision shape offset = -combined_center_of_mass
|
||||
shape_offset: Vec3,
|
||||
shape_aabb: AABB,
|
||||
name: name.Name,
|
||||
//
|
||||
next_plus_one: i32,
|
||||
}
|
||||
@ -189,13 +216,30 @@ Shape_Convex :: struct {
|
||||
mesh: collision.Convex,
|
||||
center_of_mass: Vec3,
|
||||
inertia_tensor: Matrix3,
|
||||
total_volume: f32,
|
||||
}
|
||||
|
||||
Collision_Shape :: union {
|
||||
Collision_Shape_Internal :: union {
|
||||
Shape_Box,
|
||||
Internal_Shape_Convex,
|
||||
}
|
||||
|
||||
Shape_Compound :: struct {
|
||||
rel_x: Vec3,
|
||||
rel_q: Quat,
|
||||
inner_shape: Collision_Shape_Internal,
|
||||
next_index: i32,
|
||||
prev_index: i32,
|
||||
}
|
||||
|
||||
Collision_Shape :: struct {
|
||||
rel_x: Vec3,
|
||||
rel_q: Quat,
|
||||
inner_shape: Collision_Shape_Internal,
|
||||
next_index: i32,
|
||||
prev_index: i32,
|
||||
}
|
||||
|
||||
Suspension_Constraint :: struct {
|
||||
alive: bool,
|
||||
// Pos relative to the body
|
||||
@ -442,25 +486,45 @@ get_body :: proc(sim_state: ^Sim_State, handle: Body_Handle) -> Body_Ptr {
|
||||
return &sim_state.bodies_slice[index]
|
||||
}
|
||||
|
||||
remove_shape :: proc(sim_state: ^Sim_State, shape: Collision_Shape) {
|
||||
switch &s in shape {
|
||||
case Shape_Box:
|
||||
case Internal_Shape_Convex:
|
||||
convex_container_remove(&sim_state.convex_container, s.mesh)
|
||||
remove_shape :: proc(sim_state: ^Sim_State, idx: i32) {
|
||||
cur_idx := idx
|
||||
for {
|
||||
shape := &sim_state.shapes[cur_idx]
|
||||
|
||||
switch &s in shape.inner_shape {
|
||||
case Shape_Box:
|
||||
case Internal_Shape_Convex:
|
||||
convex_container_remove(&sim_state.convex_container, s.mesh)
|
||||
}
|
||||
|
||||
prev_idx := cur_idx
|
||||
cur_idx = shape.next_index
|
||||
free_shape(sim_state, prev_idx)
|
||||
if cur_idx == idx {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Input_Shape :: union {
|
||||
Input_Shape_Internal :: union {
|
||||
Shape_Box,
|
||||
Shape_Convex,
|
||||
}
|
||||
|
||||
Input_Shape :: struct {
|
||||
rel_x: Vec3,
|
||||
rel_q: Quat,
|
||||
density_minus_one: f32,
|
||||
inner_shape: Input_Shape_Internal,
|
||||
}
|
||||
|
||||
Body_Config :: struct {
|
||||
name: name.Name,
|
||||
initial_pos: Vec3,
|
||||
initial_rot: Quat,
|
||||
initial_vel: Vec3,
|
||||
initial_ang_vel: Vec3,
|
||||
shape: Input_Shape,
|
||||
shapes: []Input_Shape,
|
||||
mass: f32,
|
||||
inertia_mode: Body_Config_Inertia_Mode,
|
||||
// Unit inertia tensor
|
||||
@ -521,7 +585,7 @@ calculate_body_params_from_config :: proc(
|
||||
if config.inertia_mode == .Explicit {
|
||||
inertia_tensor = config.inertia_tensor
|
||||
} else {
|
||||
inertia_tensor = inertia_tensor_collision_shape(config.shape)
|
||||
inertia_tensor = combined_inertia_tensor(config.shapes)
|
||||
}
|
||||
inertia_tensor = inertia_tensor * Matrix3(config.mass)
|
||||
|
||||
@ -530,18 +594,64 @@ calculate_body_params_from_config :: proc(
|
||||
return
|
||||
}
|
||||
|
||||
add_shape :: proc(sim_state: ^Sim_State, shape: Input_Shape) -> (result: Collision_Shape) {
|
||||
switch s in shape {
|
||||
case Shape_Box:
|
||||
result = s
|
||||
case Shape_Convex:
|
||||
convex: Internal_Shape_Convex
|
||||
convex.mesh = convex_container_add(&sim_state.convex_container, s.mesh)
|
||||
convex.center = s.mesh.center
|
||||
convex.extent = s.mesh.extent
|
||||
convex.center_of_mass = s.center_of_mass
|
||||
convex.inertia_tensor = s.inertia_tensor
|
||||
result = convex
|
||||
alloc_shape :: proc(sim_state: ^Sim_State) -> (idx: i32) {
|
||||
if sim_state.first_free_shape_plus_one != 0 {
|
||||
idx = sim_state.first_free_shape_plus_one - 1
|
||||
new_shape := &sim_state.shapes[idx]
|
||||
sim_state.first_free_shape_plus_one = new_shape.next_index + 1
|
||||
|
||||
new_shape.prev_index = idx
|
||||
new_shape.next_index = idx
|
||||
|
||||
return idx
|
||||
} else {
|
||||
idx = i32(len(sim_state.shapes))
|
||||
append(&sim_state.shapes, Collision_Shape{})
|
||||
new_shape := &sim_state.shapes[idx]
|
||||
|
||||
new_shape.prev_index = idx
|
||||
new_shape.next_index = idx
|
||||
|
||||
return idx
|
||||
}
|
||||
}
|
||||
|
||||
free_shape :: proc(sim_state: ^Sim_State, idx: i32) {
|
||||
sim_state.shapes[idx].next_index = sim_state.first_free_shape_plus_one - 1
|
||||
sim_state.first_free_shape_plus_one = idx + 1
|
||||
}
|
||||
|
||||
add_shape :: proc(sim_state: ^Sim_State, shapes: []Input_Shape) -> (first_idx: i32) {
|
||||
first_idx = -1
|
||||
last_idx := i32(-1)
|
||||
for shape in shapes {
|
||||
new_idx := alloc_shape(sim_state)
|
||||
new_shape := &sim_state.shapes[new_idx]
|
||||
if first_idx == -1 {
|
||||
first_idx = new_idx
|
||||
} else {
|
||||
sim_state.shapes[last_idx].next_index = new_idx
|
||||
sim_state.shapes[first_idx].prev_index = new_idx
|
||||
new_shape.prev_index = last_idx
|
||||
new_shape.next_index = first_idx
|
||||
}
|
||||
last_idx = new_idx
|
||||
|
||||
new_shape.rel_x = shape.rel_x
|
||||
new_shape.rel_q = shape.rel_q
|
||||
|
||||
switch s in shape.inner_shape {
|
||||
case Shape_Box:
|
||||
new_shape.inner_shape = s
|
||||
case Shape_Convex:
|
||||
convex: Internal_Shape_Convex
|
||||
convex.mesh = convex_container_add(&sim_state.convex_container, s.mesh)
|
||||
convex.center = s.mesh.center
|
||||
convex.extent = s.mesh.extent
|
||||
convex.center_of_mass = s.center_of_mass
|
||||
convex.inertia_tensor = s.inertia_tensor
|
||||
new_shape.inner_shape = convex
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
@ -553,7 +663,10 @@ initialize_body_from_config :: proc(sim_state: ^Sim_State, body: ^Body, config:
|
||||
body.v = config.initial_vel
|
||||
body.w = config.initial_ang_vel
|
||||
|
||||
body.shape = add_shape(sim_state, config.shape)
|
||||
body.shape = add_shape(sim_state, config.shapes)
|
||||
body.shape_offset = -combined_center_of_mass(config.shapes)
|
||||
body.shape_aabb = input_shape_get_aabb(config.shapes)
|
||||
body.name = config.name
|
||||
|
||||
body.inv_mass, body.inv_inertia_tensor = calculate_body_params_from_config(config)
|
||||
}
|
||||
@ -561,7 +674,10 @@ initialize_body_from_config :: proc(sim_state: ^Sim_State, body: ^Body, config:
|
||||
update_body_from_config :: proc(sim_state: ^Sim_State, body: Body_Ptr, config: Body_Config) {
|
||||
// Inefficient, but eh
|
||||
remove_shape(sim_state, body.shape)
|
||||
body.shape = add_shape(sim_state, config.shape)
|
||||
body.shape = add_shape(sim_state, config.shapes)
|
||||
body.shape_offset = -combined_center_of_mass(config.shapes)
|
||||
body.shape_aabb = input_shape_get_aabb(config.shapes)
|
||||
body.name = config.name
|
||||
|
||||
body.inv_mass, body.inv_inertia_tensor = calculate_body_params_from_config(config)
|
||||
}
|
||||
@ -855,6 +971,7 @@ destry_sim_state :: proc(sim_state: ^Sim_State) {
|
||||
delete_soa(sim_state.suspension_constraints)
|
||||
delete(sim_state.engines)
|
||||
delete(sim_state.level_geoms)
|
||||
delete(sim_state.shapes)
|
||||
|
||||
delete_soa(sim_state.contact_container.contacts)
|
||||
delete_map(sim_state.contact_container.lookup)
|
||||
@ -863,6 +980,8 @@ destry_sim_state :: proc(sim_state: ^Sim_State) {
|
||||
spanpool.destroy_spanpool(&sim_state.gear_ratios_pool)
|
||||
spanpool.destroy_spanpool(&sim_state.geometry_vertices_pool)
|
||||
spanpool.destroy_spanpool(&sim_state.geometry_indices_pool)
|
||||
|
||||
dynamic_tlas_destroy(&sim_state.dynamic_tlas)
|
||||
}
|
||||
|
||||
destroy_physics_scene :: proc(scene: ^Scene) {
|
||||
|
@ -2,6 +2,7 @@ package physics
|
||||
|
||||
import "game:container/spanpool"
|
||||
import he "game:halfedge"
|
||||
import "libs:tracy"
|
||||
|
||||
Convex_Container :: struct {
|
||||
vertices: spanpool.Span_Pool(he.Vertex),
|
||||
@ -34,6 +35,7 @@ convex_container_get_mesh :: proc(
|
||||
) -> (
|
||||
mesh: he.Half_Edge_Mesh,
|
||||
) {
|
||||
tracy.Zone()
|
||||
mesh.vertices = spanpool.resolve_slice(&container.vertices, handle.vertices)
|
||||
mesh.faces = spanpool.resolve_slice(&container.faces, handle.faces)
|
||||
mesh.edges = spanpool.resolve_slice(&container.edges, handle.edges)
|
||||
|
@ -12,8 +12,10 @@ import "core:math/rand"
|
||||
import "core:slice"
|
||||
import "game:debug"
|
||||
import he "game:halfedge"
|
||||
import "game:name"
|
||||
import "libs:tracy"
|
||||
|
||||
_ :: name
|
||||
_ :: log
|
||||
_ :: rand
|
||||
_ :: math
|
||||
@ -77,6 +79,7 @@ Static_TLAS :: struct {
|
||||
Dynamic_TLAS :: struct {
|
||||
bvh_tree: bvh.BVH,
|
||||
body_aabbs: []bvh.AABB,
|
||||
built: bool,
|
||||
}
|
||||
|
||||
build_static_tlas :: proc(sim_state: ^Sim_State) -> Static_TLAS {
|
||||
@ -146,10 +149,17 @@ build_static_tlas :: proc(sim_state: ^Sim_State) -> Static_TLAS {
|
||||
|
||||
// TODO: free intermediate temp allocs
|
||||
// Creates TLAS using temp allocator
|
||||
build_dynamic_tlas :: proc(sim_state: ^Sim_State, config: Solver_Config) -> Dynamic_TLAS {
|
||||
build_dynamic_tlas :: proc(
|
||||
sim_state: ^Sim_State,
|
||||
config: Solver_Config,
|
||||
out_tlas: ^Dynamic_TLAS,
|
||||
allocator := context.allocator,
|
||||
) {
|
||||
tracy.Zone()
|
||||
|
||||
body_aabbs := make([]bvh.AABB, sim_state.num_bodies, context.temp_allocator)
|
||||
dynamic_tlas_destroy(out_tlas)
|
||||
|
||||
body_aabbs := make([]bvh.AABB, sim_state.num_bodies, allocator)
|
||||
body_indices := make([]u16, sim_state.num_bodies, context.temp_allocator)
|
||||
|
||||
{
|
||||
@ -174,9 +184,21 @@ build_dynamic_tlas :: proc(sim_state: ^Sim_State, config: Solver_Config) -> Dyna
|
||||
}
|
||||
}
|
||||
|
||||
sim_state_bvh := bvh.build_bvh_from_aabbs(body_aabbs, body_indices, context.temp_allocator)
|
||||
sim_state_bvh := bvh.build_bvh_from_aabbs(body_aabbs, body_indices, allocator)
|
||||
|
||||
return Dynamic_TLAS{bvh_tree = sim_state_bvh, body_aabbs = body_aabbs}
|
||||
out_tlas^ = Dynamic_TLAS {
|
||||
bvh_tree = sim_state_bvh,
|
||||
body_aabbs = body_aabbs,
|
||||
built = true,
|
||||
}
|
||||
}
|
||||
|
||||
dynamic_tlas_destroy :: proc(dyn_tlas: ^Dynamic_TLAS) {
|
||||
if dyn_tlas.built {
|
||||
bvh.destroy_bvh(&dyn_tlas.bvh_tree)
|
||||
delete(dyn_tlas.body_aabbs)
|
||||
dyn_tlas^ = {}
|
||||
}
|
||||
}
|
||||
|
||||
raycasts_level :: proc(
|
||||
@ -251,19 +273,28 @@ raycast_bodies :: proc(
|
||||
body_idx := tlas.bvh_tree.primitives[leaf_node.child_or_prim_start + j]
|
||||
|
||||
body := get_body(sim_state, index_to_body_handle(int(body_idx)))
|
||||
shape := body_get_convex_shape_world(sim_state, body, context.temp_allocator)
|
||||
|
||||
hit_t, _, tmp_normal, _, ok := collision.ray_vs_convex(
|
||||
shape,
|
||||
ray.origin,
|
||||
ray.dir,
|
||||
distance,
|
||||
)
|
||||
it := shapes_iterator(sim_state, body.shape)
|
||||
for shape in shapes_iterator_next(&it) {
|
||||
mesh := body_get_convex_shape_world(
|
||||
sim_state,
|
||||
body,
|
||||
shape^,
|
||||
context.temp_allocator,
|
||||
)
|
||||
|
||||
if ok && (!hit || hit_t < t) {
|
||||
t = hit_t
|
||||
normal = tmp_normal
|
||||
hit = true
|
||||
hit_t, _, tmp_normal, _, ok := collision.ray_vs_convex(
|
||||
mesh,
|
||||
ray.origin,
|
||||
ray.dir,
|
||||
distance,
|
||||
)
|
||||
|
||||
if ok && (!hit || hit_t < t) {
|
||||
t = hit_t
|
||||
normal = tmp_normal
|
||||
hit = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -384,10 +415,16 @@ remove_invalid_contacts :: proc(
|
||||
|
||||
pair_from_contact :: proc(contact: Contact) -> (pair: Contact_Pair) {
|
||||
if contact.type == .Body_vs_Body {
|
||||
pair = make_contact_pair_bodies(i32(contact.a) - 1, i32(contact.b) - 1)
|
||||
pair = make_contact_pair_bodies(
|
||||
i32(contact.a) - 1,
|
||||
i32(contact.b) - 1,
|
||||
contact.shape_a,
|
||||
contact.shape_b,
|
||||
)
|
||||
} else {
|
||||
pair = make_contact_pair_body_level(
|
||||
i32(body_handle_to_index(contact.a)),
|
||||
contact.shape_a,
|
||||
contact.tri_idx,
|
||||
)
|
||||
}
|
||||
@ -433,24 +470,70 @@ find_new_contacts :: proc(
|
||||
for j in 0 ..< leaf_node.prim_len {
|
||||
other_body_idx :=
|
||||
dyn_tlas.bvh_tree.primitives[leaf_node.child_or_prim_start + j]
|
||||
other_body := &sim_state.bodies_slice[other_body_idx]
|
||||
prim_aabb := dyn_tlas.body_aabbs[other_body_idx]
|
||||
|
||||
pair := make_contact_pair_bodies(i32(body_idx), i32(other_body_idx))
|
||||
if body_idx != other_body_idx &&
|
||||
(bvh.test_aabb_vs_aabb(body_aabb, prim_aabb)) &&
|
||||
!(pair in sim_state.contact_container.lookup) {
|
||||
(bvh.test_aabb_vs_aabb(body_aabb, prim_aabb)) {
|
||||
shapes_a_it := shapes_iterator(sim_state, body.shape)
|
||||
for shape_a in shapes_iterator_next(&shapes_a_it) {
|
||||
shape_a_idx := shapes_a_it.counter - 1
|
||||
|
||||
new_contact_idx := len(sim_state.contact_container.contacts)
|
||||
resize_soa(&sim_state.contact_container.contacts, new_contact_idx + 1)
|
||||
contact := &sim_state.contact_container.contacts[new_contact_idx]
|
||||
shape_a_aabb := shape_get_aabb(shape_a^)
|
||||
shape_a_aabb = body_transform_shape_aabb(body, shape_a_aabb)
|
||||
|
||||
contact^ = Contact {
|
||||
type = .Body_vs_Body,
|
||||
a = Body_Handle(i + 1),
|
||||
b = i32(other_body_idx + 1),
|
||||
shapes_b_it := shapes_iterator(sim_state, other_body.shape)
|
||||
|
||||
for shape_b in shapes_iterator_next(&shapes_b_it) {
|
||||
shape_b_idx := shapes_b_it.counter - 1
|
||||
|
||||
shape_b_aabb := shape_get_aabb(shape_b^)
|
||||
shape_b_aabb = body_transform_shape_aabb(
|
||||
other_body,
|
||||
shape_b_aabb,
|
||||
)
|
||||
|
||||
pair := make_contact_pair_bodies(
|
||||
i32(body_idx),
|
||||
i32(other_body_idx),
|
||||
shape_a_idx,
|
||||
shape_b_idx,
|
||||
)
|
||||
|
||||
bvh_aabb_a := bvh.AABB {
|
||||
min = shape_a_aabb.center - shape_a_aabb.extent,
|
||||
max = shape_a_aabb.center + shape_a_aabb.extent,
|
||||
}
|
||||
bvh_aabb_b := bvh.AABB {
|
||||
min = shape_b_aabb.center - shape_b_aabb.extent,
|
||||
max = shape_b_aabb.center + shape_b_aabb.extent,
|
||||
}
|
||||
|
||||
if bvh.test_aabb_vs_aabb(bvh_aabb_a, bvh_aabb_b) &&
|
||||
!(pair in sim_state.contact_container.lookup) {
|
||||
new_contact_idx := len(
|
||||
sim_state.contact_container.contacts,
|
||||
)
|
||||
resize_soa(
|
||||
&sim_state.contact_container.contacts,
|
||||
new_contact_idx + 1,
|
||||
)
|
||||
contact := &sim_state.contact_container.contacts[new_contact_idx]
|
||||
|
||||
contact^ = Contact {
|
||||
type = .Body_vs_Body,
|
||||
a = Body_Handle(i + 1),
|
||||
b = i32(other_body_idx + 1),
|
||||
shape_a = shape_a_idx,
|
||||
shape_b = shape_b_idx,
|
||||
}
|
||||
|
||||
sim_state.contact_container.lookup[pair] = i32(
|
||||
new_contact_idx,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sim_state.contact_container.lookup[pair] = i32(new_contact_idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -471,21 +554,43 @@ find_new_contacts :: proc(
|
||||
prim_aabb := get_triangle_aabb(tri)
|
||||
level_geom_handle := static_tlas.tri_to_level_geom[tri_idx]
|
||||
|
||||
pair := make_contact_pair_body_level(i32(body_idx), i32(tri_idx))
|
||||
aabb_overlaps := bvh.test_aabb_vs_aabb(body_aabb, prim_aabb) || true
|
||||
if aabb_overlaps && !(pair in sim_state.contact_container.lookup) {
|
||||
new_contact_idx := len(sim_state.contact_container.contacts)
|
||||
resize_soa(&sim_state.contact_container.contacts, new_contact_idx + 1)
|
||||
contact := &sim_state.contact_container.contacts[new_contact_idx]
|
||||
if bvh.test_aabb_vs_aabb(body_aabb, prim_aabb) {
|
||||
|
||||
contact^ = Contact {
|
||||
type = .Body_vs_Level,
|
||||
a = index_to_body_handle(i),
|
||||
b = i32(level_geom_handle),
|
||||
tri_idx = i32(tri_idx),
|
||||
shapes_it := shapes_iterator(sim_state, body.shape)
|
||||
for shape, shape_idx in shapes_iterator_next(&shapes_it) {
|
||||
shape_aabb := shape_get_aabb(shape^)
|
||||
shape_aabb = body_transform_shape_aabb(body, shape_aabb)
|
||||
bvh_shape_aabb := bvh.AABB {
|
||||
min = shape_aabb.center - shape_aabb.extent,
|
||||
max = shape_aabb.center + shape_aabb.extent,
|
||||
}
|
||||
|
||||
pair := make_contact_pair_body_level(
|
||||
i32(body_idx),
|
||||
shape_idx,
|
||||
i32(tri_idx),
|
||||
)
|
||||
|
||||
if bvh.test_aabb_vs_aabb(prim_aabb, bvh_shape_aabb) &&
|
||||
!(pair in sim_state.contact_container.lookup) {
|
||||
new_contact_idx := len(sim_state.contact_container.contacts)
|
||||
resize_soa(
|
||||
&sim_state.contact_container.contacts,
|
||||
new_contact_idx + 1,
|
||||
)
|
||||
contact := &sim_state.contact_container.contacts[new_contact_idx]
|
||||
|
||||
contact^ = Contact {
|
||||
type = .Body_vs_Level,
|
||||
a = index_to_body_handle(i),
|
||||
shape_a = shape_idx,
|
||||
b = i32(level_geom_handle),
|
||||
tri_idx = i32(tri_idx),
|
||||
}
|
||||
|
||||
sim_state.contact_container.lookup[pair] = i32(new_contact_idx)
|
||||
}
|
||||
}
|
||||
|
||||
sim_state.contact_container.lookup[pair] = i32(new_contact_idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -557,6 +662,8 @@ Contact :: struct {
|
||||
a: Body_Handle,
|
||||
// Body_vs_Body - Body_Handle, Body_vs_Level - Level_Geom_Handle
|
||||
b: i32,
|
||||
// shape index in each body
|
||||
shape_a, shape_b: i32,
|
||||
tri_idx: i32,
|
||||
prev_x_a, prev_x_b: Vec3,
|
||||
prev_q_a, prev_q_b: Quat,
|
||||
@ -622,12 +729,12 @@ update_contacts :: proc(sim_state: ^Sim_State, static_tlas: ^Static_TLAS) {
|
||||
// continue
|
||||
// }
|
||||
|
||||
m1 := body_get_convex_shape_world(sim_state, body)
|
||||
m1 := body_get_convex_shape_by_linear_index_world(sim_state, body, contact.shape_a)
|
||||
m2: collision.Convex
|
||||
|
||||
switch contact.type {
|
||||
case .Body_vs_Body:
|
||||
m2 = body_get_convex_shape_world(sim_state, body2)
|
||||
m2 = body_get_convex_shape_by_linear_index_world(sim_state, body2, contact.shape_b)
|
||||
case .Body_vs_Level:
|
||||
// level_geom := get_level_geom(sim_state, Level_Geom_Handle(contact.b))
|
||||
tri := get_triangle(
|
||||
@ -755,6 +862,7 @@ pgs_solve_contacts :: proc(
|
||||
|
||||
inv_w := 1.0 / w
|
||||
|
||||
|
||||
{
|
||||
// r1, r2 := p1 - body1.x, p2 - body2.x
|
||||
v1 := body_velocity_at_point(body1, p1)
|
||||
@ -1409,12 +1517,12 @@ simulate_step :: proc(scene: ^Scene, sim_state: ^Sim_State, config: Solver_Confi
|
||||
dt := f32(dt_64)
|
||||
inv_dt := f32(inv_dt_64)
|
||||
|
||||
static_tlas := build_static_tlas(sim_state)
|
||||
dyn_tlas := build_dynamic_tlas(sim_state, config)
|
||||
sim_state.static_tlas = build_static_tlas(sim_state)
|
||||
build_dynamic_tlas(sim_state, config, &sim_state.dynamic_tlas)
|
||||
|
||||
remove_invalid_contacts(sim_state, static_tlas, dyn_tlas)
|
||||
find_new_contacts(sim_state, &static_tlas, &dyn_tlas)
|
||||
update_contacts(sim_state, &static_tlas)
|
||||
remove_invalid_contacts(sim_state, sim_state.static_tlas, sim_state.dynamic_tlas)
|
||||
find_new_contacts(sim_state, &sim_state.static_tlas, &sim_state.dynamic_tlas)
|
||||
update_contacts(sim_state, &sim_state.static_tlas)
|
||||
|
||||
Solver :: enum {
|
||||
XPBD,
|
||||
@ -1430,7 +1538,14 @@ simulate_step :: proc(scene: ^Scene, sim_state: ^Sim_State, config: Solver_Confi
|
||||
}
|
||||
case .PGS:
|
||||
for _ in 0 ..< substeps {
|
||||
pgs_substep(sim_state, &static_tlas, &dyn_tlas, config, dt, inv_dt)
|
||||
pgs_substep(
|
||||
sim_state,
|
||||
&sim_state.static_tlas,
|
||||
&sim_state.dynamic_tlas,
|
||||
config,
|
||||
dt,
|
||||
inv_dt,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1564,11 +1679,7 @@ multiply_inv_intertia :: proc(body: Body_Ptr, vec: Vec3) -> (result: Vec3) {
|
||||
return result
|
||||
}
|
||||
|
||||
apply_velocity_correction :: #force_inline proc "contextless" (
|
||||
body: Body_Ptr,
|
||||
impulse: Vec3,
|
||||
pos: Vec3,
|
||||
) {
|
||||
apply_velocity_correction :: #force_inline proc(body: Body_Ptr, impulse: Vec3, pos: Vec3) {
|
||||
apply_velocity_correction_linear(body, impulse)
|
||||
|
||||
angular_impulse := lg.cross(pos - body.x, impulse)
|
||||
|
@ -26,8 +26,13 @@
|
||||
|
||||
package ui
|
||||
|
||||
import "base:builtin"
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "core:fmt"
|
||||
import "core:math"
|
||||
import "core:mem"
|
||||
import "core:reflect"
|
||||
import "core:sort"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
@ -1806,6 +1811,384 @@ end_panel :: proc(ctx: ^Context) {
|
||||
pop_container(ctx)
|
||||
}
|
||||
|
||||
keyval :: proc(ctx: ^Context, key: string, val: any, key_width := i32(100)) {
|
||||
layout_row(ctx, {key_width, -1}, 0)
|
||||
label(ctx, key)
|
||||
label(ctx, fmt.tprintf("%v", val))
|
||||
}
|
||||
|
||||
// inspect_array(ctx, ptr, n, info.elem_size, info.elem)
|
||||
inspect_array :: proc(
|
||||
ctx: ^Context,
|
||||
name: string,
|
||||
ptr: rawptr,
|
||||
n: int,
|
||||
elem_size: int,
|
||||
type_info: ^runtime.Type_Info,
|
||||
) {
|
||||
if ptr == nil && n > 0 {
|
||||
keyval(ctx, name, "nil")
|
||||
return
|
||||
}
|
||||
|
||||
if .ACTIVE in treenode(ctx, fmt.tprintf("%s len(%v)", name, n)) {
|
||||
for i in 0 ..< n {
|
||||
data := uintptr(ptr) + uintptr(elem_size * i)
|
||||
inspect_value(ctx, fmt.tprintf("[%v]", i), any{rawptr(data), type_info.id})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inspect_struct :: proc(
|
||||
ctx: ^Context,
|
||||
name: string,
|
||||
v: any,
|
||||
info: runtime.Type_Info_Struct,
|
||||
type_name: string,
|
||||
) {
|
||||
if .ACTIVE in treenode(ctx, name) {
|
||||
if .raw_union in info.flags {
|
||||
if type_name == "" {
|
||||
keyval(ctx, name, "(raw union)")
|
||||
} else {
|
||||
keyval(ctx, name, fmt.tprintf("%v{}", type_name))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
is_soa := info.soa_kind != .None
|
||||
|
||||
// is_empty := info.field_count == 0
|
||||
|
||||
if is_soa {
|
||||
base_type_name: string
|
||||
if v, ok := info.soa_base_type.variant.(runtime.Type_Info_Named); ok {
|
||||
base_type_name = v.name
|
||||
}
|
||||
|
||||
actual_field_count := info.field_count
|
||||
|
||||
n := uintptr(info.soa_len)
|
||||
|
||||
if info.soa_kind == .Slice {
|
||||
actual_field_count = info.field_count - 1 // len
|
||||
|
||||
n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^)
|
||||
|
||||
} else if info.soa_kind == .Dynamic {
|
||||
actual_field_count = info.field_count - 3 // len, cap, allocator
|
||||
|
||||
n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^)
|
||||
}
|
||||
|
||||
for index in 0 ..< n {
|
||||
field_count := -1
|
||||
|
||||
for i in 0 ..< actual_field_count {
|
||||
field_name := info.names[i]
|
||||
field_count += 1
|
||||
|
||||
if info.soa_kind == .Fixed {
|
||||
t := info.types[i].variant.(runtime.Type_Info_Array).elem
|
||||
t_size := uintptr(t.size)
|
||||
if reflect.is_any(t) {
|
||||
keyval(ctx, field_name, "any{}")
|
||||
} else {
|
||||
data := rawptr(uintptr(v.data) + info.offsets[i] + index * t_size)
|
||||
inspect_value(ctx, field_name, any{data, t.id})
|
||||
}
|
||||
} else {
|
||||
t := info.types[i].variant.(runtime.Type_Info_Multi_Pointer).elem
|
||||
t_size := uintptr(t.size)
|
||||
if reflect.is_any(t) {
|
||||
keyval(ctx, field_name, "any{}")
|
||||
} else {
|
||||
field_ptr := (^^byte)(uintptr(v.data) + info.offsets[i])^
|
||||
data := rawptr(uintptr(field_ptr) + index * t_size)
|
||||
inspect_value(ctx, field_name, any{data, t.id})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
field_count := -1
|
||||
for field_name, i in info.names[:info.field_count] {
|
||||
field_count += 1
|
||||
|
||||
if t := info.types[i]; reflect.is_any(t) {
|
||||
keyval(ctx, field_name, "any{}")
|
||||
} else {
|
||||
data := rawptr(uintptr(v.data) + info.offsets[i])
|
||||
inspect_value(ctx, field_name, any{data, t.id})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is_type_numeric :: proc(type_info: ^runtime.Type_Info) -> bool {
|
||||
#partial switch info in type_info.variant {
|
||||
case runtime.Type_Info_Float, runtime.Type_Info_Integer:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
inspect_value :: proc(ctx: ^Context, name: string, v: any, type_name: string = "") {
|
||||
if v.data == nil || v.id == nil {
|
||||
label(ctx, "<nil>")
|
||||
return
|
||||
}
|
||||
|
||||
type_info := type_info_of(v.id)
|
||||
switch info in type_info.variant {
|
||||
case runtime.Type_Info_Any: // Ignore
|
||||
case runtime.Type_Info_Parameters: // Ignore
|
||||
case runtime.Type_Info_Named:
|
||||
inspect_value(ctx, name, any{v.data, info.base.id}, info.name)
|
||||
|
||||
case runtime.Type_Info_Boolean,
|
||||
runtime.Type_Info_Integer,
|
||||
runtime.Type_Info_Rune,
|
||||
runtime.Type_Info_Float,
|
||||
runtime.Type_Info_Complex,
|
||||
runtime.Type_Info_Quaternion,
|
||||
runtime.Type_Info_Simd_Vector,
|
||||
runtime.Type_Info_Type_Id,
|
||||
runtime.Type_Info_Bit_Set,
|
||||
runtime.Type_Info_Matrix,
|
||||
runtime.Type_Info_Bit_Field,
|
||||
runtime.Type_Info_Procedure,
|
||||
runtime.Type_Info_String,
|
||||
runtime.Type_Info_Enum:
|
||||
if len(type_name) != 0 {
|
||||
keyval(ctx, name, fmt.tprintf("%s(%v)", type_name, v))
|
||||
} else {
|
||||
keyval(ctx, name, fmt.tprintf("%v", v))
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Pointer:
|
||||
// if v.id == typeid_of(^runtime.Type_Info) {
|
||||
// reflect.write_type(fi.writer, (^^runtime.Type_Info)(v.data)^, &fi.n)
|
||||
// } else {
|
||||
// ptr := (^rawptr)(v.data)^
|
||||
// if verb != 'p' && info.elem != nil {
|
||||
// a := any{ptr, info.elem.id}
|
||||
|
||||
// elem := runtime.type_info_base(info.elem)
|
||||
// if elem != nil {
|
||||
// #partial switch e in elem.variant {
|
||||
// case runtime.Type_Info_Array,
|
||||
// runtime.Type_Info_Slice,
|
||||
// runtime.Type_Info_Dynamic_Array,
|
||||
// runtime.Type_Info_Map:
|
||||
// if ptr == nil {
|
||||
// io.write_string(fi.writer, "<nil>", &fi.n)
|
||||
// return
|
||||
// }
|
||||
// if fi.indirection_level < 1 {
|
||||
// fi.indirection_level += 1
|
||||
// defer fi.indirection_level -= 1
|
||||
// io.write_byte(fi.writer, '&')
|
||||
// fmt_value(fi, a, verb)
|
||||
// return
|
||||
// }
|
||||
|
||||
// case runtime.Type_Info_Struct,
|
||||
// runtime.Type_Info_Union,
|
||||
// runtime.Type_Info_Bit_Field:
|
||||
// if ptr == nil {
|
||||
// io.write_string(fi.writer, "<nil>", &fi.n)
|
||||
// return
|
||||
// }
|
||||
// if fi.indirection_level < 1 {
|
||||
// fi.indirection_level += 1
|
||||
// defer fi.indirection_level -= 1
|
||||
// io.write_byte(fi.writer, '&', &fi.n)
|
||||
// fmt_value(fi, a, verb)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// fmt_pointer(fi, ptr, verb)
|
||||
// }
|
||||
|
||||
case runtime.Type_Info_Soa_Pointer:
|
||||
// ptr := (^runtime.Raw_Soa_Pointer)(v.data)^
|
||||
// fmt_soa_pointer(fi, ptr, verb)
|
||||
|
||||
case runtime.Type_Info_Multi_Pointer:
|
||||
// ptr := (^rawptr)(v.data)^
|
||||
// if ptr == nil {
|
||||
// io.write_string(fi.writer, "<nil>", &fi.n)
|
||||
// return
|
||||
// }
|
||||
// if verb != 'p' && info.elem != nil {
|
||||
// a := any{ptr, info.elem.id}
|
||||
|
||||
// elem := runtime.type_info_base(info.elem)
|
||||
// if elem != nil {
|
||||
// if n, ok := fi.optional_len.?; ok {
|
||||
// fi.optional_len = nil
|
||||
// fmt_array(fi, ptr, n, elem.size, elem, verb)
|
||||
// return
|
||||
// } else if fi.use_nul_termination {
|
||||
// fi.use_nul_termination = false
|
||||
// fmt_array_nul_terminated(fi, ptr, -1, elem.size, elem, verb)
|
||||
// return
|
||||
// }
|
||||
|
||||
// #partial switch e in elem.variant {
|
||||
// case runtime.Type_Info_Integer:
|
||||
// switch verb {
|
||||
// case 's', 'q':
|
||||
// switch elem.id {
|
||||
// case u8:
|
||||
// fmt_cstring(fi, cstring(ptr), verb)
|
||||
// return
|
||||
// case u16, u32, rune:
|
||||
// n := search_nul_termination(ptr, elem.size, -1)
|
||||
// fmt_array(fi, ptr, n, elem.size, elem, verb)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
// case runtime.Type_Info_Array,
|
||||
// runtime.Type_Info_Slice,
|
||||
// runtime.Type_Info_Dynamic_Array,
|
||||
// runtime.Type_Info_Map:
|
||||
// if fi.indirection_level < 1 {
|
||||
// fi.indirection_level += 1
|
||||
// defer fi.indirection_level -= 1
|
||||
// io.write_byte(fi.writer, '&', &fi.n)
|
||||
// fmt_value(fi, a, verb)
|
||||
// return
|
||||
// }
|
||||
|
||||
// case runtime.Type_Info_Struct, runtime.Type_Info_Union:
|
||||
// if fi.indirection_level < 1 {
|
||||
// fi.indirection_level += 1
|
||||
// defer fi.indirection_level -= 1
|
||||
// io.write_byte(fi.writer, '&', &fi.n)
|
||||
// fmt_value(fi, a, verb)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// fmt_pointer(fi, ptr, verb)
|
||||
|
||||
case runtime.Type_Info_Enumerated_Array:
|
||||
// fi.record_level += 1
|
||||
// defer fi.record_level -= 1
|
||||
|
||||
// if fi.hash {
|
||||
// io.write_byte(fi.writer, '[' if verb != 'w' else '{', &fi.n)
|
||||
// io.write_byte(fi.writer, '\n', &fi.n)
|
||||
// defer {
|
||||
// fmt_write_indent(fi)
|
||||
// io.write_byte(fi.writer, ']' if verb != 'w' else '}', &fi.n)
|
||||
// }
|
||||
// indent := fi.indent
|
||||
// fi.indent += 1
|
||||
// defer fi.indent = indent
|
||||
|
||||
// for i in 0 ..< info.count {
|
||||
// fmt_write_indent(fi)
|
||||
|
||||
// idx, ok := stored_enum_value_to_string(info.index, info.min_value, i)
|
||||
// if ok {
|
||||
// io.write_byte(fi.writer, '.', &fi.n)
|
||||
// io.write_string(fi.writer, idx, &fi.n)
|
||||
// } else {
|
||||
// io.write_i64(fi.writer, i64(info.min_value) + i64(i), 10, &fi.n)
|
||||
// }
|
||||
// io.write_string(fi.writer, " = ", &fi.n)
|
||||
|
||||
// data := uintptr(v.data) + uintptr(i * info.elem_size)
|
||||
// fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
|
||||
|
||||
// io.write_string(fi.writer, ",\n", &fi.n)
|
||||
// }
|
||||
// } else {
|
||||
// io.write_byte(fi.writer, '[' if verb != 'w' else '{', &fi.n)
|
||||
// defer io.write_byte(fi.writer, ']' if verb != 'w' else '}', &fi.n)
|
||||
// for i in 0 ..< info.count {
|
||||
// if i > 0 {io.write_string(fi.writer, ", ", &fi.n)}
|
||||
|
||||
// idx, ok := stored_enum_value_to_string(info.index, info.min_value, i)
|
||||
// if ok {
|
||||
// io.write_byte(fi.writer, '.', &fi.n)
|
||||
// io.write_string(fi.writer, idx, &fi.n)
|
||||
// } else {
|
||||
// io.write_i64(fi.writer, i64(info.min_value) + i64(i), 10, &fi.n)
|
||||
// }
|
||||
// io.write_string(fi.writer, " = ", &fi.n)
|
||||
|
||||
// data := uintptr(v.data) + uintptr(i * info.elem_size)
|
||||
// fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
|
||||
// }
|
||||
// }
|
||||
|
||||
case runtime.Type_Info_Array:
|
||||
n := info.count
|
||||
ptr := v.data
|
||||
if info.count <= 4 && is_type_numeric(info.elem) {
|
||||
keyval(ctx, name, v)
|
||||
} else {
|
||||
inspect_array(ctx, name, ptr, n, info.elem_size, info.elem)
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Slice:
|
||||
slice := cast(^mem.Raw_Slice)v.data
|
||||
n := slice.len
|
||||
ptr := slice.data
|
||||
inspect_array(ctx, name, ptr, n, info.elem_size, info.elem)
|
||||
|
||||
case runtime.Type_Info_Dynamic_Array:
|
||||
array := cast(^mem.Raw_Dynamic_Array)v.data
|
||||
n := array.len
|
||||
ptr := array.data
|
||||
inspect_array(ctx, name, ptr, n, info.elem_size, info.elem)
|
||||
|
||||
case runtime.Type_Info_Map:
|
||||
if .ACTIVE in treenode(ctx, name) {
|
||||
m := (^mem.Raw_Map)(v.data)
|
||||
if m != nil {
|
||||
if info.map_info == nil {
|
||||
return
|
||||
}
|
||||
map_cap := uintptr(runtime.map_cap(m^))
|
||||
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
|
||||
j := 0
|
||||
for bucket_index in 0 ..< map_cap {
|
||||
runtime.map_hash_is_valid(hs[bucket_index]) or_continue
|
||||
|
||||
j += 1
|
||||
|
||||
key := runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index)
|
||||
value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index)
|
||||
|
||||
|
||||
inspect_value(
|
||||
ctx,
|
||||
fmt.tprintf("%v", any{rawptr(key), info.key.id}),
|
||||
any{rawptr(value), info.value.id},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Struct:
|
||||
inspect_struct(ctx, name, v, info, "")
|
||||
|
||||
case runtime.Type_Info_Union:
|
||||
// fmt_union(fi, v, verb, info, type_info.size)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
mouse_released :: #force_inline proc(ctx: ^Context) -> bool {return ctx.mouse_released_bits != nil}
|
||||
@(private)
|
||||
|
Loading…
x
Reference in New Issue
Block a user