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,
40 },
41 scene::{
42 self,
43 collider::{self, ColliderShape, GeometrySource},
44 debug::SceneDrawingContext,
45 graph::{isometric_global_transform, Graph, NodePool},
46 joint::{JointLocalFrames, JointParams},
47 mesh::{
48 buffer::{VertexAttributeUsage, VertexReadTrait},
49 Mesh,
50 },
51 node::{Node, NodeTrait},
52 rigidbody::{self, ApplyAction},
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, QueryPipeline},
69 prelude::{HeightFieldCellStatus, JointAxis},
70};
71use std::{
72 cell::{Cell, RefCell},
73 cmp::Ordering,
74 fmt::{Debug, Formatter},
75 hash::Hash,
76 num::NonZeroUsize,
77 sync::Arc,
78 time::Duration,
79};
80use strum_macros::{AsRefStr, EnumString, VariantNames};
81
82use fyrox_graph::{BaseSceneGraph, SceneGraphNode};
83pub use rapier3d::geometry::shape::*;
84
85#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
87pub enum FeatureId {
88 Vertex(u32),
90 Edge(u32),
92 Face(u32),
94 Unknown,
96}
97
98impl From<rapier3d::geometry::FeatureId> for FeatureId {
99 fn from(v: rapier3d::geometry::FeatureId) -> Self {
100 match v {
101 rapier3d::geometry::FeatureId::Vertex(v) => FeatureId::Vertex(v),
102 rapier3d::geometry::FeatureId::Edge(v) => FeatureId::Edge(v),
103 rapier3d::geometry::FeatureId::Face(v) => FeatureId::Face(v),
104 rapier3d::geometry::FeatureId::Unknown => FeatureId::Unknown,
105 }
106 }
107}
108
109impl From<rapier2d::geometry::FeatureId> for FeatureId {
110 fn from(v: rapier2d::geometry::FeatureId) -> Self {
111 match v {
112 rapier2d::geometry::FeatureId::Vertex(v) => FeatureId::Vertex(v),
113 rapier2d::geometry::FeatureId::Face(v) => FeatureId::Face(v),
114 rapier2d::geometry::FeatureId::Unknown => FeatureId::Unknown,
115 }
116 }
117}
118
119#[derive(
127 Copy, Clone, Debug, PartialEq, Eq, Visit, Reflect, VariantNames, EnumString, AsRefStr, Default,
128)]
129#[repr(u32)]
130pub enum CoefficientCombineRule {
131 #[default]
133 Average = 0,
134 Min,
136 Multiply,
138 Max,
140}
141
142uuid_provider!(CoefficientCombineRule = "775d5598-c283-4b44-9cc0-2e23dc8936f4");
143
144impl From<rapier3d::dynamics::CoefficientCombineRule> for CoefficientCombineRule {
145 fn from(v: rapier3d::dynamics::CoefficientCombineRule) -> Self {
146 match v {
147 rapier3d::dynamics::CoefficientCombineRule::Average => CoefficientCombineRule::Average,
148 rapier3d::dynamics::CoefficientCombineRule::Min => CoefficientCombineRule::Min,
149 rapier3d::dynamics::CoefficientCombineRule::Multiply => {
150 CoefficientCombineRule::Multiply
151 }
152 rapier3d::dynamics::CoefficientCombineRule::Max => CoefficientCombineRule::Max,
153 }
154 }
155}
156
157impl Into<rapier3d::dynamics::CoefficientCombineRule> for CoefficientCombineRule {
158 fn into(self) -> rapier3d::dynamics::CoefficientCombineRule {
159 match self {
160 CoefficientCombineRule::Average => rapier3d::dynamics::CoefficientCombineRule::Average,
161 CoefficientCombineRule::Min => rapier3d::dynamics::CoefficientCombineRule::Min,
162 CoefficientCombineRule::Multiply => {
163 rapier3d::dynamics::CoefficientCombineRule::Multiply
164 }
165 CoefficientCombineRule::Max => rapier3d::dynamics::CoefficientCombineRule::Max,
166 }
167 }
168}
169
170impl Into<rapier2d::dynamics::CoefficientCombineRule> for CoefficientCombineRule {
171 fn into(self) -> rapier2d::dynamics::CoefficientCombineRule {
172 match self {
173 CoefficientCombineRule::Average => rapier2d::dynamics::CoefficientCombineRule::Average,
174 CoefficientCombineRule::Min => rapier2d::dynamics::CoefficientCombineRule::Min,
175 CoefficientCombineRule::Multiply => {
176 rapier2d::dynamics::CoefficientCombineRule::Multiply
177 }
178 CoefficientCombineRule::Max => rapier2d::dynamics::CoefficientCombineRule::Max,
179 }
180 }
181}
182
183#[derive(Debug, Default, Clone)]
185pub struct PhysicsPerformanceStatistics {
186 pub step_time: Duration,
188
189 pub total_ray_cast_time: Cell<Duration>,
191}
192
193impl PhysicsPerformanceStatistics {
194 pub fn reset(&mut self) {
196 *self = Default::default();
197 }
198
199 pub fn total(&self) -> Duration {
201 self.step_time + self.total_ray_cast_time.get()
202 }
203}
204
205#[derive(Debug, Clone, PartialEq)]
207pub struct Intersection {
208 pub collider: Handle<Node>,
210
211 pub normal: Vector3<f32>,
213
214 pub position: Point3<f32>,
216
217 pub feature: FeatureId,
227
228 pub toi: f32,
230}
231
232pub struct RayCastOptions {
234 pub ray_origin: Point3<f32>,
236
237 pub ray_direction: Vector3<f32>,
239
240 pub max_len: f32,
242
243 pub groups: collider::InteractionGroups,
245
246 pub sort_results: bool,
248}
249
250pub trait QueryResultsStorage {
254 fn push(&mut self, intersection: Intersection) -> bool;
257
258 fn clear(&mut self);
260
261 fn sort_intersections_by<C: FnMut(&Intersection, &Intersection) -> Ordering>(&mut self, cmp: C);
263}
264
265impl QueryResultsStorage for Vec<Intersection> {
266 fn push(&mut self, intersection: Intersection) -> bool {
267 self.push(intersection);
268 true
269 }
270
271 fn clear(&mut self) {
272 self.clear()
273 }
274
275 fn sort_intersections_by<C>(&mut self, cmp: C)
276 where
277 C: FnMut(&Intersection, &Intersection) -> Ordering,
278 {
279 self.sort_by(cmp);
280 }
281}
282
283impl<const CAP: usize> QueryResultsStorage for ArrayVec<Intersection, CAP> {
284 fn push(&mut self, intersection: Intersection) -> bool {
285 self.try_push(intersection).is_ok()
286 }
287
288 fn clear(&mut self) {
289 self.clear()
290 }
291
292 fn sort_intersections_by<C>(&mut self, cmp: C)
293 where
294 C: FnMut(&Intersection, &Intersection) -> Ordering,
295 {
296 self.sort_by(cmp);
297 }
298}
299
300#[derive(Debug, Clone, PartialEq)]
302pub struct ContactData {
303 pub local_p1: Vector3<f32>,
305 pub local_p2: Vector3<f32>,
307 pub dist: f32,
309 pub impulse: f32,
312 pub tangent_impulse: Vector2<f32>,
315}
316
317#[derive(Debug, Clone, PartialEq)]
319pub struct ContactManifold {
320 pub points: Vec<ContactData>,
322 pub local_n1: Vector3<f32>,
324 pub local_n2: Vector3<f32>,
326 pub rigid_body1: Handle<Node>,
328 pub rigid_body2: Handle<Node>,
330 pub normal: Vector3<f32>,
332}
333
334#[derive(Debug, Clone, PartialEq)]
336pub struct ContactPair {
337 pub collider1: Handle<Node>,
339 pub collider2: Handle<Node>,
341 pub manifolds: Vec<ContactManifold>,
344 pub has_any_active_contact: bool,
346}
347
348impl ContactPair {
349 fn from_native(c: &rapier3d::geometry::ContactPair, physics: &PhysicsWorld) -> Option<Self> {
350 Some(ContactPair {
351 collider1: Handle::decode_from_u128(physics.colliders.get(c.collider1)?.user_data),
352 collider2: Handle::decode_from_u128(physics.colliders.get(c.collider2)?.user_data),
353 manifolds: c
354 .manifolds
355 .iter()
356 .filter_map(|m| {
357 Some(ContactManifold {
358 points: m
359 .points
360 .iter()
361 .map(|p| ContactData {
362 local_p1: p.local_p1.coords,
363 local_p2: p.local_p2.coords,
364 dist: p.dist,
365 impulse: p.data.impulse,
366 tangent_impulse: p.data.tangent_impulse,
367 })
368 .collect(),
369 local_n1: m.local_n1,
370 local_n2: m.local_n2,
371 rigid_body1: m.data.rigid_body1.and_then(|h| {
372 physics
373 .bodies
374 .get(h)
375 .map(|b| Handle::decode_from_u128(b.user_data))
376 })?,
377 rigid_body2: m.data.rigid_body2.and_then(|h| {
378 physics
379 .bodies
380 .get(h)
381 .map(|b| Handle::decode_from_u128(b.user_data))
382 })?,
383 normal: m.data.normal,
384 })
385 })
386 .collect(),
387 has_any_active_contact: c.has_any_active_contact,
388 })
389 }
390}
391
392#[derive(Debug, Clone, PartialEq)]
394pub struct IntersectionPair {
395 pub collider1: Handle<Node>,
397 pub collider2: Handle<Node>,
399 pub has_any_active_contact: bool,
401}
402
403pub(super) struct Container<S, A>
404where
405 A: Hash + Eq + Clone,
406{
407 set: S,
408 map: BiDirHashMap<A, Handle<Node>>,
409}
410
411fn convert_joint_params(
412 params: scene::joint::JointParams,
413 local_frame1: Isometry3<f32>,
414 local_frame2: Isometry3<f32>,
415) -> GenericJoint {
416 let locked_axis = match params {
417 JointParams::BallJoint(_) => JointAxesMask::LOCKED_SPHERICAL_AXES,
418 JointParams::FixedJoint(_) => JointAxesMask::LOCKED_FIXED_AXES,
419 JointParams::PrismaticJoint(_) => JointAxesMask::LOCKED_PRISMATIC_AXES,
420 JointParams::RevoluteJoint(_) => JointAxesMask::LOCKED_REVOLUTE_AXES,
421 };
422
423 let mut joint = GenericJointBuilder::new(locked_axis)
424 .local_frame1(local_frame1)
425 .local_frame2(local_frame2)
426 .build();
427
428 match params {
429 scene::joint::JointParams::BallJoint(v) => {
430 if v.x_limits_enabled {
431 joint.set_limits(
432 JointAxis::AngX,
433 [v.x_limits_angles.start, v.x_limits_angles.end],
434 );
435 }
436 if v.y_limits_enabled {
437 joint.set_limits(
438 JointAxis::AngY,
439 [v.y_limits_angles.start, v.y_limits_angles.end],
440 );
441 }
442 if v.z_limits_enabled {
443 joint.set_limits(
444 JointAxis::AngZ,
445 [v.z_limits_angles.start, v.z_limits_angles.end],
446 );
447 }
448 }
449 scene::joint::JointParams::FixedJoint(_) => {}
450 scene::joint::JointParams::PrismaticJoint(v) => {
451 if v.limits_enabled {
452 joint.set_limits(JointAxis::LinX, [v.limits.start, v.limits.end]);
453 }
454 }
455 scene::joint::JointParams::RevoluteJoint(v) => {
456 if v.limits_enabled {
457 joint.set_limits(JointAxis::AngX, [v.limits.start, v.limits.end]);
458 }
459 }
460 }
461
462 joint
463}
464
465fn make_trimesh(
468 owner_inv_transform: Matrix4<f32>,
469 owner: Handle<Node>,
470 sources: &[GeometrySource],
471 nodes: &NodePool,
472) -> Option<SharedShape> {
473 let mut mesh_builder = RawMeshBuilder::new(0, 0);
474
475 let root_inv_transform = owner_inv_transform;
481
482 for &source in sources {
483 if let Some(mesh) = nodes.try_borrow(source.0).and_then(|n| n.cast::<Mesh>()) {
484 let global_transform = root_inv_transform * mesh.global_transform();
485
486 for surface in mesh.surfaces() {
487 let shared_data = surface.data();
488 let shared_data = shared_data.data_ref();
489
490 let vertices = &shared_data.vertex_buffer;
491 for triangle in shared_data.geometry_buffer.iter() {
492 let a = RawVertex::from(
493 global_transform
494 .transform_point(&Point3::from(
495 vertices
496 .get(triangle[0] as usize)
497 .unwrap()
498 .read_3_f32(VertexAttributeUsage::Position)
499 .unwrap(),
500 ))
501 .coords,
502 );
503 let b = RawVertex::from(
504 global_transform
505 .transform_point(&Point3::from(
506 vertices
507 .get(triangle[1] as usize)
508 .unwrap()
509 .read_3_f32(VertexAttributeUsage::Position)
510 .unwrap(),
511 ))
512 .coords,
513 );
514 let c = RawVertex::from(
515 global_transform
516 .transform_point(&Point3::from(
517 vertices
518 .get(triangle[2] as usize)
519 .unwrap()
520 .read_3_f32(VertexAttributeUsage::Position)
521 .unwrap(),
522 ))
523 .coords,
524 );
525
526 mesh_builder.insert(a);
527 mesh_builder.insert(b);
528 mesh_builder.insert(c);
529 }
530 }
531 }
532 }
533
534 let raw_mesh = mesh_builder.build();
535
536 let vertices: Vec<Point3<f32>> = raw_mesh
537 .vertices
538 .into_iter()
539 .map(|v| Point3::new(v.x, v.y, v.z))
540 .collect();
541
542 let indices = raw_mesh
543 .triangles
544 .into_iter()
545 .map(|t| [t.0[0], t.0[1], t.0[2]])
546 .collect::<Vec<_>>();
547
548 if indices.is_empty() {
549 Log::writeln(
550 MessageKind::Warning,
551 format!(
552 "Failed to create triangle mesh collider for {}, it has no vertices!",
553 nodes[owner].name()
554 ),
555 );
556
557 SharedShape::trimesh(vec![Point3::new(0.0, 0.0, 0.0)], vec![[0, 0, 0]]).ok()
558 } else {
559 SharedShape::trimesh(vertices, indices).ok()
560 }
561}
562
563fn make_polyhedron_shape(owner_inv_transform: Matrix4<f32>, mesh: &Mesh) -> SharedShape {
566 let mut mesh_builder = RawMeshBuilder::new(0, 0);
567
568 let root_inv_transform = owner_inv_transform;
574
575 let global_transform = root_inv_transform * mesh.global_transform();
576
577 for surface in mesh.surfaces() {
578 let shared_data = surface.data();
579 let shared_data = shared_data.data_ref();
580
581 let vertices = &shared_data.vertex_buffer;
582 for triangle in shared_data.geometry_buffer.iter() {
583 let a = RawVertex::from(
584 global_transform
585 .transform_point(&Point3::from(
586 vertices
587 .get(triangle[0] as usize)
588 .unwrap()
589 .read_3_f32(VertexAttributeUsage::Position)
590 .unwrap(),
591 ))
592 .coords,
593 );
594 let b = RawVertex::from(
595 global_transform
596 .transform_point(&Point3::from(
597 vertices
598 .get(triangle[1] as usize)
599 .unwrap()
600 .read_3_f32(VertexAttributeUsage::Position)
601 .unwrap(),
602 ))
603 .coords,
604 );
605 let c = RawVertex::from(
606 global_transform
607 .transform_point(&Point3::from(
608 vertices
609 .get(triangle[2] as usize)
610 .unwrap()
611 .read_3_f32(VertexAttributeUsage::Position)
612 .unwrap(),
613 ))
614 .coords,
615 );
616
617 mesh_builder.insert(a);
618 mesh_builder.insert(b);
619 mesh_builder.insert(c);
620 }
621 }
622
623 let raw_mesh = mesh_builder.build();
624
625 let vertices: Vec<Point3<f32>> = raw_mesh
626 .vertices
627 .into_iter()
628 .map(|v| Point3::new(v.x, v.y, v.z))
629 .collect();
630
631 let indices = raw_mesh
632 .triangles
633 .into_iter()
634 .map(|t| [t.0[0], t.0[1], t.0[2]])
635 .collect::<Vec<_>>();
636
637 SharedShape::convex_decomposition(&vertices, &indices)
638}
639
640fn make_heightfield(terrain: &Terrain) -> Option<SharedShape> {
642 assert!(!terrain.chunks_ref().is_empty());
643
644 let scale = terrain.local_transform().scale();
646
647 let height_map_size = terrain.height_map_size();
649 let chunk_size = height_map_size.map(|x| x - 3);
650 let chunk_min = terrain
651 .chunks_ref()
652 .iter()
653 .map(Chunk::grid_position)
654 .reduce(|a, b| a.inf(&b));
655 let chunk_max = terrain
656 .chunks_ref()
657 .iter()
658 .map(Chunk::grid_position)
659 .reduce(|a, b| a.sup(&b));
660 let (Some(chunk_min), Some(chunk_max)) = (chunk_min, chunk_max) else {
661 return None;
662 };
663 let row_range = chunk_max.y - chunk_min.y + 1;
664 let col_range = chunk_max.x - chunk_min.x + 1;
665 let nrows = chunk_size.y * row_range as u32 + 1;
666 let ncols = chunk_size.x * col_range as u32 + 1;
667
668 let mut data = vec![0.0; (nrows * ncols) as usize];
670 for chunk in terrain.chunks_ref() {
671 let texture = chunk.heightmap().data_ref();
672 let height_map = texture.data_of_type::<f32>().unwrap();
673 let pos = (chunk.grid_position() - chunk_min).map(|x| x as u32);
674 let (ox, oy) = (pos.x * chunk_size.x, pos.y * chunk_size.y);
675 for iy in 0..height_map_size.y - 2 {
676 for ix in 0..height_map_size.x - 2 {
677 let (x, y) = (ix + 1, iy + 1);
678 let value = height_map[(y * height_map_size.x + x) as usize] * scale.y;
679 data[((ox + ix) * nrows + oy + iy) as usize] = value;
680 }
681 }
682 }
683 let x_scale = terrain.chunk_size().x * scale.x * col_range as f32;
684 let z_scale = terrain.chunk_size().y * scale.z * row_range as f32;
685 let x_pos = terrain.chunk_size().x * scale.x * chunk_min.x as f32;
686 let z_pos = terrain.chunk_size().y * scale.z * chunk_min.y as f32;
687 let mut hf = HeightField::new(
688 DMatrix::from_data(VecStorage::new(
689 Dyn(nrows as usize),
690 Dyn(ncols as usize),
691 data,
692 )),
693 Vector3::new(x_scale, 1.0, z_scale),
694 );
695 hf.cells_statuses_mut()
696 .fill(HeightFieldCellStatus::CELL_REMOVED);
697 let hole_mask_size = terrain.hole_mask_size();
698 for chunk in terrain.chunks_ref() {
699 let Some(texture) = chunk.hole_mask().map(|t| t.data_ref()) else {
700 continue;
701 };
702 let hole_mask = texture.data_of_type::<u8>().unwrap();
703 let pos = (chunk.grid_position() - chunk_min).map(|x| x as u32);
704 let (ox, oy) = (pos.x * chunk_size.x, pos.y * chunk_size.y);
705
706 for iy in 0..hole_mask_size.y {
707 for ix in 0..hole_mask_size.x {
708 let is_hole = hole_mask[(iy * hole_mask_size.x + ix) as usize] < 128;
709 let (x, y) = (ox + ix, oy + iy);
710 if !is_hole {
711 hf.set_cell_status(y as usize, x as usize, HeightFieldCellStatus::empty());
712 }
713 }
714 }
715 }
716 Some(SharedShape::compound(vec![(
719 Isometry3::translation(x_scale * 0.5 + x_pos, 0.0, z_scale * 0.5 + z_pos),
720 SharedShape::new(hf),
721 )]))
722}
723
724fn collider_shape_into_native_shape(
726 shape: &ColliderShape,
727 owner_inv_global_transform: Matrix4<f32>,
728 owner_collider: Handle<Node>,
729 pool: &NodePool,
730) -> Option<SharedShape> {
731 match shape {
732 ColliderShape::Ball(ball) => Some(SharedShape::ball(ball.radius)),
733
734 ColliderShape::Cylinder(cylinder) => {
735 Some(SharedShape::cylinder(cylinder.half_height, cylinder.radius))
736 }
737 ColliderShape::Cone(cone) => Some(SharedShape::cone(cone.half_height, cone.radius)),
738 ColliderShape::Cuboid(cuboid) => {
739 Some(SharedShape(Arc::new(Cuboid::new(cuboid.half_extents))))
740 }
741 ColliderShape::Capsule(capsule) => Some(SharedShape::capsule(
742 Point3::from(capsule.begin),
743 Point3::from(capsule.end),
744 capsule.radius,
745 )),
746 ColliderShape::Segment(segment) => Some(SharedShape::segment(
747 Point3::from(segment.begin),
748 Point3::from(segment.end),
749 )),
750 ColliderShape::Triangle(triangle) => Some(SharedShape::triangle(
751 Point3::from(triangle.a),
752 Point3::from(triangle.b),
753 Point3::from(triangle.c),
754 )),
755 ColliderShape::Trimesh(trimesh) => {
756 if trimesh.sources.is_empty() {
757 None
758 } else {
759 make_trimesh(
760 owner_inv_global_transform,
761 owner_collider,
762 &trimesh.sources,
763 pool,
764 )
765 }
766 }
767 ColliderShape::Heightfield(heightfield) => pool
768 .try_borrow(heightfield.geometry_source.0)
769 .and_then(|n| n.cast::<Terrain>())
770 .and_then(make_heightfield),
771 ColliderShape::Polyhedron(polyhedron) => pool
772 .try_borrow(polyhedron.geometry_source.0)
773 .and_then(|n| n.cast::<Mesh>())
774 .map(|mesh| make_polyhedron_shape(owner_inv_global_transform, mesh)),
775 }
776}
777
778#[derive(Copy, Clone, Visit, Reflect, Debug, PartialEq)]
785#[visit(optional)]
786pub struct IntegrationParameters {
787 #[reflect(min_value = 0.0, description = "The time step length (default: None)")]
790 pub dt: Option<f32>,
791
792 #[reflect(
801 min_value = 0.0,
802 description = "Minimum timestep size when using CCD with multiple\
803 substeps (default `1.0 / 60.0 / 100.0`)"
804 )]
805 pub min_ccd_dt: f32,
806
807 #[reflect(
811 min_value = 0.0,
812 description = "The damping ratio used by the springs for contact constraint stabilization.
813Larger values make the constraints more compliant (allowing more visible penetrations
814before stabilization). Default `5.0`."
815 )]
816 pub contact_damping_ratio: f32,
817
818 #[reflect(
824 min_value = 0.0,
825 description = "The natural frequency used by the springs for contact constraint regularization.
826Increasing this value will make it so that penetrations get fixed more quickly at the
827expense of potential jitter effects due to overshooting. In order to make the simulation
828look stiffer, it is recommended to increase the `contact_damping_ratio` instead of this
829value. Default: `30.0`"
830 )]
831 pub contact_natural_frequency: f32,
832
833 #[reflect(
837 min_value = 0.0,
838 description = "The natural frequency used by the springs for joint constraint regularization.
839Increasing this value will make it so that penetrations get fixed more quickly. Default: `1.0e6`."
840 )]
841 pub joint_natural_frequency: f32,
842
843 #[reflect(
846 min_value = 0.0,
847 description = "The fraction of critical damping applied to the joint for \
848 constraints regularization (default: `0.8`)."
849 )]
850 pub joint_damping_ratio: f32,
851
852 #[reflect(
854 min_value = 0.0,
855 description = "Amount of penetration the engine wont attempt to correct (default: `0.002m`)."
856 )]
857 pub allowed_linear_error: f32,
858
859 #[reflect(
861 min_value = 0.0,
862 description = "Maximum amount of penetration the solver will attempt to resolve in one timestep (default: `10.0`)."
863 )]
864 pub normalized_max_corrective_velocity: f32,
865
866 #[reflect(
868 min_value = 0.0,
869 description = "The maximal distance separating two objects that will generate \
870 predictive contacts (default: `0.002`)."
871 )]
872 pub prediction_distance: f32,
873
874 #[reflect(
876 min_value = 0.0,
877 description = "The number of solver iterations run by the constraints solver for calculating forces (default: `4`)."
878 )]
879 pub num_solver_iterations: usize,
880
881 #[reflect(
883 min_value = 0.0,
884 description = "Number of addition friction resolution iteration run during the last solver sub-step (default: `4`)."
885 )]
886 pub num_additional_friction_iterations: usize,
887
888 #[reflect(
890 min_value = 0.0,
891 description = "Number of internal Project Gauss Seidel (PGS) iterations run at each solver iteration (default: `1`)."
892 )]
893 pub num_internal_pgs_iterations: usize,
894
895 #[reflect(
897 min_value = 0.0,
898 description = "Minimum number of dynamic bodies in each active island (default: `128`)."
899 )]
900 pub min_island_size: u32,
901
902 #[reflect(
904 min_value = 0.0,
905 description = "Maximum number of substeps performed by the solver (default: `4`)."
906 )]
907 pub max_ccd_substeps: u32,
908
909 pub warmstart_coefficient: f32,
912
913 pub length_unit: f32,
921
922 pub num_internal_stabilization_iterations: usize,
924}
925
926impl Default for IntegrationParameters {
927 fn default() -> Self {
928 Self {
929 dt: None,
930 min_ccd_dt: 1.0 / 60.0 / 100.0,
931 contact_damping_ratio: 5.0,
932 contact_natural_frequency: 30.0,
933 joint_natural_frequency: 1.0e6,
934 joint_damping_ratio: 1.0,
935 warmstart_coefficient: 1.0,
936 allowed_linear_error: 0.002,
937 normalized_max_corrective_velocity: 10.0,
938 prediction_distance: 0.002,
939 num_internal_pgs_iterations: 1,
940 num_additional_friction_iterations: 4,
941 num_solver_iterations: 4,
942 min_island_size: 128,
943 max_ccd_substeps: 4,
944 length_unit: 1.0,
945 num_internal_stabilization_iterations: 4,
946 }
947 }
948}
949
950#[derive(Visit, Reflect)]
954pub struct PhysicsWorld {
955 pub enabled: InheritableVariable<bool>,
957
958 #[visit(optional)]
960 pub integration_parameters: InheritableVariable<IntegrationParameters>,
961
962 pub gravity: InheritableVariable<Vector3<f32>>,
964
965 #[visit(skip)]
967 #[reflect(hidden)]
968 pub performance_statistics: PhysicsPerformanceStatistics,
969
970 #[visit(skip)]
972 #[reflect(hidden)]
973 pipeline: PhysicsPipeline,
974 #[visit(skip)]
976 #[reflect(hidden)]
977 broad_phase: DefaultBroadPhase,
978 #[visit(skip)]
980 #[reflect(hidden)]
981 narrow_phase: NarrowPhase,
982 #[visit(skip)]
984 #[reflect(hidden)]
985 ccd_solver: CCDSolver,
986 #[visit(skip)]
989 #[reflect(hidden)]
990 islands: IslandManager,
991 #[visit(skip)]
993 #[reflect(hidden)]
994 bodies: RigidBodySet,
995 #[visit(skip)]
997 #[reflect(hidden)]
998 pub(crate) colliders: ColliderSet,
999 #[visit(skip)]
1001 #[reflect(hidden)]
1002 joints: Container<ImpulseJointSet, ImpulseJointHandle>,
1003 #[visit(skip)]
1005 #[reflect(hidden)]
1006 multibody_joints: Container<MultibodyJointSet, MultibodyJointHandle>,
1007 #[visit(skip)]
1009 #[reflect(hidden)]
1010 event_handler: Box<dyn EventHandler>,
1011 #[visit(skip)]
1012 #[reflect(hidden)]
1013 query: RefCell<QueryPipeline>,
1014 #[visit(skip)]
1015 #[reflect(hidden)]
1016 debug_render_pipeline: Mutex<DebugRenderPipeline>,
1017}
1018
1019impl Clone for PhysicsWorld {
1020 fn clone(&self) -> Self {
1021 PhysicsWorld {
1022 enabled: self.enabled.clone(),
1023 integration_parameters: self.integration_parameters.clone(),
1024 gravity: self.gravity.clone(),
1025 ..Default::default()
1026 }
1027 }
1028}
1029
1030fn isometry_from_global_transform(transform: &Matrix4<f32>) -> Isometry3<f32> {
1031 Isometry3 {
1032 translation: Translation3::new(transform[12], transform[13], transform[14]),
1033 rotation: UnitQuaternion::from_matrix_eps(
1034 &transform.basis(),
1035 f32::EPSILON,
1036 16,
1037 UnitQuaternion::identity(),
1038 ),
1039 }
1040}
1041
1042fn calculate_local_frames(
1043 joint: &dyn NodeTrait,
1044 body1: &dyn NodeTrait,
1045 body2: &dyn NodeTrait,
1046) -> (Isometry3<f32>, Isometry3<f32>) {
1047 let joint_isometry = isometry_from_global_transform(&joint.global_transform());
1048
1049 (
1050 isometry_from_global_transform(&body1.global_transform()).inverse() * joint_isometry,
1051 isometry_from_global_transform(&body2.global_transform()).inverse() * joint_isometry,
1052 )
1053}
1054
1055fn u32_to_group(v: u32) -> rapier3d::geometry::Group {
1056 rapier3d::geometry::Group::from_bits(v).unwrap_or_else(rapier3d::geometry::Group::all)
1057}
1058
1059#[derive(Copy, Clone, Default)]
1061#[allow(clippy::type_complexity)]
1062pub struct QueryFilter<'a> {
1063 pub flags: collider::QueryFilterFlags,
1065 pub groups: Option<collider::InteractionGroups>,
1068 pub exclude_collider: Option<Handle<Node>>,
1070 pub exclude_rigid_body: Option<Handle<Node>>,
1072 pub predicate: Option<&'a dyn Fn(Handle<Node>, &collider::Collider) -> bool>,
1074}
1075
1076#[derive(Copy, Clone, Debug)]
1078pub struct TOI {
1079 pub toi: f32,
1081 pub witness1: Point3<f32>,
1085 pub witness2: Point3<f32>,
1089 pub normal1: UnitVector3<f32>,
1093 pub normal2: UnitVector3<f32>,
1097 pub status: collider::TOIStatus,
1099}
1100
1101impl PhysicsWorld {
1102 pub(super) fn new() -> Self {
1104 Self {
1105 enabled: true.into(),
1106 pipeline: PhysicsPipeline::new(),
1107 gravity: Vector3::new(0.0, -9.81, 0.0).into(),
1108 integration_parameters: IntegrationParameters::default().into(),
1109 broad_phase: DefaultBroadPhase::new(),
1110 narrow_phase: NarrowPhase::new(),
1111 ccd_solver: CCDSolver::new(),
1112 islands: IslandManager::new(),
1113 bodies: RigidBodySet::new(),
1114 colliders: ColliderSet::new(),
1115 joints: Container {
1116 set: ImpulseJointSet::new(),
1117 map: Default::default(),
1118 },
1119 multibody_joints: Container {
1120 set: MultibodyJointSet::new(),
1121 map: Default::default(),
1122 },
1123 event_handler: Box::new(()),
1124 query: RefCell::new(Default::default()),
1125 performance_statistics: Default::default(),
1126 debug_render_pipeline: Default::default(),
1127 }
1128 }
1129
1130 pub(super) fn update(&mut self, dt: f32) {
1131 let time = instant::Instant::now();
1132
1133 if *self.enabled {
1134 let integration_parameters = rapier3d::dynamics::IntegrationParameters {
1135 dt: self.integration_parameters.dt.unwrap_or(dt),
1136 min_ccd_dt: self.integration_parameters.min_ccd_dt,
1137 contact_damping_ratio: self.integration_parameters.contact_damping_ratio,
1138 contact_natural_frequency: self.integration_parameters.contact_natural_frequency,
1139 joint_natural_frequency: self.integration_parameters.joint_natural_frequency,
1140 joint_damping_ratio: self.integration_parameters.joint_damping_ratio,
1141 warmstart_coefficient: self.integration_parameters.warmstart_coefficient,
1142 length_unit: self.integration_parameters.length_unit,
1143 normalized_allowed_linear_error: self.integration_parameters.allowed_linear_error,
1144 normalized_max_corrective_velocity: self
1145 .integration_parameters
1146 .normalized_max_corrective_velocity,
1147 normalized_prediction_distance: self.integration_parameters.prediction_distance,
1148 num_solver_iterations: NonZeroUsize::new(
1149 self.integration_parameters.num_solver_iterations,
1150 )
1151 .unwrap(),
1152 num_additional_friction_iterations: self
1153 .integration_parameters
1154 .num_additional_friction_iterations,
1155 num_internal_pgs_iterations: self
1156 .integration_parameters
1157 .num_internal_pgs_iterations,
1158 num_internal_stabilization_iterations: self
1159 .integration_parameters
1160 .num_internal_stabilization_iterations,
1161 min_island_size: self.integration_parameters.min_island_size as usize,
1162 max_ccd_substeps: self.integration_parameters.max_ccd_substeps as usize,
1163 };
1164
1165 self.pipeline.step(
1166 &self.gravity,
1167 &integration_parameters,
1168 &mut self.islands,
1169 &mut self.broad_phase,
1170 &mut self.narrow_phase,
1171 &mut self.bodies,
1172 &mut self.colliders,
1173 &mut self.joints.set,
1174 &mut self.multibody_joints.set,
1175 &mut self.ccd_solver,
1176 None,
1179 &(),
1180 &*self.event_handler,
1181 );
1182 }
1183
1184 self.performance_statistics.step_time += instant::Instant::now() - time;
1185 }
1186
1187 pub(super) fn add_body(&mut self, owner: Handle<Node>, mut body: RigidBody) -> RigidBodyHandle {
1188 body.user_data = owner.encode_to_u128();
1189 self.bodies.insert(body)
1190 }
1191
1192 pub(crate) fn remove_body(&mut self, handle: RigidBodyHandle) {
1193 self.bodies.remove(
1194 handle,
1195 &mut self.islands,
1196 &mut self.colliders,
1197 &mut self.joints.set,
1198 &mut self.multibody_joints.set,
1199 true,
1200 );
1201 }
1202
1203 pub(super) fn add_collider(
1204 &mut self,
1205 owner: Handle<Node>,
1206 parent_body: RigidBodyHandle,
1207 mut collider: Collider,
1208 ) -> ColliderHandle {
1209 collider.user_data = owner.encode_to_u128();
1210 self.colliders
1211 .insert_with_parent(collider, parent_body, &mut self.bodies)
1212 }
1213
1214 pub(crate) fn remove_collider(&mut self, handle: ColliderHandle) -> bool {
1215 self.colliders
1216 .remove(handle, &mut self.islands, &mut self.bodies, false)
1217 .is_some()
1218 }
1219
1220 pub(super) fn add_joint(
1221 &mut self,
1222 owner: Handle<Node>,
1223 body1: RigidBodyHandle,
1224 body2: RigidBodyHandle,
1225 joint: GenericJoint,
1226 ) -> ImpulseJointHandle {
1227 let handle = self.joints.set.insert(body1, body2, joint, false);
1228 self.joints.map.insert(handle, owner);
1229 handle
1230 }
1231
1232 pub(crate) fn remove_joint(&mut self, handle: ImpulseJointHandle) {
1233 if self.joints.set.remove(handle, false).is_some() {
1234 assert!(self.joints.map.remove_by_key(&handle).is_some());
1235 }
1236 }
1237
1238 pub fn draw(&self, context: &mut SceneDrawingContext) {
1241 self.debug_render_pipeline.lock().render(
1242 context,
1243 &self.bodies,
1244 &self.colliders,
1245 &self.joints.set,
1246 &self.multibody_joints.set,
1247 &self.narrow_phase,
1248 );
1249 }
1250
1251 pub fn cast_ray<S: QueryResultsStorage>(&self, opts: RayCastOptions, query_buffer: &mut S) {
1253 let time = instant::Instant::now();
1254
1255 let mut query = self.query.borrow_mut();
1256
1257 query.update(&self.colliders);
1263
1264 query_buffer.clear();
1265 let ray = Ray::new(
1266 opts.ray_origin,
1267 opts.ray_direction
1268 .try_normalize(f32::EPSILON)
1269 .unwrap_or_default(),
1270 );
1271 query.intersections_with_ray(
1272 &self.bodies,
1273 &self.colliders,
1274 &ray,
1275 opts.max_len,
1276 true,
1277 rapier3d::pipeline::QueryFilter::new().groups(InteractionGroups::new(
1278 u32_to_group(opts.groups.memberships.0),
1279 u32_to_group(opts.groups.filter.0),
1280 )),
1281 |handle, intersection| {
1282 query_buffer.push(Intersection {
1283 collider: Handle::decode_from_u128(
1284 self.colliders.get(handle).unwrap().user_data,
1285 ),
1286 normal: intersection.normal,
1287 position: ray.point_at(intersection.time_of_impact),
1288 feature: intersection.feature.into(),
1289 toi: intersection.time_of_impact,
1290 })
1291 },
1292 );
1293 if opts.sort_results {
1294 query_buffer.sort_intersections_by(|a, b| {
1295 if a.toi > b.toi {
1296 Ordering::Greater
1297 } else if a.toi < b.toi {
1298 Ordering::Less
1299 } else {
1300 Ordering::Equal
1301 }
1302 })
1303 }
1304
1305 self.performance_statistics.total_ray_cast_time.set(
1306 self.performance_statistics.total_ray_cast_time.get()
1307 + (instant::Instant::now() - time),
1308 );
1309 }
1310
1311 pub fn cast_shape(
1331 &self,
1332 graph: &Graph,
1333 shape: &dyn Shape,
1334 shape_pos: &Isometry3<f32>,
1335 shape_vel: &Vector3<f32>,
1336 max_toi: f32,
1337 stop_at_penetration: bool,
1338 filter: QueryFilter,
1339 ) -> Option<(Handle<Node>, TOI)> {
1340 let predicate = |handle: ColliderHandle, _: &Collider| -> bool {
1341 if let Some(pred) = filter.predicate {
1342 let h = Handle::decode_from_u128(self.colliders.get(handle).unwrap().user_data);
1343 pred(
1344 h,
1345 graph.node(h).component_ref::<collider::Collider>().unwrap(),
1346 )
1347 } else {
1348 true
1349 }
1350 };
1351
1352 let filter = rapier3d::pipeline::QueryFilter {
1353 flags: rapier3d::pipeline::QueryFilterFlags::from_bits(filter.flags.bits()).unwrap(),
1354 groups: filter.groups.map(|g| {
1355 InteractionGroups::new(u32_to_group(g.memberships.0), u32_to_group(g.filter.0))
1356 }),
1357 exclude_collider: filter
1358 .exclude_collider
1359 .and_then(|h| graph.try_get(h))
1360 .and_then(|n| n.component_ref::<collider::Collider>())
1361 .map(|c| c.native.get()),
1362 exclude_rigid_body: filter
1363 .exclude_collider
1364 .and_then(|h| graph.try_get(h))
1365 .and_then(|n| n.component_ref::<rigidbody::RigidBody>())
1366 .map(|c| c.native.get()),
1367 predicate: Some(&predicate),
1368 };
1369
1370 let query = self.query.borrow_mut();
1371
1372 let opts = ShapeCastOptions {
1373 max_time_of_impact: max_toi,
1374 target_distance: 0.0,
1375 stop_at_penetration,
1376 compute_impact_geometry_on_penetration: true,
1377 };
1378
1379 query
1380 .cast_shape(
1381 &self.bodies,
1382 &self.colliders,
1383 shape_pos,
1384 shape_vel,
1385 shape,
1386 opts,
1387 filter,
1388 )
1389 .map(|(handle, toi)| {
1390 (
1391 Handle::decode_from_u128(self.colliders.get(handle).unwrap().user_data),
1392 TOI {
1393 toi: toi.time_of_impact,
1394 witness1: toi.witness1,
1395 witness2: toi.witness2,
1396 normal1: toi.normal1,
1397 normal2: toi.normal2,
1398 status: toi.status.into(),
1399 },
1400 )
1401 })
1402 }
1403
1404 pub(crate) fn set_rigid_body_position(
1405 &mut self,
1406 rigid_body: &scene::rigidbody::RigidBody,
1407 new_global_transform: &Matrix4<f32>,
1408 ) {
1409 if let Some(native) = self.bodies.get_mut(rigid_body.native.get()) {
1410 native.set_position(
1411 isometry_from_global_transform(new_global_transform),
1412 false,
1415 );
1416 }
1417 }
1418
1419 pub(crate) fn sync_rigid_body_node(
1420 &mut self,
1421 rigid_body: &mut scene::rigidbody::RigidBody,
1422 parent_transform: Matrix4<f32>,
1423 ) {
1424 if *self.enabled {
1425 if let Some(native) = self.bodies.get(rigid_body.native.get()) {
1426 if native.body_type() == RigidBodyType::Dynamic {
1427 let local_transform: Matrix4<f32> = parent_transform
1428 .try_inverse()
1429 .unwrap_or_else(Matrix4::identity)
1430 * native.position().to_homogeneous();
1431
1432 let new_local_rotation = UnitQuaternion::from_matrix_eps(
1433 &local_transform.basis(),
1434 f32::EPSILON,
1435 16,
1436 UnitQuaternion::identity(),
1437 );
1438 let new_local_position = Vector3::new(
1439 local_transform[12],
1440 local_transform[13],
1441 local_transform[14],
1442 );
1443
1444 let local_transform = rigid_body.local_transform();
1448 if **local_transform.position() != new_local_position
1449 || **local_transform.rotation() != new_local_rotation
1450 {
1451 rigid_body
1452 .local_transform_mut()
1453 .set_position(new_local_position)
1454 .set_rotation(new_local_rotation);
1455 }
1456
1457 rigid_body
1458 .lin_vel
1459 .set_value_with_flags(*native.linvel(), VariableFlags::MODIFIED);
1460 rigid_body
1461 .ang_vel
1462 .set_value_with_flags(*native.angvel(), VariableFlags::MODIFIED);
1463 rigid_body.sleeping = native.is_sleeping();
1464 }
1465 }
1466 }
1467 }
1468
1469 pub(crate) fn sync_to_rigid_body_node(
1470 &mut self,
1471 handle: Handle<Node>,
1472 rigid_body_node: &scene::rigidbody::RigidBody,
1473 ) {
1474 if !rigid_body_node.is_globally_enabled() {
1475 self.remove_body(rigid_body_node.native.get());
1476 rigid_body_node.native.set(Default::default());
1477 return;
1478 }
1479
1480 if rigid_body_node.native.get() != RigidBodyHandle::invalid() {
1484 let mut actions = rigid_body_node.actions.lock();
1485 if rigid_body_node.need_sync_model() || !actions.is_empty() {
1486 if let Some(native) = self.bodies.get_mut(rigid_body_node.native.get()) {
1487 rigid_body_node
1490 .body_type
1491 .try_sync_model(|v| native.set_body_type(v.into(), false));
1492 rigid_body_node
1493 .lin_vel
1494 .try_sync_model(|v| native.set_linvel(v, false));
1495 rigid_body_node
1496 .ang_vel
1497 .try_sync_model(|v| native.set_angvel(v, false));
1498 rigid_body_node
1499 .mass
1500 .try_sync_model(|v| native.set_additional_mass(v, true));
1501 rigid_body_node
1502 .lin_damping
1503 .try_sync_model(|v| native.set_linear_damping(v));
1504 rigid_body_node
1505 .ang_damping
1506 .try_sync_model(|v| native.set_angular_damping(v));
1507 rigid_body_node
1508 .ccd_enabled
1509 .try_sync_model(|v| native.enable_ccd(v));
1510 rigid_body_node.can_sleep.try_sync_model(|v| {
1511 let activation = native.activation_mut();
1512 if v {
1513 activation.normalized_linear_threshold =
1514 RigidBodyActivation::default_normalized_linear_threshold();
1515 activation.angular_threshold =
1516 RigidBodyActivation::default_angular_threshold();
1517 } else {
1518 activation.sleeping = false;
1519 activation.normalized_linear_threshold = -1.0;
1520 activation.angular_threshold = -1.0;
1521 };
1522 });
1523 rigid_body_node
1524 .translation_locked
1525 .try_sync_model(|v| native.lock_translations(v, false));
1526 rigid_body_node.x_rotation_locked.try_sync_model(|v| {
1527 native.set_enabled_rotations(
1528 !v,
1529 !native.is_rotation_locked()[1],
1530 !native.is_rotation_locked()[2],
1531 false,
1532 );
1533 });
1534 rigid_body_node.y_rotation_locked.try_sync_model(|v| {
1535 native.set_enabled_rotations(
1536 !native.is_rotation_locked()[0],
1537 !v,
1538 !native.is_rotation_locked()[2],
1539 false,
1540 );
1541 });
1542 rigid_body_node.z_rotation_locked.try_sync_model(|v| {
1543 native.set_enabled_rotations(
1544 !native.is_rotation_locked()[0],
1545 !native.is_rotation_locked()[1],
1546 !v,
1547 false,
1548 );
1549 });
1550 rigid_body_node
1551 .dominance
1552 .try_sync_model(|v| native.set_dominance_group(v));
1553 rigid_body_node
1554 .gravity_scale
1555 .try_sync_model(|v| native.set_gravity_scale(v, false));
1556
1557 if rigid_body_node.reset_forces.replace(false) {
1560 native.reset_forces(false);
1561 native.reset_torques(false);
1562 }
1563
1564 while let Some(action) = actions.pop_front() {
1565 match action {
1566 ApplyAction::Force(force) => {
1567 native.add_force(force, false);
1568 rigid_body_node.reset_forces.set(true);
1569 }
1570 ApplyAction::Torque(torque) => {
1571 native.add_torque(torque, false);
1572 rigid_body_node.reset_forces.set(true);
1573 }
1574 ApplyAction::ForceAtPoint { force, point } => {
1575 native.add_force_at_point(force, Point3::from(point), false);
1576 rigid_body_node.reset_forces.set(true);
1577 }
1578 ApplyAction::Impulse(impulse) => native.apply_impulse(impulse, false),
1579 ApplyAction::TorqueImpulse(impulse) => {
1580 native.apply_torque_impulse(impulse, false)
1581 }
1582 ApplyAction::ImpulseAtPoint { impulse, point } => {
1583 native.apply_impulse_at_point(impulse, Point3::from(point), false)
1584 }
1585 ApplyAction::WakeUp => native.wake_up(true),
1586 }
1587 }
1588 }
1589 }
1590 } else {
1591 let mut builder = RigidBodyBuilder::new(rigid_body_node.body_type().into())
1592 .position(isometry_from_global_transform(
1593 &rigid_body_node.global_transform(),
1594 ))
1595 .ccd_enabled(rigid_body_node.is_ccd_enabled())
1596 .additional_mass(rigid_body_node.mass())
1597 .angvel(*rigid_body_node.ang_vel)
1598 .linvel(*rigid_body_node.lin_vel)
1599 .linear_damping(*rigid_body_node.lin_damping)
1600 .angular_damping(*rigid_body_node.ang_damping)
1601 .can_sleep(rigid_body_node.is_can_sleep())
1602 .sleeping(rigid_body_node.is_sleeping())
1603 .dominance_group(rigid_body_node.dominance())
1604 .gravity_scale(rigid_body_node.gravity_scale())
1605 .enabled_rotations(
1606 !rigid_body_node.is_x_rotation_locked(),
1607 !rigid_body_node.is_y_rotation_locked(),
1608 !rigid_body_node.is_z_rotation_locked(),
1609 );
1610
1611 if rigid_body_node.is_translation_locked() {
1612 builder = builder.lock_translations();
1613 }
1614
1615 rigid_body_node
1616 .native
1617 .set(self.add_body(handle, builder.build()));
1618
1619 Log::writeln(
1620 MessageKind::Information,
1621 format!(
1622 "Native rigid body was created for node {}",
1623 rigid_body_node.name()
1624 ),
1625 );
1626 }
1627 }
1628
1629 pub(crate) fn sync_to_collider_node(
1630 &mut self,
1631 nodes: &NodePool,
1632 handle: Handle<Node>,
1633 collider_node: &scene::collider::Collider,
1634 ) {
1635 if !collider_node.is_globally_enabled() {
1636 self.remove_collider(collider_node.native.get());
1637 collider_node.native.set(Default::default());
1638 return;
1639 }
1640
1641 let anything_changed = collider_node.needs_sync_model();
1642
1643 if collider_node.native.get() != ColliderHandle::invalid() {
1649 if anything_changed {
1650 if let Some(native) = self.colliders.get_mut(collider_node.native.get()) {
1651 collider_node
1652 .restitution
1653 .try_sync_model(|v| native.set_restitution(v));
1654 collider_node.collision_groups.try_sync_model(|v| {
1655 native.set_collision_groups(InteractionGroups::new(
1656 u32_to_group(v.memberships.0),
1657 u32_to_group(v.filter.0),
1658 ))
1659 });
1660 collider_node.solver_groups.try_sync_model(|v| {
1661 native.set_solver_groups(InteractionGroups::new(
1662 u32_to_group(v.memberships.0),
1663 u32_to_group(v.filter.0),
1664 ))
1665 });
1666 collider_node
1667 .friction
1668 .try_sync_model(|v| native.set_friction(v));
1669 collider_node
1670 .is_sensor
1671 .try_sync_model(|v| native.set_sensor(v));
1672 collider_node
1673 .friction_combine_rule
1674 .try_sync_model(|v| native.set_friction_combine_rule(v.into()));
1675 collider_node
1676 .restitution_combine_rule
1677 .try_sync_model(|v| native.set_restitution_combine_rule(v.into()));
1678 let mut remove_collider = false;
1679 collider_node.shape.try_sync_model(|v| {
1680 let inv_global_transform = isometric_global_transform(nodes, handle)
1681 .try_inverse()
1682 .unwrap_or_default();
1683
1684 if let Some(shape) = collider_shape_into_native_shape(
1685 &v,
1686 inv_global_transform,
1687 handle,
1688 nodes,
1689 ) {
1690 native.set_shape(shape);
1691 } else {
1692 remove_collider = true;
1693 }
1694 });
1695 if remove_collider {
1696 self.remove_collider(collider_node.native.get());
1697 collider_node.native.set(ColliderHandle::invalid());
1698 }
1699 }
1700 }
1701 } else if let Some(parent_body) = nodes
1702 .try_borrow(collider_node.parent())
1703 .and_then(|n| n.cast::<scene::rigidbody::RigidBody>())
1704 {
1705 if parent_body.native.get() != RigidBodyHandle::invalid() {
1706 let inv_global_transform = isometric_global_transform(nodes, handle)
1707 .try_inverse()
1708 .unwrap();
1709 let rigid_body_native = parent_body.native.get();
1710 if let Some(shape) = collider_shape_into_native_shape(
1711 collider_node.shape(),
1712 inv_global_transform,
1713 handle,
1714 nodes,
1715 ) {
1716 let mut builder = ColliderBuilder::new(shape)
1717 .position(Isometry3 {
1718 rotation: **collider_node.local_transform().rotation(),
1719 translation: Translation3 {
1720 vector: **collider_node.local_transform().position(),
1721 },
1722 })
1723 .friction(collider_node.friction())
1724 .restitution(collider_node.restitution())
1725 .collision_groups(InteractionGroups::new(
1726 u32_to_group(collider_node.collision_groups().memberships.0),
1727 u32_to_group(collider_node.collision_groups().filter.0),
1728 ))
1729 .friction_combine_rule(collider_node.friction_combine_rule().into())
1730 .restitution_combine_rule(collider_node.restitution_combine_rule().into())
1731 .solver_groups(InteractionGroups::new(
1732 u32_to_group(collider_node.solver_groups().memberships.0),
1733 u32_to_group(collider_node.solver_groups().filter.0),
1734 ))
1735 .sensor(collider_node.is_sensor());
1736
1737 if let Some(density) = collider_node.density() {
1738 builder = builder.density(density);
1739 }
1740
1741 let native_handle =
1742 self.add_collider(handle, rigid_body_native, builder.build());
1743
1744 collider_node.native.set(native_handle);
1745
1746 Log::writeln(
1747 MessageKind::Information,
1748 format!(
1749 "Native collider was created for node {}",
1750 collider_node.name()
1751 ),
1752 );
1753 }
1754 }
1755 }
1756 }
1757
1758 pub(crate) fn sync_to_joint_node(
1759 &mut self,
1760 nodes: &NodePool,
1761 handle: Handle<Node>,
1762 joint: &scene::joint::Joint,
1763 ) {
1764 if !joint.is_globally_enabled() {
1765 self.remove_joint(joint.native.get());
1766 joint.native.set(ImpulseJointHandle(Default::default()));
1767 return;
1768 }
1769
1770 if let Some(native) = self.joints.set.get_mut(joint.native.get(), false) {
1771 joint.body1.try_sync_model(|v| {
1772 if let Some(rigid_body_node) = nodes
1773 .try_borrow(v)
1774 .and_then(|n| n.cast::<scene::rigidbody::RigidBody>())
1775 {
1776 native.body1 = rigid_body_node.native.get();
1777 }
1778 });
1779 joint.body2.try_sync_model(|v| {
1780 if let Some(rigid_body_node) = nodes
1781 .try_borrow(v)
1782 .and_then(|n| n.cast::<scene::rigidbody::RigidBody>())
1783 {
1784 native.body2 = rigid_body_node.native.get();
1785 }
1786 });
1787 joint.params.try_sync_model(|v| {
1788 native.data =
1789 convert_joint_params(v, native.data.local_frame1, native.data.local_frame2)
1791 });
1792 joint.contacts_enabled.try_sync_model(|v| {
1793 native.data.set_contacts_enabled(v);
1794 });
1795
1796 let mut local_frames = joint.local_frames.borrow_mut();
1797 if local_frames.is_none() {
1798 if let (Some(body1), Some(body2)) = (
1799 nodes
1800 .try_borrow(joint.body1())
1801 .and_then(|n| n.cast::<scene::rigidbody::RigidBody>()),
1802 nodes
1803 .try_borrow(joint.body2())
1804 .and_then(|n| n.cast::<scene::rigidbody::RigidBody>()),
1805 ) {
1806 let (local_frame1, local_frame2) = calculate_local_frames(joint, body1, body2);
1807 native.data =
1808 convert_joint_params((*joint.params).clone(), local_frame1, local_frame2);
1809 *local_frames = Some(JointLocalFrames::new(&local_frame1, &local_frame2));
1810 }
1811 }
1812 } else {
1813 let body1_handle = joint.body1();
1814 let body2_handle = joint.body2();
1815 let params = joint.params().clone();
1816
1817 if let (Some(body1), Some(body2)) = (
1820 nodes.try_borrow(body1_handle).and_then(|n| {
1821 n.cast::<scene::rigidbody::RigidBody>()
1822 .filter(|b| self.bodies.get(b.native.get()).is_some())
1823 }),
1824 nodes.try_borrow(body2_handle).and_then(|n| {
1825 n.cast::<scene::rigidbody::RigidBody>()
1826 .filter(|b| self.bodies.get(b.native.get()).is_some())
1827 }),
1828 ) {
1829 let mut local_frames = joint.local_frames.borrow_mut();
1831 let (local_frame1, local_frame2) = local_frames
1832 .clone()
1833 .map(|frames| {
1834 (
1835 Isometry3 {
1836 rotation: frames.body1.rotation,
1837 translation: Translation {
1838 vector: frames.body1.position,
1839 },
1840 },
1841 Isometry3 {
1842 rotation: frames.body2.rotation,
1843 translation: Translation {
1844 vector: frames.body2.position,
1845 },
1846 },
1847 )
1848 })
1849 .unwrap_or_else(|| calculate_local_frames(joint, body1, body2));
1850
1851 let native_body1 = body1.native.get();
1852 let native_body2 = body2.native.get();
1853
1854 let mut native_joint = convert_joint_params(params, local_frame1, local_frame2);
1855 native_joint.contacts_enabled = joint.is_contacts_enabled();
1856 let native_handle =
1857 self.add_joint(handle, native_body1, native_body2, native_joint);
1858
1859 joint.native.set(native_handle);
1860 *local_frames = Some(JointLocalFrames::new(&local_frame1, &local_frame2));
1861
1862 Log::writeln(
1863 MessageKind::Information,
1864 format!("Native joint was created for node {}", joint.name()),
1865 );
1866 }
1867 }
1868 }
1869
1870 pub(crate) fn intersections_with(
1872 &self,
1873 collider: ColliderHandle,
1874 ) -> impl Iterator<Item = IntersectionPair> + '_ {
1875 self.narrow_phase
1876 .intersection_pairs_with(collider)
1877 .filter_map(|(collider1, collider2, intersecting)| {
1878 Some(IntersectionPair {
1879 collider1: Handle::decode_from_u128(self.colliders.get(collider1)?.user_data),
1880 collider2: Handle::decode_from_u128(self.colliders.get(collider2)?.user_data),
1881 has_any_active_contact: intersecting,
1882 })
1883 })
1884 }
1885
1886 pub(crate) fn contacts_with(
1888 &self,
1889 collider: ColliderHandle,
1890 ) -> impl Iterator<Item = ContactPair> + '_ {
1891 self.narrow_phase
1892 .contact_pairs_with(collider)
1895 .filter_map(|c| ContactPair::from_native(c, self))
1896 }
1897
1898 pub fn contacts(&self) -> impl Iterator<Item = ContactPair> + '_ {
1900 self.narrow_phase
1901 .contact_pairs()
1902 .filter_map(|c| ContactPair::from_native(c, self))
1903 }
1904}
1905
1906impl Default for PhysicsWorld {
1907 fn default() -> Self {
1908 Self::new()
1909 }
1910}
1911
1912impl Debug for PhysicsWorld {
1913 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1914 write!(f, "PhysicsWorld")
1915 }
1916}