use super::surface::{RoadCondition, RoadSurface};
use super::{DriverInput, DynamicVehicle, Environment, WheelDynamicsState};
use crate::math::{Fix128, Vec3Fix};
use crate::solver::{PhysicsWorld, RigidBody};
use crate::wind_zone::WindZone;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[must_use]
pub fn time_to_collision(gap: Fix128, v_rear: Fix128, v_front: Fix128) -> Option<Fix128> {
let closing = v_rear - v_front;
if closing <= Fix128::ZERO {
return None;
}
gap.max(Fix128::ZERO).checked_div(closing)
}
#[must_use]
pub fn time_headway(gap: Fix128, v_rear: Fix128) -> Option<Fix128> {
if v_rear <= Fix128::ZERO {
return None;
}
gap.max(Fix128::ZERO).checked_div(v_rear)
}
#[must_use]
pub fn longitudinal_state(
rear: &RigidBody,
front: &RigidBody,
dir: Vec3Fix,
extent: Fix128,
) -> (Fix128, Fix128, Fix128) {
let gap = (front.position - rear.position).dot(dir) - extent;
(gap, rear.velocity.dot(dir), front.velocity.dot(dir))
}
#[must_use]
pub fn up_from_gravity(gravity: Vec3Fix) -> Option<Vec3Fix> {
let len = gravity.length();
if len.is_zero() {
return None;
}
Some(-gravity / len)
}
fn horizontal(v: Vec3Fix, up: Vec3Fix) -> Vec3Fix {
v - up * v.dot(up)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StoppingDistanceMeter {
up: Vec3Fix,
rest_speed: Fix128,
last: Vec3Fix,
last_velocity: Vec3Fix,
travelled: Fix128,
samples: u64,
stopped_at: Option<u64>,
}
impl StoppingDistanceMeter {
#[must_use]
pub fn new(chassis: &RigidBody, up: Vec3Fix, rest_speed: Fix128) -> Self {
Self {
up,
rest_speed: rest_speed.max(Fix128::ZERO),
last: chassis.position,
last_velocity: horizontal(chassis.velocity, up),
travelled: Fix128::ZERO,
samples: 0,
stopped_at: None,
}
}
pub fn sample(&mut self, chassis: &RigidBody) -> bool {
if self.stopped_at.is_some() {
return true;
}
self.samples += 1;
self.travelled =
self.travelled + horizontal(chassis.position - self.last, self.up).length();
self.last = chassis.position;
let v = horizontal(chassis.velocity, self.up);
let (dir, len) = self.last_velocity.normalize_with_length();
let forward = if len.is_zero() {
v.length()
} else {
v.dot(dir)
};
if forward <= self.rest_speed {
self.stopped_at = Some(self.samples);
}
self.last_velocity = v;
self.stopped_at.is_some()
}
#[must_use]
pub fn is_stopped(&self) -> bool {
self.stopped_at.is_some()
}
#[must_use]
pub fn distance(&self) -> Option<Fix128> {
self.stopped_at.map(|_| self.travelled)
}
#[must_use]
pub fn travelled(&self) -> Fix128 {
self.travelled
}
#[must_use]
pub fn frames_to_stop(&self) -> Option<u64> {
self.stopped_at
}
}
#[derive(Clone, Debug)]
pub struct ScenarioVehicle {
pub vehicle: DynamicVehicle,
pub body: usize,
}
pub struct Scenario<R: RoadSurface> {
pub world: PhysicsWorld,
pub road: R,
pub condition: RoadCondition,
pub wind: Option<WindZone>,
pub vehicles: Vec<ScenarioVehicle>,
pub dt: Fix128,
pub time: Fix128,
pub frame: u64,
recording: Option<Recording>,
}
impl<R: RoadSurface> Scenario<R> {
#[must_use]
pub fn new(world: PhysicsWorld, road: R, condition: RoadCondition, dt: Fix128) -> Self {
Self {
world,
road,
condition,
wind: None,
vehicles: Vec::new(),
dt,
time: Fix128::ZERO,
frame: 0,
recording: None,
}
}
pub fn add_vehicle(&mut self, vehicle: DynamicVehicle, chassis: RigidBody) -> usize {
let body = self.world.add_body(chassis);
self.vehicles.push(ScenarioVehicle { vehicle, body });
self.vehicles.len() - 1
}
#[must_use]
pub fn chassis(&self, i: usize) -> Option<&RigidBody> {
self.vehicles
.get(i)
.and_then(|v| self.world.bodies.get(v.body))
}
pub fn step(&mut self, inputs: &[DriverInput]) {
if self.dt <= Fix128::ZERO {
return;
}
for (i, v) in self.vehicles.iter_mut().enumerate() {
v.vehicle.input = inputs.get(i).copied().unwrap_or_default();
}
self.advance();
}
pub fn step_with<F>(&mut self, frames: usize, mut control: F)
where
F: FnMut(u64, usize, &DynamicVehicle, &RigidBody) -> DriverInput,
{
if self.dt <= Fix128::ZERO {
return;
}
for _ in 0..frames {
let frame = self.frame;
let mut inputs = Vec::with_capacity(self.vehicles.len());
for (i, v) in self.vehicles.iter().enumerate() {
inputs.push(control(frame, i, &v.vehicle, &self.world.bodies[v.body]));
}
for (v, inp) in self.vehicles.iter_mut().zip(inputs) {
v.vehicle.input = inp;
}
self.advance();
}
}
pub fn run_tracks(&mut self, tracks: &[&[DriverInput]], frames: usize) {
if self.dt <= Fix128::ZERO {
return;
}
for k in 0..frames {
for (i, v) in self.vehicles.iter_mut().enumerate() {
v.vehicle.input = tracks
.get(i)
.and_then(|t| t.get(k))
.copied()
.unwrap_or_default();
}
self.advance();
}
}
fn advance(&mut self) {
let env = Environment {
condition: &self.condition,
wind: self.wind.as_ref(),
time: self.time,
};
for v in &mut self.vehicles {
v.vehicle
.update(&mut self.world.bodies[v.body], &self.road, &env, self.dt);
}
self.world.step(self.dt);
self.time = self.time + self.dt;
self.frame += 1;
if let Some(rec) = self.recording.as_mut() {
rec.inputs
.extend(self.vehicles.iter().map(|v| v.vehicle.input));
rec.frames += 1;
}
}
pub fn start_recording(&mut self) {
self.recording = Some(Recording {
dt: self.dt,
start_time: self.time,
start_frame: self.frame,
world_state: self.world.serialize_state(),
vehicles: self
.vehicles
.iter()
.map(|v| VehicleSnapshot {
body: v.body,
engine_rpm: v.vehicle.engine_rpm,
current_gear: v.vehicle.config.powertrain.current_gear,
input: v.vehicle.input,
wheels: v.vehicle.wheels.clone(),
})
.collect(),
frames: 0,
inputs: Vec::new(),
});
}
#[must_use]
pub fn is_recording(&self) -> bool {
self.recording.is_some()
}
pub fn stop_recording(&mut self) -> Option<Recording> {
self.recording.take()
}
}
pub const RECORDING_MAGIC: [u8; 4] = *b"AVSR";
pub const RECORDING_VERSION: u16 = 1;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct VehicleSnapshot {
pub body: usize,
pub engine_rpm: Fix128,
pub current_gear: usize,
pub input: DriverInput,
pub wheels: Vec<WheelDynamicsState>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Recording {
dt: Fix128,
start_time: Fix128,
start_frame: u64,
world_state: Vec<u8>,
vehicles: Vec<VehicleSnapshot>,
frames: u64,
inputs: Vec<DriverInput>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ReplayError {
BadMagic,
UnsupportedVersion(u16),
Truncated,
TrailingBytes,
InvalidFlag,
Oversized,
VehicleCountMismatch {
recorded: usize,
scenario: usize,
},
BodyMismatch {
vehicle: usize,
recorded: usize,
scenario: usize,
},
WheelCountMismatch {
vehicle: usize,
recorded: usize,
scenario: usize,
},
WorldStateRejected,
}
impl Recording {
#[must_use]
pub fn frame_count(&self) -> usize {
self.frames as usize
}
#[must_use]
pub fn vehicle_count(&self) -> usize {
self.vehicles.len()
}
#[must_use]
pub fn vehicles(&self) -> &[VehicleSnapshot] {
&self.vehicles
}
#[must_use]
pub fn dt(&self) -> Fix128 {
self.dt
}
#[must_use]
pub fn inputs(&self, k: usize) -> Option<&[DriverInput]> {
if k >= self.frame_count() {
return None;
}
let n = self.vehicles.len();
Some(&self.inputs[k * n..(k + 1) * n])
}
pub fn input_mut(&mut self, k: usize, i: usize) -> Option<&mut DriverInput> {
let n = self.vehicles.len();
if k >= self.frame_count() || i >= n {
return None;
}
self.inputs.get_mut(k * n + i)
}
pub fn restore<R: RoadSurface>(&self, scenario: &mut Scenario<R>) -> Result<(), ReplayError> {
if scenario.vehicles.len() != self.vehicles.len() {
return Err(ReplayError::VehicleCountMismatch {
recorded: self.vehicles.len(),
scenario: scenario.vehicles.len(),
});
}
for (i, (snap, v)) in self.vehicles.iter().zip(&scenario.vehicles).enumerate() {
if snap.body != v.body {
return Err(ReplayError::BodyMismatch {
vehicle: i,
recorded: snap.body,
scenario: v.body,
});
}
let wheels = v.vehicle.config.base.wheels.len();
if snap.wheels.len() != wheels {
return Err(ReplayError::WheelCountMismatch {
vehicle: i,
recorded: snap.wheels.len(),
scenario: wheels,
});
}
}
if !scenario.world.deserialize_state(&self.world_state) {
return Err(ReplayError::WorldStateRejected);
}
for (snap, v) in self.vehicles.iter().zip(&mut scenario.vehicles) {
v.vehicle.wheels.clone_from(&snap.wheels);
v.vehicle.engine_rpm = snap.engine_rpm;
v.vehicle.config.powertrain.current_gear = snap.current_gear;
v.vehicle.input = snap.input;
}
scenario.dt = self.dt;
scenario.time = self.start_time;
scenario.frame = self.start_frame;
Ok(())
}
pub fn replay<R: RoadSurface>(&self, scenario: &mut Scenario<R>) -> Result<(), ReplayError> {
self.restore(scenario)?;
for k in 0..self.frame_count() {
if let Some(inputs) = self.inputs(k) {
scenario.step(inputs);
}
}
Ok(())
}
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
let mut out = Vec::new();
out.extend_from_slice(&RECORDING_MAGIC);
out.extend_from_slice(&RECORDING_VERSION.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
put_fix(&mut out, self.dt);
put_fix(&mut out, self.start_time);
out.extend_from_slice(&self.start_frame.to_le_bytes());
out.extend_from_slice(&(self.world_state.len() as u32).to_le_bytes());
out.extend_from_slice(&self.world_state);
out.extend_from_slice(&(self.vehicles.len() as u32).to_le_bytes());
for v in &self.vehicles {
out.extend_from_slice(&(v.body as u64).to_le_bytes());
put_fix(&mut out, v.engine_rpm);
out.extend_from_slice(&(v.current_gear as u64).to_le_bytes());
put_input(&mut out, &v.input);
out.extend_from_slice(&(v.wheels.len() as u32).to_le_bytes());
for w in &v.wheels {
put_wheel(&mut out, w);
}
}
out.extend_from_slice(&self.frames.to_le_bytes());
for inp in &self.inputs {
put_input(&mut out, inp);
}
out
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, ReplayError> {
let mut r = Reader { bytes, pos: 0 };
if r.take(4)? != RECORDING_MAGIC {
return Err(ReplayError::BadMagic);
}
let version = r.u16()?;
if version != RECORDING_VERSION {
return Err(ReplayError::UnsupportedVersion(version));
}
let _reserved = r.u16()?;
let dt = r.fix()?;
let start_time = r.fix()?;
let start_frame = r.u64()?;
let blob_len = r.u32()? as usize;
let world_state = r.take(blob_len)?.to_vec();
let n = r.u32()? as usize;
let mut vehicles = Vec::new();
for _ in 0..n {
let body = usize::try_from(r.u64()?).map_err(|_| ReplayError::Oversized)?;
let engine_rpm = r.fix()?;
let current_gear = usize::try_from(r.u64()?).map_err(|_| ReplayError::Oversized)?;
let input = r.input()?;
let nw = r.u32()? as usize;
let mut wheels = Vec::new();
for _ in 0..nw {
wheels.push(r.wheel()?);
}
vehicles.push(VehicleSnapshot {
body,
engine_rpm,
current_gear,
input,
wheels,
});
}
let frames = r.u64()?;
let count = usize::try_from(frames)
.ok()
.and_then(|f| f.checked_mul(n))
.ok_or(ReplayError::Oversized)?;
let need = count
.checked_mul(INPUT_BYTES)
.ok_or(ReplayError::Oversized)?;
if r.remaining() < need {
return Err(ReplayError::Truncated);
}
let mut inputs = Vec::with_capacity(count);
for _ in 0..count {
inputs.push(r.input()?);
}
if r.remaining() != 0 {
return Err(ReplayError::TrailingBytes);
}
Ok(Self {
dt,
start_time,
start_frame,
world_state,
vehicles,
frames,
inputs,
})
}
}
const INPUT_BYTES: usize = 64;
fn put_fix(out: &mut Vec<u8>, x: Fix128) {
out.extend_from_slice(&x.hi.to_le_bytes());
out.extend_from_slice(&x.lo.to_le_bytes());
}
fn put_vec(out: &mut Vec<u8>, v: Vec3Fix) {
put_fix(out, v.x);
put_fix(out, v.y);
put_fix(out, v.z);
}
fn put_input(out: &mut Vec<u8>, i: &DriverInput) {
put_fix(out, i.throttle);
put_fix(out, i.brake);
put_fix(out, i.handbrake);
put_fix(out, i.steering);
}
fn put_wheel(out: &mut Vec<u8>, w: &WheelDynamicsState) {
out.push(u8::from(w.grounded));
put_vec(out, w.contact_point);
put_vec(out, w.contact_normal);
for x in [
w.compression,
w.normal_load,
w.steer_angle,
w.omega,
w.spin_angle,
w.slip_ratio,
w.slip_tan_alpha,
w.longitudinal_force,
w.lateral_force,
w.brake_torque,
] {
put_fix(out, x);
}
out.push(u8::from(w.abs_active));
match w.anchor {
Some(a) => {
out.push(1);
put_vec(out, a);
}
None => {
out.push(0);
put_vec(out, Vec3Fix::ZERO);
}
}
}
struct Reader<'a> {
bytes: &'a [u8],
pos: usize,
}
impl<'a> Reader<'a> {
fn remaining(&self) -> usize {
self.bytes.len() - self.pos
}
fn take(&mut self, n: usize) -> Result<&'a [u8], ReplayError> {
if self.remaining() < n {
return Err(ReplayError::Truncated);
}
let s = &self.bytes[self.pos..self.pos + n];
self.pos += n;
Ok(s)
}
fn array<const N: usize>(&mut self) -> Result<[u8; N], ReplayError> {
let mut a = [0u8; N];
a.copy_from_slice(self.take(N)?);
Ok(a)
}
fn u16(&mut self) -> Result<u16, ReplayError> {
Ok(u16::from_le_bytes(self.array()?))
}
fn u32(&mut self) -> Result<u32, ReplayError> {
Ok(u32::from_le_bytes(self.array()?))
}
fn u64(&mut self) -> Result<u64, ReplayError> {
Ok(u64::from_le_bytes(self.array()?))
}
fn flag(&mut self) -> Result<bool, ReplayError> {
match self.take(1)?[0] {
0 => Ok(false),
1 => Ok(true),
_ => Err(ReplayError::InvalidFlag),
}
}
fn fix(&mut self) -> Result<Fix128, ReplayError> {
let hi = i64::from_le_bytes(self.array()?);
let lo = u64::from_le_bytes(self.array()?);
Ok(Fix128::from_raw(hi, lo))
}
fn vec(&mut self) -> Result<Vec3Fix, ReplayError> {
Ok(Vec3Fix::new(self.fix()?, self.fix()?, self.fix()?))
}
fn input(&mut self) -> Result<DriverInput, ReplayError> {
Ok(DriverInput {
throttle: self.fix()?,
brake: self.fix()?,
handbrake: self.fix()?,
steering: self.fix()?,
})
}
fn wheel(&mut self) -> Result<WheelDynamicsState, ReplayError> {
let grounded = self.flag()?;
let contact_point = self.vec()?;
let contact_normal = self.vec()?;
let compression = self.fix()?;
let normal_load = self.fix()?;
let steer_angle = self.fix()?;
let omega = self.fix()?;
let spin_angle = self.fix()?;
let slip_ratio = self.fix()?;
let slip_tan_alpha = self.fix()?;
let longitudinal_force = self.fix()?;
let lateral_force = self.fix()?;
let brake_torque = self.fix()?;
let abs_active = self.flag()?;
let has_anchor = self.flag()?;
let a = self.vec()?;
Ok(WheelDynamicsState {
grounded,
contact_point,
contact_normal,
compression,
normal_load,
steer_angle,
omega,
spin_angle,
slip_ratio,
slip_tan_alpha,
longitudinal_force,
lateral_force,
brake_torque,
abs_active,
anchor: has_anchor.then_some(a),
})
}
}