fyrox_impl/scene/dim2/
joint.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Joint is used to restrict motion of two rigid bodies.
22
23use crate::scene::node::constructor::NodeConstructor;
24use crate::{
25    core::{
26        algebra::{Isometry2, Matrix4, UnitComplex, Vector2},
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        dim2::rigidbody::RigidBody,
39        graph::Graph,
40        node::{Node, NodeTrait, SyncContext},
41        Scene,
42    },
43};
44use fyrox_core::uuid_provider;
45use fyrox_graph::constructor::ConstructorProvider;
46use fyrox_graph::{BaseSceneGraph, SceneGraphNode};
47use rapier2d::dynamics::ImpulseJointHandle;
48use std::{
49    cell::{Cell, RefCell},
50    ops::{Deref, DerefMut, Range},
51};
52use strum_macros::{AsRefStr, EnumString, VariantNames};
53
54/// Ball joint locks any translational moves between two objects on the axis between objects, but
55/// allows rigid bodies to perform relative rotations. The real world example is a human shoulder,
56/// pendulum, etc.
57#[derive(Clone, Debug, Visit, PartialEq, Reflect)]
58pub struct BallJoint {
59    /// Whether angular limits are enabled or not. Default is `false`
60    #[reflect(description = "Whether angular limits are enabled or not.")]
61    #[visit(optional)] // Backward compatibility
62    pub limits_enabled: bool,
63
64    /// Allowed angles range for the joint (in radians).
65    #[reflect(description = "Allowed angles range for the joint (in radians).")]
66    #[visit(optional)] // Backward compatibility
67    pub limits_angles: Range<f32>,
68}
69
70impl Default for BallJoint {
71    fn default() -> Self {
72        Self {
73            limits_enabled: false,
74            limits_angles: -std::f32::consts::PI..std::f32::consts::PI,
75        }
76    }
77}
78
79/// A fixed joint ensures that two rigid bodies does not move relative to each other. There is no
80/// straightforward real-world example, but it can be thought as two bodies were "welded" together.
81#[derive(Clone, Debug, Default, Visit, PartialEq, Reflect, Eq)]
82pub struct FixedJoint;
83
84/// Prismatic joint prevents any relative movement between two rigid-bodies, except for relative
85/// translations along one axis. The real world example is a sliders that used to support drawers.
86#[derive(Clone, Debug, Visit, PartialEq, Reflect)]
87pub struct PrismaticJoint {
88    /// Whether linear limits along local X axis of the joint are enabled or not. Default is `false`
89    #[reflect(
90        description = "Whether linear limits along local X axis of the joint are enabled or not."
91    )]
92    #[visit(optional)] // Backward compatibility
93    pub limits_enabled: bool,
94
95    /// Allowed linear distance range along local X axis of the joint.
96    #[reflect(description = "Allowed linear distance range along local X axis of the joint.")]
97    #[visit(optional)] // Backward compatibility
98    pub limits: Range<f32>,
99}
100
101impl Default for PrismaticJoint {
102    fn default() -> Self {
103        Self {
104            limits_enabled: false,
105            limits: -std::f32::consts::PI..std::f32::consts::PI,
106        }
107    }
108}
109
110/// The exact kind of the joint.
111#[derive(Clone, Debug, PartialEq, Visit, Reflect, AsRefStr, EnumString, VariantNames)]
112pub enum JointParams {
113    /// See [`BallJoint`] for more info.
114    BallJoint(BallJoint),
115    /// See [`FixedJoint`] for more info.
116    FixedJoint(FixedJoint),
117    /// See [`PrismaticJoint`] for more info.
118    PrismaticJoint(PrismaticJoint),
119}
120
121uuid_provider!(JointParams = "e1fa2015-3ea3-47bb-8ad3-d408559c9643");
122
123impl Default for JointParams {
124    fn default() -> Self {
125        Self::BallJoint(Default::default())
126    }
127}
128
129#[derive(Visit, Reflect, Debug, Clone, Default)]
130pub(crate) struct LocalFrame {
131    pub position: Vector2<f32>,
132    pub rotation: UnitComplex<f32>,
133}
134
135impl LocalFrame {
136    pub fn new(isometry: &Isometry2<f32>) -> Self {
137        Self {
138            position: isometry.translation.vector,
139            rotation: isometry.rotation,
140        }
141    }
142}
143
144#[derive(Visit, Reflect, Debug, Clone, Default)]
145pub(crate) struct JointLocalFrames {
146    pub body1: LocalFrame,
147    pub body2: LocalFrame,
148}
149
150impl JointLocalFrames {
151    pub fn new(isometry1: &Isometry2<f32>, isometry2: &Isometry2<f32>) -> Self {
152        Self {
153            body1: LocalFrame::new(isometry1),
154            body2: LocalFrame::new(isometry2),
155        }
156    }
157}
158
159/// Joint is used to restrict motion of two rigid bodies. There are numerous examples of joints in
160/// real life: door hinge, ball joints in human arms, etc.
161#[derive(Visit, Reflect, Debug, ComponentProvider)]
162pub struct Joint {
163    base: Base,
164
165    #[reflect(setter = "set_params")]
166    pub(crate) params: InheritableVariable<JointParams>,
167
168    #[reflect(setter = "set_body1")]
169    pub(crate) body1: InheritableVariable<Handle<Node>>,
170
171    #[reflect(setter = "set_body2")]
172    pub(crate) body2: InheritableVariable<Handle<Node>>,
173
174    #[visit(optional)] // Backward compatibility
175    #[reflect(setter = "set_contacts_enabled")]
176    pub(crate) contacts_enabled: InheritableVariable<bool>,
177
178    #[visit(optional)]
179    #[reflect(hidden)]
180    pub(crate) local_frames: RefCell<Option<JointLocalFrames>>,
181
182    #[visit(skip)]
183    #[reflect(hidden)]
184    pub(crate) native: Cell<ImpulseJointHandle>,
185}
186
187impl Default for Joint {
188    fn default() -> Self {
189        Self {
190            base: Default::default(),
191            params: Default::default(),
192            body1: Default::default(),
193            body2: Default::default(),
194            local_frames: Default::default(),
195            contacts_enabled: InheritableVariable::new_modified(true),
196            // Do not copy. The copy will have its own native representation.
197            native: Cell::new(ImpulseJointHandle::invalid()),
198        }
199    }
200}
201
202impl Deref for Joint {
203    type Target = Base;
204
205    fn deref(&self) -> &Self::Target {
206        &self.base
207    }
208}
209
210impl DerefMut for Joint {
211    fn deref_mut(&mut self) -> &mut Self::Target {
212        &mut self.base
213    }
214}
215
216impl Clone for Joint {
217    fn clone(&self) -> Self {
218        Self {
219            base: self.base.clone(),
220            params: self.params.clone(),
221            body1: self.body1.clone(),
222            body2: self.body2.clone(),
223            local_frames: self.local_frames.clone(),
224            contacts_enabled: self.contacts_enabled.clone(),
225            native: Cell::new(ImpulseJointHandle::invalid()),
226        }
227    }
228}
229
230impl TypeUuidProvider for Joint {
231    fn type_uuid() -> Uuid {
232        uuid!("b8d66eda-b69f-4c57-80ba-d76665573565")
233    }
234}
235
236impl Joint {
237    /// Sets new parameters of the joint.
238    pub fn set_params(&mut self, params: JointParams) -> JointParams {
239        self.params.set_value_and_mark_modified(params)
240    }
241
242    /// Returns a shared reference to the current joint parameters.
243    pub fn params(&self) -> &JointParams {
244        &self.params
245    }
246
247    /// Returns a mutable reference to the current joint parameters. Obtaining the mutable reference
248    /// will force the engine to do additional calculations to reflect changes to the physics engine.
249    pub fn params_mut(&mut self) -> &mut JointParams {
250        self.params.get_value_mut_and_mark_modified()
251    }
252
253    /// Sets the first body of the joint. The handle should point to the RigidBody node, otherwise
254    /// the joint will have no effect!
255    pub fn set_body1(&mut self, handle: Handle<Node>) -> Handle<Node> {
256        self.body1.set_value_and_mark_modified(handle)
257    }
258
259    /// Returns current first body of the joint.
260    pub fn body1(&self) -> Handle<Node> {
261        *self.body1
262    }
263
264    /// Sets the second body of the joint. The handle should point to the RigidBody node, otherwise
265    /// the joint will have no effect!
266    pub fn set_body2(&mut self, handle: Handle<Node>) -> Handle<Node> {
267        self.body2.set_value_and_mark_modified(handle)
268    }
269
270    /// Returns current second body of the joint.
271    pub fn body2(&self) -> Handle<Node> {
272        *self.body2
273    }
274
275    /// Sets whether the connected bodies should ignore collisions with each other or not.  
276    pub fn set_contacts_enabled(&mut self, enabled: bool) -> bool {
277        self.contacts_enabled.set_value_and_mark_modified(enabled)
278    }
279
280    /// Returns true if contacts between connected bodies is enabled, false - otherwise.
281    pub fn is_contacts_enabled(&self) -> bool {
282        *self.contacts_enabled
283    }
284}
285
286impl ConstructorProvider<Node, Graph> for Joint {
287    fn constructor() -> NodeConstructor {
288        NodeConstructor::new::<Self>()
289            .with_variant("Ball Joint 2D", |_| {
290                JointBuilder::new(BaseBuilder::new().with_name("Ball Joint 2D"))
291                    .with_params(JointParams::BallJoint(Default::default()))
292                    .build_node()
293                    .into()
294            })
295            .with_variant("Prismatic Joint 2D", |_| {
296                JointBuilder::new(BaseBuilder::new().with_name("Prismatic Joint 2D"))
297                    .with_params(JointParams::PrismaticJoint(Default::default()))
298                    .build_node()
299                    .into()
300            })
301            .with_variant("Fixed Joint 2D", |_| {
302                JointBuilder::new(BaseBuilder::new().with_name("Fixed Joint 2D"))
303                    .with_params(JointParams::FixedJoint(Default::default()))
304                    .build_node()
305                    .into()
306            })
307            .with_group("Physics 2D")
308    }
309}
310
311impl NodeTrait for Joint {
312    fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
313        self.base.local_bounding_box()
314    }
315
316    fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
317        self.base.world_bounding_box()
318    }
319
320    fn id(&self) -> Uuid {
321        Self::type_uuid()
322    }
323
324    fn on_removed_from_graph(&mut self, graph: &mut Graph) {
325        graph.physics2d.remove_joint(self.native.get());
326        self.native.set(ImpulseJointHandle::invalid());
327
328        Log::info(format!(
329            "Native joint 2D was removed for node: {}",
330            self.name()
331        ));
332    }
333
334    fn sync_native(&self, self_handle: Handle<Node>, context: &mut SyncContext) {
335        context
336            .physics2d
337            .sync_to_joint_node(context.nodes, self_handle, self);
338    }
339
340    fn on_global_transform_changed(
341        &self,
342        new_global_transform: &Matrix4<f32>,
343        _context: &mut SyncContext,
344    ) {
345        if !m4x4_approx_eq(new_global_transform, &self.global_transform()) {
346            self.local_frames.borrow_mut().take();
347        }
348    }
349
350    fn validate(&self, scene: &Scene) -> Result<(), String> {
351        if let Some(body1) = scene.graph.try_get(self.body1()) {
352            if body1.component_ref::<RigidBody>().is_none() {
353                return Err("First body of 2D Joint must be an \
354                    instance of 2D Rigid Body!"
355                    .to_string());
356            }
357        } else {
358            return Err("2D Joint has invalid or unassigned handle to a \
359            first body, the joint will not operate!"
360                .to_string());
361        }
362
363        if let Some(body2) = scene.graph.try_get(self.body2()) {
364            if body2.component_ref::<RigidBody>().is_none() {
365                return Err("Second body of 2D Joint must be an instance \
366                    of 2D Rigid Body!"
367                    .to_string());
368            }
369        } else {
370            return Err("2D Joint has invalid or unassigned handle to a \
371            second body, the joint will not operate!"
372                .to_string());
373        }
374
375        Ok(())
376    }
377}
378
379/// Joint builder allows you to build Joint node in a declarative manner.
380pub struct JointBuilder {
381    base_builder: BaseBuilder,
382    params: JointParams,
383    body1: Handle<Node>,
384    body2: Handle<Node>,
385    contacts_enabled: bool,
386}
387
388impl JointBuilder {
389    /// Creates a new joint builder instance.
390    pub fn new(base_builder: BaseBuilder) -> Self {
391        Self {
392            base_builder,
393            params: Default::default(),
394            body1: Default::default(),
395            body2: Default::default(),
396            contacts_enabled: true,
397        }
398    }
399
400    /// Sets desired joint parameters which defines exact type of the joint.
401    pub fn with_params(mut self, params: JointParams) -> Self {
402        self.params = params;
403        self
404    }
405
406    /// Sets desired first body of the joint. This handle should be a handle to rigid body node,
407    /// otherwise joint will have no effect!
408    pub fn with_body1(mut self, body1: Handle<Node>) -> Self {
409        self.body1 = body1;
410        self
411    }
412
413    /// Sets desired second body of the joint. This handle should be a handle to rigid body node,
414    /// otherwise joint will have no effect!
415    pub fn with_body2(mut self, body2: Handle<Node>) -> Self {
416        self.body2 = body2;
417        self
418    }
419
420    /// Sets whether the connected bodies should ignore collisions with each other or not.  
421    pub fn with_contacts_enabled(mut self, enabled: bool) -> Self {
422        self.contacts_enabled = enabled;
423        self
424    }
425
426    /// Creates new Joint node, but does not add it to the graph.
427    pub fn build_joint(self) -> Joint {
428        Joint {
429            base: self.base_builder.build_base(),
430            params: self.params.into(),
431            body1: self.body1.into(),
432            body2: self.body2.into(),
433            local_frames: Default::default(),
434            contacts_enabled: self.contacts_enabled.into(),
435            native: Cell::new(ImpulseJointHandle::invalid()),
436        }
437    }
438
439    /// Creates new Joint node, but does not add it to the graph.
440    pub fn build_node(self) -> Node {
441        Node::new(self.build_joint())
442    }
443
444    /// Creates new Joint node and adds it to the graph.
445    pub fn build(self, graph: &mut Graph) -> Handle<Node> {
446        graph.add_node(self.build_node())
447    }
448}