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_y := f32(0.5)
|
||||
rest := f32(0.7)
|
||||
natural_frequency := f32(1)
|
||||
damping := f32(0.2)
|
||||
natural_frequency := f32(1.2)
|
||||
damping := f32(0.3)
|
||||
radius := f32(0.737649) / 2
|
||||
wheel_front_z := f32(1.6)
|
||||
wheel_back_z := f32(-1.63)
|
||||
@ -861,14 +861,20 @@ Orbit_Camera :: struct {
|
||||
|
||||
Car_Follow_Camera :: struct {
|
||||
// Params
|
||||
body: physics.Body_Handle,
|
||||
com_offset: rl.Vector3,
|
||||
distance: f32,
|
||||
body: physics.Body_Handle,
|
||||
com_offset: rl.Vector3,
|
||||
distance: f32,
|
||||
yaw, pitch: f32,
|
||||
|
||||
// if > 0 manual rotation is enabled, ticked down by dt
|
||||
manual_rotation_time: f32,
|
||||
has_yaw_pitch: bool,
|
||||
|
||||
// State
|
||||
pos: rl.Vector3,
|
||||
target: rl.Vector3,
|
||||
up: rl.Vector3,
|
||||
pos: rl.Vector3,
|
||||
follow_target: rl.Vector3,
|
||||
target: rl.Vector3,
|
||||
up: rl.Vector3,
|
||||
}
|
||||
|
||||
Game_Camera :: union {
|
||||
@ -879,7 +885,7 @@ Game_Camera :: union {
|
||||
|
||||
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 {
|
||||
rl.GetGamepadAxisMovement(0, .RIGHT_X),
|
||||
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
|
||||
|
||||
@(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
|
||||
GAMEPAD_SENSE :: 1
|
||||
|
||||
if linalg.length2(mouse_delta) > linalg.length2(gamepad_delta) {
|
||||
sense = MOUSE_SENSE
|
||||
delta = mouse_delta
|
||||
is_mouse = true
|
||||
} else {
|
||||
sense = GAMEPAD_SENSE * rl.GetFrameTime()
|
||||
delta = gamepad_delta
|
||||
is_mouse = false
|
||||
}
|
||||
|
||||
return
|
||||
@ -922,7 +938,7 @@ orbit_camera_update :: proc(camera: ^Orbit_Camera) {
|
||||
camera.target =
|
||||
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.pitch += delta.y * sense
|
||||
@ -950,23 +966,61 @@ orbit_camera_to_rl :: proc(camera: Orbit_Camera) -> rl.Camera3D {
|
||||
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) {
|
||||
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}
|
||||
|
||||
// forward := physics.body_local_to_world_vec(body, physics.Vec3{0, 0, 1})
|
||||
// up := physics.body_local_to_world_vec(body, physics.Vec3{0, 1, 0})
|
||||
delta, sense, _ := collect_camera_input()
|
||||
|
||||
dir := linalg.normalize0(camera.pos - camera.target)
|
||||
if dir == 0 {
|
||||
dir = {0, 0, -1}
|
||||
distance := f32(7)
|
||||
|
||||
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 {
|
||||
@ -974,7 +1028,7 @@ follow_camera_to_rl :: proc(camera: Car_Follow_Camera) -> rl.Camera3D {
|
||||
position = camera.pos,
|
||||
target = camera.target,
|
||||
up = rl.Vector3{0, 1, 0},
|
||||
fovy = 50,
|
||||
fovy = 60,
|
||||
projection = .PERSPECTIVE,
|
||||
}
|
||||
}
|
||||
@ -1014,7 +1068,7 @@ free_camera_rotation :: proc(camera: Free_Camera) -> linalg.Matrix3f32 {
|
||||
}
|
||||
|
||||
free_camera_update :: proc(camera: ^Free_Camera) {
|
||||
delta, sense := collect_camera_input()
|
||||
delta, sense, _ := collect_camera_input()
|
||||
|
||||
camera.yaw -= delta.x * 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)
|
||||
NUM_SIM_STATES :: 2
|
||||
NUM_SIM_STATES :: 2 when DEV_BUILD else 1
|
||||
|
||||
Scene :: struct {
|
||||
assetman: ^assets.Asset_Manager,
|
||||
@ -352,6 +352,17 @@ Suspension_Constraint :: struct {
|
||||
brake_impulse: f32,
|
||||
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
|
||||
slip_angle: f32,
|
||||
slip_ratio: f32,
|
||||
@ -591,6 +602,16 @@ allocate_interpolated_body :: proc(
|
||||
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(
|
||||
scene: ^Scene,
|
||||
solver_config: Solver_Config,
|
||||
@ -604,27 +625,18 @@ get_interpolated_body :: proc(
|
||||
return result
|
||||
}
|
||||
|
||||
prev_sim_state := get_prev_sim_state(scene)
|
||||
sim_state := get_sim_state(scene)
|
||||
|
||||
prev_body := get_body(prev_sim_state, handle)
|
||||
body := get_body(sim_state, handle)
|
||||
|
||||
result^ = Body {
|
||||
q = lg.QUATERNIONF32_IDENTITY,
|
||||
}
|
||||
|
||||
if prev_body.alive && body.alive {
|
||||
if body.alive {
|
||||
// interpolate
|
||||
t := scene.solver_state.accumulated_time / solver_config.timestep
|
||||
t := calc_interpolate_alpha(scene, solver_config)
|
||||
result^ = body^
|
||||
result.x = lg.lerp(prev_body.x, body.x, t)
|
||||
result.q = lg.quaternion_slerp_f32(prev_body.q, body.q, t)
|
||||
result.v = lg.lerp(prev_body.v, body.v, t)
|
||||
result.x = lg.lerp(body.prev_x, body.x, t)
|
||||
result.q = lg.quaternion_slerp_f32(body.prev_q, body.q, 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
|
||||
result.w = lg.lerp(prev_body.w, body.w, t)
|
||||
} else if body.alive {
|
||||
result^ = body^
|
||||
result.w = lg.lerp(body.prev_w, body.w, t)
|
||||
}
|
||||
|
||||
return result
|
||||
@ -665,27 +677,21 @@ get_interpolated_wheel :: proc(
|
||||
return result
|
||||
}
|
||||
|
||||
prev_sim_state := get_prev_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)
|
||||
|
||||
if prev_wheel.alive && wheel.alive {
|
||||
result^ = wheel^
|
||||
if wheel.alive {
|
||||
// interpolate
|
||||
t := scene.solver_state.accumulated_time / solver_config.timestep
|
||||
result^ = prev_wheel^
|
||||
result.hit_t = lg.lerp(prev_wheel.hit_t, wheel.hit_t, t)
|
||||
result.hit_point = lg.lerp(prev_wheel.hit_point, wheel.hit_point, t)
|
||||
result.hit_normal = lg.normalize0(lg.lerp(prev_wheel.hit_normal, wheel.hit_normal, t))
|
||||
result.turn_angle = lg.lerp(prev_wheel.turn_angle, wheel.turn_angle, t)
|
||||
result.turn_assist = lg.lerp(prev_wheel.turn_assist, wheel.turn_assist, t)
|
||||
result.q = lg.lerp(prev_wheel.q, wheel.q, 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^
|
||||
t := calc_interpolate_alpha(scene, solver_config)
|
||||
result.hit_t = lg.lerp(wheel.prev_hit_t, wheel.hit_t, t)
|
||||
result.hit_point = lg.lerp(wheel.prev_hit_point, wheel.hit_point, t)
|
||||
result.hit_normal = lg.normalize0(lg.lerp(wheel.prev_hit_normal, wheel.hit_normal, t))
|
||||
result.turn_angle = lg.lerp(wheel.prev_turn_angle, wheel.turn_angle, t)
|
||||
result.turn_assist = lg.lerp(wheel.prev_turn_assist, wheel.turn_assist, t)
|
||||
result.q = lg.lerp(wheel.prev_q, wheel.q, t)
|
||||
result.w = lg.lerp(wheel.prev_w, wheel.w, t)
|
||||
}
|
||||
|
||||
return result
|
||||
@ -883,6 +889,11 @@ initialize_body_from_config :: proc(sim_state: ^Sim_State, body: ^Body, config:
|
||||
body.v = config.initial_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_offset = -combined_center_of_mass(config.shapes) + config.com_shift
|
||||
body.shape_aabb = input_shape_get_aabb(config.shapes)
|
||||
|
@ -1344,7 +1344,11 @@ pgs_solve_suspension :: proc(
|
||||
) *
|
||||
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()
|
||||
|
||||
// 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
|
||||
|
||||
dt_64 := f64(config.timestep) / f64(substeps)
|
||||
|
Loading…
x
Reference in New Issue
Block a user