use rinecs::{
SystemThreadLocal, EntitiesThreadLocal, ResourcesThreadLocal, Read,
Entity,
};
use crate::{
components::Visible,
renderer::{
pbr_material::MaterialCache,
resources,
components::RenderPlane as GpuRenderPlane,
geometry::{SubmeshBuffers},
},
transformation::RenderPlane,
material::MaterialRef,
};
#[cfg(feature="gl_multidraw_indirect")]
use super::geometry::{CommandBuffer, CommandBufferData};
#[cfg(feature="gl_material_unified_ubo")]
use super::pbr_material::MaterialPool;
use rin::{
gl,
window::{ Window, WindowT },
graphics::mvp,
};
use generational_arena::Arena;
use glin::UntypedOffscreenBuffer;
pub struct Renderer{
debug_material: MaterialRef,
}
impl Renderer{
pub(crate) fn new(debug_material: MaterialRef) -> Renderer{
Renderer{
debug_material
}
}
}
impl Renderer{
fn render<R, B>(&self,
glin: &glin::Context<R>,
camera_ubo: B,
renderplane: Option<Entity>,
entities: &EntitiesThreadLocal,
resources: &ResourcesThreadLocal)
where R: glin::RenderSurface,
B: glin::BufferRange<mvp::CameraData>
{
let material_cache = resources.get::<MaterialCache>().unwrap();
let programs_pool = resources.get::<Arena<gl::Program>>().unwrap();
let lighting_uniforms = resources.get::<resources::LightingUniforms>().unwrap();
#[cfg(feature="gl_material_unified_ubo")]
let materials_pool = resources.get::<MaterialPool>().unwrap();
let opaque_geom = resources.get::<resources::OpaqueSortedGeometry>().unwrap();
let translucent_geom = resources.get::<resources::TranslucentSortedGeometry>().unwrap();
#[cfg(not(feature="gl_multidraw_indirect"))]
fn render_vao<'a, R, V, U>(
glin: &glin::Context<R>,
vao: V,
uniforms: U,
program: &glin::Program,
num_instances: usize)
where R: glin::RenderSurface,
V: glin::VaoDraw,
U: IntoIterator<Item = &'a glin::program::Uniform>,
{
if num_instances > 1 {
glin.draw_instanced(vao, num_instances, program, uniforms)
.is_ok();
}else{
glin.draw(vao, program, uniforms)
.is_ok();
}
}
let model_data_gpu = resources.get::<resources::ModelMatricesBuffer>().unwrap();
let mut prev_program = None;
let mut prev_material = None;
#[cfg(not(feature="gl_material_unified_ubo"))]
let glin = glin.with(&[
gl::Property::DepthTest(true),
gl::UBOBindingPoints::Camera.to_buffer_binding(camera_ubo),
]);
#[cfg(feature="gl_material_unified_ubo")]
let glin = glin.with(&[
gl::Property::DepthTest(true),
gl::UBOBindingPoints::Camera.to_buffer_binding(camera_ubo),
gl::UBOBindingPoints::Material.to_buffer_binding(materials_pool.buffer())
]);
let mut glin_properties = glin.shallow_clone();
#[cfg(feature="gl_multidraw_indirect")]
{
let command_buffer = resources.get::<CommandBuffer>().unwrap();
let command_buffer_data = resources.get::<CommandBufferData>().unwrap();
for &(start, end) in command_buffer_data.segments_per_renderplane[&renderplane].iter() {
let geom_index = if start < opaque_geom.len(){
&opaque_geom[start]
}else{
&translucent_geom[start - opaque_geom.len()]
};
let material = &material_cache[geom_index.materialref];
let program = &programs_pool[*material.programref];
if let Some(submeshbuffer) = entities
.entity_components::<Read<SubmeshBuffers>>(&geom_index.geometryref)
{
if let Ok(vao) = submeshbuffer[geom_index.submesh]
.full_vao(&glin, resources, &model_data_gpu)
{
let elements = submeshbuffer[geom_index.submesh].has_indices();
let vao = vao.full_range(submeshbuffer[geom_index.submesh].primitive_type());
let command = command_buffer.0.range(start .. end);
if prev_material.map(|prev_material| prev_material == geom_index.materialref).unwrap_or(false){
let uniforms = &[];
if elements {
glin_properties.multi_draw_elements_command(vao, command, program, uniforms)
.is_ok();
}else{
glin_properties.multi_draw_arrays_command(vao, command, program, uniforms)
.is_ok();
}
}else if prev_program.map(|prev_program| prev_program == program.id()).unwrap_or(false){
let uniforms = material.uniforms.iter();
glin_properties = glin.with(&material.properties);
if elements{
glin_properties.multi_draw_elements_command(vao, command, program, uniforms)
.is_ok();
}else{
glin_properties.multi_draw_arrays_command(vao, command, program, uniforms)
.is_ok();
}
}else{
let uniforms = material.uniforms.iter()
.chain(lighting_uniforms.iter());
glin_properties = glin.with(&material.properties);
if elements{
glin_properties.multi_draw_elements_command(vao, command, program, uniforms)
.is_ok();
}else{
glin_properties.multi_draw_arrays_command(vao, command, program, uniforms)
.is_ok();
}
}
}
}
prev_material = Some(geom_index.materialref);
prev_program = Some(program.id());
}
}
#[cfg(not(feature="gl_multidraw_indirect"))]
{
let geom_index = opaque_geom.iter().chain(translucent_geom.iter());
#[cfg(all(feature="gl_material_unified_ubo", not(feature="gl_base_instance")))]
let material_offsets = materials_pool.offsets_buffer();
let mut next = 0;
for geom_index in geom_index {
let num_instances = geom_index.num_instances();
if geom_index.renderplane.is_none() || geom_index.renderplane != renderplane {
let material = &material_cache[geom_index.materialref];
let program = &programs_pool[*material.programref];
if let Some(submeshbuffer) = entities.entity_components::<Read<SubmeshBuffers>>(&geom_index.geometryref){
#[cfg(not(feature="gl_base_instance"))]
let vao = {
let vao = submeshbuffer[geom_index.submesh]
.full_vao(&glin, resources, &model_data_gpu)
.unwrap();
#[cfg(feature="gl_material_unified_ubo")]
{
let material_offset = material_offsets.range(next .. next + num_instances);
vao.set_instance_attribute_buffer_at(4, material_offset, 1).unwrap();
}
let model_normal = model_data_gpu.range(next .. next + num_instances);
vao.set_instance_attribute_buffer_at(5, model_normal, 1).unwrap();
submeshbuffer[geom_index.submesh].range_vao(resources, vao)
};
#[cfg(feature="gl_base_instance")]
let vao = submeshbuffer[geom_index.submesh]
.vao_base_instance(&glin, resources, &model_data_gpu, next as u32)
.unwrap();
if prev_material.map(|prev_material| prev_material == geom_index.materialref).unwrap_or(false){
let uniforms = &[];
render_vao(&glin_properties, &vao, uniforms, program, num_instances);
}else if prev_program.map(|prev_program| prev_program == program.id()).unwrap_or(false){
let uniforms = material.uniforms.iter();
glin_properties = glin.with(&material.properties);
render_vao(&glin_properties, &vao, uniforms, program, num_instances);
}else{
let uniforms = material.uniforms.iter()
.chain(lighting_uniforms.iter());
glin_properties = glin.with(&material.properties);
render_vao(&glin_properties, &vao, uniforms, program, num_instances);
}
}
prev_material = Some(geom_index.materialref);
prev_program = Some(program.id());
}
next += num_instances;
}
}
}
}
impl<'a> SystemThreadLocal<'a> for Renderer{
fn run(&mut self, entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let viewport = if let Some(fbo) = resources.get::<resources::ScreenRenderBuffer>(){
fbo.viewport()
}else{
let window = resources.get::<Window>().unwrap();
let viewport = window.viewport();
glin::Rect{
left: viewport.pos.x as u32,
bottom: viewport.pos.y as u32,
width: viewport.width as u32,
height: viewport.height as u32,
}
};
let glin = resources.get::<gl::Renderer<'static>>().unwrap();
let glin = glin.with_properties(&[
gl::Property::Viewport(viewport),
]);
let lighting_ubo = resources.get::<resources::LightingUBO>().unwrap();
let glin = glin.context()
.with(&[gl::UBOBindingPoints::Lighting.to_buffer_binding(&lighting_ubo.ubo)]);
for (entity, _, renderplanes, gpu_renderplane) in entities.iter_for::<(
Entity,
Read<Visible>,
Read<RenderPlane>,
Read<GpuRenderPlane>)>().filter(|(_,v,_,_)| v.is_visible())
{
for (renderplane, gpu_renderplane) in renderplanes.iter().zip(gpu_renderplane) {
let glin = if renderplane.biased_clip_plane().is_some(){
glin.with(&[
gl::Property::ClipDistance(vec![0]),
])
}else{
glin.with(&[])
};
let glin = glin.with_fbo(&gpu_renderplane);
glin.clear_color(&[0., 0., 0., 0.]);
glin.clear_depth();
self.render(
&glin,
&gpu_renderplane.camera_ubo,
Some(entity),
&entities,
&resources);
}
}
let camera_ubo = resources.get::<resources::CameraUBO>().unwrap();
if let Some(fbo) = resources.get::<resources::ScreenRenderBuffer>(){
let fbo_glin = glin.with_fbo(&*fbo);
let fbo_glin = if fbo.depth_prepass().is_some(){
fbo_glin.clear_color(&[0., 0., 0., 0.]);
fbo_glin.with(&[
glin::Property::DepthMask(false),
glin::Property::DepthFunc(gl::EQUAL),
glin::Property::ColorMask([true, true, true, true]),
])
}else{
fbo_glin.clear_color(&[0., 0., 0., 0.]);
fbo_glin.clear_depth();
fbo_glin
};
self.render(&fbo_glin, &camera_ubo.ubo, None, &entities, &resources);
glin.clear_color(&[0., 0., 0., 0.]);
glin.clear_depth();
}else{
glin.clear_color(&[0., 0., 0., 0.]);
glin.clear_depth();
self.render(&glin, &camera_ubo.ubo, None, &entities, &resources);
}
}
}