Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rapier 0.20 feature: RevoluteJoint::angle #530

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ and new features. Please have a look at the

- Update to rapier `0.21`.
- Update to nalgebra `0.33`.
- `ImpulseJoint` and `MultibodyJoint` now have a more detailed enum `JointDescription` instead of a `GenericJoint`.
You can still access its inner `GenericJoint` with `.generic_joint()`.

## v0.27.0-rc.1 (18 June 2024)

Expand Down
3 changes: 1 addition & 2 deletions bevy_rapier2d/examples/debug_despawn2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,7 @@ fn spawn_cube(commands: &mut Commands, game: &mut Game) {
block_entities[*i],
RevoluteJointBuilder::new()
.local_anchor1(anchor_1)
.local_anchor2(anchor_2)
.build(),
.local_anchor2(anchor_2),
))
.id();
game.current_cube_joints.push(id);
Expand Down
28 changes: 26 additions & 2 deletions bevy_rapier3d/examples/joints3.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use bevy::prelude::*;
use std::time::Duration;

use bevy::{prelude::*, time::common_conditions::once_after_delay};
use bevy_rapier3d::prelude::*;

fn main() {
Expand All @@ -14,6 +16,11 @@ fn main() {
RapierDebugRenderPlugin::default(),
))
.add_systems(Startup, (setup_graphics, setup_physics))
.add_systems(
Last,
(print_impulse_revolute_joints,)
.run_if(once_after_delay(Duration::from_secs_f32(1f32))),
)
.run();
}

Expand Down Expand Up @@ -134,7 +141,6 @@ fn create_revolute_joints(commands: &mut Commands, origin: Vec3, num: usize) {
RevoluteJointBuilder::new(z).local_anchor2(Vec3::new(0.0, 0.0, -shift)),
RevoluteJointBuilder::new(x).local_anchor2(Vec3::new(shift, 0.0, 0.0)),
];

commands
.entity(handles[0])
.insert(ImpulseJoint::new(curr_parent, revs[0]));
Expand Down Expand Up @@ -276,3 +282,21 @@ pub fn setup_physics(mut commands: Commands) {
create_rope_joints(&mut commands, Vec3::new(30.0, 10.0, 0.0), 5);
create_ball_joints(&mut commands, 15);
}

pub fn print_impulse_revolute_joints(
context: Res<RapierContext>,
joints: Query<(Entity, &ImpulseJoint)>,
) {
for (entity, impulse_joint) in joints.iter() {
match &impulse_joint.data {
JointDescription::RevoluteJoint(_revolute_joint) => {
println!(
"angle for {}: {:?}",
entity,
context.angle_for_entity_impulse_revolute_joint(entity),
);
}
_ => {}
}
}
}
15 changes: 12 additions & 3 deletions src/dynamics/fixed_joint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ use crate::dynamics::{GenericJoint, GenericJointBuilder};
use crate::math::{Rot, Vect};
use rapier::dynamics::JointAxesMask;

use super::JointDescription;

#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(transparent)]
/// A fixed joint, locks all relative motion between two bodies.
pub struct FixedJoint {
data: GenericJoint,
/// The underlying joint data.
pub data: GenericJoint,
}

impl Default for FixedJoint {
Expand Down Expand Up @@ -134,8 +137,14 @@ impl FixedJointBuilder {
}
}

impl From<FixedJointBuilder> for GenericJoint {
fn from(joint: FixedJointBuilder) -> GenericJoint {
impl From<FixedJointBuilder> for JointDescription {
fn from(joint: FixedJointBuilder) -> JointDescription {
joint.0.into()
}
}

impl From<FixedJoint> for JointDescription {
fn from(joint: FixedJoint) -> JointDescription {
JointDescription::FixedJoint(joint)
}
}
92 changes: 83 additions & 9 deletions src/dynamics/joint.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,73 @@
use crate::dynamics::GenericJoint;
use bevy::prelude::*;
use rapier::dynamics::{ImpulseJointHandle, MultibodyJointHandle};

pub use rapier::dynamics::{JointAxesMask, JointAxis, MotorModel};

use super::{FixedJoint, GenericJoint, PrismaticJoint, RevoluteJoint, RopeJoint, SpringJoint};

#[cfg(feature = "dim3")]
use super::SphericalJoint;

/// Wrapper enum over a specific joint.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum JointDescription {
/// See [`FixedJoint`]
FixedJoint(FixedJoint),
/// See [`GenericJoint`]
GenericJoint(GenericJoint),
/// See [`PrismaticJoint`]
PrismaticJoint(PrismaticJoint),
/// See [`RevoluteJoint`]
RevoluteJoint(RevoluteJoint),
/// See [`RopeJoint`]
RopeJoint(RopeJoint),
/// See [`SphericalJoint`]
#[cfg(feature = "dim3")]
SphericalJoint(SphericalJoint),
/// See [`SpringJoint`]
SpringJoint(SpringJoint),
}

impl JointDescription {
/// The underlying generic joint.
pub fn generic_joint(&self) -> &GenericJoint {
match self {
JointDescription::FixedJoint(j) => &j.data,
JointDescription::GenericJoint(j) => j,
JointDescription::PrismaticJoint(j) => &j.data,
JointDescription::RevoluteJoint(j) => j.data(),
JointDescription::RopeJoint(j) => j.data(),
#[cfg(feature = "dim3")]
JointDescription::SphericalJoint(j) => j.data(),
JointDescription::SpringJoint(j) => j.data(),
}
}
/// The underlying generic joint.
pub fn generic_joint_mut(&mut self) -> &mut GenericJoint {
match self {
JointDescription::FixedJoint(ref mut j) => &mut j.data,
JointDescription::GenericJoint(ref mut j) => j,
JointDescription::PrismaticJoint(ref mut j) => &mut j.data,
JointDescription::RevoluteJoint(ref mut j) => &mut j.data,
JointDescription::RopeJoint(ref mut j) => &mut j.data,
#[cfg(feature = "dim3")]
JointDescription::SphericalJoint(ref mut j) => &mut j.data,
JointDescription::SpringJoint(ref mut j) => &mut j.data,
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These could be AsRef/AsMut implementations instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true, I'll do that.

For the story behind this explicit naming: ideally I would have liked to wrap our joints (bevy_rapier::FixedJoint, etc...) around the correct joint from rapier (rapier::FixedJoint, etc.), so a user could use the relevant functions from rapier if some functions were not exposed by bevy_rapier (it can easily be forgotten...), but I had difficulties, probably the builder had to be updated significantly so I dropped the idea.

See #529 for similar reasoning logic.


/// A trait used for different constraint types applied to joints,
/// see [`ImpulseJoint`] and [`MultibodyJoint`].
pub trait JointConstraint {
/// The entity containing the rigid-body used as the first endpoint of this joint.
fn parent(&self) -> Entity;
/// Access the joint’s description.
fn data(&self) -> &JointDescription;
/// Access mutably the joint’s description.
fn data_mut(&mut self) -> &mut JointDescription;
}
Vrixyz marked this conversation as resolved.
Show resolved Hide resolved

/// The handle of an impulse joint added to the physics scene.
#[derive(Copy, Clone, Debug, Component)]
pub struct RapierImpulseJointHandle(pub ImpulseJointHandle);
Expand All @@ -28,12 +92,12 @@ pub struct ImpulseJoint {
/// The entity containing the rigid-body used as the first endpoint of this joint.
pub parent: Entity,
/// The joint’s description.
pub data: GenericJoint,
pub data: JointDescription,
}

impl ImpulseJoint {
/// Initializes an impulse-based joint from its first endpoint and the joint description.
pub fn new(parent: Entity, data: impl Into<GenericJoint>) -> Self {
pub fn new(parent: Entity, data: impl Into<JointDescription>) -> Self {
Self {
parent,
data: data.into(),
Expand All @@ -55,16 +119,26 @@ pub struct MultibodyJoint {
/// The entity containing the rigid-body used as the first endpoint of this joint.
pub parent: Entity,
/// The joint’s description.
pub data: GenericJoint,
pub data: JointDescription,
}

impl MultibodyJoint {
/// Initializes an joint based on reduced coordinates from its first endpoint and
/// the joint description.
pub fn new(parent: Entity, data: impl Into<GenericJoint>) -> Self {
Self {
parent,
data: data.into(),
}
pub fn new(parent: Entity, data: JointDescription) -> Self {
Self { parent, data }
}
}
impl JointConstraint for ImpulseJoint {
fn parent(&self) -> Entity {
self.parent
}

fn data(&self) -> &JointDescription {
&self.data
}

fn data_mut(&mut self) -> &mut JointDescription {
&mut self.data
}
}
15 changes: 12 additions & 3 deletions src/dynamics/prismatic_joint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ use crate::dynamics::{GenericJoint, GenericJointBuilder};
use crate::math::{Real, Vect};
use rapier::dynamics::{JointAxesMask, JointAxis, JointLimits, JointMotor, MotorModel};

use super::JointDescription;

#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(transparent)]
/// A prismatic joint, locks all relative motion between two bodies except for translation along the joint’s principal axis.
pub struct PrismaticJoint {
data: GenericJoint,
/// The underlying joint data.
pub data: GenericJoint,
}

impl PrismaticJoint {
Expand Down Expand Up @@ -253,8 +256,14 @@ impl PrismaticJointBuilder {
}
}

impl From<PrismaticJointBuilder> for GenericJoint {
fn from(joint: PrismaticJointBuilder) -> GenericJoint {
impl From<PrismaticJointBuilder> for JointDescription {
fn from(joint: PrismaticJointBuilder) -> JointDescription {
joint.0.into()
}
}

impl From<PrismaticJoint> for JointDescription {
fn from(joint: PrismaticJoint) -> JointDescription {
JointDescription::PrismaticJoint(joint)
}
}
53 changes: 49 additions & 4 deletions src/dynamics/revolute_joint.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
use crate::dynamics::{GenericJoint, GenericJointBuilder};
use crate::math::{Real, Vect};
use rapier::dynamics::{JointAxesMask, JointAxis, JointLimits, JointMotor, MotorModel};
use crate::plugin::RapierContext;
use bevy::prelude::Entity;
use rapier::dynamics::{
JointAxesMask, JointAxis, JointLimits, JointMotor, MotorModel, RigidBodyHandle, RigidBodySet,
};

use super::JointDescription;

#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(transparent)]
/// A revolute joint, locks all relative motion except for rotation along the joint’s principal axis.
pub struct RevoluteJoint {
data: GenericJoint,
/// The underlying joint data.
pub data: GenericJoint,
Vrixyz marked this conversation as resolved.
Show resolved Hide resolved
}

#[cfg(feature = "dim2")]
Expand Down Expand Up @@ -138,6 +145,38 @@ impl RevoluteJoint {
self.data.set_limits(JointAxis::AngX, limits);
self
}

/// The angle along the free degree of freedom of this revolute joint in `[-π, π]`.
Vrixyz marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Parameters
/// - `bodies` : the rigid body set from [`super::super::RapierContext`]
/// - `body1`: the first rigid-body attached to this revolute joint, obtained through [`rapier::dynamics::ImpulseJoint`] or [`rapier::dynamics::MultibodyJoint`].
/// - `body2`: the second rigid-body attached to this revolute joint, obtained through [`rapier::dynamics::ImpulseJoint`] or [`rapier::dynamics::MultibodyJoint`].
pub fn angle_from_handles(
&self,
bodies: &RigidBodySet,
body1: RigidBodyHandle,
body2: RigidBodyHandle,
) -> f32 {
// NOTE: unwrap will always succeed since `Self` is known to be a revolute joint.
let joint = self.data.raw.as_revolute().unwrap();

let rb1 = &bodies[body1];
let rb2 = &bodies[body2];
joint.angle(rb1.rotation(), rb2.rotation())
}

/// The angle along the free degree of freedom of this revolute joint in `[-π, π]`.
///
/// # Parameters
/// - `bodies` : the rigid body set from [`super::super::RapierContext`]
/// - `body1`: the first rigid-body attached to this revolute joint.
/// - `body2`: the second rigid-body attached to this revolute joint.
pub fn angle(&self, context: &RapierContext, body1: Entity, body2: Entity) -> f32 {
let rb1 = context.entity2body().get(&body1).unwrap();
let rb2 = context.entity2body().get(&body2).unwrap();
self.angle_from_handles(&context.bodies, *rb1, *rb2)
}
}

impl From<RevoluteJoint> for GenericJoint {
Expand Down Expand Up @@ -244,8 +283,14 @@ impl RevoluteJointBuilder {
}
}

impl From<RevoluteJointBuilder> for GenericJoint {
fn from(joint: RevoluteJointBuilder) -> GenericJoint {
impl From<RevoluteJointBuilder> for JointDescription {
fn from(joint: RevoluteJointBuilder) -> JointDescription {
joint.0.into()
}
}

impl From<RevoluteJoint> for JointDescription {
fn from(joint: RevoluteJoint) -> JointDescription {
JointDescription::RevoluteJoint(joint)
}
}
15 changes: 12 additions & 3 deletions src/dynamics/rope_joint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ use crate::dynamics::{GenericJoint, GenericJointBuilder};
use crate::math::{Real, Vect};
use rapier::dynamics::{JointAxesMask, JointAxis, JointLimits, JointMotor, MotorModel};

use super::JointDescription;

#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(transparent)]
/// A rope joint, limits the maximum distance between two bodies
pub struct RopeJoint {
data: GenericJoint,
/// The underlying joint data.
pub data: GenericJoint,
}

impl RopeJoint {
Expand Down Expand Up @@ -262,8 +265,14 @@ impl RopeJointBuilder {
}
}

impl From<RopeJointBuilder> for GenericJoint {
fn from(joint: RopeJointBuilder) -> GenericJoint {
impl From<RopeJointBuilder> for JointDescription {
fn from(joint: RopeJointBuilder) -> JointDescription {
joint.0.into()
}
}

impl From<RopeJoint> for JointDescription {
fn from(joint: RopeJoint) -> JointDescription {
JointDescription::RopeJoint(joint)
}
}
Loading