191 lines
5.1 KiB
Odin
191 lines
5.1 KiB
Odin
package physics
|
|
|
|
import rl "vendor:raylib"
|
|
|
|
Scene :: struct {
|
|
bodies: #soa[dynamic]Body,
|
|
suspension_constraints: #soa[dynamic]Suspension_Constraint,
|
|
first_free_body_plus_one: i32,
|
|
first_free_suspension_constraint_plus_one: i32,
|
|
|
|
// 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,
|
|
}
|
|
|
|
Body :: struct {
|
|
// Is this body alive (if not it doesn't exist)
|
|
alive: bool,
|
|
// Pos
|
|
x: rl.Vector3,
|
|
// Linear vel
|
|
v: rl.Vector3,
|
|
// Orientation
|
|
q: rl.Quaternion,
|
|
// Angular vel (omega)
|
|
w: rl.Vector3,
|
|
// Mass
|
|
inv_mass: f32,
|
|
// Moment of inertia
|
|
inv_inertia_tensor: rl.Vector3,
|
|
//
|
|
next_plus_one: i32,
|
|
}
|
|
|
|
Suspension_Constraint :: struct {
|
|
alive: bool,
|
|
// Pos relative to the body
|
|
rel_pos: rl.Vector3,
|
|
// Dir relative to the body
|
|
rel_dir: rl.Vector3,
|
|
// Handle of the rigid body
|
|
body: Body_Handle,
|
|
// Wheel radius
|
|
radius: f32,
|
|
// Rest distance
|
|
rest: f32,
|
|
// Inverse stiffness
|
|
compliance: f32,
|
|
// How much to damp velocity of the spring
|
|
damping: f32,
|
|
|
|
// Runtime state
|
|
hit: bool,
|
|
hit_point: rl.Vector3,
|
|
// rel_hit_point = rel_pos + rel_dir * hit_t
|
|
hit_t: f32,
|
|
turn_angle: f32,
|
|
drive_impulse: f32,
|
|
brake_impulse: f32,
|
|
applied_impulse: rl.Vector3,
|
|
|
|
// Free list
|
|
next_plus_one: i32,
|
|
}
|
|
|
|
// Index plus one, so handle 0 maps to invalid body
|
|
Body_Handle :: distinct i32
|
|
Suspension_Constraint_Handle :: distinct i32
|
|
|
|
INVALID_BODY :: Body_Handle(0)
|
|
INVALID_SUSPENSION_CONSTRAINT :: Suspension_Constraint_Handle(0)
|
|
|
|
is_body_handle_valid :: proc(handle: Body_Handle) -> bool {
|
|
return i32(handle) > 0
|
|
}
|
|
is_suspension_constraint_handle_valid :: proc(handle: Suspension_Constraint_Handle) -> bool {
|
|
return i32(handle) > 0
|
|
}
|
|
is_handle_valid :: proc {
|
|
is_body_handle_valid,
|
|
is_suspension_constraint_handle_valid,
|
|
}
|
|
|
|
Body_Ptr :: #soa^#soa[]Body
|
|
Suspension_Constraint_Ptr :: #soa^#soa[]Suspension_Constraint
|
|
|
|
_invalid_body: #soa[1]Body
|
|
_invalid_body_slice := _invalid_body[:]
|
|
|
|
_invalid_suspension_constraint: #soa[1]Suspension_Constraint
|
|
_invalid_suspension_constraint_slice := _invalid_suspension_constraint[:]
|
|
|
|
/// Returns pointer to soa slice. NEVER STORE IT
|
|
get_body :: proc(scene: ^Scene, handle: Body_Handle) -> Body_Ptr {
|
|
index := int(handle) - 1
|
|
if index < 0 || index >= len(scene.bodies_slice) {
|
|
return &_invalid_body_slice[0]
|
|
}
|
|
|
|
return &scene.bodies_slice[index]
|
|
}
|
|
|
|
add_body :: proc(scene: ^Scene, body: Body) -> Body_Handle {
|
|
body_copy := body
|
|
|
|
body_copy.alive = true
|
|
body_copy.next_plus_one = 0
|
|
|
|
if scene.first_free_body_plus_one > 0 {
|
|
index := scene.first_free_body_plus_one
|
|
new_body := get_body(scene, Body_Handle(index))
|
|
next_plus_one := new_body.next_plus_one
|
|
new_body^ = body_copy
|
|
scene.first_free_body_plus_one = next_plus_one
|
|
return Body_Handle(index)
|
|
}
|
|
|
|
append_soa(&scene.bodies, body_copy)
|
|
index := len(scene.bodies)
|
|
|
|
scene.bodies_slice = scene.bodies[:]
|
|
|
|
return Body_Handle(index)
|
|
}
|
|
|
|
remove_body :: proc(scene: ^Scene, handle: Body_Handle) {
|
|
if int(handle) > 1 {
|
|
body := get_body(scene, handle)
|
|
|
|
body.alive = false
|
|
body.next_plus_one = scene.first_free_body_plus_one
|
|
scene.first_free_body_plus_one = i32(handle)
|
|
}
|
|
}
|
|
|
|
/// Returns pointer to soa slice. NEVER STORE IT
|
|
get_suspension_constraint :: proc(
|
|
scene: ^Scene,
|
|
handle: Suspension_Constraint_Handle,
|
|
) -> Suspension_Constraint_Ptr {
|
|
if !is_handle_valid(handle) {
|
|
return &_invalid_suspension_constraint_slice[0]
|
|
}
|
|
|
|
index := int(handle) - 1
|
|
return &scene.suspension_constraints_slice[index]
|
|
}
|
|
|
|
add_suspension_constraint :: proc(
|
|
scene: ^Scene,
|
|
constraint: Suspension_Constraint,
|
|
) -> Suspension_Constraint_Handle {
|
|
copy := constraint
|
|
|
|
copy.alive = true
|
|
copy.next_plus_one = 0
|
|
|
|
if scene.first_free_suspension_constraint_plus_one > 0 {
|
|
index := scene.first_free_suspension_constraint_plus_one
|
|
new_constraint := get_suspension_constraint(scene, Suspension_Constraint_Handle(index))
|
|
next_plus_one := new_constraint.next_plus_one
|
|
new_constraint^ = copy
|
|
scene.first_free_suspension_constraint_plus_one = next_plus_one
|
|
return Suspension_Constraint_Handle(index)
|
|
}
|
|
|
|
append_soa(&scene.suspension_constraints, copy)
|
|
scene.suspension_constraints_slice = scene.suspension_constraints[:]
|
|
index := len(scene.suspension_constraints)
|
|
return Suspension_Constraint_Handle(index)
|
|
}
|
|
|
|
remove_suspension_constraint :: proc(scene: ^Scene, handle: Suspension_Constraint_Handle) {
|
|
if is_handle_valid(handle) {
|
|
constraint := get_suspension_constraint(scene, handle)
|
|
|
|
constraint.alive = false
|
|
constraint.next_plus_one = scene.first_free_suspension_constraint_plus_one
|
|
scene.first_free_suspension_constraint_plus_one = i32(handle)
|
|
}
|
|
}
|
|
|
|
_get_first_free_body :: proc(scene: ^Scene) -> i32 {
|
|
return scene.first_free_body_plus_one - 1
|
|
}
|
|
|
|
destroy_physics_scene :: proc(scene: ^Scene) {
|
|
delete_soa(scene.bodies)
|
|
delete_soa(scene.suspension_constraints)
|
|
}
|