1use crate::scene::node::constructor::NodeConstructor;
32use crate::{
33 core::{
34 algebra::{Isometry3, Matrix4, UnitQuaternion, Vector3},
35 log::Log,
36 math::{aabb::AxisAlignedBoundingBox, m4x4_approx_eq},
37 parking_lot::Mutex,
38 pool::Handle,
39 reflect::prelude::*,
40 type_traits::prelude::*,
41 uuid::{uuid, Uuid},
42 variable::InheritableVariable,
43 visitor::prelude::*,
44 },
45 scene::{
46 base::{Base, BaseBuilder},
47 collider::Collider,
48 graph::Graph,
49 node::{Node, NodeTrait, SyncContext, UpdateContext},
50 Scene,
51 },
52};
53use fyrox_core::uuid_provider;
54use fyrox_graph::constructor::ConstructorProvider;
55use fyrox_graph::{BaseSceneGraph, SceneGraph};
56use rapier3d::{dynamics, prelude::RigidBodyHandle};
57use std::{
58 cell::Cell,
59 collections::VecDeque,
60 fmt::{Debug, Formatter},
61 ops::{Deref, DerefMut},
62};
63use strum_macros::{AsRefStr, EnumString, VariantNames};
64
65#[derive(
67 Copy, Clone, Debug, Reflect, Visit, PartialEq, Eq, Hash, AsRefStr, EnumString, VariantNames,
68)]
69#[repr(u32)]
70pub enum RigidBodyType {
71 Dynamic = 0,
73 Static = 1,
75 KinematicPositionBased = 2,
78 KinematicVelocityBased = 3,
81}
82
83uuid_provider!(RigidBodyType = "562d2907-1b41-483a-8ca2-12eebaff7f5d");
84
85impl Default for RigidBodyType {
86 fn default() -> Self {
87 Self::Dynamic
88 }
89}
90
91impl From<dynamics::RigidBodyType> for RigidBodyType {
92 fn from(s: dynamics::RigidBodyType) -> Self {
93 match s {
94 dynamics::RigidBodyType::Dynamic => Self::Dynamic,
95 dynamics::RigidBodyType::Fixed => Self::Static,
96 dynamics::RigidBodyType::KinematicPositionBased => Self::KinematicPositionBased,
97 dynamics::RigidBodyType::KinematicVelocityBased => Self::KinematicVelocityBased,
98 }
99 }
100}
101
102impl From<RigidBodyType> for rapier3d::dynamics::RigidBodyType {
103 fn from(v: RigidBodyType) -> Self {
104 match v {
105 RigidBodyType::Dynamic => rapier3d::dynamics::RigidBodyType::Dynamic,
106 RigidBodyType::Static => rapier3d::dynamics::RigidBodyType::Fixed,
107 RigidBodyType::KinematicPositionBased => {
108 rapier3d::dynamics::RigidBodyType::KinematicPositionBased
109 }
110 RigidBodyType::KinematicVelocityBased => {
111 rapier3d::dynamics::RigidBodyType::KinematicVelocityBased
112 }
113 }
114 }
115}
116
117impl From<RigidBodyType> for rapier2d::dynamics::RigidBodyType {
118 fn from(v: RigidBodyType) -> Self {
119 match v {
120 RigidBodyType::Dynamic => rapier2d::dynamics::RigidBodyType::Dynamic,
121 RigidBodyType::Static => rapier2d::dynamics::RigidBodyType::Fixed,
122 RigidBodyType::KinematicPositionBased => {
123 rapier2d::dynamics::RigidBodyType::KinematicPositionBased
124 }
125 RigidBodyType::KinematicVelocityBased => {
126 rapier2d::dynamics::RigidBodyType::KinematicVelocityBased
127 }
128 }
129 }
130}
131
132#[derive(Debug)]
133pub(crate) enum ApplyAction {
134 Force(Vector3<f32>),
135 Torque(Vector3<f32>),
136 ForceAtPoint {
137 force: Vector3<f32>,
138 point: Vector3<f32>,
139 },
140 Impulse(Vector3<f32>),
141 TorqueImpulse(Vector3<f32>),
142 ImpulseAtPoint {
143 impulse: Vector3<f32>,
144 point: Vector3<f32>,
145 },
146 WakeUp,
147 NextTranslation(Vector3<f32>),
148 NextRotation(UnitQuaternion<f32>),
149 NextPosition(Isometry3<f32>),
150}
151
152#[derive(Copy, Clone, Debug, Reflect, Visit, PartialEq, AsRefStr, EnumString, VariantNames)]
153pub enum RigidBodyMassPropertiesType {
155 Default,
157 Additional {
159 center_of_mass: Vector3<f32>,
161 principal_inertia: Vector3<f32>,
163 },
164}
165uuid_provider!(RigidBodyMassPropertiesType = "663b6a92-9c0f-4f47-b66a-6b4293312a5d");
166
167#[derive(Visit, Reflect, ComponentProvider)]
175#[reflect(derived_type = "Node")]
176pub struct RigidBody {
177 base: Base,
178
179 #[reflect(setter = "set_lin_vel")]
180 pub(crate) lin_vel: InheritableVariable<Vector3<f32>>,
181
182 #[reflect(setter = "set_ang_vel")]
183 pub(crate) ang_vel: InheritableVariable<Vector3<f32>>,
184
185 #[reflect(setter = "set_lin_damping")]
186 pub(crate) lin_damping: InheritableVariable<f32>,
187
188 #[reflect(setter = "set_ang_damping")]
189 pub(crate) ang_damping: InheritableVariable<f32>,
190
191 #[reflect(setter = "set_body_type")]
192 pub(crate) body_type: InheritableVariable<RigidBodyType>,
193
194 #[reflect(min_value = 0.0, step = 0.05)]
195 #[reflect(setter = "set_mass")]
196 pub(crate) mass: InheritableVariable<f32>,
197
198 #[reflect(setter = "lock_x_rotations")]
199 pub(crate) x_rotation_locked: InheritableVariable<bool>,
200
201 #[reflect(setter = "lock_y_rotations")]
202 pub(crate) y_rotation_locked: InheritableVariable<bool>,
203
204 #[reflect(setter = "lock_z_rotations")]
205 pub(crate) z_rotation_locked: InheritableVariable<bool>,
206
207 #[reflect(setter = "lock_translation")]
208 pub(crate) translation_locked: InheritableVariable<bool>,
209
210 #[reflect(setter = "enable_ccd")]
211 pub(crate) ccd_enabled: InheritableVariable<bool>,
212
213 #[reflect(setter = "set_can_sleep")]
214 pub(crate) can_sleep: InheritableVariable<bool>,
215
216 #[reflect(setter = "set_dominance")]
217 pub(crate) dominance: InheritableVariable<i8>,
218
219 #[reflect(setter = "set_gravity_scale")]
220 pub(crate) gravity_scale: InheritableVariable<f32>,
221
222 #[visit(optional)]
223 #[reflect(setter = "set_mass_properties_type")]
224 pub(crate) mass_properties_type: InheritableVariable<RigidBodyMassPropertiesType>,
225
226 #[visit(skip)]
227 #[reflect(hidden)]
228 pub(crate) sleeping: bool,
229 #[visit(skip)]
230 #[reflect(hidden)]
231 pub(crate) reset_forces: Cell<bool>,
232 #[visit(skip)]
233 #[reflect(hidden)]
234 pub(crate) native: Cell<RigidBodyHandle>,
235 #[visit(skip)]
236 #[reflect(hidden)]
237 pub(crate) actions: Mutex<VecDeque<ApplyAction>>,
238}
239
240impl Debug for RigidBody {
241 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
242 write!(f, "RigidBody")
243 }
244}
245
246impl Default for RigidBody {
247 fn default() -> Self {
248 Self {
249 base: Default::default(),
250 lin_vel: Default::default(),
251 ang_vel: Default::default(),
252 lin_damping: Default::default(),
253 ang_damping: Default::default(),
254 sleeping: Default::default(),
255 body_type: InheritableVariable::new_modified(RigidBodyType::Dynamic),
256 mass: InheritableVariable::new_modified(1.0),
257 x_rotation_locked: Default::default(),
258 y_rotation_locked: Default::default(),
259 z_rotation_locked: Default::default(),
260 translation_locked: Default::default(),
261 ccd_enabled: Default::default(),
262 can_sleep: InheritableVariable::new_modified(true),
263 dominance: Default::default(),
264 gravity_scale: InheritableVariable::new_modified(1.0),
265 native: Cell::new(RigidBodyHandle::invalid()),
266 actions: Default::default(),
267 reset_forces: Default::default(),
268 mass_properties_type: InheritableVariable::new_modified(
269 RigidBodyMassPropertiesType::Default,
270 ),
271 }
272 }
273}
274
275impl Deref for RigidBody {
276 type Target = Base;
277
278 fn deref(&self) -> &Self::Target {
279 &self.base
280 }
281}
282
283impl DerefMut for RigidBody {
284 fn deref_mut(&mut self) -> &mut Self::Target {
285 &mut self.base
286 }
287}
288
289impl Clone for RigidBody {
290 fn clone(&self) -> Self {
291 Self {
292 base: self.base.clone(),
293 lin_vel: self.lin_vel.clone(),
294 ang_vel: self.ang_vel.clone(),
295 lin_damping: self.lin_damping.clone(),
296 ang_damping: self.ang_damping.clone(),
297 sleeping: self.sleeping,
298 body_type: self.body_type.clone(),
299 mass: self.mass.clone(),
300 x_rotation_locked: self.x_rotation_locked.clone(),
301 y_rotation_locked: self.y_rotation_locked.clone(),
302 z_rotation_locked: self.z_rotation_locked.clone(),
303 translation_locked: self.translation_locked.clone(),
304 ccd_enabled: self.ccd_enabled.clone(),
305 can_sleep: self.can_sleep.clone(),
306 dominance: self.dominance.clone(),
307 gravity_scale: self.gravity_scale.clone(),
308 native: Cell::new(RigidBodyHandle::invalid()),
310 actions: Default::default(),
311 reset_forces: self.reset_forces.clone(),
312 mass_properties_type: self.mass_properties_type.clone(),
313 }
314 }
315}
316
317impl TypeUuidProvider for RigidBody {
318 fn type_uuid() -> Uuid {
319 uuid!("4be15a7c-3566-49c4-bba8-2f4ccc57ffed")
320 }
321}
322
323impl RigidBody {
324 pub fn set_lin_vel(&mut self, lin_vel: Vector3<f32>) -> Vector3<f32> {
327 self.lin_vel.set_value_and_mark_modified(lin_vel)
328 }
329
330 pub fn lin_vel(&self) -> Vector3<f32> {
332 *self.lin_vel
333 }
334
335 pub fn set_ang_vel(&mut self, ang_vel: Vector3<f32>) -> Vector3<f32> {
338 self.ang_vel.set_value_and_mark_modified(ang_vel)
339 }
340
341 pub fn ang_vel(&self) -> Vector3<f32> {
343 *self.ang_vel
344 }
345
346 pub fn set_mass(&mut self, mass: f32) -> f32 {
349 self.mass.set_value_and_mark_modified(mass)
350 }
351
352 pub fn mass(&self) -> f32 {
354 *self.mass
355 }
356
357 pub fn set_ang_damping(&mut self, damping: f32) -> f32 {
360 self.ang_damping.set_value_and_mark_modified(damping)
361 }
362
363 pub fn ang_damping(&self) -> f32 {
365 *self.ang_damping
366 }
367
368 pub fn set_lin_damping(&mut self, damping: f32) -> f32 {
371 self.lin_damping.set_value_and_mark_modified(damping)
372 }
373
374 pub fn lin_damping(&self) -> f32 {
376 *self.lin_damping
377 }
378
379 pub fn lock_x_rotations(&mut self, state: bool) -> bool {
381 self.x_rotation_locked.set_value_and_mark_modified(state)
382 }
383
384 pub fn is_x_rotation_locked(&self) -> bool {
386 *self.x_rotation_locked
387 }
388
389 pub fn lock_y_rotations(&mut self, state: bool) -> bool {
391 self.y_rotation_locked.set_value_and_mark_modified(state)
392 }
393
394 pub fn is_y_rotation_locked(&self) -> bool {
396 *self.y_rotation_locked
397 }
398
399 pub fn lock_z_rotations(&mut self, state: bool) -> bool {
401 self.z_rotation_locked.set_value_and_mark_modified(state)
402 }
403
404 pub fn is_z_rotation_locked(&self) -> bool {
406 *self.z_rotation_locked
407 }
408
409 pub fn lock_rotations(&mut self, locked: bool) {
411 self.x_rotation_locked.set_value_and_mark_modified(locked);
412 self.y_rotation_locked.set_value_and_mark_modified(locked);
413 self.z_rotation_locked.set_value_and_mark_modified(locked);
414 }
415
416 pub fn lock_translation(&mut self, state: bool) -> bool {
418 self.translation_locked.set_value_and_mark_modified(state)
419 }
420
421 pub fn is_translation_locked(&self) -> bool {
423 *self.translation_locked
424 }
425
426 pub fn set_body_type(&mut self, body_type: RigidBodyType) -> RigidBodyType {
428 self.body_type.set_value_and_mark_modified(body_type)
429 }
430
431 pub fn body_type(&self) -> RigidBodyType {
433 *self.body_type
434 }
435
436 pub fn is_sleeping(&self) -> bool {
439 self.sleeping
440 }
441
442 pub fn is_ccd_enabled(&self) -> bool {
444 *self.ccd_enabled
445 }
446
447 pub fn enable_ccd(&mut self, enable: bool) -> bool {
450 self.ccd_enabled.set_value_and_mark_modified(enable)
451 }
452
453 pub fn set_gravity_scale(&mut self, scale: f32) -> f32 {
455 self.gravity_scale.set_value_and_mark_modified(scale)
456 }
457
458 pub fn set_mass_properties_type(
460 &mut self,
461 mass_properties_type: RigidBodyMassPropertiesType,
462 ) -> RigidBodyMassPropertiesType {
463 self.mass_properties_type
464 .set_value_and_mark_modified(mass_properties_type)
465 }
466
467 pub fn mass_properties_type(&self) -> RigidBodyMassPropertiesType {
469 *self.mass_properties_type
470 }
471
472 pub fn gravity_scale(&self) -> f32 {
474 *self.gravity_scale
475 }
476
477 pub fn set_dominance(&mut self, dominance: i8) -> i8 {
482 self.dominance.set_value_and_mark_modified(dominance)
483 }
484
485 pub fn dominance(&self) -> i8 {
487 *self.dominance
488 }
489
490 pub fn apply_force(&mut self, force: Vector3<f32>) {
493 self.actions.get_mut().push_back(ApplyAction::Force(force))
494 }
495
496 pub fn apply_torque(&mut self, torque: Vector3<f32>) {
499 self.actions
500 .get_mut()
501 .push_back(ApplyAction::Torque(torque))
502 }
503
504 pub fn apply_force_at_point(&mut self, force: Vector3<f32>, point: Vector3<f32>) {
507 self.actions
508 .get_mut()
509 .push_back(ApplyAction::ForceAtPoint { force, point })
510 }
511
512 pub fn apply_impulse(&mut self, impulse: Vector3<f32>) {
515 self.actions
516 .get_mut()
517 .push_back(ApplyAction::Impulse(impulse))
518 }
519
520 pub fn apply_torque_impulse(&mut self, torque_impulse: Vector3<f32>) {
523 self.actions
524 .get_mut()
525 .push_back(ApplyAction::TorqueImpulse(torque_impulse))
526 }
527
528 pub fn apply_impulse_at_point(&mut self, impulse: Vector3<f32>, point: Vector3<f32>) {
532 self.actions
533 .get_mut()
534 .push_back(ApplyAction::ImpulseAtPoint { impulse, point })
535 }
536
537 pub fn set_next_kinematic_translation(&mut self, translation: Vector3<f32>) {
540 self.actions
541 .get_mut()
542 .push_back(ApplyAction::NextTranslation(translation));
543 }
544
545 pub fn set_next_kinematic_rotation(&mut self, rotation: UnitQuaternion<f32>) {
548 self.actions
549 .get_mut()
550 .push_back(ApplyAction::NextRotation(rotation));
551 }
552
553 pub fn set_next_kinematic_position(&mut self, position: Isometry3<f32>) {
556 self.actions
557 .get_mut()
558 .push_back(ApplyAction::NextPosition(position));
559 }
560
561 pub fn set_can_sleep(&mut self, can_sleep: bool) -> bool {
564 self.can_sleep.set_value_and_mark_modified(can_sleep)
565 }
566
567 pub fn is_can_sleep(&self) -> bool {
569 *self.can_sleep
570 }
571
572 pub fn wake_up(&mut self) {
574 self.actions.get_mut().push_back(ApplyAction::WakeUp)
575 }
576
577 pub(crate) fn need_sync_model(&self) -> bool {
578 self.lin_vel.need_sync()
579 || self.ang_vel.need_sync()
580 || self.lin_damping.need_sync()
581 || self.ang_damping.need_sync()
582 || self.body_type.need_sync()
583 || self.mass.need_sync()
584 || self.x_rotation_locked.need_sync()
585 || self.y_rotation_locked.need_sync()
586 || self.z_rotation_locked.need_sync()
587 || self.translation_locked.need_sync()
588 || self.ccd_enabled.need_sync()
589 || self.can_sleep.need_sync()
590 || self.dominance.need_sync()
591 || self.gravity_scale.need_sync()
592 || self.reset_forces.get()
593 }
594}
595
596impl ConstructorProvider<Node, Graph> for RigidBody {
597 fn constructor() -> NodeConstructor {
598 NodeConstructor::new::<Self>()
599 .with_variant("Rigid Body", |_| {
600 RigidBodyBuilder::new(BaseBuilder::new().with_name("Rigid Body"))
601 .build_node()
602 .into()
603 })
604 .with_group("Physics")
605 }
606}
607
608impl NodeTrait for RigidBody {
609 fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
610 self.base.local_bounding_box()
611 }
612
613 fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
614 self.base.world_bounding_box()
615 }
616
617 fn id(&self) -> Uuid {
618 Self::type_uuid()
619 }
620
621 fn on_removed_from_graph(&mut self, graph: &mut Graph) {
622 graph.physics.remove_body(self.native.get());
623 self.native.set(RigidBodyHandle::invalid());
624
625 Log::info(format!(
626 "Native rigid body was removed for node: {}",
627 self.name()
628 ));
629 }
630
631 fn sync_native(&self, self_handle: Handle<Node>, context: &mut SyncContext) {
632 context.physics.sync_to_rigid_body_node(self_handle, self);
633 }
634
635 fn on_global_transform_changed(
636 &self,
637 new_global_transform: &Matrix4<f32>,
638 context: &mut SyncContext,
639 ) {
640 if !m4x4_approx_eq(new_global_transform, &self.global_transform()) {
641 context
642 .physics
643 .set_rigid_body_position(self, new_global_transform);
644 }
645 }
646
647 fn update(&mut self, context: &mut UpdateContext) {
648 context.physics.sync_rigid_body_node(
649 self,
650 context
652 .nodes
653 .try_borrow(self.parent)
654 .map(|p| p.global_transform())
655 .unwrap_or_else(Matrix4::identity),
656 );
657 }
658
659 fn validate(&self, scene: &Scene) -> Result<(), String> {
660 for &child in self.children() {
661 if scene.graph.try_get_of_type::<Collider>(child).is_some() {
662 return Ok(());
663 }
664 }
665
666 Err("The 3D rigid body must have at least one 3D collider as a \
667 direct child node to work correctly!"
668 .to_string())
669 }
670}
671
672pub struct RigidBodyBuilder {
674 base_builder: BaseBuilder,
675 lin_vel: Vector3<f32>,
676 ang_vel: Vector3<f32>,
677 lin_damping: f32,
678 ang_damping: f32,
679 sleeping: bool,
680 body_type: RigidBodyType,
681 mass: f32,
682 x_rotation_locked: bool,
683 y_rotation_locked: bool,
684 z_rotation_locked: bool,
685 translation_locked: bool,
686 ccd_enabled: bool,
687 can_sleep: bool,
688 dominance: i8,
689 gravity_scale: f32,
690 mass_properties_type: RigidBodyMassPropertiesType,
691}
692
693impl RigidBodyBuilder {
694 pub fn new(base_builder: BaseBuilder) -> Self {
696 Self {
697 base_builder,
698 lin_vel: Default::default(),
699 ang_vel: Default::default(),
700 lin_damping: 0.0,
701 ang_damping: 0.0,
702 sleeping: false,
703 body_type: RigidBodyType::Dynamic,
704 mass: 1.0,
705 x_rotation_locked: false,
706 y_rotation_locked: false,
707 z_rotation_locked: false,
708 translation_locked: false,
709 ccd_enabled: false,
710 can_sleep: true,
711 dominance: 0,
712 gravity_scale: 1.0,
713 mass_properties_type: RigidBodyMassPropertiesType::Default,
714 }
715 }
716
717 pub fn with_body_type(mut self, body_type: RigidBodyType) -> Self {
719 self.body_type = body_type;
720 self
721 }
722
723 pub fn with_mass(mut self, mass: f32) -> Self {
725 self.mass = mass;
726 self
727 }
728
729 pub fn with_ccd_enabled(mut self, enabled: bool) -> Self {
731 self.ccd_enabled = enabled;
732 self
733 }
734
735 pub fn with_lin_vel(mut self, lin_vel: Vector3<f32>) -> Self {
737 self.lin_vel = lin_vel;
738 self
739 }
740
741 pub fn with_ang_vel(mut self, ang_vel: Vector3<f32>) -> Self {
743 self.ang_vel = ang_vel;
744 self
745 }
746
747 pub fn with_ang_damping(mut self, ang_damping: f32) -> Self {
749 self.ang_damping = ang_damping;
750 self
751 }
752
753 pub fn with_lin_damping(mut self, lin_damping: f32) -> Self {
755 self.lin_damping = lin_damping;
756 self
757 }
758
759 pub fn with_x_rotation_locked(mut self, x_rotation_locked: bool) -> Self {
761 self.x_rotation_locked = x_rotation_locked;
762 self
763 }
764
765 pub fn with_y_rotation_locked(mut self, y_rotation_locked: bool) -> Self {
767 self.y_rotation_locked = y_rotation_locked;
768 self
769 }
770
771 pub fn with_z_rotation_locked(mut self, z_rotation_locked: bool) -> Self {
773 self.z_rotation_locked = z_rotation_locked;
774 self
775 }
776
777 pub fn with_translation_locked(mut self, translation_locked: bool) -> Self {
779 self.translation_locked = translation_locked;
780 self
781 }
782
783 pub fn with_locked_rotations(mut self, locked: bool) -> Self {
785 self.x_rotation_locked = locked;
786 self.y_rotation_locked = locked;
787 self.z_rotation_locked = locked;
788 self
789 }
790
791 pub fn with_sleeping(mut self, sleeping: bool) -> Self {
793 self.sleeping = sleeping;
794 self
795 }
796
797 pub fn with_can_sleep(mut self, can_sleep: bool) -> Self {
799 self.can_sleep = can_sleep;
800 self
801 }
802
803 pub fn with_dominance(mut self, dominance: i8) -> Self {
805 self.dominance = dominance;
806 self
807 }
808
809 pub fn with_gravity_scale(mut self, gravity_scale: f32) -> Self {
811 self.gravity_scale = gravity_scale;
812 self
813 }
814
815 pub fn with_mass_properties_type(
817 mut self,
818 mass_properties_type: RigidBodyMassPropertiesType,
819 ) -> Self {
820 self.mass_properties_type = mass_properties_type;
821 self
822 }
823
824 pub fn build_rigid_body(self) -> RigidBody {
826 RigidBody {
827 base: self.base_builder.build_base(),
828 lin_vel: self.lin_vel.into(),
829 ang_vel: self.ang_vel.into(),
830 lin_damping: self.lin_damping.into(),
831 ang_damping: self.ang_damping.into(),
832 sleeping: self.sleeping,
833 body_type: self.body_type.into(),
834 mass: self.mass.into(),
835 x_rotation_locked: self.x_rotation_locked.into(),
836 y_rotation_locked: self.y_rotation_locked.into(),
837 z_rotation_locked: self.z_rotation_locked.into(),
838 translation_locked: self.translation_locked.into(),
839 ccd_enabled: self.ccd_enabled.into(),
840 can_sleep: self.can_sleep.into(),
841 dominance: self.dominance.into(),
842 gravity_scale: self.gravity_scale.into(),
843 native: Cell::new(RigidBodyHandle::invalid()),
844 actions: Default::default(),
845 reset_forces: Default::default(),
846 mass_properties_type: self.mass_properties_type.into(),
847 }
848 }
849
850 pub fn build_node(self) -> Node {
852 Node::new(self.build_rigid_body())
853 }
854
855 pub fn build(self, graph: &mut Graph) -> Handle<Node> {
857 graph.add_node(self.build_node())
858 }
859}