diff options
| author | Sébastien Crozet <developer@crozet.re> | 2021-06-02 17:15:46 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-06-02 17:15:46 +0200 |
| commit | 6ba1c9dec184adcba2c68cc1851dc05587fd0bf0 (patch) | |
| tree | b672cfc4db1d2f426dad931d77098ecb4a600358 /src | |
| parent | 3bac79ecacdeaa18de19127b7a6c82cbfab29d14 (diff) | |
| parent | bde6657287cd32a801abb996322c520673406418 (diff) | |
| download | rapier-6ba1c9dec184adcba2c68cc1851dc05587fd0bf0.tar.gz rapier-6ba1c9dec184adcba2c68cc1851dc05587fd0bf0.tar.bz2 rapier-6ba1c9dec184adcba2c68cc1851dc05587fd0bf0.zip | |
Merge pull request #196 from dimforge/api_changes
More API changes
Diffstat (limited to 'src')
32 files changed, 1111 insertions, 558 deletions
diff --git a/src/data/coarena.rs b/src/data/coarena.rs index cd64910..ac6e43f 100644 --- a/src/data/coarena.rs +++ b/src/data/coarena.rs @@ -13,6 +13,14 @@ impl<T> Coarena<T> { Self { data: Vec::new() } } + /// Gets a specific element from the coarena without specifying its generation number. + /// + /// It is strongly encouraged to use `Coarena::get` instead of this method because this method + /// can suffer from the ABA problem. + pub fn get_unknown_gen(&self, index: u32) -> Option<&T> { + self.data.get(index as usize).map(|(_, t)| t) + } + /// Gets a specific element from the coarena, if it exists. pub fn get(&self, index: Index) -> Option<&T> { let (i, g) = index.into_raw_parts(); diff --git a/src/dynamics/ccd/ccd_solver.rs b/src/dynamics/ccd/ccd_solver.rs index b348283..7e95f08 100644 --- a/src/dynamics/ccd/ccd_solver.rs +++ b/src/dynamics/ccd/ccd_solver.rs @@ -10,6 +10,7 @@ use crate::geometry::{ use crate::math::Real; use crate::parry::utils::SortedPair; use crate::pipeline::{EventHandler, QueryPipeline, QueryPipelineMode}; +use crate::prelude::{ActiveEvents, ColliderFlags}; use parry::query::{DefaultQueryDispatcher, QueryDispatcher}; use parry::utils::hashmap::HashMap; use std::collections::BinaryHeap; @@ -66,7 +67,7 @@ impl CCDSolver { &RigidBodyCcd, &RigidBodyMassProps, ) = bodies.index_bundle(handle.0); - let local_com = &mprops.mass_properties.local_com; + let local_com = &mprops.local_mprops.local_com; let min_toi = (ccd.ccd_thickness * 0.15 @@ -139,7 +140,8 @@ impl CCDSolver { Colliders: ComponentSetOption<ColliderParent> + ComponentSet<ColliderPosition> + ComponentSet<ColliderShape> - + ComponentSet<ColliderType>, + + ComponentSet<ColliderType> + + ComponentSet<ColliderFlags>, { // Update the query pipeline. self.query_pipeline.update_with_mode( @@ -200,16 +202,21 @@ impl CCDSolver { { let co_parent1: Option<&ColliderParent> = colliders.get(ch1.0); let co_parent2: Option<&ColliderParent> = colliders.get(ch2.0); - let c1: (_, _, _) = colliders.index_bundle(ch1.0); - let c2: (_, _, _) = colliders.index_bundle(ch2.0); + let c1: (_, _, _, &ColliderFlags) = colliders.index_bundle(ch1.0); + let c2: (_, _, _, &ColliderFlags) = colliders.index_bundle(ch2.0); let co_type1: &ColliderType = colliders.index(ch1.0); let co_type2: &ColliderType = colliders.index(ch1.0); let bh1 = co_parent1.map(|p| p.handle); let bh2 = co_parent2.map(|p| p.handle); - if bh1 == bh2 || (co_type1.is_sensor() || co_type2.is_sensor()) { - // Ignore self-intersection and sensors. + // Ignore self-intersection and sensors and apply collision groups filter. + if bh1 == bh2 // Ignore self-intersection. + || (co_type1.is_sensor() || co_type2.is_sensor()) // Ignore sensors. + || !c1.3.collision_groups.test(c2.3.collision_groups) // Apply collision groups. + || !c1.3.solver_groups.test(c2.3.solver_groups) + // Apply solver groups. + { return true; } @@ -225,8 +232,8 @@ impl CCDSolver { self.query_pipeline.query_dispatcher(), *ch1, *ch2, - (c1.0, c1.1, c1.2, co_parent1), - (c2.0, c2.1, c2.2, co_parent2), + (c1.0, c1.1, c1.2, c1.3, co_parent1), + (c2.0, c2.1, c2.2, c2.3, co_parent2), Some((rb_pos1, rb_vels1, rb_mprops1, rb_ccd1)), b2, None, @@ -272,7 +279,9 @@ impl CCDSolver { Colliders: ComponentSetOption<ColliderParent> + ComponentSet<ColliderPosition> + ComponentSet<ColliderShape> - + ComponentSet<ColliderType>, + + ComponentSet<ColliderType> + + ComponentSet<ColliderFlags> + + ComponentSet<ColliderFlags>, { let mut frozen = HashMap::<_, Real>::default(); let mut all_toi = BinaryHeap::new(); @@ -334,14 +343,15 @@ impl CCDSolver { { let co_parent1: Option<&ColliderParent> = colliders.get(ch1.0); let co_parent2: Option<&ColliderParent> = colliders.get(ch2.0); - let c1: (_, _, _) = colliders.index_bundle(ch1.0); - let c2: (_, _, _) = colliders.index_bundle(ch2.0); + let c1: (_, _, _, &ColliderFlags) = colliders.index_bundle(ch1.0); + let c2: (_, _, _, &ColliderFlags) = colliders.index_bundle(ch2.0); let bh1 = co_parent1.map(|p| p.handle); let bh2 = co_parent2.map(|p| p.handle); - if bh1 == bh2 { - // Ignore self-intersection. + // Ignore self-intersections and apply groups filter. + if bh1 == bh2 || !c1.3.collision_groups.test(c2.3.collision_groups) + { return true; } @@ -358,8 +368,8 @@ impl CCDSolver { self.query_pipeline.query_dispatcher(), *ch1, *ch2, - (c1.0, c1.1, c1.2, co_parent1), - (c2.0, c2.1, c2.2, co_parent2), + (c1.0, c1.1, c1.2, c1.3, co_parent1), + (c2.0, c2.1, c2.2, c2.3, co_parent2), b1, b2, None, @@ -398,7 +408,7 @@ impl CCDSolver { // NOTE: all static bodies (and kinematic bodies?) should be considered as "frozen", this // may avoid some resweeps. - let mut intersections_to_check = vec![]; + let mut pseudo_intersections_to_check = vec![]; while let Some(toi) = all_toi.pop() { assert!(toi.toi <= dt); @@ -420,7 +430,7 @@ impl CCDSolver { continue; } - if toi.is_intersection_test { + if toi.is_pseudo_intersection_test { // NOTE: this test is redundant with the previous `if !should_freeze && ...` // but let's keep it to avoid tricky regressions if we end up swapping both // `if` for some reasons in the future. @@ -428,7 +438,7 @@ impl CCDSolver { // This is only an intersection so we don't have to freeze and there is no // need to resweep. However we will need to see if we have to generate // intersection events, so push the TOI for further testing. - intersections_to_check.push(toi); + pseudo_intersections_to_check.push(toi); } continue; } @@ -460,14 +470,14 @@ impl CCDSolver { .colliders_with_aabb_intersecting_aabb(&aabb, |ch2| { let co_parent1: Option<&ColliderParent> = colliders.get(ch1.0); let co_parent2: Option<&ColliderParent> = colliders.get(ch2.0); - let c1: (_, _, _) = colliders.index_bundle(ch1.0); - let c2: (_, _, _) = colliders.index_bundle(ch2.0); + let c1: (_, _, _, &ColliderFlags) = colliders.index_bundle(ch1.0); + let c2: (_, _, _, &ColliderFlags) = colliders.index_bundle(ch2.0); let bh1 = co_parent1.map(|p| p.handle); let bh2 = co_parent2.map(|p| p.handle); - if bh1 == bh2 { - // Ignore self-intersection. + // Ignore self-intersection and apply groups filter. + if bh1 == bh2 || !c1.3.collision_groups.test(c2.3.collision_groups) { return true; } @@ -496,8 +506,8 @@ impl CCDSolver { self.query_pipeline.query_dispatcher(), *ch1, *ch2, - (c1.0, c1.1, c1.2, co_parent1), - (c2.0, c2.1, c2.2, co_parent2), + (c1.0, c1.1, c1.2, c1.3, co_parent1), + (c2.0, c2.1, c2.2, c2.3, co_parent2), b1, b2, frozen1.copied(), @@ -514,7 +524,7 @@ impl CCDSolver { } } - for toi in intersections_to_check { + for toi in pseudo_intersections_to_check { // See if the intersection is still active once the bodies // reach their final positions. // - If the intersection is still active, don't report it yet. It will be @@ -522,10 +532,29 @@ impl CCDSolver { // - If the intersection isn't active anymore, and it wasn't intersecting // before, then we need to generate one interaction-start and one interaction-stop // events because it will never be detected by the narrow-phase because of tunneling. - let (co_pos1, co_shape1): (&ColliderPosition, &ColliderShape) = - colliders.index_bundle(toi.c1.0); - let (co_pos2, co_shape2): (&ColliderPosition, &ColliderShape) = - colliders.index_bundle(toi.c2.0); + let (co_type1, co_pos1, co_shape1, co_flags1): ( + &ColliderType, + &ColliderPosition, + &ColliderShape, + &ColliderFlags, + ) = colliders.index_bundle(toi.c1.0); + let (co_type2, co_pos2, co_shape2, co_flags2): ( + &ColliderType, + &ColliderPosition, + &ColliderShape, + &ColliderFlags, + ) = colliders.index_bundle(toi.c2.0); + + if !co_type1.is_sensor() && !co_type2.is_sensor() { + // TODO: this happens if we found a TOI between two non-sensor + // colliders with mismatching solver_flags. It is not clear + // what we should do in this case: we could report a + // contact started/contact stopped event for example. But in + // that case, what contact pair should be pass to these events? + // For now we just ignore this special case. Let's wait for an actual + // use-case to come up before we determine what we want to do here. + continue; + } let co_next_pos1 = if let Some(b1) = toi.b1 { let co_parent1: &ColliderParent = colliders.get(toi.c1.0).unwrap(); @@ -535,7 +564,7 @@ impl CCDSolver { &RigidBodyMassProps, ) = bodies.index_bundle(b1.0); - let local_com1 = &rb_mprops1.mass_properties.local_com; + let local_com1 = &rb_mprops1.local_mprops.local_com; let frozen1 = frozen.get(&b1); let pos1 = frozen1 .map(|t| rb_vels1.integrate(*t, &rb_pos1.position, local_com1)) @@ -553,7 +582,7 @@ impl CCDSolver { &RigidBodyMassProps, ) = bodies.index_bundle(b2.0); - let local_com2 = &rb_mprops2.mass_properties.local_com; + let local_com2 = &rb_mprops2.local_mprops.local_com; let frozen2 = frozen.get(&b2); let pos2 = frozen2 .map(|t| rb_vels2.integrate(*t, &rb_pos2.position, local_com2)) @@ -575,7 +604,11 @@ impl CCDSolver { .intersection_test(&next_coll_pos12, co_shape1.as_ref(), co_shape2.as_ref()) .unwrap_or(false); - if !intersect_before && !intersect_after { + if !intersect_before + && !intersect_after + && (co_flags1.active_events | co_flags2.active_events) + .contains(ActiveEvents::INTERSECTION_EVENTS) + { // Emit one intersection-started and one intersection-stopped event. events.handle_intersection_event(IntersectionEvent::new(toi.c1, toi.c2, true)); events.handle_intersection_event(IntersectionEvent::new(toi.c1, toi.c2, false)); diff --git a/src/dynamics/ccd/toi_entry.rs b/src/dynamics/ccd/toi_entry.rs index 4637940..f937979 100644 --- a/src/dynamics/ccd/toi_entry.rs +++ b/src/dynamics/ccd/toi_entry.rs @@ -2,7 +2,7 @@ use crate::dynamics::{ RigidBodyCcd, RigidBodyHandle, RigidBodyMassProps, RigidBodyPosition, RigidBodyVelocity, }; use crate::geometry::{ - ColliderHandle, ColliderParent, ColliderPosition, ColliderShape, ColliderType, + ColliderFlags, ColliderHandle, ColliderParent, ColliderPosition, ColliderShape, ColliderType, }; use crate::math::Real; use parry::query::{NonlinearRigidMotion, QueryDispatcher}; @@ -14,7 +14,9 @@ pub struct TOIEntry { pub b1: Option<RigidBodyHandle>, pub c2: ColliderHandle, pub b2: Option<RigidBodyHandle>, - pub is_intersection_test: bool, + // We call this "pseudo" intersection because this also + // includes colliders pairs with mismatching solver_groups. + pub is_pseudo_intersection_test: bool, pub timestamp: usize, } @@ -25,7 +27,7 @@ impl TOIEntry { b1: Option<RigidBodyHandle>, c2: ColliderHandle, b2: Option<RigidBodyHandle>, - is_intersection_test: bool, + is_pseudo_intersection_test: bool, timestamp: usize, ) -> Self { Self { @@ -34,7 +36,7 @@ impl TOIEntry { b1, c2, b2, - is_intersection_test, + is_pseudo_intersection_test, timestamp, } } @@ -47,12 +49,14 @@ impl TOIEntry { &ColliderType, &ColliderShape, &ColliderPosition, + &ColliderFlags, Option<&ColliderParent>, ), c2: ( &ColliderType, &ColliderShape, &ColliderPosition, + &ColliderFlags, Option<&ColliderParent>, ), b1: Option<( @@ -78,8 +82,8 @@ impl TOIEntry { return None; } - let (co_type1, co_shape1, co_pos1, co_parent1) = c1; - let (co_type2, co_shape2, co_pos2, co_parent2) = c2; + let (co_type1, co_shape1, co_pos1, co_flags1, co_parent1) = c1; + let (co_type2, co_shape2, co_pos2, co_flags2, co_parent2) = c2; let linvel1 = frozen1.is_none() as u32 as Real * b1.map(|b| b.1.linvel).unwrap_or(na::zero()); @@ -104,7 +108,9 @@ impl TOIEntry { // keep it since more conservatism is good at this stage. let thickness = (co_shape1.0.ccd_thickness() + co_shape2.0.ccd_thickness()) + smallest_contact_dist.max(0.0); - let is_intersection_test = co_type1.is_sensor() || co_type2.is_sensor(); + let is_pseudo_intersection_test = co_type1.is_sensor() + || co_type2.is_sensor() + || !co_flags1.solver_groups.test(co_flags2.solver_groups); if (end_time - start_time) * vel12 < thickness { return None; @@ -135,7 +141,7 @@ impl TOIEntry { // If the TOI search involves two non-sensor colliders then // we don't want to stop the TOI search at the first penetration // because the colliders may be in a separating trajectory. - let stop_at_penetration = is_intersection_test; + let stop_at_penetration = is_pseudo_intersection_test; let res_toi = query_dispatcher .nonlinear_time_of_impact( @@ -157,7 +163,7 @@ impl TOIEntry { co_parent1.map(|p| p.handle), ch2, co_parent2.map(|p| p.handle), - is_intersection_test, + is_pseudo_intersection_test, 0, )) } @@ -173,7 +179,7 @@ impl TOIEntry { if ccd.ccd_active { NonlinearRigidMotion::new( poss.position, - mprops.mass_properties.local_com, + mprops.local_mprops.local_com, vels.linvel, vels.angvel, ) diff --git a/src/dynamics/integration_parameters.rs b/src/dynamics/integration_parameters.rs index e039bfc..4725319 100644 --- a/src/dynamics/integration_parameters.rs +++ b/src/dynamics/integration_parameters.rs @@ -10,7 +10,7 @@ pub struct IntegrationParameters { /// /// When CCD with multiple substeps is enabled, the timestep is subdivided /// into smaller pieces. This timestep subdivision won't generate timestep - /// lengths smaller than `min_dt`. + /// lengths smaller than `min_ccd_dt`. /// /// Setting this to a large value will reduce the opportunity to performing /// CCD substepping, resulting in potentially more time dropped by the @@ -18,17 +18,6 @@ pub struct IntegrationParameters { /// to numerical instabilities. pub min_ccd_dt: Real, - // /// If `true` and if rapier is compiled with the `parallel` feature, this will enable rayon-based multithreading (default: `true`). - // /// - // /// This parameter is ignored if rapier is not compiled with is `parallel` feature. - // /// Refer to rayon's documentation regarding how to configure the number of threads with either - // /// `rayon::ThreadPoolBuilder::new().num_threads(4).build_global().unwrap()` or `ThreadPool::install`. - // /// Note that using only one thread with `multithreading_enabled` set to `true` will result on a slower - // /// simulation than setting `multithreading_enabled` to `false`. - // pub multithreading_enabled: bool, - // /// If `true`, the world's `step` method will stop right after resolving exactly one CCD event (default: `false`). - // /// This allows the user to take action during a timestep, in-between two CCD events. - // pub return_after_ccd_substep: bool, /// The Error Reduction Parameter in `[0, 1]` is the proportion of /// the positional error to be corrected at each time step (default: `0.2`). pub erp: Real, diff --git a/src/dynamics/island_manager.rs b/src/dynamics/island_manager.rs index b79cdb2..34fa3bd 100644 --- a/src/dynamics/island_manager.rs +++ b/src/dynamics/island_manager.rs @@ -229,19 +229,17 @@ impl IslandManager { stack: &mut Vec<RigidBodyHandle>, ) { for collider_handle in &rb_colliders.0 { - if let Some(contacts) = narrow_phase.contacts_with(*collider_handle) { - for inter in contacts { - for manifold in &inter.2.manifolds { - if !manifold.data.solver_contacts.is_empty() { - let other = crate::utils::select_other( - (inter.0, inter.1), - *collider_handle, - ); - if let Some(other_body) = colliders.get(other.0) { - stack.push(other_body.handle); - } - break; + for inter in narrow_phase.contacts_with(*collider_handle) { + for manifold in &inter.manifolds { + if !manifold.data.solver_contacts.is_empty() { + let other = crate::utils::select_other( + (inter.collider1, inter.collider2), + *collider_handle, + ); + if let Some(other_body) = colliders.get(other.0) { + stack.push(other_body.handle); } + break; } } } diff --git a/src/dynamics/joint/fixed_joint.rs b/src/dynamics/joint/fixed_joint.rs index 2917757..3f87e8d 100644 --- a/src/dynamics/joint/fixed_joint.rs +++ b/src/dynamics/joint/fixed_joint.rs @@ -8,10 +8,10 @@ use crate::math::{Isometry, Real, SpacialVector}; pub struct FixedJoint { /// The frame of reference for the first body affected by this joint, expressed in the local frame /// of the first body. - pub local_anchor1: Isometry<Real>, + pub local_frame1: Isometry<Real>, /// The frame of reference for the second body affected by this joint, expressed in the local frame /// of the first body. - pub local_anchor2: Isometry<Real>, + pub local_frame2: Isometry<Real>, /// The impulse applied to the first body affected by this joint. /// /// The impulse applied to the second body affected by this joint is given by `-impulse`. @@ -23,10 +23,10 @@ pub struct FixedJoint { impl FixedJoint { /// Creates a new fixed joint from the frames of reference of both bodies. - pub fn new(local_anchor1: Isometry<Real>, local_anchor2: Isometry<Real>) -> Self { + pub fn new(local_frame1: Isometry<Real>, local_frame2: Isometry<Real>) -> Self { Self { - local_anchor1, - local_anchor2, + local_frame1, + local_frame2, impulse: SpacialVector::zeros(), } } diff --git a/src/dynamics/rigid_body.rs b/src/dynamics/rigid_body.rs index 824ec92..63c2221 100644 --- a/src/dynamics/rigid_body.rs +++ b/src/dynamics/rigid_body.rs @@ -4,8 +4,7 @@ use crate::dynamics::{ RigidBodyMassPropsFlags, RigidBodyPosition, RigidBodyType, RigidBodyVelocity, }; use crate::geometry::{ - Collider, ColliderHandle, ColliderMassProperties, ColliderParent, ColliderPosition, - ColliderShape, + Collider, ColliderHandle, ColliderMassProps, ColliderParent, ColliderPosition, ColliderShape, }; use crate::math::{AngVector, Isometry, Point, Real, Rotation, Vector}; use crate::utils::{self, WCross}; @@ -48,7 +47,7 @@ impl RigidBody { rb_ccd: RigidBodyCcd::default(), rb_ids: RigidBodyIds::default(), rb_colliders: RigidBodyColliders::default(), - rb_activation: RigidBodyActivation::new_active(), + rb_activation: RigidBodyActivation::active(), changes: RigidBodyChanges::all(), rb_type: RigidBodyType::Dynamic, rb_dominance: RigidBodyDominance::default(), @@ -112,7 +111,7 @@ impl RigidBody { /// The mass properties of this rigid-body. #[inline] pub fn mass_properties(&self) -> &MassProperties { - &self.rb_mprops.mass_properties + &self.rb_mprops.local_mprops } /// The dominance group of this rigid-body. @@ -124,6 +123,72 @@ impl RigidBody { self.rb_dominance.effective_group(&self.rb_type) } + #[inline] + /// Locks or unlocks all the rotations of this rigid-body. + pub fn lock_rotations(&mut self, locked: bool, wake_up: bool) { + if self.is_dynamic() { + if wake_up { + self.wake_up(true); + } + + self.rb_mprops + .flags + .set(RigidBodyMassPropsFlags::ROTATION_LOCKED_X, locked); + self.rb_mprops + .flags + .set(RigidBodyMassPropsFlags::ROTATION_LOCKED_Y, locked); + self.rb_mprops + .flags + .set(RigidBodyMassPropsFlags::ROTATION_LOCKED_Z, locked); + self.update_world_mass_properties(); + } + } + + #[inline] + /// Locks or unlocks rotations of this rigid-body along each cartesian axes. + pub fn restrict_rotations( + &mut self, + allow_rotations_x: bool, + allow_rotations_y: bool, + allow_rotations_z: bool, + wake_up: bool, + ) { + if self.is_dynamic() { + if wake_up { + self.wake_up(true); + } + + self.rb_mprops.flags.set( + RigidBodyMassPropsFlags::ROTATION_LOCKED_X, + allow_rotations_x, + ); + self.rb_mprops.flags.set( + RigidBodyMassPropsFlags::ROTATION_LOCKED_Y, + allow_rotations_y, + ); + self.rb_mprops.flags.set( + RigidBodyMassPropsFlags::ROTATION_LOCKED_Z, + allow_rotations_z, + ); + self.update_world_mass_properties(); + } + } + + #[inline] + /// Locks or unlocks all the rotations of this rigid-body. + pub fn lock_translations(&mut self, locked: bool, wake_up: bool) { + if self.is_dynamic() { + if wake_up { + self.wake_up(true); + } + + self.rb_mprops + .flags + .set(RigidBodyMassPropsFlags::TRANSLATION_LOCKED, locked); + self.update_world_mass_properties(); + } + } + /// Are the translations of this rigid-body locked? pub fn is_translation_locked(&self) -> bool { self.rb_mprops @@ -190,7 +255,7 @@ impl RigidBody { self.wake_up(true); } - self.rb_mprops.mass_properties = props; + self.rb_mprops.local_mprops = props; self.update_world_mass_properties(); } @@ -210,7 +275,7 @@ impl RigidBody { /// /// A kinematic body can move freely but is not affected by forces. pub fn is_kinematic(&self) -> bool { - self.rb_type == RigidBodyType::Kinematic + self.rb_type.is_kinematic() } /// Is this rigid body static? @@ -224,7 +289,7 @@ impl RigidBody { /// /// Returns zero if this rigid body has an infinite mass. pub fn mass(&self) -> Real { - utils::inv(self.rb_mprops.mass_properties.inv_mass) + utils::inv(self.rb_mprops.local_mprops.inv_mass) } /// The predicted position of this rigid-body. @@ -251,6 +316,16 @@ impl RigidBody { self.rb_forces.gravity_scale = scale; } + /// The dominance group of this rigid-body. + pub fn dominance_group(&self) -> i8 { + self.rb_dominance.0 + } + + /// The dominance group of this rigid-body. + pub fn set_dominance_group(&mut self, dominance: i8) { + self.rb_dominance.0 = dominance + } + /// Adds a collider to this rigid-body. // TODO ECS: we keep this public for now just to simply our experiments on bevy_rapier. pub fn add_collider( @@ -259,7 +334,7 @@ impl RigidBody { co_parent: &ColliderParent, co_pos: &mut ColliderPosition, co_shape: &ColliderShape, - co_mprops: &ColliderMassProperties, + co_mprops: &ColliderMassProps, ) { self.rb_colliders.attach_collider( &mut self.changes, @@ -279,10 +354,11 @@ impl RigidBody { if let Some(i) = self.rb_colliders.0.iter().position(|e| *e == handle) { self.changes.set(RigidBodyChanges::COLLIDERS, true); self.rb_colliders.0.swap_remove(i); + let mass_properties = coll |
