Skip to main content

nightshade_api/
lib.rs

1//! # nightshade-api
2//!
3//! A procedural high level API over the nightshade engine. Write a full 3d
4//! scene or a small game as straight-line code with free functions and plain
5//! data. No trait to implement, no callbacks to wire up, no ECS knowledge
6//! required to get started.
7//!
8//! A spinning cube:
9//!
10//! ```ignore
11//! use nightshade_api::prelude::*;
12//!
13//! fn main() {
14//!     let mut app = open();
15//!     let cube = spawn_cube(&mut app.world, vec3(0.0, 0.5, 0.0));
16//!     while frame(&mut app) {
17//!         let step = delta_time(&app.world);
18//!         rotate(&mut app.world, cube, Vec3::y(), step);
19//!     }
20//! }
21//! ```
22//!
23//! Add to Cargo.toml:
24//!
25//! ```toml
26//! nightshade-api = "0.53"
27//! ```
28//!
29//! ## What you get for free
30//!
31//! [`prelude::open`] gives you a window with a sky, a sun with shadows, a
32//! reference grid, an orbit camera focused on the origin, prototype textures
33//! like `"checkerboard"`, and escape to exit. Every program starts from a lit,
34//! navigable scene. Override any of it with one call: [`prelude::set_background`],
35//! [`prelude::show_grid`], [`prelude::fly_camera`], [`prelude::set_sun`].
36//!
37//! ## The two entry points
38//!
39//! Own the loop (native only). Setup is ordinary code before the loop, state
40//! is ordinary locals across loop iterations:
41//!
42//! ```ignore
43//! let mut app = open();
44//! let mut score = 0;
45//! while frame(&mut app) {
46//!     score += 1;
47//! }
48//! ```
49//!
50//! Or hand the engine the loop with [`run`](fn@crate::prelude::run), which also works on
51//! wasm. Setup returns your state, the update closure receives it back every
52//! frame:
53//!
54//! ```ignore
55//! run(
56//!     |world| spawn_cube(world, vec3(0.0, 0.5, 0.0)),
57//!     |world, cube| {
58//!         let step = delta_time(world);
59//!         rotate(world, *cube, Vec3::y(), step);
60//!     },
61//! )
62//! .unwrap();
63//! ```
64//!
65//! `run` returns a `Result`, so a real `main` returns it. For several per-frame
66//! jobs, the [`run!`](crate::run!) macro takes any number of update systems:
67//!
68//! ```ignore
69//! fn main() -> Result<(), Box<dyn std::error::Error>> {
70//!     run!(setup, handle_input, move_player, check_collisions)
71//! }
72//! ```
73//!
74//! For immediate-mode UI, enable the `egui` feature, call
75//! [`enable_egui`](fn@crate::prelude::enable_egui) once in setup, then draw from
76//! your update closure by pulling the frame's context with
77//! [`egui_context`](fn@crate::prelude::egui_context). No extra closure: it
78//! composes with [`run`](fn@crate::prelude::run) and [`run!`](crate::run!) as is.
79//! `egui` is re-exported from the prelude.
80//!
81//! ```ignore
82//! run(
83//!     |world| { enable_egui(world); spawn_cube(world, vec3(0.0, 0.5, 0.0)) },
84//!     |world, cube| {
85//!         rotate(world, *cube, Vec3::y(), delta_time(world));
86//!         if let Some(ctx) = egui_context(world) {
87//!             egui::Window::new("Inspector").show(&ctx, |ui| ui.label(format!("{cube:?}")));
88//!         }
89//!     },
90//! )
91//! .unwrap();
92//! ```
93//!
94//! ## Vocabulary
95//!
96//! Two verbs carry the lifetime rules. `spawn_` is retained: the thing exists
97//! until you [`prelude::despawn`] it. `draw_` is immediate: visible for
98//! exactly one frame, redraw it every frame you want it on screen.
99//!
100//! - Scene content: [`prelude::spawn_cube`], [`prelude::spawn_sphere`],
101//!   [`prelude::spawn_floor`], [`prelude::spawn_model`], [`prelude::spawn_object`]
102//! - Crowds: [`prelude::spawn_objects`], [`prelude::spawn_instanced`]
103//! - Tags: [`prelude::tag`], [`prelude::tagged`], [`prelude::for_each_tagged`]
104//! - Looks: [`prelude::set_color`], [`prelude::set_metallic_roughness`],
105//!   [`prelude::set_emissive`], [`prelude::set_texture`],
106//!   [`prelude::set_normal_texture`]
107//! - Placement: [`prelude::set_position`], [`prelude::rotate`], [`prelude::set_scale`],
108//!   [`prelude::set_parent`]
109//! - Cameras: [`prelude::orbit_camera`], [`prelude::fly_camera`],
110//!   [`prelude::first_person`], [`prelude::fixed_camera`],
111//!   [`prelude::set_field_of_view`], [`prelude::set_orthographic`]
112//! - Input: [`prelude::key_down`], [`prelude::wasd`], [`prelude::mouse_clicked`],
113//!   [`prelude::clicked_entity`]
114//! - Time: [`prelude::delta_time`], [`prelude::set_time_scale`], [`prelude::pause`],
115//!   [`prelude::Timer`], [`prelude::Stopwatch`]
116//! - Immediate drawing: [`prelude::draw_cube`], [`prelude::draw_sphere`],
117//!   [`prelude::draw_line`]
118//! - Text: [`prelude::spawn_text`], [`prelude::set_text`], [`prelude::spawn_label`]
119//! - UI: [`prelude::spawn_panel`], [`prelude::panel_button`],
120//!   [`prelude::panel_label`], [`prelude::button_clicked`]; plus the full widget
121//!   set: [`prelude::panel_data_grid`], [`prelude::panel_tree_view`],
122//!   [`prelude::panel_property_grid`], [`prelude::panel_color_picker`],
123//!   [`prelude::panel_date_picker`], [`prelude::panel_command_palette`],
124//!   [`prelude::panel_modal`], [`prelude::panel_virtual_list`]
125//! - Animation: [`prelude::play_animation`], [`prelude::play_animation_named`],
126//!   [`prelude::blend_to_animation`], [`prelude::set_animation_speed`]
127//! - Post-processing: [`prelude::set_ssao`], [`prelude::set_ssr`],
128//!   [`prelude::set_tonemap`], [`prelude::set_color_grading`]
129//! - Live physics tuning: [`prelude::set_friction`], [`prelude::set_restitution`],
130//!   [`prelude::set_mass`], [`prelude::set_gravity_scale`]
131//! - Layout containers: [`prelude::panel_row`], [`prelude::panel_grid`],
132//!   [`prelude::panel_scroll`]; world-space UI: [`prelude::spawn_world_panel`]
133//! - Animation extras: [`prelude::add_animation_event`],
134//!   [`prelude::add_animation_layer`], [`prelude::reach_to`], [`prelude::aim_at`]
135//! - Prefabs and undo: [`prelude::make_prefab`], [`prelude::spawn_prefab`],
136//!   [`prelude::UndoStack`]
137//!
138//! ## Reading the scene back
139//!
140//! The setters have readers, which is what a tool that edits a scene rather than
141//! just building one needs. [`prelude::describe_entity`] gathers an entity's
142//! whole editable state, [`prelude::color`] and friends read one field,
143//! [`prelude::scene_tree`] and [`prelude::children`] walk the hierarchy,
144//! [`prelude::list_materials`] reads the shared material registry, and
145//! [`prelude::bounds_of`] with [`prelude::frame_entities`] measure and frame a
146//! selection. [`prelude::save_scene`] and [`prelude::load_scene`] round-trip the
147//! whole world to bytes. Components are added, removed, and snapshotted for undo
148//! by [`prelude::ComponentKind`], the surface the standalone editor is built on.
149//!
150//! ## Commands
151//!
152//! Every call also has a data form. [`prelude::Command`] is a serde enum with
153//! one variant per function, [`prelude::submit_command`] runs one, and
154//! [`prelude::submit_commands`] runs a batch where a later command can name an
155//! entity an earlier one produced with [`prelude::Ref::Result`], so one batch
156//! builds and wires up a scene. The enum is the wire format a binding targets:
157//! build `Command` values, read [`prelude::CommandReply`] back, with the json
158//! schema from [`prelude::command_schema`]. The free functions stay the real
159//! implementations and the dispatch forwards to them.
160//! See the `commands` example and `docs/COMMAND_API.md`.
161//!
162//! ## Dropping down to the engine
163//!
164//! Every function here takes the real engine [`prelude::World`] and bottoms
165//! out in normal nightshade calls. Nothing is hidden behind a wrapper type,
166//! so when a program outgrows the facade you replace one call site at a time.
167//! The full engine is re-exported at [`nightshade`], one path away:
168//!
169//! ```ignore
170//! use nightshade_api::nightshade::prelude::*;
171//! ```
172//!
173//! ## A member world for game components
174//!
175//! A game that outgrows plain entities declares its own components with the
176//! `freecs::dynamic_schema!` macro and registers them as a third member world
177//! of the engine group, sharing entity identity with the render entities. The
178//! engine [`World`](prelude::World) owns rendering and transforms while the
179//! member world holds movement, behavior, and rules on the same entities, so
180//! there is no link component and one recursive despawn tears down both. Push
181//! game state into the engine with ordinary facade calls like
182//! [`set_position`](prelude::set_position); `freecs` comes from the prelude,
183//! so `use nightshade_api::prelude::*` is enough to define and drive the
184//! member world. The `member_world` example builds the whole pattern end to
185//! end.
186//!
187//! ## Web apps
188//!
189//! For a browser app the engine runs in a web worker on an `OffscreenCanvas`
190//! and a Leptos page drives it from the main thread. Both halves of that seam
191//! are features of this crate: `offscreen` is the worker side
192//! (`offscreen::WorkerHostPlugin` hosts the composed `App` in the worker,
193//! owning the canvas, the render loop, and the page conversation, with
194//! `run_offscreen` as the closure form), and `leptos` is the page side (the
195//! `web` module, an engine-free Leptos component library with a worker-backed
196//! `EngineViewport`, usable with `default-features = false`). The shared wire
197//! types are in `wire`. The `template_leptos` and `template_api_leptos`
198//! directories are the working references.
199//!
200//! ## Examples
201//!
202//! The `examples/` directory is the tour. Run one with
203//! `just run-example solar_system` from the repo root, or
204//! `cargo run -r -p nightshade-api --example solar_system`. Every example also
205//! runs in the browser with `just run-example-wasm solar_system`, which serves
206//! it through trunk.
207
208#[cfg(feature = "engine")]
209pub use nightshade;
210
211#[cfg(feature = "engine")]
212mod animate;
213#[cfg(all(feature = "engine", not(target_arch = "wasm32")))]
214mod app;
215#[cfg(feature = "engine")]
216mod appearance;
217#[cfg(feature = "audio")]
218mod audio;
219#[cfg(feature = "engine")]
220mod bounds;
221#[cfg(feature = "engine")]
222mod camera;
223#[cfg(feature = "physics")]
224mod character;
225#[cfg(feature = "engine")]
226mod clock;
227#[cfg(feature = "engine")]
228mod cloth;
229#[cfg(feature = "engine")]
230mod command;
231#[cfg(feature = "engine")]
232mod cutscene;
233#[cfg(feature = "engine")]
234mod decals;
235#[cfg(feature = "engine")]
236mod draw;
237#[cfg(feature = "scripting")]
238mod dynamic_de;
239#[cfg(feature = "protocol")]
240pub mod editor;
241#[cfg(feature = "engine")]
242mod effects;
243#[cfg(feature = "engine")]
244mod environment;
245#[cfg(feature = "engine")]
246mod events;
247#[cfg(feature = "engine")]
248mod filesystem;
249#[cfg(feature = "engine")]
250mod groups;
251#[cfg(feature = "engine")]
252mod hierarchy;
253#[cfg(feature = "engine")]
254mod input;
255#[cfg(feature = "engine")]
256mod inspect;
257#[cfg(feature = "engine")]
258mod lighting;
259#[cfg(feature = "engine")]
260mod materials;
261#[cfg(feature = "engine")]
262mod mesh;
263#[cfg(feature = "engine")]
264mod messaging;
265#[cfg(feature = "engine")]
266mod morph;
267#[cfg(feature = "navmesh")]
268mod navigation;
269#[cfg(all(feature = "net", not(target_arch = "wasm32")))]
270mod net;
271#[cfg(all(target_arch = "wasm32", feature = "offscreen"))]
272pub mod offscreen;
273#[cfg(feature = "engine")]
274mod palette;
275#[cfg(feature = "physics")]
276mod physics;
277#[cfg(feature = "picking")]
278mod picking;
279#[cfg(feature = "engine")]
280mod placement;
281#[cfg(feature = "engine")]
282mod prefab;
283#[cfg(feature = "engine")]
284mod reflect;
285#[cfg(feature = "engine")]
286mod render;
287#[cfg(feature = "engine")]
288mod runner;
289#[cfg(feature = "engine")]
290mod scene;
291#[cfg(feature = "scripting")]
292mod scripting;
293#[cfg(feature = "engine")]
294mod serialize;
295#[cfg(any(feature = "terrain", feature = "grass"))]
296mod terrain;
297#[cfg(feature = "engine")]
298mod text;
299#[cfg(any(feature = "leptos", feature = "offscreen"))]
300pub mod transfer;
301#[cfg(feature = "engine")]
302mod ui;
303#[cfg(feature = "engine")]
304mod undo;
305#[cfg(feature = "leptos")]
306pub mod web;
307#[cfg(feature = "engine")]
308mod window;
309#[cfg(any(feature = "leptos", feature = "offscreen"))]
310pub mod wire;
311#[cfg(feature = "picking")]
312mod world_ui;
313
314#[cfg(feature = "leptos")]
315pub use leptos;
316
317/// Everything in one import.
318///
319/// ```ignore
320/// use nightshade_api::prelude::*;
321/// ```
322///
323/// Pulls in the full api surface plus the engine types it hands you:
324/// [`World`](crate::prelude::World), [`Entity`](crate::prelude::Entity), the math types, [`KeyCode`](crate::prelude::KeyCode), and [`MouseButton`](crate::prelude::MouseButton).
325#[cfg(feature = "engine")]
326pub mod prelude {
327    pub use crate::animate::*;
328    #[cfg(not(target_arch = "wasm32"))]
329    pub use crate::app::{App, Window, frame, open, open_with, render_image};
330    pub use crate::appearance::*;
331    #[cfg(feature = "audio")]
332    pub use crate::audio::*;
333    pub use crate::bounds::*;
334    pub use crate::camera::*;
335    #[cfg(feature = "physics")]
336    pub use crate::character::*;
337    pub use crate::clock::*;
338    pub use crate::cloth::*;
339    pub use crate::command::*;
340    pub use crate::cutscene::*;
341    pub use crate::decals::*;
342    pub use crate::draw::*;
343    pub use crate::effects::*;
344    pub use crate::environment::*;
345    pub use crate::events::*;
346    pub use crate::filesystem::*;
347    pub use crate::groups::*;
348    pub use crate::hierarchy::*;
349    pub use crate::input::*;
350    pub use crate::inspect::*;
351    pub use crate::lighting::*;
352    pub use crate::materials::*;
353    pub use crate::mesh::*;
354    pub use crate::messaging::*;
355    pub use crate::morph::*;
356    #[cfg(feature = "navmesh")]
357    pub use crate::navigation::*;
358    #[cfg(all(feature = "net", not(target_arch = "wasm32")))]
359    pub use crate::net::*;
360    pub use crate::palette::*;
361    #[cfg(feature = "physics")]
362    pub use crate::physics::*;
363    #[cfg(feature = "picking")]
364    pub use crate::picking::*;
365    pub use crate::placement::*;
366    pub use crate::prefab::*;
367    pub use crate::reflect::*;
368    pub use crate::render::*;
369    pub use crate::run;
370    pub use crate::runner::{run, run_scene, systems};
371    pub use crate::scene::*;
372    #[cfg(feature = "scripting")]
373    pub use crate::scripting::*;
374    pub use crate::serialize::*;
375    #[cfg(any(feature = "terrain", feature = "grass"))]
376    pub use crate::terrain::*;
377    pub use crate::text::*;
378    pub use crate::ui::*;
379    pub use crate::undo::*;
380    pub use crate::window::*;
381    #[cfg(feature = "picking")]
382    pub use crate::world_ui::*;
383    pub use nightshade::ecs::world::{CORE, GAME, UI};
384    #[cfg(feature = "navmesh")]
385    pub use nightshade::prelude::RecastNavMeshConfig;
386    pub use nightshade::prelude::freecs;
387    #[cfg(feature = "egui")]
388    pub use nightshade::prelude::{egui, egui_context};
389    pub use nightshade::render::material::Material;
390    pub use nightshade::render::particles::ParticleEmitter;
391
392    #[cfg(feature = "physics")]
393    pub use nightshade::plugins::physics::joints::{
394        FixedJoint, JointAxisDirection, JointHandle, RevoluteJoint, RopeJoint, SpringJoint,
395    };
396    #[cfg(feature = "physics")]
397    pub use nightshade::prelude::{CollisionEvent, RaycastHit};
398    pub use nightshade::prelude::{
399        EasingFunction, Entity, Fog, InstanceTransform, KeyCode, Line, MouseButton, ShadingMode,
400        SplitDirection, TextAlignment, Vec2, Vec3, Vec4, VerticalAlignment, ViewportShading, World,
401        nalgebra_glm, vec2, vec3, vec4,
402    };
403    pub use nightshade::render::config::DepthOfField;
404}