use std::sync::Arc;
use glam::{UVec2, Vec4};
use rend3::{types::TextureHandle, util::output::OutputFrame, ModeData, RenderRoutine, Renderer};
use wgpu::{
Color, CommandBuffer, Device, Extent3d, LoadOp, Operations, RenderPassColorAttachment,
RenderPassDepthStencilAttachment, RenderPassDescriptor, TextureDescriptor, TextureDimension, TextureFormat,
TextureUsages, TextureView, TextureViewDescriptor,
};
pub mod common;
pub mod culling;
pub mod directional;
pub mod opaque;
pub mod shaders;
pub mod skybox;
pub mod tonemapping;
pub mod uniforms;
pub mod vertex;
pub struct PbrRenderRoutine {
pub interfaces: common::interfaces::ShaderInterfaces,
pub samplers: common::samplers::Samplers,
pub cpu_culler: culling::cpu::CpuCuller,
pub gpu_culler: ModeData<(), culling::gpu::GpuCuller>,
pub shadow_passes: directional::DirectionalShadowPass,
pub skybox_pass: skybox::SkyboxPass,
pub opaque_pass: opaque::OpaquePass,
pub tonemapping_pass: tonemapping::TonemappingPass,
pub skybox_texture: Option<TextureHandle>,
pub internal_buffer: TextureView,
pub internal_depth_buffer: TextureView,
}
impl PbrRenderRoutine {
pub fn new(renderer: &Renderer, resolution: UVec2) -> Self {
let device = renderer.device();
let mode = renderer.mode();
let interfaces = common::interfaces::ShaderInterfaces::new(device);
let samplers = common::samplers::Samplers::new(device, mode, &interfaces.samplers_bgl);
let cpu_culler = culling::cpu::CpuCuller::new();
let gpu_culler = mode.into_data(|| (), || culling::gpu::GpuCuller::new(device));
let gpu_d2_texture_manager_guard = mode.into_data(|| (), || renderer.d2_texture_manager.read());
let gpu_d2_texture_bgl = gpu_d2_texture_manager_guard
.as_ref()
.map(|_| (), |guard| guard.gpu_bgl());
let directional_light_manager = &renderer.directional_light_manager.read();
let colorless_depth_pipeline = Arc::new(common::depth_pass::build_depth_pass_shader(
common::depth_pass::BuildDepthPassShaderArgs {
mode,
device,
interfaces: &interfaces,
texture_bgl: gpu_d2_texture_bgl,
materials: &renderer.material_manager.read(),
include_color: false,
},
));
let colored_depth_pipeline = Arc::new(common::depth_pass::build_depth_pass_shader(
common::depth_pass::BuildDepthPassShaderArgs {
mode,
device,
interfaces: &interfaces,
texture_bgl: gpu_d2_texture_bgl,
materials: &renderer.material_manager.read(),
include_color: true,
},
));
let skybox_pipeline = common::skybox_pass::build_skybox_shader(common::skybox_pass::BuildSkyboxShaderArgs {
mode,
device,
interfaces: &interfaces,
});
let opaque_pipeline = Arc::new(common::opaque_pass::build_opaque_pass_shader(
common::opaque_pass::BuildOpaquePassShaderArgs {
mode,
device,
interfaces: &interfaces,
directional_light_bgl: directional_light_manager.get_bgl(),
texture_bgl: gpu_d2_texture_bgl,
materials: &renderer.material_manager.read(),
},
));
let shadow_passes = directional::DirectionalShadowPass::new(Arc::clone(&colorless_depth_pipeline));
let skybox_pass = skybox::SkyboxPass::new(skybox_pipeline);
let opaque_pass = opaque::OpaquePass::new(Arc::clone(&colored_depth_pipeline), Arc::clone(&opaque_pipeline));
let tonemapping_pass = tonemapping::TonemappingPass::new(tonemapping::TonemappingPassNewArgs {
device,
interfaces: &interfaces,
});
let internal_buffer = create_internal_buffer(device, resolution);
let internal_depth_buffer = create_internal_depth_buffer(device, resolution);
Self {
interfaces,
samplers,
cpu_culler,
gpu_culler,
shadow_passes,
skybox_pass,
opaque_pass,
tonemapping_pass,
skybox_texture: None,
internal_buffer,
internal_depth_buffer,
}
}
pub fn set_background_texture(&mut self, handle: Option<TextureHandle>) {
self.skybox_texture = handle;
}
pub fn resize(&mut self, device: &Device, resolution: UVec2) {
self.internal_buffer = create_internal_buffer(device, resolution);
self.internal_depth_buffer = create_internal_depth_buffer(device, resolution);
}
}
impl RenderRoutine for PbrRenderRoutine {
fn render(&mut self, renderer: Arc<Renderer>, encoders: &mut Vec<CommandBuffer>, frame: &OutputFrame) {
let mut encoder = renderer.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("primary encoder"),
});
let mesh_manager = renderer.mesh_manager.read();
let mut directional_light = renderer.directional_light_manager.write();
let mut materials = renderer.material_manager.write();
let mut d2_textures = renderer.d2_texture_manager.write();
let mut d2c_textures = renderer.d2c_texture_manager.write();
directional_light.ready(&renderer.device, &renderer.queue);
materials.ready(&renderer.device, &renderer.queue, &d2_textures);
let d2_texture_output = d2_textures.ready(&renderer.device);
let _d2c_texture_output = d2c_textures.ready(&renderer.device);
let objects = renderer.object_manager.read().ready();
self.skybox_pass.update_skybox(skybox::UpdateSkyboxArgs {
device: &renderer.device,
d2c_texture_manager: &d2c_textures,
interfaces: &self.interfaces,
new_skybox_handle: self.skybox_texture,
});
let culler = self.gpu_culler.as_ref().map_cpu(|_| &self.cpu_culler);
let culled_lights = self
.shadow_passes
.cull_shadows(directional::DirectionalShadowPassCullShadowsArgs {
device: &renderer.device,
encoder: &mut encoder,
culler,
materials: &materials,
interfaces: &self.interfaces,
lights: &directional_light,
objects: &objects,
});
let global_resources = renderer.global_resources.read();
let culled_objects = self.opaque_pass.cull_opaque(opaque::OpaquePassCullArgs {
device: &renderer.device,
encoder: &mut encoder,
culler,
materials: &materials,
interfaces: &self.interfaces,
camera: &global_resources.camera,
objects: &objects,
});
let d2_texture_output_bg_ref = d2_texture_output.bg.as_ref().map(|_| (), |a| &**a);
self.shadow_passes
.draw_culled_shadows(directional::DirectionalShadowPassDrawCulledShadowsArgs {
encoder: &mut encoder,
materials: &materials,
meshes: mesh_manager.buffers(),
samplers: &self.samplers,
texture_bg: d2_texture_output_bg_ref,
culled_lights: &culled_lights,
});
let primary_camera_uniform_bg = uniforms::create_shader_uniform(uniforms::CreateShaderUniformArgs {
device: &renderer.device,
camera: &global_resources.camera,
interfaces: &self.interfaces,
ambient: Vec4::new(0.0, 0.0, 0.0, 1.0),
});
let mut rpass = encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("primary renderpass"),
color_attachments: &[RenderPassColorAttachment {
view: &self.internal_buffer,
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(Color::BLACK),
store: true,
},
}],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &self.internal_depth_buffer,
depth_ops: Some(Operations {
load: LoadOp::Clear(0.0),
store: true,
}),
stencil_ops: None,
}),
});
self.opaque_pass.prepass(opaque::OpaquePassPrepassArgs {
rpass: &mut rpass,
materials: &materials,
meshes: mesh_manager.buffers(),
samplers: &self.samplers,
texture_bg: d2_texture_output_bg_ref,
culled_objects: &culled_objects,
});
self.skybox_pass.draw_skybox(skybox::SkyboxPassDrawArgs {
rpass: &mut rpass,
samplers: &self.samplers,
shader_uniform_bg: &primary_camera_uniform_bg,
});
self.opaque_pass.draw(opaque::OpaquePassDrawArgs {
rpass: &mut rpass,
materials: &materials,
meshes: mesh_manager.buffers(),
samplers: &self.samplers,
directional_light_bg: directional_light.get_bg(),
texture_bg: d2_texture_output_bg_ref,
shader_uniform_bg: &primary_camera_uniform_bg,
culled_objects: &culled_objects,
});
drop(rpass);
self.tonemapping_pass.blit(tonemapping::TonemappingPassBlitArgs {
device: &renderer.device,
encoder: &mut encoder,
interfaces: &self.interfaces,
samplers: &self.samplers,
source: &self.internal_buffer,
target: frame.as_view(),
});
encoders.push(encoder.finish())
}
}
fn create_internal_buffer(device: &Device, resolution: UVec2) -> TextureView {
device
.create_texture(&TextureDescriptor {
label: Some("internal renderbuffer"),
size: Extent3d {
width: resolution.x,
height: resolution.y,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Rgba16Float,
usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_SRC,
})
.create_view(&TextureViewDescriptor::default())
}
fn create_internal_depth_buffer(device: &Device, resolution: UVec2) -> TextureView {
device
.create_texture(&TextureDescriptor {
label: Some("internal depth renderbuffer"),
size: Extent3d {
width: resolution.x,
height: resolution.y,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Depth32Float,
usage: TextureUsages::RENDER_ATTACHMENT,
})
.create_view(&TextureViewDescriptor::default())
}