mirage_engine/lib.rs
1//! An immediate-mode `3D` engine for simple games: implement [`Game`] and
2//! start the engine with [`run`]. See `examples/breakout-game.rs`.
3//!
4//! Coordinates are right-handed with `+Y` up and cameras looking down `−Z`;
5//! time is seconds, and colors are linear `f32`, encoded to sRGB on screen.
6
7#![warn(missing_docs)]
8
9use core::fmt;
10use core::time::Duration;
11
12pub use glam as math;
13
14/// The parallel iterators a game writes, which run on the workers the engine
15/// builds at startup; a game declares no version of its own. Parallel work
16/// is the few games' own, so the engine's prelude carries none of this: a
17/// game that writes it names this module's prelude in a `use` of its own.
18///
19/// A parallel sum over `f32` values adds them in whatever order the workers
20/// finish in, so it reads different bits run to run. A fold over fixed
21/// chunks reads the same bits at any worker count and on either target:
22///
23/// ```
24/// use mirage_engine::rayon::prelude::*;
25///
26/// fn total(values: &[f32]) -> f32 {
27/// values
28/// .par_chunks(1024)
29/// .map(|chunk| chunk.iter().sum::<f32>())
30/// .collect::<Vec<f32>>()
31/// .iter()
32/// .sum()
33/// }
34/// ```
35pub use rayon;
36
37/// The UI toolkit [`FrameContext::ui`] builds with; games never declare a
38/// version of it themselves.
39#[cfg(feature = "ui")]
40pub use egui;
41
42pub use animation::{AnimationStates, Animator, Motion, Progress, Transition};
43pub use assets::{Assets, ReliefData, ShadingData, TextureData};
44pub use camera::{Camera, Lens, Projection, View};
45pub use catalog::Catalog;
46pub use color::Color;
47pub use context::{FrameContext, InitContext, Startup, TickContext};
48pub use holds::{Holds, Seats};
49pub use input::{
50 Axis2Binding, AxisBinding, ButtonAxis, ButtonAxis2, ButtonBinding, Cursor, InputAction,
51 InputActions, InputAxis2Action, InputAxisAction, InputButtonAction, JoystickControl, Key,
52 MouseButton, NoInputActions, NoInputAxes, NoInputAxes2, NoInputButtons, Pad, PadAxis,
53 PointerDelta, Stick, WheelDelta,
54};
55pub use light::{Light, MAX_LIGHTS, MAX_SHADOWS, Spot};
56pub use material::Material;
57pub use mesh::{Clip, Mesh, Meshes, NoClips, NoMeshes, NoParts, Part, Slot};
58pub use mirage_engine_derive::{
59 Catalog, Clip, InputAxis2Action, InputAxisAction, InputButtonAction, Part, Saves, ShaderValues,
60};
61/// A point in time a run reads on either target: `std`'s `Instant` on the
62/// desktop, `web-time`'s in the browser, spelled the same either way.
63/// Required if a game times its own work on both targets.
64///
65/// One `Instant` minus another is a [`Duration`].
66pub use platform::Instant;
67pub use post_effect::{EffectStage, NoPostEffects, PostEffect, PostEffects};
68pub use ray::Ray;
69pub use save::{SaveKey, SaveValue, Saves};
70pub use skybox::{NoSkyboxes, SkyboxData, Skyboxes};
71pub use sound::{MAX_VOICES, NoSounds, SoundCue, SoundData, Sounds};
72pub use surface_style::{DrawPass, NoSurfaceStyles, SurfaceStyle, SurfaceStyles};
73pub use tonemap::Tonemap;
74pub use transform::Transform;
75
76pub use shader_values::ShaderValues;
77
78#[doc(hidden)]
79pub use holds::Sealed;
80#[doc(hidden)]
81pub use post_effect::Declarations as PostEffectDeclarations;
82#[doc(hidden)]
83pub use surface_style::Declarations as SurfaceStyleDeclarations;
84
85use math::UVec2;
86use renderer::{mesh_cache, shadows};
87
88// `#[derive(ShaderValues)]` names the engine by its own name, which the engine
89// is not to itself without this.
90extern crate self as mirage_engine;
91
92/// A game driven by the engine, which owns it for the whole run.
93///
94/// A vocabulary is a type of the game's own, usually an enum, whose values
95/// name everything of one kind the game may use; the contexts are typed by
96/// it. Six of them state what this game may draw, play, read, set its sky,
97/// style, and pass its frame through, and each has an empty one for a game
98/// with none of that kind, so every call in a tick or a frame takes a value
99/// of this game's own vocabulary and no other.
100///
101/// ```
102/// use mirage_engine::prelude::*;
103///
104/// meshes! { enum Shape { Cube } }
105///
106/// struct Hello;
107///
108/// impl Game for Hello {
109/// type Meshes = Shape;
110/// type Sounds = NoSounds;
111/// type InputActions = NoInputActions;
112/// type Skyboxes = NoSkyboxes;
113/// type SurfaceStyles = NoSurfaceStyles;
114/// type PostEffects = NoPostEffects;
115///
116/// fn tick(&mut self, _ctx: &mut TickContext<'_, Self>) {}
117///
118/// fn frame(&mut self, ctx: &mut FrameContext<'_, Self>) {
119/// ctx.draw(Cube.at(Vec3::ZERO));
120/// }
121/// }
122/// ```
123pub trait Game: Sized + 'static {
124 /// The set of every mesh this game draws — the enum [`meshes!`] writes,
125 /// one variant per mesh type — or `()` for a game that draws nothing.
126 ///
127 /// Startup builds every value the set's types catalog, and
128 /// [`FrameContext::draw`] takes a mesh the set holds and no other.
129 type Meshes: Meshes + 'static;
130
131 /// The values naming the sounds this game plays, usually an enum, or
132 /// [`NoSounds`] for a game with none.
133 ///
134 /// Startup builds every value of it, and the contexts are typed by it, so
135 /// [`FrameContext::play`] takes a value of this type and no other.
136 type Sounds: Sounds + 'static;
137
138 /// The three vocabularies naming what the player can do, usually enums,
139 /// or [`NoInputActions`] for a game that reads no input; [`Key`] for a
140 /// prototype that binds each key to itself.
141 ///
142 /// Startup materializes these into the table a player rebinds, and their
143 /// names are what a rebind is kept under. The contexts are typed by the
144 /// set, so [`FrameContext::down`] and every other query take an action
145 /// it seats and no other.
146 type InputActions: InputActions + 'static;
147
148 /// The set of every style this game draws with — the enum
149 /// [`surface_styles!`] writes — or `()` for a game that draws with the
150 /// built-in look alone.
151 ///
152 /// Startup compiles each of them into a pipeline of its own, and stops
153 /// the game where any of their WGSL does not compile.
154 type SurfaceStyles: SurfaceStyles + 'static;
155
156 /// The values naming the skies this game draws and is lit by, usually
157 /// an enum, or [`NoSkyboxes`] for a game under the default sky.
158 ///
159 /// Startup builds every value of it, and the contexts are typed by it,
160 /// so [`FrameContext::set_skybox`] takes a value of this type and no
161 /// other.
162 type Skyboxes: Skyboxes + 'static;
163
164 /// The set of every post effect this game passes its frame through — the
165 /// enum [`post_effects!`] writes — or `()` for a game that draws the
166 /// frame as the post chain leaves it.
167 ///
168 /// The post chain is the passes that take a drawn frame to the window,
169 /// and [`EffectStage`] states where in it an effect runs. Startup
170 /// compiles each effect into a pipeline of its own, and stops the game
171 /// where any of their WGSL does not compile.
172 type PostEffects: PostEffects + 'static;
173
174 /// Moves the simulation by one fixed step.
175 ///
176 /// Called zero or more times per drawn frame, one per
177 /// [`Config::tick_interval`] of elapsed time.
178 fn tick(&mut self, ctx: &mut TickContext<'_, Self>);
179
180 /// Runs the game's logic for one drawn frame, drawing as it goes.
181 fn frame(&mut self, ctx: &mut FrameContext<'_, Self>);
182}
183
184/// Opens the window and runs the game `init` builds until it is closed.
185///
186/// `init` runs once the GPU exists; returning `Err` aborts startup.
187///
188/// Blocks on the desktop; passes control to the browser's event loop and returns
189/// there. Errors that stop startup are reported to the standard error
190/// stream, or to the console and a page overlay in the browser.
191pub fn run<G: Game>(
192 config: Config,
193 init: impl FnOnce(&mut InitContext<'_, G>) -> Result<G, Error> + Send + 'static,
194) {
195 platform::run::<G>(config, init);
196}
197
198/// Presentation settings for a game.
199#[derive(Clone, Debug)]
200pub struct Config {
201 title: String,
202 size: UVec2,
203 tick_interval: Duration,
204 canvas_id: Option<String>,
205 asset_sources: Vec<String>,
206 antialiasing: bool,
207 tonemap: Tonemap,
208 shadow_resolution: u32,
209 mesh_memory: usize,
210 /// What a game set for itself, or nothing to count by whatever the
211 /// platform states.
212 double_click_interval: Option<Duration>,
213}
214
215impl Config {
216 /// The window size used when none is set, in logical pixels.
217 pub const DEFAULT_SIZE: UVec2 = UVec2::new(1280, 720);
218
219 /// The simulation step used when none is set: a sixtieth of a second.
220 pub const DEFAULT_TICK_INTERVAL: Duration = Duration::from_nanos(16_666_667);
221
222 /// A configuration with `title` and the engine's other defaults.
223 pub fn new(title: impl Into<String>) -> Self {
224 Self {
225 title: title.into(),
226 size: Self::DEFAULT_SIZE,
227 tick_interval: Self::DEFAULT_TICK_INTERVAL,
228 double_click_interval: None,
229 canvas_id: None,
230 asset_sources: Vec::new(),
231 antialiasing: true,
232 tonemap: Tonemap::default(),
233 shadow_resolution: shadows::DEFAULT_RESOLUTION,
234 mesh_memory: mesh_cache::DEFAULT_MEMORY,
235 }
236 }
237
238 /// Loads these sources before the game starts, into the [`Assets`] its
239 /// meshes build from.
240 ///
241 /// Sources are file paths on the desktop, relative to the working
242 /// directory the game runs in, and URLs relative to the page in the
243 /// browser. Anything that fails to load, decode, or match a cataloged
244 /// mesh's slots stops startup with an error.
245 #[must_use]
246 pub fn with_assets(mut self, sources: impl IntoIterator<Item = impl Into<String>>) -> Self {
247 self.asset_sources = sources.into_iter().map(Into::into).collect();
248 self
249 }
250
251 /// Requests an initial window size in logical pixels.
252 ///
253 /// The browser ignores it: the canvas fills the page, so the page sizes
254 /// the game.
255 #[must_use]
256 pub fn with_size(mut self, width: u32, height: u32) -> Self {
257 self.size = UVec2::new(width, height);
258 self
259 }
260
261 /// Draws edges smoothly, over four samples per pixel; on by default.
262 ///
263 /// Off draws one sample per pixel, which is less work.
264 #[must_use]
265 pub fn with_antialiasing(mut self, antialiasing: bool) -> Self {
266 self.antialiasing = antialiasing;
267 self
268 }
269
270 /// Sets the curve the frame is drawn to the screen through;
271 /// [`Tonemap::Neutral`] by default.
272 #[must_use]
273 pub fn with_tonemap(mut self, tonemap: Tonemap) -> Self {
274 self.tonemap = tonemap;
275 self
276 }
277
278 /// Sets the side of the depth map a light flagged with
279 /// [`Light::shadow`] draws into, in texels; `2048` by default.
280 ///
281 /// Held within `256..=4096`. A larger map has denser shadow edges and
282 /// costs more memory; each of a point light's six faces is half this
283 /// side. The maps live in arrays that grow to the most a frame ever
284 /// requested and never give it back — casting lights hold the most
285 /// memory they ever took for the whole run.
286 #[must_use]
287 pub fn with_shadow_resolution(mut self, texels: u32) -> Self {
288 // A side below this is not dense enough for a shadow edge, and one
289 // above it costs more than a simple game should spend.
290 self.shadow_resolution =
291 texels.clamp(shadows::SMALLEST_RESOLUTION, shadows::LARGEST_RESOLUTION);
292 self
293 }
294
295 /// Sets the memory the engine keeps built mesh data in, in bytes; `256`
296 /// MB by default.
297 ///
298 /// Past it the engine drops the copies used least and builds them again
299 /// when the game draws those meshes; a game whose meshes fit never
300 /// builds one twice. It bounds the system memory the copies hold, not
301 /// what the meshes take on the GPU. A mesh built through
302 /// [`prepare`](InitContext::prepare) is held like any other: past the cap
303 /// it too can be dropped and built again.
304 #[must_use]
305 pub fn with_mesh_memory(mut self, bytes: usize) -> Self {
306 self.mesh_memory = bytes;
307 self
308 }
309
310 /// Sets the simulated time one [`Game::tick`] covers, held to at least
311 /// `Duration::from_micros(1)`; [`Config::DEFAULT_TICK_INTERVAL`] by
312 /// default.
313 #[must_use]
314 pub fn with_tick_interval(mut self, interval: Duration) -> Self {
315 self.tick_interval = interval.max(time::MIN_TICK_INTERVAL);
316 self
317 }
318
319 /// Sets how long after a press a second one still counts as a double
320 /// click, which [`FrameContext::clicks`] counts by.
321 ///
322 /// Without this the engine counts by what the desktop states, and by
323 /// `400` milliseconds where the desktop states none; no browser states
324 /// one. A windowless session counts by those `400` milliseconds until
325 /// this sets another.
326 #[must_use]
327 pub fn with_double_click_interval(mut self, interval: Duration) -> Self {
328 self.double_click_interval = Some(interval);
329 self
330 }
331
332 /// Draws into the page's `<canvas>` with this id, instead of the one the
333 /// engine creates to fill the viewport.
334 ///
335 /// Ignored outside the browser; startup fails if the page has no such
336 /// canvas. A page of the game's own carries
337 /// `data-mirage-worker="<the worker script>"` on its `<body>` beside that
338 /// canvas, naming the script every worker of the run is started on — the
339 /// game thread and each worker of the pool; startup stops naming the
340 /// attribute where the page carries none.
341 #[must_use]
342 pub fn with_canvas_id(mut self, id: impl Into<String>) -> Self {
343 self.canvas_id = Some(id.into());
344 self
345 }
346
347 /// The window title.
348 pub fn title(&self) -> &str {
349 &self.title
350 }
351
352 /// The requested initial window size, in logical pixels.
353 pub fn size(&self) -> UVec2 {
354 self.size
355 }
356
357 /// Simulated time one [`Game::tick`] covers.
358 pub fn tick_interval(&self) -> Duration {
359 self.tick_interval
360 }
361
362 /// How long after a press a second one still counts as a double click,
363 /// where the game set it: `None` to count by what the platform states.
364 pub(crate) fn double_click_interval(&self) -> Option<Duration> {
365 self.double_click_interval
366 }
367
368 /// The page canvas to draw into, if the game named one.
369 pub fn canvas_id(&self) -> Option<&str> {
370 self.canvas_id.as_deref()
371 }
372
373 /// The asset sources loaded before the game starts, in load order.
374 pub fn asset_sources(&self) -> &[String] {
375 &self.asset_sources
376 }
377
378 /// Whether edges are drawn smoothly.
379 pub fn antialiasing(&self) -> bool {
380 self.antialiasing
381 }
382
383 /// The curve the frame is drawn to the screen through.
384 pub fn tonemap(&self) -> Tonemap {
385 self.tonemap
386 }
387
388 /// The side of one light's depth map, in texels.
389 pub fn shadow_resolution(&self) -> u32 {
390 self.shadow_resolution
391 }
392
393 /// Memory the engine keeps built mesh data in, in bytes.
394 pub fn mesh_memory(&self) -> usize {
395 self.mesh_memory
396 }
397}
398
399/// Something the engine could not do.
400///
401/// Only the edges of the API (startup and asset decoding) produce one, so
402/// code that runs each frame never handles an error.
403#[derive(Clone, Debug)]
404pub struct Error {
405 msg: String,
406}
407
408impl Error {
409 /// An error described by `msg`.
410 pub fn msg(msg: impl Into<String>) -> Self {
411 Self { msg: msg.into() }
412 }
413}
414
415impl fmt::Display for Error {
416 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
417 f.write_str(&self.msg)
418 }
419}
420
421impl std::error::Error for Error {}
422
423mod assets;
424mod camera;
425mod catalog;
426mod color;
427mod context;
428mod gpu;
429mod holds;
430mod light;
431mod material;
432mod overrun;
433mod platform;
434mod renderer;
435mod skybox;
436mod time;
437mod tonemap;
438mod transform;
439mod ui;
440
441pub mod animation;
442pub mod input;
443pub mod mesh;
444pub mod post_effect;
445pub mod ray;
446pub mod save;
447pub mod shader_values;
448pub mod sound;
449pub mod surface_style;
450
451#[cfg(all(feature = "offscreen", not(target_arch = "wasm32")))]
452pub mod headless;
453
454#[doc(hidden)]
455pub mod doctests {
456 //! `compile_fail` checks for what the type system holds; hidden from
457 //! the public docs, run by `cargo test`.
458 //!
459 //! The README's own code is compiled here too, so that it cannot go
460 //! stale: see [`readme`].
461 //!
462 //! A `Catalog` variant with fields:
463 //!
464 //! ```compile_fail
465 //! #[derive(mirage_engine::Catalog, Clone, Eq, Hash, PartialEq)]
466 //! enum Shape {
467 //! Asteroid { seed: u32 },
468 //! }
469 //! ```
470 //!
471 //! A `Catalog` type under two attributes:
472 //!
473 //! ```compile_fail
474 //! #[derive(mirage_engine::Catalog, Hash, PartialEq, Eq, Clone)]
475 //! #[catalog(Self { seed: 0 })]
476 //! #[catalog(Self { seed: 1 })]
477 //! struct Asteroid { seed: u32 }
478 //! ```
479 //!
480 //! A `Catalog` attribute naming no value:
481 //!
482 //! ```compile_fail
483 //! #[derive(mirage_engine::Catalog, Hash, PartialEq, Eq, Clone)]
484 //! #[catalog()]
485 //! struct Asteroid { seed: u32 }
486 //! ```
487 //!
488 //! A `Part` variant with fields:
489 //!
490 //! ```compile_fail
491 //! #[derive(mirage_engine::Part, Clone, Debug, Eq, Hash, PartialEq)]
492 //! enum HullPart {
493 //! Plating { layer: u32 },
494 //! }
495 //! ```
496 //!
497 //! A `Clip` variant with fields:
498 //!
499 //! ```compile_fail
500 //! #[derive(mirage_engine::Clip, Clone, Debug, Eq, Hash, PartialEq)]
501 //! enum Pace {
502 //! Walk { speed: u32 },
503 //! }
504 //! ```
505 //!
506 //! A part of one mesh on a draw of another:
507 //!
508 //! ```compile_fail
509 //! use mirage_engine::prelude::*;
510 //!
511 //! #[derive(Catalog, Clone, Eq, Hash, PartialEq)]
512 //! struct Paddle;
513 //!
514 //! #[derive(Part, Clone, Debug, Eq, Hash, PartialEq)]
515 //! enum PaddlePart {
516 //! Face,
517 //! }
518 //!
519 //! impl Mesh<PaddlePart> for Paddle {
520 //! fn build(&self, assets: &Assets) -> MeshData<PaddlePart> {
521 //! assets.mesh("paddle")
522 //! }
523 //! }
524 //!
525 //! let _ = Cube
526 //! .at::<NoSurfaceStyles>(Vec3::ZERO)
527 //! .material_of(PaddlePart::Face, Material::default());
528 //! ```
529 //!
530 //! A draw of a mesh the game does not name in its
531 //! [`Meshes`](crate::Game::Meshes):
532 //!
533 //! ```compile_fail
534 //! use mirage_engine::prelude::*;
535 //!
536 //! meshes! { enum Shape { Cube } }
537 //!
538 //! struct Spheres;
539 //!
540 //! impl Game for Spheres {
541 //! type Meshes = Shape;
542 //! type Sounds = NoSounds;
543 //! type InputActions = NoInputActions;
544 //! type Skyboxes = NoSkyboxes;
545 //! type SurfaceStyles = NoSurfaceStyles;
546 //! type PostEffects = NoPostEffects;
547 //!
548 //! fn tick(&mut self, _ctx: &mut TickContext<'_, Self>) {}
549 //!
550 //! fn frame(&mut self, ctx: &mut FrameContext<'_, Self>) {
551 //! ctx.draw(Sphere { subdivisions: 1 }.at(Vec3::ZERO));
552 //! }
553 //! }
554 //! ```
555 //!
556 //! A set naming one mesh type twice:
557 //!
558 //! ```compile_fail
559 //! use mirage_engine::prelude::*;
560 //!
561 //! meshes! { enum Shape { Cube, Cube } }
562 //! ```
563 //!
564 //! An action with fields:
565 //!
566 //! ```compile_fail
567 //! #[derive(mirage_engine::InputButtonAction, Clone, Copy)]
568 //! enum Verb {
569 //! Fire { rounds: u32 },
570 //! }
571 //! ```
572 //!
573 //! A save key with fields:
574 //!
575 //! ```compile_fail
576 //! #[derive(mirage_engine::Saves, Clone, Copy)]
577 //! enum Progress {
578 //! Level { number: u32 },
579 //! }
580 //! ```
581 //!
582 //! A sound of a vocabulary the game does not name as its
583 //! [`Sounds`](crate::Game::Sounds):
584 //!
585 //! ```compile_fail
586 //! use mirage_engine::prelude::*;
587 //!
588 //! meshes! { enum Only { Cube } }
589 //!
590 //! #[derive(Catalog, Clone, Eq, Hash, PartialEq)]
591 //! enum Music {
592 //! Theme,
593 //! }
594 //!
595 //! impl Sounds for Music {
596 //! fn build(&self, assets: &Assets) -> SoundData {
597 //! assets.sound("theme")
598 //! }
599 //! }
600 //!
601 //! struct Quiet;
602 //!
603 //! impl Game for Quiet {
604 //! type Meshes = Only;
605 //! type Sounds = NoSounds;
606 //! type InputActions = NoInputActions;
607 //! type Skyboxes = NoSkyboxes;
608 //! type SurfaceStyles = NoSurfaceStyles;
609 //! type PostEffects = NoPostEffects;
610 //!
611 //! fn tick(&mut self, _ctx: &mut TickContext<'_, Self>) {}
612 //!
613 //! fn frame(&mut self, ctx: &mut FrameContext<'_, Self>) {
614 //! ctx.play(Music::Theme);
615 //! }
616 //! }
617 //! ```
618 //!
619 //! A sky of a vocabulary the game does not name as its
620 //! [`Skyboxes`](crate::Game::Skyboxes):
621 //!
622 //! ```compile_fail
623 //! use mirage_engine::prelude::*;
624 //!
625 //! meshes! { enum Only { Cube } }
626 //!
627 //! #[derive(Catalog, Clone, Debug, Eq, Hash, PartialEq)]
628 //! enum Weather {
629 //! Clear,
630 //! Storm,
631 //! }
632 //!
633 //! impl Skyboxes for Weather {
634 //! fn build(&self, assets: &Assets) -> SkyboxData {
635 //! assets.skybox("sky")
636 //! }
637 //! }
638 //!
639 //! struct Outdoors;
640 //!
641 //! impl Game for Outdoors {
642 //! type Meshes = Only;
643 //! type Sounds = NoSounds;
644 //! type InputActions = NoInputActions;
645 //! type Skyboxes = NoSkyboxes;
646 //! type SurfaceStyles = NoSurfaceStyles;
647 //! type PostEffects = NoPostEffects;
648 //!
649 //! fn tick(&mut self, _ctx: &mut TickContext<'_, Self>) {}
650 //!
651 //! fn frame(&mut self, ctx: &mut FrameContext<'_, Self>) {
652 //! ctx.set_skybox(Weather::Storm);
653 //! }
654 //! }
655 //! ```
656 //!
657 //! An action of a vocabulary the game does not name as its
658 //! [`InputActions`](crate::Game::InputActions):
659 //!
660 //! ```compile_fail
661 //! use mirage_engine::prelude::*;
662 //!
663 //! meshes! { enum Only { Cube } }
664 //!
665 //! #[derive(InputButtonAction, Clone, Copy)]
666 //! enum Verb {
667 //! Jump,
668 //! }
669 //!
670 //! impl InputButtonAction for Verb {
671 //! fn bindings(&self) -> Vec<ButtonBinding> {
672 //! vec![Key::Space.into()]
673 //! }
674 //! }
675 //!
676 //! struct Controls;
677 //!
678 //! impl InputActions for Controls {
679 //! type Button = Verb;
680 //! type Axis = NoInputAxes;
681 //! type Axis2 = NoInputAxes2;
682 //! }
683 //!
684 //! struct Jumper;
685 //!
686 //! impl Game for Jumper {
687 //! type Meshes = Only;
688 //! type Sounds = NoSounds;
689 //! type InputActions = Controls;
690 //! type Skyboxes = NoSkyboxes;
691 //! type SurfaceStyles = NoSurfaceStyles;
692 //! type PostEffects = NoPostEffects;
693 //!
694 //! fn tick(&mut self, _ctx: &mut TickContext<'_, Self>) {}
695 //!
696 //! fn frame(&mut self, ctx: &mut FrameContext<'_, Self>) {
697 //! let _ = ctx.pressed(Key::Escape);
698 //! }
699 //! }
700 //! ```
701 //!
702 //! A draw with a style the game does not name in its
703 //! [`SurfaceStyles`](crate::Game::SurfaceStyles):
704 //!
705 //! ```compile_fail
706 //! use mirage_engine::prelude::*;
707 //!
708 //! meshes! { enum Only { Cube } }
709 //!
710 //! #[derive(Default, ShaderValues)]
711 //! struct Water;
712 //!
713 //! impl SurfaceStyle for Water {
714 //! const PASS: DrawPass = DrawPass::Translucent;
715 //! }
716 //!
717 //! #[derive(Default, ShaderValues)]
718 //! struct Foam;
719 //!
720 //! impl SurfaceStyle for Foam {
721 //! const PASS: DrawPass = DrawPass::Translucent;
722 //! }
723 //!
724 //! #[derive(Default, ShaderValues)]
725 //! struct Ripple;
726 //!
727 //! impl SurfaceStyle for Ripple {
728 //! const PASS: DrawPass = DrawPass::Opaque;
729 //! }
730 //!
731 //! surface_styles! { enum Looks { Water, Foam } }
732 //!
733 //! struct Pond;
734 //!
735 //! impl Game for Pond {
736 //! type Meshes = Only;
737 //! type Sounds = NoSounds;
738 //! type InputActions = NoInputActions;
739 //! type Skyboxes = NoSkyboxes;
740 //! type SurfaceStyles = Looks;
741 //! type PostEffects = NoPostEffects;
742 //!
743 //! fn tick(&mut self, _ctx: &mut TickContext<'_, Self>) {}
744 //!
745 //! fn frame(&mut self, ctx: &mut FrameContext<'_, Self>) {
746 //! ctx.draw(Cube.at(Vec3::ZERO).surface_style::<Ripple>());
747 //! }
748 //! }
749 //! ```
750 //!
751 //! The values of a style the game does not name in its
752 //! [`SurfaceStyles`](crate::Game::SurfaceStyles):
753 //!
754 //! ```compile_fail
755 //! use mirage_engine::prelude::*;
756 //!
757 //! meshes! { enum Only { Cube } }
758 //!
759 //! #[derive(Default, ShaderValues)]
760 //! struct Water;
761 //!
762 //! impl SurfaceStyle for Water {
763 //! const PASS: DrawPass = DrawPass::Translucent;
764 //! }
765 //!
766 //! #[derive(Default, ShaderValues)]
767 //! struct Foam;
768 //!
769 //! impl SurfaceStyle for Foam {
770 //! const PASS: DrawPass = DrawPass::Translucent;
771 //! }
772 //!
773 //! #[derive(Default, ShaderValues)]
774 //! struct Ripple;
775 //!
776 //! impl SurfaceStyle for Ripple {
777 //! const PASS: DrawPass = DrawPass::Opaque;
778 //! }
779 //!
780 //! surface_styles! { enum Looks { Water, Foam } }
781 //!
782 //! struct Pond;
783 //!
784 //! impl Game for Pond {
785 //! type Meshes = Only;
786 //! type Sounds = NoSounds;
787 //! type InputActions = NoInputActions;
788 //! type Skyboxes = NoSkyboxes;
789 //! type SurfaceStyles = Looks;
790 //! type PostEffects = NoPostEffects;
791 //!
792 //! fn tick(&mut self, _ctx: &mut TickContext<'_, Self>) {}
793 //!
794 //! fn frame(&mut self, ctx: &mut FrameContext<'_, Self>) {
795 //! ctx.set_surface_style(Ripple);
796 //! }
797 //! }
798 //! ```
799 //!
800 //! A post effect the game does not name in its
801 //! [`PostEffects`](crate::Game::PostEffects):
802 //!
803 //! ```compile_fail
804 //! use mirage_engine::prelude::*;
805 //!
806 //! meshes! { enum Only { Cube } }
807 //!
808 //! #[derive(ShaderValues)]
809 //! struct Vignette;
810 //!
811 //! impl PostEffect for Vignette {
812 //! const STAGE: EffectStage = EffectStage::ToneMapped;
813 //! const SHADER: &'static str = "fn draw(pixel: Pixel) -> vec4<f32> { return pixel.color; }";
814 //! }
815 //!
816 //! #[derive(ShaderValues)]
817 //! struct Scanlines;
818 //!
819 //! impl PostEffect for Scanlines {
820 //! const STAGE: EffectStage = EffectStage::ToneMapped;
821 //! const SHADER: &'static str = "fn draw(pixel: Pixel) -> vec4<f32> { return pixel.color; }";
822 //! }
823 //!
824 //! #[derive(ShaderValues)]
825 //! struct Grain;
826 //!
827 //! impl PostEffect for Grain {
828 //! const STAGE: EffectStage = EffectStage::ToneMapped;
829 //! const SHADER: &'static str = "fn draw(pixel: Pixel) -> vec4<f32> { return pixel.color; }";
830 //! }
831 //!
832 //! post_effects! { enum Look { Vignette, Scanlines } }
833 //!
834 //! struct Space;
835 //!
836 //! impl Game for Space {
837 //! type Meshes = Only;
838 //! type Sounds = NoSounds;
839 //! type InputActions = NoInputActions;
840 //! type Skyboxes = NoSkyboxes;
841 //! type SurfaceStyles = NoSurfaceStyles;
842 //! type PostEffects = Look;
843 //!
844 //! fn tick(&mut self, _ctx: &mut TickContext<'_, Self>) {}
845 //!
846 //! fn frame(&mut self, ctx: &mut FrameContext<'_, Self>) {
847 //! ctx.set_post_effect(Grain);
848 //! }
849 //! }
850 //! ```
851 //!
852 //! A set naming one style twice, and one naming an effect twice:
853 //!
854 //! ```compile_fail
855 //! use mirage_engine::prelude::*;
856 //!
857 //! #[derive(Default, ShaderValues)]
858 //! struct Water;
859 //!
860 //! impl SurfaceStyle for Water {
861 //! const PASS: DrawPass = DrawPass::Translucent;
862 //! }
863 //!
864 //! surface_styles! { enum Looks { Water, Water } }
865 //! ```
866 //!
867 //! ```compile_fail
868 //! use mirage_engine::prelude::*;
869 //!
870 //! #[derive(ShaderValues)]
871 //! struct Grain;
872 //!
873 //! impl PostEffect for Grain {
874 //! const STAGE: EffectStage = EffectStage::ToneMapped;
875 //! const SHADER: &'static str = "fn draw(pixel: Pixel) -> vec4<f32> { return pixel.color; }";
876 //! }
877 //!
878 //! post_effects! { enum Look { Grain, Grain } }
879 //! ```
880 //!
881 //! A value of a kind no shader lane holds:
882 //!
883 //! ```compile_fail
884 //! #[derive(mirage_engine::ShaderValues)]
885 //! struct Ripples {
886 //! steps: bool,
887 //! }
888 //! ```
889 //!
890 //! Values a shader could not read by name:
891 //!
892 //! ```compile_fail
893 //! #[derive(mirage_engine::ShaderValues)]
894 //! struct Ripples(f32);
895 //! ```
896 //!
897 //! A relief, and the same call given a sampler of its own:
898 //!
899 //! ```
900 //! use mirage_engine::{ReliefData, math::UVec2};
901 //!
902 //! ReliefData::rgba8(UVec2::ONE, vec![128, 128, 255, 0]);
903 //! ```
904 //!
905 //! ```compile_fail
906 //! use mirage_engine::{ReliefData, math::UVec2};
907 //!
908 //! ReliefData::rgba8(UVec2::ONE, vec![128, 128, 255, 0]).pixelated();
909 //! ```
910 //!
911 //! A shading map given a sampler of its own:
912 //!
913 //! ```compile_fail
914 //! use mirage_engine::{ShadingData, math::UVec2};
915 //!
916 //! ShadingData::rgba8(UVec2::ONE, vec![255, 255, 255, 255]).pixelated();
917 //! ```
918 //!
919 //! A vocabulary of one kind declaring the bindings of another:
920 //!
921 //! ```compile_fail
922 //! #[derive(mirage_engine::InputAxisAction, Clone, Copy)]
923 //! enum Lever {
924 //! Throttle,
925 //! }
926 //!
927 //! impl mirage_engine::InputButtonAction for Lever {
928 //! fn bindings(&self) -> Vec<mirage_engine::ButtonBinding> {
929 //! Vec::new()
930 //! }
931 //! }
932 //! ```
933 #[doc = include_str!("../README.md")]
934 pub mod readme {}
935}
936
937/// The items of the public API most games use.
938pub mod prelude {
939 #[cfg(feature = "ui")]
940 pub use crate::egui;
941 pub use crate::math::*;
942 pub use crate::mesh::{
943 Cube, Frame, Instance, Mesh, MeshData, Meshes, NoClips, NoMeshes, NoParts, Plane, Quad,
944 Sheet, Slot, Sphere, Vertex,
945 };
946 pub use crate::{
947 AnimationStates, Animator, Assets, Axis2Binding, AxisBinding, ButtonAxis, ButtonAxis2,
948 ButtonBinding, Camera, Catalog, Clip, Color, Config, Cursor, DrawPass, EffectStage, Error,
949 FrameContext, Game, Holds, InitContext, InputAction, InputActions, InputAxis2Action,
950 InputAxisAction, InputButtonAction, Instant, JoystickControl, Key, Light, Material, Motion,
951 MouseButton, NoInputActions, NoInputAxes, NoInputAxes2, NoInputButtons, NoPostEffects,
952 NoSkyboxes, NoSounds, NoSurfaceStyles, Pad, PadAxis, Part, PointerDelta, PostEffect,
953 PostEffects, Progress, Projection, Ray, ReliefData, SaveKey, SaveValue, Saves, Seats,
954 ShaderValues, ShadingData, SkyboxData, Skyboxes, SoundCue, SoundData, Sounds, Spot,
955 Startup, Stick, SurfaceStyle, SurfaceStyles, TextureData, TickContext, Tonemap, Transform,
956 Transition, View, WheelDelta, run,
957 };
958 pub use crate::{meshes, post_effects, surface_styles};
959}