nightshade 0.8.0

A cross-platform data-oriented game engine.
Documentation
use super::{ComponentInspector, InspectorContext};
use crate::ecs::physics::components::{ColliderComponent, ColliderShape, RigidBodyComponent};
use crate::ecs::physics::types::{LockedAxes, RigidBodyType};
use crate::prelude::*;

pub struct PhysicsInspector;

impl ComponentInspector for PhysicsInspector {
    fn name(&self) -> &str {
        "Physics"
    }

    fn has_component(&self, world: &World, entity: Entity) -> bool {
        world.entity_has_rigid_body(entity)
    }

    fn add_component(&self, world: &mut World, entity: Entity) {
        world.add_components(
            entity,
            crate::ecs::world::RIGID_BODY | crate::ecs::world::COLLIDER,
        );
        world.set_rigid_body(entity, RigidBodyComponent::new_dynamic());
        world.set_collider(entity, ColliderComponent::default());
    }

    fn remove_component(&self, world: &mut World, entity: Entity) {
        world.remove_rigid_body(entity);
        world.remove_collider(entity);
    }

    fn ui(
        &mut self,
        world: &mut World,
        entity: Entity,
        ui: &mut egui::Ui,
        _context: &mut InspectorContext,
    ) {
        rigid_body_ui(world, entity, ui);
        ui.separator();
        collider_ui(world, entity, ui);
    }
}

fn rigid_body_ui(world: &mut World, entity: Entity, ui: &mut egui::Ui) {
    if let Some(rigid_body) = world.get_rigid_body_mut(entity) {
        ui.label(egui::RichText::new("Rigid Body").strong());

        let type_label = match rigid_body.body_type {
            RigidBodyType::Dynamic => "Dynamic",
            RigidBodyType::Fixed => "Fixed",
            RigidBodyType::KinematicPositionBased => "Kinematic (Position)",
            RigidBodyType::KinematicVelocityBased => "Kinematic (Velocity)",
        };
        egui::ComboBox::from_id_salt("rb_type")
            .selected_text(type_label)
            .show_ui(ui, |ui| {
                ui.selectable_value(&mut rigid_body.body_type, RigidBodyType::Dynamic, "Dynamic");
                ui.selectable_value(&mut rigid_body.body_type, RigidBodyType::Fixed, "Fixed");
                ui.selectable_value(
                    &mut rigid_body.body_type,
                    RigidBodyType::KinematicPositionBased,
                    "Kinematic (Position)",
                );
                ui.selectable_value(
                    &mut rigid_body.body_type,
                    RigidBodyType::KinematicVelocityBased,
                    "Kinematic (Velocity)",
                );
            });

        if rigid_body.body_type == RigidBodyType::Dynamic {
            ui.horizontal(|ui| {
                ui.label("Mass:");
                ui.add(
                    egui::DragValue::new(&mut rigid_body.mass)
                        .speed(0.1)
                        .range(0.001..=10000.0),
                );
            });
        }

        ui.separator();
        ui.label(egui::RichText::new("Locked Axes").strong());

        let mut translation_x = rigid_body
            .locked_axes
            .contains(LockedAxes::TRANSLATION_LOCKED_X);
        let mut translation_y = rigid_body
            .locked_axes
            .contains(LockedAxes::TRANSLATION_LOCKED_Y);
        let mut translation_z = rigid_body
            .locked_axes
            .contains(LockedAxes::TRANSLATION_LOCKED_Z);
        let mut rotation_x = rigid_body
            .locked_axes
            .contains(LockedAxes::ROTATION_LOCKED_X);
        let mut rotation_y = rigid_body
            .locked_axes
            .contains(LockedAxes::ROTATION_LOCKED_Y);
        let mut rotation_z = rigid_body
            .locked_axes
            .contains(LockedAxes::ROTATION_LOCKED_Z);

        ui.label("Translation:");
        ui.horizontal(|ui| {
            ui.checkbox(&mut translation_x, "X");
            ui.checkbox(&mut translation_y, "Y");
            ui.checkbox(&mut translation_z, "Z");
        });

        ui.label("Rotation:");
        ui.horizontal(|ui| {
            ui.checkbox(&mut rotation_x, "X");
            ui.checkbox(&mut rotation_y, "Y");
            ui.checkbox(&mut rotation_z, "Z");
        });

        let mut axes = LockedAxes::empty();
        if translation_x {
            axes |= LockedAxes::TRANSLATION_LOCKED_X;
        }
        if translation_y {
            axes |= LockedAxes::TRANSLATION_LOCKED_Y;
        }
        if translation_z {
            axes |= LockedAxes::TRANSLATION_LOCKED_Z;
        }
        if rotation_x {
            axes |= LockedAxes::ROTATION_LOCKED_X;
        }
        if rotation_y {
            axes |= LockedAxes::ROTATION_LOCKED_Y;
        }
        if rotation_z {
            axes |= LockedAxes::ROTATION_LOCKED_Z;
        }
        rigid_body.locked_axes = axes;
    }
}

fn collider_ui(world: &mut World, entity: Entity, ui: &mut egui::Ui) {
    if let Some(collider) = world.get_collider_mut(entity) {
        ui.label(egui::RichText::new("Collider").strong());

        let shape_label = match &collider.shape {
            ColliderShape::Ball { .. } => "Ball",
            ColliderShape::Cuboid { .. } => "Cuboid",
            ColliderShape::Capsule { .. } => "Capsule",
            ColliderShape::Cylinder { .. } => "Cylinder",
            ColliderShape::Cone { .. } => "Cone",
            ColliderShape::TriMesh { .. } => "TriMesh",
            ColliderShape::HeightField { .. } => "HeightField",
            ColliderShape::ConvexMesh { .. } => "ConvexMesh",
        };
        ui.horizontal(|ui| {
            ui.label("Shape:");
            ui.label(shape_label);
        });

        match &mut collider.shape {
            ColliderShape::Ball { radius } => {
                ui.horizontal(|ui| {
                    ui.label("Radius:");
                    ui.add(
                        egui::DragValue::new(radius)
                            .speed(0.01)
                            .range(0.001..=100.0),
                    );
                });
            }
            ColliderShape::Cuboid { hx, hy, hz } => {
                ui.horizontal(|ui| {
                    ui.add(
                        egui::DragValue::new(hx)
                            .speed(0.01)
                            .range(0.001..=100.0)
                            .prefix("X: "),
                    );
                    ui.add(
                        egui::DragValue::new(hy)
                            .speed(0.01)
                            .range(0.001..=100.0)
                            .prefix("Y: "),
                    );
                    ui.add(
                        egui::DragValue::new(hz)
                            .speed(0.01)
                            .range(0.001..=100.0)
                            .prefix("Z: "),
                    );
                });
            }
            ColliderShape::Capsule {
                half_height,
                radius,
            } => {
                ui.horizontal(|ui| {
                    ui.label("Half Height:");
                    ui.add(
                        egui::DragValue::new(half_height)
                            .speed(0.01)
                            .range(0.001..=100.0),
                    );
                });
                ui.horizontal(|ui| {
                    ui.label("Radius:");
                    ui.add(
                        egui::DragValue::new(radius)
                            .speed(0.01)
                            .range(0.001..=100.0),
                    );
                });
            }
            ColliderShape::Cylinder {
                half_height,
                radius,
            } => {
                ui.horizontal(|ui| {
                    ui.label("Half Height:");
                    ui.add(
                        egui::DragValue::new(half_height)
                            .speed(0.01)
                            .range(0.001..=100.0),
                    );
                });
                ui.horizontal(|ui| {
                    ui.label("Radius:");
                    ui.add(
                        egui::DragValue::new(radius)
                            .speed(0.01)
                            .range(0.001..=100.0),
                    );
                });
            }
            ColliderShape::Cone {
                half_height,
                radius,
            } => {
                ui.horizontal(|ui| {
                    ui.label("Half Height:");
                    ui.add(
                        egui::DragValue::new(half_height)
                            .speed(0.01)
                            .range(0.001..=100.0),
                    );
                });
                ui.horizontal(|ui| {
                    ui.label("Radius:");
                    ui.add(
                        egui::DragValue::new(radius)
                            .speed(0.01)
                            .range(0.001..=100.0),
                    );
                });
            }
            _ => {
                ui.label(
                    egui::RichText::new("Shape parameters not editable")
                        .small()
                        .weak(),
                );
            }
        }

        ui.separator();

        ui.horizontal(|ui| {
            ui.label("Friction:");
            ui.add(egui::Slider::new(&mut collider.friction, 0.0..=2.0));
        });

        ui.horizontal(|ui| {
            ui.label("Restitution:");
            ui.add(egui::Slider::new(&mut collider.restitution, 0.0..=1.0));
        });

        ui.horizontal(|ui| {
            ui.label("Sensor:");
            ui.checkbox(&mut collider.is_sensor, "");
        });
    }
}