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