Skip to main content

cvkg_core/
render_tier.rs

1#[derive(
2    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
3)]
4pub enum RenderTier {
5    /// High-performance GPU path (WebGPU / Vulkan / Metal / DX12) with full shader support.
6    Tier1GPU = 0,
7    /// Mid-tier GPU path (WebGL2 / OpenGL 3.3) with standard shader support.
8    Tier2GPU = 1,
9    /// Fallback software or basic hardware path (Canvas 2D / GDI+) with limited effects.
10    Tier3Fallback = 2,
11}
12// =============================================================================
13// BERSERKER UNIFORMS
14// =============================================================================
15use bytemuck::{Pod, Zeroable};
16/// Fully themeable color palette for the Berserker pipeline.
17#[repr(C)]
18#[derive(Copy, Clone, Debug, PartialEq, Pod, Zeroable, serde::Serialize, serde::Deserialize)]
19pub struct ColorTheme {
20    pub primary_neon: [f32; 4], // (R, G, B, intensity)
21    pub shatter_neon: [f32; 4],
22    pub glass_base: [f32; 4],
23    pub glass_edge: [f32; 4],
24    pub rune_glow: [f32; 4],
25    pub ember_core: [f32; 4],
26    pub background_deep: [f32; 4],
27    pub mani_glow: [f32; 4], // (R, G, B, radius)
28    pub glass_blur_strength: f32,
29    pub shatter_edge_width: f32,
30    pub neon_bloom_radius: f32,
31    pub rune_opacity: f32,
32    /// Weight of adaptive tint from backdrop [0.0, 1.0].
33    /// 0.0 = static theme tint, 1.0 = fully adaptive.
34    pub glass_tint_adapt: f32,
35    /// Per-frame glass IOR override. 0.0 = use shader default (1.45).
36    pub glass_ior: f32,
37    /// Color space for framebuffer output. 0 = sRGB (default), 1 = Display P3, 2 = Adobe RGB.
38    pub color_space: u32,
39    // Padding to match WGSL uniform buffer 16-byte struct alignment (total = 176 bytes).
40    pub _pad0: f32,
41    pub _pad1: f32,
42    pub _pad2: f32,
43    pub _pad3: f32,
44    pub _pad4: f32,
45}
46// P2-9: Compile-time layout verification between Rust ColorTheme and WGSL.
47// WGSL std140 struct size = 176 bytes (164 raw + 12 alignment padding).
48// Rust repr(C) struct must match exactly.
49const _: () = assert!(
50    std::mem::size_of::<ColorTheme>() == 176,
51    "ColorTheme Rust/WGSL layout mismatch: expected 176 bytes"
52);
53impl ColorTheme {
54    /// Asgard Mode: The high-fidelity "Cyberpunk Viking" aesthetic.
55    pub fn asgard() -> Self {
56        Self {
57            primary_neon: [0.0, 1.0, 0.95, 1.2],
58            shatter_neon: [1.0, 0.0, 0.75, 1.5],
59            glass_base: [0.04, 0.04, 0.06, 0.82],
60            glass_edge: [0.0, 0.45, 0.55, 0.6],
61            rune_glow: [0.75, 0.98, 1.0, 0.9],
62            ember_core: [0.95, 0.12, 0.12, 1.0],
63            background_deep: [0.01, 0.01, 0.03, 1.0],
64            mani_glow: [0.7, 0.9, 1.0, 0.05],
65            glass_blur_strength: 0.6,
66            shatter_edge_width: 1.8,
67            neon_bloom_radius: 0.022,
68            rune_opacity: 0.55,
69            glass_tint_adapt: 0.35,
70            glass_ior: 1.45,
71            color_space: 0,
72            _pad0: 0.0,
73            _pad1: 0.0,
74            _pad2: 0.0,
75            _pad3: 0.0,
76            _pad4: 0.0,
77        }
78    }
79
80    /// Midgard Mode: A clean, functional tactical HUD for standard operations.
81    pub fn midgard() -> Self {
82        Self {
83            primary_neon: [0.2, 0.4, 0.6, 1.0], // Muted blue
84            shatter_neon: [0.5, 0.5, 0.5, 1.0], // Neutral gray
85            glass_base: [0.1, 0.12, 0.15, 1.0], // Solid slate
86            glass_edge: [0.3, 0.35, 0.4, 1.0],  // Subtle border
87            rune_glow: [0.8, 0.8, 0.8, 0.0],    // Runes disabled
88            ember_core: [0.5, 0.5, 0.5, 1.0],
89            background_deep: [0.05, 0.05, 0.07, 1.0],
90            mani_glow: [0.0, 0.0, 0.0, 0.0], // No cursor glow
91            glass_blur_strength: 0.0,        // No blur
92            shatter_edge_width: 1.0,
93            neon_bloom_radius: 0.0,
94            rune_opacity: 0.0,
95            glass_tint_adapt: 0.0,
96            glass_ior: 1.0,
97            color_space: 0,
98            _pad0: 0.0,
99            _pad1: 0.0,
100            _pad2: 0.0,
101            _pad3: 0.0,
102            _pad4: 0.0,
103        }
104    }
105
106    pub fn cyberpunk_viking() -> Self {
107        Self::asgard()
108    }
109    pub fn vibrant_glass() -> Self {
110        Self {
111            primary_neon: [0.0, 1.0, 0.95, 1.2],
112            shatter_neon: [1.0, 0.0, 0.75, 1.5],
113            glass_base: [0.55, 0.6, 0.7, 0.08], // Luminous cool tint
114            glass_edge: [0.7, 0.85, 1.0, 0.45], // Subtle blue-white rim
115            rune_glow: [0.75, 0.98, 1.0, 0.9],
116            ember_core: [1.0, 0.4, 0.1, 1.0],
117            background_deep: [0.05, 0.05, 0.1, 1.0],
118            mani_glow: [0.7, 0.9, 1.0, 0.05],
119            glass_blur_strength: 0.9,
120            shatter_edge_width: 1.8,
121            neon_bloom_radius: 0.022,
122            rune_opacity: 0.55,
123            glass_tint_adapt: 0.65,
124            glass_ior: 1.45,
125            color_space: 0,
126            _pad0: 0.0,
127            _pad1: 0.0,
128            _pad2: 0.0,
129            _pad3: 0.0,
130            _pad4: 0.0,
131        }
132    }
133
134    /// Berserker Mode: Blood-iron neon, aggressive contrast, forge-heated glass.
135    pub fn berserker() -> Self {
136        Self {
137            primary_neon: [1.0, 0.08, 0.12, 1.1], // Calibrated intensity down from 1.8 to 1.1 for legibility
138            shatter_neon: [0.95, 0.92, 0.88, 1.0], // Muted from 1.6 to 1.0
139            glass_base: [0.02, 0.01, 0.01, 0.92],  // Darker flat blood-iron background
140            glass_edge: [0.7, 0.15, 0.05, 0.55],   // Clean blood-red neon edge
141            rune_glow: [0.95, 0.35, 0.1, 0.6],     // Warm fire-rune glow
142            ember_core: [0.98, 0.25, 0.05, 0.8],
143            background_deep: [0.005, 0.002, 0.002, 1.0], // Deeper flat black canvas
144            mani_glow: [0.8, 0.15, 0.02, 0.03],    // Smaller cursor halo
145            glass_blur_strength: 0.5,              // Reduced blur for clean contrast
146            shatter_edge_width: 2.0,
147            neon_bloom_radius: 0.018,              // Reduced bloom radius from 0.035 to 0.018 for legibility
148            rune_opacity: 0.45,                    // Softened background rune glow
149            glass_tint_adapt: 0.1,
150            glass_ior: 1.5,
151            color_space: 0,
152            _pad0: 0.0,
153            _pad1: 0.0,
154            _pad2: 0.0,
155            _pad3: 0.0,
156            _pad4: 0.0,
157        }
158    }
159}
160impl Default for ColorTheme {
161    fn default() -> Self {
162        Self::berserker()
163    }
164}
165/// Per-frame scene state for the Berserker pipeline.
166#[repr(C)]
167#[derive(Copy, Clone, Debug, Pod, Zeroable, serde::Serialize, serde::Deserialize)]
168pub struct SceneUniforms {
169    pub view: glam::Mat4,
170    pub proj: glam::Mat4,
171    pub time: f32,
172    pub delta_time: f32,
173    pub resolution: [f32; 2],
174    pub mouse: [f32; 2],
175    pub mouse_velocity: [f32; 2],
176    pub shatter_origin: [f32; 2],
177    pub shatter_time: f32,
178    pub shatter_force: f32,
179    pub berzerker_rage: f32,
180    pub berzerker_mode: u32,
181    pub scroll_offset: f32,
182    pub scale_factor: f32,
183    pub scene_type: u32,
184    pub _pad_vec2_align: [u32; 1], // 4-byte pad: WGSL vec2<f32> requires 8-byte alignment
185    pub fireball_pos: [f32; 2],
186    pub camera_pos: [f32; 3],
187    pub _pad_cam: f32,
188    pub light_direction: [f32; 3],
189    pub _pad_light_dir: f32,
190    pub light_color: [f32; 3],
191    pub _pad_light_color: f32,
192    pub shadow_map_size: f32,
193    pub shadow_bias: f32,
194    pub _pad_shadow: [u32; 2],  // 8 bytes padding for 16-byte alignment of light_vp
195    pub light_vp: glam::Mat4,
196}
197
198pub const SCENE_AURORA: u32 = 0;
199pub const SCENE_VOID: u32 = 1;
200pub const SCENE_NEBULA: u32 = 2;
201pub const SCENE_GLITCH: u32 = 3;
202pub const SCENE_YGGDRASIL: u32 = 4;
203
204/// Resolve a scene name string to a scene preset constant.
205/// Case-insensitive. Supports: "aurora", "void", "nebula", "glitch", "yggdrasil".
206/// Also supports common aliases: "empty", "none" → VOID.
207/// Returns None if the name is not recognized.
208pub fn resolve_scene_by_name(name: &str) -> Option<u32> {
209    let normalized = name.to_lowercase().replace(['-', '_', ' ', '.'], "");
210    match normalized.as_str() {
211        "aurora" => Some(SCENE_AURORA),
212        "void" | "empty" | "none" | "blank" => Some(SCENE_VOID),
213        "nebula" => Some(SCENE_NEBULA),
214        "glitch" => Some(SCENE_GLITCH),
215        "yggdrasil" | "worldtree" | "tree" => Some(SCENE_YGGDRASIL),
216        _ => None,
217    }
218}
219
220impl SceneUniforms {
221    pub fn new(width: f32, height: f32) -> Self {
222        Self {
223            view: glam::Mat4::IDENTITY,
224            proj: glam::Mat4::orthographic_lh(0.0, width, height, 0.0, -100.0, 100.0),
225            time: 0.0,
226            delta_time: 0.016,
227            resolution: [width, height],
228            mouse: [0.5, 0.5],
229            mouse_velocity: [0.0, 0.0],
230            shatter_origin: [0.5, 0.5],
231            shatter_time: -100.0,
232            shatter_force: 0.0,
233            berzerker_rage: 0.0,
234            berzerker_mode: 0,
235            scroll_offset: 0.0,
236            scale_factor: 1.0,
237            scene_type: SCENE_AURORA,
238            _pad_vec2_align: [0],
239            fireball_pos: [0.0, 0.0],
240            camera_pos: [0.0, 0.0, -5.0],
241            _pad_cam: 0.0,
242            light_direction: [0.5, 0.8, 0.6],
243            _pad_light_dir: 0.0,
244            light_color: [1.0, 0.95, 0.9],
245            _pad_light_color: 0.0,
246            shadow_map_size: 1024.0,
247            shadow_bias: 0.005,
248            _pad_shadow: [0, 0],
249            light_vp: glam::Mat4::IDENTITY,
250        }
251    }
252}