nightshade 0.53.0

A cross-platform data-oriented game engine.
Documentation
//! ECS world definition and component/resource re-exports.
//!
//! The [`World`] struct is the central data store for all game state, generated by
//! the `freecs::ecs!` macro. It contains:
//!
//! - **Entities**: Created with `spawn_entities(world, flags, count)`, identified by [`Entity`] (u64)
//! - **Components**: Bitflag-selected data (e.g., `LOCAL_TRANSFORM | RENDER_MESH`)
//! - **Resources**: Global singletons accessed via `world.resources`
//!
//! Key resource paths:
//!
//! - `world.resources.window.timing` — frame timing ([`WindowTiming`](resources::WindowTiming))
//! - `world.resources.input` — keyboard/mouse/touch state ([`Input`](resources::Input))
//! - `world.resources.render_settings` — shading and post-process settings ([`RenderSettings`](crate::render::config::RenderSettings))
//! - `world.resources.active_camera` — current camera entity
//! - `world.resources.entities.names` — name-to-entity mapping

pub mod components {
    pub use crate::ecs::animation::components::{
        AnimationChannel, AnimationClip, AnimationInterpolation, AnimationPlayer,
        AnimationProperty, AnimationSampler, AnimationSamplerOutput, AnimationValue,
    };
    pub use crate::ecs::camera::components::{
        Camera, CameraEnvironment, CameraPostProcess, ConstrainedAspect, FogOverride,
        OrthographicCamera, PanOrbitCamera, PerspectiveCamera, Projection, ShadingMode, Smoothing,
        ViewportShading,
    };
    pub use crate::plugins::audio::components::{AudioBus, AudioListener, AudioSource};
    pub use crate::render::bounding_volume::{BoundingVolume, OrientedBoundingBox};
    pub use crate::render::config::ViewportUpdateMode;

    pub use crate::ecs::cloth::components::{Cloth, ClothPinning};
    pub use crate::ecs::decal::components::Decal;
    pub use crate::ecs::input::components::Hovered;
    pub use crate::ecs::light::components::{AreaLightShape, Light, LightType};
    pub use crate::ecs::lines::components::{Line, Lines, MAX_LINES};
    pub use crate::ecs::material::components::{
        MaterialRef, MaterialVariantMapping, MaterialVariants,
    };
    pub use crate::ecs::mesh::components::{InstanceTransform, InstancedMesh, RenderMesh};
    pub use crate::ecs::morph::components::MorphWeights;
    pub use crate::ecs::primitives::CastsShadow;
    pub use crate::ecs::primitives::EasingFunction;
    pub use crate::ecs::primitives::Guid;
    pub use crate::ecs::primitives::Name;
    pub use crate::ecs::primitives::Visibility;
    pub use crate::ecs::primitives::{CameraCullingMask, CullingMask};
    pub use crate::ecs::skin::components::{Joint, Skin};
    pub use crate::ecs::text::components::{
        Text, TextCharacterBackgroundColors, TextCharacterColors, TextProperties,
    };
    pub use crate::ecs::transform::components::{
        ChildOf, GlobalTransform, LocalTransform, Rotation,
    };
    pub use crate::ecs::ui::components::{
        StateTransition, UiButtonData, UiDepthMode, UiLayoutNode, UiLayoutRoot, UiNodeColor,
        UiNodeContent, UiNodeInteraction, UiStateWeights, UiThemeBinding,
    };
    pub use crate::ecs::vfx::components::{Beam, LightningBolt, Trail, VfxAnimator, VfxHandle};
    pub use crate::ecs::water::components::Water;
    pub use crate::plugins::physics::components::{
        CharacterControllerComponent, ColliderComponent, ColliderShape, CollisionListener,
        PhysicsInterpolation, RigidBodyComponent,
    };
    pub use crate::render::material::{AlphaMode, Material};
    pub use crate::render::particles::{
        ColorGradient, EmitterShape, EmitterType, ParticleEmitter, ParticleSystemStats,
        ParticleTextureUpload,
    };
    pub use crate::render::render_layer::RenderLayer;
    pub use crate::render::text_data::{TextAlignment, VerticalAlignment};
}

pub mod resources {
    pub use crate::ecs::asset_state::AssetState;
    pub use crate::ecs::cloth::resources::ClothMeshSync;
    pub use crate::ecs::entity_registry::EntityRegistry;
    pub use crate::ecs::event_bus::resources::{EventBus, Message};
    pub use crate::ecs::graphics::selection::Selection;
    pub use crate::ecs::input::resources::{Input, MouseState};
    pub use crate::ecs::loading::LoadingState;
    pub use crate::ecs::material::resources::MaterialRegistry;
    #[cfg(feature = "assets")]
    pub use crate::ecs::prefab::resources::CachedPrefab;
    pub use crate::ecs::prefab::resources::{AnimationCache, PrefabCache};
    pub use crate::ecs::text::resources::{TextCache, TextState};
    pub use crate::ecs::transform::resources::TransformState;
    pub use crate::ecs::ui::UserInterface;
    pub use crate::ecs::window::resources::{Window, WindowTiming};
    pub use crate::ecs::world::cleanup::CleanupState;
    #[cfg(feature = "audio")]
    pub use crate::plugins::audio::resources::AudioEngine;
    #[cfg(feature = "physics")]
    pub use crate::plugins::physics::resources::PhysicsWorld;
    pub use crate::render::config::ViewportRect;
    pub use crate::render::config::{
        Atmosphere, ColorGrading, ColorGradingPreset, DebugDraw, Fog, GpuBackend, GpuDeviceType,
        GpuProfile, MeshLodChain, MeshLodLevel, RenderSettings, RendererState, TonemapAlgorithm,
        VertexSnap,
    };
    pub use crate::render::mesh_cache::MeshCache;
    pub use crate::render::wgpu::texture_cache::TextureCache;
    pub use crate::render::wind::Wind;
}

pub mod events {
    pub use crate::ecs::event_bus::resources::{InputEvent, KeyState, Message};
    pub use crate::ecs::input::events::AppEvent;
}

pub use crate::ecs::input::events::AppEvent;

pub mod cleanup;
pub mod commands;
pub mod scratch;
pub use commands::{
    CommandQueues, EcsCommand, RenderCommand, cleanup_unused_resources_system,
    despawn_entities_with_cache_cleanup, load_hdr_skybox, load_procedural_textures,
    process_commands_system, set_material_with_textures, spawn_3d_billboard_text_with_properties,
    spawn_3d_text_with_properties, spawn_cone_at, spawn_cube_at, spawn_cylinder_at,
    spawn_instanced_mesh, spawn_instanced_mesh_with_material, spawn_mesh_at, spawn_plane_at,
    spawn_sphere_at, spawn_sun, spawn_torus_at,
};
#[cfg(not(target_arch = "wasm32"))]
pub use commands::{capture_screenshot, capture_screenshot_to_path};
pub use scratch::Scratch;

pub mod animation_systems {
    pub use crate::ecs::animation::systems::{apply_animations, update_animation_players};
}

pub use freecs::Entity;
pub use nalgebra_glm::{Mat4, Quat, Vec2, Vec3, Vec4};

freecs::dynamic_schema! {
    serde pub(crate) fn register_core_components {
            animation_player: crate::ecs::animation::components::AnimationPlayer => ANIMATION_PLAYER,
            audio_listener: crate::plugins::audio::components::AudioListener => AUDIO_LISTENER,
            audio_source: crate::plugins::audio::components::AudioSource => AUDIO_SOURCE,
            bounding_volume: crate::render::bounding_volume::BoundingVolume => BOUNDING_VOLUME,
            camera_culling_mask: crate::ecs::primitives::CameraCullingMask => CAMERA_CULLING_MASK,
            camera_environment: crate::ecs::camera::components::CameraEnvironment => CAMERA_ENVIRONMENT,
            camera_post_process: crate::ecs::camera::components::CameraPostProcess => CAMERA_POST_PROCESS,
            camera: crate::ecs::camera::components::Camera => CAMERA,
            casts_shadow: crate::ecs::primitives::CastsShadow => CASTS_SHADOW,
            character_controller: crate::plugins::physics::components::CharacterControllerComponent => CHARACTER_CONTROLLER,
            cloth: crate::ecs::cloth::components::Cloth => CLOTH,
            collider: crate::plugins::physics::components::ColliderComponent => COLLIDER,
            collision_listener: crate::plugins::physics::components::CollisionListener => COLLISION_LISTENER,
            constrained_aspect: crate::ecs::camera::components::ConstrainedAspect => CONSTRAINED_ASPECT,
            culling_mask: crate::ecs::primitives::CullingMask => CULLING_MASK,
            decal: crate::ecs::decal::components::Decal => DECAL,
            global_transform: crate::ecs::transform::components::GlobalTransform => GLOBAL_TRANSFORM,
            guid: crate::ecs::primitives::Guid => GUID,
            hovered: crate::ecs::input::components::Hovered => HOVERED,
            ignore_parent_scale: crate::ecs::transform::components::IgnoreParentScale => IGNORE_PARENT_SCALE,
            instanced_mesh: crate::ecs::mesh::components::InstancedMesh => INSTANCED_MESH,
            joint: crate::ecs::skin::components::Joint => JOINT,
            light: crate::ecs::light::components::Light => LIGHT,
            lines: crate::ecs::lines::components::Lines => LINES,
            local_transform: crate::ecs::transform::components::LocalTransform => LOCAL_TRANSFORM,
            material_ref: crate::ecs::material::components::MaterialRef => MATERIAL_REF,
            material_variants: crate::ecs::material::components::MaterialVariants => MATERIAL_VARIANTS,
            morph_weights: crate::ecs::morph::components::MorphWeights => MORPH_WEIGHTS,
            name: crate::ecs::primitives::Name => NAME,
            navmesh_agent: crate::plugins::navmesh::components::NavMeshAgent => NAVMESH_AGENT,
            pan_orbit_camera: crate::ecs::camera::components::PanOrbitCamera => PAN_ORBIT_CAMERA,
            child_of: freecs::dynamic::ChildOf => PARENT,
            particle_emitter: crate::render::particles::ParticleEmitter => PARTICLE_EMITTER,
            physics_interpolation: crate::plugins::physics::components::PhysicsInterpolation => PHYSICS_INTERPOLATION,
            prefab_source: crate::ecs::prefab::components::PrefabSource => PREFAB_SOURCE,
            render_layer: crate::render::render_layer::RenderLayer => RENDER_LAYER,
            render_mesh: crate::ecs::mesh::components::RenderMesh => RENDER_MESH,
            rigid_body: crate::plugins::physics::components::RigidBodyComponent => RIGID_BODY,
            rotation: crate::ecs::transform::components::Rotation => ROTATION,
            script: crate::ecs::script::components::Script => SCRIPT,
            skin: crate::ecs::skin::components::Skin => SKIN,
            text_character_background_colors: crate::ecs::text::components::TextCharacterBackgroundColors => TEXT_CHARACTER_BACKGROUND_COLORS,
            text_character_colors: crate::ecs::text::components::TextCharacterColors => TEXT_CHARACTER_COLORS,
            text: crate::ecs::text::components::Text => TEXT,
            third_person_camera: crate::ecs::camera::components::ThirdPersonCamera => THIRD_PERSON_CAMERA,
            viewport_shading: crate::ecs::camera::components::ViewportShading => VIEWPORT_SHADING,
            viewport_update_mode: crate::render::config::ViewportUpdateMode => VIEWPORT_UPDATE_MODE,
            visibility: crate::ecs::primitives::Visibility => VISIBILITY,
            water: crate::ecs::water::components::Water => WATER,
            beam: crate::ecs::vfx::components::Beam => BEAM,
            lightning_bolt: crate::ecs::vfx::components::LightningBolt => LIGHTNING_BOLT,
            trail: crate::ecs::vfx::components::Trail => TRAIL,
            vfx_animator: crate::ecs::vfx::components::VfxAnimator => VFX_ANIMATOR,
    }
}

freecs::dynamic_schema! {
    fn register_ui_components {
            ui_breadcrumb: crate::ecs::ui::components::UiBreadcrumbData => UI_BREADCRUMB,
            ui_button: crate::ecs::ui::components::UiButtonData => UI_BUTTON,
            ui_canvas: crate::ecs::ui::components::UiCanvasData => UI_CANVAS,
            ui_checkbox: crate::ecs::ui::components::UiCheckboxData => UI_CHECKBOX,
            ui_collapsing_header: crate::ecs::ui::components::UiCollapsingHeaderData => UI_COLLAPSING_HEADER,
            ui_color_picker: crate::ecs::ui::components::UiColorPickerData => UI_COLOR_PICKER,
            ui_color_wheel: crate::ecs::ui::components::UiColorWheelData => UI_COLOR_WHEEL,
            ui_command_palette: crate::ecs::ui::components::UiCommandPaletteData => UI_COMMAND_PALETTE,
            ui_context_menu: crate::ecs::ui::components::UiContextMenuData => UI_CONTEXT_MENU,
            ui_data_grid: crate::ecs::ui::components::UiDataGridData => UI_DATA_GRID,
            ui_date_picker: crate::ecs::ui::components::UiDatePickerData => UI_DATE_PICKER,
            ui_drag_source: crate::ecs::ui::components::UiDragSource => UI_DRAG_SOURCE,
            ui_drag_value: crate::ecs::ui::components::UiDragValueData => UI_DRAG_VALUE,
            ui_drop_target: crate::ecs::ui::components::UiDropTarget => UI_DROP_TARGET,
            ui_dropdown: crate::ecs::ui::components::UiDropdownData => UI_DROPDOWN,
            ui_layout_node: crate::ecs::ui::components::UiLayoutNode => UI_LAYOUT_NODE,
            ui_layout_root: crate::ecs::ui::components::UiLayoutRoot => UI_LAYOUT_ROOT,
            ui_menu: crate::ecs::ui::components::UiMenuData => UI_MENU,
            ui_modal_dialog: crate::ecs::ui::components::UiModalDialogData => UI_MODAL_DIALOG,
            ui_multi_select: crate::ecs::ui::components::UiMultiSelectData => UI_MULTI_SELECT,
            ui_node_blended_transform: crate::ecs::ui::components::UiNodeBlendedTransform => UI_NODE_BLENDED_TRANSFORM,
            ui_node_color: crate::ecs::ui::components::UiNodeColor => UI_NODE_COLOR,
            ui_node_content: crate::ecs::ui::components::UiNodeContent => UI_NODE_CONTENT,
            ui_node_effect: crate::ecs::ui::components::UiNodeEffect => UI_NODE_EFFECT,
            ui_node_interaction: crate::ecs::ui::components::UiNodeInteraction => UI_NODE_INTERACTION,
            ui_node_shadow_states: crate::ecs::ui::components::UiNodeShadowStates => UI_NODE_SHADOW_STATES,
            ui_node_shadow: crate::ecs::ui::components::UiNodeShadow => UI_NODE_SHADOW,
            ui_node_transform_states: crate::ecs::ui::components::UiNodeTransformStates => UI_NODE_TRANSFORM_STATES,
            ui_panel: crate::ecs::ui::components::UiPanelData => UI_PANEL,
            ui_progress_bar: crate::ecs::ui::components::UiProgressBarData => UI_PROGRESS_BAR,
            ui_property_grid: crate::ecs::ui::components::UiPropertyGridData => UI_PROPERTY_GRID,
            ui_radio: crate::ecs::ui::components::UiRadioData => UI_RADIO,
            ui_range_slider: crate::ecs::ui::components::UiRangeSliderData => UI_RANGE_SLIDER,
            ui_rich_text_editor: crate::ecs::ui::components::UiRichTextEditorData => UI_RICH_TEXT_EDITOR,
            ui_rich_text: crate::ecs::ui::components::UiRichTextData => UI_RICH_TEXT,
            ui_scroll_area: crate::ecs::ui::components::UiScrollAreaData => UI_SCROLL_AREA,
            ui_selectable_label: crate::ecs::ui::components::UiSelectableLabelData => UI_SELECTABLE_LABEL,
            ui_slider: crate::ecs::ui::components::UiSliderData => UI_SLIDER,
            ui_spinner: crate::ecs::ui::components::UiSpinnerData => UI_SPINNER,
            ui_splitter: crate::ecs::ui::components::UiSplitterData => UI_SPLITTER,
            ui_state_weights: crate::ecs::ui::components::UiStateWeights => UI_STATE_WEIGHTS,
            ui_tab_bar: crate::ecs::ui::components::UiTabBarData => UI_TAB_BAR,
            ui_text_area: crate::ecs::ui::components::UiTextAreaData => UI_TEXT_AREA,
            ui_text_input: crate::ecs::ui::components::UiTextInputData => UI_TEXT_INPUT,
            ui_theme_binding: crate::ecs::ui::components::UiThemeBinding => UI_THEME_BINDING,
            ui_tile_container: crate::ecs::ui::components::UiTileContainerData => UI_TILE_CONTAINER,
            ui_toggle: crate::ecs::ui::components::UiToggleData => UI_TOGGLE,
            ui_tree_node: crate::ecs::ui::components::UiTreeNodeData => UI_TREE_NODE,
            ui_tree_view: crate::ecs::ui::components::UiTreeViewData => UI_TREE_VIEW,
            ui_virtual_list: crate::ecs::ui::components::UiVirtualListData => UI_VIRTUAL_LIST,
    }
}

#[derive(Default)]
pub struct Resources {
    pub active_camera: Option<freecs::Entity>,
    #[cfg(all(
        feature = "assets",
        feature = "file_watcher",
        not(target_arch = "wasm32")
    ))]
    pub asset_watcher: crate::ecs::asset_watcher::AssetWatcher,
    pub assets: crate::ecs::asset_state::AssetState,
    #[cfg(feature = "audio")]
    pub audio: crate::plugins::audio::resources::AudioEngine,
    pub cleanup: crate::ecs::world::cleanup::CleanupState,
    pub cloth_sync: crate::ecs::cloth::resources::ClothMeshSync,
    pub commands: crate::ecs::world::commands::CommandQueues,
    pub cutscene: crate::ecs::cutscene::resources::CutsceneDirector,
    pub component_types: crate::ecs::scene::typed::ComponentTypeRegistry,
    pub entities: crate::ecs::entity_registry::EntityRegistry,
    pub event_bus: crate::ecs::event_bus::resources::EventBus,
    #[cfg(all(feature = "file_watcher", not(target_arch = "wasm32")))]
    pub file_watcher: crate::plugins::file_watcher::FileWatcher,
    pub gpu_picking: crate::ecs::gpu_picking::GpuPicking,
    #[cfg(feature = "grass")]
    pub grass: crate::render::grass_config::GrassSettings,
    pub render_settings: crate::render::config::RenderSettings,
    pub debug_draw: crate::render::config::DebugDraw,
    pub selection: crate::ecs::graphics::selection::Selection,
    pub renderer_state: crate::render::config::RendererState,
    pub input: crate::ecs::input::resources::Input,
    pub is_runtime: bool,
    pub loading: crate::ecs::loading::LoadingState,
    pub mesh_render_state: crate::render::mesh_state::MeshRenderState,
    pub monitors: crate::ecs::window::resources::Monitors,
    #[cfg(feature = "navmesh")]
    pub navmesh: crate::plugins::navmesh::resources::NavMeshWorld,
    #[cfg(feature = "physics")]
    pub physics: crate::plugins::physics::resources::PhysicsWorld,
    #[cfg(feature = "physics")]
    pub picking_world: crate::ecs::picking::resources::PickingWorld,
    pub retained_ui: crate::ecs::ui::resources::RetainedUiState,
    #[cfg(feature = "egui")]
    pub egui: crate::plugins::egui::EguiState,
    pub events: crate::ecs::event::Events,
    pub schedules: crate::schedule::Schedules,
    pub global_scripts: crate::ecs::script::components::GlobalScripts,
    pub scratch: crate::ecs::world::scratch::Scratch,
    #[cfg(all(feature = "steam", not(target_arch = "wasm32")))]
    pub steam: crate::plugins::steam::SteamResources,
    #[cfg(feature = "terrain")]
    pub terrain: crate::render::terrain_config::TerrainSettings,
    #[cfg(feature = "terrain")]
    pub terrain_render: crate::render::terrain_config::TerrainRenderState,
    pub text: crate::ecs::text::resources::TextState,
    pub texture_cache: crate::render::wgpu::texture_cache::TextureCache,
    pub transform_state: crate::ecs::transform::resources::TransformState,
    pub hierarchy: freecs::dynamic::HierarchyIndex,
    pub render_scene_sync: crate::ecs::graphics::scene_sync::RenderSceneSync,
    pub tweens: crate::ecs::tween::Tweens,
    pub ui_image_allocator: crate::ecs::ui::resources::UiImageLayerAllocator,
    pub user_interface: crate::ecs::ui::UserInterface,
    pub wind: crate::render::wind::Wind,
    pub window: crate::ecs::window::resources::Window,
    pub world_id: u64,
}

freecs::dynamic_worlds! {
    pub fn build_engine_ecs {
        CORE => register_core_components,
        UI => register_ui_components,
    }
}

/// Index of an app's game member world in `world.ecs.worlds`, the first
/// slot after the engine's members. Apps register their schema there once
/// at initialize: `world.ecs.add_world_at(GAME, register_game_components())`.
pub const GAME: usize = 2;

/// The engine's ECS: a [`freecs::dynamic::DynEcs`] group with the core and
/// retained-UI member worlds over one shared entity allocator, plus the
/// engine's resources. The group keeps the lifecycle log (handle allocation
/// and death anywhere); each member world keeps its own row-level structural
/// log. Member access is `world.ecs.worlds[CORE]` / `world.ecs.worlds[UI]`;
/// group operations (spawning, despawn broadcast, liveness) are available
/// directly on `World` through deref.
pub struct World {
    pub ecs: freecs::dynamic::DynEcs,
    pub resources: Resources,
}

impl Default for World {
    fn default() -> Self {
        Self {
            ecs: build_engine_ecs(),
            resources: Resources::default(),
        }
    }
}

impl std::ops::Deref for World {
    type Target = freecs::dynamic::DynEcs;

    fn deref(&self) -> &Self::Target {
        &self.ecs
    }
}

impl std::ops::DerefMut for World {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.ecs
    }
}

impl freecs::dynamic::ResourceHost for World {
    fn resource_map_mut(&mut self) -> &mut freecs::dynamic::ResourceMap {
        &mut self.ecs.resources
    }
}

pub use crate::ecs::input::resources::DroppedFile;