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