205 lines
5.2 KiB
Odin
205 lines
5.2 KiB
Odin
package physics
|
|
|
|
import lg "core:math/linalg"
|
|
import rl "vendor:raylib"
|
|
|
|
Body_Config_Inertia_Mode :: enum {
|
|
From_Shape,
|
|
Explicit,
|
|
}
|
|
|
|
// Immediate mode stuff for testing
|
|
Body_Config :: struct {
|
|
initial_pos: rl.Vector3,
|
|
initial_rot: rl.Quaternion,
|
|
initial_vel: rl.Vector3,
|
|
initial_ang_vel: rl.Vector3,
|
|
shape: Collision_Shape,
|
|
mass: f32,
|
|
inertia_mode: Body_Config_Inertia_Mode,
|
|
// Unit inertia tensor
|
|
inertia_tensor: rl.Vector3,
|
|
}
|
|
|
|
// TODO: rename to wheel
|
|
Suspension_Constraint_Config :: struct {
|
|
rel_pos: rl.Vector3,
|
|
rel_dir: rl.Vector3,
|
|
body: Body_Handle,
|
|
rest: f32,
|
|
compliance: f32,
|
|
damping: f32,
|
|
radius: f32,
|
|
}
|
|
|
|
calculate_body_params_from_config :: proc(
|
|
config: Body_Config,
|
|
) -> (
|
|
inv_mass: f32,
|
|
inv_inertia_tensor: rl.Vector3,
|
|
) {
|
|
inv_mass = config.mass == 0 ? 0 : 1.0 / config.mass
|
|
|
|
inertia_tensor: rl.Vector3
|
|
if config.inertia_mode == .Explicit {
|
|
inertia_tensor = config.inertia_tensor
|
|
} else {
|
|
inertia_tensor = inertia_tensor_collision_shape(config.shape)
|
|
}
|
|
inertia_tensor *= config.mass
|
|
|
|
inv_inertia_tensor = lg.all(lg.equal(inertia_tensor, rl.Vector3(0))) ? 0 : 1.0 / inertia_tensor
|
|
|
|
return
|
|
}
|
|
|
|
initialize_body_from_config :: proc(body: ^Body, config: Body_Config) {
|
|
body.x = config.initial_pos
|
|
body.q = config.initial_rot
|
|
body.v = config.initial_vel
|
|
body.w = config.initial_ang_vel
|
|
body.shape = config.shape
|
|
|
|
body.inv_mass, body.inv_inertia_tensor = calculate_body_params_from_config(config)
|
|
}
|
|
|
|
update_body_from_config :: proc(body: Body_Ptr, config: Body_Config) {
|
|
body.shape = config.shape
|
|
body.inv_mass, body.inv_inertia_tensor = calculate_body_params_from_config(config)
|
|
}
|
|
|
|
update_suspension_constraint_from_config :: proc(
|
|
constraint: Suspension_Constraint_Ptr,
|
|
config: Suspension_Constraint_Config,
|
|
) {
|
|
constraint.rel_pos = config.rel_pos
|
|
constraint.rel_dir = config.rel_dir
|
|
constraint.body = config.body
|
|
constraint.rest = config.rest
|
|
constraint.compliance = config.compliance
|
|
constraint.damping = config.damping
|
|
constraint.radius = config.radius
|
|
}
|
|
|
|
immediate_body :: proc(
|
|
scene: ^Scene,
|
|
state: ^Solver_State,
|
|
id: u32,
|
|
config: Body_Config,
|
|
) -> (
|
|
handle: Body_Handle,
|
|
) {
|
|
if id in state.immedate_bodies {
|
|
body := &state.immedate_bodies[id]
|
|
if body.last_ref != state.simulation_frame {
|
|
body.last_ref = state.simulation_frame
|
|
state.num_referenced_bodies += 1
|
|
}
|
|
handle = body.handle
|
|
update_body_from_config(get_body(scene, handle), config)
|
|
} else {
|
|
new_body: Body
|
|
state.num_referenced_bodies += 1
|
|
initialize_body_from_config(&new_body, config)
|
|
handle = add_body(scene, new_body)
|
|
state.immedate_bodies[id] = {
|
|
handle = handle,
|
|
last_ref = state.simulation_frame,
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
immediate_suspension_constraint :: proc(
|
|
scene: ^Scene,
|
|
state: ^Solver_State,
|
|
id: u32,
|
|
config: Suspension_Constraint_Config,
|
|
) -> (
|
|
handle: Suspension_Constraint_Handle,
|
|
) {
|
|
if id in state.immediate_suspension_constraints {
|
|
constraint := &state.immediate_suspension_constraints[id]
|
|
if constraint.last_ref != state.simulation_frame {
|
|
constraint.last_ref = state.simulation_frame
|
|
state.num_referenced_suspension_constraints += 1
|
|
}
|
|
handle = constraint.handle
|
|
} else {
|
|
state.num_referenced_suspension_constraints += 1
|
|
handle = add_suspension_constraint(scene, {})
|
|
state.immediate_suspension_constraints[id] = {
|
|
handle = handle,
|
|
last_ref = state.simulation_frame,
|
|
}
|
|
}
|
|
|
|
update_suspension_constraint_from_config(get_suspension_constraint(scene, handle), config)
|
|
|
|
return
|
|
}
|
|
|
|
prune_immediate :: proc(scene: ^Scene, state: ^Solver_State) {
|
|
prune_immediate_bodies(scene, state)
|
|
prune_immediate_suspension_constraints(scene, state)
|
|
}
|
|
|
|
// TODO: Generic version
|
|
prune_immediate_bodies :: proc(scene: ^Scene, state: ^Solver_State) {
|
|
if int(state.num_referenced_bodies) == len(state.immedate_bodies) {
|
|
return
|
|
}
|
|
|
|
num_unreferenced_bodies := len(state.immedate_bodies) - int(state.num_referenced_bodies)
|
|
assert(num_unreferenced_bodies >= 0)
|
|
|
|
bodies_to_remove := make([]u32, num_unreferenced_bodies, context.temp_allocator)
|
|
|
|
i := 0
|
|
for k, &v in state.immedate_bodies {
|
|
if v.last_ref != state.simulation_frame {
|
|
bodies_to_remove[i] = k
|
|
i += 1
|
|
}
|
|
}
|
|
|
|
assert(i == len(bodies_to_remove))
|
|
|
|
for k in bodies_to_remove {
|
|
handle := state.immedate_bodies[k].handle
|
|
delete_key(&state.immedate_bodies, k)
|
|
remove_body(scene, handle)
|
|
}
|
|
}
|
|
|
|
prune_immediate_suspension_constraints :: proc(scene: ^Scene, state: ^Solver_State) {
|
|
if int(state.num_referenced_suspension_constraints) ==
|
|
len(state.immediate_suspension_constraints) {
|
|
return
|
|
}
|
|
|
|
num_unreferenced_constraints :=
|
|
len(state.immediate_suspension_constraints) -
|
|
int(state.num_referenced_suspension_constraints)
|
|
assert(num_unreferenced_constraints >= 0)
|
|
|
|
constraints_to_remove := make([]u32, num_unreferenced_constraints, context.temp_allocator)
|
|
|
|
i := 0
|
|
for k, &v in state.immediate_suspension_constraints {
|
|
if v.last_ref != state.simulation_frame {
|
|
constraints_to_remove[i] = k
|
|
i += 1
|
|
}
|
|
}
|
|
|
|
assert(i == len(constraints_to_remove))
|
|
|
|
for k in constraints_to_remove {
|
|
handle := state.immediate_suspension_constraints[k].handle
|
|
delete_key(&state.immediate_suspension_constraints, k)
|
|
remove_suspension_constraint(scene, handle)
|
|
}
|
|
}
|