Change how interpolation works, add mouse movement override to follow camera
This commit is contained in:
parent
7f928a6f2c
commit
5181b9b813
@ -597,8 +597,8 @@ update_world :: proc(world: ^World, dt: f32, config: World_Update_Config) {
|
|||||||
wheel_extent_x_back := f32(2) / 2
|
wheel_extent_x_back := f32(2) / 2
|
||||||
wheel_y := f32(0.5)
|
wheel_y := f32(0.5)
|
||||||
rest := f32(0.7)
|
rest := f32(0.7)
|
||||||
natural_frequency := f32(1)
|
natural_frequency := f32(1.2)
|
||||||
damping := f32(0.2)
|
damping := f32(0.3)
|
||||||
radius := f32(0.737649) / 2
|
radius := f32(0.737649) / 2
|
||||||
wheel_front_z := f32(1.6)
|
wheel_front_z := f32(1.6)
|
||||||
wheel_back_z := f32(-1.63)
|
wheel_back_z := f32(-1.63)
|
||||||
@ -861,14 +861,20 @@ Orbit_Camera :: struct {
|
|||||||
|
|
||||||
Car_Follow_Camera :: struct {
|
Car_Follow_Camera :: struct {
|
||||||
// Params
|
// Params
|
||||||
body: physics.Body_Handle,
|
body: physics.Body_Handle,
|
||||||
com_offset: rl.Vector3,
|
com_offset: rl.Vector3,
|
||||||
distance: f32,
|
distance: f32,
|
||||||
|
yaw, pitch: f32,
|
||||||
|
|
||||||
|
// if > 0 manual rotation is enabled, ticked down by dt
|
||||||
|
manual_rotation_time: f32,
|
||||||
|
has_yaw_pitch: bool,
|
||||||
|
|
||||||
// State
|
// State
|
||||||
pos: rl.Vector3,
|
pos: rl.Vector3,
|
||||||
target: rl.Vector3,
|
follow_target: rl.Vector3,
|
||||||
up: rl.Vector3,
|
target: rl.Vector3,
|
||||||
|
up: rl.Vector3,
|
||||||
}
|
}
|
||||||
|
|
||||||
Game_Camera :: union {
|
Game_Camera :: union {
|
||||||
@ -879,7 +885,7 @@ Game_Camera :: union {
|
|||||||
|
|
||||||
GAMEPAD_DEADZONE :: f32(0.07)
|
GAMEPAD_DEADZONE :: f32(0.07)
|
||||||
|
|
||||||
collect_camera_input :: proc() -> (delta: rl.Vector2, sense: f32) {
|
collect_camera_input :: proc() -> (delta: rl.Vector2, sense: f32, is_mouse: bool) {
|
||||||
gamepad_delta := rl.Vector2 {
|
gamepad_delta := rl.Vector2 {
|
||||||
rl.GetGamepadAxisMovement(0, .RIGHT_X),
|
rl.GetGamepadAxisMovement(0, .RIGHT_X),
|
||||||
rl.GetGamepadAxisMovement(0, .RIGHT_Y),
|
rl.GetGamepadAxisMovement(0, .RIGHT_Y),
|
||||||
@ -903,15 +909,25 @@ collect_camera_input :: proc() -> (delta: rl.Vector2, sense: f32) {
|
|||||||
|
|
||||||
mouse_delta := g_mem.mouse_captured ? rl.GetMouseDelta() : 0
|
mouse_delta := g_mem.mouse_captured ? rl.GetMouseDelta() : 0
|
||||||
|
|
||||||
|
@(static) first_delta := true
|
||||||
|
if mouse_delta != 0 && first_delta {
|
||||||
|
first_delta = false
|
||||||
|
mouse_delta = 0
|
||||||
|
} else if mouse_delta == 0 {
|
||||||
|
first_delta = true
|
||||||
|
}
|
||||||
|
|
||||||
MOUSE_SENSE :: 0.005
|
MOUSE_SENSE :: 0.005
|
||||||
GAMEPAD_SENSE :: 1
|
GAMEPAD_SENSE :: 1
|
||||||
|
|
||||||
if linalg.length2(mouse_delta) > linalg.length2(gamepad_delta) {
|
if linalg.length2(mouse_delta) > linalg.length2(gamepad_delta) {
|
||||||
sense = MOUSE_SENSE
|
sense = MOUSE_SENSE
|
||||||
delta = mouse_delta
|
delta = mouse_delta
|
||||||
|
is_mouse = true
|
||||||
} else {
|
} else {
|
||||||
sense = GAMEPAD_SENSE * rl.GetFrameTime()
|
sense = GAMEPAD_SENSE * rl.GetFrameTime()
|
||||||
delta = gamepad_delta
|
delta = gamepad_delta
|
||||||
|
is_mouse = false
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -922,7 +938,7 @@ orbit_camera_update :: proc(camera: ^Orbit_Camera) {
|
|||||||
camera.target =
|
camera.target =
|
||||||
physics.get_body(physics.get_sim_state(&world.physics_scene), world.car_handle).x
|
physics.get_body(physics.get_sim_state(&world.physics_scene), world.car_handle).x
|
||||||
|
|
||||||
delta, sense := collect_camera_input()
|
delta, sense, _ := collect_camera_input()
|
||||||
|
|
||||||
camera.yaw += delta.x * sense
|
camera.yaw += delta.x * sense
|
||||||
camera.pitch += delta.y * sense
|
camera.pitch += delta.y * sense
|
||||||
@ -950,23 +966,61 @@ orbit_camera_to_rl :: proc(camera: Orbit_Camera) -> rl.Camera3D {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dir_to_yaw_pitch :: proc "contextless" (dir: rl.Vector3) -> (yaw: f32, pitch: f32) {
|
||||||
|
pitch = math.asin(dir.y)
|
||||||
|
yaw = -math.atan2(dir.x, dir.z) + math.PI
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
yaw_pitch_to_rotation :: proc "contextless" (yaw, pitch: f32) -> linalg.Matrix3f32 {
|
||||||
|
return(
|
||||||
|
linalg.matrix3_rotate(-yaw, rl.Vector3{0, 1, 0}) *
|
||||||
|
linalg.matrix3_rotate(-pitch, rl.Vector3{1, 0, 0}) \
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
follow_camera_update :: proc(world: ^World, camera: ^Car_Follow_Camera, dt: f32) {
|
follow_camera_update :: proc(world: ^World, camera: ^Car_Follow_Camera, dt: f32) {
|
||||||
body := physics.get_interpolated_body(&world.physics_scene, SOLVER_CONFIG, camera.body)
|
body := physics.get_interpolated_body(&world.physics_scene, SOLVER_CONFIG, camera.body)
|
||||||
|
|
||||||
|
camera.follow_target = body.x + physics.Vec3{0, 4, 0}
|
||||||
camera.target = body.x + physics.Vec3{0, 2.5, 0}
|
camera.target = body.x + physics.Vec3{0, 2.5, 0}
|
||||||
|
|
||||||
// forward := physics.body_local_to_world_vec(body, physics.Vec3{0, 0, 1})
|
delta, sense, _ := collect_camera_input()
|
||||||
// up := physics.body_local_to_world_vec(body, physics.Vec3{0, 1, 0})
|
|
||||||
|
|
||||||
dir := linalg.normalize0(camera.pos - camera.target)
|
distance := f32(7)
|
||||||
if dir == 0 {
|
|
||||||
dir = {0, 0, -1}
|
if delta != 0 {
|
||||||
|
camera.manual_rotation_time = 2 // 3 seconds we keep rotation
|
||||||
}
|
}
|
||||||
distance := f32(8)
|
|
||||||
pos_target := camera.target + dir * distance
|
|
||||||
log.debugf("pos_target: {}", pos_target)
|
|
||||||
|
|
||||||
camera.pos = emath.exp_smooth(pos_target, camera.pos, 40, dt)
|
if camera.manual_rotation_time > 0 {
|
||||||
|
camera.manual_rotation_time -= dt
|
||||||
|
|
||||||
|
if !camera.has_yaw_pitch {
|
||||||
|
camera.has_yaw_pitch = true
|
||||||
|
camera.yaw, camera.pitch = dir_to_yaw_pitch(
|
||||||
|
linalg.normalize0(camera.follow_target - camera.pos),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
camera.yaw += delta.x * sense
|
||||||
|
camera.pitch += delta.y * sense
|
||||||
|
camera.pitch = math.clamp(camera.pitch, -math.PI / 2.0 + 0.0001, math.PI / 2.0 - 0.0001)
|
||||||
|
|
||||||
|
camera.pos =
|
||||||
|
camera.follow_target +
|
||||||
|
yaw_pitch_to_rotation(camera.yaw, camera.pitch) * rl.Vector3{0, 0, 1} * distance
|
||||||
|
} else {
|
||||||
|
dir := linalg.normalize0(camera.pos - camera.follow_target)
|
||||||
|
if dir == 0 {
|
||||||
|
dir = {0, 0, -1}
|
||||||
|
}
|
||||||
|
|
||||||
|
camera.has_yaw_pitch = false
|
||||||
|
|
||||||
|
pos_target := camera.follow_target + dir * distance
|
||||||
|
camera.pos = pos_target
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
follow_camera_to_rl :: proc(camera: Car_Follow_Camera) -> rl.Camera3D {
|
follow_camera_to_rl :: proc(camera: Car_Follow_Camera) -> rl.Camera3D {
|
||||||
@ -974,7 +1028,7 @@ follow_camera_to_rl :: proc(camera: Car_Follow_Camera) -> rl.Camera3D {
|
|||||||
position = camera.pos,
|
position = camera.pos,
|
||||||
target = camera.target,
|
target = camera.target,
|
||||||
up = rl.Vector3{0, 1, 0},
|
up = rl.Vector3{0, 1, 0},
|
||||||
fovy = 50,
|
fovy = 60,
|
||||||
projection = .PERSPECTIVE,
|
projection = .PERSPECTIVE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1014,7 +1068,7 @@ free_camera_rotation :: proc(camera: Free_Camera) -> linalg.Matrix3f32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
free_camera_update :: proc(camera: ^Free_Camera) {
|
free_camera_update :: proc(camera: ^Free_Camera) {
|
||||||
delta, sense := collect_camera_input()
|
delta, sense, _ := collect_camera_input()
|
||||||
|
|
||||||
camera.yaw -= delta.x * sense
|
camera.yaw -= delta.x * sense
|
||||||
camera.pitch -= delta.y * sense
|
camera.pitch -= delta.y * sense
|
||||||
|
@ -155,7 +155,7 @@ sim_state_interp_cache_destroy :: proc(cache: ^Sim_State_Interp_Cache) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DEV_BUILD :: #config(DEV, false)
|
DEV_BUILD :: #config(DEV, false)
|
||||||
NUM_SIM_STATES :: 2
|
NUM_SIM_STATES :: 2 when DEV_BUILD else 1
|
||||||
|
|
||||||
Scene :: struct {
|
Scene :: struct {
|
||||||
assetman: ^assets.Asset_Manager,
|
assetman: ^assets.Asset_Manager,
|
||||||
@ -352,6 +352,17 @@ Suspension_Constraint :: struct {
|
|||||||
brake_impulse: f32,
|
brake_impulse: f32,
|
||||||
applied_impulse: Vec3,
|
applied_impulse: Vec3,
|
||||||
|
|
||||||
|
// Prev state for interp
|
||||||
|
prev_q: f32,
|
||||||
|
prev_w: f32,
|
||||||
|
prev_hit: bool,
|
||||||
|
prev_hit_normal: Vec3,
|
||||||
|
prev_hit_body: Body_Handle,
|
||||||
|
prev_hit_point: Vec3,
|
||||||
|
prev_hit_t: f32,
|
||||||
|
prev_turn_angle: f32,
|
||||||
|
prev_turn_assist: f32,
|
||||||
|
|
||||||
// Convenience for debug visualization to avoid recomputing
|
// Convenience for debug visualization to avoid recomputing
|
||||||
slip_angle: f32,
|
slip_angle: f32,
|
||||||
slip_ratio: f32,
|
slip_ratio: f32,
|
||||||
@ -591,6 +602,16 @@ allocate_interpolated_body :: proc(
|
|||||||
return idx, false
|
return idx, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
calc_interpolate_alpha :: proc "contextless" (
|
||||||
|
scene: ^Scene,
|
||||||
|
solver_config: Solver_Config,
|
||||||
|
) -> (
|
||||||
|
t: f32,
|
||||||
|
) {
|
||||||
|
t = scene.solver_state.accumulated_time / solver_config.timestep
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
get_interpolated_body :: proc(
|
get_interpolated_body :: proc(
|
||||||
scene: ^Scene,
|
scene: ^Scene,
|
||||||
solver_config: Solver_Config,
|
solver_config: Solver_Config,
|
||||||
@ -604,27 +625,18 @@ get_interpolated_body :: proc(
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
prev_sim_state := get_prev_sim_state(scene)
|
|
||||||
sim_state := get_sim_state(scene)
|
sim_state := get_sim_state(scene)
|
||||||
|
|
||||||
prev_body := get_body(prev_sim_state, handle)
|
|
||||||
body := get_body(sim_state, handle)
|
body := get_body(sim_state, handle)
|
||||||
|
|
||||||
result^ = Body {
|
if body.alive {
|
||||||
q = lg.QUATERNIONF32_IDENTITY,
|
|
||||||
}
|
|
||||||
|
|
||||||
if prev_body.alive && body.alive {
|
|
||||||
// interpolate
|
// interpolate
|
||||||
t := scene.solver_state.accumulated_time / solver_config.timestep
|
t := calc_interpolate_alpha(scene, solver_config)
|
||||||
result^ = body^
|
result^ = body^
|
||||||
result.x = lg.lerp(prev_body.x, body.x, t)
|
result.x = lg.lerp(body.prev_x, body.x, t)
|
||||||
result.q = lg.quaternion_slerp_f32(prev_body.q, body.q, t)
|
result.q = lg.quaternion_slerp_f32(body.prev_q, body.q, t)
|
||||||
result.v = lg.lerp(prev_body.v, body.v, t)
|
result.v = lg.lerp(body.prev_v, body.v, t)
|
||||||
// I don't think that's right, but not going to be used anyway probably
|
// I don't think that's right, but not going to be used anyway probably
|
||||||
result.w = lg.lerp(prev_body.w, body.w, t)
|
result.w = lg.lerp(body.prev_w, body.w, t)
|
||||||
} else if body.alive {
|
|
||||||
result^ = body^
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@ -665,27 +677,21 @@ get_interpolated_wheel :: proc(
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
prev_sim_state := get_prev_sim_state(scene)
|
|
||||||
sim_state := get_sim_state(scene)
|
sim_state := get_sim_state(scene)
|
||||||
|
|
||||||
prev_wheel := get_suspension_constraint(prev_sim_state, handle)
|
|
||||||
wheel := get_suspension_constraint(sim_state, handle)
|
wheel := get_suspension_constraint(sim_state, handle)
|
||||||
|
|
||||||
if prev_wheel.alive && wheel.alive {
|
result^ = wheel^
|
||||||
|
if wheel.alive {
|
||||||
// interpolate
|
// interpolate
|
||||||
t := scene.solver_state.accumulated_time / solver_config.timestep
|
t := calc_interpolate_alpha(scene, solver_config)
|
||||||
result^ = prev_wheel^
|
result.hit_t = lg.lerp(wheel.prev_hit_t, wheel.hit_t, t)
|
||||||
result.hit_t = lg.lerp(prev_wheel.hit_t, wheel.hit_t, t)
|
result.hit_point = lg.lerp(wheel.prev_hit_point, wheel.hit_point, t)
|
||||||
result.hit_point = lg.lerp(prev_wheel.hit_point, wheel.hit_point, t)
|
result.hit_normal = lg.normalize0(lg.lerp(wheel.prev_hit_normal, wheel.hit_normal, t))
|
||||||
result.hit_normal = lg.normalize0(lg.lerp(prev_wheel.hit_normal, wheel.hit_normal, t))
|
result.turn_angle = lg.lerp(wheel.prev_turn_angle, wheel.turn_angle, t)
|
||||||
result.turn_angle = lg.lerp(prev_wheel.turn_angle, wheel.turn_angle, t)
|
result.turn_assist = lg.lerp(wheel.prev_turn_assist, wheel.turn_assist, t)
|
||||||
result.turn_assist = lg.lerp(prev_wheel.turn_assist, wheel.turn_assist, t)
|
result.q = lg.lerp(wheel.prev_q, wheel.q, t)
|
||||||
result.q = lg.lerp(prev_wheel.q, wheel.q, t)
|
result.w = lg.lerp(wheel.prev_w, wheel.w, t)
|
||||||
result.w = lg.lerp(prev_wheel.w, wheel.w, t)
|
|
||||||
} else if prev_wheel.alive {
|
|
||||||
result^ = prev_wheel^
|
|
||||||
} else if wheel.alive {
|
|
||||||
result^ = wheel^
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@ -883,6 +889,11 @@ initialize_body_from_config :: proc(sim_state: ^Sim_State, body: ^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.prev_x = body.x
|
||||||
|
body.prev_q = body.q
|
||||||
|
body.prev_v = body.v
|
||||||
|
body.prev_w = body.w
|
||||||
|
|
||||||
body.shape = add_shape(sim_state, config.shapes)
|
body.shape = add_shape(sim_state, config.shapes)
|
||||||
body.shape_offset = -combined_center_of_mass(config.shapes) + config.com_shift
|
body.shape_offset = -combined_center_of_mass(config.shapes) + config.com_shift
|
||||||
body.shape_aabb = input_shape_get_aabb(config.shapes)
|
body.shape_aabb = input_shape_get_aabb(config.shapes)
|
||||||
|
@ -1344,7 +1344,11 @@ pgs_solve_suspension :: proc(
|
|||||||
) *
|
) *
|
||||||
math.sign(lateral_to_longitudinal_relation)
|
math.sign(lateral_to_longitudinal_relation)
|
||||||
|
|
||||||
v.turn_assist = drift_amount * math.PI * 0.15
|
v.turn_assist =
|
||||||
|
math.smoothstep(f32(0), f32(1), lg.dot(body_forward_vec, body.v)) *
|
||||||
|
drift_amount *
|
||||||
|
math.PI *
|
||||||
|
0.15
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1781,6 +1785,36 @@ simulate_step :: proc(
|
|||||||
) {
|
) {
|
||||||
tracy.Zone()
|
tracy.Zone()
|
||||||
|
|
||||||
|
// Save prev state
|
||||||
|
{
|
||||||
|
for i in 0 ..< len(sim_state.bodies) {
|
||||||
|
body := &sim_state.bodies_slice[i]
|
||||||
|
|
||||||
|
if body.alive {
|
||||||
|
body.prev_x = body.x
|
||||||
|
body.prev_v = body.v
|
||||||
|
body.prev_q = body.q
|
||||||
|
body.prev_w = body.w
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0 ..< len(sim_state.suspension_constraints) {
|
||||||
|
wheel := &sim_state.suspension_constraints_slice[i]
|
||||||
|
|
||||||
|
if wheel.alive {
|
||||||
|
wheel.prev_q = wheel.q
|
||||||
|
wheel.prev_w = wheel.w
|
||||||
|
wheel.prev_hit = wheel.hit
|
||||||
|
wheel.prev_hit_normal = wheel.hit_normal
|
||||||
|
wheel.prev_hit_body = wheel.hit_body
|
||||||
|
wheel.prev_hit_point = wheel.hit_point
|
||||||
|
wheel.prev_hit_t = wheel.hit_t
|
||||||
|
wheel.prev_turn_angle = wheel.turn_angle
|
||||||
|
wheel.prev_turn_assist = wheel.turn_assist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
substeps := config.substreps_minus_one + 1
|
substeps := config.substreps_minus_one + 1
|
||||||
|
|
||||||
dt_64 := f64(config.timestep) / f64(substeps)
|
dt_64 := f64(config.timestep) / f64(substeps)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user