box2d_rust/recording/
mod.rs1use crate::math_functions::{Aabb, Pos};
18use crate::world::World;
19
20mod ops;
21mod ops_body;
22mod ops_joint;
23mod ops_query;
24mod ops_shape;
25mod player;
26mod player_queries;
27mod snapshot;
28mod snapshot_joints;
29mod snapshot_structs;
30mod snapshot_world;
31mod write;
32
33pub use ops::*;
34pub use ops_body::*;
35pub use ops_joint::*;
36pub use ops_query::*;
37pub use ops_shape::*;
38pub use player::*;
39pub use player_queries::{RecQueryHit, RecQueryInfo, RecQueryType};
40pub use snapshot::*;
41pub use snapshot_world::*;
42pub use write::*;
43
44pub const SNAP_FNV_INIT: u64 = 14695981039346656037;
46
47pub const SNAP_FNV_PRIME: u64 = 1099511628211;
49
50pub const REC_MAGIC: u32 = 0x43523242;
52
53pub const REC_VERSION_MAJOR: u16 = 3;
57pub const REC_VERSION_MINOR: u16 = 2;
58
59pub fn fnv_mix_position(mut hash: u64, p: Pos) -> u64 {
63 let (bx, by) = pos_bits(p);
66 hash = (hash ^ bx).wrapping_mul(SNAP_FNV_PRIME);
67 hash = (hash ^ by).wrapping_mul(SNAP_FNV_PRIME);
68 hash
69}
70
71#[cfg(feature = "double-precision")]
72fn pos_bits(p: Pos) -> (u64, u64) {
73 (p.x.to_bits(), p.y.to_bits())
74}
75
76#[cfg(not(feature = "double-precision"))]
77fn pos_bits(p: Pos) -> (u64, u64) {
78 (p.x.to_bits() as u64, p.y.to_bits() as u64)
79}
80
81#[derive(Debug, Clone, Copy, PartialEq)]
83pub struct RecHeader {
84 pub magic: u32,
86 pub version_major: u16,
87 pub version_minor: u16,
88 pub length_scale: f32,
90 pub pointer_width: u8,
92 pub big_endian: u8,
94 pub validation_enabled: u8,
96 pub snapshot_size: u64,
98}
99
100impl RecHeader {
101 pub const SIZE: usize = 32;
102
103 pub fn write(&self, buf: &mut Vec<u8>) {
106 rec_w_u32(buf, self.magic);
107 rec_w_u16(buf, self.version_major);
108 rec_w_u16(buf, self.version_minor);
109 rec_w_u32(buf, 0); rec_w_f32(buf, self.length_scale);
111 rec_w_u8(buf, 0); rec_w_u8(buf, self.pointer_width);
113 rec_w_u8(buf, self.big_endian);
114 rec_w_u8(buf, self.validation_enabled);
115 rec_w_u32(buf, 0); rec_w_u64(buf, self.snapshot_size);
117 }
118
119 pub fn read(data: &[u8]) -> Option<RecHeader> {
121 if data.len() < Self::SIZE {
122 return None;
123 }
124 let u16_at = |o: usize| u16::from_le_bytes([data[o], data[o + 1]]);
125 let u32_at =
126 |o: usize| u32::from_le_bytes([data[o], data[o + 1], data[o + 2], data[o + 3]]);
127 let u64_at = |o: usize| {
128 u64::from_le_bytes([
129 data[o],
130 data[o + 1],
131 data[o + 2],
132 data[o + 3],
133 data[o + 4],
134 data[o + 5],
135 data[o + 6],
136 data[o + 7],
137 ])
138 };
139 Some(RecHeader {
140 magic: u32_at(0),
141 version_major: u16_at(4),
142 version_minor: u16_at(6),
143 length_scale: f32::from_bits(u32_at(12)),
144 pointer_width: data[17],
145 big_endian: data[18],
146 validation_enabled: data[19],
147 snapshot_size: u64_at(24),
148 })
149 }
150}
151
152#[derive(Debug, Default)]
156pub struct Recording {
157 pub buffer: Vec<u8>,
158
159 record_start: usize,
161
162 pub accumulated_bounds: Aabb,
166 pub have_bounds: bool,
167}
168
169impl Recording {
170 pub fn new(capacity_hint: usize) -> Recording {
172 Recording {
173 buffer: Vec::with_capacity(capacity_hint),
174 record_start: 0,
175 accumulated_bounds: Aabb::default(),
176 have_bounds: false,
177 }
178 }
179
180 pub fn begin_record(&mut self, opcode: u8) {
183 rec_w_u8(&mut self.buffer, opcode);
184 self.record_start = self.buffer.len();
185 self.buffer.extend_from_slice(&[0, 0, 0]);
186 }
187
188 pub fn end_record(&mut self) {
191 let payload_size = self.buffer.len() - self.record_start - 3;
192 debug_assert!(payload_size < (1 << 24));
193 let p = &mut self.buffer[self.record_start..self.record_start + 3];
194 p[0] = payload_size as u8;
195 p[1] = (payload_size >> 8) as u8;
196 p[2] = (payload_size >> 16) as u8;
197 }
198
199 pub fn commit_record(&mut self, opcode: u8, payload: &[u8]) {
202 debug_assert!(payload.len() < (1 << 24));
203 rec_w_u8(&mut self.buffer, opcode);
204 let size = payload.len();
205 self.buffer
206 .extend_from_slice(&[size as u8, (size >> 8) as u8, (size >> 16) as u8]);
207 self.buffer.extend_from_slice(payload);
208 }
209
210 pub fn accumulate_bounds(&mut self, bounds: Aabb) {
213 if self.have_bounds {
214 self.accumulated_bounds =
215 crate::math_functions::aabb_union(self.accumulated_bounds, bounds);
216 } else {
217 self.accumulated_bounds = bounds;
218 self.have_bounds = true;
219 }
220 }
221}
222
223pub fn rec_reserve_u32(buf: &mut Vec<u8>) -> usize {
226 let offset = buf.len();
227 buf.extend_from_slice(&[0, 0, 0, 0]);
228 offset
229}
230
231pub fn rec_patch_u32(buf: &mut [u8], offset: usize, v: u32) {
233 debug_assert!(offset + 4 <= buf.len());
234 buf[offset..offset + 4].copy_from_slice(&v.to_le_bytes());
235}
236
237pub fn hash_world_state(world: &World) -> u64 {
241 let mut hash = SNAP_FNV_INIT;
242 let prime = SNAP_FNV_PRIME;
243
244 let mix_f32 = |hash: &mut u64, f: f32| {
245 *hash = (*hash ^ f.to_bits() as u64).wrapping_mul(prime);
246 };
247
248 for (i, body) in world.bodies.iter().enumerate() {
249 if body.id != i as i32 {
250 continue;
252 }
253
254 let sim = &world.solver_sets[body.set_index as usize].body_sims[body.local_index as usize];
255
256 hash = fnv_mix_position(hash, sim.transform.p);
257 mix_f32(&mut hash, sim.transform.q.c);
258 mix_f32(&mut hash, sim.transform.q.s);
259
260 if body.set_index == crate::solver_set::AWAKE_SET {
261 let state = &world.solver_sets[crate::solver_set::AWAKE_SET as usize].body_states
262 [body.local_index as usize];
263 mix_f32(&mut hash, state.linear_velocity.x);
264 mix_f32(&mut hash, state.linear_velocity.y);
265 mix_f32(&mut hash, state.angular_velocity);
266 }
267 }
268
269 hash
270}