1use std::collections::HashMap;
2
3use crate::{
4 components::{
5 ColorsGPU, DiffuseTex, EnvironmentMapGpu, FacesGPU, ModelMatrix, Name, NormalTex, NormalsGPU, Renderable, RoughnessTex, ShadowCaster,
6 ShadowMap, TangentsGPU, UVsGPU, VertsGPU, VisMesh,
7 },
8 config::RenderConfig,
9 forward_renderer::{bind_group_collection::BindGroupCollection, locals::LocalEntData},
10 light::Light,
11 scene::Scene,
12 selector::Selector,
13};
14use easy_wgpu::{
15 bind_group::{BindGroupBuilder, BindGroupDesc, BindGroupWrapper},
16 bind_group_layout::{BindGroupLayoutBuilder, BindGroupLayoutDesc},
17 buffer::Buffer,
18};
19use easy_wgpu::{
22 gpu::Gpu,
23 texture::{TexParams, Texture},
24 utils::create_empty_group,
25};
26use gloss_hecs::Entity;
27use log::debug;
28
29use super::{pipeline_runner::PipelineRunner, upload_pass::PerFrameUniforms};
30
31use easy_wgpu::pipeline::RenderPipelineDescBuilder;
32
33use super::upload_pass::MAX_NUM_SHADOWS;
34use encase;
35
36use gloss_utils::numerical::align;
37
38#[include_wgsl_oil::include_wgsl_oil("../../../shaders/gbuffer_mesh_vert.wgsl")]
40mod vert_shader_code {}
41#[allow(clippy::approx_constant)]
42#[include_wgsl_oil::include_wgsl_oil("../../../shaders/gbuffer_mesh_frag.wgsl")]
43mod frag_shader_code {}
44
45pub struct MeshPipeline {
47 render_pipeline: wgpu::RenderPipeline,
48 _empty_group: wgpu::BindGroup,
49 locals_uniform: Buffer, locals_bind_groups: LocalsBindGroups,
51 input_layout: wgpu::BindGroupLayout,
54 input_bind_group: Option<BindGroupWrapper>,
55 local_scene: Scene,
59 passthrough_light: Light,
63}
64
65impl MeshPipeline {
66 pub fn new(gpu: &Gpu, params: &RenderConfig, color_target_format: wgpu::TextureFormat, depth_target_format: wgpu::TextureFormat) -> Self {
70 const_assert!(std::mem::size_of::<Locals>() % 16 == 0);
72
73 let input_layout_desc = Self::input_layout_desc();
74 let input_layout = input_layout_desc.clone().into_bind_group_layout(gpu.device());
75
76 let render_pipeline = RenderPipelineDescBuilder::new()
78 .label("mesh_pipeline")
79 .shader_code_vert(vert_shader_code::SOURCE)
81 .shader_code_frag(frag_shader_code::SOURCE)
82 .shader_label("mesh_shader")
83 .add_bind_group_layout_desc(PerFrameUniforms::build_layout_desc())
84 .add_bind_group_layout_desc(input_layout_desc)
85 .add_bind_group_layout_desc(LocalsBindGroups::build_layout_desc())
86 .add_vertex_buffer_layout(VertsGPU::vertex_buffer_layout::<0>())
87 .add_vertex_buffer_layout(UVsGPU::vertex_buffer_layout::<1>())
88 .add_vertex_buffer_layout(NormalsGPU::vertex_buffer_layout::<2>())
89 .add_vertex_buffer_layout(TangentsGPU::vertex_buffer_layout::<3>())
90 .add_vertex_buffer_layout(ColorsGPU::vertex_buffer_layout::<4>())
91 .add_render_target(wgpu::ColorTargetState {
92 format: color_target_format,
93 blend: None,
94 write_mask: wgpu::ColorWrites::ALL,
95 })
96 .depth_state(Some(wgpu::DepthStencilState {
97 format: depth_target_format,
98 depth_write_enabled: true,
99 depth_compare: wgpu::CompareFunction::Greater,
100 stencil: wgpu::StencilState {
101 front: wgpu::StencilFaceState {
102 compare: wgpu::CompareFunction::Always,
103 fail_op: wgpu::StencilOperation::Replace,
104 depth_fail_op: wgpu::StencilOperation::Replace,
105 pass_op: wgpu::StencilOperation::Replace,
106 },
107 back: wgpu::StencilFaceState {
108 compare: wgpu::CompareFunction::Always,
109 fail_op: wgpu::StencilOperation::Replace,
110 depth_fail_op: wgpu::StencilOperation::Replace,
111 pass_op: wgpu::StencilOperation::Replace,
112 },
113 read_mask: 0x00,
114 write_mask: 0xFF,
115 },
116 bias: wgpu::DepthBiasState::default(),
117 }))
118 .multisample(wgpu::MultisampleState {
119 count: params.msaa_nr_samples,
120 ..Default::default()
121 })
122 .build_pipeline(gpu.device());
123
124 let empty_group = create_empty_group(gpu.device());
125
126 let size_bytes = 0x10000;
127 let usage = wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM;
128 let locals_uniform = Buffer::new_empty(gpu.device(), usage, Some("local_buffer"), size_bytes);
129
130 let locals_bind_groups = LocalsBindGroups::new(gpu);
131
132 let mut local_scene = Scene::new();
135 let passthrough_tex = Texture::new(
138 gpu.device(),
139 4,
140 4,
141 wgpu::TextureFormat::Depth32Float,
142 wgpu::TextureUsages::TEXTURE_BINDING,
143 TexParams::default(),
144 );
145 let passthrough_light = Light::new("compose_pass_passthrough_light", &mut local_scene);
148 let _ = local_scene.world.insert_one(
149 passthrough_light.entity,
150 ShadowMap {
151 tex_depth: passthrough_tex,
152 },
154 );
155
156 Self {
157 render_pipeline,
158 _empty_group: empty_group,
159 locals_uniform,
160 locals_bind_groups,
161 input_layout,
162 input_bind_group: None,
163 local_scene,
165 passthrough_light,
166 }
167 }
168}
169impl PipelineRunner for MeshPipeline {
170 type QueryItems<'a> = (
171 &'a VertsGPU,
172 &'a FacesGPU,
173 &'a UVsGPU,
174 &'a NormalsGPU,
175 &'a TangentsGPU,
176 &'a ColorsGPU,
177 &'a DiffuseTex,
178 &'a NormalTex,
179 &'a RoughnessTex,
180 &'a VisMesh,
181 &'a Name,
182 );
183 type QueryState<'a> = gloss_hecs::QueryBorrow<'a, gloss_hecs::With<Self::QueryItems<'a>, &'a Renderable>>;
184
185 fn query_state(scene: &Scene) -> Self::QueryState<'_> {
186 scene.world.query::<Self::QueryItems<'_>>().with::<&Renderable>()
187 }
188
189 fn prepare<'a>(&mut self, gpu: &Gpu, per_frame_uniforms: &PerFrameUniforms, scene: &'a Scene) -> Self::QueryState<'a> {
190 self.begin_pass();
191
192 self.update_locals(gpu, scene);
193
194 self.update_input_bind_group(gpu, scene, per_frame_uniforms);
196
197 Self::query_state(scene)
198 }
199
200 #[allow(clippy::too_many_lines)]
203 fn run<'r>(
204 &'r mut self,
205 render_pass: &mut wgpu::RenderPass<'r>,
206 per_frame_uniforms: &'r PerFrameUniforms,
207 _render_params: &RenderConfig,
208 query_state: &'r mut Self::QueryState<'_>,
209 scene: &Scene,
210 ) {
211 if query_state.iter().count() == 0 {
213 return;
214 }
215 let selector = scene.get_resource::<&Selector>();
216 render_pass.set_pipeline(&self.render_pipeline);
217
218 render_pass.set_bind_group(0, &per_frame_uniforms.bind_group, &[]);
220 render_pass.set_bind_group(1, self.input_bind_group.as_ref().unwrap().bg(), &[]);
222
223 render_pass.set_stencil_reference(0);
232 for (_id, (verts, faces, uvs, normals, tangents, colors, _diffuse_tex, _normal_tex, _roughness_tex, vis_mesh, name)) in query_state.iter() {
233 if !vis_mesh.show_mesh {
234 continue;
235 }
236
237 if let Ok(ref current_selector) = selector {
239 if name.0 == current_selector.current_selected {
240 continue;
241 }
242 }
243
244 let (local_bg, offset) = &self.locals_bind_groups.mesh2local_bind[&name.0.clone()];
246 render_pass.set_bind_group(2, local_bg.bg(), &[*offset]);
247 render_pass.set_vertex_buffer(0, verts.buf.slice(..));
248 render_pass.set_vertex_buffer(1, uvs.buf.slice(..));
249 render_pass.set_vertex_buffer(2, normals.buf.slice(..));
250 render_pass.set_vertex_buffer(3, tangents.buf.slice(..));
251 render_pass.set_vertex_buffer(4, colors.buf.slice(..));
252 render_pass.set_index_buffer(faces.buf.slice(..), wgpu::IndexFormat::Uint32);
253 render_pass.draw_indexed(0..faces.nr_triangles * 3, 0, 0..1);
254 }
255
256 for (_id, (verts, faces, uvs, normals, tangents, colors, _diffuse_tex, _normal_tex, _roughness_tex, vis_mesh, name)) in query_state.iter() {
257 if let Ok(ref current_selector) = selector {
258 if name.0 == current_selector.current_selected {
259 if !vis_mesh.show_mesh {
260 return;
261 }
262
263 render_pass.set_stencil_reference(1);
264
265 let (local_bg, offset) = &self.locals_bind_groups.mesh2local_bind[¤t_selector.current_selected.clone()];
266 render_pass.set_bind_group(2, local_bg.bg(), &[*offset]);
267 render_pass.set_vertex_buffer(0, verts.buf.slice(..));
268 render_pass.set_vertex_buffer(1, uvs.buf.slice(..));
269 render_pass.set_vertex_buffer(2, normals.buf.slice(..));
270 render_pass.set_vertex_buffer(3, tangents.buf.slice(..));
271 render_pass.set_vertex_buffer(4, colors.buf.slice(..));
272 render_pass.set_index_buffer(faces.buf.slice(..), wgpu::IndexFormat::Uint32);
273 render_pass.draw_indexed(0..faces.nr_triangles * 3, 0, 0..1);
274 }
275 }
276 }
277
278 }
308
309 fn begin_pass(&mut self) {}
310
311 fn input_layout_desc() -> BindGroupLayoutDesc {
312 BindGroupLayoutBuilder::new()
313 .label("compose_input_layout")
314 .add_entry_cubemap(wgpu::ShaderStages::FRAGMENT, wgpu::TextureSampleType::Float { filterable: true })
316 .add_entry_cubemap(wgpu::ShaderStages::FRAGMENT, wgpu::TextureSampleType::Float { filterable: true })
318 .add_entries_tex(
320 wgpu::ShaderStages::FRAGMENT,
321 wgpu::TextureSampleType::Depth, MAX_NUM_SHADOWS,
325 )
326 .build()
327 }
328
329 fn update_input_bind_group(&mut self, gpu: &Gpu, scene: &Scene, per_frame_uniforms: &PerFrameUniforms) {
330 let env_map = scene.get_resource::<&EnvironmentMapGpu>().unwrap();
332
333 let mut shadow_maps = Vec::new();
334
335 for i in 0..MAX_NUM_SHADOWS {
338 let is_within_valid_lights: bool = i < per_frame_uniforms.idx_ubo2light.len();
339 if is_within_valid_lights && scene.world.has::<ShadowCaster>(per_frame_uniforms.idx_ubo2light[i]).unwrap() {
340 let entity = per_frame_uniforms.idx_ubo2light[i];
341 let shadow = scene
342 .get_comp::<&ShadowMap>(&entity)
343 .expect("The lights who have a ShadowCaster should also have ShadowMap at this point.");
344 shadow_maps.push(shadow);
345 } else {
346 let shadow: gloss_hecs::Ref<'_, ShadowMap> = self
348 .local_scene
349 .get_comp::<&ShadowMap>(&self.passthrough_light.entity)
350 .expect("Dummy light should have ShadowMap");
351 shadow_maps.push(shadow);
352 }
353 }
354
355 let entries = BindGroupBuilder::new()
356 .add_entry_tex(&env_map.diffuse_tex)
357 .add_entry_tex(&env_map.specular_tex)
358 .add_entry_tex(&shadow_maps[0].tex_depth)
359 .add_entry_tex(&shadow_maps[1].tex_depth)
360 .add_entry_tex(&shadow_maps[2].tex_depth)
361 .build_entries();
362 let stale = self.input_bind_group.as_ref().is_none_or(|b| b.is_stale(&entries));
364 if stale {
365 debug!("compose input bind group is stale, recreating");
366 self.input_bind_group = Some(BindGroupDesc::new("compose_input_bg", entries).into_bind_group_wrapper(gpu.device(), &self.input_layout));
367 }
368 }
369
370 fn update_locals(&mut self, gpu: &Gpu, scene: &Scene) {
373 Self::update_locals_inner::<Locals, _>(
374 gpu,
375 scene,
376 &mut self.locals_uniform,
377 &mut self.locals_bind_groups,
378 &mut Self::query_state(scene),
379 );
380 }
381}
382
383#[repr(C)]
385#[derive(Clone, Copy, encase::ShaderType)]
386struct Locals {
387 model_matrix: nalgebra::Matrix4<f32>,
388 color_type: i32,
389 solid_color: nalgebra::Vector4<f32>,
390 metalness: f32,
391 perceptual_roughness: f32,
392 roughness_black_lvl: f32,
393 uv_scale: f32,
394 is_floor: u32,
395 pad_c: f32,
398 pad_d: f32,
399}
400impl LocalEntData for Locals {
401 fn new(entity: Entity, scene: &Scene) -> Self {
402 let model_matrix = scene.get_comp::<&ModelMatrix>(&entity).unwrap().0.to_homogeneous();
403 let vis_mesh = scene.get_comp::<&VisMesh>(&entity).unwrap();
404 let color_type = vis_mesh.color_type as i32;
405 let is_floor = if let Some(floor) = scene.get_floor() {
406 floor.entity == entity
407 } else {
408 false
409 };
410 let is_floor = u32::from(is_floor);
411 Locals {
412 model_matrix,
413 color_type,
414 solid_color: vis_mesh.solid_color,
415 metalness: vis_mesh.metalness,
416 perceptual_roughness: vis_mesh.perceptual_roughness,
417 roughness_black_lvl: vis_mesh.roughness_black_lvl,
418 uv_scale: vis_mesh.uv_scale,
419 is_floor,
420 pad_c: 0.0,
421 pad_d: 0.0,
422 }
423 }
424}
425
426struct LocalsBindGroups {
427 layout: wgpu::BindGroupLayout,
428 pub mesh2local_bind: HashMap<String, (BindGroupWrapper, u32)>,
429}
430impl BindGroupCollection for LocalsBindGroups {
431 fn new(gpu: &Gpu) -> Self {
432 Self {
433 layout: Self::build_layout_desc().into_bind_group_layout(gpu.device()),
434 mesh2local_bind: HashMap::default(),
435 }
436 }
437
438 fn build_layout_desc() -> BindGroupLayoutDesc {
441 BindGroupLayoutBuilder::new()
442 .label("gbuffer_pass_locals_layout")
443 .add_entry_uniform(
445 wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
446 true,
447 wgpu::BufferSize::new(u64::from(align(u32::try_from(std::mem::size_of::<Locals>()).unwrap(), 256))),
448 )
449 .add_entry_tex(wgpu::ShaderStages::FRAGMENT, wgpu::TextureSampleType::Float { filterable: true })
451 .add_entry_tex(wgpu::ShaderStages::FRAGMENT, wgpu::TextureSampleType::Float { filterable: true })
453 .add_entry_tex(wgpu::ShaderStages::FRAGMENT, wgpu::TextureSampleType::Float { filterable: true })
455 .build()
456 }
457
458 fn update_bind_group(&mut self, entity: Entity, gpu: &Gpu, mesh_name: &str, ubo: &Buffer, offset_in_ubo: u32, scene: &Scene) {
459 let diffuse_tex = &scene.get_comp::<&DiffuseTex>(&entity).unwrap().0;
461 let normal_tex = &scene.get_comp::<&NormalTex>(&entity).unwrap().0;
462 let roughness_tex = &scene.get_comp::<&RoughnessTex>(&entity).unwrap().0;
463
464 let entries = BindGroupBuilder::new()
465 .add_entry_buf_chunk::<Locals>(&ubo.buffer)
466 .add_entry_tex(diffuse_tex)
467 .add_entry_tex(normal_tex)
468 .add_entry_tex(roughness_tex)
469 .build_entries();
470
471 self.update_if_stale(mesh_name, entries, offset_in_ubo, gpu);
472 }
473
474 fn get_layout(&self) -> &wgpu::BindGroupLayout {
475 &self.layout
476 }
477 fn get_mut_entity2binds(&mut self) -> &mut HashMap<String, (BindGroupWrapper, u32)> {
478 &mut self.mesh2local_bind
479 }
480}