rapier3d/dynamics/joint/multibody_joint/
multibody_joint_set.rs

1use parry::utils::hashset::HashSet;
2
3use crate::data::{Arena, Coarena, Index};
4use crate::dynamics::joint::MultibodyLink;
5use crate::dynamics::{GenericJoint, Multibody, MultibodyJoint, RigidBodyHandle};
6use crate::geometry::{InteractionGraph, RigidBodyGraphIndex};
7
8/// The unique handle of an multibody_joint added to a `MultibodyJointSet`.
9#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
10#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
11#[repr(transparent)]
12pub struct MultibodyJointHandle(pub Index);
13
14/// The temporary index of a multibody added to a `MultibodyJointSet`.
15#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
16#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
17#[repr(transparent)]
18pub struct MultibodyIndex(pub Index);
19
20impl MultibodyJointHandle {
21    /// Converts this handle into its (index, generation) components.
22    pub fn into_raw_parts(self) -> (u32, u32) {
23        self.0.into_raw_parts()
24    }
25
26    /// Reconstructs an handle from its (index, generation) components.
27    pub fn from_raw_parts(id: u32, generation: u32) -> Self {
28        Self(Index::from_raw_parts(id, generation))
29    }
30
31    /// An always-invalid rigid-body handle.
32    pub fn invalid() -> Self {
33        Self(Index::from_raw_parts(
34            crate::INVALID_U32,
35            crate::INVALID_U32,
36        ))
37    }
38}
39
40impl Default for MultibodyJointHandle {
41    fn default() -> Self {
42        Self::invalid()
43    }
44}
45
46#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
47#[derive(Copy, Clone, Debug, PartialEq, Eq)]
48/// Indexes usable to get a multibody link from a `MultibodyJointSet`.
49///
50/// ```ignore
51/// // With:
52/// //     multibody_joint_set: MultibodyJointSet
53/// //     multibody_link_id: MultibodyLinkId
54/// let multibody = &multibody_joint_set[multibody_link_id.multibody];
55/// let link = multibody.link(multibody_link_id.id).expect("Link not found.");
56/// ```
57pub struct MultibodyLinkId {
58    pub(crate) graph_id: RigidBodyGraphIndex,
59    /// The multibody index to be used as `&multibody_joint_set[multibody]` to
60    /// retrieve the multibody reference.
61    pub multibody: MultibodyIndex,
62    /// The multibody link index to be given to [`Multibody::link`].
63    pub id: usize,
64}
65
66impl Default for MultibodyLinkId {
67    fn default() -> Self {
68        Self {
69            graph_id: RigidBodyGraphIndex::new(crate::INVALID_U32),
70            multibody: MultibodyIndex(Index::from_raw_parts(
71                crate::INVALID_U32,
72                crate::INVALID_U32,
73            )),
74            id: 0,
75        }
76    }
77}
78
79#[derive(Default)]
80/// A set of rigid bodies that can be handled by a physics pipeline.
81#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
82#[derive(Clone, Debug)]
83pub struct MultibodyJointSet {
84    pub(crate) multibodies: Arena<Multibody>, // NOTE: a Slab would be sufficient.
85    pub(crate) rb2mb: Coarena<MultibodyLinkId>,
86    // NOTE: this is mostly for the island extraction. So perhaps we won’t need
87    //       that any more in the future when we improve our island builder.
88    pub(crate) connectivity_graph: InteractionGraph<RigidBodyHandle, ()>,
89    pub(crate) to_wake_up: HashSet<RigidBodyHandle>,
90}
91
92impl MultibodyJointSet {
93    /// Create a new empty set of multibodies.
94    pub fn new() -> Self {
95        Self {
96            multibodies: Arena::new(),
97            rb2mb: Coarena::new(),
98            connectivity_graph: InteractionGraph::new(),
99            to_wake_up: HashSet::default(),
100        }
101    }
102
103    /// Iterates through all the multibody joints from this set.
104    pub fn iter(
105        &self,
106    ) -> impl Iterator<
107        Item = (
108            MultibodyJointHandle,
109            &MultibodyLinkId,
110            &Multibody,
111            &MultibodyLink,
112        ),
113    > {
114        self.rb2mb
115            .iter()
116            .filter(|(_, link)| link.id > 0) // The first link of a rigid-body hasn’t been added by the user.
117            .map(|(h, link)| {
118                let mb = &self.multibodies[link.multibody.0];
119                (MultibodyJointHandle(h), link, mb, mb.link(link.id).unwrap())
120            })
121    }
122
123    /// Inserts a new kinematic multibody joint into this set.
124    pub fn insert_kinematic(
125        &mut self,
126        body1: RigidBodyHandle,
127        body2: RigidBodyHandle,
128        data: impl Into<GenericJoint>,
129        wake_up: bool,
130    ) -> Option<MultibodyJointHandle> {
131        self.do_insert(body1, body2, data, true, wake_up)
132    }
133
134    /// Inserts a new multibody joint into this set.
135    pub fn insert(
136        &mut self,
137        body1: RigidBodyHandle,
138        body2: RigidBodyHandle,
139        data: impl Into<GenericJoint>,
140        wake_up: bool,
141    ) -> Option<MultibodyJointHandle> {
142        self.do_insert(body1, body2, data, false, wake_up)
143    }
144
145    /// Inserts a new multibody_joint into this set.
146    #[profiling::function]
147    fn do_insert(
148        &mut self,
149        body1: RigidBodyHandle,
150        body2: RigidBodyHandle,
151        data: impl Into<GenericJoint>,
152        kinematic: bool,
153        wake_up: bool,
154    ) -> Option<MultibodyJointHandle> {
155        let link1 = self.rb2mb.get(body1.0).copied().unwrap_or_else(|| {
156            let mb_handle = self.multibodies.insert(Multibody::with_root(body1, true));
157            MultibodyLinkId {
158                graph_id: self.connectivity_graph.graph.add_node(body1),
159                multibody: MultibodyIndex(mb_handle),
160                id: 0,
161            }
162        });
163
164        let link2 = self.rb2mb.get(body2.0).copied().unwrap_or_else(|| {
165            let mb_handle = self.multibodies.insert(Multibody::with_root(body2, true));
166            MultibodyLinkId {
167                graph_id: self.connectivity_graph.graph.add_node(body2),
168                multibody: MultibodyIndex(mb_handle),
169                id: 0,
170            }
171        });
172
173        if link1.multibody == link2.multibody || link2.id != 0 {
174            // This would introduce an invalid configuration.
175            return None;
176        }
177
178        self.connectivity_graph
179            .graph
180            .add_edge(link1.graph_id, link2.graph_id, ());
181        self.rb2mb.insert(body1.0, link1);
182        self.rb2mb.insert(body2.0, link2);
183
184        let mb2 = self.multibodies.remove(link2.multibody.0).unwrap();
185        let multibody1 = &mut self.multibodies[link1.multibody.0];
186
187        for mb_link2 in mb2.links() {
188            let link = self.rb2mb.get_mut(mb_link2.rigid_body.0).unwrap();
189            link.multibody = link1.multibody;
190            link.id += multibody1.num_links();
191        }
192
193        multibody1.append(mb2, link1.id, MultibodyJoint::new(data.into(), kinematic));
194
195        if wake_up {
196            self.to_wake_up.insert(body1);
197            self.to_wake_up.insert(body2);
198        }
199
200        // Because each rigid-body can only have one parent link,
201        // we can use the second rigid-body’s handle as the multibody_joint’s
202        // handle.
203        Some(MultibodyJointHandle(body2.0))
204    }
205
206    /// Removes a multibody_joint from this set.
207    #[profiling::function]
208    pub fn remove(&mut self, handle: MultibodyJointHandle, wake_up: bool) {
209        if let Some(removed) = self.rb2mb.get(handle.0).copied() {
210            let multibody = self.multibodies.remove(removed.multibody.0).unwrap();
211
212            // Remove the edge from the connectivity graph.
213            if let Some(parent_link) = multibody.link(removed.id).unwrap().parent_id() {
214                let parent_rb = multibody.link(parent_link).unwrap().rigid_body;
215                let parent_graph_id = self.rb2mb.get(parent_rb.0).unwrap().graph_id;
216                self.connectivity_graph
217                    .remove_edge(parent_graph_id, removed.graph_id);
218
219                if wake_up {
220                    self.to_wake_up.insert(RigidBodyHandle(handle.0));
221                    self.to_wake_up.insert(parent_rb);
222                }
223
224                // TODO: remove the node if it no longer has any attached edges?
225
226                // Extract the individual sub-trees generated by this removal.
227                let multibodies = multibody.remove_link(removed.id, true);
228
229                // Update the rb2mb mapping.
230                for multibody in multibodies {
231                    if multibody.num_links() == 1 {
232                        // We don’t have any multibody_joint attached to this body, remove it.
233                        let isolated_link = multibody.link(0).unwrap();
234                        let isolated_graph_id =
235                            self.rb2mb.get(isolated_link.rigid_body.0).unwrap().graph_id;
236                        if let Some(other) = self.connectivity_graph.remove_node(isolated_graph_id)
237                        {
238                            self.rb2mb.get_mut(other.0).unwrap().graph_id = isolated_graph_id;
239                        }
240                    } else {
241                        let mb_id = self.multibodies.insert(multibody);
242                        for link in self.multibodies[mb_id].links() {
243                            let ids = self.rb2mb.get_mut(link.rigid_body.0).unwrap();
244                            ids.multibody = MultibodyIndex(mb_id);
245                            ids.id = link.internal_id;
246                        }
247                    }
248                }
249            }
250        }
251    }
252
253    /// Removes all the multibody_joints from the multibody the given rigid-body is part of.
254    #[profiling::function]
255    pub fn remove_multibody_articulations(&mut self, handle: RigidBodyHandle, wake_up: bool) {
256        if let Some(removed) = self.rb2mb.get(handle.0).copied() {
257            // Remove the multibody.
258            let multibody = self.multibodies.remove(removed.multibody.0).unwrap();
259            for link in multibody.links() {
260                let rb_handle = link.rigid_body;
261
262                if wake_up {
263                    self.to_wake_up.insert(rb_handle);
264                }
265
266                // Remove the rigid-body <-> multibody mapping for this link.
267                let removed = self.rb2mb.remove(rb_handle.0, Default::default()).unwrap();
268                // Remove the node (and all it’s edges) from the connectivity graph.
269                if let Some(other) = self.connectivity_graph.remove_node(removed.graph_id) {
270                    self.rb2mb.get_mut(other.0).unwrap().graph_id = removed.graph_id;
271                }
272            }
273        }
274    }
275
276    /// Removes all the multibody joints attached to a rigid-body.
277    #[profiling::function]
278    pub fn remove_joints_attached_to_rigid_body(&mut self, rb_to_remove: RigidBodyHandle) {
279        // TODO: optimize this.
280        if let Some(link_to_remove) = self.rb2mb.get(rb_to_remove.0).copied() {
281            let mut articulations_to_remove = vec![];
282            for (rb1, rb2, _) in self
283                .connectivity_graph
284                .interactions_with(link_to_remove.graph_id)
285            {
286                // There is a multibody_joint handle is equal to the second rigid-body’s handle.
287                articulations_to_remove.push(MultibodyJointHandle(rb2.0));
288
289                self.to_wake_up.insert(rb1);
290                self.to_wake_up.insert(rb2);
291            }
292
293            for articulation_handle in articulations_to_remove {
294                self.remove(articulation_handle, true);
295            }
296        }
297    }
298
299    /// Returns the link of this multibody attached to the given rigid-body.
300    ///
301    /// Returns `None` if `rb` isn’t part of any rigid-body.
302    pub fn rigid_body_link(&self, rb: RigidBodyHandle) -> Option<&MultibodyLinkId> {
303        self.rb2mb.get(rb.0)
304    }
305
306    /// Gets a reference to a multibody, based on its temporary index.
307    pub fn get_multibody(&self, index: MultibodyIndex) -> Option<&Multibody> {
308        self.multibodies.get(index.0)
309    }
310
311    /// Gets a mutable reference to a multibody, based on its temporary index.
312    /// `MultibodyJointSet`.
313    pub fn get_multibody_mut(&mut self, index: MultibodyIndex) -> Option<&mut Multibody> {
314        // TODO: modification tracking.
315        self.multibodies.get_mut(index.0)
316    }
317
318    /// Gets a mutable reference to a multibody, based on its temporary index.
319    ///
320    /// This method will bypass any modification-detection automatically done by the
321    /// `MultibodyJointSet`.
322    pub fn get_multibody_mut_internal(&mut self, index: MultibodyIndex) -> Option<&mut Multibody> {
323        self.multibodies.get_mut(index.0)
324    }
325
326    /// Gets a reference to the multibody identified by its `handle`.
327    pub fn get(&self, handle: MultibodyJointHandle) -> Option<(&Multibody, usize)> {
328        let link = self.rb2mb.get(handle.0)?;
329        let multibody = self.multibodies.get(link.multibody.0)?;
330        Some((multibody, link.id))
331    }
332
333    /// Gets a mutable reference to the multibody identified by its `handle`.
334    pub fn get_mut(&mut self, handle: MultibodyJointHandle) -> Option<(&mut Multibody, usize)> {
335        let link = self.rb2mb.get(handle.0)?;
336        let multibody = self.multibodies.get_mut(link.multibody.0)?;
337        Some((multibody, link.id))
338    }
339
340    /// Gets a mutable reference to the multibody identified by its `handle`.
341    ///
342    /// This method will bypass any modification-detection automatically done by the MultibodyJointSet.
343    pub fn get_mut_internal(
344        &mut self,
345        handle: MultibodyJointHandle,
346    ) -> Option<(&mut Multibody, usize)> {
347        // TODO: modification tracking?
348        let link = self.rb2mb.get(handle.0)?;
349        let multibody = self.multibodies.get_mut(link.multibody.0)?;
350        Some((multibody, link.id))
351    }
352
353    /// Gets the joint with the given handle without a known generation.
354    ///
355    /// This is useful when you know you want the joint at index `i` but
356    /// don't know what is its current generation number. Generation numbers are
357    /// used to protect from the ABA problem because the joint position `i`
358    /// are recycled between two insertion and a removal.
359    ///
360    /// Using this is discouraged in favor of `self.get(handle)` which does not
361    /// suffer form the ABA problem.
362    pub fn get_unknown_gen(&self, i: u32) -> Option<(&Multibody, usize, MultibodyJointHandle)> {
363        let link = self.rb2mb.get_unknown_gen(i)?;
364        let generation = self.rb2mb.get_gen(i)?;
365        let multibody = self.multibodies.get(link.multibody.0)?;
366        Some((
367            multibody,
368            link.id,
369            MultibodyJointHandle(Index::from_raw_parts(i, generation)),
370        ))
371    }
372
373    /// Returns the joint between two rigid-bodies (if it exists).
374    pub fn joint_between(
375        &self,
376        rb1: RigidBodyHandle,
377        rb2: RigidBodyHandle,
378    ) -> Option<(MultibodyJointHandle, &Multibody, &MultibodyLink)> {
379        let id1 = self.rb2mb.get(rb1.0)?;
380        let id2 = self.rb2mb.get(rb2.0)?;
381
382        // Both bodies must be part of the same multibody.
383        if id1.multibody != id2.multibody {
384            return None;
385        }
386
387        let mb = self.multibodies.get(id1.multibody.0)?;
388
389        // NOTE: if there is a joint between these two bodies, then
390        //       one of the bodies must be the parent of the other.
391        let link1 = mb.link(id1.id)?;
392        let parent1 = link1.parent_id();
393
394        if parent1 == Some(id2.id) {
395            Some((MultibodyJointHandle(rb1.0), mb, link1))
396        } else {
397            let link2 = mb.link(id2.id)?;
398            let parent2 = link2.parent_id();
399
400            if parent2 == Some(id1.id) {
401                Some((MultibodyJointHandle(rb2.0), mb, link2))
402            } else {
403                None
404            }
405        }
406    }
407
408    /// Iterates through all the joints attached to the given rigid-body.
409    #[profiling::function]
410    pub fn attached_joints(
411        &self,
412        rb: RigidBodyHandle,
413    ) -> impl Iterator<Item = (RigidBodyHandle, RigidBodyHandle, MultibodyJointHandle)> + '_ {
414        self.rb2mb
415            .get(rb.0)
416            .into_iter()
417            .flat_map(move |link| self.connectivity_graph.interactions_with(link.graph_id))
418            .map(|inter| {
419                // NOTE: the joint handle is always equal to the handle of the second rigid-body.
420                (inter.0, inter.1, MultibodyJointHandle(inter.1.0))
421            })
422    }
423
424    /// Iterate through the handles of all the rigid-bodies attached to this rigid-body
425    /// by a multibody_joint.
426    pub fn attached_bodies(
427        &self,
428        body: RigidBodyHandle,
429    ) -> impl Iterator<Item = RigidBodyHandle> + '_ {
430        self.rb2mb
431            .get(body.0)
432            .into_iter()
433            .flat_map(move |id| self.connectivity_graph.interactions_with(id.graph_id))
434            .map(move |inter| crate::utils::select_other((inter.0, inter.1), body))
435    }
436
437    /// Iterate through the handles of all the rigid-bodies attached to this rigid-body
438    /// by an enabled multibody_joint.
439    #[profiling::function]
440    pub fn bodies_attached_with_enabled_joint(
441        &self,
442        body: RigidBodyHandle,
443    ) -> impl Iterator<Item = RigidBodyHandle> + '_ {
444        self.attached_bodies(body).filter(move |other| {
445            if let Some((_, _, link)) = self.joint_between(body, *other) {
446                link.joint.data.is_enabled()
447            } else {
448                false
449            }
450        })
451    }
452
453    /// Iterates through all the multibodies on this set.
454    pub fn multibodies(&self) -> impl Iterator<Item = &Multibody> {
455        self.multibodies.iter().map(|e| e.1)
456    }
457}
458
459impl std::ops::Index<MultibodyIndex> for MultibodyJointSet {
460    type Output = Multibody;
461
462    fn index(&self, index: MultibodyIndex) -> &Multibody {
463        &self.multibodies[index.0]
464    }
465}
466
467// impl Index<MultibodyJointHandle> for MultibodyJointSet {
468//     type Output = Multibody;
469//
470//     fn index(&self, index: MultibodyJointHandle) -> &Multibody {
471//         &self.multibodies[index.0]
472//     }
473// }