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