diff options
| author | Sébastien Crozet <developer@crozet.re> | 2021-05-01 10:17:23 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-05-01 10:17:23 +0200 |
| commit | a385efc5582c7918f11c01a2b6bf26a46919d3a0 (patch) | |
| tree | c5b9c5e6fcb5561421e2b4b9d99f28e4c83c745e /src/geometry | |
| parent | aaf80bfa872c6f29b248cab8eb5658ab0d73cb4a (diff) | |
| parent | 2dfbd9ae92c139e306afc87994adac82489f30eb (diff) | |
| download | rapier-a385efc5582c7918f11c01a2b6bf26a46919d3a0.tar.gz rapier-a385efc5582c7918f11c01a2b6bf26a46919d3a0.tar.bz2 rapier-a385efc5582c7918f11c01a2b6bf26a46919d3a0.zip | |
Merge pull request #183 from dimforge/bundles
Make Rapier accept any kind of data storage instead of RigidBodySet/ColliderSet
Diffstat (limited to 'src/geometry')
| -rw-r--r-- | src/geometry/broad_phase_multi_sap/broad_phase.rs | 235 | ||||
| -rw-r--r-- | src/geometry/broad_phase_multi_sap/sap_layer.rs | 5 | ||||
| -rw-r--r-- | src/geometry/collider.rs | 309 | ||||
| -rw-r--r-- | src/geometry/collider_components.rs | 270 | ||||
| -rw-r--r-- | src/geometry/collider_set.rs | 281 | ||||
| -rw-r--r-- | src/geometry/contact_pair.rs | 22 | ||||
| -rw-r--r-- | src/geometry/mod.rs | 19 | ||||
| -rw-r--r-- | src/geometry/narrow_phase.rs | 605 |
8 files changed, 1090 insertions, 656 deletions
diff --git a/src/geometry/broad_phase_multi_sap/broad_phase.rs b/src/geometry/broad_phase_multi_sap/broad_phase.rs index fc79d6d..230d54d 100644 --- a/src/geometry/broad_phase_multi_sap/broad_phase.rs +++ b/src/geometry/broad_phase_multi_sap/broad_phase.rs @@ -1,15 +1,17 @@ use super::{ BroadPhasePairEvent, ColliderPair, SAPLayer, SAPProxies, SAPProxy, SAPProxyData, SAPRegionPool, }; -use crate::data::pubsub::Subscription; use crate::geometry::broad_phase_multi_sap::SAPProxyIndex; -use crate::geometry::collider::ColliderChanges; -use crate::geometry::{ColliderSet, RemovedCollider}; +use crate::geometry::{ + ColliderBroadPhaseData, ColliderChanges, ColliderHandle, ColliderPosition, ColliderShape, +}; use crate::math::Real; use crate::utils::IndexMut2; use parry::bounding_volume::BoundingVolume; use parry::utils::hashmap::HashMap; +use crate::data::{BundleSet, ComponentSet, ComponentSetMut}; + /// A broad-phase combining a Hierarchical Grid and Sweep-and-Prune. /// /// The basic Sweep-and-Prune (SAP) algorithm has one significant flaws: @@ -78,8 +80,19 @@ pub struct BroadPhase { layers: Vec<SAPLayer>, smallest_layer: u8, largest_layer: u8, - removed_colliders: Option<Subscription<RemovedCollider>>, deleted_any: bool, + // NOTE: we maintain this hashmap to simplify collider removal. + // This information is also present in the ColliderProxyId + // component. However if that component is removed, we need + // a way to access it to do some cleanup. + // Note that we could just remove the ColliderProxyId component + // altogether but that would be slow because of the need to + // always access this hashmap. Instead, we access this hashmap + // only when the collider has been added/removed. + // Another alternative would be to remove ColliderProxyId and + // just use a Coarena. But this seems like it could use too + // much memory. + colliders_proxy_ids: HashMap<ColliderHandle, SAPProxyIndex>, #[cfg_attr(feature = "serde-serialize", serde(skip))] region_pool: SAPRegionPool, // To avoid repeated allocations. // We could think serializing this workspace is useless. @@ -107,13 +120,13 @@ impl BroadPhase { /// Create a new empty broad-phase. pub fn new() -> Self { BroadPhase { - removed_colliders: None, proxies: SAPProxies::new(), layers: Vec::new(), smallest_layer: 0, largest_layer: 0, region_pool: Vec::new(), reporting: HashMap::default(), + colliders_proxy_ids: HashMap::default(), deleted_any: false, } } @@ -123,26 +136,13 @@ impl BroadPhase { /// For each colliders marked as removed, we make their containing layer mark /// its proxy as pre-deleted. The actual proxy removal will happen at the end /// of the `BroadPhase::update`. - fn handle_removed_colliders(&mut self, colliders: &mut ColliderSet) { - // Ensure we already subscribed the collider-removed events. - if self.removed_colliders.is_none() { - self.removed_colliders = Some(colliders.removed_colliders.subscribe()); - } - - // Extract the cursor to avoid borrowing issues. - let cursor = self.removed_colliders.take().unwrap(); - - // Read all the collider-removed events, and remove the corresponding proxy. - for collider in colliders.removed_colliders.read(&cursor) { - self.predelete_proxy(collider.proxy_index); + fn handle_removed_colliders(&mut self, removed_colliders: &[ColliderHandle]) { + // For each removed collider, remove the corresponding proxy. + for removed in removed_colliders { + if let Some(proxy_id) = self.colliders_proxy_ids.get(removed).copied() { + self.predelete_proxy(proxy_id); + } } - - // NOTE: We don't acknowledge the cursor just yet because we need - // to traverse the set of removed colliders one more time after - // the broad-phase update. - - // Re-insert the cursor we extracted to avoid borrowing issues. - self.removed_colliders = Some(cursor); } /// Pre-deletes a proxy from this broad-phase. @@ -173,7 +173,7 @@ impl BroadPhase { /// This method will actually remove from the proxy list all the proxies /// marked as deletable by `self.predelete_proxy`, making their proxy /// handles re-usable by new proxies. - fn complete_removals(&mut self, colliders: &mut ColliderSet) { + fn complete_removals(&mut self, removed_colliders: &[ColliderHandle]) { // If there is no layer, there is nothing to remove. if self.layers.is_empty() { return; @@ -215,13 +215,13 @@ impl BroadPhase { /* * Actually remove the colliders proxies. */ - let cursor = self.removed_colliders.as_ref().unwrap(); - for collider in colliders.removed_colliders.read(&cursor) { - if collider.proxy_index != crate::INVALID_U32 { - self.proxies.remove(collider.proxy_index); + for removed in removed_colliders { + if let Some(proxy_id) = self.colliders_proxy_ids.remove(removed) { + if proxy_id != crate::INVALID_U32 { + self.proxies.remove(proxy_id); + } } } - colliders.removed_colliders.ack(&cursor); } /// Finalize the insertion of the layer identified by `layer_id`. @@ -336,67 +336,127 @@ impl BroadPhase { } } + fn handle_modified_collider( + &mut self, + prediction_distance: Real, + handle: ColliderHandle, + proxy_index: &mut u32, + collider: (&ColliderPosition, &ColliderShape, &ColliderChanges), + ) -> bool { + let (co_pos, co_shape, co_changes) = collider; + + let mut aabb = co_shape + .compute_aabb(co_pos) + .loosened(prediction_distance / 2.0); + + aabb.mins = super::clamp_point(aabb.mins); + aabb.maxs = super::clamp_point(aabb.maxs); + + let layer_id = if let Some(proxy) = self.proxies.get_mut(*proxy_index) { + let mut layer_id = proxy.layer_id; + proxy.aabb = aabb; + + if co_changes.contains(ColliderChanges::SHAPE) { + // If the shape was changed, then we need to see if this proxy should be + // migrated to a larger layer. Indeed, if the shape was replaced by + // a much larger shape, we need to promote the proxy to a bigger layer + // to avoid the O(n²) discretization problem. + let new_layer_depth = super::layer_containing_aabb(&aabb); + if new_layer_depth > proxy.layer_depth { + self.layers[proxy.layer_id as usize] + .proper_proxy_moved_to_bigger_layer(&mut self.proxies, *proxy_index); + + // We need to promote the proxy to the bigger layer. + layer_id = self.ensure_layer_exists(new_layer_depth); + self.proxies[*proxy_index].layer_id = layer_id; + } + } + + layer_id + } else { + let layer_depth = super::layer_containing_aabb(&aabb); + let layer_id = self.ensure_layer_exists(layer_depth); + + // Create the proxy. + let proxy = SAPProxy::collider(handle, aabb, layer_id, layer_depth); + *proxy_index = self.proxies.insert(proxy); + layer_id + }; + + let layer = &mut self.layers[layer_id as usize]; + + // Preupdate the collider in the layer. + layer.preupdate_collider( + *proxy_index, + &aabb, + &mut self.proxies, + &mut self.region_pool, + ); + let need_region_propagation = !layer.created_regions.is_empty(); + + need_region_propagation + } + /// Updates the broad-phase, taking into account the new collider positions. - pub fn update( + pub fn update<Colliders>( &mut self, prediction_distance: Real, - colliders: &mut ColliderSet, + colliders: &mut Colliders, + modified_colliders: &[ColliderHandle], + removed_colliders: &[ColliderHandle], events: &mut Vec<BroadPhasePairEvent>, - ) { + ) where + Colliders: ComponentSetMut<ColliderBroadPhaseData> + + ComponentSet<ColliderChanges> + + ComponentSet<ColliderPosition> + + ComponentSet<ColliderShape>, + { // Phase 1: pre-delete the collisions that have been deleted. - self.handle_removed_colliders(colliders); + self.handle_removed_colliders(removed_colliders); let mut need_region_propagation = false; // Phase 2: pre-delete the collisions that have been deleted. - colliders.foreach_modified_colliders_mut_internal(|handle, collider| { - if !collider.changes.needs_broad_phase_update() { - return; - } - - let mut aabb = collider.compute_aabb().loosened(prediction_distance / 2.0); - aabb.mins = super::clamp_point(aabb.mins); - aabb.maxs = super::clamp_point(aabb.maxs); - - let layer_id = if let Some(proxy) = self.proxies.get_mut(collider.proxy_index) { - let mut layer_id = proxy.layer_id; - proxy.aabb = aabb; - - if collider.changes.contains(ColliderChanges::SHAPE) { - // If the shape was changed, then we need to see if this proxy should be - // migrated to a larger layer. Indeed, if the shape was replaced by - // a much larger shape, we need to promote the proxy to a bigger layer - // to avoid the O(n²) discretization problem. - let new_layer_depth = super::layer_containing_aabb(&aabb); - if new_layer_depth > proxy.layer_depth { - self.layers[proxy.layer_id as usize].proper_proxy_moved_to_bigger_layer( - &mut self.proxies, - collider.proxy_index, - ); - - // We need to promote the proxy to the bigger layer. - layer_id = self.ensure_layer_exists(new_layer_depth); - self.proxies[collider.proxy_index].layer_id = layer_id; - } + for handle in modified_colliders { + // NOTE: we use `get` because the collider may no longer + // exist if it has been removed. + let co_changes: Option<&ColliderChanges> = colliders.get(handle.0); + + if let Some(co_changes) = co_changes { + let (co_bf_data, co_pos, co_shape): ( + &ColliderBroadPhaseData, + &ColliderPosition, + &ColliderShape, + ) = colliders.index_bundle(handle.0); + + if !co_changes.needs_broad_phase_update() { + return; + } + let mut new_proxy_id = co_bf_data.proxy_index; + + if self.handle_modified_collider( + prediction_distance, + *handle, + &mut new_proxy_id, + (co_pos, co_shape, co_changes), + ) { + need_region_propagation = true; } - layer_id - } else { - let layer_depth = super::layer_containing_aabb(&aabb); - let layer_id = self.ensure_layer_exists(layer_depth); - - // Create the proxy. - let proxy = SAPProxy::collider(handle, aabb, layer_id, layer_depth); - collider.proxy_index = self.proxies.insert(proxy); - layer_id - }; - - let layer = &mut self.layers[layer_id as usize]; + if co_bf_data.proxy_index != new_proxy_id { + self.colliders_proxy_ids.insert(*handle, new_proxy_id); - // Preupdate the collider in the layer. - layer.preupdate_collider(collider, &aabb, &mut self.proxies, &mut self.region_pool); - need_region_propagation = need_region_propagation || !layer.created_regions.is_empty(); - }); + // Make sure we have the new proxy index in case + // the collider was added for the first time. + colliders.set_internal( + handle.0, + ColliderBroadPhaseData { + proxy_index: new_proxy_id, + }, + ); + } + } + } // Phase 3: bottom-up pass to propagate new regions from smaller layers to larger layers. if need_region_propagation { @@ -408,7 +468,7 @@ impl BroadPhase { // Phase 5: bottom-up pass to remove proxies, and propagate region removed from smaller // layers to possible remove regions from larger layers that would become empty that way. - self.complete_removals(colliders); + self.complete_removals(removed_colliders); } /// Propagate regions from the smallest layers up to the larger layers. @@ -523,7 +583,7 @@ impl BroadPhase { #[cfg(test)] mod test { - use crate::dynamics::{JointSet, RigidBodyBuilder, RigidBodySet}; + use crate::dynamics::{IslandManager, JointSet, RigidBodyBuilder, RigidBodySet}; use crate::geometry::{BroadPhase, ColliderBuilder, ColliderSet}; #[test] @@ -532,25 +592,26 @@ mod test { let mut bodies = RigidBodySet::new(); let mut colliders = ColliderSet::new(); let mut joints = JointSet::new(); + let mut islands = IslandManager::new(); let rb = RigidBodyBuilder::new_dynamic().build(); let co = ColliderBuilder::ball(0.5).build(); let hrb = bodies.insert(rb); - colliders.insert(co, hrb, &mut bodies); + let coh = colliders.insert(co, hrb, &mut bodies); let mut events = Vec::new(); - broad_phase.update(0.0, &mut colliders, &mut events); + broad_phase.update(0.0, &mut colliders, &[coh], &[], &mut events); - bodies.remove(hrb, &mut colliders, &mut joints); - broad_phase.update(0.0, &mut colliders, &mut events); + bodies.remove(hrb, &mut islands, &mut colliders, &mut joints); + broad_phase.update(0.0, &mut colliders, &[], &[coh], &mut events); // Create another body. let rb = RigidBodyBuilder::new_dynamic().build(); let co = ColliderBuilder::ball(0.5).build(); let hrb = bodies.insert(rb); - colliders.insert(co, hrb, &mut bodies); + let coh = colliders.insert(co, hrb, &mut bodies); // Make sure the proxy handles is recycled properly. - broad_phase.update(0.0, &mut colliders, &mut events); + broad_phase.update(0.0, &mut colliders, &[coh], &[], &mut events); } } diff --git a/src/geometry/broad_phase_multi_sap/sap_layer.rs b/src/geometry/broad_phase_multi_sap/sap_layer.rs index 9a9c296..e4a9f42 100644 --- a/src/geometry/broad_phase_multi_sap/sap_layer.rs +++ b/src/geometry/broad_phase_multi_sap/sap_layer.rs @@ -1,6 +1,6 @@ use super::{SAPProxies, SAPProxy, SAPRegion, SAPRegionPool}; use crate::geometry::broad_phase_multi_sap::DELETED_AABB_VALUE; -use crate::geometry::{Collider, SAPProxyIndex, AABB}; +use crate::geometry::{SAPProxyIndex, AABB}; use crate::math::{Point, Real}; use parry::utils::hashmap::{Entry, HashMap}; @@ -213,12 +213,11 @@ impl SAPLayer { pub fn preupdate_collider( &mut self, - collider: &Collider, + proxy_id: u32, aabb: &AABB, proxies: &mut SAPProxies, pool: &mut SAPRegionPool, ) { - let proxy_id = collider.proxy_index; let start = super::point_key(aabb.mins, self.region_width); let end = super::point_key(aabb.maxs, self.region_width); diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index 2b08a96..08295c1 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -1,231 +1,160 @@ use crate::dynamics::{CoefficientCombineRule, MassProperties, RigidBodyHandle}; -use crate::geometry::{InteractionGroups, SAPProxyIndex, SharedShape, SolverFlags}; +use crate::geometry::{ + ColliderBroadPhaseData, ColliderChanges, ColliderGroups, ColliderMassProperties, + ColliderMaterial, ColliderParent, ColliderPosition, ColliderShape, ColliderType, + InteractionGroups, SharedShape, SolverFlags, +}; use crate::math::{AngVector, Isometry, Point, Real, Rotation, Vector, DIM}; use crate::parry::transformation::vhacd::VHACDParameters; use na::Unit; -use parry::bounding_volume::{BoundingVolume, AABB}; +use parry::bounding_volume::AABB; use parry::shape::Shape; -bitflags::bitflags! { - #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] - /// Flags affecting the behavior of the constraints solver for a given contact manifold. - pub(crate) struct ColliderFlags: u8 { - const SENSOR = 1 << 0; - const FRICTION_COMBINE_RULE_01 = 1 << 1; - const FRICTION_COMBINE_RULE_10 = 1 << 2; - const RESTITUTION_COMBINE_RULE_01 = 1 << 3; - const RESTITUTION_COMBINE_RULE_10 = 1 << 4; - } -} - -impl ColliderFlags { - pub fn is_sensor(self) -> bool { - self.contains(ColliderFlags::SENSOR) - } - - pub fn friction_combine_rule_value(self) -> u8 { - (self.bits & 0b0000_0110) >> 1 - } - - pub fn restitution_combine_rule_value(self) -> u8 { - (self.bits & 0b0001_1000) >> 3 - } - - pub fn with_friction_combine_rule(mut self, rule: CoefficientCombineRule) -> Self { - self.bits = (self.bits & !0b0000_0110) | ((rule as u8) << 1); - self - } - - pub fn with_restitution_combine_rule(mut self, rule: CoefficientCombineRule) -> Self { - self.bits = (self.bits & !0b0001_1000) | ((rule as u8) << 3); - self - } -} - -#[derive(Clone)] -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] -enum MassInfo { - /// `MassProperties` are computed with the help of [`SharedShape::mass_properties`]. - Density(Real), - MassProperties(Box<MassProperties>), -} - -bitflags::bitflags! { - #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] - /// Flags describing how the collider has been modified by the user. - pub(crate) struct ColliderChanges: u32 { - const MODIFIED = 1 << 0; - const POSITION_WRT_PARENT = 1 << 1; // => BF & NF updates. - const POSITION = 1 << 2; // => BF & NF updates. - const COLLISION_GROUPS = 1 << 3; // => NF update. - const SOLVER_GROUPS = 1 << 4; // => NF update. - const SHAPE = 1 << 5; // => BF & NF update. NF pair workspace invalidation. - const SENSOR = 1 << 6; // => NF update. NF pair invalidation. - } -} - -impl ColliderChanges { - pub fn needs_broad_phase_update(self) -> bool { - self.intersects( - ColliderChanges::POSITION_WRT_PARENT - | ColliderChanges::POSITION - | ColliderChanges::SHAPE, - ) - } - - pub fn needs_narrow_phase_update(self) -> bool { - self.bits() > 1 - } -} - #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[derive(Clone)] /// A geometric entity that can be attached to a body so it can be affected by contacts and proximity queries. /// /// To build a new collider, use the `ColliderBuilder` structure. pub struct Collider { - shape: SharedShape, - mass_info: MassInfo, - pub(crate) flags: ColliderFlags, - pub(crate) solver_flags: SolverFlags, - pub(crate) changes: ColliderChanges, - pub(crate) parent: RigidBodyHandle, - pub(crate) delta: Isometry<Real>, - pub(crate) position: Isometry<Real>, - /// The friction coefficient of this collider. - pub friction: Real, - /// The restitution coefficient of this collider. - pub restitution: Real, - pub(crate) collision_groups: InteractionGroups, - pub(crate) solver_groups: InteractionGroups, - pub(crate) proxy_index: SAPProxyIndex, + pub(crate) co_type: ColliderType, + pub(crate) co_shape: ColliderShape, + pub(crate) co_mprops: ColliderMassProperties, + pub(crate) co_changes: ColliderChanges, + pub(crate) co_parent: ColliderParent, + pub(crate) co_pos: ColliderPosition, + pub(crate) co_material: ColliderMaterial, + pub(crate) co_groups: ColliderGroups, + pub(crate) co_bf_data: ColliderBroadPhaseData, /// User-defined data associated to this rigid-body. pub user_data: u128, } impl Collider { pub(crate) fn reset_internal_references(&mut self) { - self.parent = RigidBodyHandle::invalid(); - self.proxy_index = crate::INVALID_U32; - self.changes = ColliderChanges::empty(); + self.co_parent.handle = RigidBodyHandle::invalid(); + self.co_bf_data.proxy_index = crate::INVALID_U32; + self.co_changes = ColliderChanges::all(); } /// The rigid body this collider is attached to. pub fn parent(&self) -> RigidBodyHandle { - self.parent + self.co_parent.handle } /// Is this collider a sensor? pub fn is_sensor(&self) -> bool { - self.flags.is_sensor() + self.co_type.is_sensor() } /// The combine rule used by this collider to combine its friction /// coefficient with the friction coefficient of the other collider it /// is in contact with. pub fn friction_combine_rule(&self) -> CoefficientCombineRule { - CoefficientCombineRule::from_value(self.flags.friction_combine_rule_value()) + self.co_material.friction_combine_rule } /// Sets the combine rule used by this collider to combine its friction /// coefficient with the friction coefficient of the other collider it /// is in contact with. pub fn set_friction_combine_rule(&mut self, rule: CoefficientCombineRule) { - self.flags = self.flags.with_friction_combine_rule(rule); + self.co_material.friction_combine_rule = rule; } /// The combine rule used by this collider to combine its restitution /// coefficient with the restitution coefficient of the other collider it /// is in contact with. pub fn restitution_combine_rule(&self) -> CoefficientCombineRule { - CoefficientCombineRule::from_value(self.flags.restitution_combine_rule_value()) + self.co_material.restitution_combine_rule } /// Sets the combine rule used by this collider to combine its restitution /// coefficient with the restitution coefficient of the other collider it /// is in contact with. pub fn set_restitution_combine_rule(&mut self, rule: CoefficientCombineRule) { - self.flags = self.flags.with_restitution_combine_rule(rule) + self.co_material.restitution_combine_rule = rule; } /// Sets whether or not this is a sensor collider. pub fn set_sensor(&mut self, is_sensor: bool) { if is_sensor != self.is_sensor() { - self.changes.insert(ColliderChanges::SENSOR); - self.flags.set(ColliderFlags::SENSOR, is_sensor); + self.co_changes.insert(ColliderChanges::TYPE); + self.co_type = if is_sensor { + ColliderType::Sensor + } else { + ColliderType::Solid + }; } } #[doc(hidden)] pub fn set_position_debug(&mut self, position: Isometry<Real>) { - self.position = position; + self.co_pos.0 = position; } /// The position of this collider expressed in the local-space of the rigid-body it is attached to. #[deprecated(note = "use `.position_wrt_parent()` instead.")] pub fn delta(&self) -> &Isometry<Real> { - &self.delta + &self.co_parent.pos_wrt_parent } /// The world-space position of this collider. pub fn position(&self) -> &Isometry<Real> { - &self.position - } - - /// Sets the position of this collider wrt. its parent rigid-body. - pub(crate) fn set_position(&mut self, position: Isometry<Real>) { - self.changes.insert(ColliderChanges::POSITION); - self.position = position; + &self.co_pos } /// The position of this collider wrt the body it is attached to. pub fn position_wrt_parent(&self) -> &Isometry<Real> { - &self.delta + &self.co_parent.pos_wrt_parent } /// Sets the position of this collider wrt. its parent rigid-body. pub fn set_position_wrt_parent(&mut self, position: Isometry<Real>) { - self.changes.insert(ColliderChanges::POSITION_WRT_PARENT); - self.delta = position; + self.co_changes.insert(ColliderChanges::PARENT); + self.co_parent.pos_wrt_parent = position; } /// The collision groups used by this collider. pub fn collision_groups(&self) -> InteractionGroups { - self.collision_groups + self.co_groups.collision_groups } /// Sets the collision groups of this collider. pub fn set_collision_groups(&mut self, groups: InteractionGroups) { - if self.collision_groups != groups { - self.changes.insert(ColliderChanges::COLLISION_GROUPS); - self.collision_groups = groups; + if self.co_groups.collision_groups != groups { + self.co_changes.insert(ColliderChanges::GROUPS); + self.co_groups.collision_groups = groups; } } /// The solver groups used by this collider. pub fn solver_groups(&self) -> InteractionGroups { - self.solver_groups + self.co_groups.solver_groups } /// Sets the solver groups of this collider. pub fn set_solver_groups(&mut self, groups: InteractionGroups) { - if self.solver_groups != groups { - self.changes.insert(ColliderChanges::SOLVER_GROUPS); - self.solver_groups = groups; + if self.co_groups.solver_groups != groups { + self.co_changes.insert(ColliderChanges::GROUPS); + self.co_groups.solver_groups = groups; } } + /// The material (friction and restitution properties) of this collider. + pub fn material(&self) -> &ColliderMaterial { + &self.co_material + } + /// The density of this collider, if set. pub fn density(&self) -> Option<Real> { - match &self.mass_info { - MassInfo::Density(density) => Some(*density), - MassInfo::MassProperties(_) => None, + match &self.co_mprops { + ColliderMassProperties::Density(density) => Some(*density), + ColliderMassProperties::MassProperties(_) => None, } } /// The geometric shape of this collider. pub fn shape(&self) -> &dyn Shape { - &*self.shape.0 + self.co_shape.as_ref() } /// A mutable reference to the geometric shape of this collider. @@ -234,33 +163,33 @@ impl Collider { /// cloned first so that `self` contains a unique copy of that /// shape that you can modify. pub fn shape_mut(&mut self) -> &mut dyn Shape { - self.changes.insert(ColliderChanges::SHAPE); - self.shape.make_mut() + self.co_changes.insert(ColliderChanges::SHAPE); + self.co_shape.make_mut() } /// Sets the shape of this collider. pub fn set_shape(&mut self, shape: SharedShape) { - self.changes.insert(ColliderChanges::SHAPE); - self.shape = shape; + self.co_changes.insert(ColliderChanges::SHAPE); + self.co_shape = shape; } /// Compute the axis-aligned bounding box of this collider. pub fn compute_aabb(&self) -> AABB { - self.shape.compute_aabb(&self.position) + self.co_shape.compute_aabb(&self.co_pos) } - /// Compute the axis-aligned bounding box of this collider. + /// Compute the axis-aligned bounding box of this collider moving from its current position + /// to the given `next_position` pub fn compute_swept_aabb(&self, next_position: &Isometry<Real>) -> AABB { - let aabb1 = self.shape.compute_aabb(&self.position); - let aabb2 = self.shape.compute_aabb(next_position); - aabb1.merged(&aabb2) + self.co_shape + .compute_swept_aabb(&self.co_pos, next_position) } /// Compute the local-space mass properties of this collider. pub fn mass_properties(&self) -> MassProperties { - match &self.mass_info { - MassInfo::Density(density) => self.shape.mass_properties(*density), - MassInfo::MassProperties(mass_properties) => **mass_properties, + match &self.co_mprops { + ColliderMassProperties::Density(density) => self.co_shape.mass_properties(*density), + ColliderMassProperties::MassProperties(mass_properties) => **mass_properties, } } } @@ -272,10 +201,10 @@ pub struct ColliderBuilder { /// The shape of the collider to be built. pub shape: SharedShape, /// The uniform density of the collider to be built. - density: Option<Real>, + pub density: Option<Real>, /// Overrides automatic computation of `MassProperties`. /// If None, it will be computed based on shape and density. - mass_properties: Option<MassProperties>, + pub mass_properties: Option<MassProperties>, /// The friction coefficient of the collider to be built. pub friction: Real, /// The rule used to combine two friction coefficients. @@ -285,7 +214,7 @@ pub struct ColliderBuilder { /// The rule used to combine two restitution coefficients. pub restitution_combine_rule: CoefficientCombineRule, /// The position of this collider relative to the local frame of the rigid-body it is attached to. - pub delta: Isometry<Real>, + pub pos_wrt_parent: Isometry<Real>, /// Is this collider a sensor? pub is_sensor: bool, /// Do we have to always call the contact modifier @@ -308,7 +237,7 @@ impl ColliderBuilder { mass_properties: None, friction: Self::default_friction(), restitution: 0.0, - delta: Isometry::identity(), + pos_wrt_parent: Isometry::identity(), is_sensor: false, user_data: 0, collision_groups: InteractionGroups::all(), @@ -646,8 +575,8 @@ impl ColliderBuilder { /// relative to the rigid-body it is attached to. #[cfg(feature = "dim2")] pub fn translation(mut self, x: Real, y: Real) -> Self { - self.delta.translation.x = x; - self.delta.translation.y = y; + self.pos_wrt_parent.translation.x = x; + self.pos_wrt_parent.translation.y = y; self } @@ -655,23 +584,23 @@ impl ColliderBuilder { /// relative to the rigid-body it is attached to. #[cfg(feature = "dim3")] pub fn translation(mut self, x: Real, y: Real, z: Real) -> Self { - self.delta.translation.x = x; - self.delta.translation.y = y; - self.delta.translation.z = z; + self.pos_wrt_parent.translation.x = x; + self.pos_wrt_parent.translation.y = y; + self.pos_wrt_parent.translation.z = z; self } /// Sets the initial orientation of the collider to be created, /// relative to the rigid-body it is attached to. pub fn rotation(mut self, angle: AngVector<Real>) -> Self { - self.delta.rotation = Rotation::new(angle); + self.pos_wrt_parent.rotation = Rotation::new(angle); self } /// Sets the initial position (translation and orientation) of the collider to be created, /// relative to the rigid-body it is attached to. pub fn position_wrt_parent(mut self, pos: Isometry<Real>) -> Self { - self.delta = pos; + self.pos_wrt_parent = pos; self } @@ -679,53 +608,97 @@ impl ColliderBuilder { /// relative to the rigid-body it is attached to. #[deprecated(note = "Use `.position_wrt_parent` instead.")] pub fn position(mut self, pos: Isometry<Real>) -> Self { - self.delta = pos; + self.pos_wrt_parent = pos; self } /// Set the position of this collider in the local-space of the rigid-body it is attached to. - #[deprecated(note = "Use `.position` instead.")] + #[deprecated(note = "Use `.position_wrt_parent` instead.")] pub fn delta(mut self, delta: Isometry<Real>) -> Self { - self.delta = delta; + self.pos_wrt_parent = delta; self } /// Builds a new collider attached to the given rigid-body. pub fn build(&self) -> Collider { + let (co_changes, co_pos, co_bf_data, co_shape, co_type, co_groups, co_material, co_mprops) = + self.components(); + let co_parent = ColliderParent { + pos_wrt_parent: co_pos.0, + handle: RigidBodyHandle::invalid(), + }; + Collider { + co_shape, + co_mprops, + co_material, + co_parent, + co_changes, + co_pos, + co_bf_data, + co_groups, + co_type, + user_data: self.user_data, + } + } + + /// Builds all the components required by a collider. + pub fn components( + &self, + ) -> ( + ColliderChanges, + ColliderPosition, + ColliderBroadPhaseData, + ColliderShape, + ColliderType, + ColliderGroups, + ColliderMaterial, + ColliderMassProperties, + ) { let mass_info = if let Some(mp) = self.mass_properties { - MassInfo::MassProperties(Box::new(mp)) + ColliderMassProperties::MassProperties(Box::new(mp)) } else { let default_density = if self.is_sensor { 0.0 } else { 1.0 }; let density = self.density.unwrap_or(default_density); - MassInfo::Density(density) + ColliderMassProperties::Density(density) }; |
