1use crate::scene::node::constructor::NodeConstructor;
24use crate::{
25 core::{
26 algebra::Matrix4,
27 log::Log,
28 math::{aabb::AxisAlignedBoundingBox, m4x4_approx_eq},
29 pool::Handle,
30 reflect::prelude::*,
31 type_traits::prelude::*,
32 uuid::{uuid, Uuid},
33 variable::InheritableVariable,
34 visitor::prelude::*,
35 },
36 scene::{
37 base::{Base, BaseBuilder},
38 graph::Graph,
39 node::{Node, NodeTrait, SyncContext},
40 rigidbody::RigidBody,
41 Scene,
42 },
43};
44use fyrox_core::algebra::{Isometry3, Vector3};
45use fyrox_core::uuid_provider;
46use fyrox_graph::constructor::ConstructorProvider;
47use fyrox_graph::{BaseSceneGraph, SceneGraphNode};
48use rapier2d::na::UnitQuaternion;
49use rapier3d::dynamics::ImpulseJointHandle;
50use std::cell::RefCell;
51use std::{
52 cell::Cell,
53 ops::{Deref, DerefMut, Range},
54};
55use strum_macros::{AsRefStr, EnumString, VariantNames};
56
57#[derive(Clone, Debug, Visit, PartialEq, Reflect)]
61pub struct BallJoint {
62 #[reflect(description = "Whether X angular limits are enabled or not.")]
64 #[visit(optional)] pub x_limits_enabled: bool,
66
67 #[reflect(description = "Allowed angle range around local X axis of the joint (in radians).")]
69 #[visit(optional)] pub x_limits_angles: Range<f32>,
71
72 #[reflect(description = "Whether Y angular limits are enabled or not.")]
74 #[visit(optional)] pub y_limits_enabled: bool,
76
77 #[reflect(description = "Allowed angle range around local Y axis of the joint (in radians).")]
79 #[visit(optional)] pub y_limits_angles: Range<f32>,
81
82 #[reflect(description = "Whether Z angular limits are enabled or not.")]
84 #[visit(optional)] pub z_limits_enabled: bool,
86
87 #[reflect(description = "Allowed angle range around local Z axis of the joint (in radians).")]
89 #[visit(optional)] pub z_limits_angles: Range<f32>,
91}
92
93impl Default for BallJoint {
94 fn default() -> Self {
95 Self {
96 x_limits_enabled: false,
97 x_limits_angles: -std::f32::consts::PI..std::f32::consts::PI,
98 y_limits_enabled: false,
99 y_limits_angles: -std::f32::consts::PI..std::f32::consts::PI,
100 z_limits_enabled: false,
101 z_limits_angles: -std::f32::consts::PI..std::f32::consts::PI,
102 }
103 }
104}
105
106#[derive(Clone, Debug, Visit, PartialEq, Reflect, Default, Eq)]
109pub struct FixedJoint;
110
111#[derive(Clone, Debug, Visit, PartialEq, Reflect)]
114pub struct PrismaticJoint {
115 #[reflect(description = "Whether linear limits along local joint X axis are enabled or not.")]
117 #[visit(optional)] pub limits_enabled: bool,
119
120 #[reflect(
122 description = "The min an max relative position of the attached bodies along local X axis of the joint."
123 )]
124 #[visit(optional)] pub limits: Range<f32>,
126}
127
128impl Default for PrismaticJoint {
129 fn default() -> Self {
130 Self {
131 limits_enabled: false,
132 limits: -std::f32::consts::PI..std::f32::consts::PI,
133 }
134 }
135}
136
137#[derive(Clone, Debug, Visit, PartialEq, Reflect)]
141pub struct RevoluteJoint {
142 #[reflect(
144 description = "Whether angular limits around local X axis of the joint are enabled or not."
145 )]
146 #[visit(optional)] pub limits_enabled: bool,
148
149 #[reflect(description = "Allowed angle range around local X axis of the joint (in radians).")]
151 #[visit(optional)] pub limits: Range<f32>,
153}
154
155impl Default for RevoluteJoint {
156 fn default() -> Self {
157 Self {
158 limits_enabled: false,
159 limits: -std::f32::consts::PI..std::f32::consts::PI,
160 }
161 }
162}
163
164#[derive(Clone, Debug, PartialEq, Visit, Reflect, AsRefStr, EnumString, VariantNames)]
166pub enum JointParams {
167 BallJoint(BallJoint),
169 FixedJoint(FixedJoint),
171 PrismaticJoint(PrismaticJoint),
173 RevoluteJoint(RevoluteJoint),
175}
176
177uuid_provider!(JointParams = "a3e09303-9de4-4123-9492-05e27f29aaa3");
178
179impl Default for JointParams {
180 fn default() -> Self {
181 Self::BallJoint(Default::default())
182 }
183}
184
185#[derive(Visit, Reflect, Debug, Clone, Default)]
186pub(crate) struct LocalFrame {
187 pub position: Vector3<f32>,
188 pub rotation: UnitQuaternion<f32>,
189}
190
191impl LocalFrame {
192 pub fn new(isometry: &Isometry3<f32>) -> Self {
193 Self {
194 position: isometry.translation.vector,
195 rotation: isometry.rotation,
196 }
197 }
198}
199
200#[derive(Visit, Reflect, Debug, Clone, Default)]
201pub(crate) struct JointLocalFrames {
202 pub body1: LocalFrame,
203 pub body2: LocalFrame,
204}
205
206impl JointLocalFrames {
207 pub fn new(isometry1: &Isometry3<f32>, isometry2: &Isometry3<f32>) -> Self {
208 Self {
209 body1: LocalFrame::new(isometry1),
210 body2: LocalFrame::new(isometry2),
211 }
212 }
213}
214
215#[derive(Visit, Reflect, Debug, ComponentProvider)]
218pub struct Joint {
219 base: Base,
220
221 #[reflect(setter = "set_params")]
222 pub(crate) params: InheritableVariable<JointParams>,
223
224 #[reflect(setter = "set_body1")]
225 pub(crate) body1: InheritableVariable<Handle<Node>>,
226
227 #[reflect(setter = "set_body2")]
228 pub(crate) body2: InheritableVariable<Handle<Node>>,
229
230 #[reflect(setter = "set_contacts_enabled")]
231 #[visit(optional)] pub(crate) contacts_enabled: InheritableVariable<bool>,
233
234 #[reflect(setter = "set_auto_rebinding")]
235 #[visit(optional)] pub(crate) auto_rebind: InheritableVariable<bool>,
237
238 #[visit(optional)]
239 #[reflect(hidden)]
240 pub(crate) local_frames: RefCell<Option<JointLocalFrames>>,
241
242 #[visit(skip)]
243 #[reflect(hidden)]
244 pub(crate) native: Cell<ImpulseJointHandle>,
245}
246
247impl Default for Joint {
248 fn default() -> Self {
249 Self {
250 base: Default::default(),
251 params: Default::default(),
252 body1: Default::default(),
253 body2: Default::default(),
254 contacts_enabled: InheritableVariable::new_modified(true),
255 auto_rebind: true.into(),
256 local_frames: Default::default(),
257 native: Cell::new(ImpulseJointHandle::invalid()),
258 }
259 }
260}
261
262impl Deref for Joint {
263 type Target = Base;
264
265 fn deref(&self) -> &Self::Target {
266 &self.base
267 }
268}
269
270impl DerefMut for Joint {
271 fn deref_mut(&mut self) -> &mut Self::Target {
272 &mut self.base
273 }
274}
275
276impl Clone for Joint {
277 fn clone(&self) -> Self {
278 Self {
279 base: self.base.clone(),
280 params: self.params.clone(),
281 body1: self.body1.clone(),
282 body2: self.body2.clone(),
283 contacts_enabled: self.contacts_enabled.clone(),
284 local_frames: self.local_frames.clone(),
285 auto_rebind: self.auto_rebind.clone(),
287 native: Cell::new(ImpulseJointHandle::invalid()),
288 }
289 }
290}
291
292impl TypeUuidProvider for Joint {
293 fn type_uuid() -> Uuid {
294 uuid!("439d48f5-e3a3-4255-aa08-353c1ca42e3b")
295 }
296}
297
298impl Joint {
299 pub fn params(&self) -> &JointParams {
301 &self.params
302 }
303
304 pub fn params_mut(&mut self) -> &mut JointParams {
307 self.params.get_value_mut_and_mark_modified()
308 }
309
310 pub fn set_params(&mut self, params: JointParams) -> JointParams {
312 self.params.set_value_and_mark_modified(params)
313 }
314
315 pub fn set_body1(&mut self, handle: Handle<Node>) -> Handle<Node> {
318 self.body1.set_value_and_mark_modified(handle)
319 }
320
321 pub fn body1(&self) -> Handle<Node> {
323 *self.body1
324 }
325
326 pub fn set_body2(&mut self, handle: Handle<Node>) -> Handle<Node> {
329 self.body2.set_value_and_mark_modified(handle)
330 }
331
332 pub fn body2(&self) -> Handle<Node> {
334 *self.body2
335 }
336
337 pub fn set_contacts_enabled(&mut self, enabled: bool) -> bool {
339 self.contacts_enabled.set_value_and_mark_modified(enabled)
340 }
341
342 pub fn is_contacts_enabled(&self) -> bool {
344 *self.contacts_enabled
345 }
346
347 pub fn set_auto_rebinding(&mut self, enabled: bool) -> bool {
350 self.auto_rebind.set_value_and_mark_modified(enabled)
351 }
352
353 pub fn is_auto_rebinding_enabled(&self) -> bool {
355 *self.auto_rebind
356 }
357}
358
359impl ConstructorProvider<Node, Graph> for Joint {
360 fn constructor() -> NodeConstructor {
361 NodeConstructor::new::<Self>()
362 .with_variant("Revolute Joint", |_| {
363 JointBuilder::new(BaseBuilder::new().with_name("Revolute Joint"))
364 .with_params(JointParams::RevoluteJoint(Default::default()))
365 .build_node()
366 .into()
367 })
368 .with_variant("Ball Joint", |_| {
369 JointBuilder::new(BaseBuilder::new().with_name("Ball Joint"))
370 .with_params(JointParams::BallJoint(Default::default()))
371 .build_node()
372 .into()
373 })
374 .with_variant("Prismatic Joint", |_| {
375 JointBuilder::new(BaseBuilder::new().with_name("Prismatic Joint"))
376 .with_params(JointParams::PrismaticJoint(Default::default()))
377 .build_node()
378 .into()
379 })
380 .with_variant("Fixed Joint", |_| {
381 JointBuilder::new(BaseBuilder::new().with_name("Fixed Joint"))
382 .with_params(JointParams::FixedJoint(Default::default()))
383 .build_node()
384 .into()
385 })
386 .with_group("Physics")
387 }
388}
389
390impl NodeTrait for Joint {
391 fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
392 self.base.local_bounding_box()
393 }
394
395 fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
396 self.base.world_bounding_box()
397 }
398
399 fn id(&self) -> Uuid {
400 Self::type_uuid()
401 }
402
403 fn on_removed_from_graph(&mut self, graph: &mut Graph) {
404 graph.physics.remove_joint(self.native.get());
405 self.native.set(ImpulseJointHandle::invalid());
406
407 Log::info(format!(
408 "Native joint was removed for node: {}",
409 self.name()
410 ));
411 }
412
413 fn sync_native(&self, self_handle: Handle<Node>, context: &mut SyncContext) {
414 context
415 .physics
416 .sync_to_joint_node(context.nodes, self_handle, self);
417 }
418
419 fn on_global_transform_changed(
420 &self,
421 new_global_transform: &Matrix4<f32>,
422 _context: &mut SyncContext,
423 ) {
424 if *self.auto_rebind && !m4x4_approx_eq(new_global_transform, &self.global_transform()) {
425 self.local_frames.borrow_mut().take();
426 }
427 }
428
429 fn validate(&self, scene: &Scene) -> Result<(), String> {
430 if let Some(body1) = scene.graph.try_get(self.body1()) {
431 if body1.component_ref::<RigidBody>().is_none() {
432 return Err("First body of 3D Joint must be an \
433 instance of 3D Rigid Body!"
434 .to_string());
435 }
436 } else {
437 return Err("3D Joint has invalid or unassigned handle to a \
438 first body, the joint will not operate!"
439 .to_string());
440 }
441
442 if let Some(body2) = scene.graph.try_get(self.body2()) {
443 if body2.component_ref::<RigidBody>().is_none() {
444 return Err("Second body of 3D Joint must be an instance \
445 of 3D Rigid Body!"
446 .to_string());
447 }
448 } else {
449 return Err("3D Joint has invalid or unassigned handle to a \
450 second body, the joint will not operate!"
451 .to_string());
452 }
453
454 Ok(())
455 }
456}
457
458pub struct JointBuilder {
460 base_builder: BaseBuilder,
461 params: JointParams,
462 body1: Handle<Node>,
463 body2: Handle<Node>,
464 contacts_enabled: bool,
465 auto_rebind: bool,
466}
467
468impl JointBuilder {
469 pub fn new(base_builder: BaseBuilder) -> Self {
471 Self {
472 base_builder,
473 params: Default::default(),
474 body1: Default::default(),
475 body2: Default::default(),
476 contacts_enabled: true,
477 auto_rebind: true,
478 }
479 }
480
481 pub fn with_params(mut self, params: JointParams) -> Self {
483 self.params = params;
484 self
485 }
486
487 pub fn with_body1(mut self, body1: Handle<Node>) -> Self {
490 self.body1 = body1;
491 self
492 }
493
494 pub fn with_body2(mut self, body2: Handle<Node>) -> Self {
497 self.body2 = body2;
498 self
499 }
500
501 pub fn with_contacts_enabled(mut self, enabled: bool) -> Self {
503 self.contacts_enabled = enabled;
504 self
505 }
506
507 pub fn with_auto_rebinding_enabled(mut self, auto_rebind: bool) -> Self {
510 self.auto_rebind = auto_rebind;
511 self
512 }
513
514 pub fn build_joint(self) -> Joint {
516 Joint {
517 base: self.base_builder.build_base(),
518 params: self.params.into(),
519 body1: self.body1.into(),
520 body2: self.body2.into(),
521 contacts_enabled: self.contacts_enabled.into(),
522 auto_rebind: self.auto_rebind.into(),
523 local_frames: Default::default(),
524 native: Cell::new(ImpulseJointHandle::invalid()),
525 }
526 }
527
528 pub fn build_node(self) -> Node {
530 Node::new(self.build_joint())
531 }
532
533 pub fn build(self, graph: &mut Graph) -> Handle<Node> {
535 graph.add_node(self.build_node())
536 }
537}