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