1use crate::{
24 core::{
25 algebra::{
26 Isometry3, Matrix4, Point3, Translation, Translation3, UnitQuaternion, UnitVector3,
27 Vector2, Vector3,
28 },
29 arrayvec::ArrayVec,
30 instant,
31 log::{Log, MessageKind},
32 math::Matrix4Ext,
33 parking_lot::Mutex,
34 pool::Handle,
35 reflect::prelude::*,
36 uuid_provider,
37 variable::{InheritableVariable, VariableFlags},
38 visitor::prelude::*,
39 BiDirHashMap, SafeLock,
40 },
41 scene::{
42 self,
43 collider::{self, ColliderShape, GeometrySource},
44 debug::SceneDrawingContext,
45 graph::{isometric_global_transform, Graph, NodePool},
46 joint::{JointLocalFrames, JointMotorParams, JointParams},
47 mesh::{
48 buffer::{VertexAttributeUsage, VertexReadTrait},
49 Mesh,
50 },
51 node::{Node, NodeTrait},
52 rigidbody::{self, ApplyAction, RigidBodyMassPropertiesType},
53 terrain::{Chunk, Terrain},
54 },
55 utils::raw_mesh::{RawMeshBuilder, RawVertex},
56};
57use rapier3d::geometry::Array2;
58use rapier3d::{
59 dynamics::{
60 CCDSolver, GenericJoint, GenericJointBuilder, ImpulseJointHandle, ImpulseJointSet,
61 IslandManager, JointAxesMask, MultibodyJointHandle, MultibodyJointSet, RigidBody,
62 RigidBodyActivation, RigidBodyBuilder, RigidBodyHandle, RigidBodySet, RigidBodyType,
63 },
64 geometry::{
65 Collider, ColliderBuilder, ColliderHandle, ColliderSet, Cuboid, DefaultBroadPhase,
66 InteractionGroups, NarrowPhase, Ray, SharedShape,
67 },
68 parry::{query::ShapeCastOptions, shape::HeightField},
69 pipeline::{DebugRenderPipeline, EventHandler, PhysicsPipeline},
70 prelude::{HeightFieldCellStatus, JointAxis, MassProperties},
71};
72use std::{
73 cell::Cell,
74 cmp::Ordering,
75 fmt::{Debug, Formatter},
76 hash::Hash,
77 sync::Arc,
78 time::Duration,
79};
80use strum_macros::{AsRefStr, EnumString, VariantNames};
81
82use fyrox_graph::{SceneGraph, SceneGraphNode};
83pub use rapier3d::geometry::shape::*;
84use rapier3d::math::{Pose3, Vec3};
85use rapier3d::parry::query::DefaultQueryDispatcher;
86use rapier3d::prelude::FrictionModel;
87
88#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
90pub enum FeatureId {
91 Vertex(u32),
93 Edge(u32),
95 Face(u32),
97 Unknown,
99}
100
101impl From<rapier3d::geometry::FeatureId> for FeatureId {
102 fn from(v: rapier3d::geometry::FeatureId) -> Self {
103 match v {
104 rapier3d::geometry::FeatureId::Vertex(v) => FeatureId::Vertex(v),
105 rapier3d::geometry::FeatureId::Edge(v) => FeatureId::Edge(v),
106 rapier3d::geometry::FeatureId::Face(v) => FeatureId::Face(v),
107 rapier3d::geometry::FeatureId::Unknown => FeatureId::Unknown,
108 }
109 }
110}
111
112impl From<rapier2d::geometry::FeatureId> for FeatureId {
113 fn from(v: rapier2d::geometry::FeatureId) -> Self {
114 match v {
115 rapier2d::geometry::FeatureId::Vertex(v) => FeatureId::Vertex(v),
116 rapier2d::geometry::FeatureId::Face(v) => FeatureId::Face(v),
117 rapier2d::geometry::FeatureId::Unknown => FeatureId::Unknown,
118 }
119 }
120}
121
122#[derive(
130 Copy, Clone, Debug, PartialEq, Eq, Visit, Reflect, VariantNames, EnumString, AsRefStr, Default,
131)]
132#[repr(u32)]
133pub enum CoefficientCombineRule {
134 #[default]
136 Average = 0,
137 Min,
139 Multiply,
141 Max,
143 ClampedSum = 4,
145}
146
147uuid_provider!(CoefficientCombineRule = "775d5598-c283-4b44-9cc0-2e23dc8936f4");
148
149impl From<rapier3d::dynamics::CoefficientCombineRule> for CoefficientCombineRule {
150 fn from(v: rapier3d::dynamics::CoefficientCombineRule) -> Self {
151 match v {
152 rapier3d::dynamics::CoefficientCombineRule::Average => CoefficientCombineRule::Average,
153 rapier3d::dynamics::CoefficientCombineRule::Min => CoefficientCombineRule::Min,
154 rapier3d::dynamics::CoefficientCombineRule::Multiply => {
155 CoefficientCombineRule::Multiply
156 }
157 rapier3d::dynamics::CoefficientCombineRule::Max => CoefficientCombineRule::Max,
158 rapier3d::dynamics::CoefficientCombineRule::ClampedSum => {
159 CoefficientCombineRule::ClampedSum
160 }
161 }
162 }
163}
164
165impl Into<rapier3d::dynamics::CoefficientCombineRule> for CoefficientCombineRule {
166 fn into(self) -> rapier3d::dynamics::CoefficientCombineRule {
167 match self {
168 CoefficientCombineRule::Average => rapier3d::dynamics::CoefficientCombineRule::Average,
169 CoefficientCombineRule::Min => rapier3d::dynamics::CoefficientCombineRule::Min,
170 CoefficientCombineRule::Multiply => {
171 rapier3d::dynamics::CoefficientCombineRule::Multiply
172 }
173 CoefficientCombineRule::Max => rapier3d::dynamics::CoefficientCombineRule::Max,
174 CoefficientCombineRule::ClampedSum => {
175 rapier3d::dynamics::CoefficientCombineRule::ClampedSum
176 }
177 }
178 }
179}
180
181impl Into<rapier2d::dynamics::CoefficientCombineRule> for CoefficientCombineRule {
182 fn into(self) -> rapier2d::dynamics::CoefficientCombineRule {
183 match self {
184 CoefficientCombineRule::Average => rapier2d::dynamics::CoefficientCombineRule::Average,
185 CoefficientCombineRule::Min => rapier2d::dynamics::CoefficientCombineRule::Min,
186 CoefficientCombineRule::Multiply => {
187 rapier2d::dynamics::CoefficientCombineRule::Multiply
188 }
189 CoefficientCombineRule::Max => rapier2d::dynamics::CoefficientCombineRule::Max,
190 CoefficientCombineRule::ClampedSum => {
191 rapier2d::dynamics::CoefficientCombineRule::ClampedSum
192 }
193 }
194 }
195}
196
197#[derive(Debug, Default, Clone)]
199pub struct PhysicsPerformanceStatistics {
200 pub step_time: Duration,
202
203 pub total_ray_cast_time: Cell<Duration>,
205}
206
207impl PhysicsPerformanceStatistics {
208 pub fn reset(&mut self) {
210 *self = Default::default();
211 }
212
213 pub fn total(&self) -> Duration {
215 self.step_time + self.total_ray_cast_time.get()
216 }
217}
218
219#[derive(Debug, Clone, PartialEq)]
221pub struct Intersection {
222 pub collider: Handle<collider::Collider>,
224
225 pub normal: Vector3<f32>,
227
228 pub position: Point3<f32>,
230
231 pub feature: FeatureId,
241
242 pub toi: f32,
244}
245
246pub struct RayCastOptions {
248 pub ray_origin: Point3<f32>,
250
251 pub ray_direction: Vector3<f32>,
253
254 pub max_len: f32,
256
257 pub groups: collider::InteractionGroups,
259
260 pub sort_results: bool,
262}
263
264pub trait QueryResultsStorage {
268 fn push(&mut self, intersection: Intersection) -> bool;
271
272 fn clear(&mut self);
274
275 fn sort_intersections_by<C: FnMut(&Intersection, &Intersection) -> Ordering>(&mut self, cmp: C);
277}
278
279impl QueryResultsStorage for Vec<Intersection> {
280 fn push(&mut self, intersection: Intersection) -> bool {
281 self.push(intersection);
282 true
283 }
284
285 fn clear(&mut self) {
286 self.clear()
287 }
288
289 fn sort_intersections_by<C>(&mut self, cmp: C)
290 where
291 C: FnMut(&Intersection, &Intersection) -> Ordering,
292 {
293 self.sort_by(cmp);
294 }
295}
296
297impl<const CAP: usize> QueryResultsStorage for ArrayVec<Intersection, CAP> {
298 fn push(&mut self, intersection: Intersection) -> bool {
299 self.try_push(intersection).is_ok()
300 }
301
302 fn clear(&mut self) {
303 self.clear()
304 }
305
306 fn sort_intersections_by<C>(&mut self, cmp: C)
307 where
308 C: FnMut(&Intersection, &Intersection) -> Ordering,
309 {
310 self.sort_by(cmp);
311 }
312}
313
314#[derive(Debug, Clone, PartialEq)]
316pub struct ContactData {
317 pub local_p1: Vector3<f32>,
319 pub local_p2: Vector3<f32>,
321 pub dist: f32,
323 pub impulse: f32,
326 pub tangent_impulse: Vector2<f32>,
329}
330
331#[derive(Debug, Clone, PartialEq)]
333pub struct ContactManifold {
334 pub points: Vec<ContactData>,
336 pub local_n1: Vector3<f32>,
338 pub local_n2: Vector3<f32>,
340 pub rigid_body1: Handle<rigidbody::RigidBody>,
342 pub rigid_body2: Handle<rigidbody::RigidBody>,
344 pub normal: Vector3<f32>,
346}
347
348#[derive(Debug, Clone, PartialEq)]
350pub struct ContactPair {
351 pub collider1: Handle<collider::Collider>,
353 pub collider2: Handle<collider::Collider>,
355 pub manifolds: Vec<ContactManifold>,
358 pub has_any_active_contact: bool,
360}
361
362impl ContactPair {
363 #[inline]
369 pub fn other(&self, subject: Handle<collider::Collider>) -> Handle<collider::Collider> {
370 if subject == self.collider1 {
371 self.collider2
372 } else {
373 self.collider1
374 }
375 }
376
377 fn from_native(c: &rapier3d::geometry::ContactPair, physics: &PhysicsWorld) -> Option<Self> {
378 Some(ContactPair {
379 collider1: Handle::decode_from_u128(physics.colliders.get(c.collider1)?.user_data),
380 collider2: Handle::decode_from_u128(physics.colliders.get(c.collider2)?.user_data),
381 manifolds: c
382 .manifolds
383 .iter()
384 .filter_map(|m| {
385 Some(ContactManifold {
386 points: m
387 .points
388 .iter()
389 .map(|p| ContactData {
390 local_p1: p.local_p1.into(),
391 local_p2: p.local_p2.into(),
392 dist: p.dist,
393 impulse: p.data.impulse,
394 tangent_impulse: p.data.tangent_impulse,
395 })
396 .collect(),
397 local_n1: m.local_n1.into(),
398 local_n2: m.local_n2.into(),
399 rigid_body1: m.data.rigid_body1.and_then(|h| {
400 physics
401 .bodies
402 .get(h)
403 .map(|b| Handle::decode_from_u128(b.user_data))
404 })?,
405 rigid_body2: m.data.rigid_body2.and_then(|h| {
406 physics
407 .bodies
408 .get(h)
409 .map(|b| Handle::decode_from_u128(b.user_data))
410 })?,
411 normal: m.data.normal.into(),
412 })
413 })
414 .collect(),
415 has_any_active_contact: c.has_any_active_contact(),
416 })
417 }
418}
419
420#[derive(Debug, Clone, PartialEq)]
422pub struct IntersectionPair {
423 pub collider1: Handle<collider::Collider>,
425 pub collider2: Handle<collider::Collider>,
427 pub has_any_active_contact: bool,
430}
431
432impl IntersectionPair {
433 #[inline]
439 pub fn other(&self, subject: Handle<collider::Collider>) -> Handle<collider::Collider> {
440 if subject == self.collider1 {
441 self.collider2
442 } else {
443 self.collider1
444 }
445 }
446}
447
448pub(super) struct Container<S, A>
449where
450 A: Hash + Eq + Clone,
451{
452 set: S,
453 map: BiDirHashMap<A, Handle<Node>>,
454}
455
456fn convert_joint_params(
457 params: scene::joint::JointParams,
458 local_frame1: Isometry3<f32>,
459 local_frame2: Isometry3<f32>,
460) -> GenericJoint {
461 let locked_axis = match params {
462 JointParams::BallJoint(_) => JointAxesMask::LOCKED_SPHERICAL_AXES,
463 JointParams::FixedJoint(_) => JointAxesMask::LOCKED_FIXED_AXES,
464 JointParams::PrismaticJoint(_) => JointAxesMask::LOCKED_PRISMATIC_AXES,
465 JointParams::RevoluteJoint(_) => JointAxesMask::LOCKED_REVOLUTE_AXES,
466 };
467
468 let mut joint = GenericJointBuilder::new(locked_axis)
469 .local_frame1(local_frame1.into())
470 .local_frame2(local_frame2.into())
471 .build();
472
473 match params {
474 scene::joint::JointParams::BallJoint(v) => {
475 if v.x_limits_enabled {
476 joint.set_limits(
477 JointAxis::AngX,
478 [v.x_limits_angles.start, v.x_limits_angles.end],
479 );
480 }
481 if v.y_limits_enabled {
482 joint.set_limits(
483 JointAxis::AngY,
484 [v.y_limits_angles.start, v.y_limits_angles.end],
485 );
486 }
487 if v.z_limits_enabled {
488 joint.set_limits(
489 JointAxis::AngZ,
490 [v.z_limits_angles.start, v.z_limits_angles.end],
491 );
492 }
493 }
494 scene::joint::JointParams::FixedJoint(_) => {}
495 scene::joint::JointParams::PrismaticJoint(v) => {
496 if v.limits_enabled {
497 joint.set_limits(JointAxis::LinX, [v.limits.start, v.limits.end]);
498 }
499 }
500 scene::joint::JointParams::RevoluteJoint(v) => {
501 if v.limits_enabled {
502 joint.set_limits(JointAxis::AngX, [v.limits.start, v.limits.end]);
503 }
504 }
505 }
506
507 joint
508}
509
510fn make_trimesh(
513 owner_inv_transform: Matrix4<f32>,
514 owner: Handle<Node>,
515 sources: &[GeometrySource],
516 nodes: &NodePool,
517) -> Option<SharedShape> {
518 let mut mesh_builder = RawMeshBuilder::new(0, 0);
519
520 let root_inv_transform = owner_inv_transform;
526
527 for &source in sources {
528 if let Ok(mesh) = nodes.try_get_component_of_type::<Mesh>(source.0) {
529 let global_transform = root_inv_transform * mesh.global_transform();
530
531 for surface in mesh.surfaces() {
532 let shared_data = surface.data();
533 if !shared_data.is_ok() {
534 continue;
535 }
536 let shared_data = shared_data.data_ref();
537
538 let vertices = &shared_data.vertex_buffer;
539 for triangle in shared_data.geometry_buffer.iter() {
540 let a = RawVertex::from(
541 global_transform
542 .transform_point(&Point3::from(
543 vertices
544 .get(triangle[0] as usize)
545 .unwrap()
546 .read_3_f32(VertexAttributeUsage::Position)
547 .unwrap(),
548 ))
549 .coords,
550 );
551 let b = RawVertex::from(
552 global_transform
553 .transform_point(&Point3::from(
554 vertices
555 .get(triangle[1] as usize)
556 .unwrap()
557 .read_3_f32(VertexAttributeUsage::Position)
558 .unwrap(),
559 ))
560 .coords,
561 );
562 let c = RawVertex::from(
563 global_transform
564 .transform_point(&Point3::from(
565 vertices
566 .get(triangle[2] as usize)
567 .unwrap()
568 .read_3_f32(VertexAttributeUsage::Position)
569 .unwrap(),
570 ))
571 .coords,
572 );
573
574 mesh_builder.insert(a);
575 mesh_builder.insert(b);
576 mesh_builder.insert(c);
577 }
578 }
579 }
580 }
581
582 let raw_mesh = mesh_builder.build();
583
584 let vertices: Vec<Vec3> = raw_mesh
585 .vertices
586 .into_iter()
587 .map(|v| Vec3::new(v.x, v.y, v.z))
588 .collect();
589
590 let indices = raw_mesh
591 .triangles
592 .into_iter()
593 .map(|t| [t.0[0], t.0[1], t.0[2]])
594 .collect::<Vec<_>>();
595
596 if indices.is_empty() {
597 Log::writeln(
598 MessageKind::Warning,
599 format!(
600 "Failed to create triangle mesh collider for {}, it has no vertices!",
601 nodes[owner].name()
602 ),
603 );
604
605 SharedShape::trimesh(vec![Vec3::new(0.0, 0.0, 0.0)], vec![[0, 0, 0]]).ok()
606 } else {
607 SharedShape::trimesh(vertices, indices).ok()
608 }
609}
610
611fn make_polyhedron_shape(owner_inv_transform: Matrix4<f32>, mesh: &Mesh) -> SharedShape {
614 let mut mesh_builder = RawMeshBuilder::new(0, 0);
615
616 let root_inv_transform = owner_inv_transform;
622
623 let global_transform = root_inv_transform * mesh.global_transform();
624
625 for surface in mesh.surfaces() {
626 let shared_data = surface.data();
627 let shared_data = shared_data.data_ref();
628
629 let vertices = &shared_data.vertex_buffer;
630 for triangle in shared_data.geometry_buffer.iter() {
631 let a = RawVertex::from(
632 global_transform
633 .transform_point(&Point3::from(
634 vertices
635 .get(triangle[0] as usize)
636 .unwrap()
637 .read_3_f32(VertexAttributeUsage::Position)
638 .unwrap(),
639 ))
640 .coords,
641 );
642 let b = RawVertex::from(
643 global_transform
644 .transform_point(&Point3::from(
645 vertices
646 .get(triangle[1] as usize)
647 .unwrap()
648 .read_3_f32(VertexAttributeUsage::Position)
649 .unwrap(),
650 ))
651 .coords,
652 );
653 let c = RawVertex::from(
654 global_transform
655 .transform_point(&Point3::from(
656 vertices
657 .get(triangle[2] as usize)
658 .unwrap()
659 .read_3_f32(VertexAttributeUsage::Position)
660 .unwrap(),
661 ))
662 .coords,
663 );
664
665 mesh_builder.insert(a);
666 mesh_builder.insert(b);
667 mesh_builder.insert(c);
668 }
669 }
670
671 let raw_mesh = mesh_builder.build();
672
673 let vertices: Vec<Vec3> = raw_mesh
674 .vertices
675 .into_iter()
676 .map(|v| Vec3::new(v.x, v.y, v.z))
677 .collect();
678
679 let indices = raw_mesh
680 .triangles
681 .into_iter()
682 .map(|t| [t.0[0], t.0[1], t.0[2]])
683 .collect::<Vec<_>>();
684
685 SharedShape::convex_decomposition(&vertices, &indices)
686}
687
688fn make_heightfield(terrain: &Terrain) -> Option<SharedShape> {
690 assert!(!terrain.chunks_ref().is_empty());
691
692 let scale = terrain.local_transform().scale();
694
695 let height_map_size = terrain.height_map_size();
697 let chunk_size = height_map_size.map(|x| x - 3);
698 let chunk_min = terrain
699 .chunks_ref()
700 .iter()
701 .map(Chunk::grid_position)
702 .reduce(|a, b| a.inf(&b));
703 let chunk_max = terrain
704 .chunks_ref()
705 .iter()
706 .map(Chunk::grid_position)
707 .reduce(|a, b| a.sup(&b));
708 let (Some(chunk_min), Some(chunk_max)) = (chunk_min, chunk_max) else {
709 return None;
710 };
711 let row_range = chunk_max.y - chunk_min.y + 1;
712 let col_range = chunk_max.x - chunk_min.x + 1;
713 let nrows = chunk_size.y * row_range as u32 + 1;
714 let ncols = chunk_size.x * col_range as u32 + 1;
715
716 let mut data = vec![0.0; (nrows * ncols) as usize];
718 for chunk in terrain.chunks_ref() {
719 let texture = chunk.heightmap().data_ref();
720 let height_map = texture.data_of_type::<f32>().unwrap();
721 let pos = (chunk.grid_position() - chunk_min).map(|x| x as u32);
722 let (ox, oy) = (pos.x * chunk_size.x, pos.y * chunk_size.y);
723 for iy in 0..height_map_size.y - 2 {
724 for ix in 0..height_map_size.x - 2 {
725 let (x, y) = (ix + 1, iy + 1);
726 let value = height_map[(y * height_map_size.x + x) as usize] * scale.y;
727 data[((ox + ix) * nrows + oy + iy) as usize] = value;
728 }
729 }
730 }
731 let x_scale = terrain.chunk_size().x * scale.x * col_range as f32;
732 let z_scale = terrain.chunk_size().y * scale.z * row_range as f32;
733 let mut hf = HeightField::new(
734 Array2::new(nrows as usize, ncols as usize, data),
735 Vec3::new(x_scale, 1.0, z_scale),
736 );
737 if terrain.holes_enabled() {
738 let hole_mask_size = terrain.hole_mask_size();
739 for chunk in terrain.chunks_ref() {
740 let texture = chunk.hole_mask().map(|t| t.data_ref());
741 let hole_mask = texture.as_ref().map(|t| t.data_of_type::<u8>().unwrap());
742 let pos = (chunk.grid_position() - chunk_min).map(|x| x as u32);
743 let (ox, oy) = (pos.x * chunk_size.x, pos.y * chunk_size.y);
744
745 for iy in 0..hole_mask_size.y {
746 for ix in 0..hole_mask_size.x {
747 let is_hole = hole_mask
748 .map(|m| m[(iy * hole_mask_size.x + ix) as usize] < 128)
749 .unwrap_or_default();
750 let (x, y) = (ox + ix, oy + iy);
751 if is_hole {
752 hf.set_cell_status(
753 y as usize,
754 x as usize,
755 HeightFieldCellStatus::CELL_REMOVED,
756 );
757 } else {
758 hf.set_cell_status(y as usize, x as usize, HeightFieldCellStatus::empty());
759 }
760 }
761 }
762 }
763 }
764 Some(SharedShape::new(hf))
767}
768
769fn collider_shape_into_native_shape(
771 shape: &ColliderShape,
772 owner_inv_global_transform: Matrix4<f32>,
773 owner_collider: Handle<Node>,
774 pool: &NodePool,
775) -> Option<SharedShape> {
776 match shape {
777 ColliderShape::Ball(ball) => Some(SharedShape::ball(ball.radius)),
778
779 ColliderShape::Cylinder(cylinder) => {
780 Some(SharedShape::cylinder(cylinder.half_height, cylinder.radius))
781 }
782 ColliderShape::Cone(cone) => Some(SharedShape::cone(cone.half_height, cone.radius)),
783 ColliderShape::Cuboid(cuboid) => Some(SharedShape(Arc::new(Cuboid::new(
784 cuboid.half_extents.into(),
785 )))),
786 ColliderShape::Capsule(capsule) => Some(SharedShape::capsule(
787 capsule.begin.into(),
788 capsule.end.into(),
789 capsule.radius,
790 )),
791 ColliderShape::Segment(segment) => Some(SharedShape::segment(
792 segment.begin.into(),
793 segment.end.into(),
794 )),
795 ColliderShape::Triangle(triangle) => Some(SharedShape::triangle(
796 triangle.a.into(),
797 triangle.b.into(),
798 triangle.c.into(),
799 )),
800 ColliderShape::Trimesh(trimesh) => {
801 if trimesh.sources.is_empty() {
802 None
803 } else {
804 make_trimesh(
805 owner_inv_global_transform,
806 owner_collider,
807 &trimesh.sources,
808 pool,
809 )
810 }
811 }
812 ColliderShape::Heightfield(heightfield) => pool
813 .try_get_component_of_type::<Terrain>(heightfield.geometry_source.0)
814 .ok()
815 .and_then(make_heightfield),
816 ColliderShape::Polyhedron(polyhedron) => pool
817 .try_get_component_of_type::<Mesh>(polyhedron.geometry_source.0)
818 .ok()
819 .map(|mesh| make_polyhedron_shape(owner_inv_global_transform, mesh)),
820 }
821}
822
823#[derive(Copy, Clone, Visit, Reflect, Debug, PartialEq)]
830#[visit(optional)]
831pub struct IntegrationParameters {
832 #[reflect(min_value = 0.0)]
835 pub dt: Option<f32>,
836
837 #[reflect(min_value = 0.0)]
846 pub min_ccd_dt: f32,
847
848 #[reflect(min_value = 0.0)]
850 pub allowed_linear_error: f32,
851
852 #[reflect(min_value = 0.0)]
854 pub normalized_max_corrective_velocity: f32,
855
856 #[reflect(min_value = 0.0)]
858 pub prediction_distance: f32,
859
860 #[reflect(min_value = 0.0)]
862 pub num_solver_iterations: usize,
863
864 #[reflect(min_value = 0.0)]
866 pub num_internal_pgs_iterations: usize,
867
868 #[reflect(min_value = 0.0)]
870 pub min_island_size: u32,
871
872 #[reflect(min_value = 0.0)]
874 pub max_ccd_substeps: u32,
875
876 pub warmstart_coefficient: f32,
879
880 pub length_unit: f32,
888
889 pub num_internal_stabilization_iterations: usize,
891}
892
893impl Default for IntegrationParameters {
894 fn default() -> Self {
895 Self {
896 dt: None,
897 min_ccd_dt: 1.0 / 60.0 / 100.0,
898 warmstart_coefficient: 1.0,
899 allowed_linear_error: 0.002,
900 normalized_max_corrective_velocity: 10.0,
901 prediction_distance: 0.002,
902 num_internal_pgs_iterations: 1,
903 num_solver_iterations: 4,
904 min_island_size: 128,
905 max_ccd_substeps: 4,
906 length_unit: 1.0,
907 num_internal_stabilization_iterations: 4,
908 }
909 }
910}
911
912#[derive(Visit, Reflect)]
916pub struct PhysicsWorld {
917 pub enabled: InheritableVariable<bool>,
919
920 #[visit(optional)]
922 pub integration_parameters: InheritableVariable<IntegrationParameters>,
923
924 pub gravity: InheritableVariable<Vector3<f32>>,
926
927 #[visit(skip)]
929 #[reflect(hidden)]
930 pub performance_statistics: PhysicsPerformanceStatistics,
931
932 #[visit(skip)]
934 #[reflect(hidden)]
935 pipeline: PhysicsPipeline,
936 #[visit(skip)]
938 #[reflect(hidden)]
939 broad_phase: DefaultBroadPhase,
940 #[visit(skip)]
942 #[reflect(hidden)]
943 narrow_phase: NarrowPhase,
944 #[visit(skip)]
946 #[reflect(hidden)]
947 ccd_solver: CCDSolver,
948 #[visit(skip)]
951 #[reflect(hidden)]
952 islands: IslandManager,
953 #[visit(skip)]
955 #[reflect(hidden)]
956 bodies: RigidBodySet,
957 #[visit(skip)]
959 #[reflect(hidden)]
960 pub(crate) colliders: ColliderSet,
961 #[visit(skip)]
963 #[reflect(hidden)]
964 joints: Container<ImpulseJointSet, ImpulseJointHandle>,
965 #[visit(skip)]
967 #[reflect(hidden)]
968 multibody_joints: Container<MultibodyJointSet, MultibodyJointHandle>,
969 #[visit(skip)]
971 #[reflect(hidden)]
972 event_handler: Box<dyn EventHandler>,
973 #[visit(skip)]
974 #[reflect(hidden)]
975 debug_render_pipeline: Mutex<DebugRenderPipeline>,
976}
977
978impl Clone for PhysicsWorld {
979 fn clone(&self) -> Self {
980 PhysicsWorld {
981 enabled: self.enabled.clone(),
982 integration_parameters: self.integration_parameters.clone(),
983 gravity: self.gravity.clone(),
984 ..Default::default()
985 }
986 }
987}
988
989fn isometry_from_global_transform(transform: &Matrix4<f32>) -> Isometry3<f32> {
990 Isometry3 {
991 translation: Translation3::new(transform[12], transform[13], transform[14]),
992 rotation: UnitQuaternion::from_matrix_eps(
993 &transform.basis(),
994 f32::EPSILON,
995 16,
996 UnitQuaternion::identity(),
997 ),
998 }
999}
1000
1001fn calculate_local_frames(
1002 joint: &dyn NodeTrait,
1003 body1: &dyn NodeTrait,
1004 body2: &dyn NodeTrait,
1005) -> (Isometry3<f32>, Isometry3<f32>) {
1006 let joint_isometry = isometry_from_global_transform(&joint.global_transform());
1007
1008 (
1009 isometry_from_global_transform(&body1.global_transform()).inverse() * joint_isometry,
1010 isometry_from_global_transform(&body2.global_transform()).inverse() * joint_isometry,
1011 )
1012}
1013
1014fn u32_to_group(v: u32) -> rapier3d::geometry::Group {
1015 rapier3d::geometry::Group::from_bits(v).unwrap_or_else(rapier3d::geometry::Group::all)
1016}
1017
1018#[derive(Copy, Clone, Default)]
1020#[allow(clippy::type_complexity)]
1021pub struct QueryFilter<'a> {
1022 pub flags: collider::QueryFilterFlags,
1024 pub groups: Option<collider::InteractionGroups>,
1027 pub exclude_collider: Option<Handle<Node>>,
1029 pub exclude_rigid_body: Option<Handle<Node>>,
1031 pub predicate: Option<&'a dyn Fn(Handle<Node>, &collider::Collider) -> bool>,
1033}
1034
1035#[derive(Copy, Clone, Debug)]
1037pub struct TOI {
1038 pub toi: f32,
1040 pub witness1: Point3<f32>,
1044 pub witness2: Point3<f32>,
1048 pub normal1: UnitVector3<f32>,
1052 pub normal2: UnitVector3<f32>,
1056 pub status: collider::TOIStatus,
1058}
1059
1060impl PhysicsWorld {
1061 pub(super) fn new() -> Self {
1063 Self {
1064 enabled: true.into(),
1065 pipeline: PhysicsPipeline::new(),
1066 gravity: Vector3::new(0.0, -9.81, 0.0).into(),
1067 integration_parameters: IntegrationParameters::default().into(),
1068 broad_phase: DefaultBroadPhase::new(),
1069 narrow_phase: NarrowPhase::new(),
1070 ccd_solver: CCDSolver::new(),
1071 islands: IslandManager::new(),
1072 bodies: RigidBodySet::new(),
1073 colliders: ColliderSet::new(),
1074 joints: Container {
1075 set: ImpulseJointSet::new(),
1076 map: Default::default(),
1077 },
1078 multibody_joints: Container {
1079 set: MultibodyJointSet::new(),
1080 map: Default::default(),
1081 },
1082 event_handler: Box::new(()),
1083 performance_statistics: Default::default(),
1084 debug_render_pipeline: Default::default(),
1085 }
1086 }
1087
1088 pub(super) fn update(&mut self, dt: f32, dt_enabled: bool) {
1099 let time = instant::Instant::now();
1100 let parameter_dt = self.integration_parameters.dt;
1101 let parameter_dt = if parameter_dt == Some(0.0) {
1102 None
1103 } else {
1104 parameter_dt
1105 };
1106 let dt = if dt_enabled {
1107 parameter_dt.unwrap_or(dt)
1108 } else {
1109 0.0
1110 };
1111
1112 if *self.enabled {
1113 let integration_parameters = rapier3d::dynamics::IntegrationParameters {
1114 dt,
1115 min_ccd_dt: self.integration_parameters.min_ccd_dt,
1116 contact_softness: rapier3d::dynamics::SpringCoefficients::contact_defaults(),
1117 warmstart_coefficient: self.integration_parameters.warmstart_coefficient,
1118 length_unit: self.integration_parameters.length_unit,
1119 normalized_allowed_linear_error: self.integration_parameters.allowed_linear_error,
1120 normalized_max_corrective_velocity: self
1121 .integration_parameters
1122 .normalized_max_corrective_velocity,
1123 normalized_prediction_distance: self.integration_parameters.prediction_distance,
1124 num_solver_iterations: self.integration_parameters.num_solver_iterations,
1125 num_internal_pgs_iterations: self
1126 .integration_parameters
1127 .num_internal_pgs_iterations,
1128 num_internal_stabilization_iterations: self
1129 .integration_parameters
1130 .num_internal_stabilization_iterations,
1131 min_island_size: self.integration_parameters.min_island_size as usize,
1132 max_ccd_substeps: self.integration_parameters.max_ccd_substeps as usize,
1133 friction_model: FrictionModel::default(),
1134 };
1135
1136 self.pipeline.step(
1137 (*self.gravity).into(),
1138 &integration_parameters,
1139 &mut self.islands,
1140 &mut self.broad_phase,
1141 &mut self.narrow_phase,
1142 &mut self.bodies,
1143 &mut self.colliders,
1144 &mut self.joints.set,
1145 &mut self.multibody_joints.set,
1146 &mut self.ccd_solver,
1147 &(),
1148 &*self.event_handler,
1149 );
1150 }
1151
1152 self.performance_statistics.step_time += instant::Instant::now() - time;
1153 }
1154
1155 pub(super) fn add_body(&mut self, owner: Handle<Node>, mut body: RigidBody) -> RigidBodyHandle {
1156 body.user_data = owner.encode_to_u128();
1157 self.bodies.insert(body)
1158 }
1159
1160 pub(crate) fn remove_body(&mut self, handle: RigidBodyHandle) {
1161 self.bodies.remove(
1162 handle,
1163 &mut self.islands,
1164 &mut self.colliders,
1165 &mut self.joints.set,
1166 &mut self.multibody_joints.set,
1167 true,
1168 );
1169 }
1170
1171 pub(super) fn add_collider(
1172 &mut self,
1173 owner: Handle<Node>,
1174 parent_body: RigidBodyHandle,
1175 mut collider: Collider,
1176 ) -> ColliderHandle {
1177 collider.user_data = owner.encode_to_u128();
1178 self.colliders
1179 .insert_with_parent(collider, parent_body, &mut self.bodies)
1180 }
1181
1182 pub(crate) fn remove_collider(&mut self, handle: ColliderHandle) -> bool {
1183 self.colliders
1184 .remove(handle, &mut self.islands, &mut self.bodies, false)
1185 .is_some()
1186 }
1187
1188 pub(super) fn add_joint(
1189 &mut self,
1190 owner: Handle<Node>,
1191 body1: RigidBodyHandle,
1192 body2: RigidBodyHandle,
1193 joint: GenericJoint,
1194 ) -> ImpulseJointHandle {
1195 let handle = self.joints.set.insert(body1, body2, joint, false);
1196 self.joints.map.insert(handle, owner);
1197 handle
1198 }
1199
1200 pub(crate) fn remove_joint(&mut self, handle: ImpulseJointHandle) {
1201 if self.joints.set.remove(handle, false).is_some() {
1202 assert!(self.joints.map.remove_by_key(&handle).is_some());
1203 }
1204 }
1205
1206 pub fn draw(&self, context: &mut SceneDrawingContext) {
1209 self.debug_render_pipeline.safe_lock().render(
1210 context,
1211 &self.bodies,
1212 &self.colliders,
1213 &self.joints.set,
1214 &self.multibody_joints.set,
1215 &self.narrow_phase,
1216 );
1217 }
1218
1219 pub fn cast_ray<S: QueryResultsStorage>(&self, opts: RayCastOptions, query_buffer: &mut S) {
1221 let time = instant::Instant::now();
1222
1223 let query = self.broad_phase.as_query_pipeline(
1224 &DefaultQueryDispatcher,
1225 &self.bodies,
1226 &self.colliders,
1227 rapier3d::pipeline::QueryFilter::new().groups(InteractionGroups::new(
1228 u32_to_group(opts.groups.memberships.0),
1229 u32_to_group(opts.groups.filter.0),
1230 Default::default(),
1231 )),
1232 );
1233
1234 query_buffer.clear();
1235 let ray = Ray::new(
1236 opts.ray_origin.into(),
1237 opts.ray_direction
1238 .try_normalize(f32::EPSILON)
1239 .unwrap_or_default()
1240 .into(),
1241 );
1242 for (_, collider, intersection) in query.intersect_ray(ray, opts.max_len, true) {
1243 query_buffer.push(Intersection {
1244 collider: Handle::decode_from_u128(collider.user_data),
1245 normal: intersection.normal.into(),
1246 position: ray.point_at(intersection.time_of_impact).into(),
1247 feature: intersection.feature.into(),
1248 toi: intersection.time_of_impact,
1249 });
1250 }
1251 if opts.sort_results {
1252 query_buffer.sort_intersections_by(|a, b| {
1253 if a.toi > b.toi {
1254 Ordering::Greater
1255 } else if a.toi < b.toi {
1256 Ordering::Less
1257 } else {
1258 Ordering::Equal
1259 }
1260 })
1261 }
1262
1263 self.performance_statistics.total_ray_cast_time.set(
1264 self.performance_statistics.total_ray_cast_time.get()
1265 + (instant::Instant::now() - time),
1266 );
1267 }
1268
1269 pub fn cast_shape(
1289 &self,
1290 graph: &Graph,
1291 shape: &dyn Shape,
1292 shape_pos: &Isometry3<f32>,
1293 shape_vel: &Vector3<f32>,
1294 max_toi: f32,
1295 stop_at_penetration: bool,
1296 filter: QueryFilter,
1297 ) -> Option<(Handle<Node>, TOI)> {
1298 let predicate = |handle: ColliderHandle, _: &Collider| -> bool {
1299 if let Some(pred) = filter.predicate {
1300 let h = Handle::decode_from_u128(self.colliders.get(handle).unwrap().user_data);
1301 pred(
1302 h,
1303 graph.node(h).component_ref::<collider::Collider>().unwrap(),
1304 )
1305 } else {
1306 true
1307 }
1308 };
1309
1310 let filter = rapier3d::pipeline::QueryFilter {
1311 flags: rapier3d::pipeline::QueryFilterFlags::from_bits(filter.flags.bits()).unwrap(),
1312 groups: filter.groups.map(|g| {
1313 InteractionGroups::new(
1314 u32_to_group(g.memberships.0),
1315 u32_to_group(g.filter.0),
1316 Default::default(),
1317 )
1318 }),
1319 exclude_collider: filter
1320 .exclude_collider
1321 .and_then(|h| graph.try_get_of_type::<collider::Collider>(h).ok())
1322 .map(|c| c.native.get()),
1323 exclude_rigid_body: filter
1324 .exclude_collider
1325 .and_then(|h| graph.try_get_of_type::<rigidbody::RigidBody>(h).ok())
1326 .map(|c| c.native.get()),
1327 predicate: Some(&predicate),
1328 };
1329
1330 let query = self.broad_phase.as_query_pipeline(
1331 &DefaultQueryDispatcher,
1332 &self.bodies,
1333 &self.colliders,
1334 filter,
1335 );
1336
1337 let opts = ShapeCastOptions {
1338 max_time_of_impact: max_toi,
1339 target_distance: 0.0,
1340 stop_at_penetration,
1341 compute_impact_geometry_on_penetration: true,
1342 };
1343
1344 query
1345 .cast_shape(&Pose3::from(*shape_pos), (*shape_vel).into(), shape, opts)
1346 .map(|(handle, toi)| {
1347 (
1348 Handle::decode_from_u128(self.colliders.get(handle).unwrap().user_data),
1349 TOI {
1350 toi: toi.time_of_impact,
1351 witness1: toi.witness1.into(),
1352 witness2: toi.witness2.into(),
1353 normal1: UnitVector3::new_normalize(toi.normal1.into()),
1354 normal2: UnitVector3::new_normalize(toi.normal2.into()),
1355 status: toi.status.into(),
1356 },
1357 )
1358 })
1359 }
1360
1361 pub(crate) fn set_rigid_body_position(
1362 &mut self,
1363 rigid_body: &scene::rigidbody::RigidBody,
1364 new_global_transform: &Matrix4<f32>,
1365 ) {
1366 if let Some(native) = self.bodies.get_mut(rigid_body.native.get()) {
1367 native.set_position(
1368 isometry_from_global_transform(new_global_transform).into(),
1369 false,
1372 );
1373 }
1374 }
1375
1376 pub(crate) fn sync_rigid_body_node(
1377 &mut self,
1378 rigid_body: &mut scene::rigidbody::RigidBody,
1379 parent_transform: Matrix4<f32>,
1380 ) {
1381 if *self.enabled {
1382 if let Some(native) = self.bodies.get(rigid_body.native.get()) {
1383 if native.body_type() != RigidBodyType::Fixed {
1384 let local_transform: Matrix4<f32> = parent_transform
1385 .try_inverse()
1386 .unwrap_or_else(Matrix4::identity)
1387 * Isometry3::from(*native.position()).to_homogeneous();
1388
1389 let new_local_rotation = UnitQuaternion::from_matrix_eps(
1390 &local_transform.basis(),
1391 f32::EPSILON,
1392 16,
1393 UnitQuaternion::identity(),
1394 );
1395 let new_local_position = Vector3::new(
1396 local_transform[12],
1397 local_transform[13],
1398 local_transform[14],
1399 );
1400
1401 let local_transform = rigid_body.local_transform();
1405 if **local_transform.position() != new_local_position
1406 || **local_transform.rotation() != new_local_rotation
1407 {
1408 rigid_body
1409 .local_transform_mut()
1410 .set_position(new_local_position)
1411 .set_rotation(new_local_rotation);
1412 }
1413
1414 rigid_body
1415 .lin_vel
1416 .set_value_with_flags(native.linvel().into(), VariableFlags::MODIFIED);
1417 rigid_body
1418 .ang_vel
1419 .set_value_with_flags(native.angvel().into(), VariableFlags::MODIFIED);
1420 rigid_body.sleeping = native.is_sleeping();
1421 }
1422 }
1423 }
1424 }
1425
1426 pub(crate) fn sync_to_rigid_body_node(
1427 &mut self,
1428 handle: Handle<Node>,
1429 rigid_body_node: &scene::rigidbody::RigidBody,
1430 ) {
1431 if !rigid_body_node.is_globally_enabled() {
1432 self.remove_body(rigid_body_node.native.get());
1433 rigid_body_node.native.set(Default::default());
1434 return;
1435 }
1436
1437 if rigid_body_node.native.get() != RigidBodyHandle::invalid() {
1441 let mut actions = rigid_body_node.actions.safe_lock();
1442 if rigid_body_node.need_sync_model() || !actions.is_empty() {
1443 if let Some(native) = self.bodies.get_mut(rigid_body_node.native.get()) {
1444 rigid_body_node
1447 .body_type
1448 .try_sync_model(|v| native.set_body_type(v.into(), false));
1449 rigid_body_node
1450 .lin_vel
1451 .try_sync_model(|v| native.set_linvel(v.into(), true));
1452 rigid_body_node
1453 .ang_vel
1454 .try_sync_model(|v| native.set_angvel(v.into(), true));
1455 rigid_body_node.mass.try_sync_model(|v| {
1456 match *rigid_body_node.mass_properties_type {
1457 RigidBodyMassPropertiesType::Default => {
1458 native.set_additional_mass(v, false);
1459 }
1460 RigidBodyMassPropertiesType::Additional {
1461 center_of_mass,
1462 principal_inertia,
1463 } => {
1464 native.set_additional_mass_properties(
1465 MassProperties::new(
1466 center_of_mass.into(),
1467 v,
1468 principal_inertia.into(),
1469 ),
1470 false,
1471 );
1472 }
1473 };
1474 });
1475 rigid_body_node.mass_properties_type.try_sync_model(|v| {
1476 match v {
1477 RigidBodyMassPropertiesType::Default => {
1478 native.set_additional_mass(*rigid_body_node.mass, false);
1479 }
1480 RigidBodyMassPropertiesType::Additional {
1481 center_of_mass,
1482 principal_inertia,
1483 } => {
1484 native.set_additional_mass_properties(
1485 MassProperties::new(
1486 center_of_mass.into(),
1487 *rigid_body_node.mass,
1488 principal_inertia.into(),
1489 ),
1490 false,
1491 );
1492 }
1493 };
1494 });
1495 rigid_body_node
1496 .lin_damping
1497 .try_sync_model(|v| native.set_linear_damping(v));
1498 rigid_body_node
1499 .ang_damping
1500 .try_sync_model(|v| native.set_angular_damping(v));
1501 rigid_body_node
1502 .ccd_enabled
1503 .try_sync_model(|v| native.enable_ccd(v));
1504 rigid_body_node.can_sleep.try_sync_model(|v| {
1505 if v {
1506 *native.activation_mut() = RigidBodyActivation::active();
1507 } else {
1508 *native.activation_mut() = RigidBodyActivation::cannot_sleep();
1509 };
1510 });
1511 rigid_body_node
1512 .translation_locked
1513 .try_sync_model(|v| native.lock_translations(v, false));
1514 rigid_body_node.x_rotation_locked.try_sync_model(|v| {
1515 native.set_enabled_rotations(
1516 !v,
1517 !native.is_rotation_locked()[1],
1518 !native.is_rotation_locked()[2],
1519 false,
1520 );
1521 });
1522 rigid_body_node.y_rotation_locked.try_sync_model(|v| {
1523 native.set_enabled_rotations(
1524 !native.is_rotation_locked()[0],
1525 !v,
1526 !native.is_rotation_locked()[2],
1527 false,
1528 );
1529 });
1530 rigid_body_node.z_rotation_locked.try_sync_model(|v| {
1531 native.set_enabled_rotations(
1532 !native.is_rotation_locked()[0],
1533 !native.is_rotation_locked()[1],
1534 !v,
1535 false,
1536 );
1537 });
1538 rigid_body_node
1539 .dominance
1540 .try_sync_model(|v| native.set_dominance_group(v));
1541 rigid_body_node
1542 .gravity_scale
1543 .try_sync_model(|v| native.set_gravity_scale(v, false));
1544
1545 if rigid_body_node.reset_forces.replace(false) {
1548 native.reset_forces(false);
1549 native.reset_torques(false);
1550 }
1551
1552 while let Some(action) = actions.pop_front() {
1553 match action {
1554 ApplyAction::Force(force) => {
1555 native.add_force(force.into(), false);
1556 rigid_body_node.reset_forces.set(true);
1557 }
1558 ApplyAction::Torque(torque) => {
1559 native.add_torque(torque.into(), false);
1560 rigid_body_node.reset_forces.set(true);
1561 }
1562 ApplyAction::ForceAtPoint { force, point } => {
1563 native.add_force_at_point(force.into(), point.into(), false);
1564 rigid_body_node.reset_forces.set(true);
1565 }
1566 ApplyAction::Impulse(impulse) => {
1567 native.apply_impulse(impulse.into(), false)
1568 }
1569 ApplyAction::TorqueImpulse(impulse) => {
1570 native.apply_torque_impulse(impulse.into(), false)
1571 }
1572 ApplyAction::ImpulseAtPoint { impulse, point } => {
1573 native.apply_impulse_at_point(impulse.into(), point.into(), false)
1574 }
1575 ApplyAction::WakeUp => native.wake_up(true),
1576 ApplyAction::NextTranslation(position) => {
1577 native.set_next_kinematic_translation(position.into())
1578 }
1579 ApplyAction::NextRotation(rotation) => {
1580 native.set_next_kinematic_rotation(rotation.into())
1581 }
1582 ApplyAction::NextPosition(position) => {
1583 native.set_next_kinematic_position(position.into())
1584 }
1585 }
1586 }
1587 }
1588 }
1589 } else {
1590 let mut builder = RigidBodyBuilder::new(rigid_body_node.body_type().into())
1591 .pose(isometry_from_global_transform(&rigid_body_node.global_transform()).into())
1592 .ccd_enabled(rigid_body_node.is_ccd_enabled())
1593 .additional_mass(rigid_body_node.mass())
1594 .angvel((*rigid_body_node.ang_vel).into())
1595 .linvel((*rigid_body_node.lin_vel).into())
1596 .linear_damping(*rigid_body_node.lin_damping)
1597 .angular_damping(*rigid_body_node.ang_damping)
1598 .can_sleep(rigid_body_node.is_can_sleep())
1599 .dominance_group(rigid_body_node.dominance())
1600 .gravity_scale(rigid_body_node.gravity_scale())
1601 .enabled_rotations(
1602 !rigid_body_node.is_x_rotation_locked(),
1603 !rigid_body_node.is_y_rotation_locked(),
1604 !rigid_body_node.is_z_rotation_locked(),
1605 );
1606
1607 match *rigid_body_node.mass_properties_type {
1608 RigidBodyMassPropertiesType::Default => {
1609 builder = builder.additional_mass(*rigid_body_node.mass);
1610 }
1611 RigidBodyMassPropertiesType::Additional {
1612 center_of_mass,
1613 principal_inertia,
1614 } => {
1615 builder = builder.additional_mass_properties(MassProperties::new(
1616 center_of_mass.into(),
1617 *rigid_body_node.mass,
1618 principal_inertia.into(),
1619 ));
1620 }
1621 };
1622 if rigid_body_node.is_translation_locked() {
1623 builder = builder.lock_translations();
1624 }
1625
1626 rigid_body_node
1627 .native
1628 .set(self.add_body(handle, builder.build()));
1629
1630 Log::writeln(
1631 MessageKind::Information,
1632 format!(
1633 "Native rigid body was created for node {}",
1634 rigid_body_node.name()
1635 ),
1636 );
1637 }
1638 }
1639
1640 pub(crate) fn sync_to_collider_node(
1641 &mut self,
1642 nodes: &NodePool,
1643 handle: Handle<Node>,
1644 collider_node: &scene::collider::Collider,
1645 ) {
1646 if !collider_node.is_globally_enabled() {
1647 self.remove_collider(collider_node.native.get());
1648 collider_node.native.set(Default::default());
1649 return;
1650 }
1651
1652 let anything_changed = collider_node.needs_sync_model();
1653
1654 if collider_node.native.get() != ColliderHandle::invalid() {
1660 if anything_changed {
1661 if let Some(native) = self.colliders.get_mut(collider_node.native.get()) {
1662 collider_node
1663 .restitution
1664 .try_sync_model(|v| native.set_restitution(v));
1665 collider_node.collision_groups.try_sync_model(|v| {
1666 native.set_collision_groups(InteractionGroups::new(
1667 u32_to_group(v.memberships.0),
1668 u32_to_group(v.filter.0),
1669 Default::default(),
1670 ))
1671 });
1672 collider_node.solver_groups.try_sync_model(|v| {
1673 native.set_solver_groups(InteractionGroups::new(
1674 u32_to_group(v.memberships.0),
1675 u32_to_group(v.filter.0),
1676 Default::default(),
1677 ))
1678 });
1679 collider_node
1680 .friction
1681 .try_sync_model(|v| native.set_friction(v));
1682 collider_node
1683 .is_sensor
1684 .try_sync_model(|v| native.set_sensor(v));
1685 collider_node
1686 .friction_combine_rule
1687 .try_sync_model(|v| native.set_friction_combine_rule(v.into()));
1688 collider_node
1689 .restitution_combine_rule
1690 .try_sync_model(|v| native.set_restitution_combine_rule(v.into()));
1691 let mut remove_collider = false;
1692 collider_node.shape.try_sync_model(|v| {
1693 let inv_global_transform = isometric_global_transform(nodes, handle)
1694 .try_inverse()
1695 .unwrap_or_default();
1696
1697 if let Some(shape) = collider_shape_into_native_shape(
1698 &v,
1699 inv_global_transform,
1700 handle,
1701 nodes,
1702 ) {
1703 native.set_shape(shape);
1704 } else {
1705 remove_collider = true;
1706 }
1707 });
1708 if remove_collider {
1709 self.remove_collider(collider_node.native.get());
1710 collider_node.native.set(ColliderHandle::invalid());
1711 }
1712 }
1713 }
1714 } else if let Ok(parent_body) =
1715 nodes.try_get_component_of_type::<scene::rigidbody::RigidBody>(collider_node.parent())
1716 {
1717 if parent_body.native.get() != RigidBodyHandle::invalid() {
1718 let inv_global_transform = isometric_global_transform(nodes, handle)
1719 .try_inverse()
1720 .unwrap();
1721 let rigid_body_native = parent_body.native.get();
1722 if let Some(shape) = collider_shape_into_native_shape(
1723 collider_node.shape(),
1724 inv_global_transform,
1725 handle,
1726 nodes,
1727 ) {
1728 let mut builder = ColliderBuilder::new(shape)
1729 .position(
1730 Isometry3 {
1731 rotation: **collider_node.local_transform().rotation(),
1732 translation: Translation3 {
1733 vector: **collider_node.local_transform().position(),
1734 },
1735 }
1736 .into(),
1737 )
1738 .friction(collider_node.friction())
1739 .restitution(collider_node.restitution())
1740 .collision_groups(InteractionGroups::new(
1741 u32_to_group(collider_node.collision_groups().memberships.0),
1742 u32_to_group(collider_node.collision_groups().filter.0),
1743 Default::default(),
1744 ))
1745 .friction_combine_rule(collider_node.friction_combine_rule().into())
1746 .restitution_combine_rule(collider_node.restitution_combine_rule().into())
1747 .solver_groups(InteractionGroups::new(
1748 u32_to_group(collider_node.solver_groups().memberships.0),
1749 u32_to_group(collider_node.solver_groups().filter.0),
1750 Default::default(),
1751 ))
1752 .sensor(collider_node.is_sensor());
1753
1754 if let Some(density) = collider_node.density() {
1755 builder = builder.density(density);
1756 }
1757
1758 let native_handle =
1759 self.add_collider(handle, rigid_body_native, builder.build());
1760
1761 collider_node.native.set(native_handle);
1762
1763 Log::writeln(
1764 MessageKind::Information,
1765 format!(
1766 "Native collider was created for node {}",
1767 collider_node.name()
1768 ),
1769 );
1770 }
1771 }
1772 }
1773 }
1774
1775 pub(crate) fn sync_to_joint_node(
1776 &mut self,
1777 nodes: &NodePool,
1778 handle: Handle<Node>,
1779 joint: &scene::joint::Joint,
1780 ) {
1781 if !joint.is_globally_enabled() {
1782 self.remove_joint(joint.native.get());
1783 joint.native.set(ImpulseJointHandle(Default::default()));
1784 return;
1785 }
1786
1787 if let Some(native) = self.joints.set.get_mut(joint.native.get(), false) {
1788 joint.body1.try_sync_model(|v| {
1789 if let Ok(rigid_body_node) = nodes.try_get(v) {
1790 native.body1 = rigid_body_node.native.get();
1791 }
1792 });
1793 joint.body2.try_sync_model(|v| {
1794 if let Ok(rigid_body_node) = nodes.try_get(v) {
1795 native.body2 = rigid_body_node.native.get();
1796 }
1797 });
1798 joint.params.try_sync_model(|v| {
1799 native.data =
1800 convert_joint_params(v, native.data.local_frame1.into(), native.data.local_frame2.into())
1802 });
1803 joint.motor_params.try_sync_model(|v|{
1804 let joint_axes: &[JointAxis] = match joint.params.get_value_ref(){
1807 JointParams::PrismaticJoint(_) => &[JointAxis::LinX][..], JointParams::RevoluteJoint(_) => &[JointAxis::AngX][..],
1809 JointParams::BallJoint(_) => &[JointAxis::AngX, JointAxis::AngY, JointAxis::AngZ][..],
1810 _ => {
1811 Log::warn("Try to modify motor parameters for unsupported joint type, this operation will be ignored.");
1812 return;
1813 }
1814 };
1815 for joint_axis in joint_axes {
1816 native.data.set_motor_model(*joint_axis, rapier3d::prelude::MotorModel::ForceBased);
1818 let JointMotorParams {
1819 target_vel,
1820 target_pos,
1821 stiffness,
1822 damping,
1823 max_force
1824 } = v;
1825 native.data.set_motor(*joint_axis, target_pos, target_vel, stiffness, damping);
1826 native.data.set_motor_max_force(*joint_axis, max_force);
1827 }
1828 let Some(body1) = self.bodies.get_mut(native.body1) else {
1832 return;
1833 };
1834 body1.wake_up(true);
1835 let Some(body2) = self.bodies.get_mut(native.body2) else {
1836 return;
1837 };
1838 body2.wake_up(true);
1839 });
1840 joint.contacts_enabled.try_sync_model(|v| {
1841 native.data.set_contacts_enabled(v);
1842 });
1843
1844 let mut local_frames = joint.local_frames.borrow_mut();
1845 if local_frames.is_none() {
1846 if let (Ok(body1), Ok(body2)) =
1847 (nodes.try_get(joint.body1()), nodes.try_get(joint.body2()))
1848 {
1849 let (local_frame1, local_frame2) = calculate_local_frames(joint, body1, body2);
1850 native.data =
1851 convert_joint_params((*joint.params).clone(), local_frame1, local_frame2);
1852 *local_frames = Some(JointLocalFrames::new(&local_frame1, &local_frame2));
1853 }
1854 }
1855 } else {
1856 let body1_handle = joint.body1();
1857 let body2_handle = joint.body2();
1858 let params = joint.params().clone();
1859
1860 if let (Some(body1), Some(body2)) = (
1863 nodes
1864 .try_get(body1_handle)
1865 .ok()
1866 .filter(|b| self.bodies.get(b.native.get()).is_some()),
1867 nodes
1868 .try_get(body2_handle)
1869 .ok()
1870 .filter(|b| self.bodies.get(b.native.get()).is_some()),
1871 ) {
1872 let native_body1 = body1.native.get();
1873 let native_body2 = body2.native.get();
1874
1875 if self.bodies.get(native_body1).is_none()
1876 || self.bodies.get(native_body2).is_none()
1877 {
1878 return;
1882 }
1883
1884 let mut local_frames = joint.local_frames.borrow_mut();
1886 let (local_frame1, local_frame2) = local_frames
1887 .clone()
1888 .map(|frames| {
1889 (
1890 Isometry3 {
1891 rotation: frames.body1.rotation,
1892 translation: Translation {
1893 vector: frames.body1.position,
1894 },
1895 },
1896 Isometry3 {
1897 rotation: frames.body2.rotation,
1898 translation: Translation {
1899 vector: frames.body2.position,
1900 },
1901 },
1902 )
1903 })
1904 .unwrap_or_else(|| calculate_local_frames(joint, body1, body2));
1905
1906 let mut native_joint = convert_joint_params(params, local_frame1, local_frame2);
1907 native_joint.contacts_enabled = joint.is_contacts_enabled();
1908 let native_handle =
1909 self.add_joint(handle, native_body1, native_body2, native_joint);
1910
1911 joint.native.set(native_handle);
1912 *local_frames = Some(JointLocalFrames::new(&local_frame1, &local_frame2));
1913
1914 Log::writeln(
1915 MessageKind::Information,
1916 format!("Native joint was created for node {}", joint.name()),
1917 );
1918 }
1919 }
1920 }
1921
1922 pub(crate) fn intersections_with(
1924 &self,
1925 collider: ColliderHandle,
1926 ) -> impl Iterator<Item = IntersectionPair> + '_ {
1927 self.narrow_phase
1928 .intersection_pairs_with(collider)
1929 .filter_map(|(collider1, collider2, intersecting)| {
1930 Some(IntersectionPair {
1931 collider1: Handle::decode_from_u128(self.colliders.get(collider1)?.user_data),
1932 collider2: Handle::decode_from_u128(self.colliders.get(collider2)?.user_data),
1933 has_any_active_contact: intersecting,
1934 })
1935 })
1936 }
1937
1938 pub(crate) fn contacts_with(
1940 &self,
1941 collider: ColliderHandle,
1942 ) -> impl Iterator<Item = ContactPair> + '_ {
1943 self.narrow_phase
1944 .contact_pairs_with(collider)
1947 .filter_map(|c| ContactPair::from_native(c, self))
1948 }
1949
1950 pub fn contacts(&self) -> impl Iterator<Item = ContactPair> + '_ {
1952 self.narrow_phase
1953 .contact_pairs()
1954 .filter_map(|c| ContactPair::from_native(c, self))
1955 }
1956}
1957
1958impl Default for PhysicsWorld {
1959 fn default() -> Self {
1960 Self::new()
1961 }
1962}
1963
1964impl Debug for PhysicsWorld {
1965 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1966 write!(f, "PhysicsWorld")
1967 }
1968}