Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

### Added

- `InteractionTestMode`: Specifies which method should be used to test interactions. Supports `AND` and `OR`.
- `CoefficientCombineRule::Sum` - Adds the two coefficients and does a clamp to have at most 1.
- `RigidBodySet` and `ColliderSet` have a new constructor `with_capacity`.
- Use `profiling` crate to provide helpful profiling information in different tools.
- The testbeds have been updated to use `puffin_egui`
Expand All @@ -19,6 +21,9 @@
- `InteractionGroups` default value for `memberships` is now `GROUP_1` (#706)
- `ImpulseJointSet::get_mut` has a new parameter `wake_up: bool`, to wake up connected bodies.
- Removed unmaintained `instant` in favor of `web-time`. This effectively removes the `wasm-bindgen` transitive dependency as it's no longer needed.
- `InteractionGroups` struct now contains `InteractionTestMode`. Continues [rapier/pull/170](https://github.com/dimforge/rapier/pull/170) for [rapier/issues/622](https://github.com/dimforge/rapier/issues/622)
- `InteractionGroups` constructor now requires an `InteractionTestMode` parameter. If you want same behaviour as before, use `InteractionTestMode::AND` (eg. `InteractionGroups::new(Group::GROUP_1, Group::GROUP_1, InteractionTestMode::AND)`)
- `CoefficientCombineRule::Min` - now makes sure it uses a non zero value as result by using `coeff1.min(coeff2).abs()`

## v0.22.0 (20 July 2024)

Expand Down
6 changes: 4 additions & 2 deletions examples2d/collision_groups2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ pub fn init_world(testbed: &mut Testbed) {
/*
* Setup groups
*/
const GREEN_GROUP: InteractionGroups = InteractionGroups::new(Group::GROUP_1, Group::GROUP_1);
const BLUE_GROUP: InteractionGroups = InteractionGroups::new(Group::GROUP_2, Group::GROUP_2);
const GREEN_GROUP: InteractionGroups =
InteractionGroups::new(Group::GROUP_1, Group::GROUP_1, InteractionTestMode::AND);
const BLUE_GROUP: InteractionGroups =
InteractionGroups::new(Group::GROUP_2, Group::GROUP_2, InteractionTestMode::AND);

/*
* A green floor that will collide with the GREEN group only.
Expand Down
6 changes: 4 additions & 2 deletions examples3d/collision_groups3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ pub fn init_world(testbed: &mut Testbed) {
/*
* Setup groups
*/
const GREEN_GROUP: InteractionGroups = InteractionGroups::new(Group::GROUP_1, Group::GROUP_1);
const BLUE_GROUP: InteractionGroups = InteractionGroups::new(Group::GROUP_2, Group::GROUP_2);
const GREEN_GROUP: InteractionGroups =
InteractionGroups::new(Group::GROUP_1, Group::GROUP_1, InteractionTestMode::AND);
const BLUE_GROUP: InteractionGroups =
InteractionGroups::new(Group::GROUP_2, Group::GROUP_2, InteractionTestMode::AND);

/*
* A green floor that will collide with the GREEN group only.
Expand Down
12 changes: 10 additions & 2 deletions examples3d/vehicle_joints3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ pub fn init_world(testbed: &mut Testbed) {

let body_co = ColliderBuilder::cuboid(0.65, 0.3, 0.9)
.density(100.0)
.collision_groups(InteractionGroups::new(CAR_GROUP, !CAR_GROUP));
.collision_groups(InteractionGroups::new(
CAR_GROUP,
!CAR_GROUP,
InteractionTestMode::AND,
));
let body_rb = RigidBodyBuilder::dynamic()
.position(body_position.into())
.build();
Expand Down Expand Up @@ -85,7 +89,11 @@ pub fn init_world(testbed: &mut Testbed) {
// is mathematically simpler than a cylinder and cheaper to compute for collision-detection.
let wheel_co = ColliderBuilder::ball(wheel_radius)
.density(100.0)
.collision_groups(InteractionGroups::new(CAR_GROUP, !CAR_GROUP))
.collision_groups(InteractionGroups::new(
CAR_GROUP,
!CAR_GROUP,
InteractionTestMode::AND,
))
.friction(1.0);
let wheel_rb = RigidBodyBuilder::dynamic().position(wheel_center.into());
let wheel_handle = bodies.insert(wheel_rb);
Expand Down
11 changes: 10 additions & 1 deletion src/dynamics/coefficient_combine_rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub enum CoefficientCombineRule {
Multiply,
/// The greatest coefficient is chosen.
Max,
/// The sum of the two coefficients.
Sum,
}

impl CoefficientCombineRule {
Expand All @@ -27,8 +29,15 @@ impl CoefficientCombineRule {

match effective_rule {
0 => (coeff1 + coeff2) / 2.0,
1 => coeff1.min(coeff2),
1 => {
// Even though coeffs are meant to be positive, godot use-case has negative values.
// We're following their logic here.
// Context: https://github.com/dimforge/rapier/pull/741#discussion_r1862402948
coeff1.min(coeff2).abs()
}
2 => coeff1 * coeff2,
4 => (coeff1 + coeff2).clamp(0.0, 1.0),
// 3 is missing as Max is the default one in case of mismatch.
_ => coeff1.max(coeff2),
}
}
Expand Down
68 changes: 58 additions & 10 deletions src/geometry/interaction_groups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@
/// - The interaction groups filter.
///
/// An interaction is allowed between two filters `a` and `b` when two conditions
/// are met simultaneously:
/// are met simultaneously for [`InteractionTestMode::AND`] or individually for [`InteractionTestMode::OR`]::
/// - The groups membership of `a` has at least one bit set to `1` in common with the groups filter of `b`.
/// - The groups membership of `b` has at least one bit set to `1` in common with the groups filter of `a`.
///
/// In other words, interactions are allowed between two filter iff. the following condition is met:
/// In other words, interactions are allowed between two filter iff. the following condition is met
/// for [`InteractionTestMode::AND`]:
/// ```ignore
/// (self.memberships & rhs.filter) != 0 && (rhs.memberships & self.filter) != 0
/// (self.memberships.bits() & rhs.filter.bits()) != 0 && (rhs.memberships.bits() & self.filter.bits()) != 0
/// ```
/// or for [`InteractionTestMode::OR`]:
/// ```ignore
/// (self.memberships.bits() & rhs.filter.bits()) != 0 || (rhs.memberships.bits() & self.filter.bits()) != 0
/// ```
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
Expand All @@ -23,25 +28,45 @@ pub struct InteractionGroups {
pub memberships: Group,
/// Groups filter.
pub filter: Group,
/// Interaction test mode
///
/// In case of different test modes between two [`InteractionGroups`], [`InteractionTestMode::And`] is given priority.
pub test_mode: InteractionTestMode,
}

#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Default)]
/// Specifies which method should be used to test interactions.
///
/// In case of different test modes between two [`InteractionGroups`], [`InteractionTestMode::And`] is given priority.
pub enum InteractionTestMode {
/// Use [`InteractionGroups::test_and`].
#[default]
And,
/// Use [`InteractionGroups::test_or`], iff. the `rhs` is also [`InteractionTestMode::Or`].
///
/// If the `rhs` is not [`InteractionTestMode::Or`], use [`InteractionGroups::test_and`].
Or,
}

impl InteractionGroups {
/// Initializes with the given interaction groups and interaction mask.
pub const fn new(memberships: Group, filter: Group) -> Self {
pub const fn new(memberships: Group, filter: Group, test_mode: InteractionTestMode) -> Self {
Self {
memberships,
filter,
test_mode,
}
}

/// Allow interaction with everything.
pub const fn all() -> Self {
Self::new(Group::ALL, Group::ALL)
Self::new(Group::ALL, Group::ALL, InteractionTestMode::AND)
}

/// Prevent all interactions.
pub const fn none() -> Self {
Self::new(Group::NONE, Group::NONE)
Self::new(Group::NONE, Group::NONE, InteractionTestMode::AND)
}

/// Sets the group this filter is part of.
Expand All @@ -59,21 +84,44 @@ impl InteractionGroups {
/// Check if interactions should be allowed based on the interaction memberships and filter.
///
/// An interaction is allowed iff. the memberships of `self` contain at least one bit set to 1 in common
/// with the filter of `rhs`, and vice-versa.
/// with the filter of `rhs`, **and** vice-versa.
#[inline]
pub const fn test(self, rhs: Self) -> bool {
// NOTE: since const ops is not stable, we have to convert `Group` into u32
// to use & operator in const context.
pub const fn test_and(self, rhs: Self) -> bool {
(self.memberships.bits() & rhs.filter.bits()) != 0
&& (rhs.memberships.bits() & self.filter.bits()) != 0
}

/// Check if interactions should be allowed based on the interaction memberships and filter.
///
/// An interaction is allowed iff. the groups of `self` contain at least one bit set to 1 in common
/// with the mask of `rhs`, **or** vice-versa.
#[inline]
pub const fn test_or(self, rhs: Self) -> bool {
(self.memberships.bits() & rhs.filter.bits()) != 0
|| (rhs.memberships.bits() & self.filter.bits()) != 0
}

/// Check if interactions should be allowed based on the interaction memberships and filter.
///
/// See [`InteractionTestMode`] for more info.
#[inline]
pub fn test(self, rhs: Self) -> bool {
if self.test_mode == InteractionTestMode::And {
self.test_and(rhs)
} else if rhs.test_mode == InteractionTestMode::Or {
self.test_and(rhs)
} else {
self.test_or(rhs)
}
}
}

impl Default for InteractionGroups {
fn default() -> Self {
Self {
memberships: Group::GROUP_1,
filter: Group::ALL,
test_mode: InteractionTestMode::AND,
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/geometry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub use self::contact_pair::{
pub use self::interaction_graph::{
ColliderGraphIndex, InteractionGraph, RigidBodyGraphIndex, TemporaryInteractionIndex,
};
pub use self::interaction_groups::{Group, InteractionGroups};
pub use self::interaction_groups::{Group, InteractionGroups, InteractionTestMode};
pub use self::mesh_converter::{MeshConverter, MeshConverterError};
pub use self::narrow_phase::NarrowPhase;

Expand Down