use crate::HDR_BYTES;
use crate::ecs::EditorWorld;
use crate::systems::retained_ui;
use crate::systems::shell::EditorShellContext;
use crate::systems::{
atmosphere, camera, grab, graphics, input, light_gizmos, loading, mode, model, perf_test,
picking, project_io, random, render, shell, skeleton_debug, sun,
};
use nightshade::ecs::camera::systems::{camera_controllers_system, pan_orbit_update_transform};
use nightshade::prelude::*;
use nightshade::render::wgpu::rendergraph::RenderGraph;
use nightshade::run::RenderResources;
use nightshade::shell::ShellState;
pub struct Editor {
pub editor_world: EditorWorld,
pub shell: ShellState<EditorShellContext>,
}
impl Default for Editor {
fn default() -> Self {
Self {
editor_world: EditorWorld::default(),
shell: shell::new_shell(),
}
}
}
impl State for Editor {
fn configure_render_graph(
&mut self,
graph: &mut RenderGraph<World>,
device: &wgpu::Device,
_: wgpu::TextureFormat,
resources: RenderResources,
) {
render::configure_graph(graph, device, resources);
}
fn initialize(&mut self, world: &mut World) {
world.resources.window.title = "Nightshade Editor".to_string();
graphics::configure_defaults(world);
load_hdr_skybox(world, HDR_BYTES.to_vec());
capture_procedural_atmosphere_ibl(world, Atmosphere::Sky, 0.0);
sun::spawn_with_shadows(&mut self.editor_world, world);
camera::spawn_default(&mut self.editor_world, world);
project_io::new_project(&mut self.editor_world, world);
const GLTF_DATA: &[u8] = include_bytes!("../assets/gltf/DamagedHelmet.glb");
loading::load_gltf_bytes(&mut self.editor_world, "DamagedHelmet.glb", GLTF_DATA);
loading::preload_browsers(&mut self.editor_world);
retained_ui::init(&mut self.editor_world, world);
}
fn run_systems(&mut self, world: &mut World) {
let grab_active = grab::is_active(&self.editor_world);
if !shell::is_capturing_input(&self.shell) && !input::ui_capturing(world) && !grab_active {
escape_key_exit_system(world);
camera_controllers_system(world);
} else {
pan_orbit_update_transform(world);
}
camera::handle_reset_input(&mut self.editor_world, world);
atmosphere::switch_system(&mut self.editor_world, world);
let mut shortcut_actions: Vec<crate::systems::retained_ui::Action> = Vec::new();
if !grab_active {
input::poll_shortcuts(
&mut self.editor_world.resources.shortcuts,
world,
&mut shortcut_actions,
);
if !shortcut_actions.is_empty() {
self.editor_world
.resources
.ui_interaction
.actions
.extend(shortcut_actions);
}
}
random::poll(&mut self.editor_world);
perf_test::poll(&mut self.editor_world, world);
loading::poll_pending_imports(&mut self.editor_world, world);
loading::poll_browsers(&mut self.editor_world, world);
loading::poll_thumbnails(&mut self.editor_world, world);
loading::poll_fit_frames(&mut self.editor_world, world);
loading::update_top_progress_bar(&self.editor_world, world);
project_io::poll_pending_loads(&mut self.editor_world, world);
project_io::poll_thumbnail_isolation(&mut self.editor_world, world);
let force_resync = std::mem::take(
&mut self
.editor_world
.resources
.writeback_state
.needs_full_resync,
);
crate::scene_writeback::reconcile(
&mut self.editor_world.resources.project,
&mut self.editor_world.resources.editor_scene,
&mut self.editor_world.resources.writeback_state,
world,
force_resync,
);
atmosphere::tick_day_night(&mut self.editor_world, world);
sun::update(&mut self.editor_world, world);
light_gizmos::update(&mut self.editor_world, world);
skeleton_debug::update(&mut self.editor_world, world);
sync_snap_to_engine(&self.editor_world, world);
model::rotate(&mut self.editor_world, world);
let shell_capturing = shell::is_capturing_input(&self.shell);
mode::tick_mode(&mut self.editor_world, world, shell_capturing);
retained_ui::update(&mut self.editor_world, world);
grab::tick(&mut self.editor_world, world);
if !grab::is_active(&self.editor_world) {
picking::update(&mut self.editor_world, world);
} else {
crate::systems::selection::sync_to_engine(&self.editor_world, world);
}
let undo_count_before = self.editor_world.resources.undo.undo_len();
crate::undo::poll_gizmo_drag(
&mut self.editor_world.resources.gizmo_drag,
&mut self.editor_world.resources.undo,
&self.editor_world.resources.editor_scene,
world,
);
if self.editor_world.resources.undo.undo_len() > undo_count_before {
self.editor_world.resources.ui_handles.inspector.dirty = true;
}
shell::run(&mut self.shell, world);
let events = std::mem::take(&mut world.resources.input.events);
let mut dropped_files: Vec<nightshade::ecs::input::resources::DroppedFile> = Vec::new();
for event in events {
match event {
AppEvent::Keyboard { key, state } => {
shell::handle_key(&mut self.shell, world, key, state);
}
AppEvent::FileDropped(file) => {
dropped_files.push(file);
}
#[cfg(not(target_arch = "wasm32"))]
AppEvent::FileDroppedPath(path) => {
loading::handle_dropped_path(&mut self.editor_world, world, &path);
}
_ => {}
}
}
if !dropped_files.is_empty() {
loading::handle_dropped_files(&mut self.editor_world, world, &dropped_files);
}
}
}
fn sync_snap_to_engine(editor_world: &EditorWorld, world: &mut World) {
let snap = &editor_world.resources.snap;
let gizmos = &mut world.resources.user_interface.gizmos;
if snap.enabled {
gizmos.translation_snap = Some(snap.translation_step);
gizmos.rotation_snap_radians = Some(snap.rotation_step_degrees.to_radians());
gizmos.scale_snap = Some(snap.scale_step);
} else {
gizmos.translation_snap = None;
gizmos.rotation_snap_radians = None;
gizmos.scale_snap = None;
}
}