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 snapshot;
26mod snapshot_joints;
27mod snapshot_structs;
28mod snapshot_world;
29mod write;
30
31pub use ops::*;
32pub use ops_body::*;
33pub use ops_joint::*;
34pub use ops_query::*;
35pub use ops_shape::*;
36pub use snapshot::*;
37pub use snapshot_world::*;
38pub use write::*;
39
40pub const SNAP_FNV_INIT: u64 = 14695981039346656037;
42
43pub const SNAP_FNV_PRIME: u64 = 1099511628211;
45
46pub const REC_MAGIC: u32 = 0x43523242;
48
49pub const REC_VERSION_MAJOR: u16 = 3;
53pub const REC_VERSION_MINOR: u16 = 2;
54
55pub fn fnv_mix_position(mut hash: u64, p: Pos) -> u64 {
59 let (bx, by) = pos_bits(p);
62 hash = (hash ^ bx).wrapping_mul(SNAP_FNV_PRIME);
63 hash = (hash ^ by).wrapping_mul(SNAP_FNV_PRIME);
64 hash
65}
66
67#[cfg(feature = "double-precision")]
68fn pos_bits(p: Pos) -> (u64, u64) {
69 (p.x.to_bits(), p.y.to_bits())
70}
71
72#[cfg(not(feature = "double-precision"))]
73fn pos_bits(p: Pos) -> (u64, u64) {
74 (p.x.to_bits() as u64, p.y.to_bits() as u64)
75}
76
77#[derive(Debug, Clone, Copy, PartialEq)]
79pub struct RecHeader {
80 pub magic: u32,
82 pub version_major: u16,
83 pub version_minor: u16,
84 pub length_scale: f32,
86 pub pointer_width: u8,
88 pub big_endian: u8,
90 pub validation_enabled: u8,
92 pub snapshot_size: u64,
94}
95
96impl RecHeader {
97 pub const SIZE: usize = 32;
98
99 pub fn write(&self, buf: &mut Vec<u8>) {
102 rec_w_u32(buf, self.magic);
103 rec_w_u16(buf, self.version_major);
104 rec_w_u16(buf, self.version_minor);
105 rec_w_u32(buf, 0); rec_w_f32(buf, self.length_scale);
107 rec_w_u8(buf, 0); rec_w_u8(buf, self.pointer_width);
109 rec_w_u8(buf, self.big_endian);
110 rec_w_u8(buf, self.validation_enabled);
111 rec_w_u32(buf, 0); rec_w_u64(buf, self.snapshot_size);
113 }
114
115 pub fn read(data: &[u8]) -> Option<RecHeader> {
117 if data.len() < Self::SIZE {
118 return None;
119 }
120 let u16_at = |o: usize| u16::from_le_bytes([data[o], data[o + 1]]);
121 let u32_at =
122 |o: usize| u32::from_le_bytes([data[o], data[o + 1], data[o + 2], data[o + 3]]);
123 let u64_at = |o: usize| {
124 u64::from_le_bytes([
125 data[o],
126 data[o + 1],
127 data[o + 2],
128 data[o + 3],
129 data[o + 4],
130 data[o + 5],
131 data[o + 6],
132 data[o + 7],
133 ])
134 };
135 Some(RecHeader {
136 magic: u32_at(0),
137 version_major: u16_at(4),
138 version_minor: u16_at(6),
139 length_scale: f32::from_bits(u32_at(12)),
140 pointer_width: data[17],
141 big_endian: data[18],
142 validation_enabled: data[19],
143 snapshot_size: u64_at(24),
144 })
145 }
146}
147
148#[derive(Debug, Default)]
152pub struct Recording {
153 pub buffer: Vec<u8>,
154
155 record_start: usize,
157
158 pub accumulated_bounds: Aabb,
162 pub have_bounds: bool,
163}
164
165impl Recording {
166 pub fn new(capacity_hint: usize) -> Recording {
168 Recording {
169 buffer: Vec::with_capacity(capacity_hint),
170 record_start: 0,
171 accumulated_bounds: Aabb::default(),
172 have_bounds: false,
173 }
174 }
175
176 pub fn begin_record(&mut self, opcode: u8) {
179 rec_w_u8(&mut self.buffer, opcode);
180 self.record_start = self.buffer.len();
181 self.buffer.extend_from_slice(&[0, 0, 0]);
182 }
183
184 pub fn end_record(&mut self) {
187 let payload_size = self.buffer.len() - self.record_start - 3;
188 debug_assert!(payload_size < (1 << 24));
189 let p = &mut self.buffer[self.record_start..self.record_start + 3];
190 p[0] = payload_size as u8;
191 p[1] = (payload_size >> 8) as u8;
192 p[2] = (payload_size >> 16) as u8;
193 }
194
195 pub fn commit_record(&mut self, opcode: u8, payload: &[u8]) {
198 debug_assert!(payload.len() < (1 << 24));
199 rec_w_u8(&mut self.buffer, opcode);
200 let size = payload.len();
201 self.buffer
202 .extend_from_slice(&[size as u8, (size >> 8) as u8, (size >> 16) as u8]);
203 self.buffer.extend_from_slice(payload);
204 }
205
206 pub fn accumulate_bounds(&mut self, bounds: Aabb) {
209 if self.have_bounds {
210 self.accumulated_bounds =
211 crate::math_functions::aabb_union(self.accumulated_bounds, bounds);
212 } else {
213 self.accumulated_bounds = bounds;
214 self.have_bounds = true;
215 }
216 }
217}
218
219pub fn rec_reserve_u32(buf: &mut Vec<u8>) -> usize {
222 let offset = buf.len();
223 buf.extend_from_slice(&[0, 0, 0, 0]);
224 offset
225}
226
227pub fn rec_patch_u32(buf: &mut [u8], offset: usize, v: u32) {
229 debug_assert!(offset + 4 <= buf.len());
230 buf[offset..offset + 4].copy_from_slice(&v.to_le_bytes());
231}
232
233pub fn hash_world_state(world: &World) -> u64 {
237 let mut hash = SNAP_FNV_INIT;
238 let prime = SNAP_FNV_PRIME;
239
240 let mix_f32 = |hash: &mut u64, f: f32| {
241 *hash = (*hash ^ f.to_bits() as u64).wrapping_mul(prime);
242 };
243
244 for (i, body) in world.bodies.iter().enumerate() {
245 if body.id != i as i32 {
246 continue;
248 }
249
250 let sim = &world.solver_sets[body.set_index as usize].body_sims[body.local_index as usize];
251
252 hash = fnv_mix_position(hash, sim.transform.p);
253 mix_f32(&mut hash, sim.transform.q.c);
254 mix_f32(&mut hash, sim.transform.q.s);
255
256 if body.set_index == crate::solver_set::AWAKE_SET {
257 let state = &world.solver_sets[crate::solver_set::AWAKE_SET as usize].body_states
258 [body.local_index as usize];
259 mix_f32(&mut hash, state.linear_velocity.x);
260 mix_f32(&mut hash, state.linear_velocity.y);
261 mix_f32(&mut hash, state.angular_velocity);
262 }
263 }
264
265 hash
266}