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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//! The order in which surfaces that overlap are drawn, as the source states it (`core.draw_order`, on material
//! entities).
use serde::Serialize;
use serde_json::Value;
use crate::pbr::{AlphaMode, PbrMaterial};
use crate::{Component, Entity, JsonError, World};
/// What a surface is, for the draw order: it drives depth bias, shadows and a target's material class, not the
/// sequence (that is [`DrawOrder::queue`]).
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum DrawLayer {
Opaque,
/// Alpha tested.
Cutout,
/// Drawn over the surface beneath it, coplanar or nearly so (a game's decal bucket): a consumer biases its depth
/// and does not cast its shadow.
Decal,
/// The sky: a dome, the moon's or the sun's disc, far backdrops and cloud layers. It is drawn after the opaque
/// surfaces are lit and before the blended surfaces, depth tested against the scene so that it shows only where
/// nothing nearer was drawn, and it casts no shadow. The sky's own programs give its colour: a consumer adds no
/// fog or lighting of its own to it. Whether the source's own fog composite (a volumetric fog laid over the lit
/// picture) comes before the sky or over it is the source's: its `core.render_graph` states it by the order of its
/// passes (Sekiro composites it before the sky, Elden Ring over it, in a recorded frame of each).
/// Where it stands is often relative to the view ([`crate::SkyAnchor`]).
Sky,
/// Blended.
Transparent,
}
/// `core.draw_order`: where a material's surfaces come in the source's draw sequence. `queue` is that sequence across
/// every layer, lower first, on core's own scale: [`DrawOrder::OPAQUE`] 2000, [`DrawOrder::CUTOUT`] 2450,
/// [`DrawOrder::SKY`] 2900 and [`DrawOrder::TRANSPARENT`] 3000 are where a layer's surfaces come by default (the sky
/// has no alpha mode that implies it: a source states it), and a lowering places a source's
/// own sequence on this scale (a source whose numbers are already on it keeps them). One scale for every layer, so a
/// source can order a surface of one layer among another's. Within one queue, surfaces draw in entity order, then
/// primitive order: the source order readers keep.
///
/// `depth_write`: whether the source draws the surface writing depth (a blended surface may, where the source draws it
/// among opaque ones), so a consumer orders and composites it as the source does.
///
/// A material without it draws by its alpha mode ([`DrawOrder::of`]), and is never a decal: that is a fact the
/// source states.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
pub struct DrawOrder {
pub layer: DrawLayer,
pub queue: i32,
pub depth_write: bool,
}
impl DrawOrder {
/// Core's default queues of the layers, which a material's alpha mode implies where the source states no order.
pub const OPAQUE: i32 = 2000;
pub const CUTOUT: i32 = 2450;
/// The sky's default queue: after every opaque, alpha-tested and decal surface, before the blended ones.
pub const SKY: i32 = 2900;
pub const TRANSPARENT: i32 = 3000;
/// A draw order with the conventional depth write of its layer (glTF's): opaque and alpha-tested surfaces write
/// depth, decals and blended ones do not. For a source that states the layer and queue but not the depth write.
pub fn conventional(layer: DrawLayer, queue: i32) -> DrawOrder {
DrawOrder {
layer,
queue,
depth_write: matches!(layer, DrawLayer::Opaque | DrawLayer::Cutout),
}
}
/// Material `e`'s draw order: its `core.draw_order`, else what its `pbr.material`'s alpha mode implies (opaque
/// without one), conventionally ([`DrawOrder::conventional`]).
pub fn of(world: &World, e: Entity) -> DrawOrder {
if let Some(&d) = world.get::<DrawOrder>(e) {
return d;
}
match world.get::<PbrMaterial>(e).map(|m| m.alpha_mode) {
Some(AlphaMode::Mask { .. }) => Self::conventional(DrawLayer::Cutout, Self::CUTOUT),
Some(AlphaMode::Blend) => Self::conventional(DrawLayer::Transparent, Self::TRANSPARENT),
Some(AlphaMode::Opaque) | None => Self::conventional(DrawLayer::Opaque, Self::OPAQUE),
}
}
}
impl Component for DrawOrder {
const NAME: &'static str = "core.draw_order";
const GEOMETRY: crate::Geometry = crate::Geometry::None;
/// 2: `depth_write`. 3: the `sky` layer.
const VERSION: u32 = 3;
fn to_json(&self) -> Result<Option<Value>, JsonError> {
crate::json::to_value(self).map(Some)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_material_without_one_draws_by_its_alpha_mode_and_is_never_a_decal() {
let mut w = World::new();
let blend = w.spawn();
w.insert(
blend,
PbrMaterial {
alpha_mode: AlphaMode::Blend,
..PbrMaterial::default()
},
);
let mask = w.spawn();
w.insert(
mask,
PbrMaterial {
alpha_mode: AlphaMode::Mask { cutoff: 0.5 },
..PbrMaterial::default()
},
);
let bare = w.spawn();
let decal = w.spawn();
// a decal the source states writes depth (the convention would not)
w.insert(
decal,
DrawOrder {
layer: DrawLayer::Decal,
queue: 2500,
depth_write: true,
},
);
// without one: the alpha mode's layer and queue, and glTF's depth write
assert_eq!(
DrawOrder::of(&w, blend),
DrawOrder::conventional(DrawLayer::Transparent, 3000)
);
assert!(!DrawOrder::of(&w, blend).depth_write);
assert_eq!(DrawOrder::of(&w, mask).queue, 2450);
assert!(DrawOrder::of(&w, mask).depth_write);
assert_eq!(DrawOrder::of(&w, bare).layer, DrawLayer::Opaque);
assert!(DrawOrder::of(&w, bare).depth_write);
assert!(!DrawOrder::conventional(DrawLayer::Decal, 2500).depth_write);
assert_eq!(
DrawOrder::of(&w, decal),
DrawOrder {
layer: DrawLayer::Decal,
queue: 2500,
depth_write: true
}
);
assert_eq!(
w.get::<DrawOrder>(decal).unwrap().to_json().unwrap(),
Some(serde_json::json!({"layer": "decal", "queue": 2500, "depth_write": true}))
);
}
}