1use crate::scene::node::constructor::NodeConstructor;
25use crate::{
26 core::{
27 algebra::Vector3,
28 log::Log,
29 math::aabb::AxisAlignedBoundingBox,
30 num_traits::{NumCast, One, ToPrimitive, Zero},
31 pool::Handle,
32 reflect::prelude::*,
33 type_traits::prelude::*,
34 uuid::{uuid, Uuid},
35 variable::InheritableVariable,
36 visitor::prelude::*,
37 },
38 scene::{
39 base::{Base, BaseBuilder},
40 graph::{
41 physics::{CoefficientCombineRule, ContactPair, IntersectionPair, PhysicsWorld},
42 Graph,
43 },
44 node::{Node, NodeTrait, SyncContext},
45 rigidbody::RigidBody,
46 Scene,
47 },
48};
49use fyrox_core::algebra::{Isometry3, Translation3};
50use fyrox_core::uuid_provider;
51
52use fyrox_graph::constructor::ConstructorProvider;
53use fyrox_graph::SceneGraph;
54use rapier3d::geometry::{self, ColliderHandle};
55use std::fmt::Write;
56use std::{
57 cell::Cell,
58 ops::{Add, BitAnd, BitOr, Deref, DerefMut, Mul, Not, Shl},
59};
60use strum_macros::{AsRefStr, EnumString, VariantNames};
61
62#[derive(Clone, Debug, PartialEq, Visit, Reflect)]
64pub struct BallShape {
65 #[reflect(min_value = 0.001, step = 0.05)]
67 pub radius: f32,
68}
69
70impl Default for BallShape {
71 fn default() -> Self {
72 Self { radius: 0.5 }
73 }
74}
75
76#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
78pub struct CylinderShape {
79 #[reflect(min_value = 0.001, step = 0.05)]
81 pub half_height: f32,
82 #[reflect(min_value = 0.001, step = 0.05)]
84 pub radius: f32,
85}
86
87impl Default for CylinderShape {
88 fn default() -> Self {
89 Self {
90 half_height: 0.5,
91 radius: 0.5,
92 }
93 }
94}
95
96#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
98pub struct ConeShape {
99 #[reflect(min_value = 0.001, step = 0.05)]
101 pub half_height: f32,
102 #[reflect(min_value = 0.001, step = 0.05)]
104 pub radius: f32,
105}
106
107impl Default for ConeShape {
108 fn default() -> Self {
109 Self {
110 half_height: 0.5,
111 radius: 0.5,
112 }
113 }
114}
115
116#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
118pub struct CuboidShape {
119 #[reflect(min_value = 0.001, step = 0.05)]
122 pub half_extents: Vector3<f32>,
123}
124
125impl Default for CuboidShape {
126 fn default() -> Self {
127 Self {
128 half_extents: Vector3::new(0.5, 0.5, 0.5),
129 }
130 }
131}
132
133#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
135pub struct CapsuleShape {
136 pub begin: Vector3<f32>,
138 pub end: Vector3<f32>,
140 #[reflect(min_value = 0.001, step = 0.05)]
142 pub radius: f32,
143}
144
145impl Default for CapsuleShape {
146 fn default() -> Self {
148 Self {
149 begin: Default::default(),
150 end: Vector3::new(0.0, 1.0, 0.0),
151 radius: 0.5,
152 }
153 }
154}
155
156#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
158pub struct SegmentShape {
159 pub begin: Vector3<f32>,
161 pub end: Vector3<f32>,
163}
164
165impl Default for SegmentShape {
166 fn default() -> Self {
167 Self {
168 begin: Default::default(),
169 end: Vector3::new(0.0, 1.0, 0.0),
170 }
171 }
172}
173
174#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
176pub struct TriangleShape {
177 pub a: Vector3<f32>,
179 pub b: Vector3<f32>,
181 pub c: Vector3<f32>,
183}
184
185impl Default for TriangleShape {
186 fn default() -> Self {
187 Self {
188 a: Default::default(),
189 b: Vector3::new(1.0, 0.0, 0.0),
190 c: Vector3::new(0.0, 0.0, 1.0),
191 }
192 }
193}
194
195#[derive(Default, Clone, Copy, PartialEq, Hash, Debug, Visit, Reflect, Eq)]
201pub struct GeometrySource(pub Handle<Node>);
202
203uuid_provider!(GeometrySource = "6fea7c72-c488-48a1-935f-2752a8a10e9a");
204
205#[derive(Default, Clone, Debug, Visit, Reflect, PartialEq, Eq)]
207pub struct TrimeshShape {
208 pub sources: Vec<GeometrySource>,
210}
211
212#[derive(Default, Clone, Debug, Visit, Reflect, PartialEq, Eq)]
214pub struct HeightfieldShape {
215 pub geometry_source: GeometrySource,
217}
218
219#[derive(Default, Clone, Debug, Visit, Reflect, PartialEq, Eq)]
221pub struct ConvexPolyhedronShape {
222 pub geometry_source: GeometrySource,
224}
225
226#[derive(Clone, Copy, Default, PartialEq, Reflect, Eq)]
228pub struct BitMask(pub u32);
229
230uuid_provider!(BitMask = "f2db0c2a-921b-4728-9ce4-2506d95c60fa");
231
232impl BitMask {
233 pub const fn all() -> Self {
235 Self(u32::MAX)
236 }
237 pub const fn none() -> Self {
239 Self(0)
240 }
241 pub const fn with(self, index: usize) -> Self {
243 Self(self.0 | (1 << index))
244 }
245 pub const fn without(self, index: usize) -> Self {
247 Self(self.0 & !(1 << index))
248 }
249 pub fn bit(&self, index: usize) -> bool {
251 (self.0 >> index) & 1 != 0
252 }
253 pub fn set_bit(&mut self, index: usize, value: bool) {
255 if value {
256 self.0 |= 1 << index;
257 } else {
258 self.0 &= !(1 << index);
259 }
260 }
261}
262
263impl std::fmt::Debug for BitMask {
264 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
265 write!(f, "BitMask({:08x})", self.0)
266 }
267}
268
269impl std::fmt::Display for BitMask {
270 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272 let mut value = self.0;
273 for i in 0..4 {
274 if i != 0 {
275 f.write_char(' ')?;
276 }
277 for _ in 0..8 {
278 let bit = if value & 1 == 1 { '1' } else { '0' };
279 value >>= 1;
280 f.write_char(bit)?;
281 }
282 }
283 Ok(())
284 }
285}
286
287impl Visit for BitMask {
288 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
289 self.0.visit(name, visitor)
290 }
291}
292
293impl BitOr for BitMask {
294 type Output = Self;
295
296 fn bitor(self, rhs: Self) -> Self::Output {
297 Self(self.0 | rhs.0)
298 }
299}
300
301impl BitAnd for BitMask {
302 type Output = Self;
303
304 fn bitand(self, rhs: Self) -> Self::Output {
305 Self(self.0 & rhs.0)
306 }
307}
308
309impl Mul for BitMask {
310 type Output = Self;
311
312 fn mul(self, rhs: Self) -> Self::Output {
313 Self(self.0 * rhs.0)
314 }
315}
316
317impl One for BitMask {
318 fn one() -> Self {
319 Self(1)
320 }
321}
322
323impl Add for BitMask {
324 type Output = Self;
325
326 fn add(self, rhs: Self) -> Self::Output {
327 Self(self.0 + rhs.0)
328 }
329}
330
331impl Zero for BitMask {
332 fn zero() -> Self {
333 Self(0)
334 }
335
336 fn is_zero(&self) -> bool {
337 self.0 == 0
338 }
339}
340
341impl Shl for BitMask {
342 type Output = Self;
343
344 fn shl(self, rhs: Self) -> Self::Output {
345 Self(self.0 << rhs.0)
346 }
347}
348
349impl Not for BitMask {
350 type Output = Self;
351
352 fn not(self) -> Self::Output {
353 Self(!self.0)
354 }
355}
356
357impl ToPrimitive for BitMask {
358 fn to_i64(&self) -> Option<i64> {
359 Some(self.0 as i64)
360 }
361
362 fn to_u64(&self) -> Option<u64> {
363 Some(self.0 as u64)
364 }
365}
366
367impl NumCast for BitMask {
368 fn from<T: ToPrimitive>(n: T) -> Option<Self> {
369 n.to_u32().map(Self)
370 }
371}
372
373#[derive(Visit, Debug, Clone, Copy, PartialEq, Reflect, Eq)]
389pub struct InteractionGroups {
390 pub memberships: BitMask,
392 pub filter: BitMask,
394}
395
396impl InteractionGroups {
397 pub fn new(memberships: BitMask, filter: BitMask) -> Self {
399 Self {
400 memberships,
401 filter,
402 }
403 }
404}
405
406impl std::fmt::Display for InteractionGroups {
407 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
408 write!(f, "{} (filter: {})", self.memberships, self.filter)
409 }
410}
411
412impl Default for InteractionGroups {
413 fn default() -> Self {
414 Self {
415 memberships: BitMask::all(),
416 filter: BitMask::all(),
417 }
418 }
419}
420
421impl From<geometry::InteractionGroups> for InteractionGroups {
422 fn from(g: geometry::InteractionGroups) -> Self {
423 Self {
424 memberships: BitMask(g.memberships.bits()),
425 filter: BitMask(g.filter.bits()),
426 }
427 }
428}
429
430bitflags::bitflags! {
431 #[derive(Default, Copy, Clone)]
432 pub struct QueryFilterFlags: u32 {
434 const EXCLUDE_FIXED = 1 << 1;
436 const EXCLUDE_KINEMATIC = 1 << 2;
438 const EXCLUDE_DYNAMIC = 1 << 3;
440 const EXCLUDE_SENSORS = 1 << 4;
442 const EXCLUDE_SOLIDS = 1 << 5;
444 const ONLY_DYNAMIC = Self::EXCLUDE_FIXED.bits() | Self::EXCLUDE_KINEMATIC.bits();
446 const ONLY_KINEMATIC = Self::EXCLUDE_DYNAMIC.bits() | Self::EXCLUDE_FIXED.bits();
448 const ONLY_FIXED = Self::EXCLUDE_DYNAMIC.bits() | Self::EXCLUDE_KINEMATIC.bits();
451 }
452}
453
454#[derive(Copy, Clone, Debug, PartialEq, Eq)]
456pub enum TOIStatus {
457 OutOfIterations,
462 Converged,
464 Failed,
469 Penetrating,
473}
474
475impl From<rapier3d::parry::query::ShapeCastStatus> for TOIStatus {
476 fn from(value: rapier3d::parry::query::ShapeCastStatus) -> Self {
477 match value {
478 rapier3d::parry::query::ShapeCastStatus::OutOfIterations => Self::OutOfIterations,
479 rapier3d::parry::query::ShapeCastStatus::Converged => Self::Converged,
480 rapier3d::parry::query::ShapeCastStatus::Failed => Self::Failed,
481 rapier3d::parry::query::ShapeCastStatus::PenetratingOrWithinTargetDist => {
482 Self::Penetrating
483 }
484 }
485 }
486}
487
488impl From<rapier2d::parry::query::ShapeCastStatus> for TOIStatus {
489 fn from(value: rapier2d::parry::query::ShapeCastStatus) -> Self {
490 match value {
491 rapier2d::parry::query::ShapeCastStatus::OutOfIterations => Self::OutOfIterations,
492 rapier2d::parry::query::ShapeCastStatus::Converged => Self::Converged,
493 rapier2d::parry::query::ShapeCastStatus::Failed => Self::Failed,
494 rapier2d::parry::query::ShapeCastStatus::PenetratingOrWithinTargetDist => {
495 Self::Penetrating
496 }
497 }
498 }
499}
500
501#[derive(Clone, Debug, PartialEq, Visit, Reflect, AsRefStr, EnumString, VariantNames)]
503pub enum ColliderShape {
504 Ball(BallShape),
506 Cylinder(CylinderShape),
508 Cone(ConeShape),
510 Cuboid(CuboidShape),
512 Capsule(CapsuleShape),
514 Segment(SegmentShape),
516 Triangle(TriangleShape),
518 Trimesh(TrimeshShape),
520 Heightfield(HeightfieldShape),
522 Polyhedron(ConvexPolyhedronShape),
524}
525
526uuid_provider!(ColliderShape = "2e627337-71ea-4b33-a5f1-be697f705a86");
527
528impl Default for ColliderShape {
529 fn default() -> Self {
530 Self::Ball(Default::default())
531 }
532}
533
534impl ColliderShape {
535 pub fn ball(radius: f32) -> Self {
537 Self::Ball(BallShape { radius })
538 }
539
540 pub fn cylinder(half_height: f32, radius: f32) -> Self {
543 Self::Cylinder(CylinderShape {
544 half_height,
545 radius,
546 })
547 }
548
549 pub fn cone(half_height: f32, radius: f32) -> Self {
552 Self::Cone(ConeShape {
553 half_height,
554 radius,
555 })
556 }
557
558 pub fn cuboid(hx: f32, hy: f32, hz: f32) -> Self {
560 Self::Cuboid(CuboidShape {
561 half_extents: Vector3::new(hx, hy, hz),
562 })
563 }
564
565 pub fn capsule(begin: Vector3<f32>, end: Vector3<f32>, radius: f32) -> Self {
567 Self::Capsule(CapsuleShape { begin, end, radius })
568 }
569
570 pub fn capsule_x(half_height: f32, radius: f32) -> Self {
572 let p = Vector3::x() * half_height;
573 Self::capsule(-p, p, radius)
574 }
575
576 pub fn capsule_y(half_height: f32, radius: f32) -> Self {
578 let p = Vector3::y() * half_height;
579 Self::capsule(-p, p, radius)
580 }
581
582 pub fn capsule_z(half_height: f32, radius: f32) -> Self {
584 let p = Vector3::z() * half_height;
585 Self::capsule(-p, p, radius)
586 }
587
588 pub fn segment(begin: Vector3<f32>, end: Vector3<f32>) -> Self {
590 Self::Segment(SegmentShape { begin, end })
591 }
592
593 pub fn triangle(a: Vector3<f32>, b: Vector3<f32>, c: Vector3<f32>) -> Self {
595 Self::Triangle(TriangleShape { a, b, c })
596 }
597
598 pub fn trimesh(geometry_sources: Vec<GeometrySource>) -> Self {
601 Self::Trimesh(TrimeshShape {
602 sources: geometry_sources,
603 })
604 }
605
606 pub fn heightfield(geometry_source: GeometrySource) -> Self {
608 Self::Heightfield(HeightfieldShape { geometry_source })
609 }
610}
611
612#[derive(Reflect, Visit, Debug, ComponentProvider)]
615#[reflect(derived_type = "Node")]
616pub struct Collider {
617 base: Base,
618
619 #[reflect(setter = "set_shape")]
620 pub(crate) shape: InheritableVariable<ColliderShape>,
621
622 #[reflect(min_value = 0.0, step = 0.05, setter = "set_friction")]
623 pub(crate) friction: InheritableVariable<f32>,
624
625 #[reflect(setter = "set_density")]
626 pub(crate) density: InheritableVariable<Option<f32>>,
627
628 #[reflect(min_value = 0.0, step = 0.05, setter = "set_restitution")]
629 pub(crate) restitution: InheritableVariable<f32>,
630
631 #[reflect(setter = "set_is_sensor")]
632 pub(crate) is_sensor: InheritableVariable<bool>,
633
634 #[reflect(setter = "set_collision_groups")]
635 pub(crate) collision_groups: InheritableVariable<InteractionGroups>,
636
637 #[reflect(setter = "set_solver_groups")]
638 pub(crate) solver_groups: InheritableVariable<InteractionGroups>,
639
640 #[reflect(setter = "set_friction_combine_rule")]
641 pub(crate) friction_combine_rule: InheritableVariable<CoefficientCombineRule>,
642
643 #[reflect(setter = "set_restitution_combine_rule")]
644 pub(crate) restitution_combine_rule: InheritableVariable<CoefficientCombineRule>,
645
646 #[visit(skip)]
647 #[reflect(hidden)]
648 pub(crate) native: Cell<ColliderHandle>,
649}
650
651impl Default for Collider {
652 fn default() -> Self {
653 Self {
654 base: Default::default(),
655 shape: Default::default(),
656 friction: InheritableVariable::new_modified(0.0),
657 density: InheritableVariable::new_modified(None),
658 restitution: InheritableVariable::new_modified(0.0),
659 is_sensor: InheritableVariable::new_modified(false),
660 collision_groups: Default::default(),
661 solver_groups: Default::default(),
662 friction_combine_rule: Default::default(),
663 restitution_combine_rule: Default::default(),
664 native: Cell::new(ColliderHandle::invalid()),
665 }
666 }
667}
668
669impl Deref for Collider {
670 type Target = Base;
671
672 fn deref(&self) -> &Self::Target {
673 &self.base
674 }
675}
676
677impl DerefMut for Collider {
678 fn deref_mut(&mut self) -> &mut Self::Target {
679 &mut self.base
680 }
681}
682
683impl Clone for Collider {
684 fn clone(&self) -> Self {
685 Self {
686 base: self.base.clone(),
687 shape: self.shape.clone(),
688 friction: self.friction.clone(),
689 density: self.density.clone(),
690 restitution: self.restitution.clone(),
691 is_sensor: self.is_sensor.clone(),
692 collision_groups: self.collision_groups.clone(),
693 solver_groups: self.solver_groups.clone(),
694 friction_combine_rule: self.friction_combine_rule.clone(),
695 restitution_combine_rule: self.restitution_combine_rule.clone(),
696 native: Cell::new(ColliderHandle::invalid()),
698 }
699 }
700}
701
702impl TypeUuidProvider for Collider {
703 fn type_uuid() -> Uuid {
704 uuid!("bfaa2e82-9c19-4b99-983b-3bc115744a1d")
705 }
706}
707
708impl Collider {
709 pub fn set_shape(&mut self, shape: ColliderShape) -> ColliderShape {
717 self.shape.set_value_and_mark_modified(shape)
718 }
719
720 pub fn shape(&self) -> &ColliderShape {
722 &self.shape
723 }
724
725 pub fn shape_value(&self) -> ColliderShape {
727 (*self.shape).clone()
728 }
729
730 pub fn shape_mut(&mut self) -> &mut ColliderShape {
738 self.shape.get_value_mut_and_mark_modified()
739 }
740
741 pub fn set_restitution(&mut self, restitution: f32) -> f32 {
751 self.restitution.set_value_and_mark_modified(restitution)
752 }
753
754 pub fn restitution(&self) -> f32 {
756 *self.restitution
757 }
758
759 pub fn set_density(&mut self, density: Option<f32>) -> Option<f32> {
774 self.density.set_value_and_mark_modified(density)
775 }
776
777 pub fn density(&self) -> Option<f32> {
779 *self.density
780 }
781
782 pub fn set_friction(&mut self, friction: f32) -> f32 {
792 self.friction.set_value_and_mark_modified(friction)
793 }
794
795 pub fn friction(&self) -> f32 {
797 *self.friction
798 }
799
800 pub fn set_collision_groups(&mut self, groups: InteractionGroups) -> InteractionGroups {
808 self.collision_groups.set_value_and_mark_modified(groups)
809 }
810
811 pub fn collision_groups(&self) -> InteractionGroups {
813 *self.collision_groups
814 }
815
816 pub fn set_solver_groups(&mut self, groups: InteractionGroups) -> InteractionGroups {
824 self.solver_groups.set_value_and_mark_modified(groups)
825 }
826
827 pub fn solver_groups(&self) -> InteractionGroups {
829 *self.solver_groups
830 }
831
832 pub fn set_is_sensor(&mut self, is_sensor: bool) -> bool {
841 self.is_sensor.set_value_and_mark_modified(is_sensor)
842 }
843
844 pub fn is_sensor(&self) -> bool {
846 *self.is_sensor
847 }
848
849 pub fn set_friction_combine_rule(
857 &mut self,
858 rule: CoefficientCombineRule,
859 ) -> CoefficientCombineRule {
860 self.friction_combine_rule.set_value_and_mark_modified(rule)
861 }
862
863 pub fn friction_combine_rule(&self) -> CoefficientCombineRule {
865 *self.friction_combine_rule
866 }
867
868 pub fn set_restitution_combine_rule(
876 &mut self,
877 rule: CoefficientCombineRule,
878 ) -> CoefficientCombineRule {
879 self.restitution_combine_rule
880 .set_value_and_mark_modified(rule)
881 }
882
883 pub fn restitution_combine_rule(&self) -> CoefficientCombineRule {
885 *self.restitution_combine_rule
886 }
887
888 pub fn contacts<'a>(
906 &self,
907 physics: &'a PhysicsWorld,
908 ) -> impl Iterator<Item = ContactPair> + 'a {
909 physics.contacts_with(self.native.get())
910 }
911
912 pub fn active_contacts<'a>(
927 &self,
928 physics: &'a PhysicsWorld,
929 ) -> impl Iterator<Item = ContactPair> + 'a {
930 self.contacts(physics)
931 .filter(|pair| pair.has_any_active_contact)
932 }
933
934 pub fn intersects<'a>(
948 &self,
949 physics: &'a PhysicsWorld,
950 ) -> impl Iterator<Item = IntersectionPair> + 'a {
951 physics.intersections_with(self.native.get())
952 }
953
954 pub fn active_intersects<'a>(
958 &self,
959 physics: &'a PhysicsWorld,
960 ) -> impl Iterator<Item = Handle<Collider>> + 'a {
961 let self_handle = self.handle().to_variant();
962 self.intersects(physics)
963 .filter(|pair| pair.has_any_active_contact)
964 .map(move |pair| pair.other(self_handle))
965 }
966
967 pub(crate) fn needs_sync_model(&self) -> bool {
968 self.shape.need_sync()
969 || self.friction.need_sync()
970 || self.density.need_sync()
971 || self.restitution.need_sync()
972 || self.is_sensor.need_sync()
973 || self.collision_groups.need_sync()
974 || self.solver_groups.need_sync()
975 || self.friction_combine_rule.need_sync()
976 || self.restitution_combine_rule.need_sync()
977 }
978}
979
980impl ConstructorProvider<Node, Graph> for Collider {
981 fn constructor() -> NodeConstructor {
982 NodeConstructor::new::<Self>()
983 .with_variant("Collider", |_| {
984 ColliderBuilder::new(BaseBuilder::new().with_name("Collider"))
985 .with_shape(ColliderShape::Cuboid(Default::default()))
986 .build_node()
987 .into()
988 })
989 .with_group("Physics")
990 }
991}
992
993impl NodeTrait for Collider {
994 fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
995 self.base.local_bounding_box()
996 }
997
998 fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
999 self.base.world_bounding_box()
1000 }
1001
1002 fn id(&self) -> Uuid {
1003 Self::type_uuid()
1004 }
1005
1006 fn on_removed_from_graph(&mut self, graph: &mut Graph) {
1007 graph.physics.remove_collider(self.native.get());
1008 self.native.set(ColliderHandle::invalid());
1009
1010 Log::info(format!(
1011 "Native collider was removed for node: {}",
1012 self.name()
1013 ));
1014 }
1015
1016 fn on_unlink(&mut self, graph: &mut Graph) {
1017 if graph.physics.remove_collider(self.native.get()) {
1018 self.native.set(ColliderHandle::invalid());
1020 }
1021 }
1022
1023 fn on_local_transform_changed(&self, context: &mut SyncContext) {
1024 if self.native.get() != ColliderHandle::invalid() {
1025 if let Some(native) = context.physics.colliders.get_mut(self.native.get()) {
1026 native.set_position_wrt_parent(Isometry3 {
1027 rotation: **self.local_transform().rotation(),
1028 translation: Translation3 {
1029 vector: **self.local_transform().position(),
1030 },
1031 });
1032 }
1033 }
1034 }
1035
1036 fn sync_native(&self, self_handle: Handle<Node>, context: &mut SyncContext) {
1037 context
1038 .physics
1039 .sync_to_collider_node(context.nodes, self_handle, self);
1040 }
1041
1042 fn validate(&self, scene: &Scene) -> Result<(), String> {
1043 let mut message = String::new();
1044
1045 if scene
1046 .graph
1047 .try_get_of_type::<RigidBody>(self.parent())
1048 .is_err()
1049 {
1050 message += "3D Collider must be a direct child of a 3D Rigid Body node, \
1051 otherwise it will not have any effect!";
1052 }
1053
1054 match &*self.shape {
1055 ColliderShape::Trimesh(trimesh) => {
1056 for source in trimesh.sources.iter() {
1057 if !scene.graph.is_valid_handle(source.0) {
1058 message += &format!("Trimesh data source {} handle is invalid!", source.0);
1059 }
1060 }
1061 }
1062 ColliderShape::Heightfield(heightfield) => {
1063 if !scene.graph.is_valid_handle(heightfield.geometry_source.0) {
1064 message += &format!(
1065 "Heightfield data source {} handle is invalid!",
1066 heightfield.geometry_source.0
1067 );
1068 }
1069 }
1070 ColliderShape::Polyhedron(polyhedron) => {
1071 if !scene.graph.is_valid_handle(polyhedron.geometry_source.0) {
1072 message += &format!(
1073 "Polyhedron data source {} handle is invalid!",
1074 polyhedron.geometry_source.0
1075 );
1076 }
1077 }
1078 _ => (),
1079 }
1080
1081 if message.is_empty() {
1082 Ok(())
1083 } else {
1084 Err(message)
1085 }
1086 }
1087}
1088
1089pub struct ColliderBuilder {
1091 base_builder: BaseBuilder,
1092 shape: ColliderShape,
1093 friction: f32,
1094 density: Option<f32>,
1095 restitution: f32,
1096 is_sensor: bool,
1097 collision_groups: InteractionGroups,
1098 solver_groups: InteractionGroups,
1099 friction_combine_rule: CoefficientCombineRule,
1100 restitution_combine_rule: CoefficientCombineRule,
1101}
1102
1103impl ColliderBuilder {
1104 pub fn new(base_builder: BaseBuilder) -> Self {
1106 Self {
1107 base_builder,
1108 shape: Default::default(),
1109 friction: 0.0,
1110 density: None,
1111 restitution: 0.0,
1112 is_sensor: false,
1113 collision_groups: Default::default(),
1114 solver_groups: Default::default(),
1115 friction_combine_rule: Default::default(),
1116 restitution_combine_rule: Default::default(),
1117 }
1118 }
1119
1120 pub fn with_shape(mut self, shape: ColliderShape) -> Self {
1122 self.shape = shape;
1123 self
1124 }
1125
1126 pub fn with_density(mut self, density: Option<f32>) -> Self {
1128 self.density = density;
1129 self
1130 }
1131
1132 pub fn with_restitution(mut self, restitution: f32) -> Self {
1134 self.restitution = restitution;
1135 self
1136 }
1137
1138 pub fn with_friction(mut self, friction: f32) -> Self {
1140 self.friction = friction;
1141 self
1142 }
1143
1144 pub fn with_sensor(mut self, sensor: bool) -> Self {
1146 self.is_sensor = sensor;
1147 self
1148 }
1149
1150 pub fn with_solver_groups(mut self, solver_groups: InteractionGroups) -> Self {
1152 self.solver_groups = solver_groups;
1153 self
1154 }
1155
1156 pub fn with_collision_groups(mut self, collision_groups: InteractionGroups) -> Self {
1158 self.collision_groups = collision_groups;
1159 self
1160 }
1161
1162 pub fn with_friction_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
1164 self.friction_combine_rule = rule;
1165 self
1166 }
1167
1168 pub fn with_restitution_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
1170 self.restitution_combine_rule = rule;
1171 self
1172 }
1173
1174 pub fn build_collider(self) -> Collider {
1176 Collider {
1177 base: self.base_builder.build_base(),
1178 shape: self.shape.into(),
1179 friction: self.friction.into(),
1180 density: self.density.into(),
1181 restitution: self.restitution.into(),
1182 is_sensor: self.is_sensor.into(),
1183 collision_groups: self.collision_groups.into(),
1184 solver_groups: self.solver_groups.into(),
1185 friction_combine_rule: self.friction_combine_rule.into(),
1186 restitution_combine_rule: self.restitution_combine_rule.into(),
1187 native: Cell::new(ColliderHandle::invalid()),
1188 }
1189 }
1190
1191 pub fn build_node(self) -> Node {
1193 Node::new(self.build_collider())
1194 }
1195
1196 pub fn build(self, graph: &mut Graph) -> Handle<Collider> {
1198 graph.add_node(self.build_node()).to_variant()
1199 }
1200}
1201
1202#[cfg(test)]
1203mod test {
1204 use super::*;
1205 use crate::core::algebra::Vector2;
1206 use crate::scene::{
1207 base::BaseBuilder,
1208 collider::{ColliderBuilder, ColliderShape},
1209 graph::Graph,
1210 rigidbody::{RigidBodyBuilder, RigidBodyType},
1211 };
1212
1213 #[test]
1214 fn test_collider_intersect() {
1215 let mut graph = Graph::new();
1216
1217 let mut create_rigid_body = |is_sensor| {
1218 let cube_half_size = 0.5;
1219 let collider_sensor = ColliderBuilder::new(BaseBuilder::new())
1220 .with_shape(ColliderShape::cuboid(
1221 cube_half_size,
1222 cube_half_size,
1223 cube_half_size,
1224 ))
1225 .with_sensor(is_sensor)
1226 .build(&mut graph);
1227
1228 RigidBodyBuilder::new(BaseBuilder::new().with_child(collider_sensor))
1229 .with_body_type(RigidBodyType::Static)
1230 .build(&mut graph);
1231
1232 collider_sensor
1233 };
1234
1235 let collider_sensor = create_rigid_body(true);
1236 let collider_non_sensor = create_rigid_body(false);
1237
1238 graph.update(Vector2::new(800.0, 600.0), 1.0, Default::default());
1240 graph.update(Vector2::new(800.0, 600.0), 1.0, Default::default());
1241
1242 assert_eq!(0, graph[collider_sensor].contacts(&graph.physics).count());
1244 assert_eq!(
1245 0,
1246 graph[collider_non_sensor].contacts(&graph.physics).count()
1247 );
1248
1249 assert_eq!(1, graph[collider_sensor].intersects(&graph.physics).count());
1251 assert_eq!(
1252 1,
1253 graph[collider_non_sensor]
1254 .intersects(&graph.physics)
1255 .count()
1256 );
1257 }
1258 #[test]
1259 fn test_bitmask_display() {
1260 assert_eq!(
1261 BitMask(1).to_string(),
1262 "10000000 00000000 00000000 00000000"
1263 );
1264 assert_eq!(
1265 BitMask(15).to_string(),
1266 "11110000 00000000 00000000 00000000"
1267 );
1268 assert_eq!(
1269 BitMask(16).to_string(),
1270 "00001000 00000000 00000000 00000000"
1271 );
1272 assert_eq!(
1273 BitMask(256).to_string(),
1274 "00000000 10000000 00000000 00000000"
1275 );
1276 assert_eq!(
1277 BitMask(1 << 31).to_string(),
1278 "00000000 00000000 00000000 00000001"
1279 );
1280 }
1281 #[test]
1282 fn test_bitmask_debug() {
1283 assert_eq!(format!("{:?}", BitMask(1)), "BitMask(00000001)");
1284 assert_eq!(format!("{:?}", BitMask(15)), "BitMask(0000000f)");
1285 assert_eq!(format!("{:?}", BitMask(16)), "BitMask(00000010)");
1286 assert_eq!(format!("{:?}", BitMask(256)), "BitMask(00000100)");
1287 assert_eq!(format!("{:?}", BitMask(1 << 31)), "BitMask(80000000)");
1288 }
1289 #[test]
1290 fn test_bitmask_set_bit() {
1291 let mut mask = BitMask::none();
1292 mask.set_bit(0, true);
1293 assert!(mask.bit(0));
1294 assert_eq!(mask.to_string(), "10000000 00000000 00000000 00000000");
1295 mask.set_bit(3, true);
1296 assert!(mask.bit(3));
1297 assert_eq!(mask.to_string(), "10010000 00000000 00000000 00000000");
1298 mask.set_bit(0, false);
1299 assert!(!mask.bit(0));
1300 assert_eq!(mask.to_string(), "00010000 00000000 00000000 00000000");
1301 }
1302 #[test]
1303 fn test_bitmask_with() {
1304 let mask = BitMask::none().with(8);
1305 assert_eq!(mask.to_string(), "00000000 10000000 00000000 00000000");
1306 let mask = BitMask::all().without(8);
1307 assert_eq!(mask.to_string(), "11111111 01111111 11111111 11111111");
1308 let mask = BitMask::none().with(1).with(2).with(3);
1309 assert_eq!(mask.to_string(), "01110000 00000000 00000000 00000000");
1310 }
1311}