use crate::scene::collider::ColliderShape;
use crate::scene::node::constructor::NodeConstructor;
use crate::{
core::{
algebra::{Isometry3, Matrix4, UnitQuaternion, Vector3},
log::Log,
math::{aabb::AxisAlignedBoundingBox, m4x4_approx_eq},
parking_lot::Mutex,
pool::Handle,
reflect::prelude::*,
type_traits::prelude::*,
uuid::{uuid, Uuid},
variable::InheritableVariable,
visitor::prelude::*,
},
scene::{
base::{Base, BaseBuilder},
collider::Collider,
graph::Graph,
node::{Node, NodeTrait, SyncContext, UpdateContext},
Scene,
},
};
use fyrox_core::uuid_provider;
use fyrox_graph::constructor::ConstructorProvider;
use fyrox_graph::SceneGraph;
use rapier3d::{dynamics, prelude::RigidBodyHandle};
use std::{
cell::Cell,
collections::VecDeque,
fmt::{Debug, Formatter},
ops::{Deref, DerefMut},
};
use strum_macros::{AsRefStr, EnumString, VariantNames};
#[derive(
Copy, Clone, Debug, Reflect, Visit, PartialEq, Eq, Hash, AsRefStr, EnumString, VariantNames,
)]
#[repr(u32)]
#[derive(Default)]
pub enum RigidBodyType {
#[default]
Dynamic = 0,
Static = 1,
KinematicPositionBased = 2,
KinematicVelocityBased = 3,
}
uuid_provider!(RigidBodyType = "562d2907-1b41-483a-8ca2-12eebaff7f5d");
impl From<dynamics::RigidBodyType> for RigidBodyType {
fn from(s: dynamics::RigidBodyType) -> Self {
match s {
dynamics::RigidBodyType::Dynamic => Self::Dynamic,
dynamics::RigidBodyType::Fixed => Self::Static,
dynamics::RigidBodyType::KinematicPositionBased => Self::KinematicPositionBased,
dynamics::RigidBodyType::KinematicVelocityBased => Self::KinematicVelocityBased,
}
}
}
impl From<RigidBodyType> for rapier3d::dynamics::RigidBodyType {
fn from(v: RigidBodyType) -> Self {
match v {
RigidBodyType::Dynamic => rapier3d::dynamics::RigidBodyType::Dynamic,
RigidBodyType::Static => rapier3d::dynamics::RigidBodyType::Fixed,
RigidBodyType::KinematicPositionBased => {
rapier3d::dynamics::RigidBodyType::KinematicPositionBased
}
RigidBodyType::KinematicVelocityBased => {
rapier3d::dynamics::RigidBodyType::KinematicVelocityBased
}
}
}
}
impl From<RigidBodyType> for rapier2d::dynamics::RigidBodyType {
fn from(v: RigidBodyType) -> Self {
match v {
RigidBodyType::Dynamic => rapier2d::dynamics::RigidBodyType::Dynamic,
RigidBodyType::Static => rapier2d::dynamics::RigidBodyType::Fixed,
RigidBodyType::KinematicPositionBased => {
rapier2d::dynamics::RigidBodyType::KinematicPositionBased
}
RigidBodyType::KinematicVelocityBased => {
rapier2d::dynamics::RigidBodyType::KinematicVelocityBased
}
}
}
}
#[derive(Debug)]
pub(crate) enum ApplyAction {
Force(Vector3<f32>),
Torque(Vector3<f32>),
ForceAtPoint {
force: Vector3<f32>,
point: Vector3<f32>,
},
Impulse(Vector3<f32>),
TorqueImpulse(Vector3<f32>),
ImpulseAtPoint {
impulse: Vector3<f32>,
point: Vector3<f32>,
},
WakeUp,
NextTranslation(Vector3<f32>),
NextRotation(UnitQuaternion<f32>),
NextPosition(Isometry3<f32>),
}
#[derive(Copy, Clone, Debug, Reflect, Visit, PartialEq, AsRefStr, EnumString, VariantNames)]
pub enum RigidBodyMassPropertiesType {
Default,
Additional {
center_of_mass: Vector3<f32>,
principal_inertia: Vector3<f32>,
},
}
uuid_provider!(RigidBodyMassPropertiesType = "663b6a92-9c0f-4f47-b66a-6b4293312a5d");
#[derive(Visit, Reflect, ComponentProvider)]
#[reflect(derived_type = "Node")]
pub struct RigidBody {
base: Base,
#[reflect(setter = "set_lin_vel")]
pub(crate) lin_vel: InheritableVariable<Vector3<f32>>,
#[reflect(setter = "set_ang_vel")]
pub(crate) ang_vel: InheritableVariable<Vector3<f32>>,
#[reflect(setter = "set_lin_damping")]
pub(crate) lin_damping: InheritableVariable<f32>,
#[reflect(setter = "set_ang_damping")]
pub(crate) ang_damping: InheritableVariable<f32>,
#[reflect(setter = "set_body_type")]
pub(crate) body_type: InheritableVariable<RigidBodyType>,
#[reflect(min_value = 0.0, step = 0.05)]
#[reflect(setter = "set_mass")]
pub(crate) mass: InheritableVariable<f32>,
#[reflect(setter = "lock_x_rotations")]
pub(crate) x_rotation_locked: InheritableVariable<bool>,
#[reflect(setter = "lock_y_rotations")]
pub(crate) y_rotation_locked: InheritableVariable<bool>,
#[reflect(setter = "lock_z_rotations")]
pub(crate) z_rotation_locked: InheritableVariable<bool>,
#[reflect(setter = "lock_translation")]
pub(crate) translation_locked: InheritableVariable<bool>,
#[reflect(setter = "enable_ccd")]
pub(crate) ccd_enabled: InheritableVariable<bool>,
#[reflect(setter = "set_can_sleep")]
pub(crate) can_sleep: InheritableVariable<bool>,
#[reflect(setter = "set_dominance")]
pub(crate) dominance: InheritableVariable<i8>,
#[reflect(setter = "set_gravity_scale")]
pub(crate) gravity_scale: InheritableVariable<f32>,
#[visit(optional)]
#[reflect(setter = "set_mass_properties_type")]
pub(crate) mass_properties_type: InheritableVariable<RigidBodyMassPropertiesType>,
#[visit(skip)]
#[reflect(hidden)]
pub(crate) sleeping: bool,
#[visit(skip)]
#[reflect(hidden)]
pub(crate) reset_forces: Cell<bool>,
#[visit(skip)]
#[reflect(hidden)]
pub(crate) native: Cell<RigidBodyHandle>,
#[visit(skip)]
#[reflect(hidden)]
pub(crate) actions: Mutex<VecDeque<ApplyAction>>,
}
impl Debug for RigidBody {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "RigidBody")
}
}
impl Default for RigidBody {
fn default() -> Self {
Self {
base: Default::default(),
lin_vel: Default::default(),
ang_vel: Default::default(),
lin_damping: Default::default(),
ang_damping: Default::default(),
sleeping: Default::default(),
body_type: InheritableVariable::new_modified(RigidBodyType::Dynamic),
mass: InheritableVariable::new_modified(1.0),
x_rotation_locked: Default::default(),
y_rotation_locked: Default::default(),
z_rotation_locked: Default::default(),
translation_locked: Default::default(),
ccd_enabled: Default::default(),
can_sleep: InheritableVariable::new_modified(true),
dominance: Default::default(),
gravity_scale: InheritableVariable::new_modified(1.0),
native: Cell::new(RigidBodyHandle::invalid()),
actions: Default::default(),
reset_forces: Default::default(),
mass_properties_type: InheritableVariable::new_modified(
RigidBodyMassPropertiesType::Default,
),
}
}
}
impl Deref for RigidBody {
type Target = Base;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl DerefMut for RigidBody {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
impl Clone for RigidBody {
fn clone(&self) -> Self {
Self {
base: self.base.clone(),
lin_vel: self.lin_vel.clone(),
ang_vel: self.ang_vel.clone(),
lin_damping: self.lin_damping.clone(),
ang_damping: self.ang_damping.clone(),
sleeping: self.sleeping,
body_type: self.body_type.clone(),
mass: self.mass.clone(),
x_rotation_locked: self.x_rotation_locked.clone(),
y_rotation_locked: self.y_rotation_locked.clone(),
z_rotation_locked: self.z_rotation_locked.clone(),
translation_locked: self.translation_locked.clone(),
ccd_enabled: self.ccd_enabled.clone(),
can_sleep: self.can_sleep.clone(),
dominance: self.dominance.clone(),
gravity_scale: self.gravity_scale.clone(),
native: Cell::new(RigidBodyHandle::invalid()),
actions: Default::default(),
reset_forces: self.reset_forces.clone(),
mass_properties_type: self.mass_properties_type.clone(),
}
}
}
impl TypeUuidProvider for RigidBody {
fn type_uuid() -> Uuid {
uuid!("4be15a7c-3566-49c4-bba8-2f4ccc57ffed")
}
}
impl RigidBody {
pub fn set_lin_vel(&mut self, lin_vel: Vector3<f32>) -> Vector3<f32> {
self.lin_vel.set_value_and_mark_modified(lin_vel)
}
pub fn set_lin_vel_x(&mut self, x_vel: f32) {
self.lin_vel.x = x_vel;
}
pub fn set_lin_vel_y(&mut self, y_vel: f32) {
self.lin_vel.y = y_vel;
}
pub fn set_lin_vel_z(&mut self, z_vel: f32) {
self.lin_vel.z = z_vel;
}
pub fn lin_vel(&self) -> Vector3<f32> {
*self.lin_vel
}
pub fn set_ang_vel(&mut self, ang_vel: Vector3<f32>) -> Vector3<f32> {
self.ang_vel.set_value_and_mark_modified(ang_vel)
}
pub fn ang_vel(&self) -> Vector3<f32> {
*self.ang_vel
}
pub fn set_mass(&mut self, mass: f32) -> f32 {
self.mass.set_value_and_mark_modified(mass)
}
pub fn mass(&self) -> f32 {
*self.mass
}
pub fn set_ang_damping(&mut self, damping: f32) -> f32 {
self.ang_damping.set_value_and_mark_modified(damping)
}
pub fn ang_damping(&self) -> f32 {
*self.ang_damping
}
pub fn set_lin_damping(&mut self, damping: f32) -> f32 {
self.lin_damping.set_value_and_mark_modified(damping)
}
pub fn lin_damping(&self) -> f32 {
*self.lin_damping
}
pub fn lock_x_rotations(&mut self, state: bool) -> bool {
self.x_rotation_locked.set_value_and_mark_modified(state)
}
pub fn is_x_rotation_locked(&self) -> bool {
*self.x_rotation_locked
}
pub fn lock_y_rotations(&mut self, state: bool) -> bool {
self.y_rotation_locked.set_value_and_mark_modified(state)
}
pub fn is_y_rotation_locked(&self) -> bool {
*self.y_rotation_locked
}
pub fn lock_z_rotations(&mut self, state: bool) -> bool {
self.z_rotation_locked.set_value_and_mark_modified(state)
}
pub fn is_z_rotation_locked(&self) -> bool {
*self.z_rotation_locked
}
pub fn lock_rotations(&mut self, locked: bool) {
self.x_rotation_locked.set_value_and_mark_modified(locked);
self.y_rotation_locked.set_value_and_mark_modified(locked);
self.z_rotation_locked.set_value_and_mark_modified(locked);
}
pub fn lock_translation(&mut self, state: bool) -> bool {
self.translation_locked.set_value_and_mark_modified(state)
}
pub fn is_translation_locked(&self) -> bool {
*self.translation_locked
}
pub fn set_body_type(&mut self, body_type: RigidBodyType) -> RigidBodyType {
self.body_type.set_value_and_mark_modified(body_type)
}
pub fn body_type(&self) -> RigidBodyType {
*self.body_type
}
pub fn is_sleeping(&self) -> bool {
self.sleeping
}
pub fn is_ccd_enabled(&self) -> bool {
*self.ccd_enabled
}
pub fn enable_ccd(&mut self, enable: bool) -> bool {
self.ccd_enabled.set_value_and_mark_modified(enable)
}
pub fn set_gravity_scale(&mut self, scale: f32) -> f32 {
self.gravity_scale.set_value_and_mark_modified(scale)
}
pub fn set_mass_properties_type(
&mut self,
mass_properties_type: RigidBodyMassPropertiesType,
) -> RigidBodyMassPropertiesType {
self.mass_properties_type
.set_value_and_mark_modified(mass_properties_type)
}
pub fn mass_properties_type(&self) -> RigidBodyMassPropertiesType {
*self.mass_properties_type
}
pub fn gravity_scale(&self) -> f32 {
*self.gravity_scale
}
pub fn set_dominance(&mut self, dominance: i8) -> i8 {
self.dominance.set_value_and_mark_modified(dominance)
}
pub fn dominance(&self) -> i8 {
*self.dominance
}
pub fn apply_force(&mut self, force: Vector3<f32>) {
self.actions.get_mut().push_back(ApplyAction::Force(force))
}
pub fn apply_torque(&mut self, torque: Vector3<f32>) {
self.actions
.get_mut()
.push_back(ApplyAction::Torque(torque))
}
pub fn apply_force_at_point(&mut self, force: Vector3<f32>, point: Vector3<f32>) {
self.actions
.get_mut()
.push_back(ApplyAction::ForceAtPoint { force, point })
}
pub fn apply_impulse(&mut self, impulse: Vector3<f32>) {
self.actions
.get_mut()
.push_back(ApplyAction::Impulse(impulse))
}
pub fn apply_torque_impulse(&mut self, torque_impulse: Vector3<f32>) {
self.actions
.get_mut()
.push_back(ApplyAction::TorqueImpulse(torque_impulse))
}
pub fn apply_impulse_at_point(&mut self, impulse: Vector3<f32>, point: Vector3<f32>) {
self.actions
.get_mut()
.push_back(ApplyAction::ImpulseAtPoint { impulse, point })
}
pub fn set_next_kinematic_translation(&mut self, translation: Vector3<f32>) {
self.actions
.get_mut()
.push_back(ApplyAction::NextTranslation(translation));
}
pub fn set_next_kinematic_rotation(&mut self, rotation: UnitQuaternion<f32>) {
self.actions
.get_mut()
.push_back(ApplyAction::NextRotation(rotation));
}
pub fn set_next_kinematic_position(&mut self, position: Isometry3<f32>) {
self.actions
.get_mut()
.push_back(ApplyAction::NextPosition(position));
}
pub fn set_can_sleep(&mut self, can_sleep: bool) -> bool {
self.can_sleep.set_value_and_mark_modified(can_sleep)
}
pub fn is_can_sleep(&self) -> bool {
*self.can_sleep
}
pub fn wake_up(&mut self) {
self.actions.get_mut().push_back(ApplyAction::WakeUp)
}
pub(crate) fn need_sync_model(&self) -> bool {
self.lin_vel.need_sync()
|| self.ang_vel.need_sync()
|| self.lin_damping.need_sync()
|| self.ang_damping.need_sync()
|| self.body_type.need_sync()
|| self.mass.need_sync()
|| self.x_rotation_locked.need_sync()
|| self.y_rotation_locked.need_sync()
|| self.z_rotation_locked.need_sync()
|| self.translation_locked.need_sync()
|| self.ccd_enabled.need_sync()
|| self.can_sleep.need_sync()
|| self.dominance.need_sync()
|| self.gravity_scale.need_sync()
|| self.reset_forces.get()
}
}
impl ConstructorProvider<Node, Graph> for RigidBody {
fn constructor() -> NodeConstructor {
NodeConstructor::new::<Self>()
.with_variant("Rigid Body", |_| {
RigidBodyBuilder::new(BaseBuilder::new().with_name("Rigid Body"))
.build_node()
.into()
})
.with_group("Physics")
}
}
impl NodeTrait for RigidBody {
fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
self.base.local_bounding_box()
}
fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
self.base.world_bounding_box()
}
fn id(&self) -> Uuid {
Self::type_uuid()
}
fn on_removed_from_graph(&mut self, graph: &mut Graph) {
graph.physics.remove_body(self.native.get());
self.native.set(RigidBodyHandle::invalid());
Log::info(format!(
"Native rigid body was removed for node: {}",
self.name()
));
}
fn sync_native(&self, self_handle: Handle<Node>, context: &mut SyncContext) {
context.physics.sync_to_rigid_body_node(self_handle, self);
}
fn on_global_transform_changed(
&self,
new_global_transform: &Matrix4<f32>,
context: &mut SyncContext,
) {
if !m4x4_approx_eq(new_global_transform, &self.global_transform()) {
context
.physics
.set_rigid_body_position(self, new_global_transform);
}
}
fn update(&mut self, context: &mut UpdateContext) {
context.physics.sync_rigid_body_node(
self,
context
.nodes
.try_borrow(self.parent)
.ok()
.map(|p| p.global_transform())
.unwrap_or_else(Matrix4::identity),
);
}
fn validate(&self, scene: &Scene) -> Result<(), String> {
for &child in self.children() {
if let Ok(collider) = scene.graph.try_get_of_type::<Collider>(child) {
match collider.shape() {
ColliderShape::Trimesh(_) | ColliderShape::Heightfield(_)
if *self.body_type == RigidBodyType::Dynamic =>
{
return Err(
"The 3D rigid body is marked as dynamic, but uses the collider \
that cannot be dynamic. Consider making the rigid body static."
.to_string(),
)
}
_ => (),
}
return Ok(());
}
}
Err("The 3D rigid body must have at least one 3D collider as a \
direct child node to work correctly!"
.to_string())
}
}
pub struct RigidBodyBuilder {
base_builder: BaseBuilder,
lin_vel: Vector3<f32>,
ang_vel: Vector3<f32>,
lin_damping: f32,
ang_damping: f32,
sleeping: bool,
body_type: RigidBodyType,
mass: f32,
x_rotation_locked: bool,
y_rotation_locked: bool,
z_rotation_locked: bool,
translation_locked: bool,
ccd_enabled: bool,
can_sleep: bool,
dominance: i8,
gravity_scale: f32,
mass_properties_type: RigidBodyMassPropertiesType,
}
impl RigidBodyBuilder {
pub fn new(base_builder: BaseBuilder) -> Self {
Self {
base_builder,
lin_vel: Default::default(),
ang_vel: Default::default(),
lin_damping: 0.0,
ang_damping: 0.0,
sleeping: false,
body_type: RigidBodyType::Dynamic,
mass: 1.0,
x_rotation_locked: false,
y_rotation_locked: false,
z_rotation_locked: false,
translation_locked: false,
ccd_enabled: false,
can_sleep: true,
dominance: 0,
gravity_scale: 1.0,
mass_properties_type: RigidBodyMassPropertiesType::Default,
}
}
pub fn with_body_type(mut self, body_type: RigidBodyType) -> Self {
self.body_type = body_type;
self
}
pub fn with_mass(mut self, mass: f32) -> Self {
self.mass = mass;
self
}
pub fn with_ccd_enabled(mut self, enabled: bool) -> Self {
self.ccd_enabled = enabled;
self
}
pub fn with_lin_vel(mut self, lin_vel: Vector3<f32>) -> Self {
self.lin_vel = lin_vel;
self
}
pub fn with_ang_vel(mut self, ang_vel: Vector3<f32>) -> Self {
self.ang_vel = ang_vel;
self
}
pub fn with_ang_damping(mut self, ang_damping: f32) -> Self {
self.ang_damping = ang_damping;
self
}
pub fn with_lin_damping(mut self, lin_damping: f32) -> Self {
self.lin_damping = lin_damping;
self
}
pub fn with_x_rotation_locked(mut self, x_rotation_locked: bool) -> Self {
self.x_rotation_locked = x_rotation_locked;
self
}
pub fn with_y_rotation_locked(mut self, y_rotation_locked: bool) -> Self {
self.y_rotation_locked = y_rotation_locked;
self
}
pub fn with_z_rotation_locked(mut self, z_rotation_locked: bool) -> Self {
self.z_rotation_locked = z_rotation_locked;
self
}
pub fn with_translation_locked(mut self, translation_locked: bool) -> Self {
self.translation_locked = translation_locked;
self
}
pub fn with_locked_rotations(mut self, locked: bool) -> Self {
self.x_rotation_locked = locked;
self.y_rotation_locked = locked;
self.z_rotation_locked = locked;
self
}
pub fn with_sleeping(mut self, sleeping: bool) -> Self {
self.sleeping = sleeping;
self
}
pub fn with_can_sleep(mut self, can_sleep: bool) -> Self {
self.can_sleep = can_sleep;
self
}
pub fn with_dominance(mut self, dominance: i8) -> Self {
self.dominance = dominance;
self
}
pub fn with_gravity_scale(mut self, gravity_scale: f32) -> Self {
self.gravity_scale = gravity_scale;
self
}
pub fn with_mass_properties_type(
mut self,
mass_properties_type: RigidBodyMassPropertiesType,
) -> Self {
self.mass_properties_type = mass_properties_type;
self
}
pub fn build_rigid_body(self) -> RigidBody {
RigidBody {
base: self.base_builder.build_base(),
lin_vel: self.lin_vel.into(),
ang_vel: self.ang_vel.into(),
lin_damping: self.lin_damping.into(),
ang_damping: self.ang_damping.into(),
sleeping: self.sleeping,
body_type: self.body_type.into(),
mass: self.mass.into(),
x_rotation_locked: self.x_rotation_locked.into(),
y_rotation_locked: self.y_rotation_locked.into(),
z_rotation_locked: self.z_rotation_locked.into(),
translation_locked: self.translation_locked.into(),
ccd_enabled: self.ccd_enabled.into(),
can_sleep: self.can_sleep.into(),
dominance: self.dominance.into(),
gravity_scale: self.gravity_scale.into(),
native: Cell::new(RigidBodyHandle::invalid()),
actions: Default::default(),
reset_forces: Default::default(),
mass_properties_type: self.mass_properties_type.into(),
}
}
pub fn build_node(self) -> Node {
Node::new(self.build_rigid_body())
}
pub fn build(self, graph: &mut Graph) -> Handle<RigidBody> {
graph.add_node(self.build_node()).to_variant()
}
}