mod plugin;
pub use plugin::SolverBodyPlugin;
use bevy::prelude::*;
use super::{Rotation, Vector};
use crate::{SymmetricTensor, math::Scalar, prelude::LockedAxes};
#[cfg(feature = "3d")]
use crate::{math::Quaternion, prelude::ComputedAngularInertia};
#[derive(Component, Clone, Debug, Default, Reflect)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Component, Debug)]
pub struct SolverBody {
pub linear_velocity: Vector,
#[cfg(feature = "2d")]
pub angular_velocity: Scalar,
#[cfg(feature = "3d")]
pub angular_velocity: Vector,
pub delta_position: Vector,
pub delta_rotation: Rotation,
pub flags: SolverBodyFlags,
}
impl SolverBody {
pub const DUMMY: Self = Self {
linear_velocity: Vector::ZERO,
#[cfg(feature = "2d")]
angular_velocity: 0.0,
#[cfg(feature = "3d")]
angular_velocity: Vector::ZERO,
delta_position: Vector::ZERO,
delta_rotation: Rotation::IDENTITY,
flags: SolverBodyFlags::empty(),
};
pub fn velocity_at_point(&self, point: Vector) -> Vector {
#[cfg(feature = "2d")]
{
self.linear_velocity + self.angular_velocity * point.perp()
}
#[cfg(feature = "3d")]
{
self.linear_velocity + self.angular_velocity.cross(point)
}
}
pub fn is_gyroscopic(&self) -> bool {
self.flags.contains(SolverBodyFlags::GYROSCOPIC_MOTION)
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Reflect)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug, PartialEq)]
pub struct SolverBodyFlags(u32);
bitflags::bitflags! {
impl SolverBodyFlags: u32 {
const TRANSLATION_X_LOCKED = 0b100_000;
const TRANSLATION_Y_LOCKED = 0b010_000;
const TRANSLATION_Z_LOCKED = 0b001_000;
const ROTATION_X_LOCKED = 0b000_100;
const ROTATION_Y_LOCKED = 0b000_010;
const ROTATION_Z_LOCKED = 0b000_001;
const TRANSLATION_LOCKED = Self::TRANSLATION_X_LOCKED.bits() | Self::TRANSLATION_Y_LOCKED.bits() | Self::TRANSLATION_Z_LOCKED.bits();
const ROTATION_LOCKED = Self::ROTATION_X_LOCKED.bits() | Self::ROTATION_Y_LOCKED.bits() | Self::ROTATION_Z_LOCKED.bits();
const ALL_LOCKED = Self::TRANSLATION_LOCKED.bits() | Self::ROTATION_LOCKED.bits();
const IS_KINEMATIC = 1 << 6;
const GYROSCOPIC_MOTION = 1 << 7;
}
}
impl SolverBodyFlags {
pub fn locked_axes(&self) -> LockedAxes {
LockedAxes::from_bits(self.0 as u8)
}
pub fn is_dynamic(&self) -> bool {
!self.contains(SolverBodyFlags::IS_KINEMATIC)
}
pub fn is_kinematic(&self) -> bool {
self.contains(SolverBodyFlags::IS_KINEMATIC)
}
}
#[derive(Component, Clone, Debug, Reflect)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Component, Debug)]
pub struct SolverBodyInertia {
#[cfg(feature = "2d")]
effective_inv_mass: Vector,
#[cfg(feature = "3d")]
inv_mass: Scalar,
#[cfg(feature = "2d")]
effective_inv_angular_inertia: SymmetricTensor,
#[cfg(feature = "3d")]
effective_inv_angular_inertia: SymmetricTensor,
dominance: i16,
flags: InertiaFlags,
}
impl SolverBodyInertia {
pub const DUMMY: Self = Self {
#[cfg(feature = "2d")]
effective_inv_mass: Vector::ZERO,
#[cfg(feature = "3d")]
inv_mass: 0.0,
#[cfg(feature = "2d")]
effective_inv_angular_inertia: 0.0,
#[cfg(feature = "3d")]
effective_inv_angular_inertia: SymmetricTensor::ZERO,
dominance: i8::MAX as i16 + 1,
flags: InertiaFlags::STATIC,
};
}
impl Default for SolverBodyInertia {
fn default() -> Self {
Self::DUMMY
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Reflect)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
#[reflect(Debug, PartialEq)]
pub struct InertiaFlags(u16);
bitflags::bitflags! {
impl InertiaFlags: u16 {
const TRANSLATION_X_LOCKED = 0b100_000;
const TRANSLATION_Y_LOCKED = 0b010_000;
const TRANSLATION_Z_LOCKED = 0b001_000;
const ROTATION_X_LOCKED = 0b000_100;
const ROTATION_Y_LOCKED = 0b000_010;
const ROTATION_Z_LOCKED = 0b000_001;
const TRANSLATION_LOCKED = Self::TRANSLATION_X_LOCKED.bits() | Self::TRANSLATION_Y_LOCKED.bits() | Self::TRANSLATION_Z_LOCKED.bits();
const ROTATION_LOCKED = Self::ROTATION_X_LOCKED.bits() | Self::ROTATION_Y_LOCKED.bits() | Self::ROTATION_Z_LOCKED.bits();
const ALL_LOCKED = Self::TRANSLATION_LOCKED.bits() | Self::ROTATION_LOCKED.bits();
const INFINITE_MASS = 1 << 6;
const INFINITE_ANGULAR_INERTIA = 1 << 7;
const STATIC = Self::INFINITE_MASS.bits() | Self::INFINITE_ANGULAR_INERTIA.bits();
}
}
impl InertiaFlags {
pub fn locked_axes(&self) -> LockedAxes {
LockedAxes::from_bits(self.0 as u8)
}
}
impl SolverBodyInertia {
#[inline]
#[cfg(feature = "2d")]
pub fn new(
inv_mass: Scalar,
inv_inertia: SymmetricTensor,
locked_axes: LockedAxes,
dominance: i8,
is_dynamic: bool,
) -> Self {
let mut effective_inv_mass = Vector::splat(inv_mass);
let mut effective_inv_angular_inertia = inv_inertia;
let mut flags = InertiaFlags(locked_axes.to_bits() as u16);
if inv_mass == 0.0 {
flags |= InertiaFlags::INFINITE_MASS;
}
if inv_inertia == 0.0 {
flags |= InertiaFlags::INFINITE_ANGULAR_INERTIA;
}
if locked_axes.is_translation_x_locked() {
effective_inv_mass.x = 0.0;
}
if locked_axes.is_translation_y_locked() {
effective_inv_mass.y = 0.0;
}
if locked_axes.is_rotation_locked() {
effective_inv_angular_inertia = 0.0;
}
Self {
effective_inv_mass,
effective_inv_angular_inertia,
dominance: if is_dynamic {
dominance as i16
} else {
i8::MAX as i16 + 1
},
flags: InertiaFlags(flags.0),
}
}
#[inline]
#[cfg(feature = "3d")]
pub fn new(
inv_mass: Scalar,
inv_inertia: SymmetricTensor,
locked_axes: LockedAxes,
dominance: i8,
is_dynamic: bool,
) -> Self {
let mut effective_inv_angular_inertia = inv_inertia;
let mut flags = InertiaFlags(locked_axes.to_bits() as u16);
if inv_mass == 0.0 {
flags |= InertiaFlags::INFINITE_MASS;
}
if inv_inertia == SymmetricTensor::ZERO {
flags |= InertiaFlags::INFINITE_ANGULAR_INERTIA;
}
if locked_axes.is_rotation_x_locked() {
effective_inv_angular_inertia.m00 = 0.0;
effective_inv_angular_inertia.m01 = 0.0;
effective_inv_angular_inertia.m02 = 0.0;
}
if locked_axes.is_rotation_y_locked() {
effective_inv_angular_inertia.m01 = 0.0;
effective_inv_angular_inertia.m11 = 0.0;
effective_inv_angular_inertia.m12 = 0.0;
}
if locked_axes.is_rotation_z_locked() {
effective_inv_angular_inertia.m02 = 0.0;
effective_inv_angular_inertia.m12 = 0.0;
effective_inv_angular_inertia.m22 = 0.0;
}
Self {
inv_mass,
effective_inv_angular_inertia,
dominance: if is_dynamic {
dominance as i16
} else {
i8::MAX as i16 + 1
},
flags: InertiaFlags(flags.0),
}
}
#[inline]
#[cfg(feature = "2d")]
pub fn effective_inv_mass(&self) -> Vector {
self.effective_inv_mass
}
#[inline]
#[cfg(feature = "3d")]
pub fn effective_inv_mass(&self) -> Vector {
let mut inv_mass = Vector::splat(self.inv_mass);
if self.flags.contains(InertiaFlags::TRANSLATION_X_LOCKED) {
inv_mass.x = 0.0;
}
if self.flags.contains(InertiaFlags::TRANSLATION_Y_LOCKED) {
inv_mass.y = 0.0;
}
if self.flags.contains(InertiaFlags::TRANSLATION_Z_LOCKED) {
inv_mass.z = 0.0;
}
inv_mass
}
#[inline]
#[cfg(feature = "2d")]
pub fn effective_inv_angular_inertia(&self) -> SymmetricTensor {
self.effective_inv_angular_inertia
}
#[inline]
#[cfg(feature = "3d")]
pub fn effective_inv_angular_inertia(&self) -> SymmetricTensor {
self.effective_inv_angular_inertia
}
#[inline]
#[cfg(feature = "3d")]
pub fn update_effective_inv_angular_inertia(
&mut self,
computed_angular_inertia: &ComputedAngularInertia,
rotation: Quaternion,
) {
let locked_axes = self.flags.locked_axes();
let mut effective_inv_angular_inertia =
computed_angular_inertia.rotated(rotation).inverse();
if locked_axes.is_rotation_x_locked() {
effective_inv_angular_inertia.m00 = 0.0;
effective_inv_angular_inertia.m01 = 0.0;
effective_inv_angular_inertia.m02 = 0.0;
}
if locked_axes.is_rotation_y_locked() {
effective_inv_angular_inertia.m11 = 0.0;
effective_inv_angular_inertia.m01 = 0.0;
effective_inv_angular_inertia.m12 = 0.0;
}
if locked_axes.is_rotation_z_locked() {
effective_inv_angular_inertia.m22 = 0.0;
effective_inv_angular_inertia.m02 = 0.0;
effective_inv_angular_inertia.m12 = 0.0;
}
self.effective_inv_angular_inertia = effective_inv_angular_inertia;
}
#[inline]
pub fn dominance(&self) -> i16 {
self.dominance
}
#[inline]
pub fn flags(&self) -> InertiaFlags {
self.flags
}
}