use crate::{
core::{
algebra::{Matrix4, Vector2},
log::Log,
math::{aabb::AxisAlignedBoundingBox, m4x4_approx_eq},
parking_lot::Mutex,
pool::Handle,
reflect::prelude::*,
uuid::{uuid, Uuid},
variable::InheritableVariable,
visitor::prelude::*,
TypeUuidProvider,
},
scene::{
base::{Base, BaseBuilder},
dim2::collider::Collider,
graph::Graph,
node::{Node, NodeTrait, SyncContext, UpdateContext},
rigidbody::RigidBodyType,
Scene,
},
};
use fyrox_graph::{BaseSceneGraph, SceneGraph};
use rapier2d::prelude::RigidBodyHandle;
use std::{
cell::Cell,
collections::VecDeque,
fmt::{Debug, Formatter},
ops::{Deref, DerefMut},
};
#[derive(Debug)]
pub(crate) enum ApplyAction {
Force(Vector2<f32>),
Torque(f32),
ForceAtPoint {
force: Vector2<f32>,
point: Vector2<f32>,
},
Impulse(Vector2<f32>),
TorqueImpulse(f32),
ImpulseAtPoint {
impulse: Vector2<f32>,
point: Vector2<f32>,
},
WakeUp,
}
#[derive(Visit, Reflect)]
pub struct RigidBody {
base: Base,
#[reflect(setter = "set_lin_vel")]
pub(crate) lin_vel: InheritableVariable<Vector2<f32>>,
#[reflect(setter = "set_ang_vel")]
pub(crate) ang_vel: InheritableVariable<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_rotations")]
pub(crate) 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(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),
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(),
}
}
}
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(),
rotation_locked: self.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(),
}
}
}
impl TypeUuidProvider for RigidBody {
fn type_uuid() -> Uuid {
uuid!("0b242335-75a4-4c65-9685-3e82a8979047")
}
}
impl RigidBody {
pub fn set_lin_vel(&mut self, lin_vel: Vector2<f32>) -> Vector2<f32> {
self.lin_vel.set_value_and_mark_modified(lin_vel)
}
pub fn lin_vel(&self) -> Vector2<f32> {
*self.lin_vel
}
pub fn set_ang_vel(&mut self, ang_vel: f32) -> f32 {
self.ang_vel.set_value_and_mark_modified(ang_vel)
}
pub fn ang_vel(&self) -> 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_rotations(&mut self, state: bool) -> bool {
self.rotation_locked.set_value_and_mark_modified(state)
}
pub fn is_rotation_locked(&self) -> bool {
*self.rotation_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 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: Vector2<f32>) {
self.actions.get_mut().push_back(ApplyAction::Force(force))
}
pub fn apply_torque(&mut self, torque: f32) {
self.actions
.get_mut()
.push_back(ApplyAction::Torque(torque))
}
pub fn apply_force_at_point(&mut self, force: Vector2<f32>, point: Vector2<f32>) {
self.actions
.get_mut()
.push_back(ApplyAction::ForceAtPoint { force, point })
}
pub fn apply_impulse(&mut self, impulse: Vector2<f32>) {
self.actions
.get_mut()
.push_back(ApplyAction::Impulse(impulse))
}
pub fn apply_torque_impulse(&mut self, torque_impulse: f32) {
self.actions
.get_mut()
.push_back(ApplyAction::TorqueImpulse(torque_impulse))
}
pub fn apply_impulse_at_point(&mut self, impulse: Vector2<f32>, point: Vector2<f32>) {
self.actions
.get_mut()
.push_back(ApplyAction::ImpulseAtPoint { impulse, point })
}
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.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 NodeTrait for RigidBody {
crate::impl_query_component!();
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.physics2d.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.physics2d.sync_to_rigid_body_node(self_handle, self);
}
fn sync_transform(&self, new_global_transform: &Matrix4<f32>, context: &mut SyncContext) {
if !m4x4_approx_eq(new_global_transform, &self.global_transform()) {
context
.physics2d
.set_rigid_body_position(self, new_global_transform);
}
}
fn update(&mut self, context: &mut UpdateContext) {
context.physics2d.sync_rigid_body_node(
self,
context
.nodes
.try_borrow(self.parent)
.map(|p| p.global_transform())
.unwrap_or_else(Matrix4::identity),
);
}
fn validate(&self, scene: &Scene) -> Result<(), String> {
for &child in self.children() {
if scene.graph.try_get_of_type::<Collider>(child).is_some() {
return Ok(());
}
}
Err("The 2D rigid body must have at least one 2D collider as a \
direct child node to work correctly!"
.to_string())
}
}
pub struct RigidBodyBuilder {
base_builder: BaseBuilder,
lin_vel: Vector2<f32>,
ang_vel: f32,
lin_damping: f32,
ang_damping: f32,
sleeping: bool,
body_type: RigidBodyType,
mass: f32,
rotation_locked: bool,
translation_locked: bool,
ccd_enabled: bool,
can_sleep: bool,
dominance: i8,
gravity_scale: f32,
}
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,
rotation_locked: false,
translation_locked: false,
ccd_enabled: false,
can_sleep: true,
dominance: 0,
gravity_scale: 1.0,
}
}
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: Vector2<f32>) -> Self {
self.lin_vel = lin_vel;
self
}
pub fn with_ang_vel(mut self, ang_vel: 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_rotation_locked(mut self, rotation_locked: bool) -> Self {
self.rotation_locked = rotation_locked;
self
}
pub fn with_translation_locked(mut self, translation_locked: bool) -> Self {
self.translation_locked = translation_locked;
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_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 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(),
rotation_locked: self.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(),
}
}
pub fn build_node(self) -> Node {
Node::new(self.build_rigid_body())
}
pub fn build(self, graph: &mut Graph) -> Handle<Node> {
graph.add_node(self.build_node())
}
}