1use super::collider::GeometrySource;
24use crate::{
25 core::{
26 algebra::{
27 Isometry2, Isometry3, Matrix4, Point2, Rotation3, Translation2, Translation3,
28 UnitComplex, UnitQuaternion, UnitVector2, Vector2, Vector3,
29 },
30 arrayvec::ArrayVec,
31 instant,
32 log::{Log, MessageKind},
33 math::Matrix4Ext,
34 parking_lot::Mutex,
35 pool::Handle,
36 reflect::prelude::*,
37 variable::{InheritableVariable, VariableFlags},
38 visitor::prelude::*,
39 BiDirHashMap, ImmutableString, SafeLock,
40 },
41 graph::{SceneGraph, SceneGraphNode},
42 scene::{
43 self,
44 collider::{self},
45 debug::SceneDrawingContext,
46 dim2::{
47 self,
48 collider::{ColliderShape, TileMapShape},
49 joint::{JointLocalFrames, JointMotorParams, JointParams},
50 rigidbody::ApplyAction,
51 },
52 graph::{
53 isometric_global_transform,
54 physics::{FeatureId, IntegrationParameters, PhysicsPerformanceStatistics},
55 Graph, NodePool,
56 },
57 node::{Node, NodeTrait},
58 tilemap::TileMap,
59 },
60};
61pub use rapier2d::geometry::shape::*;
62use rapier2d::math::{Pose2, Vec2};
63use rapier2d::parry::query::DefaultQueryDispatcher;
64use rapier2d::{
65 dynamics::{
66 CCDSolver, GenericJoint, GenericJointBuilder, ImpulseJointHandle, ImpulseJointSet,
67 IslandManager, JointAxesMask, JointAxis, MultibodyJointHandle, MultibodyJointSet,
68 RigidBody, RigidBodyActivation, RigidBodyBuilder, RigidBodyHandle, RigidBodySet,
69 RigidBodyType,
70 },
71 geometry::{
72 Collider, ColliderBuilder, ColliderHandle, ColliderSet, Cuboid, DefaultBroadPhase,
73 InteractionGroups, NarrowPhase, Ray, SharedShape,
74 },
75 parry::query::ShapeCastOptions,
76 pipeline::{DebugRenderPipeline, EventHandler, PhysicsPipeline},
77};
78use std::{
79 cmp::Ordering,
80 fmt::{Debug, Formatter},
81 hash::Hash,
82 sync::Arc,
83};
84
85pub trait QueryResultsStorage {
89 fn push(&mut self, intersection: Intersection) -> bool;
92
93 fn clear(&mut self);
95
96 fn sort_intersections_by<C: FnMut(&Intersection, &Intersection) -> Ordering>(&mut self, cmp: C);
98}
99
100impl QueryResultsStorage for Vec<Intersection> {
101 fn push(&mut self, intersection: Intersection) -> bool {
102 self.push(intersection);
103 true
104 }
105
106 fn clear(&mut self) {
107 self.clear()
108 }
109
110 fn sort_intersections_by<C>(&mut self, cmp: C)
111 where
112 C: FnMut(&Intersection, &Intersection) -> Ordering,
113 {
114 self.sort_by(cmp);
115 }
116}
117
118impl<const CAP: usize> QueryResultsStorage for ArrayVec<Intersection, CAP> {
119 fn push(&mut self, intersection: Intersection) -> bool {
120 self.try_push(intersection).is_ok()
121 }
122
123 fn clear(&mut self) {
124 self.clear()
125 }
126
127 fn sort_intersections_by<C>(&mut self, cmp: C)
128 where
129 C: FnMut(&Intersection, &Intersection) -> Ordering,
130 {
131 self.sort_by(cmp);
132 }
133}
134
135#[derive(Debug, Clone, PartialEq)]
137pub struct Intersection {
138 pub collider: Handle<dim2::collider::Collider>,
140
141 pub normal: Vector2<f32>,
143
144 pub position: Point2<f32>,
146
147 pub feature: FeatureId,
157
158 pub toi: f32,
160}
161
162pub struct RayCastOptions {
164 pub ray_origin: Point2<f32>,
166
167 pub ray_direction: Vector2<f32>,
169
170 pub max_len: f32,
172
173 pub groups: collider::InteractionGroups,
175
176 pub sort_results: bool,
178}
179
180#[derive(Debug, Clone, PartialEq)]
182pub struct ContactData {
183 pub local_p1: Vector2<f32>,
185 pub local_p2: Vector2<f32>,
187 pub dist: f32,
189 pub impulse: f32,
192 pub tangent_impulse: f32,
195}
196
197#[derive(Debug, Clone, PartialEq)]
199pub struct ContactManifold {
200 pub points: Vec<ContactData>,
202 pub local_n1: Vector2<f32>,
204 pub local_n2: Vector2<f32>,
206 pub rigid_body1: Handle<dim2::rigidbody::RigidBody>,
208 pub rigid_body2: Handle<dim2::rigidbody::RigidBody>,
210 pub normal: Vector2<f32>,
212}
213
214#[derive(Debug, Clone, PartialEq)]
216pub struct ContactPair {
217 pub collider1: Handle<dim2::collider::Collider>,
219 pub collider2: Handle<dim2::collider::Collider>,
221 pub manifolds: Vec<ContactManifold>,
224 pub has_any_active_contact: bool,
227}
228
229impl ContactPair {
230 #[inline]
236 pub fn other(
237 &self,
238 subject: Handle<dim2::collider::Collider>,
239 ) -> Handle<dim2::collider::Collider> {
240 if subject == self.collider1 {
241 self.collider2
242 } else {
243 self.collider1
244 }
245 }
246
247 fn from_native(c: &rapier2d::geometry::ContactPair, physics: &PhysicsWorld) -> Option<Self> {
248 Some(ContactPair {
249 collider1: Handle::decode_from_u128(physics.colliders.get(c.collider1)?.user_data),
250 collider2: Handle::decode_from_u128(physics.colliders.get(c.collider2)?.user_data),
251 manifolds: c
252 .manifolds
253 .iter()
254 .filter_map(|m| {
255 Some(ContactManifold {
256 points: m
257 .points
258 .iter()
259 .map(|p| ContactData {
260 local_p1: p.local_p1.into(),
261 local_p2: p.local_p2.into(),
262 dist: p.dist,
263 impulse: p.data.impulse,
264 tangent_impulse: p.data.tangent_impulse.x,
265 })
266 .collect(),
267 local_n1: m.local_n1.into(),
268 local_n2: m.local_n2.into(),
269 rigid_body1: m.data.rigid_body1.and_then(|h| {
270 physics
271 .bodies
272 .get(h)
273 .map(|b| Handle::decode_from_u128(b.user_data))
274 })?,
275 rigid_body2: m.data.rigid_body2.and_then(|h| {
276 physics
277 .bodies
278 .get(h)
279 .map(|b| Handle::decode_from_u128(b.user_data))
280 })?,
281 normal: m.data.normal.into(),
282 })
283 })
284 .collect(),
285 has_any_active_contact: c.has_any_active_contact(),
286 })
287 }
288}
289
290#[derive(Debug, Clone, PartialEq)]
292pub struct IntersectionPair {
293 pub collider1: Handle<dim2::collider::Collider>,
295 pub collider2: Handle<dim2::collider::Collider>,
297 pub has_any_active_contact: bool,
300}
301
302impl IntersectionPair {
303 #[inline]
309 pub fn other(
310 &self,
311 subject: Handle<dim2::collider::Collider>,
312 ) -> Handle<dim2::collider::Collider> {
313 if subject == self.collider1 {
314 self.collider2
315 } else {
316 self.collider1
317 }
318 }
319}
320
321pub(super) struct Container<S, A>
322where
323 A: Hash + Eq + Clone,
324{
325 set: S,
326 map: BiDirHashMap<A, Handle<Node>>,
327}
328
329fn convert_joint_params(
330 params: scene::dim2::joint::JointParams,
331 local_frame1: Isometry2<f32>,
332 local_frame2: Isometry2<f32>,
333) -> GenericJoint {
334 let locked_axis = match params {
335 JointParams::BallJoint(_) => JointAxesMask::LOCKED_REVOLUTE_AXES,
336 JointParams::FixedJoint(_) => JointAxesMask::LOCKED_FIXED_AXES,
337 JointParams::PrismaticJoint(_) => JointAxesMask::LOCKED_PRISMATIC_AXES,
338 };
339
340 let mut joint = GenericJointBuilder::new(locked_axis)
341 .local_frame1(local_frame1.into())
342 .local_frame2(local_frame2.into())
343 .build();
344
345 match params {
346 scene::dim2::joint::JointParams::BallJoint(v) => {
347 if v.limits_enabled {
348 joint.set_limits(
349 JointAxis::AngX,
350 [v.limits_angles.start, v.limits_angles.end],
351 );
352 }
353 }
354 scene::dim2::joint::JointParams::FixedJoint(_) => {}
355 scene::dim2::joint::JointParams::PrismaticJoint(v) => {
356 if v.limits_enabled {
357 joint.set_limits(JointAxis::LinX, [v.limits.start, v.limits.end]);
358 }
359 }
360 }
361
362 joint
363}
364
365fn tile_map_to_collider_shape(
366 tile_map: &GeometrySource,
367 owner_inv_transform: Matrix4<f32>,
368 nodes: &NodePool,
369 collider_name: &ImmutableString,
370) -> Option<SharedShape> {
371 let tile_map = nodes
372 .try_borrow(tile_map.0)
373 .ok()?
374 .component_ref::<TileMap>()?;
375
376 let tile_set_resource = tile_map.tile_set()?.data_ref();
377 let tile_set = tile_set_resource.as_loaded_ref()?;
378
379 let tile_scale = tile_map.tile_scale();
380 let global_transform = owner_inv_transform
381 * tile_map.global_transform()
382 * Matrix4::new_nonuniform_scaling(&Vector3::new(-tile_scale.x, tile_scale.y, 1.0));
383
384 let mut vertices = Vec::new();
385 let mut triangles = Vec::new();
386
387 let collider_uuid = tile_set.collider_name_to_uuid(collider_name)?;
388 let tile_data = tile_map.tiles()?.data_ref();
389 let tile_data = tile_data.as_loaded_ref()?;
390 for (position, handle) in tile_data.iter() {
391 let Some(tile_definition) = tile_set.get_tile_data(handle.into()) else {
392 continue;
393 };
394
395 if let Some(collider) = tile_definition.colliders.get(&collider_uuid) {
396 let position = position.cast::<f32>().to_homogeneous();
397 collider.build_collider_shape(
398 &global_transform,
399 position,
400 &mut vertices,
401 &mut triangles,
402 );
403 }
404 }
405
406 SharedShape::trimesh(vertices.into_iter().map(Vec2::from).collect(), triangles).ok()
407}
408
409fn collider_shape_into_native_shape(
411 shape: &ColliderShape,
412 owner_inv_transform: Matrix4<f32>,
413 nodes: &NodePool,
414) -> Option<SharedShape> {
415 match shape {
416 ColliderShape::Ball(ball) => Some(SharedShape::ball(ball.radius)),
417 ColliderShape::Cuboid(cuboid) => Some(SharedShape(Arc::new(Cuboid::new(
418 cuboid.half_extents.into(),
419 )))),
420 ColliderShape::Capsule(capsule) => Some(SharedShape::capsule(
421 capsule.begin.into(),
422 capsule.end.into(),
423 capsule.radius,
424 )),
425 ColliderShape::Segment(segment) => Some(SharedShape::segment(
426 segment.begin.into(),
427 segment.end.into(),
428 )),
429 ColliderShape::Triangle(triangle) => Some(SharedShape::triangle(
430 triangle.a.into(),
431 triangle.b.into(),
432 triangle.c.into(),
433 )),
434 ColliderShape::Trimesh(_) => {
435 None }
437 ColliderShape::Heightfield(_) => {
438 None }
440 ColliderShape::TileMap(TileMapShape {
441 tile_map,
442 layer_name: collider_layer_name,
443 }) => tile_map_to_collider_shape(tile_map, owner_inv_transform, nodes, collider_layer_name),
444 }
445}
446
447fn isometry2_to_mat4(isometry: &Isometry2<f32>) -> Matrix4<f32> {
448 Isometry3 {
449 rotation: UnitQuaternion::from_euler_angles(0.0, 0.0, isometry.rotation.angle()),
450 translation: Translation3 {
451 vector: Vector3::new(isometry.translation.x, isometry.translation.y, 0.0),
452 },
453 }
454 .to_homogeneous()
455}
456
457#[derive(Visit, Reflect)]
461pub struct PhysicsWorld {
462 pub enabled: InheritableVariable<bool>,
464
465 pub integration_parameters: InheritableVariable<IntegrationParameters>,
467
468 pub gravity: InheritableVariable<Vector2<f32>>,
470
471 #[visit(skip)]
473 #[reflect(hidden)]
474 pub performance_statistics: PhysicsPerformanceStatistics,
475
476 #[visit(skip)]
478 #[reflect(hidden)]
479 pipeline: PhysicsPipeline,
480 #[visit(skip)]
482 #[reflect(hidden)]
483 broad_phase: DefaultBroadPhase,
484 #[visit(skip)]
486 #[reflect(hidden)]
487 narrow_phase: NarrowPhase,
488 #[visit(skip)]
490 #[reflect(hidden)]
491 ccd_solver: CCDSolver,
492 #[visit(skip)]
495 #[reflect(hidden)]
496 islands: IslandManager,
497 #[visit(skip)]
499 #[reflect(hidden)]
500 bodies: RigidBodySet,
501 #[visit(skip)]
503 #[reflect(hidden)]
504 pub(crate) colliders: ColliderSet,
505 #[visit(skip)]
507 #[reflect(hidden)]
508 joints: Container<ImpulseJointSet, ImpulseJointHandle>,
509 #[visit(skip)]
511 #[reflect(hidden)]
512 multibody_joints: Container<MultibodyJointSet, MultibodyJointHandle>,
513 #[visit(skip)]
515 #[reflect(hidden)]
516 event_handler: Box<dyn EventHandler>,
517 #[visit(skip)]
518 #[reflect(hidden)]
519 debug_render_pipeline: Mutex<DebugRenderPipeline>,
520}
521
522impl Clone for PhysicsWorld {
523 fn clone(&self) -> Self {
524 PhysicsWorld {
525 enabled: self.enabled.clone(),
526 integration_parameters: self.integration_parameters.clone(),
527 gravity: self.gravity.clone(),
528 ..Default::default()
529 }
530 }
531}
532
533fn isometry_from_global_transform(transform: &Matrix4<f32>) -> Isometry2<f32> {
534 Isometry2 {
535 translation: Translation2::new(transform[12], transform[13]),
536 rotation: UnitComplex::from_angle(
537 Rotation3::from_matrix_eps(&transform.basis(), f32::EPSILON, 16, Rotation3::identity())
538 .euler_angles()
539 .2,
540 ),
541 }
542}
543
544fn calculate_local_frames(
545 joint: &dyn NodeTrait,
546 body1: &dyn NodeTrait,
547 body2: &dyn NodeTrait,
548) -> (Isometry2<f32>, Isometry2<f32>) {
549 let joint_isometry = isometry_from_global_transform(&joint.global_transform());
550
551 (
552 joint_isometry * isometry_from_global_transform(&body1.global_transform()).inverse(),
553 joint_isometry * isometry_from_global_transform(&body2.global_transform()).inverse(),
554 )
555}
556
557fn u32_to_group(v: u32) -> rapier2d::geometry::Group {
558 rapier2d::geometry::Group::from_bits(v).unwrap_or_else(rapier2d::geometry::Group::all)
559}
560
561#[derive(Copy, Clone, Default)]
563#[allow(clippy::type_complexity)]
564pub struct QueryFilter<'a> {
565 pub flags: collider::QueryFilterFlags,
567 pub groups: Option<collider::InteractionGroups>,
570 pub exclude_collider: Option<Handle<Node>>,
572 pub exclude_rigid_body: Option<Handle<Node>>,
574 pub predicate: Option<&'a dyn Fn(Handle<Node>, &collider::Collider) -> bool>,
576}
577
578#[derive(Copy, Clone, Debug)]
580pub struct TOI {
581 pub toi: f32,
583 pub witness1: Point2<f32>,
587 pub witness2: Point2<f32>,
591 pub normal1: UnitVector2<f32>,
595 pub normal2: UnitVector2<f32>,
599 pub status: collider::TOIStatus,
601}
602
603impl PhysicsWorld {
604 pub(crate) fn new() -> Self {
606 Self {
607 enabled: true.into(),
608 pipeline: PhysicsPipeline::new(),
609 gravity: Vector2::new(0.0, -9.81).into(),
610 integration_parameters: IntegrationParameters::default().into(),
611 broad_phase: DefaultBroadPhase::new(),
612 narrow_phase: NarrowPhase::new(),
613 ccd_solver: CCDSolver::new(),
614 islands: IslandManager::new(),
615 bodies: RigidBodySet::new(),
616 colliders: ColliderSet::new(),
617 joints: Container {
618 set: ImpulseJointSet::new(),
619 map: Default::default(),
620 },
621 multibody_joints: Container {
622 set: MultibodyJointSet::new(),
623 map: Default::default(),
624 },
625 event_handler: Box::new(()),
626 performance_statistics: Default::default(),
627 debug_render_pipeline: Default::default(),
628 }
629 }
630
631 pub(crate) fn update(&mut self, dt: f32, dt_enabled: bool) {
642 let time = instant::Instant::now();
643 let parameter_dt = self.integration_parameters.dt;
644 let parameter_dt = if parameter_dt == Some(0.0) {
645 None
646 } else {
647 parameter_dt
648 };
649 let dt = if dt_enabled {
650 parameter_dt.unwrap_or(dt)
651 } else {
652 0.0
653 };
654
655 if *self.enabled {
656 let integration_parameters = rapier2d::dynamics::IntegrationParameters {
657 dt,
658 min_ccd_dt: self.integration_parameters.min_ccd_dt,
659 contact_softness: rapier2d::dynamics::SpringCoefficients::contact_defaults(),
660 warmstart_coefficient: self.integration_parameters.warmstart_coefficient,
661 length_unit: self.integration_parameters.length_unit,
662 normalized_allowed_linear_error: self.integration_parameters.allowed_linear_error,
663 normalized_max_corrective_velocity: self
664 .integration_parameters
665 .normalized_max_corrective_velocity,
666 normalized_prediction_distance: self.integration_parameters.prediction_distance,
667 num_solver_iterations: self.integration_parameters.num_solver_iterations,
668 num_internal_pgs_iterations: self
669 .integration_parameters
670 .num_internal_pgs_iterations,
671 num_internal_stabilization_iterations: self
672 .integration_parameters
673 .num_internal_stabilization_iterations,
674 min_island_size: self.integration_parameters.min_island_size as usize,
675 max_ccd_substeps: self.integration_parameters.max_ccd_substeps as usize,
676 };
677
678 self.pipeline.step(
679 (*self.gravity).into(),
680 &integration_parameters,
681 &mut self.islands,
682 &mut self.broad_phase,
683 &mut self.narrow_phase,
684 &mut self.bodies,
685 &mut self.colliders,
686 &mut self.joints.set,
687 &mut self.multibody_joints.set,
688 &mut self.ccd_solver,
689 &(),
690 &*self.event_handler,
691 );
692 }
693
694 self.performance_statistics.step_time += instant::Instant::now() - time;
695 }
696
697 pub(crate) fn add_body(&mut self, owner: Handle<Node>, mut body: RigidBody) -> RigidBodyHandle {
698 body.user_data = owner.encode_to_u128();
699 self.bodies.insert(body)
700 }
701
702 pub(crate) fn remove_body(&mut self, handle: RigidBodyHandle) {
703 self.bodies.remove(
704 handle,
705 &mut self.islands,
706 &mut self.colliders,
707 &mut self.joints.set,
708 &mut self.multibody_joints.set,
709 true,
710 );
711 }
712
713 pub(crate) fn add_collider(
714 &mut self,
715 owner: Handle<Node>,
716 parent_body: RigidBodyHandle,
717 mut collider: Collider,
718 ) -> ColliderHandle {
719 collider.user_data = owner.encode_to_u128();
720 self.colliders
721 .insert_with_parent(collider, parent_body, &mut self.bodies)
722 }
723
724 pub(crate) fn remove_collider(&mut self, handle: ColliderHandle) -> bool {
725 self.colliders
726 .remove(handle, &mut self.islands, &mut self.bodies, false)
727 .is_some()
728 }
729
730 pub(crate) fn add_joint(
731 &mut self,
732 owner: Handle<Node>,
733 body1: RigidBodyHandle,
734 body2: RigidBodyHandle,
735 params: GenericJoint,
736 ) -> ImpulseJointHandle {
737 let handle = self.joints.set.insert(body1, body2, params, false);
738 self.joints.map.insert(handle, owner);
739 handle
740 }
741
742 pub(crate) fn remove_joint(&mut self, handle: ImpulseJointHandle) {
743 if self.joints.set.remove(handle, false).is_some() {
744 assert!(self.joints.map.remove_by_key(&handle).is_some());
745 }
746 }
747
748 pub fn draw(&self, context: &mut SceneDrawingContext) {
751 self.debug_render_pipeline.safe_lock().render(
752 context,
753 &self.bodies,
754 &self.colliders,
755 &self.joints.set,
756 &self.multibody_joints.set,
757 &self.narrow_phase,
758 );
759 }
760
761 pub fn cast_ray<S: QueryResultsStorage>(&self, opts: RayCastOptions, query_buffer: &mut S) {
763 let time = instant::Instant::now();
764
765 let query = self.broad_phase.as_query_pipeline(
766 &DefaultQueryDispatcher,
767 &self.bodies,
768 &self.colliders,
769 rapier2d::pipeline::QueryFilter::new().groups(InteractionGroups::new(
770 u32_to_group(opts.groups.memberships.0),
771 u32_to_group(opts.groups.filter.0),
772 Default::default(),
773 )),
774 );
775
776 query_buffer.clear();
777 let ray = Ray::new(
778 opts.ray_origin.into(),
779 opts.ray_direction
780 .try_normalize(f32::EPSILON)
781 .unwrap_or_default()
782 .into(),
783 );
784 for (_, collider, intersection) in query.intersect_ray(ray, opts.max_len, true) {
785 query_buffer.push(Intersection {
786 collider: Handle::decode_from_u128(collider.user_data),
787 normal: intersection.normal.into(),
788 position: ray.point_at(intersection.time_of_impact).into(),
789 feature: intersection.feature.into(),
790 toi: intersection.time_of_impact,
791 });
792 }
793 if opts.sort_results {
794 query_buffer.sort_intersections_by(|a, b| {
795 if a.toi > b.toi {
796 Ordering::Greater
797 } else if a.toi < b.toi {
798 Ordering::Less
799 } else {
800 Ordering::Equal
801 }
802 })
803 }
804
805 self.performance_statistics.total_ray_cast_time.set(
806 self.performance_statistics.total_ray_cast_time.get()
807 + (instant::Instant::now() - time),
808 );
809 }
810
811 pub fn cast_shape(
831 &self,
832 graph: &Graph,
833 shape: &dyn Shape,
834 shape_pos: &Isometry2<f32>,
835 shape_vel: &Vector2<f32>,
836 max_toi: f32,
837 stop_at_penetration: bool,
838 filter: QueryFilter,
839 ) -> Option<(Handle<Node>, TOI)> {
840 let predicate = |handle: ColliderHandle, _: &Collider| -> bool {
841 if let Some(pred) = filter.predicate {
842 let h = Handle::decode_from_u128(self.colliders.get(handle).unwrap().user_data);
843 pred(
844 h,
845 graph.node(h).component_ref::<collider::Collider>().unwrap(),
846 )
847 } else {
848 true
849 }
850 };
851
852 let filter = rapier2d::pipeline::QueryFilter {
853 flags: rapier2d::pipeline::QueryFilterFlags::from_bits(filter.flags.bits()).unwrap(),
854 groups: filter.groups.map(|g| {
855 InteractionGroups::new(
856 u32_to_group(g.memberships.0),
857 u32_to_group(g.filter.0),
858 Default::default(),
859 )
860 }),
861 exclude_collider: filter
862 .exclude_collider
863 .and_then(|h| graph.try_get_of_type::<dim2::collider::Collider>(h).ok())
864 .map(|c| c.native.get()),
865 exclude_rigid_body: filter
866 .exclude_collider
867 .and_then(|h| graph.try_get_of_type::<dim2::rigidbody::RigidBody>(h).ok())
868 .map(|c| c.native.get()),
869 predicate: Some(&predicate),
870 };
871
872 let query = self.broad_phase.as_query_pipeline(
873 &DefaultQueryDispatcher,
874 &self.bodies,
875 &self.colliders,
876 filter,
877 );
878
879 let opts = ShapeCastOptions {
880 max_time_of_impact: max_toi,
881 target_distance: 0.0,
882 stop_at_penetration,
883 compute_impact_geometry_on_penetration: true,
884 };
885
886 query
887 .cast_shape(&Pose2::from(*shape_pos), (*shape_vel).into(), shape, opts)
888 .map(|(handle, toi)| {
889 (
890 Handle::decode_from_u128(self.colliders.get(handle).unwrap().user_data),
891 TOI {
892 toi: toi.time_of_impact,
893 witness1: toi.witness1.into(),
894 witness2: toi.witness2.into(),
895 normal1: UnitVector2::new_normalize(toi.normal1.into()),
896 normal2: UnitVector2::new_normalize(toi.normal2.into()),
897 status: toi.status.into(),
898 },
899 )
900 })
901 }
902
903 pub(crate) fn set_rigid_body_position(
904 &mut self,
905 rigid_body: &scene::dim2::rigidbody::RigidBody,
906 new_global_transform: &Matrix4<f32>,
907 ) {
908 if let Some(native) = self.bodies.get_mut(rigid_body.native.get()) {
909 native.set_position(
910 isometry_from_global_transform(new_global_transform).into(),
911 false,
914 );
915 }
916 }
917
918 pub(crate) fn sync_rigid_body_node(
919 &mut self,
920 rigid_body: &mut scene::dim2::rigidbody::RigidBody,
921 parent_transform: Matrix4<f32>,
922 ) {
923 if *self.enabled {
924 if let Some(native) = self.bodies.get(rigid_body.native.get()) {
925 if native.body_type() != RigidBodyType::Fixed {
926 let local_transform: Matrix4<f32> = parent_transform
927 .try_inverse()
928 .unwrap_or_else(Matrix4::identity)
929 * isometry2_to_mat4(&Isometry2::from(*native.position()));
930
931 let new_local_rotation = UnitQuaternion::from_matrix_eps(
932 &local_transform.basis(),
933 f32::EPSILON,
934 16,
935 UnitQuaternion::identity(),
936 );
937 let new_x = local_transform[12];
938 let new_y = local_transform[13];
939
940 let local_transform = rigid_body.local_transform();
944 let current_position = **local_transform.position();
945 if current_position.x != new_x
946 || current_position.y != new_y
947 || **local_transform.rotation() != new_local_rotation
948 {
949 rigid_body
950 .local_transform_mut()
951 .set_position(Vector3::new(new_x, new_y, current_position.z))
953 .set_rotation(new_local_rotation);
954 }
955
956 rigid_body
957 .lin_vel
958 .set_value_with_flags(native.linvel().into(), VariableFlags::MODIFIED);
959 rigid_body
960 .ang_vel
961 .set_value_with_flags(native.angvel(), VariableFlags::MODIFIED);
962 rigid_body.sleeping = native.is_sleeping();
963 }
964 }
965 }
966 }
967
968 pub(crate) fn sync_to_rigid_body_node(
969 &mut self,
970 handle: Handle<Node>,
971 rigid_body_node: &scene::dim2::rigidbody::RigidBody,
972 ) {
973 if !rigid_body_node.is_globally_enabled() {
974 self.remove_body(rigid_body_node.native.get());
975 rigid_body_node.native.set(Default::default());
976 return;
977 }
978
979 if rigid_body_node.native.get() != RigidBodyHandle::invalid() {
983 let mut actions = rigid_body_node.actions.safe_lock();
984 if rigid_body_node.need_sync_model() || !actions.is_empty() {
985 if let Some(native) = self.bodies.get_mut(rigid_body_node.native.get()) {
986 rigid_body_node
989 .body_type
990 .try_sync_model(|v| native.set_body_type(v.into(), false));
991 rigid_body_node
992 .lin_vel
993 .try_sync_model(|v| native.set_linvel(v.into(), true));
994 rigid_body_node
995 .ang_vel
996 .try_sync_model(|v| native.set_angvel(v, true));
997 rigid_body_node.mass.try_sync_model(|v| {
998 native.set_additional_mass(v, true);
999 });
1000 rigid_body_node
1001 .lin_damping
1002 .try_sync_model(|v| native.set_linear_damping(v));
1003 rigid_body_node
1004 .ang_damping
1005 .try_sync_model(|v| native.set_angular_damping(v));
1006 rigid_body_node
1007 .ccd_enabled
1008 .try_sync_model(|v| native.enable_ccd(v));
1009 rigid_body_node.can_sleep.try_sync_model(|v| {
1010 if v {
1011 *native.activation_mut() = RigidBodyActivation::active();
1012 } else {
1013 *native.activation_mut() = RigidBodyActivation::cannot_sleep();
1014 };
1015 });
1016 rigid_body_node
1017 .translation_locked
1018 .try_sync_model(|v| native.lock_translations(v, false));
1019 rigid_body_node.rotation_locked.try_sync_model(|v| {
1020 native.set_enabled_rotations(!v, !v, !v, false);
1021 });
1022 rigid_body_node
1023 .dominance
1024 .try_sync_model(|v| native.set_dominance_group(v));
1025 rigid_body_node
1026 .gravity_scale
1027 .try_sync_model(|v| native.set_gravity_scale(v, false));
1028
1029 if rigid_body_node.reset_forces.replace(false) {
1032 native.reset_forces(false);
1033 native.reset_torques(false);
1034 }
1035
1036 while let Some(action) = actions.pop_front() {
1037 match action {
1038 ApplyAction::Force(force) => {
1039 native.add_force(force.into(), false);
1040 rigid_body_node.reset_forces.set(true);
1041 }
1042 ApplyAction::Torque(torque) => {
1043 native.add_torque(torque, false);
1044 rigid_body_node.reset_forces.set(true);
1045 }
1046 ApplyAction::ForceAtPoint { force, point } => {
1047 native.add_force_at_point(force.into(), point.into(), false);
1048 rigid_body_node.reset_forces.set(true);
1049 }
1050 ApplyAction::Impulse(impulse) => {
1051 native.apply_impulse(impulse.into(), false)
1052 }
1053 ApplyAction::TorqueImpulse(impulse) => {
1054 native.apply_torque_impulse(impulse, false)
1055 }
1056 ApplyAction::ImpulseAtPoint { impulse, point } => {
1057 native.apply_impulse_at_point(impulse.into(), point.into(), false)
1058 }
1059 ApplyAction::WakeUp => native.wake_up(true),
1060 ApplyAction::NextTranslation(position) => {
1061 native.set_next_kinematic_translation(position.into())
1062 }
1063 ApplyAction::NextRotation(rotation) => {
1064 native.set_next_kinematic_rotation(rotation.into())
1065 }
1066 ApplyAction::NextPosition(position) => {
1067 native.set_next_kinematic_position(position.into())
1068 }
1069 }
1070 }
1071 }
1072 }
1073 } else {
1074 let mut builder = RigidBodyBuilder::new(rigid_body_node.body_type().into())
1075 .pose(isometry_from_global_transform(&rigid_body_node.global_transform()).into())
1076 .ccd_enabled(rigid_body_node.is_ccd_enabled())
1077 .additional_mass(rigid_body_node.mass())
1078 .angvel(*rigid_body_node.ang_vel)
1079 .linvel((*rigid_body_node.lin_vel).into())
1080 .linear_damping(*rigid_body_node.lin_damping)
1081 .angular_damping(*rigid_body_node.ang_damping)
1082 .can_sleep(rigid_body_node.is_can_sleep())
1083 .dominance_group(rigid_body_node.dominance())
1084 .gravity_scale(rigid_body_node.gravity_scale());
1085
1086 if rigid_body_node.is_translation_locked() {
1087 builder = builder.lock_translations();
1088 }
1089
1090 let mut body = builder.build();
1091
1092 body.set_enabled_rotations(
1093 !rigid_body_node.is_rotation_locked(),
1094 !rigid_body_node.is_rotation_locked(),
1095 !rigid_body_node.is_rotation_locked(),
1096 false,
1097 );
1098
1099 rigid_body_node.native.set(self.add_body(handle, body));
1100
1101 Log::writeln(
1102 MessageKind::Information,
1103 format!(
1104 "Native rigid body was created for node {}",
1105 rigid_body_node.name()
1106 ),
1107 );
1108 }
1109 }
1110
1111 pub(crate) fn sync_to_collider_node(
1112 &mut self,
1113 nodes: &NodePool,
1114 handle: Handle<Node>,
1115 collider_node: &scene::dim2::collider::Collider,
1116 ) {
1117 if !collider_node.is_globally_enabled() {
1118 self.remove_collider(collider_node.native.get());
1119 collider_node.native.set(Default::default());
1120 return;
1121 }
1122
1123 let anything_changed = collider_node.needs_sync_model();
1124
1125 if collider_node.native.get() != ColliderHandle::invalid() {
1131 if anything_changed {
1132 if let Some(native) = self.colliders.get_mut(collider_node.native.get()) {
1133 collider_node
1134 .restitution
1135 .try_sync_model(|v| native.set_restitution(v));
1136 collider_node.collision_groups.try_sync_model(|v| {
1137 native.set_collision_groups(InteractionGroups::new(
1138 u32_to_group(v.memberships.0),
1139 u32_to_group(v.filter.0),
1140 Default::default(),
1141 ))
1142 });
1143 collider_node.solver_groups.try_sync_model(|v| {
1144 native.set_solver_groups(InteractionGroups::new(
1145 u32_to_group(v.memberships.0),
1146 u32_to_group(v.filter.0),
1147 Default::default(),
1148 ))
1149 });
1150 collider_node
1151 .friction
1152 .try_sync_model(|v| native.set_friction(v));
1153 collider_node
1154 .is_sensor
1155 .try_sync_model(|v| native.set_sensor(v));
1156 collider_node
1157 .friction_combine_rule
1158 .try_sync_model(|v| native.set_friction_combine_rule(v.into()));
1159 collider_node
1160 .restitution_combine_rule
1161 .try_sync_model(|v| native.set_restitution_combine_rule(v.into()));
1162 let mut remove_collider = false;
1163 collider_node.shape.try_sync_model(|v| {
1164 let inv_global_transform = isometric_global_transform(nodes, handle)
1165 .try_inverse()
1166 .unwrap_or_default();
1167
1168 if let Some(shape) =
1169 collider_shape_into_native_shape(&v, inv_global_transform, nodes)
1170 {
1171 native.set_shape(shape);
1172 } else {
1173 remove_collider = true;
1174 }
1175 });
1176 if remove_collider {
1177 self.remove_collider(collider_node.native.get());
1178 collider_node.native.set(ColliderHandle::invalid());
1179 }
1180 }
1181 }
1182 } else if let Ok(parent_body) =
1183 nodes.try_get_component_of_type::<dim2::rigidbody::RigidBody>(collider_node.parent())
1184 {
1185 if parent_body.native.get() != RigidBodyHandle::invalid() {
1186 let rigid_body_native = parent_body.native.get();
1187 let inv_global_transform = isometric_global_transform(nodes, handle)
1188 .try_inverse()
1189 .unwrap();
1190 if let Some(shape) = collider_shape_into_native_shape(
1191 collider_node.shape(),
1192 inv_global_transform,
1193 nodes,
1194 ) {
1195 let mut builder = ColliderBuilder::new(shape)
1196 .position(
1197 Isometry2 {
1198 rotation: UnitComplex::from_angle(
1199 collider_node.local_transform().rotation().euler_angles().2,
1200 ),
1201 translation: Translation2 {
1202 vector: collider_node.local_transform().position().xy(),
1203 },
1204 }
1205 .into(),
1206 )
1207 .friction(collider_node.friction())
1208 .restitution(collider_node.restitution())
1209 .collision_groups(InteractionGroups::new(
1210 u32_to_group(collider_node.collision_groups().memberships.0),
1211 u32_to_group(collider_node.collision_groups().filter.0),
1212 Default::default(),
1213 ))
1214 .friction_combine_rule(collider_node.friction_combine_rule().into())
1215 .restitution_combine_rule(collider_node.restitution_combine_rule().into())
1216 .solver_groups(InteractionGroups::new(
1217 u32_to_group(collider_node.solver_groups().memberships.0),
1218 u32_to_group(collider_node.solver_groups().filter.0),
1219 Default::default(),
1220 ))
1221 .sensor(collider_node.is_sensor());
1222
1223 if let Some(density) = collider_node.density() {
1224 builder = builder.density(density);
1225 }
1226
1227 let native_handle =
1228 self.add_collider(handle, rigid_body_native, builder.build());
1229
1230 collider_node.native.set(native_handle);
1231
1232 Log::writeln(
1233 MessageKind::Information,
1234 format!(
1235 "Native collider was created for node {}",
1236 collider_node.name()
1237 ),
1238 );
1239 }
1240 }
1241 }
1242 }
1243
1244 pub(crate) fn sync_to_joint_node(
1245 &mut self,
1246 nodes: &NodePool,
1247 handle: Handle<Node>,
1248 joint: &scene::dim2::joint::Joint,
1249 ) {
1250 if !joint.is_globally_enabled() {
1251 self.remove_joint(joint.native.get());
1252 joint.native.set(ImpulseJointHandle(Default::default()));
1253 return;
1254 }
1255
1256 if let Some(native) = self.joints.set.get_mut(joint.native.get(), false) {
1257 joint.body1.try_sync_model(|v| {
1258 if let Ok(rigid_body_node) = nodes.try_get(v) {
1259 native.body1 = rigid_body_node.native.get();
1260 }
1261 });
1262 joint.body2.try_sync_model(|v| {
1263 if let Ok(rigid_body_node) = nodes.try_get(v) {
1264 native.body2 = rigid_body_node.native.get();
1265 }
1266 });
1267 joint.params.try_sync_model(|v| {
1268 native.data =
1269 convert_joint_params(v, native.data.local_frame1.into(), native.data.local_frame2.into())
1271 });
1272 joint.motor_params.try_sync_model(|v|{
1273 let joint_axis = match joint.params.get_value_ref(){
1276 JointParams::PrismaticJoint(_) => JointAxis::LinX,
1277 JointParams::BallJoint(_) => JointAxis::AngX,
1278 _ => {
1279 Log::warn("Try to modify motor parameters for unsupported joint type, this operation will be ignored.");
1280 return;
1281 }
1282 };
1283 native.data.set_motor_model(joint_axis, rapier2d::prelude::MotorModel::ForceBased);
1285 let JointMotorParams {
1286 target_vel,
1287 target_pos,
1288 stiffness,
1289 damping,
1290 max_force
1291 } = v;
1292 native.data.set_motor(joint_axis, target_pos, target_vel, stiffness, damping);
1293 native.data.set_motor_max_force(joint_axis, max_force);
1294 let Some(body1) = self.bodies.get_mut(native.body1) else {
1298 return;
1299 };
1300 body1.wake_up(true);
1301 let Some(body2) = self.bodies.get_mut(native.body2) else {
1302 return;
1303 };
1304 body2.wake_up(true);
1305 });
1306 joint.contacts_enabled.try_sync_model(|v| {
1307 native.data.set_contacts_enabled(v);
1308 });
1309 let mut local_frames = joint.local_frames.borrow_mut();
1310 if local_frames.is_none() {
1311 if let (Ok(body1), Ok(body2)) =
1312 (nodes.try_get(joint.body1()), nodes.try_get(joint.body2()))
1313 {
1314 let (local_frame1, local_frame2) = calculate_local_frames(joint, body1, body2);
1315 native.data =
1316 convert_joint_params((*joint.params).clone(), local_frame1, local_frame2);
1317 *local_frames = Some(JointLocalFrames::new(&local_frame1, &local_frame2));
1318 }
1319 }
1320 } else {
1321 let body1_handle = joint.body1();
1322 let body2_handle = joint.body2();
1323 let params = joint.params().clone();
1324
1325 if let (Ok(body1), Ok(body2)) =
1327 (nodes.try_get(body1_handle), nodes.try_get(body2_handle))
1328 {
1329 let native_body1 = body1.native.get();
1330 let native_body2 = body2.native.get();
1331
1332 if self.bodies.get(native_body1).is_none()
1333 || self.bodies.get(native_body2).is_none()
1334 {
1335 return;
1339 }
1340
1341 let mut local_frames = joint.local_frames.borrow_mut();
1343 let (local_frame1, local_frame2) = local_frames
1344 .clone()
1345 .map(|frames| {
1346 (
1347 Isometry2 {
1348 rotation: frames.body1.rotation,
1349 translation: Translation2 {
1350 vector: frames.body1.position,
1351 },
1352 },
1353 Isometry2 {
1354 rotation: frames.body2.rotation,
1355 translation: Translation2 {
1356 vector: frames.body2.position,
1357 },
1358 },
1359 )
1360 })
1361 .unwrap_or_else(|| calculate_local_frames(joint, body1, body2));
1362
1363 let mut native_joint = convert_joint_params(params, local_frame1, local_frame2);
1364 native_joint.contacts_enabled = joint.is_contacts_enabled();
1365 let native_handle =
1366 self.add_joint(handle, native_body1, native_body2, native_joint);
1367
1368 joint.native.set(native_handle);
1369 *local_frames = Some(JointLocalFrames::new(&local_frame1, &local_frame2));
1370
1371 Log::writeln(
1372 MessageKind::Information,
1373 format!("Native joint was created for node {}", joint.name()),
1374 );
1375 }
1376 }
1377 }
1378
1379 pub(crate) fn intersections_with(
1381 &self,
1382 collider: ColliderHandle,
1383 ) -> impl Iterator<Item = IntersectionPair> + '_ {
1384 self.narrow_phase
1385 .intersection_pairs_with(collider)
1386 .filter_map(|(collider1, collider2, intersecting)| {
1387 Some(IntersectionPair {
1388 collider1: Handle::decode_from_u128(self.colliders.get(collider1)?.user_data),
1389 collider2: Handle::decode_from_u128(self.colliders.get(collider2)?.user_data),
1390 has_any_active_contact: intersecting,
1391 })
1392 })
1393 }
1394
1395 pub(crate) fn contacts_with(
1397 &self,
1398 collider: ColliderHandle,
1399 ) -> impl Iterator<Item = ContactPair> + '_ {
1400 self.narrow_phase
1401 .contact_pairs_with(collider)
1402 .filter_map(|c| ContactPair::from_native(c, self))
1403 }
1404
1405 pub fn contacts(&self) -> impl Iterator<Item = ContactPair> + '_ {
1407 self.narrow_phase
1408 .contact_pairs()
1409 .filter_map(|c| ContactPair::from_native(c, self))
1410 }
1411}
1412
1413impl Default for PhysicsWorld {
1414 fn default() -> Self {
1415 Self::new()
1416 }
1417}
1418
1419impl Debug for PhysicsWorld {
1420 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1421 write!(f, "PhysicsWorld")
1422 }
1423}