cvkg_render_gpu/passes/
shadow.rs1use crate::kvasir::{ExecutionContext, KvasirNode, ResourceId};
5use crate::kvasir::nodes::PassId;
6use glam::Mat4;
7use wgpu::Buffer;
8
9#[derive(Debug, Clone, Copy)]
11pub struct DirectionalLight {
12 pub direction: glam::Vec3,
13 pub color: glam::Vec3,
14 pub intensity: f32,
15}
16
17impl Default for DirectionalLight {
18 fn default() -> Self {
19 Self {
20 direction: glam::Vec3::new(0.0, -1.0, 0.0),
21 color: glam::Vec3::ONE,
22 intensity: 1.0,
23 }
24 }
25}
26
27#[derive(Debug, Clone)]
29pub struct GpuMesh3d {
30 pub vertex_buffer: Buffer,
32 pub index_buffer: Buffer,
34 pub index_count: u32,
36 pub transform: Mat4,
38}
39
40pub struct ShadowNode {
42 pub light: DirectionalLight,
43 pub shadow_map: ResourceId,
44 pub mesh_instances: Vec<GpuMesh3d>,
46 pub scene_radius: f32,
48}
49
50impl KvasirNode for ShadowNode {
51 fn label(&self) -> &'static str {
52 "ShadowPass"
53 }
54
55 fn inputs(&self) -> &[ResourceId] {
56 &[]
57 }
58
59 fn outputs(&self) -> &[ResourceId] {
60 std::slice::from_ref(&self.shadow_map)
61 }
62
63 fn pass_id(&self) -> PassId {
64 PassId::Shadow
65 }
66
67 fn execute(&self, ctx: &mut ExecutionContext) {
68 let light_dir = self.light.direction;
70 let scene_center = glam::Vec3::ZERO;
71 let light_pos = scene_center + light_dir * self.scene_radius * 2.0;
72 let light_view = glam::Mat4::look_at_lh(light_pos, scene_center, glam::Vec3::Y);
73
74 let r = self.scene_radius;
76 let light_proj = glam::Mat4::orthographic_lh(-r, r, -r, r, 0.0, self.scene_radius * 4.0);
77 let _light_vp = light_proj * light_view;
78
79 tracing::debug!(
80 "ShadowNode::execute — instances={}, shadow_map={:?}, light_dir=({:.2},{:.2},{:.2})",
81 self.mesh_instances.len(),
82 self.shadow_map,
83 light_dir.x, light_dir.y, light_dir.z,
84 );
85
86 let shadow_view = match ctx.registry.get_texture_view(self.shadow_map) {
88 Some(v) => v,
89 None => {
90 tracing::error!(
91 "ShadowNode: missing shadow map texture view for {:?}",
92 self.shadow_map,
93 );
94 return;
95 }
96 };
97
98 let mut pass = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
100 label: Some("Shadow Pass (Depth-Only)"),
101 color_attachments: &[], depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
103 view: &shadow_view,
104 depth_ops: Some(wgpu::Operations {
105 load: wgpu::LoadOp::Clear(1.0),
106 store: wgpu::StoreOp::Store,
107 }),
108 stencil_ops: None,
109 }),
110 timestamp_writes: None,
111 occlusion_query_set: None,
112 multiview_mask: None,
113 });
114
115 for mesh in self.mesh_instances.iter() {
117 pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
119 pass.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
120
121 pass.draw_indexed(0..mesh.index_count, 0, 0..1);
123 }
124 }
125}