use rinecs::{
Read, Entity, ReadNot, Write, ReadOption,
EntitiesThreadLocal, ResourcesThreadLocal, CreationProxy
};
use rin::math::*;
use rin::gl;
use crate::{
material::{
water::{self, ParametersSend}, AlphaType, texture::TextureSampler,
},
light::*,
transformation::RenderPlane,
time::Clock,
renderer::{
light::ImageBasedLight,
TextureBindingPoints,
},
};
use super::components::{
ProgramRef,
RenderPlane as GpuRenderPlane
};
use super::{
shader_data::Data,
resources::TexturesPool,
Material,
};
use generational_arena::Arena;
use glin::OffscreenBuffer;
use std::mem;
fn create_buffers(gl: &gl::Renderer, buffer_size: Vec2<u32>, msaa_samples: u32) -> (gl::Fbo, gl::Fbo) {
let fbo_reflection;
let fbo_refraction;
if msaa_samples == 0 {
fbo_reflection = gl.new_fbo().from_color_depth(
gl.new_fbo_color_attachment()
.texture(buffer_size.x, buffer_size.y, gl::fbo::ColorFormat::RGBA16F)
.unwrap(),
gl.new_fbo_depth_attachment()
.render_buffer(buffer_size.x, buffer_size.y, gl::fbo::DepthFormat::_24)
.unwrap(),
).unwrap();
fbo_refraction = gl.new_fbo().from_color_depth(
gl.new_fbo_color_attachment()
.texture(buffer_size.x, buffer_size.y, gl::fbo::ColorFormat::RGBA16F)
.unwrap(),
gl.new_fbo_depth_attachment()
.texture(buffer_size.x, buffer_size.y, gl::fbo::DepthFormat::_24)
.unwrap(),
).unwrap();
}else{
fbo_reflection = gl.new_fbo().from_color_depth(
gl.new_fbo_color_attachment()
.texture_multisampled(buffer_size.x, buffer_size.y, msaa_samples, gl::fbo::ColorFormat::RGBA16F)
.unwrap(),
gl.new_fbo_depth_attachment()
.render_buffer_multisampled(buffer_size.x, buffer_size.y, msaa_samples, gl::fbo::DepthFormat::_24)
.unwrap(),
).unwrap();
fbo_refraction = gl.new_fbo().from_color_depth(
gl.new_fbo_color_attachment()
.texture_multisampled(buffer_size.x, buffer_size.y, msaa_samples, gl::fbo::ColorFormat::RGBA16F)
.unwrap(),
gl.new_fbo_depth_attachment()
.texture_multisampled(buffer_size.x, buffer_size.y, msaa_samples, gl::fbo::DepthFormat::_24)
.unwrap(),
).unwrap();
}
(fbo_reflection, fbo_refraction)
}
pub fn upload_gpu_resources(mut entities: CreationProxy, resources: ResourcesThreadLocal) {
let gl = resources.get::<gl::Renderer<'static>>().unwrap();
let to_add = entities
.iter_for::<(Entity, Read<ParametersSend>, ReadNot<RenderPlane, GpuRenderPlane>)>()
.map(|(entity, parameters, renderplanes)| {
if let [reflection, refraction] = renderplanes{
let (fbo_reflection, fbo_refraction) = create_buffers(&gl, parameters.buffer_size, parameters.msaa_samples);
let reflection_camera_ubo = gl.new_buffer()
.from_data(&[reflection.camera_matrices().data()], gl::STATIC_DRAW)
.unwrap();
let refraction_camera_ubo = gl.new_buffer()
.from_data(&[refraction.camera_matrices().data()], gl::STATIC_DRAW)
.unwrap();
let reflection = GpuRenderPlane{
fbo: fbo_reflection,
camera_ubo: reflection_camera_ubo
};
let refraction = GpuRenderPlane{
fbo: fbo_refraction,
camera_ubo: refraction_camera_ubo
};
(entity, reflection, refraction)
}else{
unreachable!()
}
}).collect::<Vec<_>>();
for (entity, reflection, refraction) in to_add {
entities.add_slice_component_to_thread_local(&entity, vec![reflection, refraction]);
}
for (parameters, renderplanes, gpu_renderplanes) in entities.iter_for::<(Read<ParametersSend>, Read<RenderPlane>, Write<GpuRenderPlane>)>(){
if let ([reflection, refraction], [gpu_reflection, gpu_refraction]) = (renderplanes, gpu_renderplanes) {
let (w,h) = gpu_reflection.fbo.size();
let current_size = vec2(w,h);
if parameters.buffer_size != current_size {
trace!("Resizing renderplane gpu buffers {}x{} -> {}x{}",
gpu_reflection.fbo.width(), gpu_reflection.fbo.height(),
parameters.buffer_size.x, parameters.buffer_size.y);
let (fbo_reflection, fbo_refraction) = create_buffers(&gl, parameters.buffer_size, parameters.msaa_samples);
gpu_reflection.fbo = fbo_reflection;
gpu_refraction.fbo = fbo_refraction;
}
if reflection.has_changed() || refraction.has_changed() {
gpu_reflection.camera_ubo.update(&[reflection.camera_matrices().data()]);
gpu_refraction.camera_ubo.update(&[refraction.camera_matrices().data()]);
}
}
}
}
mod textures{
use rin::gl;
use rin::graphics;
image_cache!(get_image_32bit, IMAGE32, {
let brdf_lut = include_bytes!("textures/brdfLUT.exr");
graphics::image::load_from_memory(brdf_lut).unwrap()
});
image_cache!(get_image_16bit, IMAGE16, {
let brdf_lut = include_bytes!("textures/brdfLUT16.png");
graphics::image::load_from_memory(brdf_lut).unwrap()
});
image_cache!(dudv_map, DUDV, {
let map = include_bytes!("textures/water_dudvmap.png");
graphics::image::load_from_memory(map).unwrap()
}, |tex: &mut gl::Texture|{
tex.set_wrap_s(gl::REPEAT);
tex.set_wrap_t(gl::REPEAT);
});
image_cache!(normal_map, NORMAL, {
let map = include_bytes!("textures/water_normalmap.png");
graphics::image::load_from_memory(map).unwrap()
}, |tex: &mut gl::Texture|{
tex.set_wrap_s(gl::REPEAT);
tex.set_wrap_t(gl::REPEAT);
});
pub fn brdf_texture<'r, R: glin::RenderSurface>(gl: &'r gl::Renderer<R>) -> &'r glin::Texture{
if graphics::image::supports(graphics::image::Format::Exr){
get_image_32bit(gl)
}else{
get_image_16bit(gl)
}
}
}
mod shaders{
use std::cell::UnsafeCell;
use std::collections::HashMap;
use std::path::Path;
use rin::util::{LogErr, AutoLoader};
use rin::gl;
use generational_arena::Arena;
use crate::renderer::components::ProgramRef;
use crate::renderer::{
TextureBindingPoints,
use_model_as_attribute, default_glsl_version, use_material_unified_ubo,
};
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct Settings{
pub msaa_samples: u32,
pub num_directional_lights: u32,
pub num_spot_lights: u32,
pub num_point_lights: u32,
pub max_num_shadow_maps_directional: u32,
pub max_num_shadow_maps_spot: u32,
pub max_num_shadow_maps_point: u32,
pub num_shadow_maps: u32,
pub use_ibl: bool,
}
struct Loader{
loader: AutoLoader<glin::Program>,
program: ProgramRef,
}
impl Loader{
fn update(&mut self, arena: &mut Arena<glin::Program>){
match self.loader.update(){
Ok(Some(program)) => {
info!("water shader reloaded correctly");
arena.remove(*self.program);
self.program = ProgramRef::new(arena.insert(program));
}
Err(err) =>
error!("Error reloading pbr shader {}", err),
_ => ()
}
}
}
pub fn set_defaults<R: gl::RenderSurface>(program: &mut glin::Program, gl: &gl::Renderer<R>){
program.set_uniforms(uniforms!{
dudv_map: (super::textures::dudv_map(gl), 2),
normal_map: (super::textures::normal_map(gl), 3),
iblbrdf: (super::textures::brdf_texture(gl), TextureBindingPoints::IblBrdf as u32),
}).is_ok();
program
.set_uniform(&gl::UBOBindingPoints::Lighting.to_uniform_block_binding())
.is_ok();
program
.set_uniform(&gl::UBOBindingPoints::Camera.to_uniform_block_binding())
.unwrap();
program
.set_uniform(&gl::UBOBindingPoints::Material.to_uniform_block_binding())
.unwrap();
}
thread_local!(static PROGRAM: UnsafeCell<HashMap<(usize, Settings), Loader>> = UnsafeCell::new(HashMap::new()));
pub fn program<R: gl::RenderSurface>(gl: &gl::Renderer<R>, settings: &Settings, arena: &mut Arena<glin::Program>) -> ProgramRef{
let loader = PROGRAM.with(|cache|{
unsafe{
(*cache.get()).entry((gl.id(), *settings)).or_insert_with(||{
trace!("Compiling water program");
let crate_path = Path::new("");
let src_path = crate_path.join(Path::new(file!()).parent().unwrap());
let vertex_path = src_path.join("shaders/water2.vs.glsl");
let fragment_path = src_path.join("shaders/water2.fs.glsl");
let use_ibl = (settings.use_ibl as u8).to_string();
let use_ubo = (!use_material_unified_ubo() as u8).to_string();
let use_ssbo = (use_material_unified_ubo() as u8).to_string();
let inverted_ibl_lut;
if settings.use_ibl {
#[cfg(feature="freeimage")]
{
inverted_ibl_lut = "1".to_string();
}
#[cfg(feature="image")]
{
inverted_ibl_lut = "0".to_string();
}
#[cfg(not(any(feature="image", feature="freeimage")))]
{
error!("no support for image loading, can't load brdf lut for pbr materials. Add one of \"image\" or \"freeimage\" features to your Cargo.toml");
panic!();
}
}else{
inverted_ibl_lut = "0".to_string();
}
let settings = gl::autoload::ProgramSettings{
defines: vec![
("NUM_DIRECTIONAL_LIGHTS".to_string(), settings.num_directional_lights.to_string()),
("NUM_SPOT_LIGHTS".to_string(), settings.num_spot_lights.to_string()),
("NUM_POINT_LIGHTS".to_string(), settings.num_point_lights.to_string()),
("USE_MSAA".to_string(), settings.msaa_samples.to_string()),
("USE_CAMERA_UBO".to_string(), "1".to_string()),
("USE_MODEL_ATTR".to_string(), (use_model_as_attribute() as u8).to_string()),
("USE_UBOS".to_string(), use_ubo),
("USE_SSBO".to_string(), use_ssbo),
("MAX_NUM_SHADOW_MAPS_DIRECTIONAL".to_string(), settings.max_num_shadow_maps_directional.to_string()),
("MAX_NUM_SHADOW_MAPS_SPOT".to_string(), settings.max_num_shadow_maps_spot.to_string()),
("MAX_NUM_SHADOW_MAPS_POINT".to_string(), settings.max_num_shadow_maps_point.to_string()),
("NUM_SHADOW_MAPS".to_string(), settings.num_shadow_maps.to_string()),
("HAS_SHADOWMAP".to_string(), settings.num_shadow_maps.to_string()),
("USE_IBL".to_string(), use_ibl),
("INVERTED_IBL_BRDF_LUT".to_string(), inverted_ibl_lut),
],
precision: gl::program::ShaderPrecision::High,
extensions: vec![],
version: default_glsl_version(),
shaders: vec![
(gl::VERTEX_SHADER, vertex_path),
(gl::FRAGMENT_SHADER, fragment_path),
]
};
let mut loader = gl.new_auto_program(settings);
let mut program = loader.load()
.log_err("")
.map_err(|_| panic!())
.unwrap();
set_defaults(&mut program, gl);
let program = ProgramRef::new(arena.insert(program));
Loader {
loader,
program,
}
})
}
});
loader.update(arena);
set_defaults(&mut arena[*loader.program], gl);
loader.program
}
}
impl water::Parameters{
fn to_data(&self) -> Data{
let mut data = Data::with_capacity(16 * mem::size_of::<f32>());
data.push(self.frequency.to_value());
data.push(self.wave_strength.to_value());
data.push(self.speed.to_value());
data.push(self.reflective_factor.to_value());
data.push(self.normal_scale.to_value());
data.push(self.max_visible_depth.to_value());
data.push(self.water_density.to_value());
data.push(self.color_mix.to_value());
data.push(self.water_color.to_value());
data.push(self.highlights_factor.to_value());
data.push(self.highlights_power.to_value());
data.push(self.time);
data.pad::<f32>();
data
}
}
impl Material for water::Material{
fn data(&self) -> Option<Data>{
Some(self.parameters.to_data())
}
fn uniforms(&self, entities: &EntitiesThreadLocal, textures: &TexturesPool) -> Vec<glin::program::Uniform>{
let mut uniforms = if let [reflection, refraction] = *entities.component_for::<GpuRenderPlane>(&self.entity).unwrap(){
uniforms!{
reflection_texture: (reflection.render_buffer().color_tex(0).unwrap(), 0),
refraction_texture: (refraction.render_buffer().color_tex(0).unwrap(), 1),
refraction_depth_texture: (refraction.render_buffer().depth_tex().unwrap(), 4),
}
}else{
unreachable!()
};
if let Some(ibl) = entities.iter_for::<Read<ImageBasedLight>>().next(){
let specular = &textures[ibl.specular()];
uniforms.push(glin::program::uniform("envmap", &(specular, TextureBindingPoints::SpecularIbl as u32)));
}
uniforms
}
fn properties(&self) -> Vec<glin::Property>{
vec![
gl::Property::Blend(true),
gl::Property::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA),
]
}
fn program(&self, entities: &EntitiesThreadLocal, programs: &mut Arena<glin::Program>, gl: &gl::Renderer) -> ProgramRef{
let num_directional_lights = entities.iter_for::<Read<DirectionalLight>>().count() as u32;
let num_spot_lights = entities.iter_for::<Read<SpotLight>>().count() as u32;
let num_point_lights = entities.iter_for::<Read<PointLight>>().count() as u32;
let max_num_shadow_maps_directional;
let max_num_shadow_maps_spot;
let max_num_shadow_maps_point;
let num_shadow_maps;
if *self.parameters.receives_shadows {
max_num_shadow_maps_directional = entities
.iter_for::<(Read<DirectionalLight>, ReadOption<shadow::Map>, ReadOption<shadow::StaticMap>)>()
.map(|(_,s,ss)| s.is_some() as u8 + ss.is_some() as u8)
.max()
.unwrap_or(0) as u32;
max_num_shadow_maps_spot = entities
.iter_for::<(Read<SpotLight>, ReadOption<shadow::Map>, ReadOption<shadow::StaticMap>)>()
.map(|(_,s,ss)| s.is_some() as u8 + ss.is_some() as u8)
.max()
.unwrap_or(0) as u32;
max_num_shadow_maps_point = entities
.iter_for::<(Read<PointLight>, ReadOption<shadow::Map>, ReadOption<shadow::StaticMap>)>()
.map(|(_,s,ss)| s.is_some() as u8 + ss.is_some() as u8)
.max()
.unwrap_or(0) as u32;
num_shadow_maps = entities.iter_for::<Read<shadow::Map>>().count() as u32 +
entities.iter_for::<Read<shadow::StaticMap>>().count() as u32;
}else{
max_num_shadow_maps_directional = 0;
max_num_shadow_maps_spot = 0;
max_num_shadow_maps_point = 0;
num_shadow_maps = 0;
}
let use_ibl = if *self.parameters.receives_global_illumination{
entities.iter_for::<Read<ImageBasedLight>>().next().is_some()
}else{
false
};
let shader_settings = shaders::Settings{
msaa_samples: self.msaa_samples,
num_directional_lights,
num_spot_lights,
num_point_lights,
max_num_shadow_maps_directional,
max_num_shadow_maps_spot,
max_num_shadow_maps_point,
num_shadow_maps,
use_ibl,
};
shaders::program(gl, &shader_settings, programs)
}
fn double_sided(&self) -> bool{
false
}
fn textures(&self) -> Vec<&TextureSampler>{
vec![]
}
fn alpha(&self) -> f32{
self.parameters.water_color.a
}
fn alpha_type(&self) -> AlphaType{
AlphaType::Blending
}
fn update(&mut self, entities: &EntitiesThreadLocal, resources: &ResourcesThreadLocal) -> bool{
let delta = resources.get::<Clock>()
.unwrap()
.last_frame_time()
.elapsed_game_time()
.as_seconds();
self.parameters.time += delta as f32 * *self.parameters.speed;
true
}
}