use bevy_heavy::ComputeMassProperties3d;
use bevy_math::Isometry3d;
use bevy_math::bounding::{Aabb3d, Bounded3d, BoundingSphere, BoundingVolume};
use bevy_math::primitives::{Capsule3d, Cuboid, Cylinder, Sphere};
use glam::{Quat, Vec3};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub type ModuleId = u16;
pub type MaterialId = u16;
pub type EndEffectorId = u16;
#[non_exhaustive]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct RobotBlueprint {
pub root_module: Option<ModuleId>,
pub modules: HashMap<ModuleId, RobotModule>,
pub joints: Vec<JointDefinition>,
pub end_effectors: Vec<EndEffector>,
}
impl RobotBlueprint {
pub fn new() -> Self {
Self::default()
}
pub fn add_module(&mut self, id: ModuleId, module: RobotModule) {
if self.modules.is_empty() {
self.root_module = Some(id);
}
self.modules.insert(id, module);
}
pub fn add_joint(&mut self, joint: JointDefinition) {
self.joints.push(joint);
}
pub fn add_end_effector(&mut self, ee: EndEffector) {
self.end_effectors.push(ee);
}
pub fn end_effector(&self, id: EndEffectorId) -> Option<&EndEffector> {
self.end_effectors.iter().find(|e| e.id == id)
}
pub fn aabb(&self, rotation: Quat) -> Aabb3d {
let mut combined: Option<Aabb3d> = None;
for module in self.modules.values() {
let (pos, rot) = module.transform;
let isometry = Isometry3d::new(rotation * pos, rotation * rot);
let aabb = module.shape.to_bevy_primitive().aabb_3d(isometry);
combined = Some(match combined {
Some(c) => c.merge(&aabb),
None => aabb,
});
}
combined.unwrap_or(Aabb3d::new(Vec3::ZERO, Vec3::ZERO))
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RobotModule {
pub shape: ShapePrimitive,
pub mass: f32,
pub density: f32,
pub material_id: MaterialId,
pub sensors: Vec<SensorMount>,
pub transform: (Vec3, Quat),
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub enum ShapePrimitive {
Box(Vec3),
Cylinder { radius: f32, height: f32 },
Sphere(f32),
Capsule { radius: f32, height: f32 },
}
#[derive(Clone, Copy, Debug)]
pub enum BevyPrimitive {
Cuboid(Cuboid),
Cylinder(Cylinder),
Sphere(Sphere),
Capsule(Capsule3d),
}
impl ComputeMassProperties3d for BevyPrimitive {
fn mass(&self, density: f32) -> f32 {
match self {
Self::Cuboid(s) => s.mass(density),
Self::Cylinder(s) => s.mass(density),
Self::Sphere(s) => s.mass(density),
Self::Capsule(s) => s.mass(density),
}
}
fn unit_principal_angular_inertia(&self) -> Vec3 {
match self {
Self::Cuboid(s) => s.unit_principal_angular_inertia(),
Self::Cylinder(s) => s.unit_principal_angular_inertia(),
Self::Sphere(s) => s.unit_principal_angular_inertia(),
Self::Capsule(s) => s.unit_principal_angular_inertia(),
}
}
fn center_of_mass(&self) -> Vec3 {
match self {
Self::Cuboid(s) => s.center_of_mass(),
Self::Cylinder(s) => s.center_of_mass(),
Self::Sphere(s) => s.center_of_mass(),
Self::Capsule(s) => s.center_of_mass(),
}
}
}
impl Bounded3d for BevyPrimitive {
fn aabb_3d(&self, isometry: impl Into<Isometry3d>) -> Aabb3d {
match self {
Self::Cuboid(s) => s.aabb_3d(isometry),
Self::Cylinder(s) => s.aabb_3d(isometry),
Self::Sphere(s) => s.aabb_3d(isometry),
Self::Capsule(s) => s.aabb_3d(isometry),
}
}
fn bounding_sphere(&self, isometry: impl Into<Isometry3d>) -> BoundingSphere {
match self {
Self::Cuboid(s) => s.bounding_sphere(isometry),
Self::Cylinder(s) => s.bounding_sphere(isometry),
Self::Sphere(s) => s.bounding_sphere(isometry),
Self::Capsule(s) => s.bounding_sphere(isometry),
}
}
}
impl ShapePrimitive {
pub fn to_bevy_primitive(self) -> BevyPrimitive {
match self {
Self::Box(half_extents) => BevyPrimitive::Cuboid(Cuboid {
half_size: half_extents,
}),
Self::Cylinder { radius, height } => {
BevyPrimitive::Cylinder(Cylinder::new(radius, height))
}
Self::Sphere(r) => BevyPrimitive::Sphere(Sphere::new(r)),
Self::Capsule { radius, height } => {
BevyPrimitive::Capsule(Capsule3d::new(radius, height))
}
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct JointDefinition {
pub parent_id: ModuleId,
pub child_id: ModuleId,
pub anchor_parent: Vec3,
pub anchor_child: Vec3,
pub joint_type: JointType,
pub limits: Vec<AxisLimit>,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub enum JointType {
Fixed,
Hinge { axis: Vec3 },
Ball,
Prismatic { axis: Vec3 },
Screw { axis: Vec3, pitch: f32 },
}
impl JointType {
pub fn axis(&self) -> Option<Vec3> {
match self {
Self::Fixed | Self::Ball => None,
Self::Hinge { axis } | Self::Prismatic { axis } | Self::Screw { axis, .. } => {
Some(*axis)
}
}
}
pub fn kind(&self) -> JointTypeKind {
match self {
Self::Fixed => JointTypeKind::Fixed,
Self::Hinge { .. } => JointTypeKind::Hinge,
Self::Ball => JointTypeKind::Ball,
Self::Prismatic { .. } => JointTypeKind::Prismatic,
Self::Screw { .. } => JointTypeKind::Screw,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum JointTypeKind {
Fixed,
Hinge,
Ball,
Prismatic,
Screw,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct AxisLimit {
pub axis: Vec3,
pub min: f32,
pub max: f32,
pub effort: f32,
pub velocity: f32,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct EndEffector {
pub id: EndEffectorId,
pub module_id: ModuleId,
pub local_position: Vec3,
pub local_rotation: Quat,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SensorMount {
pub sensor_type: SensorType,
pub local_position: Vec3,
pub local_rotation: Quat,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub enum SensorType {
Camera,
Lidar,
Touch,
IMU,
Ultrasonic,
}