Skip to main content

dynamis_world/world/
mod.rs

1mod backend;
2mod body;
3mod clock;
4mod commands;
5mod constraint;
6mod event;
7mod ids;
8mod query;
9pub(crate) mod query_pool;
10mod readback;
11mod rows;
12mod shape;
13pub(crate) mod shape_pool;
14pub(crate) mod static_aabb;
15mod step;
16
17use crate::dynamics::capacity::{Live, StreamCapacity};
18use backend::Backend;
19use body::Bodies;
20use clock::Clock;
21use constraint::Constraints;
22use dynamis_gpu::{GpuBuffer, GpuContext, WarmupBudget, WarmupProgress};
23use dynamis_layout::{
24    COUNTER_BODIES, COUNTER_BODY_EDITS, COUNTER_BODY_MOVES, COUNTER_CONSTRAINT_COMMANDS,
25    COUNTER_CONSTRAINT_MOVES, COUNTER_CONSTRAINTS,
26};
27use dynamis_model::{BodyHandle, PhysicsConfig};
28use event::Events;
29use query::Queries;
30use shape::Shapes;
31
32pub use readback::{ContactManifold, ContactPoint};
33
34pub struct World {
35    config: PhysicsConfig,
36
37    clock: Clock,
38    backend: Backend,
39    bodies: Bodies,
40    constraints: Constraints,
41    shapes: Shapes,
42    queries: Queries,
43    events: Events,
44}
45
46fn contiguous_runs(slots: &[u32]) -> Vec<&[u32]> {
47    let mut runs = Vec::new();
48    let mut start = 0usize;
49    for index in 1..=slots.len() {
50        let breaks = index == slots.len() || slots[index] != slots[index - 1] + 1;
51        if breaks {
52            if index > start {
53                runs.push(&slots[start..index]);
54            }
55            start = index;
56        }
57    }
58    runs
59}
60
61fn assert_config(config: &PhysicsConfig) {
62    assert!(
63        (0.0..=1.0).contains(&config.relaxation),
64        "position relaxation must be within (0, 1]"
65    );
66}
67
68impl World {
69    pub fn new(gpu: GpuContext, config: PhysicsConfig) -> Self {
70        assert_config(&config);
71        let shapes = Shapes::new();
72        let backend = Backend::new(gpu, &shapes);
73        Self {
74            config,
75            clock: Clock::new(),
76            backend,
77            bodies: Bodies::new(),
78            constraints: Constraints::new(),
79            shapes,
80            queries: Queries::new(),
81            events: Events::new(),
82        }
83    }
84
85    pub fn config(&self) -> &PhysicsConfig {
86        &self.config
87    }
88
89    pub fn set_config(&mut self, config: PhysicsConfig) {
90        assert_config(&config);
91        self.config = config;
92    }
93
94    pub fn set_gravity(&mut self, gravity: [f32; 3]) {
95        self.config.gravity = gravity;
96    }
97
98    pub fn stream_capacity(&self) -> StreamCapacity {
99        self.backend.reservation.streams()
100    }
101
102    pub fn bodies(&self) -> &[BodyHandle] {
103        &self.bodies.alive
104    }
105
106    pub fn count(&self) -> usize {
107        self.bodies.alive.len()
108    }
109
110    pub(crate) fn live(&self) -> Live {
111        Live {
112            bodies: self.bodies.alive.len() as u32,
113            constraints: self.constraints.alive.len() as u32,
114            body_commands: self.bodies.commands.len() as u32,
115            constraint_commands: self.constraints.commands.len() as u32,
116            queries: self.queries.pending.len() as u32,
117        }
118    }
119
120    pub(crate) fn flush_rows(&mut self) {
121        let queue = self.backend.gpu.queue();
122        if self.shapes.dirty {
123            self.shapes.pool.upload_pending(
124                queue,
125                &self.backend.buffers.shapes.sources,
126                &self.backend.buffers.shapes.vertices,
127                &self.backend.buffers.shapes.triangles,
128                &self.backend.buffers.shapes.nodes,
129            );
130            self.shapes.dirty = false;
131        }
132        self.bodies.dirty.sort_unstable();
133        self.bodies.dirty.dedup();
134        for run in contiguous_runs(&std::mem::take(&mut self.bodies.dirty)) {
135            let mut colliders = Vec::new();
136            let mut descriptors = Vec::new();
137            let mut aabbs = Vec::new();
138            for slot in run {
139                let id = self.bodies.alive[*slot as usize].id as usize;
140                colliders.extend_from_slice(&self.collider_block_of(id));
141                descriptors.push(self.bodies.descriptors[id]);
142                aabbs.extend_from_slice(&self.aabb_block_of(id));
143            }
144            let first = run[0];
145
146            self.backend.buffers.bodies.colliders.write_at(
147                queue,
148                first as u64 * self.backend.buffers.collider_row(),
149                bytemuck::cast_slice(&colliders),
150            );
151            self.backend.buffers.bodies.descriptors.write_at(
152                queue,
153                first as u64 * self.backend.buffers.descriptor_row(),
154                bytemuck::cast_slice(&descriptors),
155            );
156            self.backend.buffers.bodies.aabbs.write_at(
157                queue,
158                first as u64 * self.backend.buffers.aabb_row(),
159                bytemuck::cast_slice(&aabbs),
160            );
161        }
162        self.constraints.dirty.sort_unstable();
163        self.constraints.dirty.dedup();
164        for run in contiguous_runs(&std::mem::take(&mut self.constraints.dirty)) {
165            let first = run[0];
166            let records = run
167                .iter()
168                .map(|slot| self.constraints.records[*slot as usize])
169                .collect::<Vec<_>>();
170            self.backend.buffers.constraints.descriptors.write_at(
171                queue,
172                first as u64 * self.backend.buffers.constraint_row(),
173                bytemuck::cast_slice(&records),
174            );
175        }
176    }
177
178    pub(crate) fn write_declared_counters(&self) {
179        let queue = self.backend.gpu.queue();
180        let declared = [
181            (COUNTER_BODIES, self.bodies.alive.len() as u32),
182            (COUNTER_CONSTRAINTS, self.constraints.alive.len() as u32),
183            (COUNTER_BODY_EDITS, self.bodies.last_edits),
184            (COUNTER_BODY_MOVES, self.bodies.last_moves),
185            (COUNTER_CONSTRAINT_COMMANDS, self.constraints.last_commands),
186            (COUNTER_CONSTRAINT_MOVES, self.constraints.last_moves),
187        ];
188        for (slot, value) in declared {
189            self.backend.buffers.counters.write_at(
190                queue,
191                slot as u64 * dynamis_layout::COUNTER_STRIDE,
192                bytemuck::cast_slice(&[value]),
193            );
194        }
195    }
196
197    pub fn state_buffer(&self) -> &GpuBuffer {
198        &self.backend.buffers.bodies.states
199    }
200
201    pub fn collider_buffer(&self) -> &GpuBuffer {
202        &self.backend.buffers.bodies.colliders
203    }
204
205    pub fn gpu(&self) -> &GpuContext {
206        &self.backend.gpu
207    }
208
209    pub fn warmup(&self, budget: WarmupBudget) -> WarmupProgress {
210        self.backend.gpu.warmup(budget)
211    }
212
213    pub fn is_warm(&self) -> bool {
214        self.backend.gpu.is_warm()
215    }
216
217    pub(crate) fn event_slot_of(&self, step: u64) -> u32 {
218        (step % crate::dynamics::buffers::EVENT_SLOTS as u64) as u32
219    }
220}