mod forces;
mod layers;
mod locked_axes;
mod mass_properties;
mod rotation;
mod world_queries;
pub use forces::*;
pub use layers::*;
pub use locked_axes::*;
pub use mass_properties::*;
pub use rotation::*;
pub use world_queries::*;
use crate::prelude::*;
use bevy::prelude::*;
use derive_more::From;
#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
#[cfg_attr(feature = "2d", doc = " angular_velocity.0 += 0.05;")]
#[cfg_attr(feature = "3d", doc = " angular_velocity.z += 0.05;")]
#[cfg_attr(feature = "2d", doc = "# use bevy_xpbd_2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "# use bevy_xpbd_3d::prelude::*;")]
#[cfg_attr(
feature = "2d",
doc = "commands.spawn((RigidBody::Dynamic, Collider::circle(0.5)));"
)]
#[cfg_attr(
feature = "3d",
doc = "commands.spawn((RigidBody::Dynamic, Collider::sphere(0.5)));"
)]
#[cfg_attr(feature = "2d", doc = "# use bevy_xpbd_2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "# use bevy_xpbd_3d::prelude::*;")]
#[cfg_attr(feature = "2d", doc = " Collider::circle(0.5),")]
#[cfg_attr(feature = "3d", doc = " Collider::sphere(0.5),")]
#[cfg_attr(feature = "2d", doc = "# use bevy_xpbd_2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "# use bevy_xpbd_3d::prelude::*;")]
#[cfg_attr(
feature = "2d",
doc = " MassPropertiesBundle::new_computed(&Collider::circle(0.5), 2.5),"
)]
#[cfg_attr(
feature = "3d",
doc = " MassPropertiesBundle::new_computed(&Collider::sphere(0.5), 2.5),"
)]
#[cfg_attr(feature = "2d", doc = "# use bevy_xpbd_2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "# use bevy_xpbd_3d::prelude::*;")]
#[cfg_attr(feature = "2d", doc = " Collider::circle(0.5),")]
#[cfg_attr(feature = "3d", doc = " Collider::sphere(0.5),")]
#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component)]
pub enum RigidBody {
#[default]
Dynamic,
Static,
Kinematic,
}
impl RigidBody {
pub fn is_dynamic(&self) -> bool {
*self == Self::Dynamic
}
pub fn is_static(&self) -> bool {
*self == Self::Static
}
pub fn is_kinematic(&self) -> bool {
*self == Self::Kinematic
}
}
#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, Eq, From)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component)]
pub struct Sleeping;
#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, From)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component)]
pub struct TimeSleeping(pub Scalar);
#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, Eq, From)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component)]
pub struct SleepingDisabled;
#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
#[cfg_attr(feature = "2d", doc = " Position::from_xy(0.0, 20.0),")]
#[cfg_attr(feature = "3d", doc = " Position::from_xyz(0.0, 2.0, 0.0),")]
#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component)]
pub struct Position(pub Vector);
impl Position {
pub fn new(position: Vector) -> Self {
Self(position)
}
#[cfg(feature = "2d")]
pub fn from_xy(x: Scalar, y: Scalar) -> Self {
Self(Vector::new(x, y))
}
#[cfg(feature = "3d")]
pub fn from_xyz(x: Scalar, y: Scalar, z: Scalar) -> Self {
Self(Vector::new(x, y, z))
}
}
impl From<GlobalTransform> for Position {
#[cfg(feature = "2d")]
fn from(value: GlobalTransform) -> Self {
Self::from_xy(
value.translation().adjust_precision().x,
value.translation().adjust_precision().y,
)
}
#[cfg(feature = "3d")]
fn from(value: GlobalTransform) -> Self {
Self::from_xyz(
value.translation().adjust_precision().x,
value.translation().adjust_precision().y,
value.translation().adjust_precision().z,
)
}
}
impl From<&GlobalTransform> for Position {
#[cfg(feature = "2d")]
fn from(value: &GlobalTransform) -> Self {
Self::from_xy(
value.translation().adjust_precision().x,
value.translation().adjust_precision().y,
)
}
#[cfg(feature = "3d")]
fn from(value: &GlobalTransform) -> Self {
Self::from_xyz(
value.translation().adjust_precision().x,
value.translation().adjust_precision().y,
value.translation().adjust_precision().z,
)
}
}
#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component)]
pub struct PreviousPosition(pub Vector);
#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component)]
pub struct AccumulatedTranslation(pub Vector);
#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component)]
pub struct LinearVelocity(pub Vector);
impl LinearVelocity {
pub const ZERO: LinearVelocity = LinearVelocity(Vector::ZERO);
}
#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
#[reflect(Component)]
pub(crate) struct PreSolveLinearVelocity(pub Vector);
#[cfg(feature = "2d")]
#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, From)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component)]
pub struct AngularVelocity(pub Scalar);
#[cfg(feature = "3d")]
#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component)]
pub struct AngularVelocity(pub Vector);
impl AngularVelocity {
#[cfg(feature = "2d")]
pub const ZERO: AngularVelocity = AngularVelocity(0.0);
#[cfg(feature = "3d")]
pub const ZERO: AngularVelocity = AngularVelocity(Vector::ZERO);
}
#[cfg(feature = "2d")]
#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, From)]
#[reflect(Component)]
pub(crate) struct PreSolveAngularVelocity(pub Scalar);
#[cfg(feature = "3d")]
#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
#[reflect(Component)]
pub(crate) struct PreSolveAngularVelocity(pub Vector);
#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
#[derive(
Component, Reflect, Debug, Clone, Copy, PartialEq, PartialOrd, Default, Deref, DerefMut, From,
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component)]
pub struct GravityScale(pub Scalar);
#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub enum CoefficientCombine {
#[default]
Average = 1,
Min = 2,
Multiply = 3,
Max = 4,
}
#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
#[doc(alias = "Bounciness")]
#[doc(alias = "Elasticity")]
#[derive(Reflect, Clone, Copy, Component, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component)]
pub struct Restitution {
pub coefficient: Scalar,
pub combine_rule: CoefficientCombine,
}
impl Restitution {
pub const ZERO: Self = Self {
coefficient: 0.0,
combine_rule: CoefficientCombine::Average,
};
pub const PERFECTLY_INELASTIC: Self = Self {
coefficient: 0.0,
combine_rule: CoefficientCombine::Average,
};
pub const PERFECTLY_ELASTIC: Self = Self {
coefficient: 1.0,
combine_rule: CoefficientCombine::Average,
};
pub fn new(coefficient: Scalar) -> Self {
Self {
coefficient: coefficient.clamp(0.0, 1.0),
combine_rule: CoefficientCombine::Average,
}
}
pub fn with_combine_rule(&self, combine_rule: CoefficientCombine) -> Self {
Self {
combine_rule,
..*self
}
}
pub fn combine(&self, other: Self) -> Self {
let rule = self.combine_rule.max(other.combine_rule);
Self {
coefficient: match rule {
CoefficientCombine::Average => (self.coefficient + other.coefficient) * 0.5,
CoefficientCombine::Min => self.coefficient.min(other.coefficient),
CoefficientCombine::Multiply => self.coefficient * other.coefficient,
CoefficientCombine::Max => self.coefficient.max(other.coefficient),
},
combine_rule: rule,
}
}
}
impl Default for Restitution {
fn default() -> Self {
Self {
coefficient: 0.3,
combine_rule: CoefficientCombine::default(),
}
}
}
impl From<Scalar> for Restitution {
fn from(coefficient: Scalar) -> Self {
Self {
coefficient,
..default()
}
}
}
#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
#[derive(Reflect, Clone, Copy, Component, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component)]
pub struct Friction {
pub dynamic_coefficient: Scalar,
pub static_coefficient: Scalar,
pub combine_rule: CoefficientCombine,
}
impl Friction {
pub const ZERO: Self = Self {
dynamic_coefficient: 0.0,
static_coefficient: 0.0,
combine_rule: CoefficientCombine::Average,
};
pub fn new(friction_coefficient: Scalar) -> Self {
Self {
dynamic_coefficient: friction_coefficient,
static_coefficient: friction_coefficient,
..default()
}
}
pub fn with_combine_rule(&self, combine_rule: CoefficientCombine) -> Self {
Self {
combine_rule,
..*self
}
}
pub fn with_dynamic_coefficient(&self, coefficient: Scalar) -> Self {
Self {
dynamic_coefficient: coefficient,
..*self
}
}
pub fn with_static_coefficient(&self, coefficient: Scalar) -> Self {
Self {
static_coefficient: coefficient,
..*self
}
}
pub fn combine(&self, other: Self) -> Self {
let rule = self.combine_rule.max(other.combine_rule);
let (dynamic1, dynamic2) = (self.dynamic_coefficient, other.dynamic_coefficient);
let (static1, static2) = (self.static_coefficient, other.static_coefficient);
Self {
dynamic_coefficient: match rule {
CoefficientCombine::Average => (dynamic1 + dynamic2) * 0.5,
CoefficientCombine::Min => dynamic1.min(dynamic2),
CoefficientCombine::Multiply => dynamic1 * dynamic2,
CoefficientCombine::Max => dynamic1.max(dynamic2),
},
static_coefficient: match rule {
CoefficientCombine::Average => (static1 + static2) * 0.5,
CoefficientCombine::Min => static1.min(static2),
CoefficientCombine::Multiply => static1 * static2,
CoefficientCombine::Max => static1.max(static2),
},
combine_rule: rule,
}
}
}
impl Default for Friction {
fn default() -> Self {
Self {
dynamic_coefficient: 0.3,
static_coefficient: 0.3,
combine_rule: CoefficientCombine::default(),
}
}
}
impl From<Scalar> for Friction {
fn from(coefficient: Scalar) -> Self {
Self {
dynamic_coefficient: coefficient,
static_coefficient: coefficient,
..default()
}
}
}
#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
#[derive(
Component, Reflect, Debug, Clone, Copy, PartialEq, PartialOrd, Default, Deref, DerefMut, From,
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component)]
pub struct LinearDamping(pub Scalar);
#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
#[derive(
Component, Reflect, Debug, Clone, Copy, PartialEq, PartialOrd, Default, Deref, DerefMut, From,
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component)]
pub struct AngularDamping(pub Scalar);
#[cfg_attr(feature = "2d", doc = "use bevy_xpbd_2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "use bevy_xpbd_3d::prelude::*;")]
#[rustfmt::skip]
#[derive(Component, Reflect, Debug, Clone, Copy, Default, Deref, DerefMut, From, PartialEq, PartialOrd, Eq, Ord)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[reflect(Component)]
pub struct Dominance(pub i8);
#[cfg(test)]
mod tests {
use crate::prelude::*;
use approx::assert_relative_eq;
#[test]
fn restitution_clamping_works() {
assert_eq!(Restitution::new(-2.0).coefficient, 0.0);
assert_eq!(Restitution::new(0.6).coefficient, 0.6);
assert_eq!(Restitution::new(3.0).coefficient, 1.0);
}
#[test]
fn coefficient_combine_works() {
let r1 = Restitution::new(0.3).with_combine_rule(CoefficientCombine::Average);
let average_result =
r1.combine(Restitution::new(0.7).with_combine_rule(CoefficientCombine::Average));
let average_expected = Restitution::new(0.5).with_combine_rule(CoefficientCombine::Average);
assert_relative_eq!(
average_result.coefficient,
average_expected.coefficient,
epsilon = 0.0001
);
assert_eq!(average_result.combine_rule, average_expected.combine_rule);
assert_eq!(
r1.combine(Restitution::new(0.7).with_combine_rule(CoefficientCombine::Min)),
Restitution::new(0.3).with_combine_rule(CoefficientCombine::Min)
);
let multiply_result =
r1.combine(Restitution::new(0.7).with_combine_rule(CoefficientCombine::Multiply));
let multiply_expected =
Restitution::new(0.21).with_combine_rule(CoefficientCombine::Multiply);
assert_relative_eq!(
multiply_result.coefficient,
multiply_expected.coefficient,
epsilon = 0.0001
);
assert_eq!(multiply_result.combine_rule, multiply_expected.combine_rule);
assert_eq!(
r1.combine(Restitution::new(0.7).with_combine_rule(CoefficientCombine::Max)),
Restitution::new(0.7).with_combine_rule(CoefficientCombine::Max)
);
}
}