use crate::kvasir::nodes::PassId;
use crate::kvasir::{ExecutionContext, KvasirNode, ResourceId};
use crate::passes::shadow::GpuMesh3d;
pub struct TransparentNode {
pub mesh_instances: Vec<GpuMesh3d>,
pub shadow_map: ResourceId,
pub camera_pos: glam::Vec3,
}
impl KvasirNode for TransparentNode {
fn label(&self) -> &'static str {
"Transparent3d"
}
fn inputs(&self) -> &[ResourceId] {
std::slice::from_ref(&self.shadow_map)
}
fn outputs(&self) -> &[ResourceId] {
&[]
}
fn pass_id(&self) -> PassId {
PassId::Transparent3d
}
fn execute(&self, ctx: &mut ExecutionContext) {
tracing::debug!(
"TransparentNode::execute — instances={}, shadow_map={:?}",
self.mesh_instances.len(),
self.shadow_map
);
if self.mesh_instances.is_empty() {
return;
}
let scene_view = match ctx
.registry
.get_texture_view(crate::kvasir::nodes::RES_SCENE)
{
Some(v) => v,
None => {
tracing::error!("TransparentNode: missing scene color target");
return;
}
};
let depth_view = ctx.depth_view;
let shadow_bind_group = match ctx.registry.get_texture_view(self.shadow_map) {
Some(shadow_view) => {
let shadow_sampler = match ctx.renderer.shadow_sampler.as_ref() {
Some(s) => s,
None => {
tracing::warn!("TransparentNode: missing shadow sampler");
return;
}
};
let ibl_view_owned = ctx
.registry
.get_texture_view(crate::kvasir::nodes::RES_BLUR_A);
let ibl_view = match &ibl_view_owned {
Some(view) => view,
None => &ctx.renderer.dummy_view,
};
Some(ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("TransparentNode PBR Material Bind Group"),
layout: &ctx.renderer.pbr_material_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&shadow_view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(shadow_sampler),
},
wgpu::BindGroupEntry {
binding: 8,
resource: wgpu::BindingResource::TextureView(ibl_view),
},
wgpu::BindGroupEntry {
binding: 9,
resource: wgpu::BindingResource::Sampler(&ctx.renderer.sampler),
},
wgpu::BindGroupEntry {
binding: 6,
resource: wgpu::BindingResource::TextureView(&ctx.renderer.dummy_view),
},
wgpu::BindGroupEntry {
binding: 7,
resource: wgpu::BindingResource::Sampler(&ctx.renderer.sampler),
},
],
}))
}
None => None,
};
let mut pass = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Transparent3d Pass (PBR + Shadows)"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &scene_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
},
depth_slice: None,
})],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: depth_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
pass.set_pipeline(&ctx.renderer.transparent_pipeline);
pass.set_bind_group(2, &ctx.renderer.berserker_bind_group, &[]);
if let Some(ref bg) = shadow_bind_group {
pass.set_bind_group(3, bg, &[]);
}
if let Some(ref inst_buffer) = ctx.renderer.instance_buffer_3d {
pass.set_vertex_buffer(1, inst_buffer.slice(..));
}
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);
let inst = mesh.instance_index;
pass.draw_indexed(0..mesh.index_count, 0, inst..(inst + 1));
}
}
}