use crate::math_functions::{Aabb, Pos};
use crate::world::World;
mod ops;
mod ops_body;
mod ops_joint;
mod ops_query;
mod ops_shape;
mod player;
mod player_queries;
mod snapshot;
mod snapshot_joints;
mod snapshot_structs;
mod snapshot_world;
mod write;
pub use ops::*;
pub use ops_body::*;
pub use ops_joint::*;
pub use ops_query::*;
pub use ops_shape::*;
pub use player::*;
pub use player_queries::{RecQueryHit, RecQueryInfo, RecQueryType};
pub use snapshot::*;
pub use snapshot_world::*;
pub use write::*;
pub const SNAP_FNV_INIT: u64 = 14695981039346656037;
pub const SNAP_FNV_PRIME: u64 = 1099511628211;
pub const REC_MAGIC: u32 = 0x43523242;
pub const REC_VERSION_MAJOR: u16 = 3;
pub const REC_VERSION_MINOR: u16 = 2;
pub fn fnv_mix_position(mut hash: u64, p: Pos) -> u64 {
let (bx, by) = pos_bits(p);
hash = (hash ^ bx).wrapping_mul(SNAP_FNV_PRIME);
hash = (hash ^ by).wrapping_mul(SNAP_FNV_PRIME);
hash
}
#[cfg(feature = "double-precision")]
fn pos_bits(p: Pos) -> (u64, u64) {
(p.x.to_bits(), p.y.to_bits())
}
#[cfg(not(feature = "double-precision"))]
fn pos_bits(p: Pos) -> (u64, u64) {
(p.x.to_bits() as u64, p.y.to_bits() as u64)
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct RecHeader {
pub magic: u32,
pub version_major: u16,
pub version_minor: u16,
pub length_scale: f32,
pub pointer_width: u8,
pub big_endian: u8,
pub validation_enabled: u8,
pub snapshot_size: u64,
}
impl RecHeader {
pub const SIZE: usize = 32;
pub fn write(&self, buf: &mut Vec<u8>) {
rec_w_u32(buf, self.magic);
rec_w_u16(buf, self.version_major);
rec_w_u16(buf, self.version_minor);
rec_w_u32(buf, 0); rec_w_f32(buf, self.length_scale);
rec_w_u8(buf, 0); rec_w_u8(buf, self.pointer_width);
rec_w_u8(buf, self.big_endian);
rec_w_u8(buf, self.validation_enabled);
rec_w_u32(buf, 0); rec_w_u64(buf, self.snapshot_size);
}
pub fn read(data: &[u8]) -> Option<RecHeader> {
if data.len() < Self::SIZE {
return None;
}
let u16_at = |o: usize| u16::from_le_bytes([data[o], data[o + 1]]);
let u32_at =
|o: usize| u32::from_le_bytes([data[o], data[o + 1], data[o + 2], data[o + 3]]);
let u64_at = |o: usize| {
u64::from_le_bytes([
data[o],
data[o + 1],
data[o + 2],
data[o + 3],
data[o + 4],
data[o + 5],
data[o + 6],
data[o + 7],
])
};
Some(RecHeader {
magic: u32_at(0),
version_major: u16_at(4),
version_minor: u16_at(6),
length_scale: f32::from_bits(u32_at(12)),
pointer_width: data[17],
big_endian: data[18],
validation_enabled: data[19],
snapshot_size: u64_at(24),
})
}
}
#[derive(Debug, Default)]
pub struct Recording {
pub buffer: Vec<u8>,
record_start: usize,
pub accumulated_bounds: Aabb,
pub have_bounds: bool,
}
impl Recording {
pub fn new(capacity_hint: usize) -> Recording {
Recording {
buffer: Vec::with_capacity(capacity_hint),
record_start: 0,
accumulated_bounds: Aabb::default(),
have_bounds: false,
}
}
pub fn begin_record(&mut self, opcode: u8) {
rec_w_u8(&mut self.buffer, opcode);
self.record_start = self.buffer.len();
self.buffer.extend_from_slice(&[0, 0, 0]);
}
pub fn end_record(&mut self) {
let payload_size = self.buffer.len() - self.record_start - 3;
debug_assert!(payload_size < (1 << 24));
let p = &mut self.buffer[self.record_start..self.record_start + 3];
p[0] = payload_size as u8;
p[1] = (payload_size >> 8) as u8;
p[2] = (payload_size >> 16) as u8;
}
pub fn commit_record(&mut self, opcode: u8, payload: &[u8]) {
debug_assert!(payload.len() < (1 << 24));
rec_w_u8(&mut self.buffer, opcode);
let size = payload.len();
self.buffer
.extend_from_slice(&[size as u8, (size >> 8) as u8, (size >> 16) as u8]);
self.buffer.extend_from_slice(payload);
}
pub fn accumulate_bounds(&mut self, bounds: Aabb) {
if self.have_bounds {
self.accumulated_bounds =
crate::math_functions::aabb_union(self.accumulated_bounds, bounds);
} else {
self.accumulated_bounds = bounds;
self.have_bounds = true;
}
}
}
pub fn rec_reserve_u32(buf: &mut Vec<u8>) -> usize {
let offset = buf.len();
buf.extend_from_slice(&[0, 0, 0, 0]);
offset
}
pub fn rec_patch_u32(buf: &mut [u8], offset: usize, v: u32) {
debug_assert!(offset + 4 <= buf.len());
buf[offset..offset + 4].copy_from_slice(&v.to_le_bytes());
}
pub fn hash_world_state(world: &World) -> u64 {
let mut hash = SNAP_FNV_INIT;
let prime = SNAP_FNV_PRIME;
let mix_f32 = |hash: &mut u64, f: f32| {
*hash = (*hash ^ f.to_bits() as u64).wrapping_mul(prime);
};
for (i, body) in world.bodies.iter().enumerate() {
if body.id != i as i32 {
continue;
}
let sim = &world.solver_sets[body.set_index as usize].body_sims[body.local_index as usize];
hash = fnv_mix_position(hash, sim.transform.p);
mix_f32(&mut hash, sim.transform.q.c);
mix_f32(&mut hash, sim.transform.q.s);
if body.set_index == crate::solver_set::AWAKE_SET {
let state = &world.solver_sets[crate::solver_set::AWAKE_SET as usize].body_states
[body.local_index as usize];
mix_f32(&mut hash, state.linear_velocity.x);
mix_f32(&mut hash, state.linear_velocity.y);
mix_f32(&mut hash, state.angular_velocity);
}
}
hash
}