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
154
155
156
157
158
159
160
161
162
163
164
165
166
//! Viewport view-mode and show-flag state: backend-agnostic per-frame render
//! selection. The mode picks what the final image shows (the lit scene, a flat
//! shading, or one G-buffer channel); the flags switch individual feature
//! passes off for the frame without touching their resources. Consumed by the
//! render graph (masking pass gates) and each backend's composite.
/// What the viewport's final image shows. `Lit` is the shipping path; the
/// other modes visualize one stage of the frame.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u32)]
pub enum ViewMode {
#[default]
/// The fully lit shipping image.
Lit = 0,
/// Surface base color with no lighting.
Unlit = 1,
/// Triangle edges only.
Wireframe = 2,
/// View-space normals from the geometry prepass.
Normals = 3,
/// Perceptual roughness from the geometry prepass.
Roughness = 4,
/// Screen-space ambient occlusion.
Occlusion = 5,
/// Linear view depth.
Depth = 6,
}
impl ViewMode {
/// Every mode, in the cycling/UI order.
pub const ALL: [ViewMode; 7] = [
ViewMode::Lit,
ViewMode::Unlit,
ViewMode::Wireframe,
ViewMode::Normals,
ViewMode::Roughness,
ViewMode::Occlusion,
ViewMode::Depth,
];
/// True for the flat-shaded modes (Unlit, Wireframe), which drop the
/// surface-effect passes for a clean readable image.
pub fn is_flat(self) -> bool {
matches!(self, ViewMode::Unlit | ViewMode::Wireframe)
}
/// True for the modes whose image is a geometry-prepass channel sampled
/// by the composite.
pub fn is_gbuffer_channel(self) -> bool {
matches!(
self,
ViewMode::Normals | ViewMode::Roughness | ViewMode::Occlusion | ViewMode::Depth
)
}
/// Short display label for menus.
pub fn label(self) -> &'static str {
match self {
ViewMode::Lit => "Lit",
ViewMode::Unlit => "Unlit",
ViewMode::Wireframe => "Wireframe",
ViewMode::Normals => "Normals",
ViewMode::Roughness => "Roughness",
ViewMode::Occlusion => "Occlusion",
ViewMode::Depth => "Depth",
}
}
}
/// Per-frame feature-pass toggles. A cleared bit skips the matching pass for
/// the frame (the cheap runtime counterpart to the init-time trims); set bits
/// leave the pass to its normal gates. Billboard icons are editor overlay
/// sprites rather than a render pass, so they have no bit here.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ShowFlags(pub u32);
impl ShowFlags {
/// Directional and local shadow passes.
pub const SHADOWS: ShowFlags = ShowFlags(1 << 0);
/// Volumetric fog.
pub const FOG: ShowFlags = ShowFlags(1 << 1);
/// Bloom.
pub const BLOOM: ShowFlags = ShowFlags(1 << 2);
/// Screen-space global illumination.
pub const SSGI: ShowFlags = ShowFlags(1 << 3);
/// Screen-space reflections.
pub const SSR: ShowFlags = ShowFlags(1 << 4);
/// The debug line pass.
pub const LINES: ShowFlags = ShowFlags(1 << 5);
/// Every flag, paired with its display label, in UI order.
pub const LABELED: [(ShowFlags, &'static str); 6] = [
(ShowFlags::SHADOWS, "Shadows"),
(ShowFlags::FOG, "Fog"),
(ShowFlags::BLOOM, "Bloom"),
(ShowFlags::SSGI, "SSGI"),
(ShowFlags::SSR, "Reflections"),
(ShowFlags::LINES, "Lines"),
];
/// Every flag set.
pub const fn all() -> ShowFlags {
ShowFlags(
ShowFlags::SHADOWS.0
| ShowFlags::FOG.0
| ShowFlags::BLOOM.0
| ShowFlags::SSGI.0
| ShowFlags::SSR.0
| ShowFlags::LINES.0,
)
}
/// Whether every bit in `other` is set here.
pub const fn contains(self, other: ShowFlags) -> bool {
self.0 & other.0 == other.0
}
#[must_use]
/// This set with `other`'s bits flipped.
pub const fn toggled(self, other: ShowFlags) -> ShowFlags {
ShowFlags(self.0 ^ other.0)
}
}
impl Default for ShowFlags {
fn default() -> Self {
ShowFlags::all()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_shows_everything() {
let all = ShowFlags::default();
for (flag, _) in ShowFlags::LABELED {
assert!(all.contains(flag));
}
assert_eq!(all, ShowFlags::all());
}
#[test]
fn toggling_clears_and_restores_one_bit() {
let some = ShowFlags::all().toggled(ShowFlags::FOG);
assert!(!some.contains(ShowFlags::FOG));
assert!(some.contains(ShowFlags::SHADOWS));
assert_eq!(some.toggled(ShowFlags::FOG), ShowFlags::all());
}
#[test]
fn mode_classes_partition_as_expected() {
assert!(!ViewMode::Lit.is_flat() && !ViewMode::Lit.is_gbuffer_channel());
assert!(ViewMode::Unlit.is_flat() && ViewMode::Wireframe.is_flat());
for m in [
ViewMode::Normals,
ViewMode::Roughness,
ViewMode::Occlusion,
ViewMode::Depth,
] {
assert!(m.is_gbuffer_channel() && !m.is_flat());
}
assert_eq!(ViewMode::ALL.len(), 7);
}
}