1use super::World;
2use dynamis_abi::COUNTER_RESTING;
3use dynamis_abi::{
4 COUNTER_CONTACTS, COUNTER_DEVICE_COUNT, COUNTER_STRIDE, ConstraintReactionRecord,
5 ConstraintRuntimeRecord, ContactRecord, Counters, DeclaredCounters, FEATURE_KIND_MASK,
6 FEATURE_TRIANGLE, FEATURE_TRIANGLE_MASK, JointStateRecord, NO_SURFACE, SHAPE_HEIGHTFIELD,
7 SHAPE_MESH,
8};
9use dynamis_model::{BodyHandle, ConstraintHandle, JointState, SurfaceDesc};
10use std::collections::HashSet;
11use std::mem::size_of;
12
13pub struct ContactPoint {
14 pub position: [f32; 3],
15 pub depth: f32,
16 pub normal_impulse: f32,
17 pub tangent_impulse: f32,
18 pub feature: u32,
19 pub triangle: Option<u32>,
20}
21
22pub struct ContactManifold {
23 pub first: BodyHandle,
24 pub second: BodyHandle,
25 pub sensor: bool,
26 pub normal: [f32; 3],
27 pub material: SurfaceDesc,
28 pub surface: Option<SurfaceDesc>,
29 pub points: Vec<ContactPoint>,
30 pub step: u64,
31}
32
33pub struct ConstraintForce {
34 pub constraint: ConstraintHandle,
35 pub first: BodyHandle,
36 pub second: BodyHandle,
37 pub force_on_second: [f32; 3],
38 pub torque_on_second: [f32; 3],
39}
40
41const COUNTER_BYTES: u64 = COUNTER_STRIDE * COUNTER_DEVICE_COUNT as u64;
42const CONTACT_BYTES: u64 = size_of::<ContactRecord>() as u64;
43const DECLARED_DEPTH: usize = 8;
44
45fn measured_counters(bytes: &[u8], counters: &mut Counters) {
46 let stride = COUNTER_STRIDE as usize;
47 for (slot, value) in counters.iter_mut().enumerate().take(COUNTER_DEVICE_COUNT) {
48 let at = slot * stride;
49 *value = u32::from_le_bytes(bytes[at..at + 4].try_into().expect("counter slot"));
50 }
51}
52
53impl World {
54 pub(crate) fn pack_step(&self, encoder: &mut wgpu::CommandEncoder) -> u64 {
55 encoder.copy_buffer_to_buffer(
56 self.backend.streams.state.counters.buffer(),
57 0,
58 self.backend.readback.pack.buffer(),
59 0,
60 COUNTER_BYTES,
61 );
62 COUNTER_BYTES
63 }
64
65 pub(crate) fn declare_step(&mut self, step: u64) {
66 let declared = DeclaredCounters {
67 bodies: self.bodies.alive.len() as u32,
68 colliders: self.colliders.used(),
69 constraints: self.constraints.alive.len() as u32,
70 body_edits: self.bodies.last_edits,
71 body_moves: self.bodies.last_moves,
72 constraint_commands: self.constraints.last_commands,
73 constraint_moves: self.constraints.last_moves,
74 };
75 assert!(
76 self.backend.declared.len() < DECLARED_DEPTH,
77 "a step declaration outlived its counter readback"
78 );
79 self.backend.declared.push_back((step, declared));
80 }
81
82 pub(crate) fn consume_pack(&mut self, step: u64, bytes: &[u8]) {
83 let (declared_step, declared) = self
84 .backend
85 .declared
86 .pop_front()
87 .expect("a counter readback retires a declared step");
88 assert_eq!(
89 declared_step, step,
90 "counter readbacks must retire in declaration order"
91 );
92 declared.write_into(&mut self.backend.measured);
93 measured_counters(bytes, &mut self.backend.measured);
94 self.accept_measured(step);
95 }
96
97 pub fn constraint_forces(&mut self) -> Vec<ConstraintForce> {
98 let count = self.constraints.alive.len();
99 if count == 0 {
100 return Vec::new();
101 }
102 self.wait();
103 let runtime = &self.backend.streams.state.constraint_runtime;
104 let bytes = count as u64 * runtime.stride();
105 let buffer = runtime.buffer().clone();
106 let read = self.read_regions("constraint force readback", &[(&buffer, 0, bytes)]);
107 dynamis_abi::decode::<ConstraintRuntimeRecord>(&read)
108 .into_iter()
109 .enumerate()
110 .map(|(row, runtime)| {
111 let constraint = self.constraints.alive[row];
112 let (first, second) = self.constraint_bodies(constraint);
113 self.constraint_force_of(constraint, first, second, runtime.reaction)
114 })
115 .collect()
116 }
117
118 pub fn constraint_force(&mut self, handle: ConstraintHandle) -> ConstraintForce {
119 self.validate_constraint(handle);
120 let row = self.constraints.index_of[handle.id as usize];
121 self.wait();
122 let runtime = &self.backend.streams.state.constraint_runtime;
123 let stride = runtime.stride();
124 let buffer = runtime.buffer().clone();
125 let read = self.read_regions(
126 "constraint force readback",
127 &[(&buffer, u64::from(row) * stride, stride)],
128 );
129 let record = dynamis_abi::decode::<ConstraintRuntimeRecord>(&read)
130 .first()
131 .copied()
132 .expect("a constraint force read covers exactly one record");
133 let (first, second) = self.constraint_bodies(handle);
134 self.constraint_force_of(handle, first, second, record.reaction)
135 }
136
137 pub fn joint_states(&mut self) -> Vec<(ConstraintHandle, JointState)> {
138 let count = self.constraints.alive.len();
139 if count == 0 {
140 return Vec::new();
141 }
142 self.wait();
143 let states = &self.backend.streams.rigid.joint_states;
144 let bytes = count as u64 * states.stride();
145 let buffer = states.buffer().clone();
146 let read = self.read_regions("joint state readback", &[(&buffer, 0, bytes)]);
147 dynamis_abi::decode::<JointStateRecord>(&read)
148 .into_iter()
149 .enumerate()
150 .map(|(row, state)| (self.constraints.alive[row], self.joint_state_of(row, state)))
151 .collect()
152 }
153
154 pub fn joint_state(&mut self, handle: ConstraintHandle) -> JointState {
155 self.validate_constraint(handle);
156 let row = self.constraints.index_of[handle.id as usize];
157 self.wait();
158 let states = &self.backend.streams.rigid.joint_states;
159 let stride = states.stride();
160 let buffer = states.buffer().clone();
161 let read = self.read_regions(
162 "joint state readback",
163 &[(&buffer, u64::from(row) * stride, stride)],
164 );
165 let state = dynamis_abi::decode::<JointStateRecord>(&read)
166 .first()
167 .copied()
168 .expect("a joint state read covers exactly one record");
169 self.joint_state_of(row as usize, state)
170 }
171
172 fn joint_state_of(&self, row: usize, state: JointStateRecord) -> JointState {
173 let kind = self.constraints.records[row].constraint_kind();
174 assert_eq!(
175 state.dof_count as usize,
176 kind.dofs().len(),
177 "the device and the host must agree on the {kind:?} dof layout"
178 );
179 JointState::new(kind, state.coordinates, state.rates, state.impulses)
180 }
181
182 fn constraint_force_of(
183 &self,
184 constraint: ConstraintHandle,
185 first: BodyHandle,
186 second: BodyHandle,
187 reaction: ConstraintReactionRecord,
188 ) -> ConstraintForce {
189 let step_dt = self.clock.sub_dt;
190 ConstraintForce {
191 constraint,
192 first,
193 second,
194 force_on_second: [
195 reaction.linear_second[0] / step_dt,
196 reaction.linear_second[1] / step_dt,
197 reaction.linear_second[2] / step_dt,
198 ],
199 torque_on_second: [
200 reaction.angular_second[0] / step_dt,
201 reaction.angular_second[1] / step_dt,
202 reaction.angular_second[2] / step_dt,
203 ],
204 }
205 }
206
207 pub fn contact_manifolds(&mut self) -> Vec<ContactManifold> {
208 self.wait();
209 let step = self.clock.step.saturating_sub(1);
210 let active = self.backend.measured[COUNTER_CONTACTS] as usize;
211 let capacity =
212 (self.backend.streams.rigid.resting_contacts.size() / CONTACT_BYTES) as usize;
213 let resting = (self.backend.measured[COUNTER_RESTING] as usize).min(capacity);
214 if active == 0 && resting == 0 {
215 return Vec::new();
216 }
217 let active_buffer = self.backend.streams.rigid.contacts.buffer().clone();
218 let resting_buffer = self.backend.streams.rigid.resting_contacts.buffer().clone();
219 let resting_live = self.backend.streams.rigid.resting_live.buffer().clone();
220 let mut regions = Vec::with_capacity(3);
221 if active > 0 {
222 regions.push((&active_buffer, 0, active as u64 * CONTACT_BYTES));
223 }
224 if resting > 0 {
225 regions.push((&resting_live, 0, resting as u64 * 4));
226 regions.push((&resting_buffer, 0, resting as u64 * CONTACT_BYTES));
227 }
228 let bytes = self.read_regions("world contact readback", ®ions);
229 let mut manifolds = Vec::with_capacity(active + resting);
230 let mut seen = HashSet::new();
231 let active_bytes = active * size_of::<ContactRecord>();
232 for record in dynamis_abi::decode::<ContactRecord>(&bytes[..active_bytes]) {
233 if seen.insert((record.a, record.b)) {
234 manifolds.push(manifold_of(&record, step, self.contact_surface(&record)));
235 }
236 }
237 if resting > 0 {
238 let live = &bytes[active_bytes..active_bytes + resting * 4];
239 let resting_bytes = &bytes[active_bytes + resting * 4..];
240 for (index, record) in dynamis_abi::decode::<ContactRecord>(resting_bytes)
241 .into_iter()
242 .enumerate()
243 {
244 if live[index * 4..index * 4 + 4] == [0, 0, 0, 0] {
245 continue;
246 }
247 if seen.insert((record.a, record.b)) {
248 manifolds.push(manifold_of(&record, step, self.contact_surface(&record)));
249 }
250 }
251 }
252 manifolds
253 }
254
255 pub(crate) fn read_regions(
256 &mut self,
257 label: &str,
258 regions: &[(&wgpu::Buffer, u64, u64)],
259 ) -> Vec<u8> {
260 let bytes: u64 = regions.iter().map(|region| region.2).sum();
261 assert!(
262 bytes > 0 && bytes.is_multiple_of(4),
263 "an inspection read must cover a positive word aligned length"
264 );
265 let device = self.backend.gpu.device().clone();
266 let mut readback = match self.backend.inspect.take() {
267 Some(readback) if readback.size() >= bytes => readback,
268 _ => dynamis_gpu::Readback::new(&device, "world inspection readback", bytes, 1),
269 };
270 let mut encoder = dynamis_gpu::SubmissionEncoder::new(&device, label);
271 assert!(
272 readback.enqueue_regions(&mut encoder, regions, 0).is_none(),
273 "an inspection read requires an idle readback"
274 );
275 self.submit(encoder);
276 let entry = readback
277 .drain()
278 .pop()
279 .expect("an inspection read retires exactly once");
280 self.backend.inspect = Some(readback);
281 entry.1
282 }
283
284 pub(crate) fn collect_readbacks(&mut self) {
285 self.backend.gpu.poll();
286 for (step, bytes) in self.backend.readback.step.collect() {
287 self.consume_pack(step, &bytes);
288 }
289 for (_, bytes) in self.backend.readback.events.collect() {
290 self.consume_events(&bytes);
291 }
292 for (_, bytes) in self.backend.readback.breaks.collect() {
293 self.consume_breaks(&bytes);
294 }
295 for (batch, bytes) in self.backend.readback.queries.collect() {
296 self.collect_query_batch(batch, &bytes);
297 }
298 for (sequence, bytes) in self.backend.readback.observations.collect() {
299 self.consume_observations(sequence, &bytes);
300 }
301 for (sequence, bytes) in self.backend.readback.collect_states() {
302 self.consume_states(sequence, &bytes);
303 }
304 #[cfg(feature = "profile")]
305 for timings in self.backend.passes.collect_timings() {
306 self.backend.pass_timings = timings;
307 }
308 }
309
310 pub(crate) fn drain_readbacks(&mut self) {
311 for (step, bytes) in self.backend.readback.step.drain() {
312 self.consume_pack(step, &bytes);
313 }
314 for (_, bytes) in self.backend.readback.events.drain() {
315 self.consume_events(&bytes);
316 }
317 for (_, bytes) in self.backend.readback.breaks.drain() {
318 self.consume_breaks(&bytes);
319 }
320 for (batch, bytes) in self.backend.readback.queries.drain() {
321 self.collect_query_batch(batch, &bytes);
322 }
323 for (sequence, bytes) in self.backend.readback.observations.drain() {
324 self.consume_observations(sequence, &bytes);
325 }
326 for (sequence, bytes) in self.backend.readback.drain_states() {
327 self.consume_states(sequence, &bytes);
328 }
329 #[cfg(feature = "profile")]
330 for timings in self.backend.passes.collect_timings() {
331 self.backend.pass_timings = timings;
332 }
333 }
334
335 pub(crate) fn accept_measured(&mut self, step: u64) {
336 self.backend.measured_step = Some(step);
337 self.note_events_due(step);
338 self.note_breaks_due(step);
339 }
340
341 pub fn measured(&self) -> &Counters {
342 &self.backend.measured
343 }
344
345 pub(crate) fn contact_surface(&self, record: &ContactRecord) -> Option<SurfaceDesc> {
346 if record.surface == NO_SURFACE {
347 return None;
348 }
349 for slot in [record.a, record.b] {
350 let collider = self.colliders.records()[slot as usize];
351 if collider.kind == SHAPE_MESH || collider.kind == SHAPE_HEIGHTFIELD {
352 return Some(
353 self.shapes
354 .pool
355 .source_surface(collider.source, record.surface),
356 );
357 }
358 }
359 panic!("a contact surface must belong to its scene geometry");
360 }
361
362 pub(crate) fn accept_constraint_break(&mut self, constraint_id: u32, generation: u32) {
363 let id = constraint_id as usize;
364 if id >= self.constraints.ids.len() {
365 return;
366 }
367 if self.constraints.ids.generation(constraint_id) != generation
368 || self.constraints.index_of[id] == u32::MAX
369 {
370 return;
371 }
372 let handle = ConstraintHandle {
373 id: constraint_id,
374 generation,
375 };
376 self.constraints.broken.push(handle);
377 self.remove_constraint(handle);
378 }
379}
380
381fn feature_triangle(feature: u32) -> Option<u32> {
382 ((feature & FEATURE_KIND_MASK) == FEATURE_TRIANGLE).then_some(feature & FEATURE_TRIANGLE_MASK)
383}
384
385fn manifold_of(record: &ContactRecord, step: u64, surface: Option<SurfaceDesc>) -> ContactManifold {
386 ContactManifold {
387 first: BodyHandle {
388 id: record.first_body_id,
389 generation: record.first_generation,
390 },
391 second: BodyHandle {
392 id: record.second_body_id,
393 generation: record.second_generation,
394 },
395 sensor: record.sensor == 1,
396 normal: record.normal,
397 material: SurfaceDesc {
398 friction: record.friction,
399 restitution: record.restitution,
400 rolling_friction: record.rolling_friction,
401 spin_friction: record.spin_friction,
402 },
403 surface,
404 points: record.points[..record.point_count as usize]
405 .iter()
406 .map(|point| ContactPoint {
407 position: point.position,
408 depth: point.depth,
409 normal_impulse: point.accumulated_normal,
410 tangent_impulse: (point.accumulated_tangent_1 * point.accumulated_tangent_1
411 + point.accumulated_tangent_2 * point.accumulated_tangent_2)
412 .sqrt(),
413 feature: point.feature,
414 triangle: feature_triangle(point.feature),
415 })
416 .collect(),
417 step,
418 }
419}