symtropy_physics/
replay.rs1use nalgebra::SVector;
15use symtropy_math::Bivector;
16
17use crate::body::{BodyHandle, BodyType, RigidBody};
18use crate::integrator;
19use crate::world::PhysicsWorld;
20
21#[derive(Clone, Debug)]
23pub enum WorldCommand<const D: usize> {
24 ApplyForce {
25 body: BodyHandle,
26 force: SVector<f64, D>,
27 },
28 ApplyImpulse {
29 body: BodyHandle,
30 impulse: SVector<f64, D>,
31 },
32 SetLinearVelocity {
33 body: BodyHandle,
34 velocity: SVector<f64, D>,
35 },
36 SetAngularVelocity {
37 body: BodyHandle,
38 velocity: Bivector<D>,
39 },
40 Wake {
41 body: BodyHandle,
42 },
43}
44
45#[derive(Clone, Debug)]
47pub struct ReplayFrame<const D: usize> {
48 pub dt: f64,
49 pub commands: Vec<WorldCommand<D>>,
50}
51
52#[derive(Clone, Debug, Default)]
54pub struct ReplayTape<const D: usize> {
55 pub frames: Vec<ReplayFrame<D>>,
56}
57
58impl<const D: usize> ReplayTape<D> {
59 pub fn push_frame(&mut self, dt: f64, commands: Vec<WorldCommand<D>>) {
60 self.frames.push(ReplayFrame { dt, commands });
61 }
62}
63
64#[derive(Clone, Debug, PartialEq, Eq)]
65pub enum ApplyCommandError {
66 MissingBody(BodyHandle),
67}
68
69pub fn apply_commands<const D: usize>(
71 world: &mut PhysicsWorld<D>,
72 commands: &[WorldCommand<D>],
73) -> Result<(), ApplyCommandError> {
74 for cmd in commands {
75 match cmd {
76 WorldCommand::ApplyForce { body, force } => {
77 let Some(b) = world.body_mut(*body) else {
78 return Err(ApplyCommandError::MissingBody(*body));
79 };
80 b.apply_force(force.clone());
81 }
82 WorldCommand::ApplyImpulse { body, impulse } => {
83 let Some(b) = world.body_mut(*body) else {
84 return Err(ApplyCommandError::MissingBody(*body));
85 };
86 integrator::apply_impulse(b, impulse);
87 }
88 WorldCommand::SetLinearVelocity { body, velocity } => {
89 let Some(b) = world.body_mut(*body) else {
90 return Err(ApplyCommandError::MissingBody(*body));
91 };
92 b.linear_velocity = velocity.clone();
93 }
94 WorldCommand::SetAngularVelocity { body, velocity } => {
95 let Some(b) = world.body_mut(*body) else {
96 return Err(ApplyCommandError::MissingBody(*body));
97 };
98 b.angular_velocity = *velocity;
99 }
100 WorldCommand::Wake { body } => {
101 let Some(b) = world.body_mut(*body) else {
102 return Err(ApplyCommandError::MissingBody(*body));
103 };
104 b.wake();
105 }
106 }
107 }
108 Ok(())
109}
110
111#[derive(Clone, Debug, PartialEq, Eq)]
113pub struct BodySnapshot<const D: usize> {
114 pub handle: BodyHandle,
115 pub body_type: BodyType,
116 pub translation: [u64; D],
117 pub rotation: [[u64; D]; D],
118 pub linear_velocity: [u64; D],
119 pub angular_velocity: [[u64; D]; D],
120 pub sleeping: bool,
121 pub sleep_counter: u32,
122}
123
124impl<const D: usize> BodySnapshot<D> {
125 pub fn from_body(body: &RigidBody<D>) -> Self {
126 let translation = std::array::from_fn(|i| body.transform.translation.0[i].to_bits());
127
128 let rot = body.transform.rotation.to_matrix();
129 let rotation =
130 std::array::from_fn(|r| std::array::from_fn(|c| rot[(r, c)].to_bits()));
131
132 let linear_velocity = std::array::from_fn(|i| body.linear_velocity[i].to_bits());
133
134 let ang = body.angular_velocity.to_matrix();
135 let angular_velocity =
136 std::array::from_fn(|r| std::array::from_fn(|c| ang[(r, c)].to_bits()));
137
138 Self {
139 handle: body.handle,
140 body_type: body.body_type,
141 translation,
142 rotation,
143 linear_velocity,
144 angular_velocity,
145 sleeping: body.sleeping,
146 sleep_counter: body.sleep_counter,
147 }
148 }
149}
150
151#[derive(Clone, Debug, PartialEq, Eq)]
153pub struct CollisionEventSnapshot<const D: usize> {
154 pub body_a: BodyHandle,
155 pub body_b: BodyHandle,
156 pub impulse: u64,
157 pub normal: [u64; D],
158 pub depth: u64,
159}
160
161impl<const D: usize> CollisionEventSnapshot<D> {
162 pub fn from_event(event: &crate::contact::CollisionEvent<D>) -> Self {
163 Self {
164 body_a: event.body_a,
165 body_b: event.body_b,
166 impulse: event.impulse.to_bits(),
167 normal: std::array::from_fn(|i| event.normal[i].to_bits()),
168 depth: event.depth.to_bits(),
169 }
170 }
171}
172
173#[derive(Clone, Debug, PartialEq, Eq)]
175pub struct WorldSnapshot<const D: usize> {
176 pub bodies: Vec<BodySnapshot<D>>,
177 pub collision_events: Vec<CollisionEventSnapshot<D>>,
178}
179
180impl<const D: usize> WorldSnapshot<D> {
181 pub fn capture(world: &PhysicsWorld<D>) -> Self {
182 let mut bodies: Vec<_> = world.bodies.iter().map(BodySnapshot::from_body).collect();
183 bodies.sort_by_key(|b| b.handle);
184
185 let mut collision_events: Vec<_> = world
186 .collision_events
187 .iter()
188 .map(CollisionEventSnapshot::from_event)
189 .collect();
190 collision_events.sort_by_key(|e| (e.body_a, e.body_b, e.impulse, e.depth));
191
192 Self {
193 bodies,
194 collision_events,
195 }
196 }
197}