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: Box<SVector<f64, D>>,
27 },
28 ApplyImpulse {
29 body: BodyHandle,
30 impulse: Box<SVector<f64, D>>,
31 },
32 SetLinearVelocity {
33 body: BodyHandle,
34 velocity: Box<SVector<f64, D>>,
35 },
36 SetAngularVelocity {
37 body: BodyHandle,
38 velocity: Box<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);
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;
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 = std::array::from_fn(|r| std::array::from_fn(|c| rot[(r, c)].to_bits()));
130
131 let linear_velocity = std::array::from_fn(|i| body.linear_velocity[i].to_bits());
132
133 let ang = body.angular_velocity.to_matrix();
134 let angular_velocity =
135 std::array::from_fn(|r| std::array::from_fn(|c| ang[(r, c)].to_bits()));
136
137 Self {
138 handle: body.handle,
139 body_type: body.body_type,
140 translation,
141 rotation,
142 linear_velocity,
143 angular_velocity,
144 sleeping: body.sleeping,
145 sleep_counter: body.sleep_counter,
146 }
147 }
148}
149
150#[derive(Clone, Debug, PartialEq, Eq)]
152pub struct CollisionEventSnapshot<const D: usize> {
153 pub body_a: BodyHandle,
154 pub body_b: BodyHandle,
155 pub impulse: u64,
156 pub normal: [u64; D],
157 pub depth: u64,
158}
159
160impl<const D: usize> CollisionEventSnapshot<D> {
161 pub fn from_event(event: &crate::contact::CollisionEvent<D>) -> Self {
162 Self {
163 body_a: event.body_a,
164 body_b: event.body_b,
165 impulse: event.impulse.to_bits(),
166 normal: std::array::from_fn(|i| event.normal[i].to_bits()),
167 depth: event.depth.to_bits(),
168 }
169 }
170}
171
172#[derive(Clone, Debug, PartialEq, Eq)]
174pub struct WorldSnapshot<const D: usize> {
175 pub bodies: Vec<BodySnapshot<D>>,
176 pub collision_events: Vec<CollisionEventSnapshot<D>>,
177}
178
179impl<const D: usize> WorldSnapshot<D> {
180 pub fn capture(world: &PhysicsWorld<D>) -> Self {
181 let mut bodies: Vec<_> = world.bodies.iter().map(BodySnapshot::from_body).collect();
182 bodies.sort_by_key(|b| b.handle);
183
184 let mut collision_events: Vec<_> = world
185 .collision_events
186 .iter()
187 .map(CollisionEventSnapshot::from_event)
188 .collect();
189 collision_events.sort_by_key(|e| (e.body_a, e.body_b, e.impulse, e.depth));
190
191 Self {
192 bodies,
193 collision_events,
194 }
195 }
196}