use crate::renderer::{
components::ProgramRef,
resources::{LightingTextures, ScreenRenderBuffer},
use_model_as_attribute,
};
use crate::light::LightInfo;
use crate::time::Clock;
use rin_material::MaterialRef;
use super::{
Shader, CameraType, PostFragment, ProgramSettings, FullMaterial, MaterialCache,
ShadowMaterialCache
};
use rin_gl::{self as gl, gl::types::GLenum};
use rin_util::{Result, Error, LogErr};
use rin_math::{vec2, Vec2, convert};
use rin_graphics::CameraExt;
use glsl::{
parser::Parse,
syntax::{
FunctionParameterDeclaration, FunctionParameterDeclarator, TypeSpecifierNonArray,
FunctionDefinition
},
};
use rinecs::{EntitiesThreadLocal, ResourcesThreadLocal, system_thread_local};
#[cfg(glsl_debug)]
use itertools::Itertools;
use generational_arena::Arena;
use regex::Regex;
use glin::CreationContext;
use std::fs;
#[cfg(glsl_debug)]
use std::path::{Path, PathBuf};
use std::io::Read;
use std::ops::{Index, IndexMut, Deref};
use std::mem;
#[derive(Clone, Hash, Debug, Eq, PartialEq)]
pub struct ProgramId{
pub defines: Vec<(String, String)>,
pub shaders: Vec<Shader>,
pub post_fragment: Option<PostFragment>,
pub light_info: Option<LightInfo>,
pub camera_type: CameraType,
pub is_translucent: bool,
}
struct ProgramData{
settings: ProgramSettings,
shaders: Vec<Shader>,
post_fragment: Option<PostFragment>,
vertex_final_replacements: Vec<(&'static str, String)>,
fragment_final_replacements: Vec<(&'static str, String)>,
}
impl ProgramData{
#[cfg(not(glsl_debug))]
fn to_settings(self) -> Result<(glin::program::Settings<String>, Vec<ShaderInfo>)> {
let mut program_includes = self.settings.includes_replace
.into_iter()
.collect::<rin_gl::HashMap<_,_>>();
let mut defines = self.settings.defines;
let mut shaders = vec![];
let mut shader_infos = vec![];
let mut post_fragment_include_name;
let post_fragment_shader_info;
match self.post_fragment {
Some(PostFragment::Source{include_name, source, replacements, base_path, includes}) => {
program_includes.extend(includes);
let source = replacements.iter()
.fold(source, |source, (from, to)| source.replace(from, to));
post_fragment_shader_info = Some(parse_shader(&include_name, &source, ShaderTy::PostFragment)?);
program_includes.insert(include_name.clone(), source);
post_fragment_include_name = Some(include_name);
}
Some(PostFragment::Path{path, replacements, base_path, includes, ..}) => {
let mut source = String::new();
let mut shader_file = fs::File::open(&path).unwrap();
shader_file.read_to_string(&mut source).unwrap();
program_includes.extend(includes);
let source = replacements.iter()
.fold(source, |source, (from, to)| source.replace(from, to));
let include_name = format!("{}", path.display());
post_fragment_shader_info = Some(parse_shader(&include_name, &source, ShaderTy::PostFragment)?);
program_includes.insert(include_name.clone(), source);
post_fragment_include_name = Some(include_name);
}
None => {
post_fragment_include_name = None;
post_fragment_shader_info = None
}
};
if let Some(post_fragment_shader_info) = post_fragment_shader_info.as_ref(){
defines.extend(post_fragment_shader_info.defines())
}
let post_fragment_call = post_fragment_shader_info
.as_ref()
.map(|shader_info| shader_info.shader_call.as_str())
.unwrap_or("");
let post_fragment_include = post_fragment_include_name
.take()
.map(|include| format!("#include \"{}\"", include));
let post_fragment_include = post_fragment_include
.as_ref()
.map(|s| s.as_str())
.unwrap_or("");
for shader in self.shaders {
let (shader, shader_info, ty) = match shader{
Shader::Source{include_name, source, replacements, base_path, includes, ty} => {
program_includes.extend(includes);
let source = replacements.iter()
.fold(source, |source, (from, to)| source.replace(from, to));
let shader_info = parse_shader(&include_name, &source, ShaderTy::Glenum(ty))?;
program_includes.insert(include_name.clone(), source);
(include_name, shader_info, ty)
}
Shader::Path{path, replacements, base_path, includes, ty, ..} => {
let mut source = String::new();
let mut shader_file = fs::File::open(&path).unwrap();
shader_file.read_to_string(&mut source).unwrap();
program_includes.extend(includes);
let source = replacements.iter()
.fold(source, |source, (from, to)| source.replace(from, to));
let include_name = format!("{}", path.display());
let shader_info = parse_shader(&include_name, &source, ShaderTy::Glenum(ty))?;
program_includes.insert(include_name.clone(), source);
(include_name, shader_info, ty)
}
};
match ty{
gl::VERTEX_SHADER => {
let shader = VERTEX_MAIN.replace("${vertex_shader}", &shader);
let shader = self.vertex_final_replacements.iter()
.fold(shader, |shader, (from, to)| shader.replace(from, to))
.replace("${shader_call}", &shader_info.shader_call)
.replace("${material_declaration}", &shader_info.material_declaration)
.replace("${retrieve_material}", &shader_info.retrieve_material);
defines.extend(shader_info.defines());
shaders.push((gl::VERTEX_SHADER, shader));
}
gl::FRAGMENT_SHADER => {
let shader = FRAGMENT_MAIN
.replace("${fragment_shader}", &shader)
.replace("${post_fragment}", post_fragment_include)
.replace("${material_declaration}", &shader_info.material_declaration)
.replace("${retrieve_material}", &shader_info.retrieve_material);
let shader = self.fragment_final_replacements.iter()
.fold(shader, |shader, (from, to)| shader.replace(from, to))
.replace("${shader_call}", &shader_info.shader_call)
.replace("${post_fragment_call}", post_fragment_call);
defines.extend(shader_info.defines());
shaders.push((gl::FRAGMENT_SHADER, shader));
}
_ => log::warn!("Shader type {} not supported yet", ty)
}
shader_infos.push(shader_info);
}
Ok((glin::program::Settings{
version: self.settings.version,
precision: self.settings.precision,
extensions: self.settings.extensions,
defines,
bindings: self.settings.bindings,
shaders,
base_path: String::new(),
includes: program_includes,
}, shader_infos))
}
#[cfg(glsl_debug)]
fn to_settings(self) -> Result<(gl::autoload::ProgramSettings<PathBuf>, Vec<ShaderInfo>)> {
let mut shader_replace = vec![];
let mut includes_search_paths = vec![];
let mut program_includes = self.settings.includes_replace
.into_iter()
.collect::<rin_gl::HashMap<_,_>>();
let mut defines = self.settings.defines;
let mut shader_infos = vec![];
let mut post_fragment_include_name;
let mut post_fragment_shader_info;
match self.post_fragment {
Some(PostFragment::Source{include_name, source, replacements, base_path, includes}) => {
program_includes.extend(includes);
let source = replacements.iter()
.fold(source, |source, (from, to)| source.replace(from, to));
post_fragment_shader_info = Some(parse_shader(&include_name, &source, ShaderTy::PostFragment)?);
program_includes.insert(include_name.clone(), source);
includes_search_paths.extend(base_path);
post_fragment_include_name = Some(include_name);
shader_replace.extend(replacements.into_iter().map(|r| (gl::FRAGMENT_SHADER, r)));
}
Some(PostFragment::Path{path, replacements, base_path, includes}) => {
program_includes.extend(includes);
let full_path = if path.is_absolute(){
includes_search_paths.push(path.parent().unwrap().to_owned());
path.clone()
}else{
includes_search_paths.extend(base_path.clone());
base_path.unwrap().join(&path)
};
let mut source = String::new();
let mut shader_file = fs::File::open(&full_path).unwrap();
shader_file.read_to_string(&mut source).unwrap();
let source = replacements.iter()
.fold(source, |source, (from, to)| source.replace(from, to));
let fragment_include_name = format!("{}", path.display());
post_fragment_shader_info = Some(parse_shader(&fragment_include_name, &source, ShaderTy::PostFragment)?);
post_fragment_include_name = Some(fragment_include_name);
shader_replace.extend(replacements.into_iter().map(|r| (gl::FRAGMENT_SHADER, r)));
}
None => {
post_fragment_include_name = None;
post_fragment_shader_info = None
}
};
if let Some(post_fragment_shader_info) = post_fragment_shader_info.as_ref(){
defines.extend(post_fragment_shader_info.defines())
}
let mut post_fragment_call = post_fragment_shader_info
.take()
.map(|shader_info| shader_info.shader_call)
.or_else(|| Some("".to_owned()));
let mut post_fragment_include = post_fragment_include_name
.take()
.map(|include| format!("#include \"{}\"", include))
.or_else(|| Some("".to_owned()));
for shader in self.shaders {
let (shader, replacements, shader_info, shader_ty) = match shader {
Shader::Source{include_name, source, replacements, base_path, includes, ty} => {
program_includes.extend(includes);
let source = replacements.iter()
.fold(source, |source, (from, to)| source.replace(from, to));
let shader_info = parse_shader(&include_name, &source, ShaderTy::Glenum(ty))?;
program_includes.insert(include_name.clone(), source);
includes_search_paths.extend(base_path);
(include_name, replacements, shader_info, ty)
}
Shader::Path{path, replacements, base_path, includes, ty} => {
program_includes.extend(includes);
let full_path = if path.is_absolute(){
includes_search_paths.push(path.parent().unwrap().to_owned());
path.clone()
}else{
includes_search_paths.extend(base_path.clone());
base_path.unwrap().join(&path)
};
let mut source = String::new();
let mut shader_file = fs::File::open(&full_path).unwrap();
shader_file.read_to_string(&mut source).unwrap();
let source = replacements.iter()
.fold(source, |source, (from, to)| source.replace(from, to));
let include_name = format!("{}", path.display());
let shader_info = parse_shader(&include_name, &source, ShaderTy::Glenum(ty))?;
(include_name, replacements, shader_info, ty)
}
};
match shader_ty{
gl::VERTEX_SHADER => {
let vertex_final_replacements = self.vertex_final_replacements.iter()
.map(|(from, to)| (from.to_string(), to.clone()));
let replacements = replacements.into_iter()
.chain(vertex_final_replacements)
.chain(Some(("${vertex_shader}".to_string(), shader)))
.chain(Some(("${shader_call}".to_string(), shader_info.shader_call.clone())))
.chain(Some(("${material_declaration}".to_string(), shader_info.material_declaration.clone())))
.chain(Some(("${retrieve_material}".to_string(), shader_info.retrieve_material.clone())))
.map(|replacement| (shader_ty, replacement));
shader_replace.extend(replacements);
defines.extend(shader_info.defines());
}
gl::FRAGMENT_SHADER => {
let fragment_final_replacements = self.fragment_final_replacements.iter()
.map(|(from, to)| (from.to_string(), to.clone()));
let replacements = replacements.into_iter()
.chain(fragment_final_replacements)
.chain(Some(("${fragment_shader}".to_string(), shader)))
.chain(Some(("${shader_call}".to_string(), shader_info.shader_call.clone())))
.chain(Some(("${post_fragment}".to_string(), post_fragment_include.take().unwrap())))
.chain(Some(("${post_fragment_call}".to_string(), post_fragment_call.take().unwrap())))
.chain(Some(("${material_declaration}".to_string(), shader_info.material_declaration.clone())))
.chain(Some(("${retrieve_material}".to_string(), shader_info.retrieve_material.clone())))
.map(|replacement| (shader_ty, replacement));
defines.extend(shader_info.defines());
shader_replace.extend(replacements);
}
_ => log::warn!("Shader type {} not supported yet", shader_ty)
}
shader_infos.push(shader_info);
}
let src_path = Path::new(file!()).parent().unwrap();
let vertex_path = src_path
.join("shaders/main.vs.glsl")
.to_owned();
let fragment_path = src_path
.join("shaders/main.fs.glsl")
.to_owned();
let includes_search_paths = includes_search_paths.into_iter().unique().collect();
Ok((gl::autoload::ProgramSettings{
version: self.settings.version,
precision: self.settings.precision,
extensions: self.settings.extensions,
defines,
shaders: vec![
(gl::VERTEX_SHADER, vertex_path),
(gl::FRAGMENT_SHADER, fragment_path),
],
shader_replace,
includes_search_paths,
includes_dictionary: program_includes,
}, shader_infos))
}
}
pub struct Program {
#[cfg(glsl_debug)]
loader: rin_util::AutoLoader<glin::Program>,
program: glin::Program,
needs_light_info: bool,
#[cfg(glsl_debug)]
needs_material_data: bool,
time_location: Option<u32>,
max_binding: u32,
}
impl Program {
#[cfg(glsl_debug)]
fn update(&mut self, lighting_textures: &LightingTextures, gl: &gl::Renderer){
match self.loader.update() {
Ok(Some(mut new_program)) => {
log::info!("shader reloaded correctly");
self.max_binding = set_default_uniforms(
&mut new_program,
gl,
self.needs_light_info,
self.needs_material_data);
self.program = new_program;
if self.needs_light_info {
self.set_lighting_textures(lighting_textures);
}
}
Err(err) => log::error!("Error reloading shader {}", err),
_ => ()
}
}
pub(super) fn set_lighting_textures(&self, lighting_textures: &LightingTextures) {
let mut max_binding = self.max_binding;
for uniform in lighting_textures.ibl.iter(){
if let Some(loc) = self.program.uniform_location(&uniform.name){
self.program.set_uniform( &glin::program::Uniform::Location(loc,
glin::program::UniformValue::Texture(
uniform.texture,
max_binding,
uniform.target)
)).unwrap();
}
max_binding -= 1;
}
for uniform in lighting_textures.shadow_maps.iter(){
if let Some(loc) = self.program.uniform_location(&uniform.sampler_name()){
self.program.set_uniform( &glin::program::Uniform::Location(loc,
glin::program::UniformValue::TextureSampler(
uniform.texture,
max_binding,
uniform.target,
uniform.sampler)
)).unwrap();
}
max_binding -= 1;
if let Some(loc) = self.program.uniform_location(&uniform.pcf_sampler_name()){
self.program.set_uniform( &glin::program::Uniform::Location(loc,
glin::program::UniformValue::TextureSampler(
uniform.texture,
max_binding,
uniform.target,
uniform.pcf_sampler)
)).unwrap();
}
max_binding -= 1;
}
}
pub(super) fn program(&self) -> &glin::Program {
&self.program
}
}
fn set_default_uniforms(
program: &mut glin::Program,
gl: &gl::Renderer,
needs_light_info: bool,
needs_material_data: bool) -> MaxBinding
{
let mut max_binding = gl.context().capabilities().max_texture_image_units;
if needs_light_info {
program
.set_uniform(&super::UBOBindingPoints::Lighting.to_uniform_block_binding())
.unwrap();
#[cfg(image_enabled)]
{
if let Some(loc) = program.uniform_location("iblbrdf"){
program.set_uniform(&glin::program::uniform_location(
loc,
&(textures::brdf_texture(gl), max_binding)))
.unwrap();
}
max_binding -= 1;
if let Some(loc) = program.uniform_location("iblclothbrdf"){
program.set_uniform(&glin::program::uniform_location(
loc,
&(textures::cloth_ashikhmin_brdf_texture(gl), max_binding)))
.unwrap();
}
max_binding -= 1;
}
#[cfg(dds)]
{
if let Some(loc) = program.uniform_location("ltc_1"){
program.set_uniform(&glin::program::uniform_location(
loc,
&(textures::ltc1_texture(gl), max_binding)))
.unwrap();
}
max_binding -= 1;
if let Some(loc) = program.uniform_location("ltc_2"){
program.set_uniform(&glin::program::uniform_location(
loc,
&(textures::ltc2_texture(gl), max_binding)))
.unwrap();
}
max_binding -= 1;
}
}
program
.set_uniform(&super::UBOBindingPoints::Camera.to_uniform_block_binding())
.unwrap();
mem::drop(program.set_uniform(&super::UBOBindingPoints::SceneCamera.to_uniform_block_binding()));
if needs_material_data {
program
.set_uniform(&super::UBOBindingPoints::Material.to_uniform_block_binding())
.unwrap();
}
max_binding
}
#[cfg(not(glsl_debug))]
type ProgramSettingsInfo = (glin::program::Settings<String>, Vec<ShaderInfo>);
#[cfg(glsl_debug)]
type ProgramSettingsInfo = (gl::autoload::ProgramSettings<PathBuf>, Vec<ShaderInfo>);
type NeedsLightInfo = bool;
type MaxBinding = u32;
pub struct ProgramCache{
reverse_index: hashbrown::HashMap<ProgramRef, ProgramId>,
programs: hashbrown::HashMap<ProgramId, ProgramRef>,
arena: Arena<Program>,
materials_to_register: Vec<Box<dyn FullMaterial>>,
unified_material_ubo: bool,
needs_indexing_hack: bool,
#[cfg(feature="gui")]
active_programs: rin_events::Property<'static, usize>,
}
#[cfg(not(glsl_debug))]
static VERTEX_MAIN: &str = include_str!("shaders/main.vs.glsl");
#[cfg(not(glsl_debug))]
static FRAGMENT_MAIN: &str = include_str!("shaders/main.fs.glsl");
impl ProgramCache{
pub fn new(
unified_material_ubo: bool,
needs_indexing_hack: bool,
#[cfg(feature="gui")]
active_programs: rin_events::Property<'static, usize>,
) -> ProgramCache
{
ProgramCache {
reverse_index: hashbrown::HashMap::default(),
programs: hashbrown::HashMap::default(),
arena: Arena::new(),
materials_to_register: vec![],
unified_material_ubo,
needs_indexing_hack,
#[cfg(feature="gui")]
active_programs,
}
}
pub fn register_program_for<M,IM>(&mut self, materials: IM)
where
M: FullMaterial + 'static,
IM: IntoIterator<Item = M>
{
let materials = materials.into_iter().map(|m| Box::new(m) as Box<dyn FullMaterial>);
self.materials_to_register.extend(materials);
}
pub fn remove(&mut self, programref: ProgramRef) {
if let Some(programid) = self.reverse_index.remove(&programref){
self.programs.remove(&programid);
}
self.arena.remove(*programref);
#[cfg(feature="gui")]
self.active_programs.set(self.arena.len());
}
fn program_settings<S: Deref<Target = ScreenRenderBuffer>>(
mut mat_program_settings: ProgramSettings,
shaders: Vec<Shader>,
post_fragment: Option<PostFragment>,
screen_renderbuffer: Option<&S>,
is_translucent: bool) -> Result<ProgramSettingsInfo>
{
let has_post_fragment = (post_fragment.is_some() as u8).to_string();
mat_program_settings.defines.push(("HAS_POST_FRAGMENT".to_owned(), has_post_fragment));
let output_position;
let output_normal;
let output_linear_depth;
let output_ambient;
let position_location;
let normal_location;
let linear_depth_location;
let ambient_location;
if let Some(screen_renderbuffer) = if !is_translucent { screen_renderbuffer } else { None } {
output_position = screen_renderbuffer.position_location().is_some();
output_normal = screen_renderbuffer.normals_location().is_some();
output_linear_depth = screen_renderbuffer.linear_depth_location().is_some();
output_ambient = screen_renderbuffer.separate_ambient_location().is_some();
position_location = screen_renderbuffer.position_location().unwrap_or(0);
normal_location = screen_renderbuffer.normals_location().unwrap_or(0);
linear_depth_location = screen_renderbuffer.linear_depth_location().unwrap_or(0);
ambient_location = screen_renderbuffer.separate_ambient_location().unwrap_or(0);
}else{
output_position = false;
output_normal = false;
output_linear_depth = false;
output_ambient = false;
position_location = 0;
normal_location= 0;
linear_depth_location= 0;
ambient_location = 0;
}
let program_data = ProgramData{
settings: mat_program_settings,
shaders,
post_fragment,
vertex_final_replacements: vec![
("${output_position}", (output_position as u8).to_string()),
("${output_normal}", (output_normal as u8).to_string()),
("${output_linear_depth}", (output_linear_depth as u8).to_string()),
("${output_ambient}", (output_ambient as u8).to_string()),
],
fragment_final_replacements: vec![
("${output_position}", (output_position as u8).to_string()),
("${output_normal}", (output_normal as u8).to_string()),
("${output_linear_depth}", (output_linear_depth as u8).to_string()),
("${output_ambient}", (output_ambient as u8).to_string()),
("${position_location}", position_location.to_string()),
("${normal_location}", normal_location.to_string()),
("${linear_depth_location}", linear_depth_location.to_string()),
("${ambient_location}", ambient_location.to_string()),
]
};
program_data.to_settings()
}
pub(super) fn program_for<S: Deref<Target = ScreenRenderBuffer>>(&mut self,
gl: &gl::Renderer,
mat_name: &str,
mat_program_settings: ProgramSettings,
is_translucent: bool,
shaders: Vec<Shader>,
post_fragment: Option<PostFragment>,
needs_material_data: bool,
light_info: &LightInfo,
camera_type: CameraType,
lighting_textures: &LightingTextures,
screen_renderbuffer: Option<&S>,
) -> (ProgramRef, NeedsLightInfo)
{
let (mut program_settings, shaders_info) = Self::program_settings(
mat_program_settings.clone(),
shaders.clone(),
post_fragment.clone(),
screen_renderbuffer,
is_translucent).unwrap();
let needs_light_info = shaders_info.iter().any(|info| info.needs_light_info.is_some());
let opt_light_info = if needs_light_info {
let mut light_info = light_info.clone();
light_info.reset_changed();
Some(light_info)
} else {
None
};
let program_id = ProgramId{
defines: mat_program_settings.defines,
post_fragment,
shaders,
light_info: opt_light_info,
camera_type,
is_translucent,
};
let mut is_new_program = false;
let unified_material_ubo = self.unified_material_ubo;
let needs_indexing_hack = self.needs_indexing_hack;
let arena = &mut self.arena;
let programref = self.programs.entry(program_id.clone()).or_insert_with(|| {
log::trace!("Reloading program for {}", mat_name);
is_new_program = true;
let use_ssbo = (unified_material_ubo as u8).to_string();
let use_material_ubo = (!unified_material_ubo as u8).to_string();
let needs_indexing_hack = (needs_indexing_hack as u8).to_string();
#[cfg(image_ty="freeimage")]
let inverted_ibl_lut = "1".to_string();
#[cfg(image_ty="image")]
#[cfg(not(any(feature="gles", feature="webgl")))]
let inverted_ibl_lut = "1".to_string();
#[cfg(image_ty="image")]
#[cfg(any(feature="gles", feature="webgl"))]
let inverted_ibl_lut = "0".to_string();
#[cfg(not(image_enabled))]
let inverted_ibl_lut = "0".to_string();
let has_clip_plane = (true as u8).to_string();
#[cfg(not(any(feature="gles", feature="webgl")))]
let clip_distance_gles = (false as u8).to_string();
#[cfg(any(feature="gles", feature="webgl"))]
let clip_distance_gles = (true as u8).to_string();
let orthographic_camera = (camera_type.contains(CameraType::ORTHOGRAPHIC) as u8).to_string();
let far_at_infinity = (camera_type.contains(CameraType::FAR_AT_INFINITY) as u8).to_string();
let reversed_z = (camera_type.contains(CameraType::REVERSED_Z) as u8).to_string();
let system_defines = if needs_light_info {
let shadow_map_type = light_info.shadow_map_type().unwrap_or("0").to_owned();
let shadow_gaussian_filter = light_info.shadow_gaussian_kernel_size().unwrap_or(0);
let debug_cascades = (light_info.debug_cascades as u8).to_string();
vec![
("USES_LIGHTS".to_string(), "1".to_owned()),
("NUM_DIRECTIONAL_LIGHTS".to_string(), light_info.num_directional_lights.to_string()),
("NUM_SPOT_LIGHTS".to_string(), light_info.num_spot_lights.to_string()),
("NUM_POINT_LIGHTS".to_string(), light_info.num_point_lights.to_string()),
("NUM_AREA_LIGHTS".to_string(), light_info.num_area_lights.to_string()),
("USE_IBL".to_string(), (light_info.use_ibl as u8).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(), "1".to_string()),
("USE_MATERIAL_UBO".to_string(), use_material_ubo),
("USE_MATERIAL_SSBO".to_string(), use_ssbo),
("HAS_DYNAMIC_SHADOW_MAPS_DIRECTIONAL".to_string(),
(light_info.has_dynamic_shadow_maps_directional as u8).to_string()),
("HAS_DYNAMIC_SHADOW_MAPS_SPOT".to_string(),
(light_info.has_dynamic_shadow_maps_spot as u8).to_string()),
("HAS_DYNAMIC_SHADOW_MAPS_POINT".to_string(),
(light_info.has_dynamic_shadow_maps_point as u8).to_string()),
("HAS_STATIC_SHADOW_MAPS_DIRECTIONAL".to_string(),
(light_info.has_static_shadow_maps_directional as u8).to_string()),
("HAS_STATIC_SHADOW_MAPS_SPOT".to_string(),
(light_info.has_static_shadow_maps_spot as u8).to_string()),
("HAS_STATIC_SHADOW_MAPS_POINT".to_string(),
(light_info.has_static_shadow_maps_point as u8).to_string()),
("NUM_SHADOW_MAPS".to_string(), light_info.num_shadow_maps.to_string()),
("NUM_SHADOW_SAMPLERS".to_string(), lighting_textures.shadow_maps.len().to_string()),
("HAS_SHADOWMAP".to_string(), ((light_info.num_shadow_maps > 0) as u8).to_string()),
("SHADOW_MAP_TYPE".to_owned(), shadow_map_type),
("DEBUG_CASCADES".to_owned(), debug_cascades),
("INVERTED_IBL_BRDF_LUT".to_string(), inverted_ibl_lut),
("NEEDS_CLIP_PLANE".to_string(), has_clip_plane),
("NEEDS_CLIP_DATA".to_string(), "1".to_string()),
("NEEDS_CAMERA_DATA".to_string(), "1".to_string()),
("CLIP_DISTANCE_GLES".to_string(), clip_distance_gles),
("GAUSSIAN_FILTER_SIZE".to_string(), shadow_gaussian_filter.to_string()),
("ORTHOGRAPHIC_CAMERA".to_string(), orthographic_camera),
("FAR_AT_INFINITY".to_string(), far_at_infinity),
("REVERSED_Z".to_string(), reversed_z),
("NEEDS_INDEXING_HACK".to_string(), needs_indexing_hack),
]
}else{
vec![
("USES_LIGHTS".to_string(), "0".to_owned()),
("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(), "1".to_string()),
("USE_MATERIAL_UBO".to_string(), use_material_ubo),
("USE_MATERIAL_SSBO".to_string(), use_ssbo),
("INVERTED_IBL_BRDF_LUT".to_string(), inverted_ibl_lut),
("NEEDS_CLIP_PLANE".to_string(), has_clip_plane),
("NEEDS_CLIP_DATA".to_string(), "1".to_string()),
("NEEDS_CAMERA_DATA".to_string(), "1".to_string()),
("CLIP_DISTANCE_GLES".to_string(), clip_distance_gles),
("ORTHOGRAPHIC_CAMERA".to_string(), orthographic_camera),
("FAR_AT_INFINITY".to_string(), far_at_infinity),
("REVERSED_Z".to_string(), reversed_z),
("NEEDS_INDEXING_HACK".to_string(), needs_indexing_hack),
]
};
program_settings.defines.extend(system_defines);
#[cfg(not(glsl_debug))]
let (program, max_binding) = {
let system_includes = rin_gl::hash_map!{
"lighting_uniforms.glsl".to_string() => include_str!("shaders/lighting_uniforms.glsl").to_owned(),
"mvp_uniforms.glsl".to_string() => include_str!("../shaders/mvp_uniforms.glsl").to_owned(),
"shadow_poisson.glsl".to_string() => include_str!("shaders/shadow_poisson.glsl").to_owned(),
"shadow_gaussian.glsl".to_string() => include_str!("shaders/shadow_gaussian.glsl").to_owned(),
"shadow_pcss.glsl".to_string() => include_str!("shaders/shadow_pcss.glsl").to_owned(),
"shadow_hard.glsl".to_string() => include_str!("shaders/shadow_hard.glsl").to_owned(),
"main_structs.glsl".to_string() => include_str!("shaders/main_structs.glsl").to_owned(),
"utils.glsl".to_string() => include_str!("shaders/utils.glsl").to_owned()
};
program_settings.includes.extend(system_includes);
let mut program = gl.new_program()
.from_settings(program_settings.clone())
.log_err(&program_settings.base_path)
.unwrap_or_else(|_| panic!());
let max_binding = set_default_uniforms(
&mut program,
gl,
needs_light_info,
needs_material_data);
(program, max_binding)
};
#[cfg(glsl_debug)]
let (program, loader, max_binding) = {
let parent_folder = Path::new(file!()).parent().unwrap();
program_settings.includes_search_paths.push(parent_folder.join("shaders/").to_owned());
program_settings.includes_search_paths.push(parent_folder.join("../shaders/").to_owned());
let mut loader = gl.new_auto_program(program_settings.clone());
let mut program = loader.load()
.log_err("")
.unwrap_or_else(|_| panic!());
let max_binding = set_default_uniforms(
&mut program,
gl,
needs_light_info,
needs_material_data);
(program, loader, max_binding)
};
let needs_time_info = shaders_info.iter().any(|info| info.needs_time_info.is_some());
let time_location = if needs_time_info {
program.uniform_location("rin_time")
}else{
None
};
let id = arena.insert(Program{
#[cfg(glsl_debug)]
loader,
program,
needs_light_info,
#[cfg(glsl_debug)]
needs_material_data,
time_location,
max_binding,
});
ProgramRef::new(id)
});
if is_new_program {
self.reverse_index.insert(*programref, program_id);
}
(*programref, needs_light_info)
}
pub(super) fn update<S>(
&mut self,
gl: &gl::Renderer,
light_info: &LightInfo,
lighting_textures: &LightingTextures,
camera_type: CameraType,
screen_renderbuffer: Option<&S>,
clock: &Clock)
where S: Deref<Target = ScreenRenderBuffer>
{
let mut materials_to_register = mem::replace(&mut self.materials_to_register, vec![]);
for material in materials_to_register.drain(..){
let program_settings = material.material().program_settings().unwrap();
let transparency = material.material().transparency().unwrap();
let is_translucent = transparency.is_translucent;
let shaders = material.material().shaders().unwrap();
let post_fragment = material.post_fragment().unwrap().cloned();
let data = material.material().data();
let needs_data = data.as_ref().map(|d| d.is_some()).unwrap_or(false);
self.program_for(
&gl,
"register program for virtual material",
program_settings,
is_translucent,
shaders,
post_fragment,
needs_data,
&light_info,
camera_type,
&lighting_textures,
screen_renderbuffer,
);
}
self.materials_to_register = materials_to_register;
let delta = clock.game_time_delta().as_seconds();
let elapsed = clock.game_time().as_seconds();
let time: Vec2 = convert(vec2(elapsed, delta));
for (_, program) in self.arena.iter_mut(){
#[cfg(glsl_debug)]
program.update(lighting_textures, gl);
if lighting_textures.has_changed && program.needs_light_info {
program.set_lighting_textures(lighting_textures)
}
if let Some(time_location) = program.time_location {
program.program
.set_uniform(&glin::program::uniform_location(time_location, &time))
.unwrap();
}
}
#[cfg(feature="gui")]
self.active_programs.set(self.arena.len());
}
pub(super) fn program_info_mut(&mut self, programref: ProgramRef) -> Option<&mut Program>{
self.arena.get_mut(*programref)
}
pub(super) fn reverse_index(&self) -> &hashbrown::HashMap<ProgramRef, ProgramId>{
&self.reverse_index
}
}
#[system_thread_local(name = "program cache updater")]
#[needs("MaterialCache<MaterialRef>", "ShadowMaterialCache", "LightingTextures")]
#[writes(ProgramCache)]
#[reads(gl::Renderer, LightingTextures, Clock, LightInfo, ScreenRenderBuffer, dyn CameraExt + Send)]
pub fn program_cache_updater(_: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let mut program_cache = resources.get_mut::<ProgramCache>().unwrap();
let glin = resources.get::<gl::Renderer<'static>>().unwrap();
let lighting_textures = resources.get::<LightingTextures>().unwrap();
let clock = resources.get::<Clock>().unwrap();
let light_info = resources.get::<LightInfo>().unwrap();
let camera = resources.as_trait::<dyn CameraExt + Send>().unwrap();
let camera_type = CameraType::from_camera(&*camera);
let screen_renderbuffer = resources.get::<ScreenRenderBuffer>();
program_cache.update(
&glin,
&light_info,
&lighting_textures,
camera_type,
screen_renderbuffer.as_ref(),
&clock,
);
}
impl Index<ProgramRef> for ProgramCache{
type Output = glin::Program;
fn index(&self, programref: ProgramRef) -> &glin::Program{
&self.arena[*programref].program
}
}
impl IndexMut<ProgramRef> for ProgramCache{
fn index_mut(&mut self, programref: ProgramRef) -> &mut glin::Program{
&mut self.arena[*programref].program
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ShaderTy{
Glenum(GLenum),
PostFragment,
}
impl Default for ShaderTy{
fn default() -> ShaderTy{
ShaderTy::Glenum(gl::FRAGMENT_SHADER)
}
}
#[derive(Clone, Debug, Default)]
struct ShaderInfo{
shader_ty: ShaderTy,
needs_camera_info: Option<glsl::syntax::TypeQualifier>,
needs_scene_camera_info: Option<glsl::syntax::TypeQualifier>,
needs_model_vertex: Option<glsl::syntax::TypeQualifier>,
needs_world_vertex: Option<glsl::syntax::TypeQualifier>,
needs_view_vertex: Option<glsl::syntax::TypeQualifier>,
needs_attributes: Option<glsl::syntax::TypeQualifier>,
needs_clip_info: Option<glsl::syntax::TypeQualifier>,
needs_time_info: Option<glsl::syntax::TypeQualifier>,
needs_light_info: Option<glsl::syntax::TypeQualifier>,
needs_material_data: Option<glsl::syntax::TypeQualifier>,
needs_fragment: bool,
shader_call: String,
retrieve_material: String,
material_declaration: String,
}
fn is_in(tyq: &Option<glsl::syntax::TypeQualifier>) -> bool {
if let Some(tyq) = tyq {
(&tyq.qualifiers).into_iter().find(|tyq| {
if let glsl::syntax::TypeQualifierSpec::Storage(storage) = tyq {
*storage == glsl::syntax::StorageQualifier::In
|| *storage == glsl::syntax::StorageQualifier::InOut
}else{
false
}
}).is_some()
}else{
false
}
}
fn is_out(tyq: &Option<glsl::syntax::TypeQualifier>) -> bool {
if let Some(tyq) = tyq {
(&tyq.qualifiers).into_iter().find(|tyq| {
if let glsl::syntax::TypeQualifierSpec::Storage(storage) = tyq {
*storage == glsl::syntax::StorageQualifier::Out
|| *storage == glsl::syntax::StorageQualifier::InOut
}else{
false
}
}).is_some()
}else{
false
}
}
impl ShaderInfo{
fn defines(&self) -> Vec<(String, String)>{
let define_prefix = match self.shader_ty{
ShaderTy::Glenum(gl::VERTEX_SHADER) => "VERTEX_".to_owned(),
ShaderTy::Glenum(gl::FRAGMENT_SHADER) => "FRAGMENT_".to_owned(),
ShaderTy::PostFragment => "POST_FRAGMENT_".to_owned(),
ShaderTy::Glenum(_) => unimplemented!("Shader type not supported yet"),
};
vec![
(define_prefix.clone() + "NEEDS_CAMERA_INFO_IN", (is_in(&self.needs_camera_info) as u8).to_string()),
(define_prefix.clone() + "NEEDS_SCENE_CAMERA_INFO_IN", (is_in(&self.needs_camera_info) as u8).to_string()),
(define_prefix.clone() + "NEEDS_CLIP_INFO_IN", (is_in(&self.needs_clip_info) as u8).to_string()),
(define_prefix.clone() + "NEEDS_MODEL_VERTEX_IN", (is_in(&self.needs_model_vertex) as u8).to_string()),
(define_prefix.clone() + "NEEDS_WORLD_VERTEX_IN", (is_in(&self.needs_world_vertex) as u8).to_string()),
(define_prefix.clone() + "NEEDS_VIEW_VERTEX_IN", (is_in(&self.needs_view_vertex) as u8).to_string()),
(define_prefix.clone() + "NEEDS_TIME_INFO_IN", (is_in(&self.needs_time_info) as u8).to_string()),
(define_prefix.clone() + "NEEDS_LIGHT_INFO_IN", (is_in(&self.needs_light_info) as u8).to_string()),
(define_prefix.clone() + "NEEDS_CAMERA_INFO_OUT", (is_out(&self.needs_camera_info) as u8).to_string()),
(define_prefix.clone() + "NEEDS_SCENE_CAMERA_INFO_OUT", (is_out(&self.needs_camera_info) as u8).to_string()),
(define_prefix.clone() + "NEEDS_CLIP_INFO_OUT", (is_out(&self.needs_clip_info) as u8).to_string()),
(define_prefix.clone() + "NEEDS_MODEL_VERTEX_OUT", (is_out(&self.needs_model_vertex) as u8).to_string()),
(define_prefix.clone() + "NEEDS_WORLD_VERTEX_OUT", (is_out(&self.needs_world_vertex) as u8).to_string()),
(define_prefix.clone() + "NEEDS_VIEW_VERTEX_OUT", (is_out(&self.needs_view_vertex) as u8).to_string()),
(define_prefix.clone() + "NEEDS_TIME_INFO_OUT", (is_out(&self.needs_time_info) as u8).to_string()),
(define_prefix.clone() + "NEEDS_LIGHT_INFO_OUT", (is_out(&self.needs_light_info) as u8).to_string()),
]
}
}
lazy_static::lazy_static! {
static ref RE_VERTEX_SHADER: Regex = Regex::new(r"(?m)^\s*(void\s*vertex_shader\s*\([^{]*\))\s*").unwrap();
static ref RE_FRAGMENT_SHADER: Regex = Regex::new(r"(?m)^\s*(void\s*fragment_shader\s*\([^{]*\))\s*").unwrap();
static ref RE_POST_FRAGMENT_SHADER: Regex = Regex::new(r"(?m)^\s*(void\s*post_fragment\s*\([^{]*\))\s*").unwrap();
}
fn parse_shader(shader_name: &str, source: &str, shader_ty: ShaderTy) -> Result<ShaderInfo>{
let mut shader_info = ShaderInfo::default();
shader_info.shader_ty = shader_ty;
let shader_fn_name = match shader_ty{
ShaderTy::Glenum(gl::VERTEX_SHADER) => "vertex_shader",
ShaderTy::Glenum(gl::FRAGMENT_SHADER) => "fragment_shader",
ShaderTy::PostFragment => "post_fragment",
ShaderTy::Glenum(_) => return Err(Error::new("Unrecognized shader type"))
};
let shader_fn = match shader_ty{
ShaderTy::Glenum(gl::VERTEX_SHADER) => RE_VERTEX_SHADER.captures(source),
ShaderTy::Glenum(gl::FRAGMENT_SHADER) => RE_FRAGMENT_SHADER.captures(source),
ShaderTy::PostFragment => RE_POST_FRAGMENT_SHADER.captures(source),
ShaderTy::Glenum(_) => unreachable!()
};
let shader_fn = shader_fn.unwrap().get(1)
.expect(&format!("Can't find shader function {} in {}", shader_fn_name, shader_name));
let shader_fn = shader_fn.as_str().to_owned() + "{}";
let shader_fn = match FunctionDefinition::parse(shader_fn){
Ok(parsed) => parsed,
Err(err) =>
return Err(Error::new(&format!("Error parsing shader: {}", err)))
};
shader_info.shader_call = shader_fn_name.to_owned() + "(";
let mut needs_coma = false;
for param in shader_fn.prototype.parameters {
match param {
FunctionParameterDeclaration::Named(tyq, FunctionParameterDeclarator{ty, ..}) |
FunctionParameterDeclaration::Unnamed(tyq, ty) => {
assert!(ty.array_specifier.is_none());
if needs_coma {
shader_info.shader_call += ", "
}
needs_coma = true;
match ty.ty {
TypeSpecifierNonArray::TypeName(ty) => match ty.as_str(){
"Camera" => {
shader_info.shader_call += "camera";
shader_info.needs_camera_info = tyq;
},
"SceneCamera" => {
shader_info.shader_call += "scene_camera";
shader_info.needs_camera_info = tyq;
},
"ModelVertex" => {
shader_info.shader_call += "model_vertex";
shader_info.needs_model_vertex = tyq;
},
"WorldVertex" => {
shader_info.shader_call += "world_vertex";
shader_info.needs_world_vertex = tyq;
},
"ViewVertex" => {
shader_info.shader_call += "view_vertex";
shader_info.needs_view_vertex = tyq;
},
"Attributes" => {
shader_info.shader_call += "attributes";
shader_info.needs_attributes = tyq;
},
"Fragment" if shader_ty == ShaderTy::Glenum(gl::FRAGMENT_SHADER)
|| shader_ty == ShaderTy::PostFragment =>
{
shader_info.shader_call += "fragment";
shader_info.needs_fragment = true;
},
"Clip" => {
shader_info.shader_call += "clip_info";
shader_info.needs_clip_info = tyq;
},
"Time" => {
shader_info.shader_call += "time_info";
shader_info.needs_time_info = tyq;
},
"LightInfo" => {
shader_info.shader_call += "light_info";
shader_info.needs_light_info = tyq;
},
"Material" => {
shader_info.shader_call += "material";
shader_info.needs_material_data = tyq;
shader_info.material_declaration = "
#define NUM_MATERIALS 100
#if USE_MATERIAL_UBO
layout (std140) uniform material_data {
Material material;
};
#elif USE_MATERIAL_SSBO
layout (std140) uniform material_data {
Material materials[NUM_MATERIALS];
};
#endif
".to_owned();
shader_info.retrieve_material = "
#if USE_MATERIAL_SSBO
Material material = materials[material_offset];
#endif
".to_owned()
}
ty => panic!("Trying to compile vertex shader with wrong type {}", ty),
}
ty => panic!("Trying to compile vertex shader with wrong type {:?}", ty)
}
}
}
}
shader_info.shader_call += ");";
Ok(shader_info)
}
#[cfg(any(image_enabled, dds))]
mod textures{
use glin;
use rin_graphics as graphics;
use rin_gl as gl;
use glin::RenderSurface;
#[cfg(image_enabled)]
gl::image_cache!(get_standard_brdf_32bit, IMAGE32, {
let brdf_lut = include_bytes!("textures/brdfLUT.exr");
graphics::image::load_from_memory(brdf_lut)
.log_err("Error decoding image")
.unwrap()
});
#[cfg(image_enabled)]
gl::image_cache!(get_standard_brdf_16bit, IMAGE16, {
let brdf_lut = include_bytes!("textures/brdfLUT16.png");
graphics::image::load_from_memory(brdf_lut)
.log_err("Error decoding image")
.unwrap()
});
#[cfg(image_enabled)]
#[cfg(image_ty = "image")]
gl::image_cache!(get_standard_brdf_hdr, IMAGE_HDR, {
let brdf_lut = include_bytes!("textures/brdfLUT.hdr");
graphics::Hdr::from_bytes(brdf_lut)
.log_err("Error decoding image")
.unwrap()
});
#[cfg(image_enabled)]
gl::image_cache!(get_cloth_ashikhmin_image_32bit, CLOTH_ASHIKHMIN_IMAGE32, {
let brdf_lut = include_bytes!("textures/cloth_ashikhmin_brdfLUT.exr");
graphics::image::load_from_memory(brdf_lut)
.log_err("Error decoding image")
.unwrap()
});
#[cfg(image_enabled)]
gl::image_cache!(get_cloth_ashikhmin_image_16bit, CLOTH_ASHIKHMIN_IMAGE16, {
let brdf_lut = include_bytes!("textures/cloth_ashikhmin_brdfLUT16.png");
graphics::image::load_from_memory(brdf_lut)
.log_err("Error decoding image")
.unwrap()
});
#[cfg(image_enabled)]
gl::image_cache!(get_cloth_charlie_image_32bit, CLOTH_CHARLIE_IMAGE32, {
let brdf_lut = include_bytes!("textures/cloth_charlie_brdfLUT.exr");
graphics::image::load_from_memory(brdf_lut)
.log_err("Error decoding image")
.unwrap()
});
#[cfg(image_enabled)]
gl::image_cache!(get_cloth_charlie_image_16bit, CLOTH_CHARLIE_IMAGE16, {
let brdf_lut = include_bytes!("textures/cloth_charlie_brdfLUT16.png");
graphics::image::load_from_memory(brdf_lut)
.log_err("Error decoding image")
.unwrap()
});
#[cfg(dds)]
gl::image_cache!(get_ltc1, LTC1_DDS, {
let brdf_lut = include_bytes!("textures/ltc_1.dds");
graphics::Dds::from_bytes(brdf_lut)
.log_err("Error decoding image")
.unwrap()
});
#[cfg(dds)]
gl::image_cache!(get_ltc2, LTC2_DDS, {
let brdf_lut = include_bytes!("textures/ltc_2.dds");
graphics::Dds::from_bytes(brdf_lut)
.log_err("Error decoding image")
.unwrap()
});
#[cfg(image_enabled)]
pub fn brdf_texture<'r, R: RenderSurface>(gl: &'r gl::Renderer<R>) -> &'r glin::Texture{
if graphics::image::supports(graphics::image::Format::Exr){
get_standard_brdf_32bit(gl)
}else{
#[cfg(any(feature="gles", feature="webgl"))]
{
get_standard_brdf_hdr(gl)
}
#[cfg(not(any(feature="gles", feature="webgl")))]
{
get_standard_brdf_16bit(gl)
}
}
}
#[cfg(dds)]
pub fn ltc1_texture<'r, R: RenderSurface>(gl: &'r gl::Renderer<R>) -> &'r glin::Texture{
get_ltc1(gl)
}
#[cfg(dds)]
pub fn ltc2_texture<'r, R: RenderSurface>(gl: &'r gl::Renderer<R>) -> &'r glin::Texture{
get_ltc2(gl)
}
#[cfg(image_enabled)]
pub fn cloth_ashikhmin_brdf_texture<'r, R: RenderSurface>(gl: &'r gl::Renderer<R>) -> &'r glin::Texture{
if graphics::image::supports(graphics::image::Format::Exr){
get_cloth_ashikhmin_image_32bit(gl)
}else{
get_cloth_ashikhmin_image_16bit(gl)
}
}
#[cfg(image_enabled)]
pub fn _cloth_charlie_brdf_texture<'r, R: RenderSurface>(gl: &'r gl::Renderer<R>) -> &'r glin::Texture{
if graphics::image::supports(graphics::image::Format::Exr){
get_cloth_charlie_image_32bit(gl)
}else{
get_cloth_charlie_image_16bit(gl)
}
}
}