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;
51use fyrox_graph::constructor::ConstructorProvider;
52use fyrox_graph::{BaseSceneGraph, SceneGraphNode};
53use rapier3d::geometry::{self, ColliderHandle};
54use std::{
55 cell::Cell,
56 ops::{Add, BitAnd, BitOr, Deref, DerefMut, Mul, Not, Shl},
57};
58use strum_macros::{AsRefStr, EnumString, VariantNames};
59
60#[derive(Clone, Debug, PartialEq, Visit, Reflect)]
62pub struct BallShape {
63 #[reflect(min_value = 0.001, step = 0.05)]
65 pub radius: f32,
66}
67
68impl Default for BallShape {
69 fn default() -> Self {
70 Self { radius: 0.5 }
71 }
72}
73
74#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
76pub struct CylinderShape {
77 #[reflect(min_value = 0.001, step = 0.05)]
79 pub half_height: f32,
80 #[reflect(min_value = 0.001, step = 0.05)]
82 pub radius: f32,
83}
84
85impl Default for CylinderShape {
86 fn default() -> Self {
87 Self {
88 half_height: 0.5,
89 radius: 0.5,
90 }
91 }
92}
93
94#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
96pub struct ConeShape {
97 #[reflect(min_value = 0.001, step = 0.05)]
99 pub half_height: f32,
100 #[reflect(min_value = 0.001, step = 0.05)]
102 pub radius: f32,
103}
104
105impl Default for ConeShape {
106 fn default() -> Self {
107 Self {
108 half_height: 0.5,
109 radius: 0.5,
110 }
111 }
112}
113
114#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
116pub struct CuboidShape {
117 #[reflect(min_value = 0.001, step = 0.05)]
120 pub half_extents: Vector3<f32>,
121}
122
123impl Default for CuboidShape {
124 fn default() -> Self {
125 Self {
126 half_extents: Vector3::new(0.5, 0.5, 0.5),
127 }
128 }
129}
130
131#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
133pub struct CapsuleShape {
134 pub begin: Vector3<f32>,
136 pub end: Vector3<f32>,
138 #[reflect(min_value = 0.001, step = 0.05)]
140 pub radius: f32,
141}
142
143impl Default for CapsuleShape {
144 fn default() -> Self {
146 Self {
147 begin: Default::default(),
148 end: Vector3::new(0.0, 1.0, 0.0),
149 radius: 0.5,
150 }
151 }
152}
153
154#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
156pub struct SegmentShape {
157 pub begin: Vector3<f32>,
159 pub end: Vector3<f32>,
161}
162
163impl Default for SegmentShape {
164 fn default() -> Self {
165 Self {
166 begin: Default::default(),
167 end: Vector3::new(0.0, 1.0, 0.0),
168 }
169 }
170}
171
172#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
174pub struct TriangleShape {
175 pub a: Vector3<f32>,
177 pub b: Vector3<f32>,
179 pub c: Vector3<f32>,
181}
182
183impl Default for TriangleShape {
184 fn default() -> Self {
185 Self {
186 a: Default::default(),
187 b: Vector3::new(1.0, 0.0, 0.0),
188 c: Vector3::new(0.0, 0.0, 1.0),
189 }
190 }
191}
192
193#[derive(Default, Clone, Copy, PartialEq, Hash, Debug, Visit, Reflect, Eq)]
199pub struct GeometrySource(pub Handle<Node>);
200
201uuid_provider!(GeometrySource = "6fea7c72-c488-48a1-935f-2752a8a10e9a");
202
203#[derive(Default, Clone, Debug, Visit, Reflect, PartialEq, Eq)]
205pub struct TrimeshShape {
206 pub sources: Vec<GeometrySource>,
208}
209
210#[derive(Default, Clone, Debug, Visit, Reflect, PartialEq, Eq)]
212pub struct HeightfieldShape {
213 pub geometry_source: GeometrySource,
215}
216
217#[derive(Default, Clone, Debug, Visit, Reflect, PartialEq, Eq)]
219pub struct ConvexPolyhedronShape {
220 pub geometry_source: GeometrySource,
222}
223
224#[derive(Clone, Copy, Default, PartialEq, Debug, Reflect, Eq)]
226pub struct BitMask(pub u32);
227
228uuid_provider!(BitMask = "f2db0c2a-921b-4728-9ce4-2506d95c60fa");
229
230impl Visit for BitMask {
231 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
232 self.0.visit(name, visitor)
233 }
234}
235
236impl BitOr for BitMask {
237 type Output = Self;
238
239 fn bitor(self, rhs: Self) -> Self::Output {
240 Self(self.0 | rhs.0)
241 }
242}
243
244impl BitAnd for BitMask {
245 type Output = Self;
246
247 fn bitand(self, rhs: Self) -> Self::Output {
248 Self(self.0 & rhs.0)
249 }
250}
251
252impl Mul for BitMask {
253 type Output = Self;
254
255 fn mul(self, rhs: Self) -> Self::Output {
256 Self(self.0 * rhs.0)
257 }
258}
259
260impl One for BitMask {
261 fn one() -> Self {
262 Self(1)
263 }
264}
265
266impl Add for BitMask {
267 type Output = Self;
268
269 fn add(self, rhs: Self) -> Self::Output {
270 Self(self.0 + rhs.0)
271 }
272}
273
274impl Zero for BitMask {
275 fn zero() -> Self {
276 Self(0)
277 }
278
279 fn is_zero(&self) -> bool {
280 self.0 == 0
281 }
282}
283
284impl Shl for BitMask {
285 type Output = Self;
286
287 fn shl(self, rhs: Self) -> Self::Output {
288 Self(self.0 << rhs.0)
289 }
290}
291
292impl Not for BitMask {
293 type Output = Self;
294
295 fn not(self) -> Self::Output {
296 Self(!self.0)
297 }
298}
299
300impl ToPrimitive for BitMask {
301 fn to_i64(&self) -> Option<i64> {
302 Some(self.0 as i64)
303 }
304
305 fn to_u64(&self) -> Option<u64> {
306 Some(self.0 as u64)
307 }
308}
309
310impl NumCast for BitMask {
311 fn from<T: ToPrimitive>(n: T) -> Option<Self> {
312 n.to_u32().map(Self)
313 }
314}
315
316#[derive(Visit, Debug, Clone, Copy, PartialEq, Reflect, Eq)]
332pub struct InteractionGroups {
333 pub memberships: BitMask,
335 pub filter: BitMask,
337}
338
339impl InteractionGroups {
340 pub fn new(memberships: BitMask, filter: BitMask) -> Self {
342 Self {
343 memberships,
344 filter,
345 }
346 }
347}
348
349impl Default for InteractionGroups {
350 fn default() -> Self {
351 Self {
352 memberships: BitMask(u32::MAX),
353 filter: BitMask(u32::MAX),
354 }
355 }
356}
357
358impl From<geometry::InteractionGroups> for InteractionGroups {
359 fn from(g: geometry::InteractionGroups) -> Self {
360 Self {
361 memberships: BitMask(g.memberships.bits()),
362 filter: BitMask(g.filter.bits()),
363 }
364 }
365}
366
367bitflags::bitflags! {
368 #[derive(Default, Copy, Clone)]
369 pub struct QueryFilterFlags: u32 {
371 const EXCLUDE_FIXED = 1 << 1;
373 const EXCLUDE_KINEMATIC = 1 << 2;
375 const EXCLUDE_DYNAMIC = 1 << 3;
377 const EXCLUDE_SENSORS = 1 << 4;
379 const EXCLUDE_SOLIDS = 1 << 5;
381 const ONLY_DYNAMIC = Self::EXCLUDE_FIXED.bits() | Self::EXCLUDE_KINEMATIC.bits();
383 const ONLY_KINEMATIC = Self::EXCLUDE_DYNAMIC.bits() | Self::EXCLUDE_FIXED.bits();
385 const ONLY_FIXED = Self::EXCLUDE_DYNAMIC.bits() | Self::EXCLUDE_KINEMATIC.bits();
388 }
389}
390
391#[derive(Copy, Clone, Debug, PartialEq, Eq)]
393pub enum TOIStatus {
394 OutOfIterations,
399 Converged,
401 Failed,
406 Penetrating,
410}
411
412impl From<rapier3d::parry::query::ShapeCastStatus> for TOIStatus {
413 fn from(value: rapier3d::parry::query::ShapeCastStatus) -> Self {
414 match value {
415 rapier3d::parry::query::ShapeCastStatus::OutOfIterations => Self::OutOfIterations,
416 rapier3d::parry::query::ShapeCastStatus::Converged => Self::Converged,
417 rapier3d::parry::query::ShapeCastStatus::Failed => Self::Failed,
418 rapier3d::parry::query::ShapeCastStatus::PenetratingOrWithinTargetDist => {
419 Self::Penetrating
420 }
421 }
422 }
423}
424
425impl From<rapier2d::parry::query::ShapeCastStatus> for TOIStatus {
426 fn from(value: rapier2d::parry::query::ShapeCastStatus) -> Self {
427 match value {
428 rapier2d::parry::query::ShapeCastStatus::OutOfIterations => Self::OutOfIterations,
429 rapier2d::parry::query::ShapeCastStatus::Converged => Self::Converged,
430 rapier2d::parry::query::ShapeCastStatus::Failed => Self::Failed,
431 rapier2d::parry::query::ShapeCastStatus::PenetratingOrWithinTargetDist => {
432 Self::Penetrating
433 }
434 }
435 }
436}
437
438#[derive(Clone, Debug, PartialEq, Visit, Reflect, AsRefStr, EnumString, VariantNames)]
440pub enum ColliderShape {
441 Ball(BallShape),
443 Cylinder(CylinderShape),
445 Cone(ConeShape),
447 Cuboid(CuboidShape),
449 Capsule(CapsuleShape),
451 Segment(SegmentShape),
453 Triangle(TriangleShape),
455 Trimesh(TrimeshShape),
457 Heightfield(HeightfieldShape),
459 Polyhedron(ConvexPolyhedronShape),
461}
462
463uuid_provider!(ColliderShape = "2e627337-71ea-4b33-a5f1-be697f705a86");
464
465impl Default for ColliderShape {
466 fn default() -> Self {
467 Self::Ball(Default::default())
468 }
469}
470
471impl ColliderShape {
472 pub fn ball(radius: f32) -> Self {
474 Self::Ball(BallShape { radius })
475 }
476
477 pub fn cylinder(half_height: f32, radius: f32) -> Self {
480 Self::Cylinder(CylinderShape {
481 half_height,
482 radius,
483 })
484 }
485
486 pub fn cone(half_height: f32, radius: f32) -> Self {
489 Self::Cone(ConeShape {
490 half_height,
491 radius,
492 })
493 }
494
495 pub fn cuboid(hx: f32, hy: f32, hz: f32) -> Self {
497 Self::Cuboid(CuboidShape {
498 half_extents: Vector3::new(hx, hy, hz),
499 })
500 }
501
502 pub fn capsule(begin: Vector3<f32>, end: Vector3<f32>, radius: f32) -> Self {
504 Self::Capsule(CapsuleShape { begin, end, radius })
505 }
506
507 pub fn capsule_x(half_height: f32, radius: f32) -> Self {
509 let p = Vector3::x() * half_height;
510 Self::capsule(-p, p, radius)
511 }
512
513 pub fn capsule_y(half_height: f32, radius: f32) -> Self {
515 let p = Vector3::y() * half_height;
516 Self::capsule(-p, p, radius)
517 }
518
519 pub fn capsule_z(half_height: f32, radius: f32) -> Self {
521 let p = Vector3::z() * half_height;
522 Self::capsule(-p, p, radius)
523 }
524
525 pub fn segment(begin: Vector3<f32>, end: Vector3<f32>) -> Self {
527 Self::Segment(SegmentShape { begin, end })
528 }
529
530 pub fn triangle(a: Vector3<f32>, b: Vector3<f32>, c: Vector3<f32>) -> Self {
532 Self::Triangle(TriangleShape { a, b, c })
533 }
534
535 pub fn trimesh(geometry_sources: Vec<GeometrySource>) -> Self {
538 Self::Trimesh(TrimeshShape {
539 sources: geometry_sources,
540 })
541 }
542
543 pub fn heightfield(geometry_source: GeometrySource) -> Self {
545 Self::Heightfield(HeightfieldShape { geometry_source })
546 }
547}
548
549#[derive(Reflect, Visit, Debug, ComponentProvider)]
552pub struct Collider {
553 base: Base,
554
555 #[reflect(setter = "set_shape")]
556 pub(crate) shape: InheritableVariable<ColliderShape>,
557
558 #[reflect(min_value = 0.0, step = 0.05, setter = "set_friction")]
559 pub(crate) friction: InheritableVariable<f32>,
560
561 #[reflect(setter = "set_density")]
562 pub(crate) density: InheritableVariable<Option<f32>>,
563
564 #[reflect(min_value = 0.0, step = 0.05, setter = "set_restitution")]
565 pub(crate) restitution: InheritableVariable<f32>,
566
567 #[reflect(setter = "set_is_sensor")]
568 pub(crate) is_sensor: InheritableVariable<bool>,
569
570 #[reflect(setter = "set_collision_groups")]
571 pub(crate) collision_groups: InheritableVariable<InteractionGroups>,
572
573 #[reflect(setter = "set_solver_groups")]
574 pub(crate) solver_groups: InheritableVariable<InteractionGroups>,
575
576 #[reflect(setter = "set_friction_combine_rule")]
577 pub(crate) friction_combine_rule: InheritableVariable<CoefficientCombineRule>,
578
579 #[reflect(setter = "set_restitution_combine_rule")]
580 pub(crate) restitution_combine_rule: InheritableVariable<CoefficientCombineRule>,
581
582 #[visit(skip)]
583 #[reflect(hidden)]
584 pub(crate) native: Cell<ColliderHandle>,
585}
586
587impl Default for Collider {
588 fn default() -> Self {
589 Self {
590 base: Default::default(),
591 shape: Default::default(),
592 friction: InheritableVariable::new_modified(0.0),
593 density: InheritableVariable::new_modified(None),
594 restitution: InheritableVariable::new_modified(0.0),
595 is_sensor: InheritableVariable::new_modified(false),
596 collision_groups: Default::default(),
597 solver_groups: Default::default(),
598 friction_combine_rule: Default::default(),
599 restitution_combine_rule: Default::default(),
600 native: Cell::new(ColliderHandle::invalid()),
601 }
602 }
603}
604
605impl Deref for Collider {
606 type Target = Base;
607
608 fn deref(&self) -> &Self::Target {
609 &self.base
610 }
611}
612
613impl DerefMut for Collider {
614 fn deref_mut(&mut self) -> &mut Self::Target {
615 &mut self.base
616 }
617}
618
619impl Clone for Collider {
620 fn clone(&self) -> Self {
621 Self {
622 base: self.base.clone(),
623 shape: self.shape.clone(),
624 friction: self.friction.clone(),
625 density: self.density.clone(),
626 restitution: self.restitution.clone(),
627 is_sensor: self.is_sensor.clone(),
628 collision_groups: self.collision_groups.clone(),
629 solver_groups: self.solver_groups.clone(),
630 friction_combine_rule: self.friction_combine_rule.clone(),
631 restitution_combine_rule: self.restitution_combine_rule.clone(),
632 native: Cell::new(ColliderHandle::invalid()),
634 }
635 }
636}
637
638impl TypeUuidProvider for Collider {
639 fn type_uuid() -> Uuid {
640 uuid!("bfaa2e82-9c19-4b99-983b-3bc115744a1d")
641 }
642}
643
644impl Collider {
645 pub fn set_shape(&mut self, shape: ColliderShape) -> ColliderShape {
653 self.shape.set_value_and_mark_modified(shape)
654 }
655
656 pub fn shape(&self) -> &ColliderShape {
658 &self.shape
659 }
660
661 pub fn shape_value(&self) -> ColliderShape {
663 (*self.shape).clone()
664 }
665
666 pub fn shape_mut(&mut self) -> &mut ColliderShape {
674 self.shape.get_value_mut_and_mark_modified()
675 }
676
677 pub fn set_restitution(&mut self, restitution: f32) -> f32 {
687 self.restitution.set_value_and_mark_modified(restitution)
688 }
689
690 pub fn restitution(&self) -> f32 {
692 *self.restitution
693 }
694
695 pub fn set_density(&mut self, density: Option<f32>) -> Option<f32> {
710 self.density.set_value_and_mark_modified(density)
711 }
712
713 pub fn density(&self) -> Option<f32> {
715 *self.density
716 }
717
718 pub fn set_friction(&mut self, friction: f32) -> f32 {
728 self.friction.set_value_and_mark_modified(friction)
729 }
730
731 pub fn friction(&self) -> f32 {
733 *self.friction
734 }
735
736 pub fn set_collision_groups(&mut self, groups: InteractionGroups) -> InteractionGroups {
744 self.collision_groups.set_value_and_mark_modified(groups)
745 }
746
747 pub fn collision_groups(&self) -> InteractionGroups {
749 *self.collision_groups
750 }
751
752 pub fn set_solver_groups(&mut self, groups: InteractionGroups) -> InteractionGroups {
760 self.solver_groups.set_value_and_mark_modified(groups)
761 }
762
763 pub fn solver_groups(&self) -> InteractionGroups {
765 *self.solver_groups
766 }
767
768 pub fn set_is_sensor(&mut self, is_sensor: bool) -> bool {
777 self.is_sensor.set_value_and_mark_modified(is_sensor)
778 }
779
780 pub fn is_sensor(&self) -> bool {
782 *self.is_sensor
783 }
784
785 pub fn set_friction_combine_rule(
793 &mut self,
794 rule: CoefficientCombineRule,
795 ) -> CoefficientCombineRule {
796 self.friction_combine_rule.set_value_and_mark_modified(rule)
797 }
798
799 pub fn friction_combine_rule(&self) -> CoefficientCombineRule {
801 *self.friction_combine_rule
802 }
803
804 pub fn set_restitution_combine_rule(
812 &mut self,
813 rule: CoefficientCombineRule,
814 ) -> CoefficientCombineRule {
815 self.restitution_combine_rule
816 .set_value_and_mark_modified(rule)
817 }
818
819 pub fn restitution_combine_rule(&self) -> CoefficientCombineRule {
821 *self.restitution_combine_rule
822 }
823
824 pub fn contacts<'a>(
827 &self,
828 physics: &'a PhysicsWorld,
829 ) -> impl Iterator<Item = ContactPair> + 'a {
830 physics.contacts_with(self.native.get())
831 }
832
833 pub fn intersects<'a>(
836 &self,
837 physics: &'a PhysicsWorld,
838 ) -> impl Iterator<Item = IntersectionPair> + 'a {
839 physics.intersections_with(self.native.get())
840 }
841
842 pub(crate) fn needs_sync_model(&self) -> bool {
843 self.shape.need_sync()
844 || self.friction.need_sync()
845 || self.density.need_sync()
846 || self.restitution.need_sync()
847 || self.is_sensor.need_sync()
848 || self.collision_groups.need_sync()
849 || self.solver_groups.need_sync()
850 || self.friction_combine_rule.need_sync()
851 || self.restitution_combine_rule.need_sync()
852 }
853}
854
855impl ConstructorProvider<Node, Graph> for Collider {
856 fn constructor() -> NodeConstructor {
857 NodeConstructor::new::<Self>()
858 .with_variant("Collider", |_| {
859 ColliderBuilder::new(BaseBuilder::new().with_name("Collider"))
860 .with_shape(ColliderShape::Cuboid(Default::default()))
861 .build_node()
862 .into()
863 })
864 .with_group("Physics")
865 }
866}
867
868impl NodeTrait for Collider {
869 fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
870 self.base.local_bounding_box()
871 }
872
873 fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
874 self.base.world_bounding_box()
875 }
876
877 fn id(&self) -> Uuid {
878 Self::type_uuid()
879 }
880
881 fn on_removed_from_graph(&mut self, graph: &mut Graph) {
882 graph.physics.remove_collider(self.native.get());
883 self.native.set(ColliderHandle::invalid());
884
885 Log::info(format!(
886 "Native collider was removed for node: {}",
887 self.name()
888 ));
889 }
890
891 fn on_unlink(&mut self, graph: &mut Graph) {
892 if graph.physics.remove_collider(self.native.get()) {
893 self.native.set(ColliderHandle::invalid());
895 }
896 }
897
898 fn on_local_transform_changed(&self, context: &mut SyncContext) {
899 if self.native.get() != ColliderHandle::invalid() {
900 if let Some(native) = context.physics.colliders.get_mut(self.native.get()) {
901 native.set_position_wrt_parent(Isometry3 {
902 rotation: **self.local_transform().rotation(),
903 translation: Translation3 {
904 vector: **self.local_transform().position(),
905 },
906 });
907 }
908 }
909 }
910
911 fn sync_native(&self, self_handle: Handle<Node>, context: &mut SyncContext) {
912 context
913 .physics
914 .sync_to_collider_node(context.nodes, self_handle, self);
915 }
916
917 fn validate(&self, scene: &Scene) -> Result<(), String> {
918 let mut message = String::new();
919
920 if scene
921 .graph
922 .try_get(self.parent())
923 .and_then(|p| p.component_ref::<RigidBody>())
924 .is_none()
925 {
926 message += "3D Collider must be a direct child of a 3D Rigid Body node, \
927 otherwise it will not have any effect!";
928 }
929
930 match &*self.shape {
931 ColliderShape::Trimesh(trimesh) => {
932 for source in trimesh.sources.iter() {
933 if !scene.graph.is_valid_handle(source.0) {
934 message += &format!("Trimesh data source {} handle is invalid!", source.0);
935 }
936 }
937 }
938 ColliderShape::Heightfield(heightfield) => {
939 if !scene.graph.is_valid_handle(heightfield.geometry_source.0) {
940 message += &format!(
941 "Heightfield data source {} handle is invalid!",
942 heightfield.geometry_source.0
943 );
944 }
945 }
946 ColliderShape::Polyhedron(polyhedron) => {
947 if !scene.graph.is_valid_handle(polyhedron.geometry_source.0) {
948 message += &format!(
949 "Polyhedron data source {} handle is invalid!",
950 polyhedron.geometry_source.0
951 );
952 }
953 }
954 _ => (),
955 }
956
957 if message.is_empty() {
958 Ok(())
959 } else {
960 Err(message)
961 }
962 }
963}
964
965pub struct ColliderBuilder {
967 base_builder: BaseBuilder,
968 shape: ColliderShape,
969 friction: f32,
970 density: Option<f32>,
971 restitution: f32,
972 is_sensor: bool,
973 collision_groups: InteractionGroups,
974 solver_groups: InteractionGroups,
975 friction_combine_rule: CoefficientCombineRule,
976 restitution_combine_rule: CoefficientCombineRule,
977}
978
979impl ColliderBuilder {
980 pub fn new(base_builder: BaseBuilder) -> Self {
982 Self {
983 base_builder,
984 shape: Default::default(),
985 friction: 0.0,
986 density: None,
987 restitution: 0.0,
988 is_sensor: false,
989 collision_groups: Default::default(),
990 solver_groups: Default::default(),
991 friction_combine_rule: Default::default(),
992 restitution_combine_rule: Default::default(),
993 }
994 }
995
996 pub fn with_shape(mut self, shape: ColliderShape) -> Self {
998 self.shape = shape;
999 self
1000 }
1001
1002 pub fn with_density(mut self, density: Option<f32>) -> Self {
1004 self.density = density;
1005 self
1006 }
1007
1008 pub fn with_restitution(mut self, restitution: f32) -> Self {
1010 self.restitution = restitution;
1011 self
1012 }
1013
1014 pub fn with_friction(mut self, friction: f32) -> Self {
1016 self.friction = friction;
1017 self
1018 }
1019
1020 pub fn with_sensor(mut self, sensor: bool) -> Self {
1022 self.is_sensor = sensor;
1023 self
1024 }
1025
1026 pub fn with_solver_groups(mut self, solver_groups: InteractionGroups) -> Self {
1028 self.solver_groups = solver_groups;
1029 self
1030 }
1031
1032 pub fn with_collision_groups(mut self, collision_groups: InteractionGroups) -> Self {
1034 self.collision_groups = collision_groups;
1035 self
1036 }
1037
1038 pub fn with_friction_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
1040 self.friction_combine_rule = rule;
1041 self
1042 }
1043
1044 pub fn with_restitution_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
1046 self.restitution_combine_rule = rule;
1047 self
1048 }
1049
1050 pub fn build_collider(self) -> Collider {
1052 Collider {
1053 base: self.base_builder.build_base(),
1054 shape: self.shape.into(),
1055 friction: self.friction.into(),
1056 density: self.density.into(),
1057 restitution: self.restitution.into(),
1058 is_sensor: self.is_sensor.into(),
1059 collision_groups: self.collision_groups.into(),
1060 solver_groups: self.solver_groups.into(),
1061 friction_combine_rule: self.friction_combine_rule.into(),
1062 restitution_combine_rule: self.restitution_combine_rule.into(),
1063 native: Cell::new(ColliderHandle::invalid()),
1064 }
1065 }
1066
1067 pub fn build_node(self) -> Node {
1069 Node::new(self.build_collider())
1070 }
1071
1072 pub fn build(self, graph: &mut Graph) -> Handle<Node> {
1074 graph.add_node(self.build_node())
1075 }
1076}
1077
1078#[cfg(test)]
1079mod test {
1080 use crate::core::algebra::Vector2;
1081 use crate::scene::{
1082 base::BaseBuilder,
1083 collider::{ColliderBuilder, ColliderShape},
1084 graph::Graph,
1085 rigidbody::{RigidBodyBuilder, RigidBodyType},
1086 };
1087
1088 #[test]
1089 fn test_collider_intersect() {
1090 let mut graph = Graph::new();
1091
1092 let mut create_rigid_body = |is_sensor| {
1093 let cube_half_size = 0.5;
1094 let collider_sensor = ColliderBuilder::new(BaseBuilder::new())
1095 .with_shape(ColliderShape::cuboid(
1096 cube_half_size,
1097 cube_half_size,
1098 cube_half_size,
1099 ))
1100 .with_sensor(is_sensor)
1101 .build(&mut graph);
1102
1103 RigidBodyBuilder::new(BaseBuilder::new().with_children(&[collider_sensor]))
1104 .with_body_type(RigidBodyType::Static)
1105 .build(&mut graph);
1106
1107 collider_sensor
1108 };
1109
1110 let collider_sensor = create_rigid_body(true);
1111 let collider_non_sensor = create_rigid_body(false);
1112
1113 graph.update(Vector2::new(800.0, 600.0), 1.0, Default::default());
1115 graph.update(Vector2::new(800.0, 600.0), 1.0, Default::default());
1116
1117 assert_eq!(
1119 0,
1120 graph[collider_sensor]
1121 .as_collider()
1122 .contacts(&graph.physics)
1123 .count()
1124 );
1125 assert_eq!(
1126 0,
1127 graph[collider_non_sensor]
1128 .as_collider()
1129 .contacts(&graph.physics)
1130 .count()
1131 );
1132
1133 assert_eq!(
1135 1,
1136 graph[collider_sensor]
1137 .as_collider()
1138 .intersects(&graph.physics)
1139 .count()
1140 );
1141 assert_eq!(
1142 1,
1143 graph[collider_non_sensor]
1144 .as_collider()
1145 .intersects(&graph.physics)
1146 .count()
1147 );
1148 }
1149}