#![deny(future_incompatible, nonstandard_style)]
#![warn(missing_docs, rust_2018_idioms, clippy::pedantic)]
#![allow(clippy::module_name_repetitions, clippy::needless_pass_by_value)]
use core::any::Any;
use std::sync::Arc;
use bevy::ecs::schedule::ShouldRun;
use bevy::prelude::*;
#[cfg(feature = "collision-from-mesh")]
pub use collision_from_mesh::PendingConvexCollision;
pub use collisions::Collisions;
pub use constraints::RotationConstraints;
pub use events::{CollisionData, CollisionEvent};
pub use gravity::Gravity;
pub use layers::{CollisionLayers, PhysicsLayer};
pub use physics_time::PhysicsTime;
pub use step::{PhysicsStepDuration, PhysicsSteps};
pub use velocity::{Acceleration, AxisAngle, Damping, Velocity};
#[cfg(feature = "collision-from-mesh")]
mod collision_from_mesh;
mod collisions;
mod constraints;
mod events;
mod gravity;
mod layers;
mod physics_time;
mod step;
pub mod utils;
mod velocity;
#[deprecated(
note = "Physics system can be added to the bevy update stage. Use bevy's add_system instead."
)]
#[doc(hidden)]
pub mod stage {
pub const ROOT: &str = "heron-physics";
pub const UPDATE: &str = "heron-before-step";
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, SystemLabel)]
pub enum PhysicsSystem {
VelocityUpdate,
TransformUpdate,
Events,
}
#[derive(Debug, Copy, Clone, Default)]
pub struct CorePlugin;
#[allow(deprecated)]
impl Plugin for CorePlugin {
fn build(&self, app: &mut App) {
app.init_resource::<Gravity>()
.init_resource::<PhysicsTime>()
.init_resource::<PhysicsSteps>()
.register_type::<CollisionShape>()
.register_type::<RigidBody>()
.register_type::<PhysicMaterial>()
.register_type::<Velocity>()
.register_type::<Acceleration>()
.register_type::<Damping>()
.register_type::<RotationConstraints>()
.register_type::<CollisionLayers>()
.register_type::<SensorShape>()
.register_type::<Collisions>()
.add_system(collisions::update_collisions_system)
.add_system_to_stage(CoreStage::PostUpdate, collisions::cleanup_collisions_system)
.add_system_to_stage(CoreStage::First, PhysicsSteps::update)
.add_stage_before(CoreStage::PostUpdate, crate::stage::ROOT, {
Schedule::default().with_stage(crate::stage::UPDATE, SystemStage::parallel())
});
#[cfg(feature = "collision-from-mesh")]
app.register_type::<PendingConvexCollision>()
.add_system(collision_from_mesh::pending_collision_system);
}
}
#[must_use]
pub fn should_run(
physics_steps: Res<'_, PhysicsSteps>,
physics_time: Res<'_, PhysicsTime>,
) -> ShouldRun {
if physics_steps.is_step_frame() && physics_time.scale() > 0.0 {
ShouldRun::Yes
} else {
ShouldRun::No
}
}
#[derive(Clone)]
pub struct CustomCollisionShape(Arc<dyn Any + Send + Sync>, &'static str);
impl CustomCollisionShape {
pub fn new<T: Any + Send + Sync>(shape: T) -> Self {
Self(Arc::new(shape), std::any::type_name::<T>())
}
#[must_use]
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
self.0.downcast_ref()
}
}
impl core::fmt::Debug for CustomCollisionShape {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("CustomCollisionShape")
.field(&format_args!("{}", &self.1))
.finish()
}
}
#[derive(Debug, Clone, Component, Reflect)]
#[non_exhaustive]
pub enum CollisionShape {
Sphere {
radius: f32,
},
Capsule {
half_segment: f32,
radius: f32,
},
Cuboid {
half_extends: Vec3,
border_radius: Option<f32>,
},
ConvexHull {
points: Vec<Vec3>,
border_radius: Option<f32>,
},
HeightField {
size: Vec2,
heights: Vec<Vec<f32>>,
},
#[cfg(dim3)]
Cone {
half_height: f32,
radius: f32,
},
#[cfg(dim3)]
Cylinder {
half_height: f32,
radius: f32,
},
Custom {
shape: CustomCollisionShape,
},
}
impl Default for CollisionShape {
fn default() -> Self {
Self::Sphere { radius: 1.0 }
}
}
#[derive(Debug, Component, Copy, Clone, Eq, PartialEq, Reflect)]
pub enum RigidBody {
Dynamic,
Static,
KinematicPositionBased,
KinematicVelocityBased,
Sensor,
}
impl Default for RigidBody {
fn default() -> Self {
Self::Dynamic
}
}
impl RigidBody {
#[must_use]
pub fn can_have_velocity(self) -> bool {
match self {
RigidBody::Dynamic | RigidBody::KinematicVelocityBased => true,
RigidBody::Static | RigidBody::Sensor | RigidBody::KinematicPositionBased => false,
}
}
}
#[derive(Debug, Component, Copy, Clone, Default, Reflect)]
pub struct SensorShape;
#[derive(Debug, Component, Copy, Clone, PartialEq, Reflect)]
pub struct PhysicMaterial {
pub restitution: f32,
pub density: f32,
pub friction: f32,
}
impl PhysicMaterial {
pub const PERFECTLY_INELASTIC_RESTITUTION: f32 = 0.0;
pub const PERFECTLY_ELASTIC_RESTITUTION: f32 = 1.0;
}
impl Default for PhysicMaterial {
fn default() -> Self {
Self {
restitution: Self::PERFECTLY_INELASTIC_RESTITUTION,
density: 1.0,
friction: 0.0,
}
}
}