use crate::{
core::{
algebra::{Matrix4, Vector2},
color::Color,
math::Rect,
sstorage::ImmutableString,
},
renderer::{
bundle::{BundleRenderContext, RenderDataBundleStorage, SurfaceInstanceData},
cache::{
shader::ShaderCache,
uniform::{UniformBufferCache, UniformMemoryAllocator},
},
debug_renderer::DebugRenderer,
framework::{
buffer::BufferUsage,
error::FrameworkError,
framebuffer::{
Attachment, AttachmentKind, BufferLocation, FrameBuffer, ResourceBindGroup,
ResourceBinding,
},
geometry_buffer::GeometryBuffer,
gpu_texture::{GpuTexture, PixelKind},
server::GraphicsServer,
uniform::StaticUniformBuffer,
BlendFactor, BlendFunc, BlendParameters, DrawParameters, ElementRange,
GeometryBufferExt,
},
gbuffer::decal::DecalShader,
occlusion::OcclusionTester,
FallbackResources, GeometryCache, QualitySettings, RenderPassStatistics, TextureCache,
},
scene::{
camera::Camera,
decal::Decal,
graph::Graph,
mesh::{surface::SurfaceData, RenderPath},
},
};
use fxhash::FxHashSet;
use std::{cell::RefCell, rc::Rc};
mod decal;
pub struct GBuffer {
framebuffer: Box<dyn FrameBuffer>,
decal_framebuffer: Box<dyn FrameBuffer>,
pub width: i32,
pub height: i32,
cube: Box<dyn GeometryBuffer>,
decal_shader: DecalShader,
render_pass_name: ImmutableString,
occlusion_tester: OcclusionTester,
}
pub(crate) struct GBufferRenderContext<'a, 'b> {
pub server: &'a dyn GraphicsServer,
pub camera: &'b Camera,
pub geom_cache: &'a mut GeometryCache,
pub bundle_storage: &'a RenderDataBundleStorage,
pub texture_cache: &'a mut TextureCache,
pub shader_cache: &'a mut ShaderCache,
pub fallback_resources: &'a FallbackResources,
pub quality_settings: &'a QualitySettings,
pub graph: &'b Graph,
pub uniform_buffer_cache: &'a mut UniformBufferCache,
pub uniform_memory_allocator: &'a mut UniformMemoryAllocator,
#[allow(dead_code)]
pub screen_space_debug_renderer: &'a mut DebugRenderer,
pub unit_quad: &'a dyn GeometryBuffer,
}
impl GBuffer {
pub fn new(
server: &dyn GraphicsServer,
width: usize,
height: usize,
) -> Result<Self, FrameworkError> {
let diffuse_texture = server.create_2d_render_target(PixelKind::RGBA8, width, height)?;
let normal_texture = server.create_2d_render_target(PixelKind::RGBA8, width, height)?;
let framebuffer = server.create_frame_buffer(
Some(Attachment {
kind: AttachmentKind::DepthStencil,
texture: server.create_2d_render_target(PixelKind::D24S8, width, height)?,
}),
vec![
Attachment {
kind: AttachmentKind::Color,
texture: diffuse_texture.clone(),
},
Attachment {
kind: AttachmentKind::Color,
texture: normal_texture.clone(),
},
Attachment {
kind: AttachmentKind::Color,
texture: server.create_2d_render_target(PixelKind::RGBA16F, width, height)?,
},
Attachment {
kind: AttachmentKind::Color,
texture: server.create_2d_render_target(PixelKind::RGBA8, width, height)?,
},
Attachment {
kind: AttachmentKind::Color,
texture: server.create_2d_render_target(PixelKind::R8UI, width, height)?,
},
],
)?;
let decal_framebuffer = server.create_frame_buffer(
None,
vec![
Attachment {
kind: AttachmentKind::Color,
texture: diffuse_texture,
},
Attachment {
kind: AttachmentKind::Color,
texture: normal_texture,
},
],
)?;
Ok(Self {
framebuffer,
width: width as i32,
height: height as i32,
decal_shader: DecalShader::new(server)?,
cube: <dyn GeometryBuffer>::from_surface_data(
&SurfaceData::make_cube(Matrix4::identity()),
BufferUsage::StaticDraw,
server,
)?,
decal_framebuffer,
render_pass_name: ImmutableString::new("GBuffer"),
occlusion_tester: OcclusionTester::new(server, width, height, 16)?,
})
}
pub fn framebuffer(&self) -> &dyn FrameBuffer {
&*self.framebuffer
}
pub fn depth(&self) -> Rc<RefCell<dyn GpuTexture>> {
self.framebuffer.depth_attachment().unwrap().texture.clone()
}
pub fn diffuse_texture(&self) -> Rc<RefCell<dyn GpuTexture>> {
self.framebuffer.color_attachments()[0].texture.clone()
}
pub fn normal_texture(&self) -> Rc<RefCell<dyn GpuTexture>> {
self.framebuffer.color_attachments()[1].texture.clone()
}
pub fn ambient_texture(&self) -> Rc<RefCell<dyn GpuTexture>> {
self.framebuffer.color_attachments()[2].texture.clone()
}
pub fn material_texture(&self) -> Rc<RefCell<dyn GpuTexture>> {
self.framebuffer.color_attachments()[3].texture.clone()
}
pub fn decal_mask_texture(&self) -> Rc<RefCell<dyn GpuTexture>> {
self.framebuffer.color_attachments()[4].texture.clone()
}
pub(crate) fn fill(
&mut self,
args: GBufferRenderContext,
) -> Result<RenderPassStatistics, FrameworkError> {
let mut statistics = RenderPassStatistics::default();
let GBufferRenderContext {
server,
camera,
geom_cache,
bundle_storage,
texture_cache,
shader_cache,
quality_settings,
fallback_resources,
graph,
uniform_buffer_cache,
unit_quad,
uniform_memory_allocator,
..
} = args;
let view_projection = camera.view_projection_matrix();
if quality_settings.use_occlusion_culling {
self.occlusion_tester.try_query_visibility_results(graph);
};
let viewport = Rect::new(0, 0, self.width, self.height);
self.framebuffer.clear(
viewport,
Some(Color::from_rgba(0, 0, 0, 0)),
Some(1.0),
Some(0),
);
let grid_cell = self
.occlusion_tester
.grid_cache
.cell(camera.global_position());
let instance_filter = |instance: &SurfaceInstanceData| {
!quality_settings.use_occlusion_culling
|| grid_cell.map_or(true, |cell| cell.is_visible(instance.node_handle))
};
statistics += bundle_storage.render_to_frame_buffer(
server,
geom_cache,
shader_cache,
|bundle| bundle.render_path == RenderPath::Deferred,
instance_filter,
BundleRenderContext {
texture_cache,
render_pass_name: &self.render_pass_name,
frame_buffer: &mut *self.framebuffer,
viewport,
uniform_memory_allocator,
use_pom: quality_settings.use_parallax_mapping,
light_position: &Default::default(),
fallback_resources,
ambient_light: Color::WHITE, scene_depth: None, },
)?;
if quality_settings.use_occlusion_culling {
let mut objects = FxHashSet::default();
for bundle in bundle_storage.bundles.iter() {
for instance in bundle.instances.iter() {
objects.insert(instance.node_handle);
}
}
self.occlusion_tester.try_run_visibility_test(
graph,
None,
unit_quad,
objects.iter(),
&*self.framebuffer,
camera.global_position(),
view_projection,
uniform_buffer_cache,
)?;
}
let inv_view_proj = view_projection.try_inverse().unwrap_or_default();
let depth = self.depth();
let decal_mask = self.decal_mask_texture();
let resolution = Vector2::new(self.width as f32, self.height as f32);
let unit_cube = &self.cube;
for decal in graph.linear_iter().filter_map(|n| n.cast::<Decal>()) {
let shader = &self.decal_shader;
let program = &*self.decal_shader.program;
let world_view_proj = view_projection * decal.global_transform();
let diffuse_texture = decal
.diffuse_texture()
.and_then(|t| texture_cache.get(server, t))
.unwrap_or(&fallback_resources.white_dummy)
.clone();
let normal_texture = decal
.normal_texture()
.and_then(|t| texture_cache.get(server, t))
.unwrap_or(&fallback_resources.normal_dummy)
.clone();
statistics += self.decal_framebuffer.draw(
&**unit_cube,
viewport,
program,
&DrawParameters {
cull_face: None,
color_write: Default::default(),
depth_write: false,
stencil_test: None,
depth_test: None,
blend: Some(BlendParameters {
func: BlendFunc::new(BlendFactor::SrcAlpha, BlendFactor::OneMinusSrcAlpha),
..Default::default()
}),
stencil_op: Default::default(),
scissor_box: None,
},
&[ResourceBindGroup {
bindings: &[
ResourceBinding::texture(&depth, &shader.scene_depth),
ResourceBinding::texture(&diffuse_texture, &shader.diffuse_texture),
ResourceBinding::texture(&normal_texture, &shader.normal_texture),
ResourceBinding::texture(&decal_mask, &shader.decal_mask),
ResourceBinding::Buffer {
buffer: uniform_buffer_cache.write(
StaticUniformBuffer::<256>::new()
.with(&world_view_proj)
.with(&inv_view_proj)
.with(
&decal.global_transform().try_inverse().unwrap_or_default(),
)
.with(&resolution)
.with(&decal.color().srgb_to_linear_f32())
.with(&(decal.layer() as u32)),
)?,
binding: BufferLocation::Auto {
shader_location: shader.uniform_buffer_binding,
},
data_usage: Default::default(),
},
],
}],
ElementRange::Full,
)?;
}
Ok(statistics)
}
}