use crate::kvasir::{ExecutionContext, KvasirNode, ResourceId};
use crate::kvasir::nodes::PassId;
use glam::Mat4;
use wgpu::Buffer;
#[derive(Debug, Clone, Copy)]
pub struct DirectionalLight {
pub direction: glam::Vec3,
pub color: glam::Vec3,
pub intensity: f32,
}
impl Default for DirectionalLight {
fn default() -> Self {
Self {
direction: glam::Vec3::new(0.0, -1.0, 0.0),
color: glam::Vec3::ONE,
intensity: 1.0,
}
}
}
#[derive(Debug, Clone)]
pub struct GpuMesh3d {
pub vertex_buffer: Buffer,
pub index_buffer: Buffer,
pub index_count: u32,
pub transform: Mat4,
}
pub struct ShadowNode {
pub light: DirectionalLight,
pub shadow_map: ResourceId,
pub mesh_instances: Vec<GpuMesh3d>,
pub scene_radius: f32,
}
impl KvasirNode for ShadowNode {
fn label(&self) -> &'static str {
"ShadowPass"
}
fn inputs(&self) -> &[ResourceId] {
&[]
}
fn outputs(&self) -> &[ResourceId] {
std::slice::from_ref(&self.shadow_map)
}
fn pass_id(&self) -> PassId {
PassId::Shadow
}
fn execute(&self, ctx: &mut ExecutionContext) {
let light_dir = self.light.direction;
let scene_center = glam::Vec3::ZERO;
let light_pos = scene_center + light_dir * self.scene_radius * 2.0;
let light_view = glam::Mat4::look_at_lh(light_pos, scene_center, glam::Vec3::Y);
let r = self.scene_radius;
let light_proj = glam::Mat4::orthographic_lh(-r, r, -r, r, 0.0, self.scene_radius * 4.0);
let _light_vp = light_proj * light_view;
tracing::debug!(
"ShadowNode::execute — instances={}, shadow_map={:?}, light_dir=({:.2},{:.2},{:.2})",
self.mesh_instances.len(),
self.shadow_map,
light_dir.x, light_dir.y, light_dir.z,
);
let shadow_view = match ctx.registry.get_texture_view(self.shadow_map) {
Some(v) => v,
None => {
tracing::error!(
"ShadowNode: missing shadow map texture view for {:?}",
self.shadow_map,
);
return;
}
};
let mut pass = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Shadow Pass (Depth-Only)"),
color_attachments: &[], depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &shadow_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
for mesh in self.mesh_instances.iter() {
pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
pass.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
pass.draw_indexed(0..mesh.index_count, 0, 0..1);
}
}
}