1pub mod collisions;
2pub mod components;
3pub mod constraints;
4pub mod density_fields;
5pub mod queries;
6pub mod solvers;
7pub mod utils;
8
9pub mod third_party {
10 pub use vek;
11}
12
13use crate::{
14 collisions::{
15 CollisionProfile, ContactDetection, ContactsCache, DensityFieldSpatialExtractor,
16 RepulsiveCollisionCallbacks, RepulsiveCollisionSolver, collect_contacts,
17 dispatch_contact_events,
18 },
19 components::{
20 AngularVelocity, BodyDensityFieldRelation, BodyMaterial, BodyParentRelation,
21 BodyParticleRelation, ExternalForces, LinearVelocity, Mass, ParticleConstraintRelation,
22 PhysicsBody, PhysicsParticle, Position,
23 },
24 constraints::distance::solve_distance_constraint,
25 density_fields::DensityFieldBox,
26 queries::shape::ShapeOverlapQuery,
27 solvers::{
28 apply_external_forces, apply_gravity, cache_current_as_previous_state, dampening_solver,
29 integrate_velocities, recalculate_velocities,
30 },
31};
32use anput::{scheduler::GraphSchedulerPlugin, view::TypedWorldView, world::Relation};
33use serde::{Deserialize, Serialize};
34use vek::Vec3;
35
36pub type Scalar = f32;
37pub use std::f32 as scalar;
38
39pub type PhysicsAccessBundleColumns = (
40 PhysicsBody,
41 PhysicsParticle,
42 BodyMaterial,
43 Mass,
44 Position,
45 LinearVelocity,
46 AngularVelocity,
47 ExternalForces,
48 CollisionProfile,
49 DensityFieldBox,
50 ContactDetection,
51 Relation<BodyParentRelation>,
52 Relation<BodyParticleRelation>,
53 Relation<BodyDensityFieldRelation>,
54 Relation<ParticleConstraintRelation>,
55);
56
57pub type PhysicsAccessView = TypedWorldView<PhysicsAccessBundleColumns>;
58
59#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
60pub struct PhysicsSimulation {
61 pub delta_time: Scalar,
62 pub gravity: Vec3<Scalar>,
63}
64
65impl Default for PhysicsSimulation {
66 fn default() -> Self {
67 Self {
68 delta_time: 1.0 / 20.0,
69 gravity: Default::default(),
70 }
71 }
72}
73
74impl PhysicsSimulation {
75 pub fn inverse_delta_time(&self) -> Scalar {
76 if self.delta_time.abs() > Scalar::EPSILON {
77 1.0 / self.delta_time
78 } else {
79 0.0
80 }
81 }
82}
83
84pub struct PhysicsPlugin<const LOCKING: bool> {
85 simulation: PhysicsSimulation,
86 shape_overlap_query: ShapeOverlapQuery,
87 install_repulsive_collision: bool,
88 install_apply_gravity: bool,
89 install_apply_external_forces: bool,
90 install_integrate_velocities: bool,
91 install_collect_contacts: bool,
92 install_dispatch_contact_events: bool,
93 repulsive_collision_callbacks: RepulsiveCollisionCallbacks,
94 install_dampening_solver: bool,
95 install_distance_constraints_solver: bool,
96}
97
98impl<const LOCKING: bool> Default for PhysicsPlugin<LOCKING> {
99 fn default() -> Self {
100 Self {
101 simulation: Default::default(),
102 shape_overlap_query: Default::default(),
103 install_repulsive_collision: true,
104 install_apply_gravity: true,
105 install_apply_external_forces: true,
106 install_integrate_velocities: true,
107 install_collect_contacts: true,
108 install_dispatch_contact_events: true,
109 repulsive_collision_callbacks: Default::default(),
110 install_dampening_solver: true,
111 install_distance_constraints_solver: true,
112 }
113 }
114}
115
116impl<const LOCKING: bool> PhysicsPlugin<LOCKING> {
117 pub fn barebones() -> Self {
118 Self {
119 simulation: PhysicsSimulation::default(),
120 shape_overlap_query: Default::default(),
121 install_repulsive_collision: false,
122 install_apply_gravity: false,
123 install_apply_external_forces: false,
124 install_integrate_velocities: false,
125 install_collect_contacts: false,
126 install_dispatch_contact_events: false,
127 repulsive_collision_callbacks: Default::default(),
128 install_dampening_solver: false,
129 install_distance_constraints_solver: false,
130 }
131 }
132
133 pub fn simulation(mut self, simulation: PhysicsSimulation) -> Self {
134 self.simulation = simulation;
135 self
136 }
137
138 pub fn shape_overlap_query(mut self, query: ShapeOverlapQuery) -> Self {
139 self.shape_overlap_query = query;
140 self
141 }
142
143 pub fn install_repulsive_collision(mut self, install: bool) -> Self {
144 self.install_repulsive_collision = install;
145 self
146 }
147
148 pub fn install_apply_gravity(mut self, install: bool) -> Self {
149 self.install_apply_gravity = install;
150 self
151 }
152
153 pub fn install_apply_external_forces(mut self, install: bool) -> Self {
154 self.install_apply_external_forces = install;
155 self
156 }
157
158 pub fn install_integrate_velocities(mut self, install: bool) -> Self {
159 self.install_integrate_velocities = install;
160 self
161 }
162
163 pub fn install_collect_contacts(mut self, install: bool) -> Self {
164 self.install_collect_contacts = install;
165 self
166 }
167
168 pub fn repulsive_collision_callbacks(mut self, callbacks: RepulsiveCollisionCallbacks) -> Self {
169 self.repulsive_collision_callbacks = callbacks;
170 self
171 }
172
173 pub fn install_dampening_solver(mut self, install: bool) -> Self {
174 self.install_dampening_solver = install;
175 self
176 }
177
178 pub fn install_distance_constraints_solver(mut self, install: bool) -> Self {
179 self.install_distance_constraints_solver = install;
180 self
181 }
182
183 pub fn make(self) -> GraphSchedulerPlugin<LOCKING> {
184 let Self {
185 simulation,
186 shape_overlap_query,
187 install_repulsive_collision,
188 install_apply_gravity,
189 install_apply_external_forces,
190 install_integrate_velocities,
191 install_collect_contacts,
192 install_dispatch_contact_events,
193 repulsive_collision_callbacks,
194 install_dampening_solver,
195 install_distance_constraints_solver,
196 } = self;
197
198 GraphSchedulerPlugin::<LOCKING>::default()
199 .name("physics_simulation")
200 .resource(simulation)
201 .resource(ContactsCache::default())
202 .plugin_setup(|plugin| {
203 plugin
204 .name("pre_simulation")
205 .maybe_setup(|plugin| {
206 if install_apply_gravity {
207 Some(plugin.system_setup(apply_gravity::<LOCKING>, |system| {
208 system.name("apply_gravity")
209 }))
210 } else {
211 None
212 }
213 })
214 .maybe_setup(|plugin| {
215 if install_apply_external_forces {
216 Some(
217 plugin.system_setup(apply_external_forces::<LOCKING>, |system| {
218 system.name("apply_external_forces")
219 }),
220 )
221 } else {
222 None
223 }
224 })
225 .maybe_setup(|plugin| {
226 if install_integrate_velocities {
227 Some(
228 plugin.system_setup(integrate_velocities::<LOCKING>, |system| {
229 system.name("integrate_velocities")
230 }),
231 )
232 } else {
233 None
234 }
235 })
236 .plugin(
237 anput_spatial::make_plugin::<LOCKING, DensityFieldSpatialExtractor>()
238 .name("extract_spatial_info"),
239 )
240 .maybe_setup(|plugin| {
241 if install_collect_contacts {
242 Some(plugin.system_setup(collect_contacts::<LOCKING>, |system| {
243 system.name("collect_contacts").local(shape_overlap_query)
244 }))
245 } else {
246 None
247 }
248 })
249 .maybe_setup(|plugin| {
250 if install_dispatch_contact_events {
251 Some(
252 plugin.system_setup(dispatch_contact_events::<LOCKING>, |system| {
253 system.name("dispatch_contact_events")
254 }),
255 )
256 } else {
257 None
258 }
259 })
260 .maybe_setup(|plugin| {
261 if install_repulsive_collision {
262 Some(plugin.system_setup(
263 RepulsiveCollisionSolver::<LOCKING>,
264 |system| {
265 system
266 .name("RepulsiveCollisionSolver")
267 .local(repulsive_collision_callbacks)
268 },
269 ))
270 } else {
271 None
272 }
273 })
274 })
275 .plugin_setup(|plugin| {
276 plugin
277 .name("pre_solvers")
278 .maybe_setup(|plugin| {
279 if install_dampening_solver {
280 Some(plugin.system_setup(dampening_solver::<LOCKING>, |system| {
281 system.name("dampening_solver")
282 }))
283 } else {
284 None
285 }
286 })
287 .system_setup(cache_current_as_previous_state::<LOCKING>, |system| {
288 system.name("cache_current_as_previous_state")
289 })
290 })
291 .plugin_setup(|plugin| {
292 plugin.name("solvers").maybe_setup(|plugin| {
293 if install_distance_constraints_solver {
294 Some(
295 plugin.system_setup(solve_distance_constraint::<LOCKING>, |system| {
296 system.name("solve_distance_constraint")
297 }),
298 )
299 } else {
300 None
301 }
302 })
303 })
304 .plugin_setup(|plugin| {
305 plugin
306 .name("post_solvers")
307 .system_setup(recalculate_velocities::<LOCKING>, |system| {
308 system.name("recalculate_velocities")
309 })
310 })
311 .plugin_setup(|plugin| plugin.name("post_simulation"))
312 }
313}