use crate::scene::node::constructor::NodeConstructor;
use crate::{
core::{
algebra::Matrix4,
log::Log,
math::{aabb::AxisAlignedBoundingBox, m4x4_approx_eq},
pool::Handle,
reflect::prelude::*,
type_traits::prelude::*,
uuid::{uuid, Uuid},
variable::InheritableVariable,
visitor::prelude::*,
},
scene::{
base::{Base, BaseBuilder},
graph::Graph,
node::{Node, NodeTrait, SyncContext},
rigidbody::RigidBody,
Scene,
},
};
use fyrox_core::algebra::{Isometry3, Vector3};
use fyrox_core::uuid_provider;
use fyrox_graph::constructor::ConstructorProvider;
use fyrox_graph::SceneGraph;
use rapier2d::na::UnitQuaternion;
use rapier3d::dynamics::ImpulseJointHandle;
use std::cell::RefCell;
use std::{
cell::Cell,
ops::{Deref, DerefMut, Range},
};
use strum_macros::{AsRefStr, EnumString, VariantNames};
#[derive(Clone, Debug, Visit, PartialEq, Reflect)]
pub struct BallJoint {
pub x_limits_enabled: bool,
pub x_limits_angles: Range<f32>,
pub y_limits_enabled: bool,
pub y_limits_angles: Range<f32>,
pub z_limits_enabled: bool,
pub z_limits_angles: Range<f32>,
}
impl Default for BallJoint {
fn default() -> Self {
Self {
x_limits_enabled: false,
x_limits_angles: -std::f32::consts::PI..std::f32::consts::PI,
y_limits_enabled: false,
y_limits_angles: -std::f32::consts::PI..std::f32::consts::PI,
z_limits_enabled: false,
z_limits_angles: -std::f32::consts::PI..std::f32::consts::PI,
}
}
}
#[derive(Clone, Debug, Visit, PartialEq, Reflect, Default, Eq)]
pub struct FixedJoint;
#[derive(Clone, Debug, Visit, PartialEq, Reflect)]
pub struct PrismaticJoint {
pub limits_enabled: bool,
pub limits: Range<f32>,
}
impl Default for PrismaticJoint {
fn default() -> Self {
Self {
limits_enabled: false,
limits: -std::f32::consts::PI..std::f32::consts::PI,
}
}
}
#[derive(Clone, Debug, Visit, PartialEq, Reflect)]
pub struct RevoluteJoint {
pub limits_enabled: bool,
pub limits: Range<f32>,
}
impl Default for RevoluteJoint {
fn default() -> Self {
Self {
limits_enabled: false,
limits: -std::f32::consts::PI..std::f32::consts::PI,
}
}
}
#[derive(Default, Clone, Debug, PartialEq, Visit, Reflect)]
pub struct JointMotorParams {
pub target_vel: f32,
pub target_pos: f32,
pub stiffness: f32,
pub damping: f32,
pub max_force: f32,
}
#[derive(Clone, Debug, PartialEq, Visit, Reflect, AsRefStr, EnumString, VariantNames)]
pub enum JointParams {
BallJoint(BallJoint),
FixedJoint(FixedJoint),
PrismaticJoint(PrismaticJoint),
RevoluteJoint(RevoluteJoint),
}
uuid_provider!(JointParams = "a3e09303-9de4-4123-9492-05e27f29aaa3");
impl Default for JointParams {
fn default() -> Self {
Self::BallJoint(Default::default())
}
}
#[derive(Visit, Reflect, Debug, Clone, Default)]
pub(crate) struct LocalFrame {
pub position: Vector3<f32>,
pub rotation: UnitQuaternion<f32>,
}
impl LocalFrame {
pub fn new(isometry: &Isometry3<f32>) -> Self {
Self {
position: isometry.translation.vector,
rotation: isometry.rotation,
}
}
}
#[derive(Visit, Reflect, Debug, Clone, Default)]
pub(crate) struct JointLocalFrames {
pub body1: LocalFrame,
pub body2: LocalFrame,
}
impl JointLocalFrames {
pub fn new(isometry1: &Isometry3<f32>, isometry2: &Isometry3<f32>) -> Self {
Self {
body1: LocalFrame::new(isometry1),
body2: LocalFrame::new(isometry2),
}
}
}
#[derive(Visit, Reflect, Debug, ComponentProvider)]
#[reflect(derived_type = "Node")]
pub struct Joint {
base: Base,
#[reflect(setter = "set_params")]
pub(crate) params: InheritableVariable<JointParams>,
#[reflect(setter = "set_motor_params")]
pub(crate) motor_params: InheritableVariable<JointMotorParams>,
#[reflect(setter = "set_body1")]
pub(crate) body1: InheritableVariable<Handle<RigidBody>>,
#[reflect(setter = "set_body2")]
pub(crate) body2: InheritableVariable<Handle<RigidBody>>,
#[reflect(setter = "set_contacts_enabled")]
pub(crate) contacts_enabled: InheritableVariable<bool>,
#[reflect(setter = "set_auto_rebinding")]
pub(crate) auto_rebind: InheritableVariable<bool>,
#[visit(optional)]
#[reflect(hidden)]
pub(crate) local_frames: RefCell<Option<JointLocalFrames>>,
#[visit(skip)]
#[reflect(hidden)]
pub(crate) native: Cell<ImpulseJointHandle>,
}
impl Default for Joint {
fn default() -> Self {
Self {
base: Default::default(),
params: Default::default(),
motor_params: Default::default(),
body1: Default::default(),
body2: Default::default(),
contacts_enabled: InheritableVariable::new_modified(true),
auto_rebind: true.into(),
local_frames: Default::default(),
native: Cell::new(ImpulseJointHandle::invalid()),
}
}
}
impl Deref for Joint {
type Target = Base;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl DerefMut for Joint {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
impl Clone for Joint {
fn clone(&self) -> Self {
Self {
base: self.base.clone(),
params: self.params.clone(),
motor_params: self.motor_params.clone(),
body1: self.body1.clone(),
body2: self.body2.clone(),
contacts_enabled: self.contacts_enabled.clone(),
local_frames: self.local_frames.clone(),
auto_rebind: self.auto_rebind.clone(),
native: Cell::new(ImpulseJointHandle::invalid()),
}
}
}
impl TypeUuidProvider for Joint {
fn type_uuid() -> Uuid {
uuid!("439d48f5-e3a3-4255-aa08-353c1ca42e3b")
}
}
impl Joint {
pub fn params(&self) -> &JointParams {
&self.params
}
pub fn params_mut(&mut self) -> &mut JointParams {
self.params.get_value_mut_and_mark_modified()
}
pub fn set_params(&mut self, params: JointParams) -> JointParams {
self.params.set_value_and_mark_modified(params)
}
pub fn motor_params(&self) -> &JointMotorParams {
&self.motor_params
}
pub fn motor_params_mut(&mut self) -> &mut JointMotorParams {
self.motor_params.get_value_mut_and_mark_modified()
}
pub fn set_motor_params(&mut self, motor_params: JointMotorParams) -> JointMotorParams {
self.motor_params.set_value_and_mark_modified(motor_params)
}
pub fn set_body1(&mut self, handle: Handle<RigidBody>) -> Handle<RigidBody> {
self.body1.set_value_and_mark_modified(handle)
}
pub fn body1(&self) -> Handle<RigidBody> {
*self.body1
}
pub fn set_body2(&mut self, handle: Handle<RigidBody>) -> Handle<RigidBody> {
self.body2.set_value_and_mark_modified(handle)
}
pub fn body2(&self) -> Handle<RigidBody> {
*self.body2
}
pub fn set_contacts_enabled(&mut self, enabled: bool) -> bool {
self.contacts_enabled.set_value_and_mark_modified(enabled)
}
pub fn is_contacts_enabled(&self) -> bool {
*self.contacts_enabled
}
pub fn set_auto_rebinding(&mut self, enabled: bool) -> bool {
self.auto_rebind.set_value_and_mark_modified(enabled)
}
pub fn is_auto_rebinding_enabled(&self) -> bool {
*self.auto_rebind
}
pub fn set_motor_force_as_prismatic(
&mut self,
force: f32,
max_vel: f32,
damping: f32,
) -> Result<(), String> {
let JointParams::PrismaticJoint(_) = self.params() else {
return Err("Joint is not a PrismaticJoint".to_string());
};
let motor_params = JointMotorParams {
target_vel: max_vel,
target_pos: 0.0,
stiffness: 0.0,
damping,
max_force: force,
};
self.set_motor_params(motor_params);
Ok(())
}
pub fn set_motor_torque_as_revolute(
&mut self,
torque: f32,
max_angular_vel: f32,
damping: f32,
) -> Result<(), String> {
let JointParams::RevoluteJoint(_) = self.params() else {
return Err("Joint is not a RevoluteJoint".to_string());
};
let motor_params = JointMotorParams {
target_vel: max_angular_vel,
target_pos: 0.0,
stiffness: 0.0,
damping,
max_force: torque,
};
self.set_motor_params(motor_params);
Ok(())
}
pub fn set_motor_target_position_as_prismatic(
&mut self,
target_position: f32,
stiffness: f32,
max_force: f32,
damping: f32,
) -> Result<(), String> {
let JointParams::PrismaticJoint(_) = self.params() else {
return Err("Joint is not a PrismaticJoint".to_string());
};
let motor_params = JointMotorParams {
target_vel: 0.0,
target_pos: target_position,
stiffness,
damping,
max_force,
};
self.set_motor_params(motor_params);
Ok(())
}
pub fn set_motor_target_angle_as_revolute(
&mut self,
target_angle: f32,
stiffness: f32,
max_torque: f32,
damping: f32,
) -> Result<(), String> {
let JointParams::RevoluteJoint(_) = self.params() else {
return Err("Joint is not a RevoluteJoint".to_string());
};
let motor_params = JointMotorParams {
target_vel: 0.0,
target_pos: target_angle,
stiffness,
damping,
max_force: max_torque,
};
self.set_motor_params(motor_params);
Ok(())
}
pub fn set_motor_resistive_torque_as_ball(
&mut self,
stiffness: f32,
max_torque: f32,
damping: f32,
) -> Result<(), String> {
let JointParams::BallJoint(_) = self.params() else {
return Err("Joint is not a BallJoint".to_string());
};
let motor_params = JointMotorParams {
target_vel: 0.0,
target_pos: 0.0,
stiffness,
damping,
max_force: max_torque,
};
self.set_motor_params(motor_params);
Ok(())
}
pub fn disable_motor(&mut self) -> Result<(), String> {
if !matches!(
self.params(),
JointParams::RevoluteJoint(_)
| JointParams::PrismaticJoint(_)
| JointParams::BallJoint(_)
) {
return Err("Joint is not a RevoluteJoint, PrismaticJoint or BallJoint".to_string());
}
let motor_params = JointMotorParams {
target_vel: 0.0,
target_pos: 0.0,
stiffness: 0.0,
damping: 0.0,
max_force: 0.0,
};
self.set_motor_params(motor_params);
Ok(())
}
}
impl ConstructorProvider<Node, Graph> for Joint {
fn constructor() -> NodeConstructor {
NodeConstructor::new::<Self>()
.with_variant("Revolute Joint", |_| {
JointBuilder::new(BaseBuilder::new().with_name("Revolute Joint"))
.with_params(JointParams::RevoluteJoint(Default::default()))
.build_node()
.into()
})
.with_variant("Ball Joint", |_| {
JointBuilder::new(BaseBuilder::new().with_name("Ball Joint"))
.with_params(JointParams::BallJoint(Default::default()))
.build_node()
.into()
})
.with_variant("Prismatic Joint", |_| {
JointBuilder::new(BaseBuilder::new().with_name("Prismatic Joint"))
.with_params(JointParams::PrismaticJoint(Default::default()))
.build_node()
.into()
})
.with_variant("Fixed Joint", |_| {
JointBuilder::new(BaseBuilder::new().with_name("Fixed Joint"))
.with_params(JointParams::FixedJoint(Default::default()))
.build_node()
.into()
})
.with_group("Physics")
}
}
impl NodeTrait for Joint {
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_joint(self.native.get());
self.native.set(ImpulseJointHandle::invalid());
Log::info(format!(
"Native joint was removed for node: {}",
self.name()
));
}
fn sync_native(&self, self_handle: Handle<Node>, context: &mut SyncContext) {
context
.physics
.sync_to_joint_node(context.nodes, self_handle, self);
}
fn on_global_transform_changed(
&self,
new_global_transform: &Matrix4<f32>,
_context: &mut SyncContext,
) {
if *self.auto_rebind && !m4x4_approx_eq(new_global_transform, &self.global_transform()) {
self.local_frames.borrow_mut().take();
}
}
fn validate(&self, scene: &Scene) -> Result<(), String> {
if scene.graph.try_get(self.body1()).is_err() {
return Err("3D Joint has invalid or unassigned handle to a \
first body, the joint will not operate!"
.to_string());
}
if scene.graph.try_get(self.body2()).is_err() {
return Err("3D Joint has invalid or unassigned handle to a \
second body, the joint will not operate!"
.to_string());
}
Ok(())
}
}
pub struct JointBuilder {
base_builder: BaseBuilder,
params: JointParams,
motor_params: JointMotorParams,
body1: Handle<RigidBody>,
body2: Handle<RigidBody>,
contacts_enabled: bool,
auto_rebind: bool,
}
impl JointBuilder {
pub fn new(base_builder: BaseBuilder) -> Self {
Self {
base_builder,
params: Default::default(),
motor_params: Default::default(),
body1: Default::default(),
body2: Default::default(),
contacts_enabled: true,
auto_rebind: true,
}
}
pub fn with_params(mut self, params: JointParams) -> Self {
self.params = params;
self
}
pub fn with_motor_params(mut self, motor_params: JointMotorParams) -> Self {
self.motor_params = motor_params;
self
}
pub fn with_body1(mut self, body1: Handle<RigidBody>) -> Self {
self.body1 = body1;
self
}
pub fn with_body2(mut self, body2: Handle<RigidBody>) -> Self {
self.body2 = body2;
self
}
pub fn with_contacts_enabled(mut self, enabled: bool) -> Self {
self.contacts_enabled = enabled;
self
}
pub fn with_auto_rebinding_enabled(mut self, auto_rebind: bool) -> Self {
self.auto_rebind = auto_rebind;
self
}
pub fn build_joint(self) -> Joint {
Joint {
base: self.base_builder.build_base(),
params: self.params.into(),
motor_params: self.motor_params.into(),
body1: self.body1.into(),
body2: self.body2.into(),
contacts_enabled: self.contacts_enabled.into(),
auto_rebind: self.auto_rebind.into(),
local_frames: Default::default(),
native: Cell::new(ImpulseJointHandle::invalid()),
}
}
pub fn build_node(self) -> Node {
Node::new(self.build_joint())
}
pub fn build(self, graph: &mut Graph) -> Handle<Joint> {
graph.add_node(self.build_node()).to_variant()
}
}