use std::any::type_name;
use bevy_log::warn;
use bevy_rapier3d::prelude::ImpulseJoint;
use bevy_serialization_core::traits::{ChangeChecked, ComponentWrapper};
use bevy_utils::prelude::default;
use rapier3d::{
dynamics::{GenericJoint, JointAxesMask, JointLimits, JointMotor, MotorModel},
na::Isometry3,
};
use bevy_ecs::{component::StorageType, prelude::*, query::QueryData};
use bevy_math::Vec3;
use bevy_reflect::prelude::*;
use bevy_transform::prelude::*;
#[derive(Component, Default, Reflect)]
pub struct JointBounded;
#[derive(Component, Clone, Copy, Default, Reflect)]
pub struct GeometryShiftMarked;
#[derive(Component, Clone, Copy, Default, Reflect)]
pub struct GeometryShifted;
#[derive(Default, PartialEq, Debug, Reflect, Clone)]
pub struct Dynamics {
pub damping: f64,
pub friction: f64,
}
#[derive(Debug, PartialEq, Reflect, Clone)]
pub struct JointLimitWrapper {
pub lower: f64,
pub upper: f64,
pub effort: f64,
pub velocity: f64,
}
impl Default for JointLimitWrapper {
fn default() -> Self {
Self {
lower: f64::MIN,
upper: f64::MAX,
effort: f64::MAX,
velocity: f64::MAX,
}
}
}
#[derive(Component, Reflect)]
#[reflect(Component)]
pub struct JointRecieverFlag {
pub id: String,
}
#[derive(QueryData)]
pub struct Linkage {
entity: Entity,
joint: &'static JointFlag,
}
impl ChangeChecked for Linkage {
type ChangeCheckedComp = JointFlag;
}
#[derive(Component, Reflect)]
#[reflect(Component)]
pub struct LinkFlag {
pub geom_offset: Vec3,
}
impl From<&JointFlag> for GenericJoint {
fn from(value: &JointFlag) -> Self {
let joint_limit = JointLimits {
max: value.joint.limit.upper as f32,
min: value.joint.limit.lower as f32,
impulse: value.joint.limit.velocity as f32,
};
let joint_motors = &value.joint.motors;
Self {
local_frame1: Isometry3 {
translation: value.joint.local_frame1.translation.into(),
rotation: value.joint.local_frame1.rotation.into(),
},
local_frame2: Isometry3 {
translation: value.joint.local_frame2.translation.into(),
rotation: value.joint.local_frame2.rotation.into(),
},
locked_axes: JointAxesMask::from_bits_truncate(value.joint.locked_axes.bits()),
limit_axes: JointAxesMask::from_bits_truncate(value.joint.limit_axes.bits()),
motor_axes: JointAxesMask::from_bits_truncate(value.joint.motor_axes.bits()),
coupled_axes: JointAxesMask::from_bits_truncate(value.joint.coupled_axes.bits()),
limits: [
joint_limit,
joint_limit,
joint_limit,
joint_limit,
joint_limit,
joint_limit,
],
motors: [
(&joint_motors[0]).into(),
(&joint_motors[1]).into(),
(&joint_motors[2]).into(),
(&joint_motors[3]).into(),
(&joint_motors[4]).into(),
(&joint_motors[5]).into(),
],
contacts_enabled: value.joint.contacts_enabled,
enabled: rapier3d::dynamics::JointEnabled::Enabled,
user_data: 0,
}
}
}
impl From<&JointFlag> for ImpulseJoint {
fn from(value: &JointFlag) -> Self {
let joint = GenericJoint::from(value);
let bevy_rapier_joint = bevy_rapier3d::dynamics::GenericJoint { raw: joint };
Self {
parent: value.parent,
data: bevy_rapier3d::prelude::TypedJoint::GenericJoint(bevy_rapier_joint),
}
}
}
impl From<&ImpulseJoint> for JointFlag {
fn from(value: &ImpulseJoint) -> Self {
let joint = match value.data {
bevy_rapier3d::prelude::TypedJoint::FixedJoint(joint) => joint.data.raw,
bevy_rapier3d::prelude::TypedJoint::GenericJoint(joint) => joint.raw,
bevy_rapier3d::prelude::TypedJoint::PrismaticJoint(joint) => joint.data.raw,
bevy_rapier3d::prelude::TypedJoint::RevoluteJoint(joint) => joint.data.raw,
bevy_rapier3d::prelude::TypedJoint::RopeJoint(joint) => joint.data.raw,
bevy_rapier3d::prelude::TypedJoint::SphericalJoint(joint) => joint.data.raw,
bevy_rapier3d::prelude::TypedJoint::SpringJoint(joint) => joint.data.raw,
};
let joint_limit_rapier =
joint
.limits(rapier3d::prelude::JointAxis::AngX)
.unwrap_or(&JointLimits {
min: 99999.0,
max: 99999.0,
impulse: 999999.0,
});
let joint_limit = JointLimitWrapper {
lower: joint_limit_rapier.min as f64,
upper: joint_limit_rapier.max as f64,
effort: Default::default(),
velocity: joint_limit_rapier.impulse as f64,
};
Self {
parent: value.parent,
joint: JointInfo {
limit: joint_limit,
dynamics: Default::default(),
local_frame1: Transform {
translation: joint.local_frame1.translation.into(),
rotation: joint.local_frame1.rotation.into(),
scale: default(),
},
local_frame2: Transform {
translation: joint.local_frame2.translation.into(),
rotation: joint.local_frame2.rotation.into(),
scale: default(),
},
locked_axes: JointAxesMaskWrapper::from_bits_truncate(joint.locked_axes.bits()),
limit_axes: JointAxesMaskWrapper::from_bits_truncate(joint.limit_axes.bits()),
motor_axes: JointAxesMaskWrapper::from_bits_truncate(joint.motor_axes.bits()),
motors: [
(&joint.motors[0]).into(),
(&joint.motors[1]).into(),
(&joint.motors[2]).into(),
(&joint.motors[3]).into(),
(&joint.motors[4]).into(),
(&joint.motors[5]).into(),
],
coupled_axes: JointAxesMaskWrapper::from_bits_truncate(joint.coupled_axes.bits()),
contacts_enabled: joint.contacts_enabled,
enabled: joint.is_enabled(),
},
}
}
}
#[derive(Reflect, Debug, PartialEq, Eq, Clone, Default)]
pub struct JointAxesMaskWrapper(u8);
bitflags::bitflags! {
impl JointAxesMaskWrapper: u8 {
const X = 1 << 0;
const Y = 1 << 1;
const Z = 1 << 2;
const ANG_X = 1 << 3;
const ANG_Y = 1 << 4;
const ANG_Z = 1 << 5;
const LOCKED_REVOLUTE_AXES = Self::X.bits() | Self::Y.bits() | Self::Z.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
const LOCKED_PRISMATIC_AXES = Self::Y.bits()| Self::Z.bits()| Self::ANG_X.bits()| Self::ANG_Y.bits()| Self::ANG_Z.bits();
const LOCKED_FIXED_AXES = Self::X.bits()| Self::Y.bits()| Self::Z.bits()| Self::ANG_X.bits()| Self::ANG_Y.bits()| Self::ANG_Z.bits();
const LOCKED_SPHERICAL_AXES = Self::X.bits()| Self::Y.bits()| Self::Z.bits();
const FREE_REVOLUTE_AXES = Self::ANG_X.bits();
const FREE_PRISMATIC_AXES = Self::X.bits();
const FREE_FIXED_AXES = 0;
const FREE_SPHERICAL_AXES = Self::ANG_X.bits()| Self::ANG_Y.bits()| Self::ANG_Z.bits();
const LIN_AXES = Self::X.bits() | Self::Y.bits() | Self::Z.bits();
const ANG_AXES = Self::ANG_X.bits() | Self::ANG_Y.bits() | Self::ANG_Z.bits();
}
}
#[derive(Debug, PartialEq, Reflect, Clone)]
#[reflect(Component)]
pub struct JointFlag {
pub parent: Entity,
pub joint: JointInfo,
}
impl ComponentWrapper for JointFlag {
type WrapperTarget = ImpulseJoint;
}
#[derive(Default, Debug, PartialEq, Reflect, Clone)]
pub struct JointInfo {
pub limit: JointLimitWrapper,
pub dynamics: Dynamics,
pub local_frame1: Transform,
pub local_frame2: Transform,
pub locked_axes: JointAxesMaskWrapper,
pub limit_axes: JointAxesMaskWrapper,
pub motor_axes: JointAxesMaskWrapper,
pub coupled_axes: JointAxesMaskWrapper,
pub motors: [JointMotorWrapper; 6],
pub contacts_enabled: bool,
pub enabled: bool,
}
impl Component for JointFlag {
const STORAGE_TYPE: StorageType = StorageType::Table;
fn register_component_hooks(_hooks: &mut bevy_ecs::component::ComponentHooks) {
_hooks.on_add(|mut world, e, _| {
let new_trans = {
let comp = match world.entity(e).get::<Self>() {
Some(val) => val,
None => {
warn!("could not get {:#?} on: {:#}", type_name::<Self>(), e);
return;
}
};
let Some(parent_trans) = world.get::<Transform>(comp.parent) else {
warn!("parent {:#?} has no trans?", comp.parent);
return;
};
let new_translation = parent_trans.translation
+ comp.joint.local_frame1.translation
- comp.joint.local_frame2.translation;
let new_result = Transform::from_translation(new_translation)
.with_rotation(parent_trans.rotation);
new_result
};
world.commands().entity(e).insert(new_trans);
});
}
}
#[derive(Reflect, PartialEq, Clone, Debug)]
pub struct JointMotorWrapper {
pub target_vel: f32,
pub target_pos: f32,
pub stiffness: f32,
pub damping: f32,
pub max_force: f32,
pub impulse: f32,
pub model: MotorModelWrapper,
}
impl Default for JointMotorWrapper {
fn default() -> Self {
Self {
max_force: f32::MAX,
target_vel: 0.0,
target_pos: 0.0,
stiffness: 0.0,
damping: 0.0,
impulse: 0.0,
model: MotorModelWrapper::default(),
}
}
}
impl From<&JointMotor> for JointMotorWrapper {
fn from(value: &JointMotor) -> Self {
Self {
target_vel: value.target_vel,
target_pos: value.target_pos,
stiffness: value.stiffness,
damping: value.damping,
max_force: value.max_force,
impulse: value.impulse,
model: (&value.model).into(),
}
}
}
impl From<&JointMotorWrapper> for JointMotor {
fn from(value: &JointMotorWrapper) -> Self {
Self {
target_vel: value.target_vel,
target_pos: value.target_pos,
stiffness: value.stiffness,
damping: value.damping,
max_force: value.max_force,
impulse: value.impulse,
model: (&value.model).into(),
}
}
}
#[derive(Reflect, Clone, Debug, PartialEq, Eq, Default)]
pub enum MotorModelWrapper {
#[default]
AccelerationBased,
ForceBased,
}
impl From<&MotorModel> for MotorModelWrapper {
fn from(value: &MotorModel) -> Self {
match value {
MotorModel::AccelerationBased => Self::AccelerationBased,
MotorModel::ForceBased => Self::ForceBased,
}
}
}
impl From<&MotorModelWrapper> for MotorModel {
fn from(value: &MotorModelWrapper) -> Self {
match value {
MotorModelWrapper::AccelerationBased => Self::AccelerationBased,
MotorModelWrapper::ForceBased => Self::ForceBased,
}
}
}