1use crate::math::{Rot, Vect};
2use bevy::ecs::{query, system::SystemParam};
3use bevy::prelude::*;
4use rapier::prelude::Real;
5
6pub(crate) const RAPIER_CONTEXT_EXPECT_ERROR: &str =
7 "RapierContextEntityLink.0 refers to an entity missing components from RapierContextSimulation.";
8
9use crate::plugin::context::{
10 DefaultRapierContext, RapierContextColliders, RapierContextJoints, RapierContextSimulation,
11 RapierQueryPipeline, RapierRigidBodySet,
12};
13
14#[derive(SystemParam)]
18pub struct ReadRapierContext<'w, 's, T: query::QueryFilter + 'static = With<DefaultRapierContext>> {
19 pub rapier_context: Query<
21 'w,
22 's,
23 (
24 &'static RapierContextSimulation,
25 &'static RapierContextColliders,
26 &'static RapierContextJoints,
27 &'static RapierRigidBodySet,
28 ),
29 T,
30 >,
31}
32
33impl<'w, 's, T: query::QueryFilter + 'static> ReadRapierContext<'w, 's, T> {
34 pub fn single(&self) -> Result<RapierContext<'_>> {
40 let (simulation, colliders, joints, rigidbody_set) = self.rapier_context.single()?;
41 Ok(RapierContext {
42 simulation,
43 colliders,
44 joints,
45 rigidbody_set,
46 })
47 }
48}
49
50#[cfg_attr(feature = "serde-serialize", derive(Serialize))]
55#[derive(query::QueryData)]
56pub struct RapierContext<'a> {
57 pub simulation: &'a RapierContextSimulation,
59 pub colliders: &'a RapierContextColliders,
61 pub joints: &'a RapierContextJoints,
63 pub rigidbody_set: &'a RapierRigidBodySet,
65}
66
67#[derive(SystemParam)]
71pub struct WriteRapierContext<'w, 's, T: query::QueryFilter + 'static = With<DefaultRapierContext>>
72{
73 pub rapier_context: Query<
75 'w,
76 's,
77 (
78 &'static mut RapierContextSimulation,
79 &'static mut RapierContextColliders,
80 &'static mut RapierContextJoints,
81 &'static mut RapierRigidBodySet,
82 ),
83 T,
84 >,
85}
86
87impl<'w, 's, T: query::QueryFilter + 'static> WriteRapierContext<'w, 's, T> {
88 pub fn single(&self) -> Result<RapierContext<'_>> {
94 let (simulation, colliders, joints, rigidbody_set) = self.rapier_context.single()?;
95 Ok(RapierContext {
96 simulation,
97 colliders,
98 joints,
99 rigidbody_set,
100 })
101 }
102
103 pub fn single_mut(&mut self) -> Result<RapierContextMut<'_>> {
109 let (simulation, colliders, joints, rigidbody_set) = self.rapier_context.single_mut()?;
110 Ok(RapierContextMut {
111 simulation,
112 colliders,
113 joints,
114 rigidbody_set,
115 })
116 }
117}
118
119pub struct RapierContextMut<'a> {
124 pub simulation: Mut<'a, RapierContextSimulation>,
126 pub colliders: Mut<'a, RapierContextColliders>,
128 pub joints: Mut<'a, RapierContextJoints>,
130 pub rigidbody_set: Mut<'a, RapierRigidBodySet>,
132}
133
134mod simulation {
136 use crate::control::CharacterCollision;
137 use crate::control::MoveShapeOptions;
138 use crate::control::MoveShapeOutput;
139 use crate::plugin::context::SimulationToRenderTime;
140 use crate::plugin::ContactPairView;
141 use crate::plugin::TimestepMode;
142 use crate::prelude::CollisionEvent;
143 use crate::prelude::ContactForceEvent;
144 use crate::prelude::RapierQueryPipelineMut;
145 use crate::prelude::RapierRigidBodyHandle;
146 use crate::prelude::TransformInterpolation;
147 use rapier::prelude::PhysicsHooks;
148 use rapier::prelude::Shape;
149
150 use super::*;
151
152 impl RapierContext<'_> {
154 pub fn contact_pair(
156 &self,
157 collider1: Entity,
158 collider2: Entity,
159 ) -> Option<ContactPairView<'_>> {
160 self.simulation
161 .contact_pair(self.colliders, self.rigidbody_set, collider1, collider2)
162 }
163
164 pub fn contact_pairs_with(
166 &self,
167 collider: Entity,
168 ) -> impl Iterator<Item = ContactPairView<'_>> {
169 self.simulation
170 .contact_pairs_with(self.colliders, self.rigidbody_set, collider)
171 }
172
173 pub fn intersection_pair(&self, collider1: Entity, collider2: Entity) -> Option<bool> {
175 self.simulation
176 .intersection_pair(self.colliders, collider1, collider2)
177 }
178
179 pub fn intersection_pairs_with(
181 &self,
182 collider: Entity,
183 ) -> impl Iterator<Item = (Entity, Entity, bool)> + '_ {
184 self.simulation
185 .intersection_pairs_with(self.colliders, collider)
186 }
187 }
188
189 impl RapierContextMut<'_> {
191 #[expect(clippy::too_many_arguments)]
193 pub fn step_simulation(
194 &mut self,
195 gravity: Vect,
196 timestep_mode: TimestepMode,
197 events: Option<(
198 &MessageWriter<CollisionEvent>,
199 &MessageWriter<ContactForceEvent>,
200 )>,
201 hooks: &dyn PhysicsHooks,
202 time: &Time,
203 sim_to_render_time: &mut SimulationToRenderTime,
204 interpolation_query: Option<
205 &mut Query<(&RapierRigidBodyHandle, &mut TransformInterpolation)>,
206 >,
207 ) {
208 self.simulation.step_simulation(
209 &mut self.colliders,
210 &mut self.joints,
211 &mut self.rigidbody_set,
212 gravity,
213 timestep_mode,
214 events,
215 hooks,
216 time,
217 sim_to_render_time,
218 interpolation_query,
219 )
220 }
221
222 #[expect(clippy::too_many_arguments)]
224 pub fn move_shape(
225 &mut self,
226 query_pipeline_mut: &mut RapierQueryPipelineMut<'_>,
227 movement: Vect,
228 shape: &dyn Shape,
229 shape_translation: Vect,
230 shape_rotation: Rot,
231 shape_mass: Real,
232 options: &MoveShapeOptions,
233 events: impl FnMut(CharacterCollision),
234 ) -> MoveShapeOutput {
235 self.simulation.move_shape(
236 &self.colliders,
237 query_pipeline_mut,
238 movement,
239 shape,
240 shape_translation,
241 shape_rotation,
242 shape_mass,
243 options,
244 events,
245 )
246 }
247
248 pub fn contact_pair(
250 &self,
251 collider1: Entity,
252 collider2: Entity,
253 ) -> Option<ContactPairView<'_>> {
254 self.simulation
255 .contact_pair(&self.colliders, &self.rigidbody_set, collider1, collider2)
256 }
257
258 pub fn contact_pairs_with(
260 &self,
261 collider: Entity,
262 ) -> impl Iterator<Item = ContactPairView<'_>> {
263 self.simulation
264 .contact_pairs_with(&self.colliders, &self.rigidbody_set, collider)
265 }
266
267 pub fn intersection_pair(&self, collider1: Entity, collider2: Entity) -> Option<bool> {
269 self.simulation
270 .intersection_pair(&self.colliders, collider1, collider2)
271 }
272
273 pub fn intersection_pairs_with(
275 &self,
276 collider: Entity,
277 ) -> impl Iterator<Item = (Entity, Entity, bool)> + '_ {
278 self.simulation
279 .intersection_pairs_with(&self.colliders, collider)
280 }
281 }
282}
283
284mod query_pipeline {
285 use rapier::{
286 parry::query::{DefaultQueryDispatcher, ShapeCastOptions},
287 prelude::Shape,
288 };
289
290 use crate::prelude::{PointProjection, QueryFilter, RayIntersection, ShapeCastHit};
291
292 use super::*;
293
294 impl RapierContext<'_> {
295 pub fn with_query_pipeline<'a, T>(
297 &'a self,
298 filter: QueryFilter<'a>,
299 scoped_fn: impl FnOnce(RapierQueryPipeline<'_>) -> T,
300 ) -> T {
301 crate::prelude::RapierQueryPipeline::new_scoped(
302 &self.simulation.broad_phase,
303 self.colliders,
304 self.rigidbody_set,
305 &filter,
306 &DefaultQueryDispatcher,
307 scoped_fn,
308 )
309 }
310 pub fn cast_ray(
312 &self,
313 ray_origin: Vect,
314 ray_dir: Vect,
315 max_toi: Real,
316 solid: bool,
317 filter: QueryFilter,
318 ) -> Option<(Entity, Real)> {
319 self.with_query_pipeline(filter, |query_pipeline| {
320 query_pipeline.cast_ray(ray_origin, ray_dir, max_toi, solid)
321 })
322 }
323
324 pub fn cast_ray_and_get_normal(
326 &self,
327 ray_origin: Vect,
328 ray_dir: Vect,
329 max_toi: Real,
330 solid: bool,
331 filter: QueryFilter,
332 ) -> Option<(Entity, RayIntersection)> {
333 self.with_query_pipeline(filter, |query_pipeline| {
334 query_pipeline.cast_ray_and_get_normal(ray_origin, ray_dir, max_toi, solid)
335 })
336 }
337
338 pub fn intersect_point(
342 &self,
343 point: Vect,
344 filter: QueryFilter,
345 mut callback: impl FnMut(Entity) -> bool,
346 ) {
347 self.with_query_pipeline(filter, |query_pipeline| {
348 for e in query_pipeline.intersect_point(point) {
349 if !callback(e) {
350 break;
351 }
352 }
353 });
354 }
355
356 pub fn intersect_ray(
360 &self,
361 ray_origin: Vect,
362 ray_dir: Vect,
363 max_toi: Real,
364 solid: bool,
365 filter: QueryFilter,
366 mut callback: impl FnMut(Entity, RayIntersection) -> bool,
367 ) {
368 self.with_query_pipeline(filter, |query_pipeline| {
369 for (e, intersection) in
370 query_pipeline.intersect_ray(ray_origin, ray_dir, max_toi, solid)
371 {
372 if !callback(e, intersection) {
373 break;
374 }
375 }
376 });
377 }
378
379 pub fn intersect_shape(
381 &self,
382 shape_pos: Vect,
383 shape_rot: Rot,
384 shape: &dyn Shape,
385 filter: QueryFilter,
386 mut callback: impl FnMut(Entity) -> bool,
387 ) {
388 self.with_query_pipeline(filter, |query_pipeline| {
389 for e in query_pipeline.intersect_shape(shape_pos, shape_rot, shape) {
390 if !callback(e) {
391 break;
392 }
393 }
394 });
395 }
396
397 pub fn intersect_aabb_conservative(
399 &self,
400 #[cfg(feature = "dim2")] aabb: bevy::math::bounding::Aabb2d,
401 #[cfg(feature = "dim3")] aabb: bevy::math::bounding::Aabb3d,
402 filter: QueryFilter,
403 mut callback: impl FnMut(Entity) -> bool,
404 ) {
405 self.with_query_pipeline(filter, |query_pipeline| {
406 for e in query_pipeline.intersect_aabb_conservative(aabb) {
407 if !callback(e) {
408 break;
409 }
410 }
411 });
412 }
413
414 pub fn cast_shape(
416 &self,
417 shape_pos: Vect,
418 shape_rot: Rot,
419 shape_vel: Vect,
420 shape: &dyn Shape,
421 options: ShapeCastOptions,
422 filter: QueryFilter,
423 ) -> Option<(Entity, ShapeCastHit)> {
424 self.with_query_pipeline(filter, |query_pipeline| {
425 query_pipeline.cast_shape(shape_pos, shape_rot, shape_vel, shape, options)
426 })
427 }
428
429 pub fn project_point(
431 &self,
432 point: Vect,
433 max_dist: f32,
434 solid: bool,
435 filter: QueryFilter,
436 ) -> Option<(Entity, PointProjection)> {
437 self.with_query_pipeline(filter, |query_pipeline| {
438 query_pipeline.project_point(point, max_dist, solid)
439 })
440 }
441 }
442
443 impl RapierContextMut<'_> {
445 pub fn cast_ray(
447 &self,
448 ray_origin: Vect,
449 ray_dir: Vect,
450 max_toi: Real,
451 solid: bool,
452 filter: QueryFilter,
453 ) -> Option<(Entity, Real)> {
454 self.with_query_pipeline(filter, |query_pipeline| {
455 query_pipeline.cast_ray(ray_origin, ray_dir, max_toi, solid)
456 })
457 }
458
459 pub fn cast_ray_and_get_normal(
461 &self,
462 ray_origin: Vect,
463 ray_dir: Vect,
464 max_toi: Real,
465 solid: bool,
466 filter: QueryFilter,
467 ) -> Option<(Entity, RayIntersection)> {
468 self.with_query_pipeline(filter, |query_pipeline| {
469 query_pipeline.cast_ray_and_get_normal(ray_origin, ray_dir, max_toi, solid)
470 })
471 }
472
473 pub fn intersect_point(
477 &self,
478 point: Vect,
479 filter: QueryFilter,
480 mut callback: impl FnMut(Entity) -> bool,
481 ) {
482 self.with_query_pipeline(filter, |query_pipeline| {
483 for e in query_pipeline.intersect_point(point) {
484 if !callback(e) {
485 break;
486 }
487 }
488 });
489 }
490
491 pub fn intersect_ray(
495 &self,
496 ray_origin: Vect,
497 ray_dir: Vect,
498 max_toi: Real,
499 solid: bool,
500 filter: QueryFilter,
501 mut callback: impl FnMut(Entity, RayIntersection) -> bool,
502 ) {
503 self.with_query_pipeline(filter, |query_pipeline| {
504 for (e, intersection) in
505 query_pipeline.intersect_ray(ray_origin, ray_dir, max_toi, solid)
506 {
507 if !callback(e, intersection) {
508 break;
509 }
510 }
511 });
512 }
513
514 pub fn intersect_shape(
516 &self,
517 shape_pos: Vect,
518 shape_rot: Rot,
519 shape: &dyn Shape,
520 filter: QueryFilter,
521 mut callback: impl FnMut(Entity) -> bool,
522 ) {
523 self.with_query_pipeline(filter, |query_pipeline| {
524 for e in query_pipeline.intersect_shape(shape_pos, shape_rot, shape) {
525 if !callback(e) {
526 break;
527 }
528 }
529 });
530 }
531
532 pub fn intersect_aabb_conservative(
534 &self,
535 #[cfg(feature = "dim2")] aabb: bevy::math::bounding::Aabb2d,
536 #[cfg(feature = "dim3")] aabb: bevy::math::bounding::Aabb3d,
537 filter: QueryFilter,
538 mut callback: impl FnMut(Entity) -> bool,
539 ) {
540 self.with_query_pipeline(filter, |query_pipeline| {
541 for e in query_pipeline.intersect_aabb_conservative(aabb) {
542 if !callback(e) {
543 break;
544 }
545 }
546 });
547 }
548
549 pub fn cast_shape(
551 &self,
552 shape_pos: Vect,
553 shape_rot: Rot,
554 shape_vel: Vect,
555 shape: &dyn Shape,
556 options: ShapeCastOptions,
557 filter: QueryFilter,
558 ) -> Option<(Entity, ShapeCastHit)> {
559 self.with_query_pipeline(filter, |query_pipeline| {
560 query_pipeline.cast_shape(shape_pos, shape_rot, shape_vel, shape, options)
561 })
562 }
563
564 pub fn project_point(
566 &self,
567 point: Vect,
568 max_dist: f32,
569 solid: bool,
570 filter: QueryFilter,
571 ) -> Option<(Entity, PointProjection)> {
572 self.with_query_pipeline(filter, |query_pipeline| {
573 query_pipeline.project_point(point, max_dist, solid)
574 })
575 }
576 }
577
578 impl RapierContextMut<'_> {
579 pub fn with_query_pipeline<'a, T>(
581 &'a self,
582 filter: QueryFilter<'a>,
583 scoped_fn: impl FnOnce(RapierQueryPipeline<'_>) -> T,
584 ) -> T {
585 crate::prelude::RapierQueryPipeline::new_scoped(
586 &self.simulation.broad_phase,
587 &self.colliders,
588 &self.rigidbody_set,
589 &filter,
590 &DefaultQueryDispatcher,
591 scoped_fn,
592 )
593 }
594 }
595}
596
597mod rigidbody_set {
598 use std::collections::HashMap;
599
600 use super::*;
601 pub use rapier::prelude::RigidBodyHandle;
602
603 impl RapierContext<'_> {
604 pub fn entity2body(&self) -> &HashMap<Entity, RigidBodyHandle> {
606 self.rigidbody_set.entity2body()
607 }
608
609 pub fn rigid_body_entity(&self, handle: RigidBodyHandle) -> Option<Entity> {
611 self.rigidbody_set.rigid_body_entity(handle)
612 }
613
614 pub fn impulse_revolute_joint_angle(&self, entity: Entity) -> Option<f32> {
616 self.rigidbody_set
617 .impulse_revolute_joint_angle(self.joints, entity)
618 }
619 }
620
621 impl RapierContextMut<'_> {
622 pub fn propagate_modified_body_positions_to_colliders(&mut self) {
624 self.rigidbody_set
625 .propagate_modified_body_positions_to_colliders(&mut self.colliders)
626 }
627
628 pub fn entity2body(&self) -> &HashMap<Entity, RigidBodyHandle> {
630 self.rigidbody_set.entity2body()
631 }
632
633 pub fn rigid_body_entity(&self, handle: RigidBodyHandle) -> Option<Entity> {
635 self.rigidbody_set.rigid_body_entity(handle)
636 }
637
638 pub fn impulse_revolute_joint_angle(&self, entity: Entity) -> Option<f32> {
640 self.rigidbody_set
641 .impulse_revolute_joint_angle(&self.joints, entity)
642 }
643 }
644}