use na::Mat4;
use super::{shadow as gpu_shadow, TexturesPool};
use crate::light::shadow;
use rinecs::{
EntitiesThreadLocal, CreationContext, CreationProxy, ResourcesThreadLocal,
Read, ReadOption, Write, Entity,
};
use crate::transformation::{
Transformation, DynamicTransformation
};
use crate::light::*;
use crate::components::{Name, Ty};
use rin::{
graphics::{NodeRef, image},
gl,
util::{Error, Result}
};
use super::resources;
use crate::material::texture::CubemapRef;
use super::TextureBindingPoints;
use std::path::Path;
use std::mem;
use std::ops::{Deref, DerefMut};
impl Into<gl::shadow_mapping::Settings> for shadow::Parameters{
fn into(self) -> gl::shadow_mapping::Settings{
gl::shadow_mapping::Settings{
size: self.map_size,
near_clip: self.near_clip,
far_clip: self.far_clip,
frustum_size: self.frustum_size,
bias: self.bias,
}
}
}
impl shadow::Resolution{
fn into_gl_format(self) -> gl::fbo::DepthFormat{
match self{
shadow::Resolution::_16 => gl::fbo::DepthFormat::_16,
shadow::Resolution::_24 => gl::fbo::DepthFormat::_24,
shadow::Resolution::_32 => gl::fbo::DepthFormat::_32,
}
}
}
#[derive(OneToNComponent, Debug)]
#[debug_as_string]
pub struct LightMatricesUBO{
pub ubo: gl::Buffer<LightAsCameraMatrices>,
}
impl Deref for LightMatricesUBO{
type Target = gl::Buffer<LightAsCameraMatrices>;
fn deref(&self) -> &gl::Buffer<LightAsCameraMatrices>{
&self.ubo
}
}
impl DerefMut for LightMatricesUBO{
fn deref_mut(&mut self) -> &mut gl::Buffer<LightAsCameraMatrices>{
&mut self.ubo
}
}
#[derive(Clone,Copy,Debug,Component, Serialize, Deserialize)]
pub struct ImageBasedLight{
diffuse: CubemapRef,
specular: CubemapRef,
pub strength: f32,
}
impl ImageBasedLight{
pub fn specular(&self) -> CubemapRef{
self.specular
}
pub fn diffuse(&self) -> CubemapRef{
self.diffuse
}
}
pub struct ImageBasedLightBuilder<'a, C: CreationContext + 'a>{
world: &'a mut C,
name: &'a str,
}
impl<'a,C: CreationContext> ImageBasedLightBuilder<'a, C>{
pub(crate) fn new(world: &'a mut C, name: &'a str) -> ImageBasedLightBuilder<'a, C>{
ImageBasedLightBuilder{
world,
name,
}
}
#[cfg(feature="dds")]
pub fn from_dds<P: AsRef<Path>>(&mut self, diffuse: P, specular: P) -> Result<ImageBasedLight>{
let gl = self.resources.resource_thread_local::<gl::Renderer<'static>>().unwrap();
let diffuse_img = Dds::load(diffuse)
.map_err(|e| Error::with_cause("Error loading diffuse map", e))?;
let diffuse = gl.new_cubemap().from_cubemap_image(&diffuse_img)
.map_err(|e| Error::with_cause("Error loading diffuse map texture", e))?;
let specular_img = Dds::load(specular)
.map_err(|e| Error::with_cause("Error loading specular map", e))?;
let specular = self.gl.new_cubemap().from_cubemap_image(&specular_img)
.map_err(|e| Error::with_cause("Error loading specular map texture", e))?;
Ok(self.from_cubemaps(diffuse, specular))
}
#[cfg(feature = "image")]
fn load_image<P: AsRef<Path>>(&self, image: P, gl: &gl::Renderer) -> Result<glin::Texture>{
if let Some("hdr") = image.as_ref().extension().and_then(|s| s.to_str()){
let image = libhdr::Hdr::load(image.as_ref())
.map_err(|e| Error::with_cause("Error loading image", e))?;
gl.new_texture().from_image(&image, gl::TEXTURE_2D)
.map_err(|e| Error::with_cause("Error loading texture", e))
}else{
let image = image::load(image)
.map_err(|e| Error::with_cause("Error loading image", e))?;
gl.new_texture().from_image(&image, gl::TEXTURE_2D)
.map_err(|e| Error::with_cause("Error loading texture", e))
}
}
#[cfg(feature = "freeimage")]
fn load_image<P: AsRef<Path>>(&self, image: P, gl: &gl::Renderer) -> Result<glin::Texture>{
let image = image::load(image)
.and_then(|img| img.flip_vertical().map_err(|e| Error::with_cause("Error loading image", e)))?;
gl.new_texture().from_image(&image, gl::TEXTURE_2D)
.map_err(|e| Error::with_cause("Error loading texture", e))
}
pub fn from_equirectangular<P: AsRef<Path>>(&mut self, diffuse_path: P, specular_path: &[P]) -> Result<Entity>{
let diffuse;
let specular;
{
let gl = self.world.resource::<gl::Renderer<'static>>().unwrap();
let diffuse_img = self.load_image(diffuse_path, &gl)?;
diffuse = gl::image_based_light
::equirectangular_to_cubemap(&diffuse_img, diffuse_img.height() as u32, glin::fbo::ColorFormat::RGBA32F, &gl);
let mut specular_size = 0;
let specular_levels = specular_path.iter().try_fold::<_, _, Result<_>>(vec![], |mut specular_levels, path|{
let specular_level = self.load_image(path, &gl)?;
if specular_size == 0{
specular_size = specular_level.height() as u32;
}
specular_levels.push(specular_level);
Ok(specular_levels)
})?;
specular = gl::image_based_light
::equirectangular_levels_to_cubemap(specular_levels, specular_size, glin::fbo::ColorFormat::RGBA32F, &gl);
}
Ok(self.from_cubemaps(diffuse, specular))
}
pub fn from_cubemaps(&mut self, mut diffuse: glin::CubeMap, mut specular: glin::CubeMap) -> Entity{
let ibl = {
let mut textures_pool = self.world.resource_mut::<TexturesPool>().unwrap();
diffuse.set_min_mag_filters(glin::gl::LINEAR, glin::gl::LINEAR);
specular.set_min_mag_filters(glin::gl::LINEAR_MIPMAP_LINEAR, glin::gl::LINEAR);
let diffuse = textures_pool.insert_cubemap(diffuse);
let specular = textures_pool.insert_cubemap(specular);
ImageBasedLight{
diffuse,
specular,
strength: 1.,
}
};
self.world.new_entity()
.add(Name(self.name.to_owned()))
.add(Light)
.add(Ty::Light)
.add(ibl)
.build()
}
}
pub fn create_missing_light_matrices(mut entities: CreationProxy, resources: ResourcesThreadLocal){
let gl = resources.get::<gl::Renderer<'static>>().unwrap();
let mut shadow_maps = resources.get_mut::<gpu_shadow::ShadowMapPool>().unwrap();
let to_add = entities.iter_for::<(
Entity,
Read<Transformation>,
ReadOption<DynamicTransformation>,
Read<DirectionalLight>,
ReadOption<shadow::Map>,
ReadOption<shadow::StaticMap>,
ReadOption<DirectionalLightMatrices>,
ReadOption<LightMatricesUBO>)>()
.filter_map(|(e,trafo,dynamic,_,s,ss,m,u)| if (s.is_some() || ss.is_some()) && (m.is_none() || u.is_none()){
let shadow_matrices = s
.map(|s| DirectionalLight::matrices_for(trafo, s));
let static_shadow_matrices = ss
.map(|ss| DirectionalLight::matrices_for(trafo, ss));
let gpu_map = s.and_then(|s|{
let settings = (*s.parameters()).into();
shadow_maps.new_shadow_map(settings, s.parameters().resolution.into_gl_format()).ok()
});
let gpu_static_map = ss.and_then(|s|{
let settings = (*s.parameters()).into();
shadow_maps.new_shadow_map(settings, s.parameters().resolution.into_gl_format()).ok()
});
Some((e, dynamic.is_some(), shadow_matrices, static_shadow_matrices, gpu_map, gpu_static_map))
}else{
None
}).collect::<Vec<_>>();
for (entity, dynamic, s_matrices, ss_matrices, shadow_map, static_map) in to_add{
let shadow_matrices = s_matrices.into_iter().chain(ss_matrices).collect::<Vec<_>>();
let usage = if dynamic { gl::DYNAMIC_DRAW } else { gl::STATIC_DRAW };
let matrices_ubos = shadow_matrices.iter().map(|matrices| LightMatricesUBO{
ubo: gl.new_buffer()
.from_data_target(&[*matrices.as_camera_matrices()], usage, gl::UNIFORM_BUFFER)
.unwrap()
}).collect::<Vec<_>>();
trace!("Adding {} matrices/ubos to a directional lights", matrices_ubos.len());
entities.add_slice_component_to(&entity, shadow_matrices);
entities.add_slice_component_to_thread_local(&entity, matrices_ubos);
if let Some(shadow_map) = shadow_map {
gl.with_fbo(shadow_maps[shadow_map].fbo()).clear_depth();
entities.add_component_to_thread_local(&entity, gpu_shadow::Map(shadow_map));
}
if let Some(static_map) = static_map {
gl.with_fbo(shadow_maps[static_map].fbo()).clear_depth();
entities.add_component_to_thread_local(&entity, gpu_shadow::StaticMap(static_map));
}
}
let to_add = entities.iter_for::<(
Entity,
Read<Transformation>,
ReadOption<DynamicTransformation>,
Read<SpotLight>,
ReadOption<shadow::Map>,
ReadOption<shadow::StaticMap>,
ReadOption<SpotLightMatrices>,
ReadOption<LightMatricesUBO>)>()
.filter_map(|(e,trafo,dynamic,spot,s,ss,m,u)| if (s.is_some() || ss.is_some()) && (m.is_none() || u.is_none()){
let shadow_matrices = s
.map(|s| spot.matrices_for(trafo, s));
let static_shadow_matrices = ss
.map(|ss| spot.matrices_for(trafo, ss));
let gpu_map = s.and_then(|s|{
let settings = (*s.parameters()).into();
shadow_maps.new_shadow_map(settings, s.parameters().resolution.into_gl_format()).ok()
});
let gpu_static_map = ss.and_then(|s|{
let settings = (*s.parameters()).into();
shadow_maps.new_shadow_map(settings, s.parameters().resolution.into_gl_format()).ok()
});
Some((e, dynamic.is_some(), shadow_matrices, static_shadow_matrices, gpu_map, gpu_static_map))
}else{
None
}).collect::<Vec<_>>();
for (entity, dynamic, s_matrices, ss_matrices, shadow_map, static_map) in to_add{
let shadow_matrices = s_matrices.into_iter().chain(ss_matrices).collect::<Vec<_>>();
let usage = if dynamic { gl::DYNAMIC_DRAW } else { gl::STATIC_DRAW };
let matrices_ubos = shadow_matrices.iter().map(|matrices| LightMatricesUBO{
ubo: gl.new_buffer()
.from_data_target(&[*matrices.as_camera_matrices()], usage, gl::UNIFORM_BUFFER)
.unwrap()
}).collect::<Vec<_>>();
trace!("Adding {} matrices/ubos to a spot lights", matrices_ubos.len());
entities.add_slice_component_to(&entity, shadow_matrices);
entities.add_slice_component_to_thread_local(&entity, matrices_ubos);
if let Some(shadow_map) = shadow_map {
gl.with_fbo(shadow_maps[shadow_map].fbo()).clear_depth();
entities.add_component_to_thread_local(&entity, gpu_shadow::Map(shadow_map));
}
if let Some(static_map) = static_map {
gl.with_fbo(shadow_maps[static_map].fbo()).clear_depth();
entities.add_component_to_thread_local(&entity, gpu_shadow::StaticMap(static_map));
}
}
}
pub fn update_lights_data(entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let shadow_maps = resources.get::<gpu_shadow::ShadowMapPool>().unwrap();
let lights_changed = entities.iter_for::<(Read<Transformation>, Read<Light>)>()
.any(|(t,_)| t.has_changed());
if lights_changed {
trace!("Recreating lights matrices");
let directional_with_shadow = entities.iter_for::<(
Read<Transformation>,
ReadOption<DynamicTransformation>,
Read<DirectionalLight>,
ReadOption<shadow::Map>,
ReadOption<shadow::StaticMap>,
Write<DirectionalLightMatrices>,
Write<LightMatricesUBO>)>();
for (trafo, dynamic, _, s, ss, matrices, matrices_ubo) in directional_with_shadow{
for ((shadow_map, matrices), matrices_ubo) in s.into_iter().map(|s| s.parameters())
.chain(ss.into_iter().map(|ss| ss.parameters()))
.zip(matrices)
.zip(matrices_ubo)
{
*matrices = DirectionalLight::matrices_for(trafo, shadow_map);
if dynamic.is_some() {
matrices_ubo.update(&[*matrices.as_camera_matrices()]);
}else{
matrices_ubo.load(&[*matrices.as_camera_matrices()], gl::STATIC_DRAW);
}
}
}
let directional_with_shadow = entities.iter_for::<(
Read<Transformation>,
ReadOption<DynamicTransformation>,
Read<SpotLight>,
ReadOption<shadow::Map>,
ReadOption<shadow::StaticMap>,
Write<SpotLightMatrices>,
Write<LightMatricesUBO>)>();
for (trafo, dynamic, spot, s, ss, matrices, matrices_ubo) in directional_with_shadow{
for ((shadow_map, matrices), matrices_ubo) in s.into_iter().map(|s| s.parameters())
.chain(ss.into_iter().map(|ss| ss.parameters()))
.zip(matrices)
.zip(matrices_ubo)
{
*matrices = spot.matrices_for(trafo, shadow_map);
if dynamic.is_some() {
matrices_ubo.update(&[*matrices.as_camera_matrices()]);
}else{
matrices_ubo.load(&[*matrices.as_camera_matrices()], gl::STATIC_DRAW);
}
}
}
}
let mut lighting_ubo = resources.get_mut::<resources::LightingUBO>().unwrap();
let light_info = resources.get::<LightInfo>().unwrap();
let mut shadow_uniforms = vec![];
if light_info.has_changed() || lights_changed || lighting_ubo.dirty {
trace!("Recreating lights ubo");
let mut light_data = resources.get_mut::<resources::LightData>().unwrap();
light_data.clear();
let directional_lights = entities.iter_for::<(
Read<Transformation>,
Read<DirectionalLight>,
ReadOption<gpu_shadow::Map>,
ReadOption<gpu_shadow::StaticMap>,
ReadOption<shadow::Type>,
ReadOption<DirectionalLightMatrices>)>();
let mut shadow_map_idx = 0i32;
for (trafo, light, shadow_map, static_shadow_map, shadow_type, matrices) in directional_lights {
light_data.push(vec4!(trafo.z_axis(), 0.));
light_data.push(*light);
light_data.pad::<[f32;3]>();
let mut num_shadow_indices = 0;
if shadow_map.is_some() && shadow_type.is_some() {
light_data.push(shadow_map_idx);
let shadow_map = &shadow_maps[**shadow_map.unwrap()];
shadow_uniforms.push(glin::program::uniform(
format!("shadow_map[{}]", shadow_map_idx),
&(shadow_map.debug_draw_sampler(),
super::TextureBindingPoints::Shadow as u32 + shadow_map_idx as u32 * 2)));
shadow_uniforms.push(glin::program::uniform(
format!("shadow_map_pcf[{}]", shadow_map_idx),
&(shadow_map.shadow_sampler(),
super::TextureBindingPoints::ShadowPcf as u32 + shadow_map_idx as u32 * 2)));
num_shadow_indices += 1;
shadow_map_idx += 1;
}
if static_shadow_map.is_some() && shadow_type.is_some() {
light_data.push(shadow_map_idx);
let shadow_map = &shadow_maps[**static_shadow_map.unwrap()];
shadow_uniforms.push(glin::program::uniform(
format!("shadow_map[{}]", shadow_map_idx),
&(shadow_map.debug_draw_sampler(),
super::TextureBindingPoints::Shadow as u32 + shadow_map_idx as u32 * 2)));
shadow_uniforms.push(glin::program::uniform(
format!("shadow_map_pcf[{}]", shadow_map_idx),
&(shadow_map.shadow_sampler(),
super::TextureBindingPoints::ShadowPcf as u32 + shadow_map_idx as u32 * 2)));
num_shadow_indices += 1;
shadow_map_idx += 1;
}
for _ in num_shadow_indices..4{
light_data.push(-1i32);
}
if let Some(matrices) = matrices.as_ref(){
light_data.push(*matrices[0].view());
}else{
light_data.pad::<Mat4>();
}
}
let spot_lights = entities.iter_for::<(
Read<Transformation>,
Read<SpotLight>,
ReadOption<Attenuation>,
ReadOption<gpu_shadow::Map>,
ReadOption<gpu_shadow::StaticMap>,
ReadOption<shadow::Type>,
ReadOption<SpotLightMatrices>)>();
for (trafo, light, attenuation, shadow_map, static_shadow_map, shadow_type, matrices) in spot_lights {
light_data.push(trafo.global_position());
light_data.push(*light);
light_data.push(trafo.z_axis());
light_data.pad::<f32>();
let mut num_shadow_indices = 0;
if shadow_map.is_some() && shadow_type.is_some() {
light_data.push(shadow_map_idx);
let shadow_map = &shadow_maps[**shadow_map.unwrap()];
shadow_uniforms.push(glin::program::uniform(
format!("shadow_map[{}]", shadow_map_idx),
&(shadow_map.debug_draw_sampler(),
super::TextureBindingPoints::Shadow as u32 + shadow_map_idx as u32 * 2)));
shadow_uniforms.push(glin::program::uniform(
format!("shadow_map_pcf[{}]", shadow_map_idx),
&(shadow_map.shadow_sampler(),
super::TextureBindingPoints::ShadowPcf as u32 + shadow_map_idx as u32 * 2)));
shadow_map_idx += 1;
num_shadow_indices += 1;
}
if static_shadow_map.is_some() && shadow_type.is_some() {
light_data.push(shadow_map_idx);
let shadow_map = &shadow_maps[**static_shadow_map.unwrap()];
shadow_uniforms.push(glin::program::uniform(
format!("shadow_map[{}]", shadow_map_idx),
&(shadow_map.debug_draw_sampler(),
super::TextureBindingPoints::Shadow as u32 + shadow_map_idx as u32 * 2)));
shadow_uniforms.push(glin::program::uniform(
format!("shadow_map_pcf[{}]", shadow_map_idx),
&(shadow_map.shadow_sampler(),
super::TextureBindingPoints::ShadowPcf as u32 + shadow_map_idx as u32 * 2)));
num_shadow_indices += 1;
shadow_map_idx += 1;
}
for _ in num_shadow_indices..4{
light_data.push(-1i32);
}
if let Some(matrices) = matrices.as_ref(){
light_data.push(*matrices[0].view());
}else{
light_data.pad::<Mat4>();
}
}
let point_lights = entities.iter_for::<(
Read<Transformation>,
Read<PointLight>,
ReadOption<Attenuation>,
ReadOption<shadow::Map>,
ReadOption<shadow::StaticMap>,
ReadOption<shadow::Type>)>();
for (trafo, light, attenuation, shadow_map, static_shadow_map, shadow_type) in point_lights {
light_data.push(trafo.global_position());
light_data.push(*light);
if shadow_map.is_some() && shadow_type.is_some() {
light_data.push(shadow_map_idx);
shadow_map_idx += 1;
}else{
light_data.push(-1i32);
}
if static_shadow_map.is_some() && shadow_type.is_some() {
light_data.push(shadow_map_idx);
shadow_map_idx += 1;
}else{
light_data.push(-1i32);
}
light_data.push([-1i32;2]);
light_data.push(unsafe{ mem::uninitialized::<Mat4>() });
}
let dir_shadows = entities.iter_for::<(
Read<DirectionalLight>,
ReadOption<shadow::Map>,
ReadOption<shadow::StaticMap>,
ReadOption<shadow::Type>,
Read<DirectionalLightMatrices>)>()
.filter_map(|(_, shadow_map, static_shadow_map, shadow_ty, matrices)|{
if (shadow_map.is_some() || static_shadow_map.is_some()) && shadow_ty.is_some() {
Some((shadow_map, static_shadow_map, shadow_ty.unwrap(), matrices))
}else{
None
}
});
for (shadow_map, static_shadow_map, shadow_ty, matrices) in dir_shadows {
for (shadow_map, matrices) in shadow_map.map(|s| s.parameters()).into_iter()
.chain(static_shadow_map.map(|s| s.parameters()).into_iter())
.zip(matrices)
{
light_data.push(shadow_map.near_clip);
light_data.push(shadow_map.far_clip);
light_data.push(shadow_map.frustum_size);
light_data.push(shadow_map.bias);
light_data.push(*matrices.biased_projection_view());
if let shadow::Type::Poisson{soft_scatter} = shadow_ty {
light_data.push(1f32);
light_data.push(*soft_scatter);
light_data.pad::<[f32;2]>();
}
}
}
let spot_shadows = entities.iter_for::<(
Read<SpotLight>,
ReadOption<shadow::Map>,
ReadOption<shadow::StaticMap>,
ReadOption<shadow::Type>,
Read<SpotLightMatrices>)>()
.filter_map(|(_, shadow_map, static_shadow_map, shadow_ty, matrices)|{
if (shadow_map.is_some() || static_shadow_map.is_some()) && shadow_ty.is_some() {
Some((shadow_map, static_shadow_map, shadow_ty.unwrap(), matrices))
}else{
None
}
});
for (shadow_map, static_shadow_map, shadow_ty, matrices) in spot_shadows {
for (shadow_map, matrices) in shadow_map.map(|s| s.parameters()).into_iter()
.chain(static_shadow_map.map(|s| s.parameters()).into_iter())
.zip(matrices)
{
light_data.push(shadow_map.near_clip);
light_data.push(shadow_map.far_clip);
light_data.push(shadow_map.frustum_size);
light_data.push(shadow_map.bias);
light_data.push(*matrices.projection_view());
if let shadow::Type::Poisson{soft_scatter} = shadow_ty {
light_data.push(1f32);
light_data.push(*soft_scatter);
light_data.pad::<[f32;2]>();
}
}
}
if let Some(ambient_light) = entities.iter_for::<Read<AmbientLight>>().next(){
light_data.push(ambient_light.diffuse);
light_data.pad::<f32>();
light_data.push(ambient_light.specular);
light_data.pad::<f32>();
}else if !entities.iter_for::<Read<ImageBasedLight>>().next().is_some(){
light_data.push([0f32;8]);
}
if lighting_ubo.len() >= light_data.len(){
lighting_ubo.ubo.update(&light_data);
}else{
lighting_ubo.ubo.load(&light_data, gl::DYNAMIC_DRAW);
}
lighting_ubo.dirty = false;
}
let mut lighting_uniforms = resources.get_mut::<resources::LightingUniforms>().unwrap();
if light_info.has_changed() || lights_changed || lighting_uniforms.dirty {
trace!("Recreating lights uniforms");
lighting_uniforms.clear();
let ibl_uniforms = if let Some(ibl) = entities.iter_for::<Read<ImageBasedLight>>().next(){
let textures_pool = resources.get::<resources::TexturesPool>().unwrap();
let diffuse = &textures_pool[ibl.diffuse];
let specular = &textures_pool[ibl.specular];
uniforms!{
diffmap: (diffuse, TextureBindingPoints::DiffuseIbl as u32),
envmap: (specular, TextureBindingPoints::SpecularIbl as u32),
ibl_strength: ibl.strength,
}
}else{
vec![]
};
lighting_uniforms.extend(ibl_uniforms.into_iter().chain(shadow_uniforms));
lighting_uniforms.dirty = false;
}
}