nightshade 0.8.0

A cross-platform data-oriented game engine.
Documentation
use super::primitives::{default_directional_light, default_point_light, default_spot_light};
use crate::editor::undo::{UndoHistory, UndoableOperation, capture_hierarchy};
use crate::prelude::*;

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum NodeType {
    Empty,
    Camera,
    DirectionalLight,
    PointLight,
    SpotLight,
    Mesh,
    ParticleEmitter,
    Decal,
    WaterPlane,
    GrassRegion,
    #[cfg(feature = "physics")]
    CharacterController,
}

pub struct AddNodeModalResult {
    pub close_modal: bool,
    pub node_to_create: Option<NodeType>,
}

pub fn add_node_modal_ui(
    ui_context: &egui::Context,
    modal_open: &mut bool,
    search: &mut String,
) -> AddNodeModalResult {
    if !*modal_open {
        return AddNodeModalResult {
            close_modal: false,
            node_to_create: None,
        };
    }

    let mut close_modal = false;
    let mut node_to_create: Option<NodeType> = None;

    egui::Window::new("Add Node")
        .open(modal_open)
        .collapsible(false)
        .resizable(false)
        .default_width(250.0)
        .anchor(egui::Align2::CENTER_CENTER, egui::Vec2::ZERO)
        .show(ui_context, |ui| {
            ui.horizontal(|ui| {
                ui.label("Search:");
                ui.text_edit_singleline(search);
            });

            ui.separator();

            let node_types: Vec<(&str, NodeType)> = vec![
                ("Empty Node", NodeType::Empty),
                ("Camera", NodeType::Camera),
                ("Directional Light", NodeType::DirectionalLight),
                ("Point Light", NodeType::PointLight),
                ("Spot Light", NodeType::SpotLight),
                ("Mesh", NodeType::Mesh),
                ("Particle Emitter", NodeType::ParticleEmitter),
                ("Decal", NodeType::Decal),
                ("Water Plane", NodeType::WaterPlane),
                ("Grass Region", NodeType::GrassRegion),
                #[cfg(feature = "physics")]
                ("Character Controller", NodeType::CharacterController),
            ];

            let search_lower = search.to_lowercase();

            egui::ScrollArea::vertical()
                .max_height(300.0)
                .show(ui, |ui| {
                    for (name, node_type) in &node_types {
                        if !search_lower.is_empty() && !name.to_lowercase().contains(&search_lower)
                        {
                            continue;
                        }
                        if ui.selectable_label(false, *name).clicked() {
                            node_to_create = Some(*node_type);
                            close_modal = true;
                        }
                    }
                });
        });

    if !*modal_open {
        close_modal = true;
    }

    AddNodeModalResult {
        close_modal,
        node_to_create,
    }
}

pub fn create_node(
    world: &mut World,
    undo_history: &mut UndoHistory,
    node_type: NodeType,
    parent: Option<Entity>,
) -> Entity {
    let name = match node_type {
        NodeType::Empty => "Node",
        NodeType::Camera => "Camera",
        NodeType::DirectionalLight => "Directional Light",
        NodeType::PointLight => "Point Light",
        NodeType::SpotLight => "Spot Light",
        NodeType::Mesh => "Mesh",
        NodeType::ParticleEmitter => "Particle Emitter",
        NodeType::Decal => "Decal",
        NodeType::WaterPlane => "Water Plane",
        NodeType::GrassRegion => "Grass Region",
        #[cfg(feature = "physics")]
        NodeType::CharacterController => "Character Controller",
    };

    let new_entity = EntityBuilder::new()
        .with_local_transform(LocalTransform::default())
        .with_global_transform(GlobalTransform::default())
        .spawn(world, 1)[0];

    if let Some(parent_entity) = parent {
        world.set_parent(new_entity, Parent(Some(parent_entity)));
    }

    match node_type {
        NodeType::Empty => {}
        NodeType::Camera => {
            world.set_camera(new_entity, Camera::default());
        }
        NodeType::DirectionalLight => {
            world.set_light(new_entity, default_directional_light());
        }
        NodeType::PointLight => {
            world.set_light(new_entity, default_point_light());
        }
        NodeType::SpotLight => {
            world.set_light(new_entity, default_spot_light());
        }
        NodeType::Mesh => {
            world.set_render_mesh(new_entity, RenderMesh::new("Cube"));
        }
        NodeType::ParticleEmitter => {
            use crate::ecs::particles::components::ParticleEmitter;
            world.add_components(new_entity, crate::ecs::world::PARTICLE_EMITTER);
            world.set_particle_emitter(
                new_entity,
                ParticleEmitter::fire(nalgebra_glm::Vec3::zeros()),
            );
        }
        NodeType::Decal => {
            use crate::ecs::decal::Decal;
            world.set_decal(new_entity, Decal::default());
        }
        NodeType::WaterPlane => {
            use crate::ecs::water::Water;
            world.set_water(new_entity, Water::default());
            world.set_render_mesh(new_entity, RenderMesh::new("Plane"));
        }
        NodeType::GrassRegion => {
            use crate::ecs::grass::GrassRegion;
            world.add_components(new_entity, crate::ecs::world::GRASS_REGION);
            world.set_grass_region(new_entity, GrassRegion::default());
        }
        #[cfg(feature = "physics")]
        NodeType::CharacterController => {
            use crate::ecs::physics::components::CharacterControllerComponent;
            world.add_components(new_entity, crate::ecs::world::CHARACTER_CONTROLLER);
            world.set_character_controller(new_entity, CharacterControllerComponent::default());
        }
    }

    world.set_name(new_entity, Name(format!("{} {}", name, new_entity.id)));

    let hierarchy = Box::new(capture_hierarchy(world, new_entity));
    undo_history.push(
        UndoableOperation::EntityCreated {
            hierarchy,
            current_entity: new_entity,
        },
        format!("Create {}", name),
    );

    new_entity
}