use crate::{
core::{
algebra::Matrix4,
log::Log,
math::{aabb::AxisAlignedBoundingBox, m4x4_approx_eq},
pool::Handle,
reflect::prelude::*,
uuid::{uuid, Uuid},
variable::InheritableVariable,
visitor::prelude::*,
TypeUuidProvider,
},
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::BaseSceneGraph;
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 {
#[reflect(description = "Whether X angular limits are enabled or not.")]
#[visit(optional)] pub x_limits_enabled: bool,
#[reflect(description = "Allowed angle range around local X axis of the joint (in radians).")]
#[visit(optional)] pub x_limits_angles: Range<f32>,
#[reflect(description = "Whether Y angular limits are enabled or not.")]
#[visit(optional)] pub y_limits_enabled: bool,
#[reflect(description = "Allowed angle range around local Y axis of the joint (in radians).")]
#[visit(optional)] pub y_limits_angles: Range<f32>,
#[reflect(description = "Whether Z angular limits are enabled or not.")]
#[visit(optional)] pub z_limits_enabled: bool,
#[reflect(description = "Allowed angle range around local Z axis of the joint (in radians).")]
#[visit(optional)] 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 {
#[reflect(description = "Whether linear limits along local joint X axis are enabled or not.")]
#[visit(optional)] pub limits_enabled: bool,
#[reflect(
description = "The min an max relative position of the attached bodies along local X axis of the joint."
)]
#[visit(optional)] 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 {
#[reflect(
description = "Whether angular limits around local X axis of the joint are enabled or not."
)]
#[visit(optional)] pub limits_enabled: bool,
#[reflect(description = "Allowed angle range around local X axis of the joint (in radians).")]
#[visit(optional)] 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(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)]
pub struct Joint {
base: Base,
#[reflect(setter = "set_params")]
pub(crate) params: InheritableVariable<JointParams>,
#[reflect(setter = "set_body1")]
pub(crate) body1: InheritableVariable<Handle<Node>>,
#[reflect(setter = "set_body2")]
pub(crate) body2: InheritableVariable<Handle<Node>>,
#[reflect(setter = "set_contacts_enabled")]
#[visit(optional)] pub(crate) contacts_enabled: InheritableVariable<bool>,
#[reflect(setter = "set_auto_rebinding")]
#[visit(optional)] 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(),
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(),
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 set_body1(&mut self, handle: Handle<Node>) -> Handle<Node> {
self.body1.set_value_and_mark_modified(handle)
}
pub fn body1(&self) -> Handle<Node> {
*self.body1
}
pub fn set_body2(&mut self, handle: Handle<Node>) -> Handle<Node> {
self.body2.set_value_and_mark_modified(handle)
}
pub fn body2(&self) -> Handle<Node> {
*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
}
}
impl NodeTrait for Joint {
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.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 sync_transform(&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 let Some(body1) = scene.graph.try_get(self.body1()) {
if body1.query_component_ref::<RigidBody>().is_none() {
return Err("First body of 3D Joint must be an \
instance of 3D Rigid Body!"
.to_string());
}
} else {
return Err("3D Joint has invalid or unassigned handle to a \
first body, the joint will not operate!"
.to_string());
}
if let Some(body2) = scene.graph.try_get(self.body2()) {
if body2.query_component_ref::<RigidBody>().is_none() {
return Err("Second body of 3D Joint must be an instance \
of 3D Rigid Body!"
.to_string());
}
} else {
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,
body1: Handle<Node>,
body2: Handle<Node>,
contacts_enabled: bool,
auto_rebind: bool,
}
impl JointBuilder {
pub fn new(base_builder: BaseBuilder) -> Self {
Self {
base_builder,
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_body1(mut self, body1: Handle<Node>) -> Self {
self.body1 = body1;
self
}
pub fn with_body2(mut self, body2: Handle<Node>) -> 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(),
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<Node> {
graph.add_node(self.build_node())
}
}