My own rigid body phyiscs that works, wat
This commit is contained in:
parent
bb77e8e821
commit
5c0dd2f9f5
@ -46,7 +46,7 @@ Car :: struct {
|
|||||||
|
|
||||||
SOLVER_CONFIG :: physics.Solver_Config {
|
SOLVER_CONFIG :: physics.Solver_Config {
|
||||||
timestep = 1.0 / 120,
|
timestep = 1.0 / 120,
|
||||||
gravity = rl.Vector3{0, 0, 0},
|
gravity = rl.Vector3{0, -9.8, 0},
|
||||||
}
|
}
|
||||||
|
|
||||||
Game_Memory :: struct {
|
Game_Memory :: struct {
|
||||||
@ -363,7 +363,7 @@ update :: proc() {
|
|||||||
physics.Body_Config {
|
physics.Body_Config {
|
||||||
initial_pos = {0, 1, 0},
|
initial_pos = {0, 1, 0},
|
||||||
initial_rot = linalg.QUATERNIONF32_IDENTITY,
|
initial_rot = linalg.QUATERNIONF32_IDENTITY,
|
||||||
initial_ang_vel = {0, 1, 0},
|
initial_ang_vel = {0, 0, 0},
|
||||||
mass = 100,
|
mass = 100,
|
||||||
inertia_tensor = physics.inertia_tensor_box(car_bounds.max - car_bounds.min),
|
inertia_tensor = physics.inertia_tensor_box(car_bounds.max - car_bounds.min),
|
||||||
},
|
},
|
||||||
@ -374,12 +374,14 @@ update :: proc() {
|
|||||||
g_mem.camera.projection = .PERSPECTIVE
|
g_mem.camera.projection = .PERSPECTIVE
|
||||||
g_mem.camera.target = physics.get_body(&get_world().physics_scene, g_mem.car_handle).x
|
g_mem.camera.target = physics.get_body(&get_world().physics_scene, g_mem.car_handle).x
|
||||||
if g_mem.camera.position == {} {
|
if g_mem.camera.position == {} {
|
||||||
g_mem.camera.position = g_mem.camera.target - rl.Vector3{0, 0, 10}
|
g_mem.camera.position = g_mem.camera.target - rl.Vector3{10, 0, 10}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1.6 is a good value
|
// 1.6 is a good value
|
||||||
wheel_extent_x := f32(2)
|
wheel_extent_x := f32(2.0)
|
||||||
rest := f32(1)
|
rest := f32(0.9)
|
||||||
|
suspension_stiffness := f32(10000)
|
||||||
|
compliance := 1.0 / suspension_stiffness
|
||||||
|
|
||||||
physics.immediate_suspension_constraint(
|
physics.immediate_suspension_constraint(
|
||||||
&get_world().physics_scene,
|
&get_world().physics_scene,
|
||||||
@ -389,6 +391,7 @@ update :: proc() {
|
|||||||
rel_pos = {-wheel_extent_x, 0, 2.5},
|
rel_pos = {-wheel_extent_x, 0, 2.5},
|
||||||
rel_dir = {0, -1, 0},
|
rel_dir = {0, -1, 0},
|
||||||
rest = rest,
|
rest = rest,
|
||||||
|
compliance = compliance,
|
||||||
body = g_mem.car_handle,
|
body = g_mem.car_handle,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -400,6 +403,7 @@ update :: proc() {
|
|||||||
rel_pos = {wheel_extent_x, 0, 2.5},
|
rel_pos = {wheel_extent_x, 0, 2.5},
|
||||||
rel_dir = {0, -1, 0},
|
rel_dir = {0, -1, 0},
|
||||||
rest = rest,
|
rest = rest,
|
||||||
|
compliance = compliance,
|
||||||
body = g_mem.car_handle,
|
body = g_mem.car_handle,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -411,6 +415,7 @@ update :: proc() {
|
|||||||
rel_pos = {-wheel_extent_x, 0, -3},
|
rel_pos = {-wheel_extent_x, 0, -3},
|
||||||
rel_dir = {0, -1, 0},
|
rel_dir = {0, -1, 0},
|
||||||
rest = rest,
|
rest = rest,
|
||||||
|
compliance = compliance,
|
||||||
body = g_mem.car_handle,
|
body = g_mem.car_handle,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -422,6 +427,7 @@ update :: proc() {
|
|||||||
rel_pos = {wheel_extent_x, 0, -3},
|
rel_pos = {wheel_extent_x, 0, -3},
|
||||||
rel_dir = {0, -1, 0},
|
rel_dir = {0, -1, 0},
|
||||||
rest = rest,
|
rest = rest,
|
||||||
|
compliance = compliance,
|
||||||
body = g_mem.car_handle,
|
body = g_mem.car_handle,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -484,6 +490,8 @@ draw :: proc() {
|
|||||||
rl.GetScreenToWorldRay(rl.GetMousePosition(), camera),
|
rl.GetScreenToWorldRay(rl.GetMousePosition(), camera),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
car_model := assets.get_model(&g_mem.assetman, "assets/toyota_corolla_ae86_trueno.glb")
|
||||||
|
|
||||||
{
|
{
|
||||||
rl.BeginMode3D(camera)
|
rl.BeginMode3D(camera)
|
||||||
defer rl.EndMode3D()
|
defer rl.EndMode3D()
|
||||||
@ -504,7 +512,6 @@ draw :: proc() {
|
|||||||
rl.DrawGrid(100, 1)
|
rl.DrawGrid(100, 1)
|
||||||
|
|
||||||
if !g_mem.editor {
|
if !g_mem.editor {
|
||||||
car_model := assets.get_model(&g_mem.assetman, "assets/toyota_corolla_ae86_trueno.glb")
|
|
||||||
|
|
||||||
car_body := physics.get_body(&get_world().physics_scene, g_mem.car_handle)
|
car_body := physics.get_body(&get_world().physics_scene, g_mem.car_handle)
|
||||||
car_matrix := rl.QuaternionToMatrix(car_body.q)
|
car_matrix := rl.QuaternionToMatrix(car_body.q)
|
||||||
@ -596,7 +603,13 @@ draw :: proc() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
car_pos := physics.get_body(&get_world().physics_scene, g_mem.car_handle).x
|
car_pos := physics.get_body(&get_world().physics_scene, g_mem.car_handle).x
|
||||||
rl.DrawText(fmt.ctprintf("Car Pos: %v", car_pos), 5, 32, 8, rl.ORANGE)
|
rl.DrawText(
|
||||||
|
fmt.ctprintf("Car Pos: %v. Mesh count: %v", car_pos, car_model.meshCount),
|
||||||
|
5,
|
||||||
|
32,
|
||||||
|
8,
|
||||||
|
rl.ORANGE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package physics
|
package physics
|
||||||
|
|
||||||
|
import lg "core:math/linalg"
|
||||||
import rl "vendor:raylib"
|
import rl "vendor:raylib"
|
||||||
|
|
||||||
inertia_tensor_box :: proc(size: rl.Vector3) -> (tensor: rl.Vector3) {
|
inertia_tensor_box :: proc(size: rl.Vector3) -> (tensor: rl.Vector3) {
|
||||||
@ -13,3 +14,13 @@ inertia_tensor_box :: proc(size: rl.Vector3) -> (tensor: rl.Vector3) {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body_local_to_world :: #force_inline proc(body: Body_Ptr, pos: rl.Vector3) -> rl.Vector3 {
|
||||||
|
return body.x + lg.quaternion_mul_vector3(body.q, pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
body_world_to_local :: #force_inline proc(body: Body_Ptr, pos: rl.Vector3) -> rl.Vector3 {
|
||||||
|
// TODO: maybe store that
|
||||||
|
inv_q := lg.quaternion_inverse(body.q)
|
||||||
|
return lg.quaternion_mul_vector3(inv_q, pos - body.x)
|
||||||
|
}
|
||||||
|
@ -27,12 +27,12 @@ initialize_body_from_config :: proc(body: ^Body, config: Body_Config) {
|
|||||||
body.v = config.initial_vel
|
body.v = config.initial_vel
|
||||||
body.w = config.initial_ang_vel
|
body.w = config.initial_ang_vel
|
||||||
body.inv_mass = 1.0 / config.mass
|
body.inv_mass = 1.0 / config.mass
|
||||||
body.inv_intertia_tensor = 1.0 / (config.inertia_tensor * config.mass)
|
body.inv_inertia_tensor = 1.0 / (config.inertia_tensor * config.mass)
|
||||||
}
|
}
|
||||||
|
|
||||||
update_body_from_config :: proc(body: Body_Ptr, config: Body_Config) {
|
update_body_from_config :: proc(body: Body_Ptr, config: Body_Config) {
|
||||||
body.inv_mass = 1.0 / config.mass
|
body.inv_mass = 1.0 / config.mass
|
||||||
body.inv_intertia_tensor = 1.0 / (config.inertia_tensor * config.mass)
|
body.inv_inertia_tensor = 1.0 / (config.inertia_tensor * config.mass)
|
||||||
}
|
}
|
||||||
|
|
||||||
update_suspension_constraint_from_config :: proc(
|
update_suspension_constraint_from_config :: proc(
|
||||||
|
@ -7,25 +7,29 @@ Scene :: struct {
|
|||||||
suspension_constraints: #soa[dynamic]Suspension_Constraint,
|
suspension_constraints: #soa[dynamic]Suspension_Constraint,
|
||||||
first_free_body_plus_one: i32,
|
first_free_body_plus_one: i32,
|
||||||
first_free_suspension_constraint_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 {
|
Body :: struct {
|
||||||
// Is this body alive (if not it doesn't exist)
|
// Is this body alive (if not it doesn't exist)
|
||||||
alive: bool,
|
alive: bool,
|
||||||
// Pos
|
// Pos
|
||||||
x: rl.Vector3,
|
x: rl.Vector3,
|
||||||
// Linear vel
|
// Linear vel
|
||||||
v: rl.Vector3,
|
v: rl.Vector3,
|
||||||
// Orientation
|
// Orientation
|
||||||
q: rl.Quaternion,
|
q: rl.Quaternion,
|
||||||
// Angular vel (omega)
|
// Angular vel (omega)
|
||||||
w: rl.Vector3,
|
w: rl.Vector3,
|
||||||
// Mass
|
// Mass
|
||||||
inv_mass: f32,
|
inv_mass: f32,
|
||||||
// Moment of inertia
|
// Moment of inertia
|
||||||
inv_intertia_tensor: rl.Vector3,
|
inv_inertia_tensor: rl.Vector3,
|
||||||
//
|
//
|
||||||
next_plus_one: i32,
|
next_plus_one: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
Suspension_Constraint :: struct {
|
Suspension_Constraint :: struct {
|
||||||
@ -55,6 +59,9 @@ Suspension_Constraint :: struct {
|
|||||||
Body_Handle :: distinct i32
|
Body_Handle :: distinct i32
|
||||||
Suspension_Constraint_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 {
|
is_body_handle_valid :: proc(handle: Body_Handle) -> bool {
|
||||||
return i32(handle) > 0
|
return i32(handle) > 0
|
||||||
}
|
}
|
||||||
@ -70,18 +77,19 @@ Body_Ptr :: #soa^#soa[]Body
|
|||||||
Suspension_Constraint_Ptr :: #soa^#soa[]Suspension_Constraint
|
Suspension_Constraint_Ptr :: #soa^#soa[]Suspension_Constraint
|
||||||
|
|
||||||
_invalid_body: #soa[1]Body
|
_invalid_body: #soa[1]Body
|
||||||
|
_invalid_body_slice: #soa[]Body
|
||||||
|
|
||||||
_invalid_suspension_constraint: #soa[1]Suspension_Constraint
|
_invalid_suspension_constraint: #soa[1]Suspension_Constraint
|
||||||
|
_invalid_suspension_constraint_slice := _invalid_suspension_constraint[:]
|
||||||
|
|
||||||
/// Returns pointer to soa slice. NEVER STORE IT
|
/// Returns pointer to soa slice. NEVER STORE IT
|
||||||
get_body :: proc(scene: ^Scene, handle: Body_Handle) -> Body_Ptr {
|
get_body :: proc(scene: ^Scene, handle: Body_Handle) -> Body_Ptr {
|
||||||
index := int(handle) - 1
|
index := int(handle) - 1
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
slice := _invalid_body[:]
|
return &_invalid_body_slice[0]
|
||||||
return &slice[0]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bodies_slice := scene.bodies[:]
|
return &scene.bodies_slice[index]
|
||||||
return &bodies_slice[index]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
add_body :: proc(scene: ^Scene, body: Body) -> Body_Handle {
|
add_body :: proc(scene: ^Scene, body: Body) -> Body_Handle {
|
||||||
@ -101,6 +109,9 @@ add_body :: proc(scene: ^Scene, body: Body) -> Body_Handle {
|
|||||||
|
|
||||||
append_soa(&scene.bodies, body_copy)
|
append_soa(&scene.bodies, body_copy)
|
||||||
index := len(scene.bodies)
|
index := len(scene.bodies)
|
||||||
|
|
||||||
|
scene.bodies_slice = scene.bodies[:]
|
||||||
|
|
||||||
return Body_Handle(index)
|
return Body_Handle(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,13 +131,11 @@ get_suspension_constraint :: proc(
|
|||||||
handle: Suspension_Constraint_Handle,
|
handle: Suspension_Constraint_Handle,
|
||||||
) -> Suspension_Constraint_Ptr {
|
) -> Suspension_Constraint_Ptr {
|
||||||
if !is_handle_valid(handle) {
|
if !is_handle_valid(handle) {
|
||||||
slice := _invalid_suspension_constraint[:]
|
return &_invalid_suspension_constraint_slice[0]
|
||||||
return &slice[0]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
index := int(handle) - 1
|
index := int(handle) - 1
|
||||||
slice := scene.suspension_constraints[:]
|
return &scene.suspension_constraints_slice[index]
|
||||||
return &slice[index]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
add_suspension_constraint :: proc(
|
add_suspension_constraint :: proc(
|
||||||
@ -148,6 +157,7 @@ add_suspension_constraint :: proc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
append_soa(&scene.suspension_constraints, copy)
|
append_soa(&scene.suspension_constraints, copy)
|
||||||
|
scene.suspension_constraints_slice = scene.suspension_constraints[:]
|
||||||
index := len(scene.suspension_constraints)
|
index := len(scene.suspension_constraints)
|
||||||
return Suspension_Constraint_Handle(index)
|
return Suspension_Constraint_Handle(index)
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
package physics
|
package physics
|
||||||
|
|
||||||
import "collision"
|
import "collision"
|
||||||
|
import "core:math"
|
||||||
import lg "core:math/linalg"
|
import lg "core:math/linalg"
|
||||||
import rl "vendor:raylib"
|
import rl "vendor:raylib"
|
||||||
|
|
||||||
|
_ :: math
|
||||||
|
|
||||||
Solver_Config :: struct {
|
Solver_Config :: struct {
|
||||||
// Will automatically do fixed timestep
|
// Will automatically do fixed timestep
|
||||||
timestep: f32,
|
timestep: f32,
|
||||||
@ -93,14 +96,29 @@ simulate_step :: proc(scene: ^Scene, config: Solver_Config) {
|
|||||||
body := get_body(scene, v.body)
|
body := get_body(scene, v.body)
|
||||||
|
|
||||||
q := body.q
|
q := body.q
|
||||||
pos := body.x
|
pos := body_local_to_world(body, v.rel_pos)
|
||||||
pos += lg.quaternion_mul_vector3(q, v.rel_pos)
|
|
||||||
dir := lg.quaternion_mul_vector3(q, v.rel_dir)
|
dir := lg.quaternion_mul_vector3(q, v.rel_dir)
|
||||||
pos2 := pos + dir * v.rest
|
pos2 := pos + dir * v.rest
|
||||||
v.hit_t, v.hit_point, v.hit = collision.intersect_segment_plane(
|
v.hit_t, v.hit_point, v.hit = collision.intersect_segment_plane(
|
||||||
{pos, pos2},
|
{pos, pos2},
|
||||||
collision.plane_from_point_normal({}, collision.Vec3{0, 1, 0}),
|
collision.plane_from_point_normal({}, collision.Vec3{0, 1, 0}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if v.hit {
|
||||||
|
corr := v.hit_point - pos
|
||||||
|
distance := lg.length(corr)
|
||||||
|
corr = corr / distance if distance > 0 else 0
|
||||||
|
|
||||||
|
apply_constraint_correction_unilateral(
|
||||||
|
dt,
|
||||||
|
body,
|
||||||
|
v.compliance,
|
||||||
|
error = distance - v.rest,
|
||||||
|
error_gradient = corr,
|
||||||
|
pos = pos,
|
||||||
|
other_combined_inv_mass = 0,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,3 +136,68 @@ simulate_step :: proc(scene: ^Scene, config: Solver_Config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apply_constraint_correction_unilateral :: proc(
|
||||||
|
dt: f32,
|
||||||
|
body: Body_Ptr,
|
||||||
|
compliance: f32,
|
||||||
|
error: f32,
|
||||||
|
error_gradient: rl.Vector3,
|
||||||
|
pos: rl.Vector3,
|
||||||
|
other_combined_inv_mass: f32 = 0,
|
||||||
|
) {
|
||||||
|
if error == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w := get_body_inverse_mass(body, error_gradient, pos)
|
||||||
|
w += other_combined_inv_mass
|
||||||
|
|
||||||
|
if w == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
alpha := compliance / dt / dt
|
||||||
|
lambda := -error / (w + alpha)
|
||||||
|
|
||||||
|
delta_pos := error_gradient * -lambda
|
||||||
|
|
||||||
|
apply_correction(body, delta_pos, pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_correction :: proc(body: Body_Ptr, corr: rl.Vector3, pos: rl.Vector3) {
|
||||||
|
body.x += corr * body.inv_mass
|
||||||
|
|
||||||
|
q := body.q
|
||||||
|
inv_q := lg.quaternion_inverse(q)
|
||||||
|
delta_omega := pos - body.x
|
||||||
|
delta_omega = lg.cross(delta_omega, corr)
|
||||||
|
delta_omega = lg.quaternion_mul_vector3(inv_q, delta_omega)
|
||||||
|
delta_omega *= body.inv_inertia_tensor
|
||||||
|
delta_omega = lg.quaternion_mul_vector3(q, delta_omega)
|
||||||
|
|
||||||
|
delta_rot := quaternion(x = delta_omega.x, y = delta_omega.y, z = delta_omega.z, w = 0)
|
||||||
|
delta_rot *= q
|
||||||
|
q.x += 0.5 * delta_rot.x
|
||||||
|
q.y += 0.5 * delta_rot.y
|
||||||
|
q.z += 0.5 * delta_rot.z
|
||||||
|
q.w += 0.5 * delta_rot.w
|
||||||
|
q = lg.normalize0(q)
|
||||||
|
|
||||||
|
body.q = q
|
||||||
|
}
|
||||||
|
|
||||||
|
get_body_inverse_mass :: proc(body: Body_Ptr, normal, pos: rl.Vector3) -> f32 {
|
||||||
|
q := body.q
|
||||||
|
inv_q := lg.quaternion_inverse(q)
|
||||||
|
|
||||||
|
rn := pos - body.x
|
||||||
|
rn = lg.cross(rn, normal)
|
||||||
|
rn = lg.quaternion_mul_vector3(inv_q, rn)
|
||||||
|
rn *= rn
|
||||||
|
|
||||||
|
w := lg.dot(rn, body.inv_inertia_tensor)
|
||||||
|
w += body.inv_mass
|
||||||
|
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user