1#![allow(
25 clippy::type_complexity,
26 clippy::unwrap_or_default,
27 dead_code,
28 unused_variables,
29 unused_imports,
30 unused_mut,
31 unused_parens
32)]
33
34pub mod error;
35pub mod kvasir;
36mod material;
37
38pub use material::builtins;
40pub use material::{CompiledMaterial, MaterialCompiler, MaterialError, MaterialGraph, MaterialOp};
41
42pub mod accessibility;
43pub mod ai;
44mod api;
45pub mod draw;
46pub mod filter;
47pub mod passes;
48pub mod pyramid;
49pub mod renderer;
50mod surtr_util;
51pub mod types;
52pub mod vertex;
53
54pub mod heim;
55pub use heim::SkylinePacker;
56
57pub mod subsystems;
61pub use subsystems::RendererConfig;
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 use super::heim::SkylinePacker;
68
69 #[test]
70 fn test_shelf_packer_basic() {
71 let mut packer = SkylinePacker::new(100, 100);
72 assert_eq!(packer.pack(10, 10), Some((0, 0)));
73 assert_eq!(packer.pack(20, 15), Some((10, 0)));
74 }
75
76 #[test]
77 fn test_shelf_packer_wrap() {
78 let mut packer = SkylinePacker::new(100, 100);
79 packer.pack(60, 10);
80 assert_eq!(packer.pack(50, 20), Some((0, 10)));
81 }
82
83 #[test]
84 fn test_parse_svg_animations() {
85 let svg = r##"
86 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
87 <g id="spinner">
88 <animateTransform attributeName="transform" type="rotate" from="0" to="360" dur="2s" />
89 </g>
90 <circle id="pulse">
91 <animate attributeName="opacity" from="0.5" to="1.0" dur="0.5s" />
92 </circle>
93 <!-- Edge cases: xlink:href, ms suffix, values list -->
94 <rect>
95 <animate xlink:href="#myRect" attributeName="x" values="10; 20; 30" dur="500ms" />
96 </rect>
97 </svg>
98 "##;
99 let anims = draw::parse_svg_animations(svg.as_bytes());
100 assert_eq!(anims.len(), 3);
101
102 assert_eq!(anims[0].target_id, "spinner");
103 assert_eq!(anims[0].keyframe_values, vec![0.0, 360.0]);
104
105 assert_eq!(anims[1].target_id, "pulse");
106 assert_eq!(anims[1].attribute_name, "opacity");
107 assert_eq!(anims[1].duration, 0.5);
108 assert_eq!(anims[1].keyframe_values, vec![0.5, 1.0]);
109
110 assert_eq!(anims[2].target_id, "myRect");
111 assert_eq!(anims[2].attribute_name, "x");
112 assert_eq!(anims[2].duration, 0.5); assert_eq!(anims[2].keyframe_values, vec![10.0, 20.0, 30.0]);
114 }
115
116 #[test]
117 fn test_shelf_packer_full() {
118 let mut packer = SkylinePacker::new(10, 10);
119 assert_eq!(packer.pack(11, 5), None);
120 assert_eq!(packer.pack(5, 11), None);
121 }
122}
123
124#[cfg(target_arch = "wasm32")]
132pub(crate) const WGSL_COMMON: &str = include_str!("shaders/common_wasm.wgsl");
133#[cfg(not(target_arch = "wasm32"))]
134pub(crate) const WGSL_COMMON: &str = include_str!("shaders/common.wgsl");
135
136pub(crate) const WGSL_SHAPES: &str = include_str!("shaders/shapes.wgsl");
137
138#[cfg(target_arch = "wasm32")]
139pub(crate) const WGSL_MATERIAL_OPAQUE: &str = include_str!("shaders/material_opaque_wasm.wgsl");
140#[cfg(not(target_arch = "wasm32"))]
141pub(crate) const WGSL_MATERIAL_OPAQUE: &str = include_str!("shaders/material_opaque.wgsl");
142
143pub(crate) const WGSL_MATERIAL_GLASS: &str = include_str!("shaders/material_glass.wgsl");
144pub(crate) const WGSL_MATERIAL_PBR: &str = include_str!("shaders/material_pbr.wgsl");
145pub(crate) const WGSL_MATERIAL_SHADOW: &str = include_str!("shaders/material_shadow.wgsl");
146pub(crate) const WGSL_BIFROST: &str = include_str!("shaders/bifrost.wgsl");
147
148#[cfg(target_arch = "wasm32")]
149pub(crate) const WGSL_BLOOM: &str = include_str!("shaders/bloom_wasm.wgsl");
150#[cfg(not(target_arch = "wasm32"))]
151pub(crate) const WGSL_BLOOM: &str = include_str!("shaders/bloom.wgsl");
152
153pub(crate) const WGSL_COLOR_BLIND: &str = include_str!("shaders/color_blind.wgsl");
154pub(crate) const WGSL_TONEMAP: &str = include_str!("shaders/tonemap.wgsl");
155pub(crate) const WGSL_PARTICLES: &str = include_str!("shaders/particles.wgsl");
156pub(crate) const WGSL_SKINNING: &str = include_str!("shaders/skinning.wgsl");
157
158pub mod color_blindness;
159
160pub use color_blindness::ColorBlindMode;
162
163pub use accesskit::{
166 ActionHandler, ActionRequest, ActivationHandler, DeactivationHandler, Node, NodeId, Role, Tree,
167 TreeId, TreeUpdate,
168};
169pub use accesskit_winit::Adapter as ShieldWallAdapter;
170
171pub use cvkg_core::{ColorTheme, SceneUniforms};
173
174pub use renderer::GpuRenderer;
175
176pub use types::{SvgAnimation, SvgModel};
178pub use vertex::{InstanceData, InstanceData3D, Vertex};
179
180pub use cvkg_core::PassNode;
182
183pub const MANIFEST: cvkg_core::FrameManifest = cvkg_core::FrameManifest {
186 phase_contributions: &[
187 cvkg_core::FramePhase::Render,
188 cvkg_core::FramePhase::Composite,
189 ],
190 pass_nodes: &[
191 cvkg_core::PassNodeDescriptor {
192 id: "geometry",
193 label: "Geometry Pass (Opaque)",
194 inputs: &[],
195 outputs: &["scene_color", "scene_depth"],
196 after: &[],
197 constructor: || -> Box<dyn cvkg_core::PassNode> {
198 Box::new(crate::passes::geometry::GeometryNode::new())
199 },
200 },
201 cvkg_core::PassNodeDescriptor {
202 id: "shadow",
203 label: "Shadow Pass (Depth-Only)",
204 inputs: &[],
205 outputs: &["shadow_map"],
206 after: &[],
207 constructor: || -> Box<dyn cvkg_core::PassNode> {
208 Box::new(crate::passes::shadow::ShadowNode {
209 light: crate::passes::shadow::DirectionalLight::default(),
210 shadow_map: crate::kvasir::ResourceId(0),
211 mesh_instances: Vec::new(),
212 cascade_splits: [8.0, 25.0, 70.0, 200.0],
213 camera_view_proj: glam::Mat4::IDENTITY,
214 })
215 },
216 },
217 cvkg_core::PassNodeDescriptor {
218 id: "opaque3d",
219 label: "Opaque 3D Pass (PBR + Shadows)",
220 inputs: &["shadow_map"],
221 outputs: &["scene_color"],
222 after: &["shadow"],
223 constructor: || -> Box<dyn cvkg_core::PassNode> {
224 Box::new(crate::passes::opaque3d::Opaque3dNode {
225 mesh_instances: Vec::new(),
226 light: crate::passes::shadow::DirectionalLight::default(),
227 shadow_map: crate::kvasir::ResourceId(0),
228 })
229 },
230 },
231 cvkg_core::PassNodeDescriptor {
232 id: "ui",
233 label: "UI Compositing",
234 inputs: &["scene_color", "scene_depth"],
235 outputs: &["ui_output"],
236 after: &["geometry"],
237 constructor: || -> Box<dyn cvkg_core::PassNode> {
238 Box::new(crate::passes::ui::UINode::new())
239 },
240 },
241 cvkg_core::PassNodeDescriptor {
242 id: "bloom_extract",
243 label: "Bloom Extract",
244 inputs: &["ui_output"],
245 outputs: &["bloom_src"],
246 after: &["ui"],
247 constructor: || -> Box<dyn cvkg_core::PassNode> {
248 Box::new(crate::passes::bloom::BloomExtractNode::new())
249 },
250 },
251 cvkg_core::PassNodeDescriptor {
252 id: "volumetric",
253 label: "Volumetric Raymarching",
254 inputs: &["scene_color"],
255 outputs: &["scene_color"],
256 after: &["geometry"],
257 constructor: || -> Box<dyn cvkg_core::PassNode> {
258 Box::new(crate::passes::volumetric::VolumetricNode::new())
259 },
260 },
261 cvkg_core::PassNodeDescriptor {
262 id: "accessibility",
263 label: "Accessibility Transform",
264 inputs: &["scene_color"],
265 outputs: &["scene_color"],
266 after: &["bloom_extract"],
267 constructor: || -> Box<dyn cvkg_core::PassNode> {
268 Box::new(crate::passes::accessibility::AccessibilityNode::new())
269 },
270 },
271 ],
272 time_budget_requests: &[
273 cvkg_core::TimeBudgetRequest {
274 phase: cvkg_core::FramePhase::Render,
275 time_slice_us: 8000,
276 skippable: false,
277 name: "render_gpu",
278 },
279 cvkg_core::TimeBudgetRequest {
280 phase: cvkg_core::FramePhase::Composite,
281 time_slice_us: 4000,
282 skippable: false,
283 name: "composite_gpu",
284 },
285 ],
286};