use rinecs::{SystemThreadLocal, EntitiesThreadLocal, ResourcesThreadLocal, Read, Entity, system_thread_local};
use crate::{
components::Visible,
renderer::{
material::MaterialCache,
resources::{self, RenderStage},
components::RenderPlane as GpuRenderPlane,
shadow,
},
transformation::RenderPlane,
DeferredScene,
};
use rin_material::MaterialRef;
use super::{geometry::{SubmeshBuffers, CommandBuffer, CommandBufferData}, material::MaterialOffsets, memory::AllocatorsIndex};
use glin::CreationContext;
#[cfg(target_os = "windows")]
use crate::glin::Vendor;
use super::material::{self, MaterialPool, ProgramCache, UnifiedMaterialOffset};
#[cfg(any(feature = "desktop", feature = "desktop_gles", feature="web"))]
use rin_window::Window;
use std::marker::PhantomData;
use rin_gl::Renderer2d;
use rin_gl as gl;
use rin_window::WindowExt;
use rin_graphics::{self as graphics, mvp};
use rin_math::{Pnt2, pnt2, vec2, Rect, Vec2};
use color::{rgba, Rgba, rgb};
use rin_events::Property;
#[cfg(gui)]
use crate::gui::Gui;
#[cfg(gui)]
use rin_gui::ControlGeometry;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RendererType{
#[cfg(not(any(feature = "webgl", feature = "gles")))]
MultiDrawIndirect,
#[cfg(not(any(feature = "webgl", feature = "gles")))]
BaseInstance,
Basic,
}
#[derive(Clone, Copy)]
pub struct GeometryIndices<'a>{
opaque: &'a resources::OpaqueSortedGeometry,
translucent: &'a resources::TranslucentSortedGeometry,
#[cfg(feature="debug_geometry")]
debug: &'a resources::DebugSortedGeometry,
}
impl<'a> GeometryIndices<'a>{
#[cfg(feature="debug_geometry")]
fn geometry_index(&self, start: usize) -> &resources::GeometryIndex{
if start < self.opaque.len(){
&self.opaque[start]
}else if start < self.opaque.len() + self.translucent.len() {
&self.translucent[start - self.opaque.len()]
}else{
&self.debug[start - (self.opaque.len() + self.translucent.len())]
}
}
#[cfg(not(feature="debug_geometry"))]
fn geometry_index(&self, start: usize) -> &resources::GeometryIndex{
if start < self.opaque.len(){
&self.opaque[start]
}else{
&self.translucent[start - self.opaque.len()]
}
}
}
pub trait RenderFn{
fn render<'a, R, B, I>(glin: &glin::Context<R>,
camera_ubo: B,
scene_camera_ubo: B,
renderplane: Option<&RenderPlane>,
renderplane_entity: Option<Entity>,
renderplane_id: Option<usize>,
entities: &EntitiesThreadLocal,
resources: &ResourcesThreadLocal,
geometries: GeometryIndices,
geom_index: I,
skip: usize,
take: Take) -> usize
where R: glin::RenderSurface,
B: glin::BufferRange<mvp::CameraData>,
I: Iterator<Item = &'a resources::GeometryIndex>;
}
pub enum Take{
Opaque,
Translucent,
TranslucentAndDebug,
#[cfg(feature="debug_geometry")]
Debug,
}
fn multidraw_indirect_take_opaque(
command_buffer_data: &CommandBufferData,
renderplane: Option<&RenderPlane>,
renderplane_entity: Option<Entity>) -> Option<usize>
{
if let Some(commands) = command_buffer_data.segments_per_renderplane.get(&renderplane_entity) {
if let Some(renderplane) = renderplane {
if renderplane.draw_translucent() && renderplane.draw_debug(){
return Some(commands.len());
}else if renderplane.draw_translucent(){
if let Some(debug_starts) = command_buffer_data.debug_starts.get(&renderplane_entity) {
return Some(*debug_starts)
}else{
return Some(commands.len())
}
}
}
command_buffer_data.translucent_starts.get(&renderplane_entity).copied().or_else(||{
if let Some(debug_starts) = command_buffer_data.debug_starts.get(&renderplane_entity) {
Some(*debug_starts)
}else{
Some(command_buffer_data.segments_per_renderplane[&renderplane_entity].len())
}
})
}else{
return None;
}
}
fn multidraw_indirect_take_translucent(command_buffer_data: &CommandBufferData) -> Option<usize> {
command_buffer_data.translucent_starts.get(&None).copied().map(|translucent_starts| {
if let Some(debug_starts) = command_buffer_data.debug_starts.get(&None) {
debug_starts - translucent_starts
}else{
command_buffer_data.segments_per_renderplane[&None].len() - translucent_starts
}
})
}
#[cfg(feature="debug_geometry")]
fn multidraw_indirect_take_debug(command_buffer_data: &CommandBufferData) -> Option<usize> {
command_buffer_data.debug_starts.get(&None).copied().map(|debug_starts| {
command_buffer_data.segments_per_renderplane[&None].len() - debug_starts
})
}
fn multidraw_indirect_take_translucent_and_debug(command_buffer_data: &CommandBufferData) -> Option<usize> {
command_buffer_data.translucent_starts.get(&None).copied().map(|translucent_starts| {
command_buffer_data.segments_per_renderplane[&None].len() - translucent_starts
}).or_else(|| command_buffer_data.debug_starts.get(&None).copied().map(|debug_starts| {
command_buffer_data.segments_per_renderplane[&None].len() - debug_starts
}))
}
pub trait MaterialProperties{
fn material_properties<'a, R>(resources: &ResourcesThreadLocal, glin: &'a glin::Context<R>) -> glin::Context<'a, R>
where R: glin::RenderSurface;
}
pub struct UnifiedMaterialUBO;
impl MaterialProperties for UnifiedMaterialUBO{
fn material_properties<'a, R>(resources: &ResourcesThreadLocal, glin: &'a glin::Context<R>) -> glin::Context<'a, R>
where R: glin::RenderSurface
{
let material_ubo = resources.get::<material::Buffer<MaterialRef, UnifiedMaterialOffset>>()
.unwrap();
glin.with(&[
super::UBOBindingPoints::Material.to_buffer_binding(material_ubo.buffer())
])
}
}
pub struct PerMaterialUBO;
impl MaterialProperties for PerMaterialUBO{
fn material_properties<'a, R>(_: &ResourcesThreadLocal, glin: &'a glin::Context<R>) -> glin::Context<'a, R>
where R: glin::RenderSurface
{
glin.with(&[])
}
}
#[cfg(not(any(feature = "gles", feature="webgl")))]
pub struct RenderMultiDrawIndirect;
#[cfg(not(any(feature = "gles", feature="webgl")))]
impl RenderFn for RenderMultiDrawIndirect {
fn render<'a, R, B, I>(glin: &glin::Context<R>,
camera_ubo: B,
scene_camera_ubo: B,
renderplane: Option<&RenderPlane>,
renderplane_entity: Option<Entity>,
renderplane_id: Option<usize>,
_: &EntitiesThreadLocal,
resources: &ResourcesThreadLocal,
geometries: GeometryIndices,
_: I,
skip: usize,
take: Take) -> usize
where R: glin::RenderSurface,
B: glin::BufferRange<mvp::CameraData>,
I: Iterator<Item = &'a resources::GeometryIndex>,
{
let command_buffer_data = resources.get::<CommandBufferData>().unwrap();
let take = match take{
Take::Opaque =>
multidraw_indirect_take_opaque(&command_buffer_data, renderplane, renderplane_entity),
Take::Translucent =>
multidraw_indirect_take_translucent(&command_buffer_data),
Take::TranslucentAndDebug =>
multidraw_indirect_take_translucent_and_debug(&command_buffer_data),
#[cfg(feature="debug_geometry")]
Take::Debug =>
multidraw_indirect_take_debug(&command_buffer_data),
};
let take = if let Some(take) = take{
take
}else{
return skip
};
let material_cache = resources.get::<MaterialCache>().unwrap();
let program_cache = resources.get::<ProgramCache>().unwrap();
let mut prev_material = None;
let glin = glin.with(&[
gl::Property::DepthTest(true),
super::UBOBindingPoints::Camera.to_buffer_binding(camera_ubo),
super::UBOBindingPoints::SceneCamera.to_buffer_binding(scene_camera_ubo),
]);
let glin = UnifiedMaterialUBO::material_properties(resources, &glin);
let is_renderplane = glin::program::uniform(
"is_renderplane",
&(renderplane.is_some() as u8 as f32));
let u_renderplane_id = renderplane_id.map(|renderplane_id| glin::program::uniform(
"renderplane_id",
&(renderplane_id as i32)));
let u_renderplane_entity = renderplane_entity.map(|renderplane_entity| glin::program::uniform(
"renderplane_entity",
&(renderplane_entity.guid() as i32)));
let mut glin_properties = glin.shallow_clone();
let command_buffer = resources.get::<CommandBuffer>().unwrap();
let command_buffer_data = resources.get::<CommandBufferData>().unwrap();
for segment in command_buffer_data.segments_per_renderplane[&renderplane_entity].iter()
.skip(skip)
.take(take)
{
let start = segment.start;
let end = segment.end;
let vao_id = segment.vao_id;
let mode = segment.primitive_type;
let elements = segment.elements;
let geom_index = geometries.geometry_index(start);
let material = &material_cache[geom_index.materialref];
let program = &program_cache[material.programref];
unsafe {
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 {
let _ = glin_properties
.multi_draw_elements_command_vao_id(vao_id, mode, command, program, uniforms);
}else{
let _ = glin_properties
.multi_draw_arrays_command_vao_id(vao_id, mode, command, program, uniforms);
}
}else{
let uniforms = material
.all_uniforms()
.chain(Some(&is_renderplane))
.chain(u_renderplane_id.as_ref())
.chain(u_renderplane_entity.as_ref());
let properties = material.alpha_type_properties.iter()
.chain(&material.properties);
glin_properties = glin.with(properties);
if elements{
let _ = glin_properties
.multi_draw_elements_command_vao_id(vao_id, mode, command, program, uniforms);
}else{
let _ = glin_properties
.multi_draw_arrays_command_vao_id(vao_id, mode, command, program, uniforms);
}
}
}
prev_material = Some(geom_index.materialref);
}
skip + take
}
}
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 {
let _ = glin.draw_instanced(vao, num_instances, program, uniforms);
}else{
let _ = glin.draw(vao, program, uniforms);
}
}
#[cfg(not(any(feature = "gles", feature="webgl")))]
pub struct RenderBaseInstance<P>{
material_properties: PhantomData<P>
}
#[cfg(not(any(feature = "gles", feature="webgl")))]
impl<P: MaterialProperties> RenderFn for RenderBaseInstance<P> {
fn render<'a, R, B, I>(glin: &glin::Context<R>,
camera_ubo: B,
scene_camera_ubo: B,
renderplane: Option<&RenderPlane>,
renderplane_entity: Option<Entity>,
renderplane_id: Option<usize>,
entities: &EntitiesThreadLocal,
resources: &ResourcesThreadLocal,
_: GeometryIndices,
geom_index: I,
skip: usize,
_: Take) -> usize
where R: glin::RenderSurface,
B: glin::BufferRange<mvp::CameraData>,
I: Iterator<Item = &'a resources::GeometryIndex>,
{
let material_cache = resources.get::<MaterialCache>().unwrap();
let program_cache = resources.get::<ProgramCache>().unwrap();
let mut prev_material = None;
let glin = glin.with(&[
gl::Property::DepthTest(true),
super::UBOBindingPoints::Camera.to_buffer_binding(camera_ubo),
super::UBOBindingPoints::SceneCamera.to_buffer_binding(scene_camera_ubo),
]);
let glin = P::material_properties(resources, &glin);
let is_renderplane = glin::program::uniform(
"is_renderplane",
&(renderplane.is_some() as u8 as f32));
let u_renderplane_id = renderplane_id.map(|renderplane_id| glin::program::uniform(
"renderplane_id",
&(renderplane_id as i32)));
let u_renderplane_entity = renderplane_entity.map(|renderplane_entity| glin::program::uniform(
"renderplane_entity",
&(renderplane_entity.guid() as i32)));
let mut glin_properties = glin.shallow_clone();
let model_data_gpu = resources.get::<resources::ModelMatricesBuffer>().unwrap();
let mut allocators = resources.get_mut::<AllocatorsIndex>().unwrap();
let material_offsets = resources.get::<MaterialOffsets>();
let mut next = skip;
for geom_index in geom_index {
let num_instances = geom_index.num_instances();
if geom_index.renderplane.is_none() || geom_index.renderplane != renderplane_entity {
let material = &material_cache[geom_index.materialref];
let program = &program_cache[material.programref];
if let Some(submeshbuffer) = entities
.entity_components::<Read<SubmeshBuffers>>(&geom_index.geometryref)
{
let vao = submeshbuffer[geom_index.submesh]
.vao_base_instance(&glin, &mut allocators, material_offsets.as_deref(), &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{
let uniforms = material
.all_uniforms()
.chain(Some(&is_renderplane))
.chain(u_renderplane_id.as_ref())
.chain(u_renderplane_entity.as_ref());
let properties = material.alpha_type_properties.iter()
.chain(&material.properties);
glin_properties = glin.with(properties);
render_vao(&glin_properties, &vao, uniforms, program, num_instances);
}
}
prev_material = Some(geom_index.materialref);
}
next += num_instances;
}
next
}
}
pub struct RenderBasic<P>{
material_properties: PhantomData<P>
}
impl<P: MaterialProperties> RenderFn for RenderBasic<P> {
fn render<'a, R, B, I>(glin: &glin::Context<R>,
camera_ubo: B,
scene_camera_ubo: B,
renderplane: Option<&RenderPlane>,
renderplane_entity: Option<Entity>,
renderplane_id: Option<usize>,
entities: &EntitiesThreadLocal,
resources: &ResourcesThreadLocal,
_: GeometryIndices,
geom_index: I,
skip: usize,
_: Take) -> usize
where R: glin::RenderSurface,
B: glin::BufferRange<mvp::CameraData>,
I: Iterator<Item = &'a resources::GeometryIndex>,
{
let material_cache = resources.get::<MaterialCache>().unwrap();
let program_cache = resources.get::<ProgramCache>().unwrap();
let mut prev_material = None;
let glin = glin.with(&[
gl::Property::DepthTest(true),
super::UBOBindingPoints::Camera.to_buffer_binding(camera_ubo),
super::UBOBindingPoints::SceneCamera.to_buffer_binding(scene_camera_ubo),
]);
let glin = P::material_properties(resources, &glin);
let is_renderplane = glin::program::uniform(
"is_renderplane",
&(renderplane.is_some() as u8 as f32));
let u_renderplane_id = renderplane_id.map(|renderplane_id| glin::program::uniform(
"renderplane_id",
&(renderplane_id as i32)));
let u_renderplane_entity = renderplane_entity.map(|renderplane_entity| glin::program::uniform(
"renderplane_entity",
&(renderplane_entity.guid() as i32)));
let mut glin_properties = glin.shallow_clone();
let model_data_gpu = resources.get::<resources::ModelMatricesBuffer>().unwrap();
let mut allocators = resources.get_mut::<AllocatorsIndex>().unwrap();
let material_offsets = resources.get::<MaterialOffsets>();
let mut next = skip;
for geom_index in geom_index {
let num_instances = geom_index.num_instances();
if geom_index.renderplane.is_none() || geom_index.renderplane != renderplane_entity {
let material = &material_cache[geom_index.materialref];
let program = &program_cache[material.programref];
if let Some(submeshbuffer) = entities
.entity_components::<Read<SubmeshBuffers>>(&geom_index.geometryref)
{
let vao = {
let vao = submeshbuffer[geom_index.submesh]
.full_vao(&glin, &mut allocators, material_offsets.as_deref(), &model_data_gpu)
.unwrap();
if let Some(material_offsets) = material_offsets.as_ref() {
let material_offsets = material_offsets.buffer();
let material_offset = material_offsets.range(next .. next + num_instances);
vao.set_instance_attribute_buffer_at(
super::AttributeBufferIndex::MaterialOffsets as usize,
material_offset.clone(),
1).unwrap();
}
let model_normal = model_data_gpu.range(next .. next + num_instances);
vao.set_instance_attribute_buffer_at(
super::AttributeBufferIndex::Matrices as usize,
model_normal.clone(),
1).unwrap();
submeshbuffer[geom_index.submesh].range_vao(&mut allocators, vao)
};
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{
let uniforms = material
.all_uniforms()
.chain(Some(&is_renderplane))
.chain(u_renderplane_id.as_ref())
.chain(u_renderplane_entity.as_ref());
let properties = material.alpha_type_properties.iter()
.chain(&material.properties);
glin_properties = glin.with(properties);
render_vao(&glin_properties, &vao, uniforms, program, num_instances);
}
}
prev_material = Some(geom_index.materialref);
}
next += num_instances;
}
next
}
}
pub struct OpaqueInstances(usize);
impl OpaqueInstances{
pub fn new() -> OpaqueInstances{
OpaqueInstances(0)
}
}
pub struct RendererOpaque<F>{
render: PhantomData<F>,
}
#[cfg(not(any(feature = "gles", feature="webgl")))]
impl RendererOpaque<RenderMultiDrawIndirect>{
pub(crate) fn new_multidraw_indirect() -> RendererOpaque<RenderMultiDrawIndirect>{
RendererOpaque{
render: PhantomData
}
}
}
#[cfg(not(any(feature = "gles", feature="webgl")))]
impl RendererOpaque<RenderBaseInstance<()>>{
pub(crate) fn new_base_instance<P: MaterialProperties>() -> RendererOpaque<RenderBaseInstance<P>>{
RendererOpaque{
render: PhantomData
}
}
}
impl RendererOpaque<RenderBasic<()>>{
pub(crate) fn new_basic<P: MaterialProperties>() -> RendererOpaque<RenderBasic<P>>{
RendererOpaque{
render: PhantomData
}
}
}
#[cfg(target_os="windows")]
mod windows_utils{
use winapi::um::d3d11::*;
use winapi::um::d3dcommon::*;
use std::ptr;
#[derive(Ord, PartialOrd, Eq, PartialEq)]
pub enum ShaderVersion{
_5_0,
_5_1,
}
pub fn check_shader_version() -> ShaderVersion {
let mut d3d_device = ptr::null_mut();
let mut device_context = ptr::null_mut();
unsafe {
D3D11CreateDevice(
ptr::null_mut(),
D3D_DRIVER_TYPE_HARDWARE,
ptr::null_mut(),
0,
&D3D_FEATURE_LEVEL_12_0,
1,
D3D11_SDK_VERSION,
&mut d3d_device,
ptr::null_mut(),
&mut device_context,
);
if d3d_device != ptr::null_mut() && device_context != ptr::null_mut() {
(*d3d_device).Release();
(*device_context).Release();
return ShaderVersion::_5_1
}else{
return ShaderVersion::_5_0
}
}
}
}
pub fn needs_indexing_hack(gl: &gl::Renderer) -> bool{
#[cfg(not(target_os = "windows"))]
{
return !gl.capabilities().is_supported("GL_ARB_gpu_shader5")
}
#[cfg(target_os = "windows")]
{
if gl.capabilities().vendor != Vendor::Intel {
!gl.capabilities().is_supported("GL_ARB_gpu_shader5")
}else{
windows_utils::check_shader_version() < windows_utils::ShaderVersion::_5_1
}
}
}
#[cfg(not(any(feature = "gles", feature="webgl")))]
pub fn choose_renderer_type(gl: &gl::Renderer) -> RendererType {
#[cfg(target_os = "windows")]
let hack_intel_windows = gl.capabilities().vendor == Vendor::Intel;
#[cfg(not(target_os = "windows"))]
let hack_intel_windows = false;
if gl.capabilities().is_supported("GL_ARB_multi_draw_indirect")
&& !hack_intel_windows
{
RendererType::MultiDrawIndirect
}else if gl.capabilities().is_supported("GL_ARB_base_instance"){
RendererType::BaseInstance
}else{
RendererType::Basic
}
}
#[cfg(any(feature = "gles", feature="webgl"))]
pub fn choose_renderer_type(gl: &gl::Renderer) -> RendererType {
RendererType::Basic
}
pub(crate) fn create_opaque_renderer(
ty: RendererType,
unified_material_ubo: bool,
scene: &mut DeferredScene)
{
#[cfg(not(any(feature="desktop", feature="desktop_gles", feature="web")))]
panic!("Can't create rederer without window type selected in features, select one of desktop, desktop_gles or web");
#[cfg(any(feature="desktop", feature="desktop_gles", feature="web"))]
match ty{
#[cfg(not(any(feature = "webgl", feature = "gles")))]
RendererType::MultiDrawIndirect => {
log::trace!("Adding multidraw indirect opaque renderer");
scene.add_system_thread_local(RendererOpaque::new_multidraw_indirect());
}
#[cfg(not(any(feature = "webgl", feature = "gles")))]
RendererType::BaseInstance => {
if unified_material_ubo {
log::trace!("Adding base instance opaque renderer with unified ubo");
scene.add_system_thread_local(RendererOpaque::new_base_instance::<UnifiedMaterialUBO>());
}else{
log::trace!("Adding base instance opaque renderer with per material ubo");
scene.add_system_thread_local(RendererOpaque::new_base_instance::<PerMaterialUBO>());
}
}
RendererType::Basic => {
if unified_material_ubo {
log::trace!("Adding basic opaque renderer with unified ubo");
scene.add_system_thread_local(RendererOpaque::new_basic::<UnifiedMaterialUBO>());
}else{
log::trace!("Adding basic opaque renderer with per material ubo");
scene.add_system_thread_local(RendererOpaque::new_basic::<PerMaterialUBO>());
}
}
}
}
#[cfg(any(feature="desktop", feature="desktop_gles", feature="web"))]
#[system_thread_local(name = "renderer opaque")]
#[needs(
"CommandBuffer",
"CommandBufferData",
"super::geometry::upload_static_shadows_command_buffer",
"super::geometry::upload_shadows_command_buffer",
"super::material::material_offsets_updater",
)]
#[needs(
"resources::CameraUBO",
"resources::LightingUBO",
"resources::LightingTextures",
"MaterialCache",
"MaterialPool",
"resources::OpaqueSortedGeometry",
"resources::TranslucentSortedGeometry",
"resources::ModelMatricesBuffer",
"shadow::Map",
"shadow::StaticMap",
"super::geometry::VertexBuffer",
"super::geometry::IndicesBuffer",
"resources::DepthPrepass",
"Visible",
"RenderPlane",
"GpuRenderPlane",
ProgramCache,
crate::BackgroundColor,
material::Buffer<MaterialRef, UnifiedMaterialOffset>
)]
#[reads(gl::Renderer)]
#[cfg_attr(feature="debug_geometry", needs("resources::DebugSortedGeometry"))]
#[updates("resources::ScreenRenderBuffer", "crate::render_stage::RenderSurfaceOpaque")]
#[writes(OpaqueInstances)]
#[gpu_stats]
impl<F: RenderFn> SystemThreadLocal for RendererOpaque<F>{
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();
#[cfg(gl_debug_groups)]
let _debug_group = glin.new_debug_group(0, "Renderer opaque");
let glin = glin.with_properties(&[
gl::Property::Viewport(viewport),
]);
let camera_ubo = resources.get::<resources::CameraUBO>().unwrap();
let lighting_ubo = resources.get::<resources::LightingUBO>().unwrap();
let glin = glin.context()
.with(&[super::UBOBindingPoints::Lighting.to_buffer_binding(&lighting_ubo.ubo)]);
let opaque_geom = resources.get::<resources::OpaqueSortedGeometry>().unwrap();
let translucent_geom = resources.get::<resources::TranslucentSortedGeometry>().unwrap();
#[cfg(feature="debug_geometry")]
let debug_geom = resources.get::<resources::DebugSortedGeometry>().unwrap();
let geometries = GeometryIndices{
opaque: &opaque_geom,
translucent: &translucent_geom,
#[cfg(feature="debug_geometry")]
debug: &debug_geom
};
for (entity, _, renderplanes, gpu_renderplane) in entities.iter_for::<(
Entity,
Read<Visible>,
Read<RenderPlane>,
Read<GpuRenderPlane>)>().filter(|(_,v,_,_)| v.is_visible())
{
let iter = renderplanes.iter().zip(gpu_renderplane).enumerate();
for (renderplane_id, (renderplane, gpu_renderplane)) in iter {
let glin = glin.with_fbo(gpu_renderplane);
glin.clear(Some(&[0., 0., 0., 1.]), Some(1.), None);
let render_plane_glin;
if renderplane.biased_clip_plane().is_some(){
#[cfg(not(any(feature="gles", feature="webgl")))]
{
render_plane_glin = glin.with(&[
gl::Property::ClipDistance(vec![0]),
]);
}
#[cfg(any(feature="gles", feature="webgl"))]
{
render_plane_glin = glin.with(&[]);
}
}else{
render_plane_glin = glin.with(&[]);
};
let start = 0;
if renderplane.draw_translucent() && renderplane.draw_debug(){
let geom_index = opaque_geom.iter().chain(translucent_geom.iter());
#[cfg(feature="debug_geometry")]
let geom_index = geom_index.chain(debug_geom.iter());
F::render(
&render_plane_glin,
&gpu_renderplane.camera_ubo,
&camera_ubo.ubo,
Some(&renderplane),
Some(entity),
Some(renderplane_id),
&entities,
&resources,
geometries,
geom_index,
start,
Take::Opaque
);
}else if renderplane.draw_translucent(){
let geom_index = opaque_geom.iter().chain(translucent_geom.iter());
F::render(
&render_plane_glin,
&gpu_renderplane.camera_ubo,
&camera_ubo.ubo,
Some(&renderplane),
Some(entity),
Some(renderplane_id),
&entities,
&resources,
geometries,
geom_index,
start,
Take::Opaque
);
}else{
F::render(
&render_plane_glin,
&gpu_renderplane.camera_ubo,
&camera_ubo.ubo,
Some(&renderplane),
Some(entity),
Some(renderplane_id),
&entities,
&resources,
geometries,
opaque_geom.iter(),
start,
Take::Opaque
);
}
}
}
#[cfg(not(any(feature = "webgl", feature = "gles")))]
let glin = if camera_ubo.zero_to_one{
glin.with(&[glin::Property::ClipControl(gl::LOWER_LEFT, gl::ZERO_TO_ONE)])
}else{
glin
};
#[cfg(not(any(feature = "webgl", feature = "gles")))]
let glin = glin.with(&[glin::Property::DepthClamp(camera_ubo.clamp_depth)]);
let clear_color = resources.get::<crate::BackgroundColor>().map(|c| rgba!(c.0, 1f32));
let opaque_instances = if let Some(mut fbo) = resources.get_mut::<resources::ScreenRenderBuffer>(){
let does_depth_prepass = fbo.depth_prepass().is_some();
let fbo_glin = glin.with_fbo(&**fbo.render_buffer(RenderStage::Opaque));
let render_context = if does_depth_prepass {
fbo_glin.clear(clear_color, None, None);
fbo_glin.with(&[
glin::Property::DepthMask(false),
glin::Property::DepthFunc(gl::EQUAL),
glin::Property::ColorMask([true, true, true, true]),
])
}else{
let fbo_glin = if camera_ubo.reversed_z{
fbo_glin.with(&[glin::Property::DepthFunc(gl::GREATER)])
}else{
fbo_glin
};
let clear_depth = if camera_ubo.reversed_z{
Some(0.)
}else{
Some(1.)
};
fbo_glin.clear(clear_color, clear_depth, None);
fbo_glin
};
let geom_index = opaque_geom.iter();
let skip = 0;
F::render(
&render_context,
&camera_ubo.ubo,
&camera_ubo.ubo,
None,
None,
None,
&entities,
&resources,
geometries,
geom_index,
skip,
Take::Opaque
)
}else{
let glin = if camera_ubo.reversed_z{
glin.with(&[glin::Property::DepthFunc(gl::GREATER)])
}else{
glin
};
let clear_depth = if camera_ubo.reversed_z{
Some(0.)
}else{
Some(1.)
};
glin.clear(clear_color, clear_depth, None);
let geom_index = opaque_geom.iter();
let skip = 0;
F::render(
&glin,
&camera_ubo.ubo,
&camera_ubo.ubo,
None,
None,
None,
&entities,
&resources,
geometries,
geom_index,
skip,
Take::Opaque
)
};
resources.get_mut::<OpaqueInstances>().unwrap().0 = opaque_instances;
}
}
pub struct RendererTranslucent<F>{
render: PhantomData<F>,
}
#[cfg(not(any(feature = "gles", feature="webgl")))]
impl RendererTranslucent<RenderMultiDrawIndirect>{
pub(crate) fn new_multidraw_indirect()
-> RendererTranslucent<RenderMultiDrawIndirect>
{
RendererTranslucent{
render: PhantomData,
}
}
}
#[cfg(not(any(feature = "gles", feature="webgl")))]
impl RendererTranslucent<RenderBaseInstance<()>>{
pub(crate) fn new_base_instance<P: MaterialProperties>()
-> RendererTranslucent<RenderBaseInstance<P>>
{
RendererTranslucent{
render: PhantomData,
}
}
}
impl RendererTranslucent<RenderBasic<()>>{
pub(crate) fn new_basic<P: MaterialProperties>()
-> RendererTranslucent<RenderBasic<P>>
{
RendererTranslucent{
render: PhantomData,
}
}
}
pub(crate) fn create_translucent_renderer(
renderer_ty: RendererType,
unified_material_ubo: bool,
scene: &mut DeferredScene)
{
#[cfg(not(any(feature="desktop", feature="desktop_gles", feature="web")))]
panic!("Can't create rederer without window type selected in features, select one of desktop, desktop_gles or web");
#[cfg(any(feature="desktop", feature="desktop_gles", feature="web"))]
match renderer_ty {
#[cfg(not(any(feature = "webgl", feature = "gles")))]
RendererType::MultiDrawIndirect => {
log::trace!("Adding multidraw indirect translucent renderer");
scene.add_system_thread_local(RendererTranslucent::new_multidraw_indirect());
}
#[cfg(not(any(feature = "webgl", feature = "gles")))]
RendererType::BaseInstance => {
if unified_material_ubo {
log::trace!("Adding base instance translucent renderer with unified ubo");
scene.add_system_thread_local(RendererTranslucent::new_base_instance::<UnifiedMaterialUBO>());
}else{
log::trace!("Adding base instance translucent renderer with per material ubo");
scene.add_system_thread_local(RendererTranslucent::new_base_instance::<PerMaterialUBO>());
}
}
RendererType::Basic => {
if unified_material_ubo {
log::trace!("Adding basic translucent renderer with unified ubo");
scene.add_system_thread_local(RendererTranslucent::new_basic::<UnifiedMaterialUBO>());
}else{
log::trace!("Adding basic translucent renderer with per material ubo");
scene.add_system_thread_local(RendererTranslucent::new_basic::<PerMaterialUBO>());
}
}
}
}
#[cfg(any(feature="desktop", feature="desktop_gles", feature="web"))]
#[system_thread_local(name = "renderer translucent")]
#[needs(
"CommandBuffer",
"CommandBufferData",
"super::geometry::upload_static_shadows_command_buffer",
"super::geometry::upload_shadows_command_buffer",
"super::material::material_offsets_updater",
)]
#[cfg_attr(feature="debug_geometry", needs("resources::DebugSortedGeometry"))]
#[needs(
"resources::CameraUBO",
"resources::LightingUBO",
"resources::LightingTextures",
"MaterialCache",
"MaterialPool",
"resources::OpaqueSortedGeometry",
"resources::TranslucentSortedGeometry",
"resources::ModelMatricesBuffer",
"shadow::Map",
"shadow::StaticMap",
"super::geometry::VertexBuffer",
"super::geometry::IndicesBuffer",
"resources::DepthPrepass",
"crate::render_stage::RenderSurfaceOpaque",
"crate::render_stage::AfterRenderSurfaceOpaque",
"crate::render_stage::PostprocessingOpaque",
crate::BackgroundColor,
OpaqueInstances,
ProgramCache,
material::Buffer<MaterialRef, UnifiedMaterialOffset>
)]
#[updates("crate::render_stage::RenderSurfaceTranslucent")]
#[reads(gl::Renderer)]
#[writes(resources::ScreenRenderBuffer)]
#[gpu_stats]
impl<F: RenderFn> SystemThreadLocal for RendererTranslucent<F>{
fn run(&mut self, entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
fn render_translucent<S: glin::UntypedOffscreenBuffer + Clone, F: RenderFn>(
fbo: Option<S>,
clear_color: Option<Rgba<f32>>,
entities: EntitiesThreadLocal,
resources: ResourcesThreadLocal)
{
let viewport = if let Some(fbo) = fbo.as_ref(){
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();
#[cfg(gl_debug_groups)]
let _debug_group = glin.new_debug_group(0, "Renderer translucent");
let lighting_ubo = resources.get::<resources::LightingUBO>().unwrap();
let glin = glin.context()
.with(&[super::UBOBindingPoints::Lighting.to_buffer_binding(&lighting_ubo.ubo)]);
let opaque_geom = resources.get::<resources::OpaqueSortedGeometry>().unwrap();
let translucent_geom = resources.get::<resources::TranslucentSortedGeometry>().unwrap();
#[cfg(feature="debug_geometry")]
let debug_geom = resources.get::<resources::DebugSortedGeometry>().unwrap();
#[cfg(not(feature="debug_geometry"))]
let geometries = GeometryIndices{
opaque: &opaque_geom,
translucent: &translucent_geom,
};
#[cfg(feature="debug_geometry")]
let geometries = GeometryIndices{
opaque: &opaque_geom,
translucent: &translucent_geom,
debug: &debug_geom
};
let camera_ubo = resources.get::<resources::CameraUBO>().unwrap();
let glin = if camera_ubo.reversed_z{
glin.with(&[glin::Property::DepthFunc(gl::GREATER)])
}else{
glin
};
#[cfg(not(any(feature = "webgl", feature = "gles")))]
let glin = if camera_ubo.zero_to_one{
glin.with(&[glin::Property::ClipControl(gl::LOWER_LEFT, gl::ZERO_TO_ONE)])
}else{
glin
};
#[cfg(not(any(feature = "webgl", feature = "gles")))]
let glin = glin.with(&[glin::Property::DepthClamp(camera_ubo.clamp_depth)]);
let skip = resources.get::<OpaqueInstances>().unwrap().0;
if let Some(fbo) = fbo {
let render_context = glin.with_fbo(fbo);
if let Some(clear_color) = clear_color {
render_context.clear_color(&clear_color);
}
let geom_index = translucent_geom.iter();
#[allow(unused_variables)]
let next = F::render(
&render_context,
&camera_ubo.ubo,
&camera_ubo.ubo,
None,
None,
None,
&entities,
&resources,
geometries,
geom_index,
skip,
Take::Translucent,
);
#[cfg(feature="debug_geometry")]
{
let geom_index = debug_geom.iter();
F::render(
&render_context,
&camera_ubo.ubo,
&camera_ubo.ubo,
None,
None,
None,
&entities,
&resources,
geometries,
geom_index,
next,
Take::Debug,
);
}
}else{
let geom_index = translucent_geom.iter();
#[cfg(feature="debug_geometry")]
let geom_index = geom_index.chain(debug_geom.iter());
let glin = glin.with(&[
gl::Property::Viewport(viewport),
]);
if let Some(clear_color) = clear_color {
glin.clear_color(&clear_color);
}
F::render(
&glin,
&camera_ubo.ubo,
&camera_ubo.ubo,
None,
None,
None,
&entities,
&resources,
geometries,
geom_index,
skip,
Take::TranslucentAndDebug
);
}
}
#[allow(unused_mut)]
let mut any_translucent = !resources.get::<resources::TranslucentSortedGeometry>()
.unwrap()
.is_empty();
#[cfg(feature="debug_geometry")]
{
any_translucent |= !resources.get::<resources::DebugSortedGeometry>()
.unwrap()
.is_empty();
}
if !any_translucent{
return;
}
if let Some(mut render_surface) = resources.get_mut::<resources::ScreenRenderBuffer>(){
let does_postpo = render_surface.separate_ambient_unresolved_attachment().is_some();
let already_cleared = render_surface.last_stage() == RenderStage::AfterOpaquePostprocess;
let clear_color = if does_postpo && !already_cleared {
resources
.get::<crate::BackgroundColor>()
.map(|c| rgba!(c.0, 0.))
} else {
None
};
let fbo = render_surface.color_depth_only_fbo(RenderStage::Translucent);
render_translucent::<_, F>(Some(&fbo), clear_color, entities, resources);
}else{
render_translucent::<&gl::Fbo, F>(None, None, entities, resources)
};
}
}
pub struct ResolveIntermediateSurfaces;
impl ResolveIntermediateSurfaces{
pub fn new() -> ResolveIntermediateSurfaces{
ResolveIntermediateSurfaces
}
}
#[cfg(feature="postprocessing")]
#[system_thread_local(name = "resolve intermediate surfaces")]
#[needs(
"resources::ScreenRenderBuffer",
"rin_postpo::PostProcessing",
"crate::render_stage::RenderSurfaceOpaque",
"crate::render_stage::AfterPostprocessingOpaque",
"crate::render_stage::RenderSurfaceTranslucent",
"crate::render_stage::AfterRenderSurfaceTranslucent",
"crate::render_stage::Postprocessing",
"crate::render_stage::AfterPostprocessing",
rin_postpo::Parameters
)]
#[updates("crate::render_stage::FinalSurface")]
#[reads(gl::Renderer)]
#[gpu_stats]
impl SystemThreadLocal for ResolveIntermediateSurfaces {
fn run(&mut self, _: EntitiesThreadLocal, resources: ResourcesThreadLocal){
if let Some(render_surface) = resources.get::<resources::ScreenRenderBuffer>(){
if render_surface.last_stage() == RenderStage::AfterFinalPostprocess {
if let Some(postpro) = resources.get::<rin_postpo::PostProcessing>(){
let gl = resources.get::<gl::Renderer<'static>>().unwrap();
#[cfg(gl_debug_groups)]
let _debug_group = gl.new_debug_group(0, "Resolve intermediate surfaces");
if let glin::fbo::ColorAttachment::TextureLevel(color, _) = render_surface.color_attachment() {
let parameters = resources.get::<rin_postpo::Parameters>().unwrap();
let fbo = postpro.last_fbo(¶meters);
let gl = gl.with_fbo(&**fbo);
let gl = gl.with_properties(&[
glin::Property::Blend(true),
glin::Property::BlendFuncSeparate(
gl::ONE,
gl::ONE_MINUS_SRC_ALPHA,
gl::ZERO,
gl::ONE),
]);
let gl = gl.with_mvp(graphics::Mvp::ortho_top_left(fbo.viewport().into()));
gl.draw_pos(color, &pnt2!(0.));
}
}
}
}
}
}
pub struct BlitToScreenSystem {
window_size: Property<'static, Vec2<i32>>
}
impl BlitToScreenSystem{
pub fn new(window_size: Property<'static, Vec2<i32>>) -> BlitToScreenSystem{
BlitToScreenSystem{
window_size
}
}
}
#[system_thread_local(name = "final surface to screen")]
#[needs(
"resources::ScreenRenderBuffer",
"crate::render_stage::RenderSurfaceOpaque",
"crate::render_stage::AfterPostprocessingOpaque",
"crate::render_stage::RenderSurfaceTranslucent",
"crate::render_stage::AfterRenderSurfaceTranslucent",
"crate::render_stage::Postprocessing",
"crate::render_stage::AfterPostprocessing",
"crate::render_stage::FinalSurface",
)]
#[updates("crate::render_stage::FinalSurfaceBlit")]
#[reads(gl::Renderer)]
#[cfg_attr(any(feature="desktop", feature="desktop_gles", feature="web"), updates("Window"))]
#[cfg_attr(feature="postprocessing", needs("rin_postpo::PostProcessing", rin_postpo::Parameters))]
#[cfg_attr(gui, reads(Gui))]
#[gpu_stats]
impl SystemThreadLocal for BlitToScreenSystem {
fn run(&mut self, _: EntitiesThreadLocal, resources: ResourcesThreadLocal){
if let Some(render_surface) = resources.get::<resources::ScreenRenderBuffer>(){
let gl = resources.get::<gl::Renderer<'static>>().unwrap();
#[cfg(gl_debug_groups)]
let _debug_group = gl.new_debug_group(0, "Blit final surface to screen");
let window_size = self.window_size.get();
gl.clear_all(Some(color::consts::BLACK), Some(1.), None);
#[cfg(gui)]
let pos = {
if let Some(gui) = resources.get::<Gui>(){
if *gui.visible().get(){
let pos = *gui.position().get();
let width = *gui.width().get();
pnt2(pos.x + width, pos.y)
}else{
Pnt2::origin()
}
}else{
Pnt2::origin()
}
};
#[cfg(not(gui))]
let pos = Pnt2::origin();
let gl = gl.with_mvp(graphics::Mvp::ortho_top_left(Rect{
pos: pnt2!(0),
width: window_size.x,
height: window_size.y,
}));
let draw_rect = |surface_ratio: f32|{
let window_ratio = (window_size.x as f32 - pos.x) / (window_size.y as f32 - pos.y);
if surface_ratio >= window_ratio{
let draw_width = window_size.x as f32 - pos.x;
let draw_height = draw_width / surface_ratio;
let draw_y = (window_size.y as f32 - pos.y - draw_height) / 2.;
Rect{
pos: pos + vec2(0., draw_y),
width: draw_width,
height: draw_height
}
}else{
let draw_height = window_size.y as f32 - pos.y;
let draw_width = draw_height * surface_ratio;
let draw_x = (window_size.x as f32 - pos.x - draw_width as f32) / 2.;
Rect{
pos: pos + vec2(draw_x, 0.),
width: draw_width,
height: draw_height,
}
}
};
#[cfg(feature="postprocessing")]
if let Some(postpro) = resources.get::<rin_postpo::PostProcessing>(){
let parameters = resources.get::<rin_postpo::Parameters>().unwrap();
let fbo = postpro.last_fbo(¶meters);
let surface_ratio = fbo.aspect_ratio();
let rect = draw_rect(surface_ratio);
gl.draw_rect(fbo.color_tex(0).unwrap(), &rect);
return;
}
if let glin::fbo::ColorAttachment::TextureLevel(color, _) = render_surface.color_attachment() {
let surface_ratio = render_surface.aspect_ratio();
let rect = draw_rect(surface_ratio);
gl.draw_rect(color, &rect);
}
}
}
}