nightshade-editor 0.14.2

Interactive map editor for the Nightshade game engine
#[cfg(not(target_arch = "wasm32"))]
use crate::systems::kenney_assets::KenneyBrowser;
use crate::systems::loading::PendingImport;
use crate::systems::polyhaven::PolyhavenBrowser;
use crate::systems::sample_assets::SampleBrowser;
#[cfg(not(target_arch = "wasm32"))]
use crate::systems::sketchfab::SketchfabBrowser;
use nightshade::ecs::scene::{AssetUuid, Scene};
use nightshade::prelude::{Atmosphere, Entity, SharedTextureQueue, Vec2, Vec3};
use std::collections::HashMap;

#[derive(Clone, Copy)]
pub struct ThumbnailSlot {
    pub layer: u32,
    pub uv_min: Vec2,
    pub uv_max: Vec2,
    pub aspect: Vec2,
}

#[derive(Default)]
pub struct PrefabInstanceLinks {
    /// Live entity → original uuid inside the source `.nsprefab`. Set
    /// when an instance is spawned and used by refresh-from-source to
    /// pair live entities with their source `SceneEntity` for override
    /// diffing.
    pub entity_to_source: HashMap<Entity, AssetUuid>,
    /// In-memory copy of every prefab `Scene` the user has imported in
    /// this session, keyed by `header.prefab_id`. Refresh-from-source
    /// uses this as the baseline so the editor doesn't need to touch
    /// disk again after the initial import.
    pub imported_scenes: HashMap<AssetUuid, Scene>,
}

pub struct LoadingState {
    pub model_entities: Vec<Entity>,
    /// Roots spawned by the dev tools menu (lines, mesh stress test,
    /// 3D text, text lattice). Tracked separately from model_entities
    /// so `clear_scene` can drop them without affecting the loaded
    /// glTF tree.
    pub dev_tool_entities: Vec<Entity>,
    pub loaded: bool,
    pub pending_imports: Vec<PendingImport>,
    pub pending_fit_frames: u32,
    pub pending_fit_roots: Vec<Entity>,
    pub current_model_name: Option<String>,
    pub last_dropped_error: Option<String>,
    pub viewer_mode: bool,
    /// Entities the editor temporarily hid for a prefab thumbnail
    /// capture. Restored after the render frame has consumed the
    /// screenshot command, which takes one render pass.
    pub pending_thumbnail_restore: Vec<Entity>,
    pub pending_thumbnail_restore_frames: u32,
}

impl Default for LoadingState {
    fn default() -> Self {
        Self {
            model_entities: Vec::new(),
            dev_tool_entities: Vec::new(),
            loaded: false,
            pending_imports: Vec::new(),
            pending_fit_frames: 0,
            pending_fit_roots: Vec::new(),
            current_model_name: None,
            last_dropped_error: None,
            viewer_mode: true,
            pending_thumbnail_restore: Vec::new(),
            pending_thumbnail_restore_frames: 0,
        }
    }
}

pub struct CameraState {
    pub camera_entity: Option<Entity>,
    pub reset_camera_on_load: bool,
    pub reset_camera_was_pressed: bool,
}

impl Default for CameraState {
    fn default() -> Self {
        Self {
            camera_entity: None,
            reset_camera_on_load: true,
            reset_camera_was_pressed: false,
        }
    }
}

pub struct SunState {
    pub sun_entity: Option<Entity>,
    pub day_night_hour: f32,
    pub last_ibl_hour: f32,
    pub previous_atmosphere: Atmosphere,
    pub left_arrow_was_pressed: bool,
    pub right_arrow_was_pressed: bool,
    pub auto_cycle: bool,
}

impl Default for SunState {
    fn default() -> Self {
        Self {
            sun_entity: None,
            day_night_hour: 12.0,
            last_ibl_hour: 12.0,
            previous_atmosphere: Atmosphere::Sky,
            left_arrow_was_pressed: false,
            right_arrow_was_pressed: false,
            auto_cycle: true,
        }
    }
}

#[derive(Default)]
pub struct RotationState {
    pub rotation_speed: f32,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RandomKind {
    Model,
    Hdri,
    Both,
    HdriAfterModel,
}

pub struct BrowserState {
    pub sample_browser: SampleBrowser,
    pub polyhaven_browser: PolyhavenBrowser,
    #[cfg(not(target_arch = "wasm32"))]
    pub sketchfab_browser: SketchfabBrowser,
    #[cfg(not(target_arch = "wasm32"))]
    pub kenney_browser: KenneyBrowser,
    pub random_resolution: u32,
    pub pending_random: Option<RandomKind>,
    pub thumbnail_queue: SharedTextureQueue,
    pub thumbnail_signature: u64,
    pub thumbnail_slots: HashMap<String, ThumbnailSlot>,
}

impl Default for BrowserState {
    fn default() -> Self {
        Self {
            sample_browser: SampleBrowser::default(),
            polyhaven_browser: PolyhavenBrowser::default(),
            #[cfg(not(target_arch = "wasm32"))]
            sketchfab_browser: SketchfabBrowser::default(),
            #[cfg(not(target_arch = "wasm32"))]
            kenney_browser: KenneyBrowser::default(),
            random_resolution: 2,
            pending_random: None,
            thumbnail_queue: nightshade::prelude::create_shared_queue(),
            thumbnail_signature: 0,
            thumbnail_slots: HashMap::new(),
        }
    }
}

#[derive(Default)]
pub struct UiState {
    pub show_tree: bool,
    pub show_inspector: bool,
    pub show_fps_label: bool,
    pub selected_entity: Option<Entity>,
    pub selected_entities: Vec<Entity>,
}

#[derive(Default)]
pub struct PickingState {
    pub press_position: Option<nightshade::prelude::Vec2>,
    pub is_dragging: bool,
    pub last_pick_leaf: Option<Entity>,
    pub last_pick_root: Option<Entity>,
    pub cycle_depth: usize,
    pub pending_alt: bool,
    pub pending_shift: bool,
}

#[derive(Default)]
pub struct LightGizmoState {
    pub entity: Option<Entity>,
}

pub struct SkeletonDebugState {
    pub enabled: bool,
    pub entity: Option<Entity>,
    pub bone_color: nightshade::prelude::Vec4,
    pub joint_color: nightshade::prelude::Vec4,
    pub joint_size: f32,
}

impl Default for SkeletonDebugState {
    fn default() -> Self {
        Self {
            enabled: false,
            entity: None,
            bone_color: nightshade::prelude::vec4(1.0, 0.65, 0.0, 1.0),
            joint_color: nightshade::prelude::vec4(0.2, 0.7, 1.0, 1.0),
            joint_size: 0.04,
        }
    }
}

pub struct SnapState {
    pub enabled: bool,
    pub translation_step: f32,
    pub rotation_step_degrees: f32,
    pub scale_step: f32,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EditorMode {
    Object,
    Edit { target: Entity },
}

pub struct ModeState {
    pub mode: EditorMode,
    pub tab_was_pressed: bool,
}

impl Default for ModeState {
    fn default() -> Self {
        Self {
            mode: EditorMode::Object,
            tab_was_pressed: false,
        }
    }
}

impl Default for SnapState {
    fn default() -> Self {
        Self {
            enabled: false,
            translation_step: 0.5,
            rotation_step_degrees: 15.0,
            scale_step: 0.1,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GrabAxis {
    X,
    Y,
    Z,
}

impl GrabAxis {
    pub fn unit(self) -> Vec3 {
        match self {
            GrabAxis::X => Vec3::new(1.0, 0.0, 0.0),
            GrabAxis::Y => Vec3::new(0.0, 1.0, 0.0),
            GrabAxis::Z => Vec3::new(0.0, 0.0, 1.0),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GrabConstraint {
    Free,
    Axis(GrabAxis),
    Plane(GrabAxis),
}

#[derive(Clone)]
pub struct GrabEntry {
    pub entity: Entity,
    pub original_local_translation: Vec3,
    pub reference_world_translation: Vec3,
}

#[derive(Clone)]
pub struct GrabSession {
    pub entries: Vec<GrabEntry>,
    pub primary_world_origin: Vec3,
    pub initial_world_hit: Vec3,
    pub plane_normal_world: Vec3,
    pub camera_entity: Entity,
    pub constraint: GrabConstraint,
}

#[derive(Default)]
pub struct GrabState {
    pub session: Option<GrabSession>,
    pub g_was_pressed: bool,
    pub x_was_pressed: bool,
    pub y_was_pressed: bool,
    pub z_was_pressed: bool,
    pub enter_was_pressed: bool,
    pub escape_was_pressed: bool,
    pub left_mouse_was_pressed: bool,
    pub right_mouse_was_pressed: bool,
}