1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//! Practical lights for Bevy: the lights a scene's props actually carry.
//!
//! A world can have thousands of torches and only afford a dozen real lights. This crate is the
//! three pieces that make that look right:
//!
//! - [`Flicker`] and [`Kindle`] — [`FlickerPlugin`] modulates a point or spot light around its base
//! intensity with the doubled sine a fire's light has, and flares a light up over
//! [`KINDLE_SECONDS`] when it is first lit instead of switching it on. [`Kindled`] is the message
//! a host hears, for the sound of a lamp catching.
//! - [`LightPool`] — [`LightPoolPlugin`] keeps a fixed number of real lights and hands them to the
//! [`PoolCandidate`]s nearest the host's focus entity every frame; the rest of the candidates are
//! virtual. The lights it spawns carry [`PoolLight`], so a host can style them (a fire's palette
//! recolours them, its flames are not drawn on them).
//! - [`FlameMaterial`], [`FlameSite`] — [`FlamePlugin`] draws a billboarded, additive shader flame
//! standing on every [`FlameSite`], out of one material and one draw per flame: `src/flame.wesl`.
//!
//! Under those three is the cinematographer's half, [`model::cinematography`]: [`key_to_fill`] and
//! [`stops`] for a scene's key against its fill, [`fill_for`] for the sky that makes a night,
//! [`fire_lumens`] for the size of a pool, and [`blackbody_colour`] and [`white_balanced`] for a
//! light's colour from the temperature it burns at. They touch no light: the same numbers a director
//! of photography sets before a lamp is hung, for a host to size and colour its practicals with.
//!
//! Every plugin is independent; adding some and not others is supported, and
//! [`LightPoolPlugin`] warns (it does not panic) when [`FlickerPlugin`] is missing.
//!
//! ```no_run
//! # use bevy::prelude::*;
//! # use bevy_lights::*;
//! # #[derive(Component)]
//! # struct MyFocus;
//! # fn main() {
//! App::new()
//! .add_plugins((FlickerPlugin, LightPoolPlugin::<MyFocus>::default(), FlamePlugin))
//! .add_systems(Startup, |mut commands: Commands| {
//! // A brazier: `diameter` is its fuel bed, which sizes the flicker through the puffing law
//! // (`f = 1.5/√D` Hz, so 0.34 m is 2.6 Hz). Leave it `None` for the un-sized rate.
//! commands.spawn((PointLight { intensity: 60_000.0, range: 12.0, ..default() },
//! Flicker { base: 60_000.0, phase: 0.0, diameter: Some(0.34), ..default() },
//! Kindle::default(),
//! // `diameter` matches the `Flicker`'s, so the drawn flame breathes with the light.
//! FlameSite { height: 0.9, phase: 0.0, kindle: 0.0, diameter: Some(0.34),
//! depth: FLICKER_DEPTH, depth_fade: false, fire: None }));
//! commands.spawn((MyFocus, Transform::default()));
//! // A carried fire: one entity holds the `FlameSite` and the `PoolCandidate`, so the light it
//! // is handed sits in the flame the host can see. `offset` is the flame's own centre, measured
//! // from the site's origin, and the lumens are sized for the height the light will sit at.
//! let height = 0.35;
//! commands.spawn((PoolCandidate {
//! offset: Vec3::from(flame_light_offset(height)),
//! intensity: 8_000.0,
//! ..default()
//! },
//! FlameSite { height, phase: 9.0, kindle: 0.0, diameter: None, depth: FLICKER_DEPTH,
//! depth_fade: false, fire: None },
//! Transform::from_xyz(4.0, 0.0, 2.0)));
//! });
//! # }
//! ```
//!
//! The `App` above is not run by the doctest: `FlamePlugin` adds a `MaterialPlugin`, which needs a
//! render app, so running it would need a GPU. `tests/flicker.rs` and `tests/pool.rs` are the
//! behavioural proof for the other two plugins, and `src/flame.rs`'s own tests cover the flame
//! dressing headlessly.
//!
//! # What this crate does not own
//!
//! The sun, the moon, the sky, exposure and weather belong to a sky crate; a gameplay illuminance
//! grid, to a light-grid crate; and which lamp is lit and when, to the game or to a society crate.
//! This crate owns the practicals: their flicker, their kindling, the pool that decides which of
//! them are real, and the flames drawn on them. It never adds a directional light and never meters
//! exposure.
//!
//! # Units
//!
//! Intensities are Bevy's: lumens for a [`PointLight`] and a [`SpotLight`]. Distances are the
//! host's world units. Nothing in this crate converts, so a host in feet is in feet throughout.
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
use *;
/// The order the crate's systems run in `Update`.
///
/// [`LightsSystems::Pool`] hands the real lights out before [`LightsSystems::Flicker`] modulates
/// them, so a light handed to a new candidate flickers at its new base in the very same frame; and
/// [`LightsSystems::Flame`] dresses sites after both, so a flame is drawn on the frame its site
/// appears.
/// Chains the crate's sets, and does it idempotently, so each plugin may call it.