nightshade 0.8.0

A cross-platform data-oriented game engine.
Documentation
use super::{ComponentInspector, InspectorContext};
use crate::prelude::*;

pub struct MeshInspector;

const MESH_TYPES: &[&str] = &["Cube", "Sphere", "Plane", "Cylinder", "Cone", "Torus"];

impl ComponentInspector for MeshInspector {
    fn name(&self) -> &str {
        "Mesh"
    }

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

    fn add_component(&self, world: &mut World, entity: Entity) {
        world.set_render_mesh(entity, RenderMesh::default());
        world.resources.mesh_render_state.mark_entity_added(entity);
    }

    fn remove_component(&self, world: &mut World, entity: Entity) {
        if let Some(render_mesh) = world.get_render_mesh(entity) {
            let mesh_name = render_mesh.name.clone();
            if let Some(&index) = world
                .resources
                .mesh_cache
                .registry
                .name_to_index
                .get(&mesh_name)
            {
                world.resources.mesh_cache.registry.remove_reference(index);
            }
        }
        world
            .resources
            .mesh_render_state
            .mark_entity_removed(entity);
        world.remove_render_mesh(entity);
    }

    fn ui(
        &mut self,
        world: &mut World,
        entity: Entity,
        ui: &mut egui::Ui,
        _context: &mut InspectorContext,
    ) {
        let previous_name = world
            .get_render_mesh(entity)
            .map(|mesh| mesh.name.clone())
            .unwrap_or_default();

        if let Some(mesh) = world.get_render_mesh_mut(entity) {
            ui.label("Mesh Type:");
            let display_name = truncate_mesh_name(&mesh.name, 30);
            egui::ComboBox::from_id_salt("mesh_type")
                .selected_text(&display_name)
                .show_ui(ui, |ui| {
                    for mesh_type in MESH_TYPES {
                        ui.selectable_value(&mut mesh.name, mesh_type.to_string(), *mesh_type);
                    }
                });
        }

        let current_name = world
            .get_render_mesh(entity)
            .map(|mesh| mesh.name.clone())
            .unwrap_or_default();

        if current_name != previous_name {
            if let Some(&index) = world
                .resources
                .mesh_cache
                .registry
                .name_to_index
                .get(&previous_name)
            {
                world.resources.mesh_cache.registry.remove_reference(index);
            }
            if let Some(&index) = world
                .resources
                .mesh_cache
                .registry
                .name_to_index
                .get(&current_name)
            {
                world.resources.mesh_cache.registry.add_reference(index);
            }
            world
                .resources
                .mesh_render_state
                .mark_entity_removed(entity);
            world.resources.mesh_render_state.mark_entity_added(entity);
        }
    }
}

fn truncate_mesh_name(name: &str, max_len: usize) -> String {
    if let Some(filename) = std::path::Path::new(name).file_name() {
        let filename_str = filename.to_string_lossy();
        if filename_str.len() <= max_len {
            return filename_str.to_string();
        }
        return format!("{}...", &filename_str[..max_len.saturating_sub(3)]);
    }
    if name.len() <= max_len {
        return name.to_string();
    }
    format!("{}...", &name[..max_len.saturating_sub(3)])
}