cvkg_render_gpu/passes/
transparent.rs1use crate::kvasir::nodes::PassId;
5use crate::kvasir::{ExecutionContext, KvasirNode, ResourceId};
6use crate::passes::shadow::GpuMesh3d;
7
8pub struct TransparentNode {
11 pub mesh_instances: Vec<GpuMesh3d>,
13 pub shadow_map: ResourceId,
15 pub camera_pos: glam::Vec3,
17}
18
19impl KvasirNode for TransparentNode {
20 fn label(&self) -> &'static str {
21 "Transparent3d"
22 }
23
24 fn inputs(&self) -> &[ResourceId] {
25 std::slice::from_ref(&self.shadow_map)
26 }
27
28 fn outputs(&self) -> &[ResourceId] {
29 &[]
30 }
31
32 fn pass_id(&self) -> PassId {
33 PassId::Transparent3d
34 }
35
36 fn execute(&self, ctx: &mut ExecutionContext) {
37 tracing::debug!(
38 "TransparentNode::execute — instances={}, shadow_map={:?}",
39 self.mesh_instances.len(),
40 self.shadow_map
41 );
42
43 if self.mesh_instances.is_empty() {
44 return;
45 }
46
47 let scene_view = match ctx
49 .registry
50 .get_texture_view(crate::kvasir::nodes::RES_SCENE)
51 {
52 Some(v) => v,
53 None => {
54 tracing::error!("TransparentNode: missing scene color target");
55 return;
56 }
57 };
58 let depth_view = ctx.depth_view;
59
60 let shadow_bind_group = match ctx.registry.get_texture_view(self.shadow_map) {
62 Some(shadow_view) => {
63 let shadow_sampler = match ctx.renderer.shadow_sampler.as_ref() {
64 Some(s) => s,
65 None => {
66 tracing::warn!("TransparentNode: missing shadow sampler");
67 return;
68 }
69 };
70
71 let ibl_view_owned = ctx
72 .registry
73 .get_texture_view(crate::kvasir::nodes::RES_BLUR_A);
74 let ibl_view = match &ibl_view_owned {
75 Some(view) => view,
76 None => &ctx.renderer.dummy_view,
77 };
78
79 Some(ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
80 label: Some("TransparentNode PBR Material Bind Group"),
81 layout: &ctx.renderer.pbr_material_bind_group_layout,
82 entries: &[
83 wgpu::BindGroupEntry {
84 binding: 0,
85 resource: wgpu::BindingResource::TextureView(&shadow_view),
86 },
87 wgpu::BindGroupEntry {
88 binding: 1,
89 resource: wgpu::BindingResource::Sampler(shadow_sampler),
90 },
91 wgpu::BindGroupEntry {
92 binding: 8,
93 resource: wgpu::BindingResource::TextureView(ibl_view),
94 },
95 wgpu::BindGroupEntry {
96 binding: 9,
97 resource: wgpu::BindingResource::Sampler(&ctx.renderer.sampler),
98 },
99 wgpu::BindGroupEntry {
100 binding: 6,
101 resource: wgpu::BindingResource::TextureView(&ctx.renderer.dummy_view),
102 },
103 wgpu::BindGroupEntry {
104 binding: 7,
105 resource: wgpu::BindingResource::Sampler(&ctx.renderer.sampler),
106 },
107 ],
108 }))
109 }
110 None => None,
111 };
112
113 let mut pass = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
115 label: Some("Transparent3d Pass (PBR + Shadows)"),
116 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
117 view: &scene_view,
118 resolve_target: None,
119 ops: wgpu::Operations {
120 load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
122 },
123 depth_slice: None,
124 })],
125 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
126 view: depth_view,
127 depth_ops: Some(wgpu::Operations {
128 load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
130 }),
131 stencil_ops: None,
132 }),
133 timestamp_writes: None,
134 occlusion_query_set: None,
135 multiview_mask: None,
136 });
137
138 pass.set_pipeline(&ctx.renderer.transparent_pipeline);
140 pass.set_bind_group(2, &ctx.renderer.berserker_bind_group, &[]);
141 if let Some(ref bg) = shadow_bind_group {
142 pass.set_bind_group(3, bg, &[]);
143 }
144
145 if let Some(ref inst_buffer) = ctx.renderer.instance_buffer_3d {
146 pass.set_vertex_buffer(1, inst_buffer.slice(..));
147 }
148
149 for mesh in self.mesh_instances.iter() {
154 pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
158 pass.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
160 let inst = mesh.instance_index;
161 pass.draw_indexed(0..mesh.index_count, 0, inst..(inst + 1));
162 }
163 }
164}