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::{SceneGraph, 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<collider::Collider>,
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<rigidbody::RigidBody>,
329 pub rigid_body2: Handle<rigidbody::RigidBody>,
331 pub normal: Vector3<f32>,
333}
334
335#[derive(Debug, Clone, PartialEq)]
337pub struct ContactPair {
338 pub collider1: Handle<collider::Collider>,
340 pub collider2: Handle<collider::Collider>,
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<collider::Collider>) -> Handle<collider::Collider> {
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<collider::Collider>,
412 pub collider2: Handle<collider::Collider>,
414 pub has_any_active_contact: bool,
417}
418
419impl IntersectionPair {
420 #[inline]
426 pub fn other(&self, subject: Handle<collider::Collider>) -> Handle<collider::Collider> {
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 Ok(mesh) = nodes.try_get_component_of_type::<Mesh>(source.0) {
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_get_component_of_type::<Terrain>(heightfield.geometry_source.0)
804 .ok()
805 .and_then(make_heightfield),
806 ColliderShape::Polyhedron(polyhedron) => pool
807 .try_get_component_of_type::<Mesh>(polyhedron.geometry_source.0)
808 .ok()
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_of_type::<collider::Collider>(h).ok())
1338 .map(|c| c.native.get()),
1339 exclude_rigid_body: filter
1340 .exclude_collider
1341 .and_then(|h| graph.try_get_of_type::<rigidbody::RigidBody>(h).ok())
1342 .map(|c| c.native.get()),
1343 predicate: Some(&predicate),
1344 };
1345
1346 let query = self.broad_phase.as_query_pipeline(
1347 &DefaultQueryDispatcher,
1348 &self.bodies,
1349 &self.colliders,
1350 filter,
1351 );
1352
1353 let opts = ShapeCastOptions {
1354 max_time_of_impact: max_toi,
1355 target_distance: 0.0,
1356 stop_at_penetration,
1357 compute_impact_geometry_on_penetration: true,
1358 };
1359
1360 query
1361 .cast_shape(shape_pos, shape_vel, shape, opts)
1362 .map(|(handle, toi)| {
1363 (
1364 Handle::decode_from_u128(self.colliders.get(handle).unwrap().user_data),
1365 TOI {
1366 toi: toi.time_of_impact,
1367 witness1: toi.witness1,
1368 witness2: toi.witness2,
1369 normal1: toi.normal1,
1370 normal2: toi.normal2,
1371 status: toi.status.into(),
1372 },
1373 )
1374 })
1375 }
1376
1377 pub(crate) fn set_rigid_body_position(
1378 &mut self,
1379 rigid_body: &scene::rigidbody::RigidBody,
1380 new_global_transform: &Matrix4<f32>,
1381 ) {
1382 if let Some(native) = self.bodies.get_mut(rigid_body.native.get()) {
1383 native.set_position(
1384 isometry_from_global_transform(new_global_transform),
1385 false,
1388 );
1389 }
1390 }
1391
1392 pub(crate) fn sync_rigid_body_node(
1393 &mut self,
1394 rigid_body: &mut scene::rigidbody::RigidBody,
1395 parent_transform: Matrix4<f32>,
1396 ) {
1397 if *self.enabled {
1398 if let Some(native) = self.bodies.get(rigid_body.native.get()) {
1399 if native.body_type() != RigidBodyType::Fixed {
1400 let local_transform: Matrix4<f32> = parent_transform
1401 .try_inverse()
1402 .unwrap_or_else(Matrix4::identity)
1403 * native.position().to_homogeneous();
1404
1405 let new_local_rotation = UnitQuaternion::from_matrix_eps(
1406 &local_transform.basis(),
1407 f32::EPSILON,
1408 16,
1409 UnitQuaternion::identity(),
1410 );
1411 let new_local_position = Vector3::new(
1412 local_transform[12],
1413 local_transform[13],
1414 local_transform[14],
1415 );
1416
1417 let local_transform = rigid_body.local_transform();
1421 if **local_transform.position() != new_local_position
1422 || **local_transform.rotation() != new_local_rotation
1423 {
1424 rigid_body
1425 .local_transform_mut()
1426 .set_position(new_local_position)
1427 .set_rotation(new_local_rotation);
1428 }
1429
1430 rigid_body
1431 .lin_vel
1432 .set_value_with_flags(*native.linvel(), VariableFlags::MODIFIED);
1433 rigid_body
1434 .ang_vel
1435 .set_value_with_flags(*native.angvel(), VariableFlags::MODIFIED);
1436 rigid_body.sleeping = native.is_sleeping();
1437 }
1438 }
1439 }
1440 }
1441
1442 pub(crate) fn sync_to_rigid_body_node(
1443 &mut self,
1444 handle: Handle<Node>,
1445 rigid_body_node: &scene::rigidbody::RigidBody,
1446 ) {
1447 if !rigid_body_node.is_globally_enabled() {
1448 self.remove_body(rigid_body_node.native.get());
1449 rigid_body_node.native.set(Default::default());
1450 return;
1451 }
1452
1453 if rigid_body_node.native.get() != RigidBodyHandle::invalid() {
1457 let mut actions = rigid_body_node.actions.safe_lock();
1458 if rigid_body_node.need_sync_model() || !actions.is_empty() {
1459 if let Some(native) = self.bodies.get_mut(rigid_body_node.native.get()) {
1460 rigid_body_node
1463 .body_type
1464 .try_sync_model(|v| native.set_body_type(v.into(), false));
1465 rigid_body_node
1466 .lin_vel
1467 .try_sync_model(|v| native.set_linvel(v, true));
1468 rigid_body_node
1469 .ang_vel
1470 .try_sync_model(|v| native.set_angvel(v, true));
1471 rigid_body_node.mass.try_sync_model(|v| {
1472 match *rigid_body_node.mass_properties_type {
1473 RigidBodyMassPropertiesType::Default => {
1474 native.set_additional_mass(v, false);
1475 }
1476 RigidBodyMassPropertiesType::Additional {
1477 center_of_mass,
1478 principal_inertia,
1479 } => {
1480 native.set_additional_mass_properties(
1481 MassProperties::new(
1482 Point3::from(center_of_mass),
1483 v,
1484 principal_inertia,
1485 ),
1486 false,
1487 );
1488 }
1489 };
1490 });
1491 rigid_body_node.mass_properties_type.try_sync_model(|v| {
1492 match v {
1493 RigidBodyMassPropertiesType::Default => {
1494 native.set_additional_mass(*rigid_body_node.mass, false);
1495 }
1496 RigidBodyMassPropertiesType::Additional {
1497 center_of_mass,
1498 principal_inertia,
1499 } => {
1500 native.set_additional_mass_properties(
1501 MassProperties::new(
1502 Point3::from(center_of_mass),
1503 *rigid_body_node.mass,
1504 principal_inertia,
1505 ),
1506 false,
1507 );
1508 }
1509 };
1510 });
1511 rigid_body_node
1512 .lin_damping
1513 .try_sync_model(|v| native.set_linear_damping(v));
1514 rigid_body_node
1515 .ang_damping
1516 .try_sync_model(|v| native.set_angular_damping(v));
1517 rigid_body_node
1518 .ccd_enabled
1519 .try_sync_model(|v| native.enable_ccd(v));
1520 rigid_body_node.can_sleep.try_sync_model(|v| {
1521 if v {
1522 *native.activation_mut() = RigidBodyActivation::active();
1523 } else {
1524 *native.activation_mut() = RigidBodyActivation::cannot_sleep();
1525 };
1526 });
1527 rigid_body_node
1528 .translation_locked
1529 .try_sync_model(|v| native.lock_translations(v, false));
1530 rigid_body_node.x_rotation_locked.try_sync_model(|v| {
1531 native.set_enabled_rotations(
1532 !v,
1533 !native.is_rotation_locked()[1],
1534 !native.is_rotation_locked()[2],
1535 false,
1536 );
1537 });
1538 rigid_body_node.y_rotation_locked.try_sync_model(|v| {
1539 native.set_enabled_rotations(
1540 !native.is_rotation_locked()[0],
1541 !v,
1542 !native.is_rotation_locked()[2],
1543 false,
1544 );
1545 });
1546 rigid_body_node.z_rotation_locked.try_sync_model(|v| {
1547 native.set_enabled_rotations(
1548 !native.is_rotation_locked()[0],
1549 !native.is_rotation_locked()[1],
1550 !v,
1551 false,
1552 );
1553 });
1554 rigid_body_node
1555 .dominance
1556 .try_sync_model(|v| native.set_dominance_group(v));
1557 rigid_body_node
1558 .gravity_scale
1559 .try_sync_model(|v| native.set_gravity_scale(v, false));
1560
1561 if rigid_body_node.reset_forces.replace(false) {
1564 native.reset_forces(false);
1565 native.reset_torques(false);
1566 }
1567
1568 while let Some(action) = actions.pop_front() {
1569 match action {
1570 ApplyAction::Force(force) => {
1571 native.add_force(force, false);
1572 rigid_body_node.reset_forces.set(true);
1573 }
1574 ApplyAction::Torque(torque) => {
1575 native.add_torque(torque, false);
1576 rigid_body_node.reset_forces.set(true);
1577 }
1578 ApplyAction::ForceAtPoint { force, point } => {
1579 native.add_force_at_point(force, Point3::from(point), false);
1580 rigid_body_node.reset_forces.set(true);
1581 }
1582 ApplyAction::Impulse(impulse) => native.apply_impulse(impulse, false),
1583 ApplyAction::TorqueImpulse(impulse) => {
1584 native.apply_torque_impulse(impulse, false)
1585 }
1586 ApplyAction::ImpulseAtPoint { impulse, point } => {
1587 native.apply_impulse_at_point(impulse, Point3::from(point), false)
1588 }
1589 ApplyAction::WakeUp => native.wake_up(true),
1590 ApplyAction::NextTranslation(position) => {
1591 native.set_next_kinematic_translation(position)
1592 }
1593 ApplyAction::NextRotation(rotation) => {
1594 native.set_next_kinematic_rotation(rotation)
1595 }
1596 ApplyAction::NextPosition(position) => {
1597 native.set_next_kinematic_position(position)
1598 }
1599 }
1600 }
1601 }
1602 }
1603 } else {
1604 let mut builder = RigidBodyBuilder::new(rigid_body_node.body_type().into())
1605 .pose(isometry_from_global_transform(
1606 &rigid_body_node.global_transform(),
1607 ))
1608 .ccd_enabled(rigid_body_node.is_ccd_enabled())
1609 .additional_mass(rigid_body_node.mass())
1610 .angvel(*rigid_body_node.ang_vel)
1611 .linvel(*rigid_body_node.lin_vel)
1612 .linear_damping(*rigid_body_node.lin_damping)
1613 .angular_damping(*rigid_body_node.ang_damping)
1614 .can_sleep(rigid_body_node.is_can_sleep())
1615 .dominance_group(rigid_body_node.dominance())
1616 .gravity_scale(rigid_body_node.gravity_scale())
1617 .enabled_rotations(
1618 !rigid_body_node.is_x_rotation_locked(),
1619 !rigid_body_node.is_y_rotation_locked(),
1620 !rigid_body_node.is_z_rotation_locked(),
1621 );
1622
1623 match *rigid_body_node.mass_properties_type {
1624 RigidBodyMassPropertiesType::Default => {
1625 builder = builder.additional_mass(*rigid_body_node.mass);
1626 }
1627 RigidBodyMassPropertiesType::Additional {
1628 center_of_mass,
1629 principal_inertia,
1630 } => {
1631 builder = builder.additional_mass_properties(MassProperties::new(
1632 Point3::from(center_of_mass),
1633 *rigid_body_node.mass,
1634 principal_inertia,
1635 ));
1636 }
1637 };
1638 if rigid_body_node.is_translation_locked() {
1639 builder = builder.lock_translations();
1640 }
1641
1642 rigid_body_node
1643 .native
1644 .set(self.add_body(handle, builder.build()));
1645
1646 Log::writeln(
1647 MessageKind::Information,
1648 format!(
1649 "Native rigid body was created for node {}",
1650 rigid_body_node.name()
1651 ),
1652 );
1653 }
1654 }
1655
1656 pub(crate) fn sync_to_collider_node(
1657 &mut self,
1658 nodes: &NodePool,
1659 handle: Handle<Node>,
1660 collider_node: &scene::collider::Collider,
1661 ) {
1662 if !collider_node.is_globally_enabled() {
1663 self.remove_collider(collider_node.native.get());
1664 collider_node.native.set(Default::default());
1665 return;
1666 }
1667
1668 let anything_changed = collider_node.needs_sync_model();
1669
1670 if collider_node.native.get() != ColliderHandle::invalid() {
1676 if anything_changed {
1677 if let Some(native) = self.colliders.get_mut(collider_node.native.get()) {
1678 collider_node
1679 .restitution
1680 .try_sync_model(|v| native.set_restitution(v));
1681 collider_node.collision_groups.try_sync_model(|v| {
1682 native.set_collision_groups(InteractionGroups::new(
1683 u32_to_group(v.memberships.0),
1684 u32_to_group(v.filter.0),
1685 ))
1686 });
1687 collider_node.solver_groups.try_sync_model(|v| {
1688 native.set_solver_groups(InteractionGroups::new(
1689 u32_to_group(v.memberships.0),
1690 u32_to_group(v.filter.0),
1691 ))
1692 });
1693 collider_node
1694 .friction
1695 .try_sync_model(|v| native.set_friction(v));
1696 collider_node
1697 .is_sensor
1698 .try_sync_model(|v| native.set_sensor(v));
1699 collider_node
1700 .friction_combine_rule
1701 .try_sync_model(|v| native.set_friction_combine_rule(v.into()));
1702 collider_node
1703 .restitution_combine_rule
1704 .try_sync_model(|v| native.set_restitution_combine_rule(v.into()));
1705 let mut remove_collider = false;
1706 collider_node.shape.try_sync_model(|v| {
1707 let inv_global_transform = isometric_global_transform(nodes, handle)
1708 .try_inverse()
1709 .unwrap_or_default();
1710
1711 if let Some(shape) = collider_shape_into_native_shape(
1712 &v,
1713 inv_global_transform,
1714 handle,
1715 nodes,
1716 ) {
1717 native.set_shape(shape);
1718 } else {
1719 remove_collider = true;
1720 }
1721 });
1722 if remove_collider {
1723 self.remove_collider(collider_node.native.get());
1724 collider_node.native.set(ColliderHandle::invalid());
1725 }
1726 }
1727 }
1728 } else if let Ok(parent_body) =
1729 nodes.try_get_component_of_type::<scene::rigidbody::RigidBody>(collider_node.parent())
1730 {
1731 if parent_body.native.get() != RigidBodyHandle::invalid() {
1732 let inv_global_transform = isometric_global_transform(nodes, handle)
1733 .try_inverse()
1734 .unwrap();
1735 let rigid_body_native = parent_body.native.get();
1736 if let Some(shape) = collider_shape_into_native_shape(
1737 collider_node.shape(),
1738 inv_global_transform,
1739 handle,
1740 nodes,
1741 ) {
1742 let mut builder = ColliderBuilder::new(shape)
1743 .position(Isometry3 {
1744 rotation: **collider_node.local_transform().rotation(),
1745 translation: Translation3 {
1746 vector: **collider_node.local_transform().position(),
1747 },
1748 })
1749 .friction(collider_node.friction())
1750 .restitution(collider_node.restitution())
1751 .collision_groups(InteractionGroups::new(
1752 u32_to_group(collider_node.collision_groups().memberships.0),
1753 u32_to_group(collider_node.collision_groups().filter.0),
1754 ))
1755 .friction_combine_rule(collider_node.friction_combine_rule().into())
1756 .restitution_combine_rule(collider_node.restitution_combine_rule().into())
1757 .solver_groups(InteractionGroups::new(
1758 u32_to_group(collider_node.solver_groups().memberships.0),
1759 u32_to_group(collider_node.solver_groups().filter.0),
1760 ))
1761 .sensor(collider_node.is_sensor());
1762
1763 if let Some(density) = collider_node.density() {
1764 builder = builder.density(density);
1765 }
1766
1767 let native_handle =
1768 self.add_collider(handle, rigid_body_native, builder.build());
1769
1770 collider_node.native.set(native_handle);
1771
1772 Log::writeln(
1773 MessageKind::Information,
1774 format!(
1775 "Native collider was created for node {}",
1776 collider_node.name()
1777 ),
1778 );
1779 }
1780 }
1781 }
1782 }
1783
1784 pub(crate) fn sync_to_joint_node(
1785 &mut self,
1786 nodes: &NodePool,
1787 handle: Handle<Node>,
1788 joint: &scene::joint::Joint,
1789 ) {
1790 if !joint.is_globally_enabled() {
1791 self.remove_joint(joint.native.get());
1792 joint.native.set(ImpulseJointHandle(Default::default()));
1793 return;
1794 }
1795
1796 if let Some(native) = self.joints.set.get_mut(joint.native.get(), false) {
1797 joint.body1.try_sync_model(|v| {
1798 if let Ok(rigid_body_node) = nodes.try_get(v) {
1799 native.body1 = rigid_body_node.native.get();
1800 }
1801 });
1802 joint.body2.try_sync_model(|v| {
1803 if let Ok(rigid_body_node) = nodes.try_get(v) {
1804 native.body2 = rigid_body_node.native.get();
1805 }
1806 });
1807 joint.params.try_sync_model(|v| {
1808 native.data =
1809 convert_joint_params(v, native.data.local_frame1, native.data.local_frame2)
1811 });
1812 joint.motor_params.try_sync_model(|v|{
1813 let joint_axes: &[JointAxis] = match joint.params.get_value_ref(){
1816 JointParams::PrismaticJoint(_) => &[JointAxis::LinX][..], JointParams::RevoluteJoint(_) => &[JointAxis::AngX][..],
1818 JointParams::BallJoint(_) => &[JointAxis::AngX, JointAxis::AngY, JointAxis::AngZ][..],
1819 _ => {
1820 Log::warn("Try to modify motor parameters for unsupported joint type, this operation will be ignored.");
1821 return;
1822 }
1823 };
1824 for joint_axis in joint_axes {
1825 native.data.set_motor_model(*joint_axis, rapier3d::prelude::MotorModel::ForceBased);
1827 let JointMotorParams {
1828 target_vel,
1829 target_pos,
1830 stiffness,
1831 damping,
1832 max_force
1833 } = v;
1834 native.data.set_motor(*joint_axis, target_pos, target_vel, stiffness, damping);
1835 native.data.set_motor_max_force(*joint_axis, max_force);
1836 }
1837 let Some(body1) = self.bodies.get_mut(native.body1) else {
1841 return;
1842 };
1843 body1.wake_up(true);
1844 let Some(body2) = self.bodies.get_mut(native.body2) else {
1845 return;
1846 };
1847 body2.wake_up(true);
1848 });
1849 joint.contacts_enabled.try_sync_model(|v| {
1850 native.data.set_contacts_enabled(v);
1851 });
1852
1853 let mut local_frames = joint.local_frames.borrow_mut();
1854 if local_frames.is_none() {
1855 if let (Ok(body1), Ok(body2)) =
1856 (nodes.try_get(joint.body1()), nodes.try_get(joint.body2()))
1857 {
1858 let (local_frame1, local_frame2) = calculate_local_frames(joint, body1, body2);
1859 native.data =
1860 convert_joint_params((*joint.params).clone(), local_frame1, local_frame2);
1861 *local_frames = Some(JointLocalFrames::new(&local_frame1, &local_frame2));
1862 }
1863 }
1864 } else {
1865 let body1_handle = joint.body1();
1866 let body2_handle = joint.body2();
1867 let params = joint.params().clone();
1868
1869 if let (Some(body1), Some(body2)) = (
1872 nodes
1873 .try_get(body1_handle)
1874 .ok()
1875 .filter(|b| self.bodies.get(b.native.get()).is_some()),
1876 nodes
1877 .try_get(body2_handle)
1878 .ok()
1879 .filter(|b| self.bodies.get(b.native.get()).is_some()),
1880 ) {
1881 let native_body1 = body1.native.get();
1882 let native_body2 = body2.native.get();
1883
1884 if self.bodies.get(native_body1).is_none()
1885 || self.bodies.get(native_body2).is_none()
1886 {
1887 return;
1891 }
1892
1893 let mut local_frames = joint.local_frames.borrow_mut();
1895 let (local_frame1, local_frame2) = local_frames
1896 .clone()
1897 .map(|frames| {
1898 (
1899 Isometry3 {
1900 rotation: frames.body1.rotation,
1901 translation: Translation {
1902 vector: frames.body1.position,
1903 },
1904 },
1905 Isometry3 {
1906 rotation: frames.body2.rotation,
1907 translation: Translation {
1908 vector: frames.body2.position,
1909 },
1910 },
1911 )
1912 })
1913 .unwrap_or_else(|| calculate_local_frames(joint, body1, body2));
1914
1915 let mut native_joint = convert_joint_params(params, local_frame1, local_frame2);
1916 native_joint.contacts_enabled = joint.is_contacts_enabled();
1917 let native_handle =
1918 self.add_joint(handle, native_body1, native_body2, native_joint);
1919
1920 joint.native.set(native_handle);
1921 *local_frames = Some(JointLocalFrames::new(&local_frame1, &local_frame2));
1922
1923 Log::writeln(
1924 MessageKind::Information,
1925 format!("Native joint was created for node {}", joint.name()),
1926 );
1927 }
1928 }
1929 }
1930
1931 pub(crate) fn intersections_with(
1933 &self,
1934 collider: ColliderHandle,
1935 ) -> impl Iterator<Item = IntersectionPair> + '_ {
1936 self.narrow_phase
1937 .intersection_pairs_with(collider)
1938 .filter_map(|(collider1, collider2, intersecting)| {
1939 Some(IntersectionPair {
1940 collider1: Handle::decode_from_u128(self.colliders.get(collider1)?.user_data),
1941 collider2: Handle::decode_from_u128(self.colliders.get(collider2)?.user_data),
1942 has_any_active_contact: intersecting,
1943 })
1944 })
1945 }
1946
1947 pub(crate) fn contacts_with(
1949 &self,
1950 collider: ColliderHandle,
1951 ) -> impl Iterator<Item = ContactPair> + '_ {
1952 self.narrow_phase
1953 .contact_pairs_with(collider)
1956 .filter_map(|c| ContactPair::from_native(c, self))
1957 }
1958
1959 pub fn contacts(&self) -> impl Iterator<Item = ContactPair> + '_ {
1961 self.narrow_phase
1962 .contact_pairs()
1963 .filter_map(|c| ContactPair::from_native(c, self))
1964 }
1965}
1966
1967impl Default for PhysicsWorld {
1968 fn default() -> Self {
1969 Self::new()
1970 }
1971}
1972
1973impl Debug for PhysicsWorld {
1974 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1975 write!(f, "PhysicsWorld")
1976 }
1977}