aboutsummaryrefslogtreecommitdiff
path: root/src/dynamics
diff options
context:
space:
mode:
authorSébastien Crozet <developer@crozet.re>2020-08-25 22:10:25 +0200
committerSébastien Crozet <developer@crozet.re>2020-08-25 22:10:25 +0200
commit754a48b7ff6d8c58b1ee08651e60112900b60455 (patch)
tree7d777a6c003f1f5d8f8d24f533f35a95a88957fe /src/dynamics
downloadrapier-754a48b7ff6d8c58b1ee08651e60112900b60455.tar.gz
rapier-754a48b7ff6d8c58b1ee08651e60112900b60455.tar.bz2
rapier-754a48b7ff6d8c58b1ee08651e60112900b60455.zip
First public release of Rapier.v0.1.0
Diffstat (limited to 'src/dynamics')
-rw-r--r--src/dynamics/integration_parameters.rs207
-rw-r--r--src/dynamics/joint/ball_joint.rs34
-rw-r--r--src/dynamics/joint/fixed_joint.rs33
-rw-r--r--src/dynamics/joint/joint.rs112
-rw-r--r--src/dynamics/joint/joint_set.rs218
-rw-r--r--src/dynamics/joint/mod.rs16
-rw-r--r--src/dynamics/joint/prismatic_joint.rs193
-rw-r--r--src/dynamics/joint/revolute_joint.rs46
-rw-r--r--src/dynamics/mass_properties.rs206
-rw-r--r--src/dynamics/mass_properties_ball.rs30
-rw-r--r--src/dynamics/mass_properties_capsule.rs60
-rw-r--r--src/dynamics/mass_properties_cuboid.rs33
-rw-r--r--src/dynamics/mass_properties_polygon.rs144
-rw-r--r--src/dynamics/mod.rs30
-rw-r--r--src/dynamics/rigid_body.rs441
-rw-r--r--src/dynamics/rigid_body_set.rs518
-rw-r--r--src/dynamics/solver/categorization.rs70
-rw-r--r--src/dynamics/solver/delta_vel.rs18
-rw-r--r--src/dynamics/solver/interaction_groups.rs434
-rw-r--r--src/dynamics/solver/island_solver.rs73
-rw-r--r--src/dynamics/solver/joint_constraint/ball_position_constraint.rs165
-rw-r--r--src/dynamics/solver/joint_constraint/ball_position_constraint_wide.rs199
-rw-r--r--src/dynamics/solver/joint_constraint/ball_velocity_constraint.rs238
-rw-r--r--src/dynamics/solver/joint_constraint/ball_velocity_constraint_wide.rs329
-rw-r--r--src/dynamics/solver/joint_constraint/fixed_position_constraint.rs139
-rw-r--r--src/dynamics/solver/joint_constraint/fixed_velocity_constraint.rs370
-rw-r--r--src/dynamics/solver/joint_constraint/fixed_velocity_constraint_wide.rs472
-rw-r--r--src/dynamics/solver/joint_constraint/joint_constraint.rs340
-rw-r--r--src/dynamics/solver/joint_constraint/joint_position_constraint.rs169
-rw-r--r--src/dynamics/solver/joint_constraint/mod.rs65
-rw-r--r--src/dynamics/solver/joint_constraint/prismatic_position_constraint.rs170
-rw-r--r--src/dynamics/solver/joint_constraint/prismatic_velocity_constraint.rs558
-rw-r--r--src/dynamics/solver/joint_constraint/prismatic_velocity_constraint_wide.rs687
-rw-r--r--src/dynamics/solver/joint_constraint/revolute_position_constraint.rs142
-rw-r--r--src/dynamics/solver/joint_constraint/revolute_velocity_constraint.rs294
-rw-r--r--src/dynamics/solver/joint_constraint/revolute_velocity_constraint_wide.rs397
-rw-r--r--src/dynamics/solver/mod.rs56
-rw-r--r--src/dynamics/solver/parallel_island_solver.rs259
-rw-r--r--src/dynamics/solver/parallel_position_solver.rs582
-rw-r--r--src/dynamics/solver/parallel_velocity_solver.rs485
-rw-r--r--src/dynamics/solver/position_constraint.rs246
-rw-r--r--src/dynamics/solver/position_constraint_wide.rs217
-rw-r--r--src/dynamics/solver/position_ground_constraint.rs189
-rw-r--r--src/dynamics/solver/position_ground_constraint_wide.rs199
-rw-r--r--src/dynamics/solver/position_solver.rs451
-rw-r--r--src/dynamics/solver/velocity_constraint.rs401
-rw-r--r--src/dynamics/solver/velocity_constraint_wide.rs344
-rw-r--r--src/dynamics/solver/velocity_ground_constraint.rs300
-rw-r--r--src/dynamics/solver/velocity_ground_constraint_wide.rs300
-rw-r--r--src/dynamics/solver/velocity_solver.rs405
50 files changed, 12084 insertions, 0 deletions
diff --git a/src/dynamics/integration_parameters.rs b/src/dynamics/integration_parameters.rs
new file mode 100644
index 0000000..faf22e1
--- /dev/null
+++ b/src/dynamics/integration_parameters.rs
@@ -0,0 +1,207 @@
+/// Parameters for a time-step of the physics engine.
+#[derive(Clone)]
+#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
+pub struct IntegrationParameters {
+ /// The timestep (default: `1.0 / 60.0`)
+ dt: f32,
+ /// The inverse of `dt`.
+ inv_dt: f32,
+ // /// If `true` and if rapier is compiled with the `parallel` feature, this will enable rayon-based multithreading (default: `true`).
+ // ///
+ // /// This parameter is ignored if rapier is not compiled with is `parallel` feature.
+ // /// Refer to rayon's documentation regarding how to configure the number of threads with either
+ // /// `rayon::ThreadPoolBuilder::new().num_threads(4).build_global().unwrap()` or `ThreadPool::install`.
+ // /// Note that using only one thread with `multithreading_enabled` set to `true` will result on a slower
+ // /// simulation than setting `multithreading_enabled` to `false`.
+ // pub multithreading_enabled: bool,
+ /// If `true`, the world's `step` method will stop right after resolving exactly one CCD event (default: `false`).
+ /// This allows the user to take action during a timestep, in-between two CCD events.
+ pub return_after_ccd_substep: bool,
+ /// The Error Reduction Parameter in `[0, 1]` is the proportion of
+ /// the positional error to be corrected at each time step (default: `0.2`).
+ pub erp: f32,
+ /// The Error Reduction Parameter for joints in `[0, 1]` is the proportion of
+ /// the positional error to be corrected at each time step (default: `0.2`).
+ pub joint_erp: f32,
+ /// Each cached impulse are multiplied by this coefficient in `[0, 1]`
+ /// when they are re-used to initialize the solver (default `1.0`).
+ pub warmstart_coeff: f32,
+ /// Contacts at points where the involved bodies have a relative
+ /// velocity smaller than this threshold wont be affected by the restitution force (default: `1.0`).
+ pub restitution_velocity_threshold: f32,
+ /// Amount of penetration the engine wont attempt to correct (default: `0.001m`).
+ pub allowed_linear_error: f32,
+ /// The maximal distance separating two objects that will generate predictive contacts (default: `0.002`).
+ pub prediction_distance: f32,
+ /// Amount of angular drift of joint limits the engine wont
+ /// attempt to correct (default: `0.001rad`).
+ pub allowed_angular_error: f32,
+ /// Maximum linear correction during one step of the non-linear position solver (default: `0.2`).
+ pub max_linear_correction: f32,
+ /// Maximum angular correction during one step of the non-linear position solver (default: `0.2`).
+ pub max_angular_correction: f32,
+ /// Maximum nonlinear SOR-prox scaling parameter when the constraint
+ /// correction direction is close to the kernel of the involved multibody's
+ /// jacobian (default: `0.2`).
+ pub max_stabilization_multiplier: f32,
+ /// Maximum number of iterations performed by the velocity constraints solver (default: `4`).
+ pub max_velocity_iterations: usize,
+ /// Maximum number of iterations performed by the position-based constraints solver (default: `1`).
+ pub max_position_iterations: usize,
+ /// Minimum number of dynamic bodies in each active island (default: `128`).
+ pub min_island_size: usize,
+ /// Maximum number of iterations performed by the position-based constraints solver for CCD steps (default: `10`).
+ ///
+ /// This should be sufficiently high so all penetration get resolved. For example, if CCD cause your
+ /// objects to stutter, that may be because the number of CCD position iterations is too low, causing
+ /// them to remain stuck in a penetration configuration for a few frames.
+ ///
+ /// The highest this number, the highest its computational cost.
+ pub max_ccd_position_iterations: usize,
+ /// Maximum number of substeps performed by the solver (default: `1`).
+ pub max_ccd_substeps: usize,
+ /// Controls the number of Proximity::Intersecting events generated by a trigger during CCD resolution (default: `false`).
+ ///
+ /// If false, triggers will only generate one Proximity::Intersecting event during a step, even
+ /// if another colliders repeatedly enters and leaves the triggers during multiple CCD substeps.
+ ///
+ /// If true, triggers will generate as many Proximity::Intersecting and Proximity::Disjoint/Proximity::WithinMargin
+ /// events as the number of times a collider repeatedly enters and leaves the triggers during multiple CCD substeps.
+ /// This is more computationally intensive.
+ pub multiple_ccd_substep_sensor_events_enabled: bool,
+ /// Whether penetration are taken into account in CCD resolution (default: `false`).
+ ///
+ /// If this is set to `false` two penetrating colliders will not be considered to have any time of impact
+ /// while they are penetrating. This may end up allowing some tunelling, but will avoid stuttering effect
+ /// when the constraints solver fails to completely separate two colliders after a CCD contact.
+ ///
+ /// If this is set to `true`, two penetrating colliders will be considered to have a time of impact
+ /// equal to 0 until the constraints solver manages to separate them. This will prevent tunnelling
+ /// almost completely, but may introduce stuttering effects when the constraints solver fails to completely
+ /// separate two colliders after a CCD contact.
+ // FIXME: this is a very binary way of handling penetration.
+ // We should provide a more flexible solution by letting the user choose some
+ // minimal amount of movement applied to an object that get stuck.
+ pub ccd_on_penetration_enabled: bool,
+}
+
+impl IntegrationParameters {
+ /// Creates a set of integration parameters with the given values.
+ pub fn new(
+ dt: f32,
+ // multithreading_enabled: bool,
+ erp: f32,
+ joint_erp: f32,
+ warmstart_coeff: f32,
+ restitution_velocity_threshold: f32,
+ allowed_linear_error: f32,
+ allowed_angular_error: f32,
+ max_linear_correction: f32,
+ max_angular_correction: f32,
+ prediction_distance: f32,
+ max_stabilization_multiplier: f32,
+ max_velocity_iterations: usize,
+ max_position_iterations: usize,
+ max_ccd_position_iterations: usize,
+ max_ccd_substeps: usize,
+ return_after_ccd_substep: bool,
+ multiple_ccd_substep_sensor_events_enabled: bool,
+ ccd_on_penetration_enabled: bool,
+ ) -> Self {
+ IntegrationParameters {
+ dt,
+ inv_dt: if dt == 0.0 { 0.0 } else { 1.0 / dt },
+ // multithreading_enabled,
+ erp,
+ joint_erp,
+ warmstart_coeff,
+ restitution_velocity_threshold,
+ allowed_linear_error,
+ allowed_angular_error,
+ max_linear_correction,
+ max_angular_correction,
+ prediction_distance,
+ max_stabilization_multiplier,
+ max_velocity_iterations,
+ max_position_iterations,
+ // FIXME: what is the optimal value for min_island_size?
+ // It should not be too big so that we don't end up with
+ // huge islands that don't fit in cache.
+ // However we don't want it to be too small and end up with
+ // tons of islands, reducing SIMD parallelism opportunities.
+ min_island_size: 128,
+ max_ccd_position_iterations,
+ max_ccd_substeps,
+ return_after_ccd_substep,
+ multiple_ccd_substep_sensor_events_enabled,
+ ccd_on_penetration_enabled,
+ }
+ }
+
+ /// The current time-stepping length.
+ #[inline(always)]
+ pub fn dt(&self) -> f32 {
+ self.dt
+ }
+
+ /// The inverse of the time-stepping length.
+ ///
+ /// This is zero if `self.dt` is zero.
+ #[inline(always)]
+ pub fn inv_dt(&self) -> f32 {
+ self.inv_dt
+ }
+
+ /// Sets the time-stepping length.
+ ///
+ /// This automatically recompute `self.inv_dt`.
+ #[inline]
+ pub fn set_dt(&mut self, dt: f32) {
+ assert!(dt >= 0.0, "The time-stepping length cannot be negative.");
+ self.dt = dt;
+ if dt == 0.0 {
+ self.inv_dt = 0.0
+ } else {
+ self.inv_dt = 1.0 / dt
+ }
+ }
+
+ /// Sets the inverse time-stepping length (i.e. the frequency).
+ ///
+ /// This automatically recompute `self.dt`.
+ #[inline]
+ pub fn set_inv_dt(&mut self, inv_dt: f32) {
+ self.inv_dt = inv_dt;
+ if inv_dt == 0.0 {
+ self.dt = 0.0
+ } else {
+ self.dt = 1.0 / inv_dt
+ }
+ }
+}
+
+impl Default for IntegrationParameters {
+ fn default() -> Self {
+ Self::new(
+ 1.0 / 60.0,
+ // true,
+ 0.2,
+ 0.2,
+ 1.0,
+ 1.0,
+ 0.005,
+ 0.001,
+ 0.2,
+ 0.2,
+ 0.002,
+ 0.2,
+ 4,
+ 1,
+ 10,
+ 1,
+ false,
+ false,
+ false,
+ )
+ }
+}
diff --git a/src/dynamics/joint/ball_joint.rs b/src/dynamics/joint/ball_joint.rs
new file mode 100644
index 0000000..ec255d4
--- /dev/null
+++ b/src/dynamics/joint/ball_joint.rs
@@ -0,0 +1,34 @@
+use crate::math::{Point, Vector};
+
+#[derive(Copy, Clone)]
+#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
+/// A joint that removes all relative linear motion between a pair of points on two bodies.
+pub struct BallJoint {
+ /// Where the ball joint is attached on the first body, expressed in the first body local frame.
+ pub local_anchor1: Point<f32>,
+ /// Where the ball joint is attached on the first body, expressed in the first body local frame.
+ pub local_anchor2: Point<f32>,
+ /// The impulse applied by this joint on the first body.
+ ///
+ /// The impulse applied to the second body is given by `-impulse`.
+ pub impulse: Vector<f32>,
+}
+
+impl BallJoint {
+ /// Creates a new Ball joint from two anchors given on the local spaces of the respective bodies.
+ pub fn new(local_anchor1: Point<f32>, local_anchor2: Point<f32>) -> Self {
+ Self::with_impulse(local_anchor1, local_anchor2, Vector::zeros())
+ }
+
+ pub(crate) fn with_impulse(
+ local_anchor1: Point<f32>,
+ local_anchor2: Point<f32>,
+ impulse: Vector<f32>,
+ ) -> Self {
+ Self {
+ local_anchor1,
+ local_anchor2,
+ impulse,
+ }
+ }
+}
diff --git a/src/dynamics/joint/fixed_joint.rs b/src/dynamics/joint/fixed_joint.rs
new file mode 100644
index 0000000..0731cfb
--- /dev/null
+++ b/src/dynamics/joint/fixed_joint.rs
@@ -0,0 +1,33 @@
+use crate::math::{Isometry, SpacialVector};
+
+#[derive(Copy, Clone)]
+#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
+/// A joint that prevents all relative movement between two bodies.
+///
+/// Given two frames of references, this joint aims to ensure these frame always coincide in world-space.
+pub struct FixedJoint {
+ /// The frame of reference for the first body affected by this joint, expressed in the local frame
+ /// of the first body.
+ pub local_anchor1: Isometry<f32>,
+ /// The frame of reference for the second body affected by this joint, expressed in the local frame
+ /// of the first body.
+ pub local_anchor2: Isometry<f32>,
+ /// The impulse applied to the first body affected by this joint.
+ ///
+ /// The impulse applied to the second body affected by this joint is given by `-impulse`.
+ /// This combines both linear and angular impulses:
+ /// - In 2D, `impulse.xy()` gives the linear impulse, and `impulse.z` the angular impulse.
+ /// - In 3D, `impulse.xyz()` gives the linear impulse, and `(impulse[3], impulse[4], impulse[5])` the angular impulse.
+ pub impulse: SpacialVector<f32>,
+}
+
+impl FixedJoint {
+ /// Creates a new fixed joint from the frames of reference of both bodies.
+ pub fn new(local_anchor1: Isometry<f32>, local_anchor2: Isometry<f32>) -> Self {
+ Self {
+ local_anchor1,
+ local_anchor2,
+ impulse: SpacialVector::zeros(),
+ }
+ }
+}
diff --git a/src/dynamics/joint/joint.rs b/src/dynamics/joint/joint.rs
new file mode 100644
index 0000000..074f802
--- /dev/null
+++ b/src/dynamics/joint/joint.rs
@@ -0,0 +1,112 @@
+#[cfg(feature = "dim3")]
+use crate::dynamics::RevoluteJoint;
+use crate::dynamics::{BallJoint, FixedJoint, JointHandle, PrismaticJoint, RigidBodyHandle};
+
+#[derive(Copy, Clone)]
+#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
+/// An enum grouping all possible types of joints.
+pub enum JointParams {
+ /// A Ball joint that removes all relative linear degrees of freedom between the affected bodies.
+ BallJoint(BallJoint),
+ /// A fixed joint that removes all relative degrees of freedom between the affected bodies.
+ FixedJoint(FixedJoint),
+ /// A prismatic joint that removes all degrees of degrees of freedom between the affected
+ /// bodies except for the translation along one axis.
+ PrismaticJoint(PrismaticJoint),
+ #[cfg(feature = "dim3")]
+ /// A revolute joint that removes all degrees of degrees of freedom between the affected
+ /// bodies except for the translation along one axis.
+ RevoluteJoint(RevoluteJoint),
+}
+
+impl JointParams {
+ /// An integer identifier for each type of joint.
+ pub fn type_id(&self) -> usize {
+ match self {
+ JointParams::BallJoint(_) => 0,
+ JointParams::FixedJoint(_) => 1,
+ JointParams::PrismaticJoint(_) => 2,
+ #[cfg(feature = "dim3")]
+ JointParams::RevoluteJoint(_) => 3,
+ }
+ }
+
+ /// Gets a reference to the underlying ball joint, if `self` is one.
+ pub fn as_ball_joint(&self) -> Option<&BallJoint> {
+ if let JointParams::BallJoint(j) = self {
+ Some(j)
+ } else {
+ None
+ }
+ }
+
+ /// Gets a reference to the underlying fixed joint, if `self` is one.
+ pub fn as_fixed_joint(&self) -> Option<&FixedJoint> {
+ if let JointParams::FixedJoint(j) = self {
+ Some(j)
+ } else {
+ None
+ }
+ }
+
+ /// Gets a reference to the underlying prismatic joint, if `self` is one.
+ pub fn as_prismatic_joint(&self) -> Option<&PrismaticJoint> {
+ if let JointParams::PrismaticJoint(j) = self {
+ Some(j)
+ } else {
+ None
+ }
+ }
+
+ /// Gets a reference to the underlying revolute joint, if `self` is one.
+ #[cfg(feature = "dim3")]
+ pub fn as_revolute_joint(&self) -> Option<&RevoluteJoint> {
+ if let JointParams::RevoluteJoint(j) = self {
+ Some(j)
+ } else {
+ None
+ }
+ }
+}
+
+impl From<BallJoint> for JointParams {
+ fn from(j: BallJoint) -> Self {
+ JointParams::BallJoint(j)
+ }
+}
+
+impl From<FixedJoint> for JointParams {
+ fn from(j: FixedJoint) -> Self {
+ JointParams::FixedJoint(j)
+ }
+}
+
+#[cfg(feature = "dim3")]
+impl From<RevoluteJoint> for JointParams {
+ fn from(j: RevoluteJoint) -> Self {
+ JointParams::RevoluteJoint(j)
+ }
+}
+
+impl From<PrismaticJoint> for JointParams {
+ fn from(j: PrismaticJoint) -> Self {
+ JointParams::PrismaticJoint(j)
+ }
+}
+
+#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
+/// A joint attached to two bodies.
+pub struct Joint {
+ /// Handle to the first body attached to this joint.
+ pub body1: RigidBodyHandle,
+ /// Handle to the second body attached to this joint.
+ pub body2: RigidBodyHandle,
+ // A joint needs to know its handle to simplify its removal.
+ pub(crate) handle: JointHandle,
+ #[cfg(feature = "parallel")]
+ pub(crate) constraint_index: usize,
+ #[cfg(feature = "parallel")]
+ pub(crate) position_constraint_index: usize,
+ /// The joint geometric parameters and impulse.
+ pub params: JointParams,
+}
diff --git a/src/dynamics/joint/joint_set.rs b/src/dynamics/joint/joint_set.rs
new file mode 100644
index 0000000..51d7432
--- /dev/null
+++ b/src/dynamics/joint/joint_set.rs
@@ -0,0 +1,218 @@
+use super::Joint;
+use crate::geometry::{InteractionGraph, RigidBodyGraphIndex, TemporaryInteractionIndex};
+
+use crate::data::arena::{Arena, Index};
+use crate::dynamics::{JointParams, RigidBodyHandle, RigidBodySet};
+
+/// The unique identifier of a joint added to the joint set.
+pub type JointHandle = Index;
+pub(crate) type JointIndex = usize;
+pub(crate) type JointGraphEdge = crate::data::graph::Edge<Joint>;
+
+#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
+/// A set of joints that can be handled by a physics `World`.
+pub struct JointSet {
+ joint_ids: Arena<TemporaryInteractionIndex>, // Map joint handles to edge ids on the graph.
+ joint_graph: InteractionGraph<Joint>,
+}
+
+impl JointSet {
+ /// Creates a new empty set of joints.
+ pub fn new() -> Self {
+ Self {
+ joint_ids: Arena::new(),
+ joint_graph: InteractionGraph::new(),
+ }
+ }
+
+ /// An always-invalid joint handle.
+ pub fn invalid_handle() -> JointHandle {
+ JointHandle::from_raw_parts(crate::INVALID_USIZE, crate::INVALID_U64)
+ }
+
+ /// The number of joints on this set.
+ pub fn len(&self) -> usize {
+ self.joint_graph.graph.edges.len()
+ }
+
+ /// Retrieve the joint graph where edges are joints and nodes are rigid body handles.
+ pub fn joint_graph(&self) -> &InteractionGraph<Joint> {
+ &self.joint_graph
+ }
+
+ /// Is the given joint handle valid?
+ pub fn contains(&self, handle: JointHandle) -> bool {
+ self.joint_ids.contains(handle)
+ }
+
+ /// Gets the joint with the given handle.
+ pub fn get(&self, handle: JointHandle) -> Option<&Joint> {
+ let id = self.joint_ids.get(handle)?;
+ self.joint_graph.graph.edge_weight(*id)
+ }
+
+ /// Gets the joint with the given handle without a known generation.
+ ///
+ /// This is useful when you know you want the joint at position `i` but
+ /// don't know what is its current generation number. Generation numbers are
+ /// used to protect from the ABA problem because the joint position `i`
+ /// are recycled between two insertion and a removal.
+ ///
+ /// Using this is discouraged in favor of `self.get(handle)` which does not
+ /// suffer form the ABA problem.
+ pub fn get_unknown_gen(&self, i: usize) -> Option<(&Joint, JointHandle)> {
+ let (id, handle) = self.joint_ids.get_unknown_gen(i)?;
+ Some((self.joint_graph.graph.edge_weight(*id)?, handle))
+ }
+
+ /// Iterates through all the joint on this set.
+ pub fn iter(&self) -> impl Iterator<Item = &Joint> {
+ self.joint_graph.graph.edges.iter().map(|e| &e.weight)
+ }
+
+ /// Iterates mutably through all the joint on this set.
+ pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Joint> {
+ self.joint_graph
+ .graph
+ .edges
+ .iter_mut()
+ .map(|e| &mut e.weight)
+ }
+
+ // /// The set of joints as an array.
+ // pub(crate) fn joints(&self) -> &[JointGraphEdge] {
+ // // self.joint_graph
+ // // .graph
+ // // .edges
+ // // .iter_mut()
+ // // .map(|e| &mut e.weight)
+ // }
+
+ #[cfg(not(feature = "parallel"))]
+ pub(crate) fn joints_mut(&mut self) -> &mut [JointGraphEdge] {
+ &mut self.joint_graph.graph.edges[..]
+ }
+
+ #[cfg(feature = "parallel")]
+ pub(crate) fn joints_vec_mut(&mut self) -> &mut Vec<JointGraphEdge> {
+ &mut self.joint_graph.graph.edges
+ }
+
+ /// Inserts a new joint into this set and retrieve its handle.
+ pub fn insert<J>(
+ &mut self,
+ bodies: &mut RigidBodySet,
+ body1: RigidBodyHandle,
+ body2: RigidBodyHandle,
+ joint_params: J,
+ ) -> JointHandle
+ where
+ J: Into<JointParams>,
+ {
+ let handle = self.joint_ids.insert(0.into());
+ let joint = Joint {
+ body1,
+ body2,
+ handle,
+ #[cfg(feature = "parallel")]
+ constraint_index: 0,
+ #[cfg(feature = "parallel")]
+ position_constraint_index: 0,
+ params: joint_params.into(),
+ };
+
+ let (rb1, rb2) = bodies.get2_mut_internal(joint.body1, joint.body2);
+ let (rb1, rb2) = (
+ rb1.expect("Attempt to attach a joint to a non-existing body."),
+ rb2.expect("Attempt to attach a joint to a non-existing body."),
+ );
+
+ // NOTE: the body won't have a graph index if it does not
+ // have any joint attached.
+ if !InteractionGraph::<Joint>::is_graph_index_valid(rb1.joint_graph_index) {
+ rb1.joint_graph_index = self.joint_graph.graph.add_node(joint.body1);
+ }
+
+ if !InteractionGraph::<Joint>::is_graph_index_valid(rb2.joint_graph_index) {
+ rb2.joint_graph_index = self.joint_graph.graph.add_node(joint.body2);
+ }
+
+ let id = self
+ .joint_graph
+ .add_edge(rb1.joint_graph_index, rb2.joint_graph_index, joint);
+
+ self.joint_ids[handle] = id;
+ handle
+ }
+
+ /// Retrieve all the joints happening between two active bodies.
+ // NOTE: this is very similar to the code from NarrowPhase::select_active_interactions.
+ pub(crate) fn select_active_interactions(
+ &self,
+ bodies: &RigidBodySet,
+ out: &mut Vec<Vec<JointIndex>&