use crate::{dynamics::integrator::VelocityIntegrationData, prelude::*};
use bevy::ecs::{
query::{Has, QueryData},
system::lifetimeless::{Read, Write},
};
use super::AccumulatedLocalAcceleration;
#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
#[cfg_attr(
feature = "2d",
doc = " forces.apply_force(Vec2::new(0.0, 10.0));"
)]
#[cfg_attr(
feature = "3d",
doc = " forces.apply_force(Vec3::new(0.0, 10.0, 0.0));"
)]
#[cfg_attr(feature = "2d", doc = "# use avian2d::{math::Vector, prelude::*};")]
#[cfg_attr(feature = "3d", doc = "# use avian3d::{math::Vector, prelude::*};")]
#[cfg_attr(feature = "2d", doc = "# use avian2d::{math::Vector, prelude::*};")]
#[cfg_attr(feature = "3d", doc = "# use avian3d::{math::Vector, prelude::*};")]
#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
#[cfg_attr(
feature = "2d",
doc = " forces.apply_linear_acceleration(direction.truncate() * 9.81);"
)]
#[cfg_attr(
feature = "3d",
doc = " forces.apply_linear_acceleration(direction * 9.81);"
)]
#[derive(QueryData)]
#[query_data(mutable)]
pub struct Forces {
position: Read<Position>,
rotation: Read<Rotation>,
linear_velocity: Write<LinearVelocity>,
angular_velocity: Write<AngularVelocity>,
mass: Read<ComputedMass>,
angular_inertia: Read<ComputedAngularInertia>,
center_of_mass: Read<ComputedCenterOfMass>,
locked_axes: Option<Read<LockedAxes>>,
integration: Write<VelocityIntegrationData>,
accumulated_local_acceleration: Write<AccumulatedLocalAcceleration>,
sleep_timer: Option<Write<SleepTimer>>,
is_sleeping: Has<Sleeping>,
}
pub struct NonWakingForcesItem<'w, 's>(pub ForcesItem<'w, 's>);
impl ForcesItem<'_, '_> {
#[inline]
#[must_use]
pub fn reborrow(&mut self) -> ForcesItem<'_, '_> {
ForcesItem {
position: self.position,
rotation: self.rotation,
linear_velocity: self.linear_velocity.reborrow(),
angular_velocity: self.angular_velocity.reborrow(),
mass: self.mass,
angular_inertia: self.angular_inertia,
center_of_mass: self.center_of_mass,
locked_axes: self.locked_axes,
integration: self.integration.reborrow(),
accumulated_local_acceleration: self.accumulated_local_acceleration.reborrow(),
sleep_timer: self.sleep_timer.as_mut().map(|s| s.reborrow()),
is_sleeping: self.is_sleeping,
}
}
#[inline]
#[must_use]
pub fn non_waking(&mut self) -> NonWakingForcesItem<'_, '_> {
NonWakingForcesItem(self.reborrow())
}
}
impl<'w, 's> NonWakingForcesItem<'w, 's> {
#[inline]
#[must_use]
pub fn waking(self) -> ForcesItem<'w, 's> {
self.0
}
}
impl ReadRigidBodyForces for ForcesItem<'_, '_> {}
impl ReadRigidBodyForces for NonWakingForcesItem<'_, '_> {}
impl ReadRigidBodyForces for ForcesReadOnlyItem<'_, '_> {}
impl WriteRigidBodyForces for ForcesItem<'_, '_> {}
impl WriteRigidBodyForces for NonWakingForcesItem<'_, '_> {}
pub trait RigidBodyForces: ReadRigidBodyForces + WriteRigidBodyForces {}
#[expect(
private_bounds,
reason = "The internal methods should not be publicly accessible."
)]
pub trait ReadRigidBodyForces: ReadRigidBodyForcesInternal {
#[inline]
fn position(&self) -> &Position {
self.pos()
}
#[inline]
fn rotation(&self) -> &Rotation {
self.rot()
}
#[inline]
fn linear_velocity(&self) -> Vector {
self.lin_vel()
}
#[inline]
fn angular_velocity(&self) -> AngularVector {
self.ang_vel()
}
#[inline]
fn accumulated_linear_acceleration(&self) -> Vector {
let world_linear_acceleration = self.integration_data().linear_increment;
let local_linear_acceleration = self.accumulated_local_acceleration().linear;
self.locked_axes()
.apply_to_vec(world_linear_acceleration + self.rot() * local_linear_acceleration)
}
#[cfg(feature = "2d")]
#[inline]
fn accumulated_angular_acceleration(&self) -> AngularVector {
self.locked_axes()
.apply_to_angular_velocity(self.integration_data().angular_increment)
}
#[cfg(feature = "3d")]
#[inline]
fn accumulated_angular_acceleration(&self) -> AngularVector {
let world_angular_acceleration = self.integration_data().angular_increment;
let local_angular_acceleration = self.accumulated_local_acceleration().angular;
self.locked_axes().apply_to_angular_velocity(
world_angular_acceleration + self.rot() * local_angular_acceleration,
)
}
#[inline]
#[doc(alias = "linear_velocity_at_point")]
fn velocity_at_point(&self, world_point: Vector) -> Vector {
let offset = world_point - self.global_center_of_mass();
#[cfg(feature = "2d")]
{
self.linear_velocity() + self.angular_velocity() * offset.perp()
}
#[cfg(feature = "3d")]
{
self.linear_velocity() + self.angular_velocity().cross(offset)
}
}
}
#[expect(
private_bounds,
reason = "The internal methods should not be publicly accessible."
)]
pub trait WriteRigidBodyForces: ReadRigidBodyForces + WriteRigidBodyForcesInternal {
#[inline]
fn apply_force(&mut self, force: Vector) {
if force != Vector::ZERO && self.try_wake_up() {
let acceleration = self.inverse_mass() * force;
self.integration_data_mut()
.apply_linear_acceleration(acceleration);
}
}
#[inline]
fn apply_force_at_point(&mut self, force: Vector, world_point: Vector) {
self.apply_force(force);
self.apply_torque(cross(world_point - self.global_center_of_mass(), force));
}
#[inline]
fn apply_local_force(&mut self, force: Vector) {
if force != Vector::ZERO && self.try_wake_up() {
let acceleration = self.inverse_mass() * force;
self.accumulated_local_acceleration_mut().linear += acceleration;
}
}
#[inline]
fn apply_torque(&mut self, torque: AngularVector) {
if torque != AngularVector::ZERO && self.try_wake_up() {
let acceleration = self.effective_inverse_angular_inertia() * torque;
self.integration_data_mut()
.apply_angular_acceleration(acceleration);
}
}
#[cfg(feature = "3d")]
#[inline]
fn apply_local_torque(&mut self, torque: AngularVector) {
if torque != AngularVector::ZERO && self.try_wake_up() {
let acceleration = self.inverse_angular_inertia() * torque;
self.accumulated_local_acceleration_mut().angular += acceleration;
}
}
#[inline]
fn apply_linear_impulse(&mut self, impulse: Vector) {
if impulse != Vector::ZERO && self.try_wake_up() {
let effective_inverse_mass = self
.locked_axes()
.apply_to_vec(Vector::splat(self.inverse_mass()));
let delta_vel = effective_inverse_mass * impulse;
*self.linear_velocity_mut() += delta_vel;
}
}
#[inline]
fn apply_linear_impulse_at_point(&mut self, impulse: Vector, world_point: Vector) {
self.apply_linear_impulse(impulse);
self.apply_angular_impulse(cross(world_point - self.global_center_of_mass(), impulse));
}
#[inline]
fn apply_local_linear_impulse(&mut self, impulse: Vector) {
if impulse != Vector::ZERO && self.try_wake_up() {
let world_impulse = self.rot() * impulse;
let effective_inverse_mass = self
.locked_axes()
.apply_to_vec(Vector::splat(self.inverse_mass()));
let delta_vel = effective_inverse_mass * world_impulse;
*self.linear_velocity_mut() += delta_vel;
}
}
#[inline]
fn apply_angular_impulse(&mut self, impulse: AngularVector) {
if impulse != AngularVector::ZERO && self.try_wake_up() {
let effective_inverse_angular_inertia = self.effective_inverse_angular_inertia();
let delta_vel = effective_inverse_angular_inertia * impulse;
*self.angular_velocity_mut() += delta_vel;
}
}
#[cfg(feature = "3d")]
#[inline]
fn apply_local_angular_impulse(&mut self, impulse: AngularVector) {
if impulse != AngularVector::ZERO && self.try_wake_up() {
let world_impulse = self.rot() * impulse;
let effective_inverse_angular_inertia = self.effective_inverse_angular_inertia();
let delta_vel = effective_inverse_angular_inertia * world_impulse;
*self.angular_velocity_mut() += delta_vel;
}
}
#[inline]
fn apply_linear_acceleration(&mut self, acceleration: Vector) {
if acceleration != Vector::ZERO && self.try_wake_up() {
self.integration_data_mut()
.apply_linear_acceleration(acceleration);
}
}
#[inline]
fn apply_linear_acceleration_at_point(&mut self, acceleration: Vector, world_point: Vector) {
self.apply_linear_acceleration(acceleration);
self.apply_angular_acceleration(cross(
world_point - self.global_center_of_mass(),
acceleration,
));
}
#[inline]
fn apply_local_linear_acceleration(&mut self, acceleration: Vector) {
if acceleration != Vector::ZERO && self.try_wake_up() {
self.accumulated_local_acceleration_mut().linear += acceleration;
}
}
#[inline]
fn apply_angular_acceleration(&mut self, acceleration: AngularVector) {
if acceleration != AngularVector::ZERO && self.try_wake_up() {
self.integration_data_mut()
.apply_angular_acceleration(acceleration);
}
}
#[cfg(feature = "3d")]
#[inline]
fn apply_local_angular_acceleration(&mut self, acceleration: AngularVector) {
if acceleration != AngularVector::ZERO && self.try_wake_up() {
self.accumulated_local_acceleration_mut().angular += acceleration;
}
}
#[inline]
fn linear_velocity_mut(&mut self) -> &mut Vector {
self.lin_vel_mut()
}
#[inline]
fn angular_velocity_mut(&mut self) -> &mut AngularVector {
self.ang_vel_mut()
}
#[inline]
fn reset_accumulated_linear_acceleration(&mut self) {
self.integration_data_mut().linear_increment = Vector::ZERO;
self.accumulated_local_acceleration_mut().linear = Vector::ZERO;
}
#[inline]
fn reset_accumulated_angular_acceleration(&mut self) {
self.integration_data_mut().angular_increment = AngularVector::ZERO;
#[cfg(feature = "3d")]
{
self.accumulated_local_acceleration_mut().angular = AngularVector::ZERO;
}
}
}
trait ReadRigidBodyForcesInternal {
fn pos(&self) -> &Position;
fn rot(&self) -> &Rotation;
fn lin_vel(&self) -> Vector;
fn ang_vel(&self) -> AngularVector;
fn global_center_of_mass(&self) -> Vector;
fn locked_axes(&self) -> LockedAxes;
fn integration_data(&self) -> &VelocityIntegrationData;
fn accumulated_local_acceleration(&self) -> &AccumulatedLocalAcceleration;
}
trait WriteRigidBodyForcesInternal: ReadRigidBodyForcesInternal {
fn lin_vel_mut(&mut self) -> &mut Vector;
fn ang_vel_mut(&mut self) -> &mut AngularVector;
fn inverse_mass(&self) -> Scalar;
#[cfg(feature = "3d")]
fn inverse_angular_inertia(&self) -> SymmetricTensor;
fn effective_inverse_angular_inertia(&self) -> SymmetricTensor;
fn integration_data_mut(&mut self) -> &mut VelocityIntegrationData;
fn accumulated_local_acceleration_mut(&mut self) -> &mut AccumulatedLocalAcceleration;
fn try_wake_up(&mut self) -> bool;
}
impl ReadRigidBodyForcesInternal for ForcesItem<'_, '_> {
#[inline]
fn pos(&self) -> &Position {
self.position
}
#[inline]
fn rot(&self) -> &Rotation {
self.rotation
}
#[inline]
fn lin_vel(&self) -> Vector {
self.linear_velocity.0
}
#[inline]
fn ang_vel(&self) -> AngularVector {
self.angular_velocity.0
}
#[inline]
fn global_center_of_mass(&self) -> Vector {
self.position.0 + self.rotation * self.center_of_mass.0
}
#[inline]
fn locked_axes(&self) -> LockedAxes {
self.locked_axes.copied().unwrap_or_default()
}
#[inline]
fn integration_data(&self) -> &VelocityIntegrationData {
&self.integration
}
#[inline]
fn accumulated_local_acceleration(&self) -> &AccumulatedLocalAcceleration {
&self.accumulated_local_acceleration
}
}
impl WriteRigidBodyForcesInternal for ForcesItem<'_, '_> {
#[inline]
fn lin_vel_mut(&mut self) -> &mut Vector {
&mut self.linear_velocity.0
}
#[inline]
fn ang_vel_mut(&mut self) -> &mut AngularVector {
&mut self.angular_velocity.0
}
#[inline]
fn inverse_mass(&self) -> Scalar {
self.mass.inverse()
}
#[inline]
#[cfg(feature = "3d")]
fn inverse_angular_inertia(&self) -> SymmetricTensor {
self.angular_inertia.inverse()
}
#[inline]
fn effective_inverse_angular_inertia(&self) -> SymmetricTensor {
#[cfg(feature = "2d")]
let global_angular_inertia = *self.angular_inertia;
#[cfg(feature = "3d")]
let global_angular_inertia = self.angular_inertia.rotated(self.rotation.0);
self.locked_axes()
.apply_to_angular_inertia(global_angular_inertia)
.inverse()
}
#[inline]
fn integration_data_mut(&mut self) -> &mut VelocityIntegrationData {
&mut self.integration
}
#[inline]
fn accumulated_local_acceleration_mut(&mut self) -> &mut AccumulatedLocalAcceleration {
&mut self.accumulated_local_acceleration
}
#[inline]
fn try_wake_up(&mut self) -> bool {
if let Some(sleep_timer) = &mut self.sleep_timer {
sleep_timer.0 = 0.0;
}
true
}
}
impl ReadRigidBodyForcesInternal for NonWakingForcesItem<'_, '_> {
#[inline]
fn pos(&self) -> &Position {
self.0.position()
}
#[inline]
fn rot(&self) -> &Rotation {
self.0.rot()
}
#[inline]
fn lin_vel(&self) -> Vector {
self.0.lin_vel()
}
#[inline]
fn ang_vel(&self) -> AngularVector {
self.0.ang_vel()
}
#[inline]
fn global_center_of_mass(&self) -> Vector {
self.0.global_center_of_mass()
}
#[inline]
fn locked_axes(&self) -> LockedAxes {
self.0.locked_axes()
}
#[inline]
fn integration_data(&self) -> &VelocityIntegrationData {
self.0.integration_data()
}
#[inline]
fn accumulated_local_acceleration(&self) -> &AccumulatedLocalAcceleration {
self.0.accumulated_local_acceleration()
}
}
impl ReadRigidBodyForcesInternal for ForcesReadOnlyItem<'_, '_> {
#[inline]
fn pos(&self) -> &Position {
self.position
}
#[inline]
fn rot(&self) -> &Rotation {
self.rotation
}
#[inline]
fn lin_vel(&self) -> Vector {
self.linear_velocity.0
}
#[inline]
fn ang_vel(&self) -> AngularVector {
self.angular_velocity.0
}
#[inline]
fn global_center_of_mass(&self) -> Vector {
self.position.0 + self.rotation * self.center_of_mass.0
}
#[inline]
fn locked_axes(&self) -> LockedAxes {
self.locked_axes.copied().unwrap_or_default()
}
#[inline]
fn integration_data(&self) -> &VelocityIntegrationData {
self.integration
}
#[inline]
fn accumulated_local_acceleration(&self) -> &AccumulatedLocalAcceleration {
self.accumulated_local_acceleration
}
}
impl WriteRigidBodyForcesInternal for NonWakingForcesItem<'_, '_> {
#[inline]
fn lin_vel_mut(&mut self) -> &mut Vector {
self.0.lin_vel_mut()
}
#[inline]
fn ang_vel_mut(&mut self) -> &mut AngularVector {
self.0.ang_vel_mut()
}
#[inline]
fn inverse_mass(&self) -> Scalar {
self.0.inverse_mass()
}
#[inline]
#[cfg(feature = "3d")]
fn inverse_angular_inertia(&self) -> SymmetricTensor {
self.0.inverse_angular_inertia()
}
#[inline]
fn effective_inverse_angular_inertia(&self) -> SymmetricTensor {
self.0.effective_inverse_angular_inertia()
}
#[inline]
fn integration_data_mut(&mut self) -> &mut VelocityIntegrationData {
self.0.integration_data_mut()
}
#[inline]
fn accumulated_local_acceleration_mut(&mut self) -> &mut AccumulatedLocalAcceleration {
self.0.accumulated_local_acceleration_mut()
}
#[inline]
fn try_wake_up(&mut self) -> bool {
!self.0.is_sleeping
}
}