1use super::events::{CollisionEvent, CollisionEventCollector, ContactForceEvent};
2#[cfg(feature = "debug_render")]
3use crate::ecs::lines::components::Line;
4use nightshade_ecs::Entity;
5use rapier3d::prelude::*;
6use std::collections::HashMap;
7
8#[derive(Debug, Clone, Copy, PartialEq)]
9pub enum JointType {
10 Fixed,
11 Revolute,
12 Prismatic,
13 Spherical,
14 Rope,
15 Spring,
16}
17
18pub struct JointInfo {
19 pub parent_entity: Entity,
20 pub child_entity: Entity,
21 pub joint_type: JointType,
22 pub collisions_enabled: bool,
23 pub spring_rest_length: Option<f32>,
24 pub spring_stiffness: Option<f32>,
25 pub spring_damping: Option<f32>,
26}
27
28pub struct PendingJoint {
29 pub parent_entity: Entity,
30 pub child_entity: Entity,
31 pub(crate) joint: GenericJoint,
32 pub joint_type: JointType,
33 pub collisions_enabled: bool,
34 pub spring_rest_length: Option<f32>,
35 pub spring_stiffness: Option<f32>,
36 pub spring_damping: Option<f32>,
37}
38
39pub struct PhysicsWorld {
40 pub gravity: nalgebra_glm::Vec3,
41 pub(crate) integration_parameters: IntegrationParameters,
42 pub(crate) physics_pipeline: PhysicsPipeline,
43 pub(crate) island_manager: IslandManager,
44 pub(crate) broad_phase: BroadPhaseBvh,
45 pub(crate) narrow_phase: NarrowPhase,
46 pub(crate) impulse_joint_set: ImpulseJointSet,
47 pub(crate) multibody_joint_set: MultibodyJointSet,
48 pub(crate) ccd_solver: CCDSolver,
49 pub(crate) physics_hooks: (),
50 pub(crate) event_collector: CollisionEventCollector,
51 pub collision_events: Vec<CollisionEvent>,
52 pub contact_force_events: Vec<ContactForceEvent>,
53 pub(crate) rigid_body_set: RigidBodySet,
54 pub(crate) collider_set: ColliderSet,
55 pub fixed_timestep: f32,
56 pub accumulator: f32,
57 pub max_substeps: u32,
58 pub interpolation_alpha: f32,
59 pub debug_draw: bool,
60 pub debug_entity: Option<nightshade_ecs::Entity>,
61 pub debug_version: u64,
62 #[cfg(feature = "debug_render")]
63 pub(crate) debug_static_lines_cache: HashMap<ColliderHandle, Vec<Line>>,
64 pub debug_last_version: u64,
65 pub pending_joints: Vec<PendingJoint>,
66 pub(crate) joint_registry: HashMap<ImpulseJointHandle, JointInfo>,
67 pub(crate) handle_to_entity: HashMap<RigidBodyHandle, Entity>,
68 pub(crate) entity_to_handle: HashMap<Entity, RigidBodyHandle>,
69 pub enabled: bool,
70 pub component_world: Option<usize>,
73}
74
75impl Default for PhysicsWorld {
76 fn default() -> Self {
77 let integration_parameters = IntegrationParameters {
78 dt: 1.0 / 60.0,
79 ..Default::default()
80 };
81
82 Self {
83 gravity: nalgebra_glm::vec3(0.0, -9.81, 0.0),
84 integration_parameters,
85 physics_pipeline: PhysicsPipeline::new(),
86 island_manager: IslandManager::new(),
87 broad_phase: BroadPhaseBvh::new(),
88 narrow_phase: NarrowPhase::new(),
89 impulse_joint_set: ImpulseJointSet::new(),
90 multibody_joint_set: MultibodyJointSet::new(),
91 ccd_solver: CCDSolver::new(),
92 physics_hooks: (),
93 event_collector: CollisionEventCollector::new(),
94 collision_events: Vec::new(),
95 contact_force_events: Vec::new(),
96 rigid_body_set: RigidBodySet::new(),
97 collider_set: ColliderSet::new(),
98 fixed_timestep: 1.0 / 60.0,
99 accumulator: 0.0,
100 max_substeps: 4,
101 interpolation_alpha: 0.0,
102 debug_draw: false,
103 debug_entity: None,
104 debug_version: 0,
105 #[cfg(feature = "debug_render")]
106 debug_static_lines_cache: HashMap::new(),
107 debug_last_version: u64::MAX,
108 pending_joints: Vec::new(),
109 joint_registry: HashMap::new(),
110 handle_to_entity: HashMap::new(),
111 entity_to_handle: HashMap::new(),
112 enabled: false,
113 component_world: None,
114 }
115 }
116}
117
118pub fn physics_world_step(physics: &mut PhysicsWorld) {
119 let _span =
120 tracing::info_span!("physics_step", colliders = physics.collider_set.len()).entered();
121
122 physics.physics_pipeline.step(
123 &physics.gravity,
124 &physics.integration_parameters,
125 &mut physics.island_manager,
126 &mut physics.broad_phase,
127 &mut physics.narrow_phase,
128 &mut physics.rigid_body_set,
129 &mut physics.collider_set,
130 &mut physics.impulse_joint_set,
131 &mut physics.multibody_joint_set,
132 &mut physics.ccd_solver,
133 &physics.physics_hooks,
134 &physics.event_collector,
135 );
136}
137
138pub fn physics_world_collision_events(physics: &PhysicsWorld) -> &[CollisionEvent] {
139 &physics.collision_events
140}
141
142pub fn physics_world_contact_force_events(physics: &PhysicsWorld) -> &[ContactForceEvent] {
143 &physics.contact_force_events
144}
145
146pub fn physics_world_set_collision_events(physics: &mut PhysicsWorld, events: Vec<CollisionEvent>) {
147 physics.collision_events = events;
148}
149
150pub fn physics_world_set_contact_force_events(
151 physics: &mut PhysicsWorld,
152 events: Vec<ContactForceEvent>,
153) {
154 physics.contact_force_events = events;
155}
156
157pub fn physics_world_clear_events(physics: &mut PhysicsWorld) {
158 physics.collision_events.clear();
159 physics.contact_force_events.clear();
160}
161
162pub(crate) fn physics_world_query_pipeline(physics: &PhysicsWorld) -> QueryPipeline<'_> {
163 physics.broad_phase.as_query_pipeline(
164 physics.narrow_phase.query_dispatcher(),
165 &physics.rigid_body_set,
166 &physics.collider_set,
167 QueryFilter::default(),
168 )
169}
170
171pub fn physics_world_get_interpolation_alpha(physics: &PhysicsWorld) -> f32 {
172 physics.interpolation_alpha
173}
174
175pub(crate) fn physics_world_add_rigid_body(
176 physics: &mut PhysicsWorld,
177 rigid_body: RigidBody,
178) -> RigidBodyHandle {
179 physics.rigid_body_set.insert(rigid_body)
180}
181
182pub(crate) fn physics_world_add_collider(
183 physics: &mut PhysicsWorld,
184 collider: Collider,
185 parent_handle: RigidBodyHandle,
186) -> ColliderHandle {
187 physics.debug_version = physics.debug_version.wrapping_add(1);
188 physics
189 .collider_set
190 .insert_with_parent(collider, parent_handle, &mut physics.rigid_body_set)
191}
192
193pub(crate) fn physics_world_remove_rigid_body(physics: &mut PhysicsWorld, handle: RigidBodyHandle) {
194 physics.debug_version = physics.debug_version.wrapping_add(1);
195 if let Some(entity) = physics.handle_to_entity.remove(&handle) {
196 physics.entity_to_handle.remove(&entity);
197 }
198 physics.rigid_body_set.remove(
199 handle,
200 &mut physics.island_manager,
201 &mut physics.collider_set,
202 &mut physics.impulse_joint_set,
203 &mut physics.multibody_joint_set,
204 true,
205 );
206}
207
208pub(crate) fn physics_world_get_rigid_body_mut(
209 physics: &mut PhysicsWorld,
210 handle: RigidBodyHandle,
211) -> Option<&mut RigidBody> {
212 physics.rigid_body_set.get_mut(handle)
213}
214
215pub fn physics_world_cast_ray(
216 physics: &PhysicsWorld,
217 origin: nalgebra_glm::Vec3,
218 direction: nalgebra_glm::Vec3,
219 max_distance: f32,
220 exclude: Option<Entity>,
221) -> Option<super::types::RaycastHit> {
222 let mut filter = QueryFilter::default();
223 if let Some(entity) = exclude
224 && let Some(handle) = physics.entity_to_handle.get(&entity)
225 {
226 filter = filter.exclude_rigid_body(*handle);
227 }
228
229 let pipeline = physics.broad_phase.as_query_pipeline(
230 physics.narrow_phase.query_dispatcher(),
231 &physics.rigid_body_set,
232 &physics.collider_set,
233 filter,
234 );
235
236 let ray = Ray::new(
237 point![origin.x, origin.y, origin.z],
238 vector![direction.x, direction.y, direction.z],
239 );
240
241 let (collider_handle, intersection) =
242 pipeline.cast_ray_and_get_normal(&ray, max_distance, true)?;
243
244 let collider = physics.collider_set.get(collider_handle)?;
245 let parent_handle = collider.parent()?;
246 let entity = *physics.handle_to_entity.get(&parent_handle)?;
247
248 let point_world = ray.point_at(intersection.time_of_impact);
249 Some(super::types::RaycastHit {
250 entity,
251 distance: intersection.time_of_impact,
252 point: nalgebra_glm::vec3(point_world.x, point_world.y, point_world.z),
253 normal: nalgebra_glm::vec3(
254 intersection.normal.x,
255 intersection.normal.y,
256 intersection.normal.z,
257 ),
258 })
259}
260
261pub fn physics_world_overlap_sphere(
262 physics: &PhysicsWorld,
263 center: nalgebra_glm::Vec3,
264 radius: f32,
265 exclude: Option<Entity>,
266) -> Vec<Entity> {
267 let mut filter = QueryFilter::default();
268 if let Some(entity) = exclude
269 && let Some(handle) = physics.entity_to_handle.get(&entity)
270 {
271 filter = filter.exclude_rigid_body(*handle);
272 }
273
274 let pipeline = physics.broad_phase.as_query_pipeline(
275 physics.narrow_phase.query_dispatcher(),
276 &physics.rigid_body_set,
277 &physics.collider_set,
278 filter,
279 );
280
281 let shape = Ball::new(radius);
282 let pose = rapier3d::na::Isometry3::translation(center.x, center.y, center.z);
283
284 let mut hits = Vec::new();
285 for (collider_handle, _collider) in pipeline.intersect_shape(pose, &shape) {
286 if let Some(collider) = physics.collider_set.get(collider_handle)
287 && let Some(parent_handle) = collider.parent()
288 && let Some(&entity) = physics.handle_to_entity.get(&parent_handle)
289 {
290 hits.push(entity);
291 }
292 }
293 hits
294}
295
296pub fn physics_world_capsule_contacts(
299 physics: &PhysicsWorld,
300 center: nalgebra_glm::Vec3,
301 half_height: f32,
302 radius: f32,
303 exclude: Option<Entity>,
304) -> Vec<super::types::ShapeContact> {
305 let mut filter = QueryFilter::default();
306 if let Some(entity) = exclude
307 && let Some(handle) = physics.entity_to_handle.get(&entity)
308 {
309 filter = filter.exclude_rigid_body(*handle);
310 }
311
312 let pipeline = physics.broad_phase.as_query_pipeline(
313 physics.narrow_phase.query_dispatcher(),
314 &physics.rigid_body_set,
315 &physics.collider_set,
316 filter,
317 );
318
319 let shape = Capsule::new_y(half_height, radius);
320 let pose = rapier3d::na::Isometry3::translation(center.x, center.y, center.z);
321
322 let mut contacts = Vec::new();
323 for (_collider_handle, collider) in pipeline.intersect_shape(pose, &shape) {
324 let Ok(Some(contact)) = rapier3d::parry::query::contact(
325 &pose,
326 &shape,
327 collider.position(),
328 collider.shape(),
329 0.0,
330 ) else {
331 continue;
332 };
333 if contact.dist >= 0.0 {
334 continue;
335 }
336 let Some(parent_handle) = collider.parent() else {
337 continue;
338 };
339 let Some(&entity) = physics.handle_to_entity.get(&parent_handle) else {
340 continue;
341 };
342 contacts.push(super::types::ShapeContact {
343 entity,
344 point: nalgebra_glm::vec3(contact.point1.x, contact.point1.y, contact.point1.z),
345 normal: nalgebra_glm::vec3(-contact.normal1.x, -contact.normal1.y, -contact.normal1.z),
346 depth: -contact.dist,
347 });
348 }
349 contacts
350}
351
352pub fn physics_world_apply_impulse(
353 physics: &mut PhysicsWorld,
354 entity: Entity,
355 impulse: nalgebra_glm::Vec3,
356) {
357 let Some(handle) = physics.entity_to_handle.get(&entity).copied() else {
358 return;
359 };
360 let Some(rb) = physics.rigid_body_set.get_mut(handle) else {
361 return;
362 };
363 rb.apply_impulse(vector![impulse.x, impulse.y, impulse.z], true);
364}
365
366pub fn physics_world_apply_impulse_at_point(
367 physics: &mut PhysicsWorld,
368 entity: Entity,
369 impulse: nalgebra_glm::Vec3,
370 point: nalgebra_glm::Vec3,
371) {
372 let Some(handle) = physics.entity_to_handle.get(&entity).copied() else {
373 return;
374 };
375 let Some(rb) = physics.rigid_body_set.get_mut(handle) else {
376 return;
377 };
378 rb.apply_impulse_at_point(
379 vector![impulse.x, impulse.y, impulse.z],
380 point![point.x, point.y, point.z],
381 true,
382 );
383}
384
385pub fn physics_world_apply_force(
386 physics: &mut PhysicsWorld,
387 entity: Entity,
388 force: nalgebra_glm::Vec3,
389) {
390 let Some(handle) = physics.entity_to_handle.get(&entity).copied() else {
391 return;
392 };
393 let Some(rb) = physics.rigid_body_set.get_mut(handle) else {
394 return;
395 };
396 rb.add_force(vector![force.x, force.y, force.z], true);
397}
398
399pub fn physics_world_apply_torque(
400 physics: &mut PhysicsWorld,
401 entity: Entity,
402 torque: nalgebra_glm::Vec3,
403) {
404 let Some(handle) = physics.entity_to_handle.get(&entity).copied() else {
405 return;
406 };
407 let Some(rb) = physics.rigid_body_set.get_mut(handle) else {
408 return;
409 };
410 rb.add_torque(vector![torque.x, torque.y, torque.z], true);
411}
412
413pub fn physics_world_linear_velocity(
414 physics: &PhysicsWorld,
415 entity: Entity,
416) -> Option<nalgebra_glm::Vec3> {
417 let handle = physics.entity_to_handle.get(&entity)?;
418 let rb = physics.rigid_body_set.get(*handle)?;
419 let velocity = rb.linvel();
420 Some(nalgebra_glm::vec3(velocity.x, velocity.y, velocity.z))
421}
422
423pub fn physics_world_set_linear_velocity(
424 physics: &mut PhysicsWorld,
425 entity: Entity,
426 velocity: nalgebra_glm::Vec3,
427) {
428 let Some(handle) = physics.entity_to_handle.get(&entity).copied() else {
429 return;
430 };
431 let Some(rb) = physics.rigid_body_set.get_mut(handle) else {
432 return;
433 };
434 rb.set_linvel(vector![velocity.x, velocity.y, velocity.z], true);
435}
436
437pub fn physics_world_wake_all(physics: &mut PhysicsWorld) {
442 for (_, rigid_body) in physics.rigid_body_set.iter_mut() {
443 rigid_body.wake_up(true);
444 }
445}
446
447pub fn physics_world_angular_velocity(
448 physics: &PhysicsWorld,
449 entity: Entity,
450) -> Option<nalgebra_glm::Vec3> {
451 let handle = physics.entity_to_handle.get(&entity)?;
452 let rb = physics.rigid_body_set.get(*handle)?;
453 let velocity = rb.angvel();
454 Some(nalgebra_glm::vec3(velocity.x, velocity.y, velocity.z))
455}
456
457pub fn physics_world_set_angular_velocity(
458 physics: &mut PhysicsWorld,
459 entity: Entity,
460 velocity: nalgebra_glm::Vec3,
461) {
462 let Some(handle) = physics.entity_to_handle.get(&entity).copied() else {
463 return;
464 };
465 let Some(rb) = physics.rigid_body_set.get_mut(handle) else {
466 return;
467 };
468 rb.set_angvel(vector![velocity.x, velocity.y, velocity.z], true);
469}
470
471pub fn physics_world_set_linear_damping(physics: &mut PhysicsWorld, entity: Entity, damping: f32) {
472 let Some(handle) = physics.entity_to_handle.get(&entity).copied() else {
473 return;
474 };
475 if let Some(rb) = physics.rigid_body_set.get_mut(handle) {
476 rb.set_linear_damping(damping);
477 }
478}
479
480pub fn physics_world_set_angular_damping(physics: &mut PhysicsWorld, entity: Entity, damping: f32) {
481 let Some(handle) = physics.entity_to_handle.get(&entity).copied() else {
482 return;
483 };
484 if let Some(rb) = physics.rigid_body_set.get_mut(handle) {
485 rb.set_angular_damping(damping);
486 }
487}
488
489pub fn physics_world_set_gravity_scale(physics: &mut PhysicsWorld, entity: Entity, scale: f32) {
490 let Some(handle) = physics.entity_to_handle.get(&entity).copied() else {
491 return;
492 };
493 if let Some(rb) = physics.rigid_body_set.get_mut(handle) {
494 rb.set_gravity_scale(scale, true);
495 }
496}
497
498pub fn physics_world_set_additional_mass(physics: &mut PhysicsWorld, entity: Entity, mass: f32) {
499 let Some(handle) = physics.entity_to_handle.get(&entity).copied() else {
500 return;
501 };
502 if let Some(rb) = physics.rigid_body_set.get_mut(handle) {
503 rb.set_additional_mass(mass, true);
504 }
505}
506
507pub fn physics_world_set_friction(physics: &mut PhysicsWorld, entity: Entity, friction: f32) {
508 let Some(handle) = physics.entity_to_handle.get(&entity).copied() else {
509 return;
510 };
511 let collider_handles: Vec<ColliderHandle> = physics
512 .rigid_body_set
513 .get(handle)
514 .map(|rb| rb.colliders().to_vec())
515 .unwrap_or_default();
516 for collider_handle in collider_handles {
517 if let Some(collider) = physics.collider_set.get_mut(collider_handle) {
518 collider.set_friction(friction);
519 }
520 }
521}
522
523pub fn physics_world_set_restitution(physics: &mut PhysicsWorld, entity: Entity, restitution: f32) {
524 let Some(handle) = physics.entity_to_handle.get(&entity).copied() else {
525 return;
526 };
527 let collider_handles: Vec<ColliderHandle> = physics
528 .rigid_body_set
529 .get(handle)
530 .map(|rb| rb.colliders().to_vec())
531 .unwrap_or_default();
532 for collider_handle in collider_handles {
533 if let Some(collider) = physics.collider_set.get_mut(collider_handle) {
534 collider.set_restitution(restitution);
535 }
536 }
537}
538
539pub fn physics_world_body_translation(
540 physics: &PhysicsWorld,
541 entity: Entity,
542) -> Option<nalgebra_glm::Vec3> {
543 let handle = physics.entity_to_handle.get(&entity)?;
544 let rb = physics.rigid_body_set.get(*handle)?;
545 let translation = rb.translation();
546 Some(nalgebra_glm::vec3(
547 translation.x,
548 translation.y,
549 translation.z,
550 ))
551}
552
553pub fn physics_world_body_rotation(
554 physics: &PhysicsWorld,
555 entity: Entity,
556) -> Option<nalgebra_glm::Quat> {
557 let handle = physics.entity_to_handle.get(&entity)?;
558 let rb = physics.rigid_body_set.get(*handle)?;
559 let rotation = rb.rotation();
560 Some(nalgebra_glm::quat(
561 rotation.i, rotation.j, rotation.k, rotation.w,
562 ))
563}