bevy_boxddd 0.2.0

Bevy integration for the boxddd Rust bindings to Box3D
Documentation
use bevy::prelude::Resource;

pub const MIN_HERTZ: f64 = 10.0;
pub const MAX_HERTZ: f64 = 240.0;
pub const DEFAULT_HERTZ: f64 = 60.0;
pub const MIN_SUB_STEPS: i32 = 1;
pub const MAX_SUB_STEPS: i32 = 16;
pub const DEFAULT_SUB_STEPS: i32 = 4;
pub const MIN_QUERY_RAY_LENGTH: f32 = 1.0;
pub const MAX_QUERY_RAY_LENGTH: f32 = 12.0;
pub const DEFAULT_QUERY_RAY_LENGTH: f32 = 6.0;
pub const MIN_QUERY_AABB_HALF_EXTENT: f32 = 0.25;
pub const MAX_QUERY_AABB_HALF_EXTENT: f32 = 3.0;
pub const DEFAULT_QUERY_AABB_HALF_EXTENT: f32 = 1.25;
pub const MIN_QUERY_SHAPE_CAST_LENGTH: f32 = 1.0;
pub const MAX_QUERY_SHAPE_CAST_LENGTH: f32 = 12.0;
pub const DEFAULT_QUERY_SHAPE_CAST_LENGTH: f32 = 6.0;
pub const MIN_QUERY_SHAPE_CAST_RADIUS: f32 = 0.1;
pub const MAX_QUERY_SHAPE_CAST_RADIUS: f32 = 1.0;
pub const DEFAULT_QUERY_SHAPE_CAST_RADIUS: f32 = 0.35;
pub const MIN_QUERY_MOVER_CAST_LENGTH: f32 = 1.0;
pub const MAX_QUERY_MOVER_CAST_LENGTH: f32 = 12.0;
pub const DEFAULT_QUERY_MOVER_CAST_LENGTH: f32 = 6.0;
pub const MIN_MATERIAL_FRICTION: f32 = 0.0;
pub const MAX_MATERIAL_FRICTION: f32 = 2.0;
pub const DEFAULT_MATERIAL_FRICTION: f32 = 0.35;
pub const MIN_MATERIAL_RESTITUTION: f32 = 0.0;
pub const MAX_MATERIAL_RESTITUTION: f32 = 1.0;
pub const DEFAULT_MATERIAL_RESTITUTION: f32 = 0.65;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum DebugDrawPreset {
    Off,
    Shapes,
    ShapesAndJoints,
    Contacts,
    Bounds,
}

impl DebugDrawPreset {
    pub const ALL: [Self; 5] = [
        Self::Off,
        Self::Shapes,
        Self::ShapesAndJoints,
        Self::Contacts,
        Self::Bounds,
    ];

    pub const fn label(self) -> &'static str {
        match self {
            Self::Off => "Off",
            Self::Shapes => "Shapes",
            Self::ShapesAndJoints => "Shapes + joints",
            Self::Contacts => "Contacts",
            Self::Bounds => "Bounds",
        }
    }

    pub const fn is_enabled(self) -> bool {
        !matches!(self, Self::Off)
    }

    pub fn options(self) -> boxddd::DebugDrawOptions {
        let mut options = boxddd::DebugDrawOptions::default();
        options.draw_shapes = false;
        options.draw_joints = false;
        options.draw_joint_extras = false;
        options.draw_bounds = false;
        options.draw_mass = false;
        options.draw_contacts = false;
        options.draw_contact_features = false;
        options.draw_contact_normals = false;
        options.draw_contact_forces = false;
        options.draw_friction_forces = false;
        options.draw_islands = false;

        match self {
            Self::Off => {}
            Self::Shapes => {
                options.draw_shapes = true;
            }
            Self::ShapesAndJoints => {
                options.draw_shapes = true;
                options.draw_joints = true;
                options.draw_joint_extras = true;
            }
            Self::Contacts => {
                options.draw_shapes = true;
                options.draw_contacts = true;
                options.draw_contact_features = true;
                options.draw_contact_normals = true;
                options.draw_contact_forces = true;
                options.draw_friction_forces = true;
            }
            Self::Bounds => {
                options.draw_shapes = true;
                options.draw_bounds = true;
            }
        }

        options
    }

    pub fn toggled(self) -> Self {
        if self.is_enabled() {
            Self::Off
        } else {
            Self::ShapesAndJoints
        }
    }
}

impl Default for DebugDrawPreset {
    fn default() -> Self {
        Self::Off
    }
}

#[derive(Resource, Debug)]
pub struct TestbedState {
    pub scene_index: usize,
    pub scene_switching_enabled: bool,
    pub paused: bool,
    pub debug_preset: DebugDrawPreset,
    pub gravity_enabled: bool,
    pub sleeping_enabled: bool,
    pub warm_starting_enabled: bool,
    pub continuous_enabled: bool,
    pub sub_step_count: i32,
    pub hertz: f64,
    pub query_lab_ray_length: f32,
    pub query_lab_aabb_half_extent: f32,
    pub query_lab_shape_cast_length: f32,
    pub query_lab_shape_cast_radius: f32,
    pub query_lab_mover_cast_length: f32,
    pub material_lab_friction: f32,
    pub material_lab_restitution: f32,
    pub single_step_pending: bool,
    pub single_step_active: bool,
}

impl Default for TestbedState {
    fn default() -> Self {
        Self {
            scene_index: 0,
            scene_switching_enabled: true,
            paused: false,
            debug_preset: DebugDrawPreset::default(),
            gravity_enabled: true,
            sleeping_enabled: true,
            warm_starting_enabled: true,
            continuous_enabled: true,
            sub_step_count: DEFAULT_SUB_STEPS,
            hertz: DEFAULT_HERTZ,
            query_lab_ray_length: DEFAULT_QUERY_RAY_LENGTH,
            query_lab_aabb_half_extent: DEFAULT_QUERY_AABB_HALF_EXTENT,
            query_lab_shape_cast_length: DEFAULT_QUERY_SHAPE_CAST_LENGTH,
            query_lab_shape_cast_radius: DEFAULT_QUERY_SHAPE_CAST_RADIUS,
            query_lab_mover_cast_length: DEFAULT_QUERY_MOVER_CAST_LENGTH,
            material_lab_friction: DEFAULT_MATERIAL_FRICTION,
            material_lab_restitution: DEFAULT_MATERIAL_RESTITUTION,
            single_step_pending: false,
            single_step_active: false,
        }
    }
}

impl TestbedState {
    pub fn launch(scene_index: usize, scene_switching_enabled: bool) -> Self {
        Self {
            scene_index,
            scene_switching_enabled,
            ..Self::default()
        }
    }

    pub fn clamp_controls(&mut self) {
        self.sub_step_count = self.sub_step_count.clamp(MIN_SUB_STEPS, MAX_SUB_STEPS);
        self.hertz = self.hertz.clamp(MIN_HERTZ, MAX_HERTZ);
        self.query_lab_ray_length = self
            .query_lab_ray_length
            .clamp(MIN_QUERY_RAY_LENGTH, MAX_QUERY_RAY_LENGTH);
        self.query_lab_aabb_half_extent = self
            .query_lab_aabb_half_extent
            .clamp(MIN_QUERY_AABB_HALF_EXTENT, MAX_QUERY_AABB_HALF_EXTENT);
        self.query_lab_shape_cast_length = self
            .query_lab_shape_cast_length
            .clamp(MIN_QUERY_SHAPE_CAST_LENGTH, MAX_QUERY_SHAPE_CAST_LENGTH);
        self.query_lab_shape_cast_radius = self
            .query_lab_shape_cast_radius
            .clamp(MIN_QUERY_SHAPE_CAST_RADIUS, MAX_QUERY_SHAPE_CAST_RADIUS);
        self.query_lab_mover_cast_length = self
            .query_lab_mover_cast_length
            .clamp(MIN_QUERY_MOVER_CAST_LENGTH, MAX_QUERY_MOVER_CAST_LENGTH);
        self.material_lab_friction = self
            .material_lab_friction
            .clamp(MIN_MATERIAL_FRICTION, MAX_MATERIAL_FRICTION);
        self.material_lab_restitution = self
            .material_lab_restitution
            .clamp(MIN_MATERIAL_RESTITUTION, MAX_MATERIAL_RESTITUTION);
    }

    pub fn fixed_timestep_seconds(&self) -> f64 {
        1.0 / self.hertz.clamp(MIN_HERTZ, MAX_HERTZ)
    }

    pub fn request_single_step(&mut self) {
        if self.paused {
            self.single_step_pending = true;
        }
    }

    pub fn cancel_single_step(&mut self) {
        self.single_step_pending = false;
        self.single_step_active = false;
    }
}