use rin_math::{Mat4, Vec4, vec4, Vec3, vec3, ToVec, convert, Pnt2, Pnt3, vec2, Vec2, UnitQuat, Unit, Vector4};
use super::{shadow as gpu_shadow, TexturesPool, resources::{LightingTexture, LightingSampler}};
use crate::light::shadow;
use rinecs::{Component, EntitiesCreation, EntitiesCreationExt, EntitiesThreadLocal, Entity, HasOption, Not, OneToNComponent, Read, ReadOption, ResourcesCreation, ResourcesThreadLocalExt, ResourcesThreadLocal, Write, creation_system, system_thread_local};
use crate::transformation::Camera;
use crate::light::*;
use crate::components::{Name, Ty, Visible};
use rin_graphics::{Node, node::DynamicTransformation};
use rin_gl::{self as gl, gl::types::GLenum};
use rin_util::Result;
use rin_math::Rect;
use rin_material::texture::{TextureCreationFlags, CubemapRef};
use super::resources;
use std::path::Path;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::iter;
#[cfg(feature = "async")]
use super::resources::ResourceFuture;
#[cfg(feature = "async")]
use futures::select;
use serde_derive::{Serialize, Deserialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
#[repr(C)]
pub struct LightAsCameraData{
projection: Mat4,
projection_view: Mat4,
view: Mat4,
clip_plane: Vec4,
viewport: Rect<i32>,
camera_pos: Pnt3,
near_plane: f32,
far_plane: f32,
padding: [f32;3],
}
#[derive(OneToNComponent, Debug)]
#[debug_as_string]
pub struct LightAsCameraUBO{
pub ubo: gl::Buffer<LightAsCameraData>,
}
impl LightAsCameraUBO{
pub fn from_directional(
gl: &gl::Renderer,
light_pos: Pnt3,
light_mats: &DirectionalLightMatrices,
shadow_map: &shadow::Parameters,
gpu_shadow_map: &gpu_shadow::ShadowMapView,
usage: GLenum) -> LightAsCameraUBO
{
let data = LightAsCameraData{
projection: *light_mats.projection(),
projection_view: *light_mats.projection_view(),
view: *light_mats.view(),
clip_plane: vec4!(0.),
viewport: gpu_shadow_map.viewport(),
camera_pos: light_pos,
near_plane: shadow_map.near_clip,
far_plane: shadow_map.far_clip,
padding: [0.;3],
};
let ubo = gl.new_buffer()
.from_data_target(&[data], usage, gl::UNIFORM_BUFFER)
.unwrap();
LightAsCameraUBO{
ubo
}
}
pub fn from_spot(
gl: &gl::Renderer,
light_pos: Pnt3,
light_mats: &SpotLightMatrices,
shadow_map: &shadow::Parameters,
gpu_shadow_map: &gpu_shadow::ShadowMapView,
usage: GLenum) -> LightAsCameraUBO
{
let data = LightAsCameraData{
projection: *light_mats.projection(),
projection_view: *light_mats.projection_view(),
view: *light_mats.view(),
clip_plane: vec4!(0.),
viewport: gpu_shadow_map.viewport(),
camera_pos: light_pos,
near_plane: shadow_map.near_clip,
far_plane: shadow_map.far_clip,
padding: [0.;3],
};
let ubo = gl.new_buffer()
.from_data_target(&[data], usage, gl::UNIFORM_BUFFER)
.unwrap();
LightAsCameraUBO{
ubo
}
}
pub fn update_directional(
&mut self,
dynamic: bool,
light_pos: Pnt3,
light_mats: &DirectionalLightMatrices,
shadow_map: &shadow::Parameters,
gpu_shadow_map: &gpu_shadow::ShadowMapView)
{
let data = LightAsCameraData{
projection: *light_mats.projection(),
projection_view: *light_mats.projection_view(),
view: *light_mats.view(),
clip_plane: vec4!(0.),
viewport: gpu_shadow_map.viewport(),
camera_pos: light_pos,
near_plane: shadow_map.near_clip,
far_plane: shadow_map.far_clip,
padding: [0.;3],
};
if dynamic {
self.ubo.update(&[data]);
}else{
self.ubo.load(&[data], gl::STATIC_DRAW);
}
}
pub fn update_spot(
&mut self,
dynamic: bool,
light_pos: Pnt3,
light_mats: &SpotLightMatrices,
shadow_map: &shadow::Parameters,
gpu_shadow_map: &gpu_shadow::ShadowMapView)
{
let data = LightAsCameraData{
projection: *light_mats.projection(),
projection_view: *light_mats.projection_view(),
view: *light_mats.view(),
clip_plane: vec4!(0.),
viewport: gpu_shadow_map.viewport(),
camera_pos: light_pos,
near_plane: shadow_map.near_clip,
far_plane: shadow_map.far_clip,
padding: [0.;3],
};
if dynamic {
self.ubo.update(&[data]);
}else{
self.ubo.load(&[data], gl::STATIC_DRAW);
}
}
}
impl Deref for LightAsCameraUBO{
type Target = gl::Buffer<LightAsCameraData>;
fn deref(&self) -> &gl::Buffer<LightAsCameraData>{
&self.ubo
}
}
impl DerefMut for LightAsCameraUBO{
fn deref_mut(&mut self) -> &mut gl::Buffer<LightAsCameraData>{
&mut self.ubo
}
}
#[derive(Clone,Copy,Debug,Component, Serialize, Deserialize)]
pub struct ImageBasedLight{
diffuse: CubemapRef,
specular: CubemapRef,
has_changed: bool,
strength: f32,
orientation: Mat4,
}
impl ImageBasedLight{
pub fn specular(&self) -> CubemapRef{
self.specular
}
pub fn diffuse(&self) -> CubemapRef{
self.diffuse
}
pub fn strength(&self) -> f32 {
self.strength
}
pub fn set_strength(&mut self, strength: f32){
self.has_changed |= self.strength != strength;
self.strength = strength;
}
}
pub struct ImageBasedLightBuilder<'a, C>{
world: &'a mut C,
name: &'a str,
up: Option<Unit<Vec3>>,
}
impl<'a, 'c, C: EntitiesCreationExt<'c> + ResourcesThreadLocalExt> ImageBasedLightBuilder<'a, C>{
pub(crate) fn new(world: &'a mut C, name: &'a str) -> ImageBasedLightBuilder<'a, C>{
ImageBasedLightBuilder{
world,
name,
up: None,
}
}
pub fn up(&mut self, up: Unit<Vec3>) -> &mut ImageBasedLightBuilder<'a, C>{
self.up = Some(up);
self
}
pub fn from_cubemap_refs(&mut self, diffuse: CubemapRef, specular: CubemapRef) -> Entity{
let orientation: Mat4 = if let Some(up) = self.up {
UnitQuat::rotation_between_axis(&Vec3::y_axis(), &up)
.unwrap()
.to_rotation_matrix()
.matrix()
.to_homogeneous()
}else{
Mat4::identity()
};
let ibl = ImageBasedLight{
diffuse,
specular,
has_changed: true,
strength: 1.,
orientation,
};
self.world.new_entity()
.add(Name(self.name.to_owned()))
.add(Light)
.add(Ty::Light)
.add(Visible::new(true))
.add(ibl)
.build()
}
#[cfg(all(dds, not(feature = "web")))]
pub fn from_dds<P: AsRef<Path>>(&mut self, diffuse_path: P, specular_path: P) -> Result<Entity>{
let diffuse;
let specular;
{
let gl = self.world.resource_thread_local::<gl::Renderer<'static>>().unwrap();
let mut textures = self.world.resource_thread_local_mut::<super::TexturesPool>().unwrap();
diffuse = textures.load_cubemap(diffuse_path, TextureCreationFlags::empty(), &gl)?;
specular = textures.load_cubemap(specular_path, TextureCreationFlags::empty(), &gl)?;
}
Ok(self.from_cubemap_refs(diffuse, specular))
}
#[cfg(all(image_enabled, not(feature = "web")))]
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_thread_local::<gl::Renderer<'static>>().unwrap();
let mut textures = self.world.resource_thread_local_mut::<super::TexturesPool>().unwrap();
diffuse = textures.load_equirectangular_cubemap(
diffuse_path,
TextureCreationFlags::empty(),
glin::fbo::ColorFormat::RGBA32F,
&gl)?;
specular = textures.load_equirectangular_levels_cubemap(
specular_path,
TextureCreationFlags::empty(),
glin::fbo::ColorFormat::RGBA32F,
&gl
)?;
}
Ok(self.from_cubemap_refs(diffuse, specular))
}
#[cfg(all(dds, feature = "async"))]
pub async fn from_dds_async<P: AsRef<Path>>(&mut self, diffuse_path: P, specular_path: P) -> Result<Entity> {
let mut diffuse_future: ResourceFuture<CubemapRef>;
let mut specular_future: ResourceFuture<CubemapRef>;
{
let mut textures = self.world.resource_mut::<super::TexturesPool>().unwrap();
diffuse_future = textures.load_cubemap_async(diffuse_path, TextureCreationFlags::empty());
specular_future = textures.load_cubemap_async(specular_path, TextureCreationFlags::empty());
}
let mut diffuse: Option<CubemapRef> = None;
let mut specular: Option<CubemapRef> = None;
loop {
select!{
d = diffuse_future => diffuse = Some(d?),
s = specular_future => specular = Some(s?),
complete => break
}
}
if let (Some(diffuse), Some(specular)) = (diffuse, specular) {
Ok(self.from_cubemap_refs(diffuse, specular))
}else{
unreachable!()
}
}
#[cfg(all(image_enabled, feature = "async"))]
pub async fn from_equirectangular_async<P: AsRef<Path>>(&mut self, diffuse_path: P, specular_path: &[P]) -> Result<Entity>{
let mut diffuse_future: ResourceFuture<CubemapRef>;
let mut specular_future: ResourceFuture<CubemapRef>;
{
let mut textures = self.world.resource_mut::<super::TexturesPool>().unwrap();
diffuse_future = textures.load_equirectangular_cubemap_async(
diffuse_path,
TextureCreationFlags::empty(),
glin::fbo::ColorFormat::RGBA32F);
specular_future = textures.load_equirectangular_levels_cubemap_async(
specular_path,
TextureCreationFlags::empty(),
glin::fbo::ColorFormat::RGBA32F);
}
let mut diffuse: Option<CubemapRef> = None;
let mut specular: Option<CubemapRef> = None;
loop {
select!{
d = diffuse_future => diffuse = Some(d?),
s = specular_future => specular = Some(s?),
complete => break
}
}
if let (Some(diffuse), Some(specular)) = (diffuse, specular) {
Ok(self.from_cubemap_refs(diffuse, specular))
}else{
unreachable!()
}
}
pub fn from_cubemaps(&mut self, mut diffuse_img: glin::CubeMap, mut specular_img: glin::CubeMap) -> Entity{
let diffuse;
let specular;
{
let mut textures_pool = self.world.resource_thread_local_mut::<TexturesPool>().unwrap();
diffuse_img.set_min_mag_filters(glin::gl::LINEAR, glin::gl::LINEAR);
specular_img.set_min_mag_filters(glin::gl::LINEAR_MIPMAP_LINEAR, glin::gl::LINEAR);
diffuse = textures_pool.insert_cubemap(diffuse_img);
specular = textures_pool.insert_cubemap(specular_img);
}
self.from_cubemap_refs(diffuse, specular)
}
}
impl ImageBasedLight{
pub(crate) fn has_changed(&self) -> bool{
self.has_changed
}
pub(crate) fn reset_changed(&mut self){
self.has_changed = false
}
}
#[creation_system(name = "create light matrices")]
#[creates(
"gpu_shadow::Map",
"gpu_shadow::StaticMap",
"LightAsCameraUBO",
"DirectionalLightMatrices",
"SpotLightMatrices",
)]
#[updates("gpu_shadow::ShadowMapPool")]
#[needs(
"Node",
"shadow::Map",
"shadow::StaticMap",
"DirectionalLight",
"SpotLight",
"PointLight",
"ImageBasedLight",
"AmbientLight",
"Camera",
)]
#[gpu_stats]
pub fn create_missing_light_matrices(mut entities: EntitiesCreation, resources: ResourcesCreation){
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<Node>, Read<Visible>),
HasOption<DynamicTransformation>,
Read<DirectionalLight>,
(ReadOption<shadow::Map>, Not<gpu_shadow::Map>),
(ReadOption<shadow::StaticMap>, Not<gpu_shadow::StaticMap>),
ReadOption<DirectionalLightMatrices>,
ReadOption<LightAsCameraUBO>)>()
.filter_map(|(e, (trafo,visible), is_dynamic, _, (cascades,_), (static_cascades,_), matrices, ubo)|
if visible.is_visible()
&& (cascades.is_some() || static_cascades.is_some())
&& (matrices.is_none() || ubo.is_none())
{
let shadow_matrices = cascades.map(|cascades| cascades
.iter()
.map(|cascade| DirectionalLight::matrices_for(trafo, cascade))
.collect::<Vec<_>>());
let gpu_maps = cascades.map(|cascades| cascades
.iter()
.map(|cascade| shadow_maps
.allocate_shadow_map(cascade.parameters().clone())
.unwrap())
.collect::<Vec<_>>());
let shadow_parameters = cascades
.map(|cascades| cascades
.iter()
.map(|m| m.parameters().clone())
.collect::<Vec<_>>());
let static_shadow_matrices = static_cascades.map(|cascades| cascades
.iter()
.map(|cascade| DirectionalLight::matrices_for(trafo, cascade))
.collect::<Vec<_>>());
let static_gpu_maps = static_cascades.map(|cascades| cascades
.iter()
.map(|cascade| shadow_maps
.allocate_shadow_map(cascade.parameters().clone())
.unwrap())
.collect::<Vec<_>>());
let static_shadow_parameters = static_cascades
.map(|cascades| cascades
.iter()
.map(|m| m.parameters().clone())
.collect::<Vec<_>>());
Some((e,
is_dynamic,
trafo.global_position(),
shadow_matrices,
static_shadow_matrices,
gpu_maps,
static_gpu_maps,
shadow_parameters,
static_shadow_parameters,
))
}else{
None
}).collect::<Vec<_>>();
for (entity,
dynamic_light,
light_pos,
shadow_matrices,
static_shadow_matrices,
shadow_map_refs,
static_shadow_map_refs,
shadow_parameters,
static_shadow_parameters) in to_add
{
let dynamic_maps = shadow_matrices.iter().flat_map(|shadow_matrices| {
shadow_matrices.into_iter()
.zip(iter::repeat(gl::DYNAMIC_DRAW))
.zip(shadow_parameters.as_ref().unwrap().into_iter())
.zip(shadow_map_refs.as_ref().unwrap().into_iter().map(|s| &shadow_maps[*s]))
});
let static_maps = static_shadow_matrices.iter().flat_map(|shadow_matrices| {
shadow_matrices.into_iter()
.zip(if dynamic_light{ iter::repeat(gl::DYNAMIC_DRAW) } else { iter::repeat(gl::STATIC_DRAW) })
.zip(static_shadow_parameters.as_ref().unwrap().into_iter())
.zip(static_shadow_map_refs.as_ref().unwrap().into_iter().map(|s| &shadow_maps[*s]))
});
let matrices_ubos = dynamic_maps
.chain(static_maps)
.map(|(((matrices, usage), parameters), map_view)| LightAsCameraUBO::from_directional(
&gl,
light_pos,
matrices,
parameters,
map_view,
usage)
)
.collect::<Vec<_>>();
log::trace!("Adding {} matrices/ubos to a directional lights", matrices_ubos.len());
let all_shadow_matrices = shadow_matrices.into_iter().flat_map(|s| s)
.chain(static_shadow_matrices.into_iter().flat_map(|s| s));
entities.add_slice_component_to(&entity, all_shadow_matrices);
entities.add_slice_component_to_thread_local(&entity, matrices_ubos);
for shadow_map in shadow_map_refs.iter().flat_map(|s| s)
.chain(static_shadow_map_refs.iter().flat_map(|s| s))
{
let shadow_map_view = &shadow_maps[*shadow_map];
gl.with_fbo(shadow_maps.fbo(shadow_map_view)).with_properties(&[
gl::Property::Scissor(Some(shadow_map_view.viewport().into()))
]).clear_depth(1.);
}
if let Some(shadow_map_refs) = shadow_map_refs {
let gpu_maps = shadow_map_refs.into_iter()
.map(|map| gpu_shadow::Map{
map,
needs_update: true,
});
entities.add_slice_component_to(&entity, gpu_maps);
}
if let Some(static_shadow_map_refs) = static_shadow_map_refs{
let static_gpu_maps = static_shadow_map_refs.into_iter()
.map(|map| gpu_shadow::StaticMap(map));
entities.add_slice_component_to(&entity, static_gpu_maps);
}
}
let to_add = entities.iter_for::<(
Entity,
(Read<Node>, Read<Visible>),
HasOption<DynamicTransformation>,
Read<SpotLight>,
(ReadOption<shadow::Map>, Not<gpu_shadow::Map>),
(ReadOption<shadow::StaticMap>, Not<gpu_shadow::StaticMap>),
ReadOption<SpotLightMatrices>,
ReadOption<LightAsCameraUBO>)>()
.filter_map(|(e, (trafo,visible), is_dynamic, spot, (shadow_map,_), (static_shadow_map,_), matrices, ubo)|
if visible.is_visible() && (matrices.is_none() || ubo.is_none()) {
if shadow_map.map(|shadow_map| shadow_map.len()).unwrap_or(0) > 1 {
panic!("Spot lights can only have one shadow map");
}
if static_shadow_map.map(|shadow_map| shadow_map.len()).unwrap_or(0) > 1 {
panic!("Spot lights can only have one shadow map");
}
let shadow_matrix = shadow_map.as_ref()
.and_then(|shadow_map| shadow_map.get(0))
.map(|shadow_map| spot.matrices_for(trafo, shadow_map));
let gpu_map = shadow_map.as_ref()
.and_then(|shadow_map| shadow_map.get(0))
.map(|shadow_map| shadow_maps
.allocate_shadow_map(shadow_map.parameters().clone())
.unwrap()
);
let shadow_parameters = shadow_map.as_ref()
.and_then(|shadow_map| shadow_map.get(0))
.map(|shadow_mao| shadow_mao.parameters().clone());
let static_shadow_matrix = static_shadow_map.as_ref()
.and_then(|shadow_map| shadow_map.get(0))
.map(|shadow_map| spot.matrices_for(trafo, shadow_map));
let static_gpu_map = static_shadow_map.as_ref()
.and_then(|shadow_map| shadow_map.get(0))
.map(|shadow_map| shadow_maps
.allocate_shadow_map(shadow_map.parameters().clone())
.unwrap()
);
let static_shadow_parameters = static_shadow_map.as_ref()
.and_then(|shadow_map| shadow_map.get(0))
.map(|shadow_mao| shadow_mao.parameters().clone());
Some((e,
is_dynamic,
trafo.global_position(),
shadow_matrix,
static_shadow_matrix,
gpu_map,
static_gpu_map,
shadow_parameters,
static_shadow_parameters,
))
}else{
None
}).collect::<Vec<_>>();
for (entity,
is_dynamic,
light_pos,
shadow_matrix,
static_shadow_matrix,
shadow_map_ref,
static_shadow_map_ref,
shadow_parameters,
static_shadow_parameters) in to_add
{
let dynamic_map = shadow_matrix.map(|shadow_matrix| {
(
shadow_matrix,
gl::DYNAMIC_DRAW,
shadow_parameters.as_ref().unwrap(),
shadow_map_ref.as_ref().map(|s| &shadow_maps[*s]).unwrap()
)
});
let static_map = static_shadow_matrix.map(|shadow_matrix| {
(
shadow_matrix,
gl::STATIC_DRAW,
static_shadow_parameters.as_ref().unwrap(),
static_shadow_map_ref.as_ref().map(|s| &shadow_maps[*s]).unwrap()
)
});
let matrices_ubos = dynamic_map.into_iter()
.chain(static_map.into_iter())
.map(|(matrix, usage, parameters, map_view)| LightAsCameraUBO::from_spot(
&gl,
light_pos,
&matrix,
parameters,
&map_view,
usage)
)
.collect::<Vec<_>>();
log::trace!("Adding {} matrices/ubos to a spot lights", matrices_ubos.len());
let all_shadow_matrices = shadow_matrix.into_iter()
.chain(static_shadow_matrix.into_iter());
entities.add_slice_component_to(&entity, all_shadow_matrices);
entities.add_slice_component_to_thread_local(&entity, matrices_ubos);
for shadow_map in shadow_map_ref.iter().chain(static_shadow_map_ref.iter()) {
let shadow_map_view = &shadow_maps[*shadow_map];
gl.with_fbo(shadow_maps.fbo(shadow_map_view)).with_properties(&[
gl::Property::Scissor(Some(shadow_map_view.viewport().into()))
]).clear_depth(1.);
}
let gpu_maps = shadow_map_ref
.map(|map| gpu_shadow::Map{
map,
needs_update: true,
});
let static_gpu_maps = static_shadow_map_ref
.map(|map| gpu_shadow::StaticMap(map));
entities.add_slice_component_to(&entity, gpu_maps);
entities.add_slice_component_to(&entity, static_gpu_maps);
}
}
#[system_thread_local(name = "update lights data")]
#[needs(
"Node",
"LightInfo",
"shadow::Type",
"shadow::Map",
"shadow::StaticMap",
"shadow::Cascades",
"shadow::StaticCascades",
)]
#[updates(
"LightAsCameraUBO",
"resources::LightingTextures",
"resources::LightData",
"resources::LightingUBO",
"DirectionalLightMatrices",
"SpotLightMatrices",
"gpu_shadow::Map",
"gpu_shadow::StaticMap"
)]
#[gpu_stats]
#[reads(
Light,
Visible,
DirectionalLight,
SpotLight,
PointLight,
AreaLight,
shadow::StaticMap,
gpu_shadow::ShadowMapPool,
LightInfo,
resources::LightingTextures,
TexturesPool
)]
#[writes(ImageBasedLight, resources::LightingUBO, resources::LightData)]
pub fn update_lights_data(mut entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
for (shadow_maps, gpu_shadow_maps) in entities.iter_for_mut::<(
Read<shadow::Map>,
Write<gpu_shadow::Map>)>()
{
for (shadow_map, gpu_shadow_map) in shadow_maps.iter().zip(gpu_shadow_maps){
gpu_shadow_map.needs_update = shadow_map.needs_update();
}
}
let light_trafos_changed = entities
.changed_iter_for::<(Read<Node>, Read<Light>)>()
.next()
.is_some();
let shadow_maps_changed = entities.changed_iter_for::<Read<shadow::Map>>().next().is_some()
|| entities.changed_iter_for::<Read<shadow::StaticMap>>().next().is_some();
let lights_visibility_changed = entities
.changed_iter_for::<(Read<Visible>, Read<Light>)>()
.next()
.is_some();
let shadow_maps = resources.get::<gpu_shadow::ShadowMapPool>().unwrap();
if light_trafos_changed || lights_visibility_changed || shadow_maps_changed {
log::trace!("Recreating lights matrices");
let directional_with_shadow = entities.iter_for_mut::<(
Read<Node>,
HasOption<DynamicTransformation>,
Read<DirectionalLight>,
Read<Visible>,
(ReadOption<shadow::Map>, ReadOption<gpu_shadow::Map>),
(ReadOption<shadow::StaticMap>, ReadOption<gpu_shadow::StaticMap>),
Write<DirectionalLightMatrices>,
Write<LightAsCameraUBO>)>().filter(|(_,_,_,v,_,_,_,_)| v.is_visible());
for (trafo, is_dynamic, _, _, (s, gpu_s), (ss, gpu_ss), matrices, light_ubo) in directional_with_shadow{
let dynamic_maps = s.into_iter()
.flat_map(|s| s).map(|s| (s.parameters(), is_dynamic))
.zip(gpu_s.into_iter().flat_map(|s| s).map(|s| &shadow_maps[s.map]));
let static_maps = ss.into_iter()
.flat_map(|s| s).map(|ss| (ss.parameters(), false))
.zip(gpu_ss.into_iter().flat_map(|s| s).map(|s| &shadow_maps[s.0]));
for ((((shadow_map, is_dynamic), map_view), matrices), light_ubo) in dynamic_maps
.chain(static_maps)
.zip(matrices)
.zip(light_ubo)
{
*matrices = DirectionalLight::matrices_for(trafo, shadow_map);
light_ubo.update_directional(
is_dynamic,
trafo.global_position(),
&matrices,
shadow_map,
map_view,
);
}
}
let spot_with_shadow = entities.iter_for_mut::<(
Read<Node>,
HasOption<DynamicTransformation>,
Read<SpotLight>,
Read<Visible>,
(ReadOption<shadow::Map>, ReadOption<gpu_shadow::Map>),
(ReadOption<shadow::StaticMap>, ReadOption<gpu_shadow::StaticMap>),
Write<SpotLightMatrices>,
Write<LightAsCameraUBO>)>().filter(|(_,_,_,v,_,_,_,_)| v.is_visible());
for (trafo, is_dynamic, spot, _, (s, gpu_s), (ss, gpu_ss), matrices, light_ubo) in spot_with_shadow{
let dynamic_maps = s.into_iter()
.flat_map(|s| s).map(|s| (s.parameters(), is_dynamic))
.zip(gpu_s.unwrap().into_iter().map(|s| &shadow_maps[s.map]));
let static_maps = ss.into_iter()
.flat_map(|s| s).map(|ss| (ss.parameters(), false))
.zip(gpu_ss.unwrap().into_iter().map(|s| &shadow_maps[s.0]));
for ((((shadow_map, is_dynamic), map_view), matrices), light_ubo) in dynamic_maps
.chain(static_maps)
.zip(matrices)
.zip(light_ubo)
{
*matrices = spot.matrices_for(trafo, shadow_map);
light_ubo.update_spot(
is_dynamic,
trafo.global_position(),
&matrices,
shadow_map,
map_view,
);
}
}
}
let mut lighting_ubo = resources.get_mut::<resources::LightingUBO>().unwrap();
let light_info = resources.get::<LightInfo>().unwrap();
let mut lighting_textures = resources.get_mut::<resources::LightingTextures>().unwrap();
lighting_textures.has_changed = false;
lighting_textures.num_shadow_maps_changed = false;
let ambient_changed = entities.iter_for::<Read<AmbientLight>>().next()
.map(|a| a.has_changed())
.unwrap_or(false);
let ibl_changed = entities.iter_for::<Read<ImageBasedLight>>().next()
.map(|a| a.has_changed())
.unwrap_or(false);
let lights_changed =
entities.changed_iter_for::<Read<DirectionalLight>>().next().is_some()
|| entities.changed_iter_for::<Read<SpotLight>>().next().is_some()
|| entities.changed_iter_for::<Read<PointLight>>().next().is_some()
|| entities.changed_iter_for::<Read<AreaLight>>().next().is_some();
if light_info.has_changed()
|| light_trafos_changed
|| lights_changed
|| lights_visibility_changed
|| lighting_ubo.dirty
|| ambient_changed
|| ibl_changed
|| shadow_maps_changed
{
log::trace!("Recreating lights ubo and shadow uniforms");
let mut light_data = resources.get_mut::<resources::LightData>().unwrap();
let prev_num_shadow_maps = lighting_textures.shadow_maps.len();
light_data.clear();
lighting_textures.shadow_maps.clear();
let mut shadow_map_sampler_index = vec![];
let directional_lights = entities.iter_for::<(
Read<Node>,
Read<DirectionalLight>,
Read<Visible>,
(ReadOption<gpu_shadow::Map>, ReadOption<shadow::Cascades>),
(ReadOption<gpu_shadow::StaticMap>, ReadOption<shadow::StaticCascades>),
ReadOption<shadow::Type>,
ReadOption<DirectionalLightMatrices>)>().filter(|(_,_,v,_,_,_,_)| v.is_visible());
let mut shadow_map_idx = 0i32;
let mut shadow_sampler_idx = 0i32;
for (trafo,
light,
_,
(maps, cascades),
(static_maps, static_cascades),
_shadow_type,
matrices) in directional_lights
{
let mut directional_light_data = light_data.start_struct();
directional_light_data.push(vec4!(trafo.global_z_axis(), 0.));
directional_light_data.push(*light.color());
directional_light_data.push(light.radius());
directional_light_data.push(light.strength());
if let Some(cascades) = cascades {
let starts = cascades.starts();
let ends = cascades.ends();
let debug = if cascades.debug_enabled() { 1f32 } else { 0. };
directional_light_data.push(debug);
directional_light_data.push(starts);
directional_light_data.push(ends);
}else{
directional_light_data.push(0f32);
directional_light_data.push(vec4(-1f32, -1., -1., -1.));
directional_light_data.push(vec4(-1f32, -1., -1., -1.));
}
if let Some(cascades) = static_cascades {
let starts = cascades.starts();
let ends = cascades.ends();
let debug = if cascades.debug_enabled() { 1f32 } else { 0. };
directional_light_data.push(debug);
directional_light_data.push(starts);
directional_light_data.push(ends);
}else{
directional_light_data.push(0f32);
directional_light_data.push(vec4(-1f32, -1., -1., -1.));
directional_light_data.push(vec4(-1f32, -1., -1., -1.));
}
if let Some(maps) = maps {
for cascade in maps {
directional_light_data.push(shadow_map_idx);
let shadow_map = &shadow_maps[cascade.map];
let lighting_sampler = LightingSampler{
shadow_sampler_idx,
texture: shadow_maps.debug_draw_sampler(shadow_map).texture_id(),
sampler: shadow_maps.debug_draw_sampler(shadow_map).sampler_id(),
pcf_sampler: shadow_maps.shadow_sampler(shadow_map).sampler_id(),
target: shadow_maps.debug_draw_sampler(shadow_map).target(),
};
if let Some(sampler) = lighting_textures.shadow_maps.get(&lighting_sampler){
shadow_map_sampler_index.push(sampler.shadow_sampler_idx);
}else{
lighting_textures.shadow_maps.insert(lighting_sampler);
shadow_map_sampler_index.push(shadow_sampler_idx);
shadow_sampler_idx += 1;
}
shadow_map_idx += 1;
}
for _ in maps.len() .. 4{
directional_light_data.push(-1i32);
}
}else{
directional_light_data.push(vec4(-1i32, -1, -1, -1));
}
if let Some(static_maps) = static_maps {
for cascade in static_maps {
directional_light_data.push(shadow_map_idx);
let shadow_map = &shadow_maps[**cascade];
let lighting_sampler = LightingSampler{
shadow_sampler_idx,
texture: shadow_maps.debug_draw_sampler(shadow_map).texture_id(),
sampler: shadow_maps.debug_draw_sampler(shadow_map).sampler_id(),
pcf_sampler: shadow_maps.shadow_sampler(shadow_map).sampler_id(),
target: shadow_maps.debug_draw_sampler(shadow_map).target(),
};
if let Some(sampler) = lighting_textures.shadow_maps.get(&lighting_sampler){
shadow_map_sampler_index.push(sampler.shadow_sampler_idx);
}else{
lighting_textures.shadow_maps.insert(lighting_sampler);
shadow_map_sampler_index.push(shadow_sampler_idx);
shadow_sampler_idx += 1;
}
shadow_map_idx += 1;
}
for _ in static_maps.len() .. 4{
directional_light_data.push(-1i32);
}
}else{
directional_light_data.push(vec4(-1i32, -1, -1, -1));
}
if let Some(matrices) = matrices.as_ref(){
directional_light_data.push(*matrices[0].view());
}else{
directional_light_data.pad::<Mat4>();
}
}
let spot_lights = entities.iter_for::<(
Read<Node>,
Read<SpotLight>,
Read<Visible>,
ReadOption<Attenuation>,
ReadOption<gpu_shadow::Map>,
ReadOption<gpu_shadow::StaticMap>,
ReadOption<shadow::Type>,
ReadOption<SpotLightMatrices>)>().filter(|(_,_,v,_,_,_,_,_)| v.is_visible());
for (trafo, light, _, attenuation, shadow_map, static_shadow_map, shadow_type, matrices) in spot_lights {
let mut spot_light_data = light_data.start_struct();
spot_light_data.push(trafo.global_position());
spot_light_data.push(light.radius());
spot_light_data.push(*light.color());
spot_light_data.push(light.strength());
spot_light_data.push(light.spot_angle());
spot_light_data.push(light.spot_cos_cutoff());
spot_light_data.push(light.spot_cos_inner_cutoff());
spot_light_data.push(light.spot_blend());
spot_light_data.push(trafo.global_z_axis());
if let Some(shadow_map) = shadow_map.as_ref().and_then(|shadow_map| shadow_map.get(0)) {
spot_light_data.push(shadow_map_idx);
let shadow_map = &shadow_maps[shadow_map.map];
let lighting_sampler = LightingSampler{
shadow_sampler_idx,
texture: shadow_maps.debug_draw_sampler(shadow_map).texture_id(),
sampler: shadow_maps.debug_draw_sampler(shadow_map).sampler_id(),
pcf_sampler: shadow_maps.shadow_sampler(shadow_map).sampler_id(),
target: shadow_maps.debug_draw_sampler(shadow_map).target(),
};
if let Some(sampler) = lighting_textures.shadow_maps.get(&lighting_sampler){
shadow_map_sampler_index.push(sampler.shadow_sampler_idx);
}else{
lighting_textures.shadow_maps.insert(lighting_sampler);
shadow_map_sampler_index.push(shadow_sampler_idx);
shadow_sampler_idx += 1;
}
shadow_map_idx += 1;
}else{
spot_light_data.push(-1i32);
}
if let Some(static_shadow_map) = static_shadow_map.as_ref().and_then(|shadow_map| shadow_map.get(0)) {
spot_light_data.push(shadow_map_idx);
let shadow_map = &shadow_maps[**static_shadow_map];
let lighting_sampler = LightingSampler{
shadow_sampler_idx,
texture: shadow_maps.debug_draw_sampler(shadow_map).texture_id(),
sampler: shadow_maps.debug_draw_sampler(shadow_map).sampler_id(),
pcf_sampler: shadow_maps.shadow_sampler(shadow_map).sampler_id(),
target: shadow_maps.debug_draw_sampler(shadow_map).target(),
};
if let Some(sampler) = lighting_textures.shadow_maps.get(&lighting_sampler){
shadow_map_sampler_index.push(sampler.shadow_sampler_idx);
}else{
lighting_textures.shadow_maps.insert(lighting_sampler);
shadow_map_sampler_index.push(shadow_sampler_idx);
shadow_sampler_idx += 1;
}
shadow_map_idx += 1;
}else{
spot_light_data.push(-1);
}
if let Some(matrices) = matrices.as_ref(){
spot_light_data.push(*matrices[0].view());
}else{
spot_light_data.pad::<Mat4>();
}
}
let point_lights = entities.iter_for::<(
Read<Node>,
Read<PointLight>,
Read<Visible>,
ReadOption<Attenuation>,
ReadOption<shadow::Map>,
ReadOption<shadow::StaticMap>,
ReadOption<shadow::Type>)>().filter(|(_,_,v,_,_,_,_)| v.is_visible());
for (trafo, light, _, attenuation, shadow_map, static_shadow_map, shadow_type) in point_lights {
let mut point_light_data = light_data.start_struct();
point_light_data.push(trafo.global_position());
point_light_data.push(light.radius());
point_light_data.push(*light.color());
point_light_data.push(light.strength());
point_light_data.push(vec4(-1, -1, -1, -1));
point_light_data.push(vec4(-1, -1, -1, -1));
point_light_data.push([-1i32;2]);
point_light_data.push(unsafe{ mem::MaybeUninit::<Mat4>::uninit().assume_init() });
}
let area_lights = entities.iter_for::<(
Read<Node>,
Read<AreaLight>,
Read<Visible>)>().filter(|(_,_,v)| v.is_visible());
for (trafo, light, _) in area_lights {
let mut area_light_data = light_data.start_struct();
area_light_data.push(trafo.global_position());
area_light_data.push(light.half_width(&trafo));
area_light_data.push(light.half_height(&trafo));
area_light_data.push(*light.color() * light.strength());
}
let mut next_sampler = 0;
let dir_shadows = entities.iter_for::<(
Read<DirectionalLight>,
Read<Visible>,
ReadOption<shadow::Map>,
ReadOption<shadow::StaticMap>,
ReadOption<gpu_shadow::Map>,
ReadOption<gpu_shadow::StaticMap>,
ReadOption<shadow::Type>,
Read<DirectionalLightMatrices>)>()
.filter_map(|(_, visible, shadow_map, static_shadow_map, gpu_map, static_gpu_map, shadow_ty, matrices)|{
if (shadow_map.is_some() || static_shadow_map.is_some())
&& (gpu_map.is_some() || static_gpu_map.is_some())
&& shadow_ty.is_some()
&& visible.is_visible()
{
Some((shadow_map, static_shadow_map, gpu_map, static_gpu_map, shadow_ty.unwrap(), matrices))
}else{
None
}
});
for (cascades, static_cascades, gpu_maps, static_gpu_maps, shadow_ty, matrices) in dir_shadows {
let dynamics = cascades.into_iter().flat_map(|s| s)
.zip(gpu_maps.into_iter().flat_map(|s| s))
.map(|(cascade, gpu_map)| (cascade.parameters(), &shadow_maps[gpu_map.map]));
let statics = static_cascades.into_iter().flat_map(|s| s)
.zip(static_gpu_maps.into_iter().flat_map(|s| s))
.map(|(cascade, gpu_map)| (cascade.parameters(), &shadow_maps[**gpu_map]));
for ((shadow_map, gpu_map), matrices) in dynamics.chain(statics).zip(matrices) {
let pos: Pnt2<f32> = convert(gpu_map.viewport().pos);
let offset = pos.to_vec() / shadow_maps.atlas_size() as f32;
let offset = vec2(offset.x, offset.y);
let factor = gpu_map.viewport().width as f32 / shadow_maps.atlas_size() as f32;
let mut shadow_map_data = light_data.start_struct();
shadow_map_data.push(shadow_map.near_clip);
shadow_map_data.push(shadow_map.far_clip);
shadow_map_data.push(shadow_map.frustum_size());
shadow_map_data.push(offset);
shadow_map_data.push(factor);
shadow_map_data.push(shadow_map.bias);
shadow_map_data.push(*matrices.biased_projection_view());
shadow_map_data.push(shadow_map_sampler_index[next_sampler]);
next_sampler += 1;
if let shadow::Type::Poisson{soft_scatter} = shadow_ty {
shadow_map_data.push(1f32);
shadow_map_data.push(*soft_scatter);
}else{
shadow_map_data.pad::<[f32;2]>();
}
}
}
let spot_shadows = entities.iter_for::<(
Read<SpotLight>,
Read<Visible>,
ReadOption<shadow::Map>,
ReadOption<shadow::StaticMap>,
ReadOption<gpu_shadow::Map>,
ReadOption<gpu_shadow::StaticMap>,
ReadOption<shadow::Type>,
Read<SpotLightMatrices>)>()
.filter_map(|(_, visible, shadow_map, static_shadow_map, gpu_map, static_gpu_map, shadow_ty, matrices)|{
if (shadow_map.is_some() || static_shadow_map.is_some())
&& (gpu_map.is_some() || static_gpu_map.is_some())
&& shadow_ty.is_some()
&& visible.is_visible()
{
Some((shadow_map, static_shadow_map, gpu_map, static_gpu_map, shadow_ty.unwrap(), matrices))
}else{
None
}
});
for (cascades, static_cascades, gpu_maps, static_gpu_maps, shadow_ty, matrices) in spot_shadows {
let dynamics = cascades.into_iter().flat_map(|s| s)
.zip(gpu_maps.into_iter().flat_map(|s| s))
.map(|(cascade, gpu_map)| (cascade.parameters(), &shadow_maps[gpu_map.map]));
let statics = static_cascades.into_iter().flat_map(|s| s)
.zip(static_gpu_maps.into_iter().flat_map(|s| s))
.map(|(cascade, gpu_map)| (cascade.parameters(), &shadow_maps[**gpu_map]));
for ((shadow_map, gpu_map), matrices) in dynamics.chain(statics).zip(matrices) {
let pos: Pnt2<f32> = convert(gpu_map.viewport().pos);
let offset = pos.to_vec() / shadow_maps.atlas_size() as f32;
let offset = vec2(offset.x, offset.y);
let factor = gpu_map.viewport().width as f32 / shadow_maps.atlas_size() as f32;
let mut shadow_map_data = light_data.start_struct();
shadow_map_data.push(shadow_map.near_clip);
shadow_map_data.push(shadow_map.far_clip);
shadow_map_data.push(shadow_map.frustum_size());
shadow_map_data.push(offset);
shadow_map_data.push(factor);
shadow_map_data.push(shadow_map.bias);
shadow_map_data.push(*matrices.projection_view());
shadow_map_data.push(shadow_map_sampler_index[next_sampler]);
next_sampler += 1;
if let shadow::Type::Poisson{soft_scatter} = shadow_ty {
shadow_map_data.push(1f32);
shadow_map_data.push(*soft_scatter);
}else{
shadow_map_data.pad::<[f32;2]>();
}
}
}
if let Some((ibl, true)) = entities.iter_for::<(Read<ImageBasedLight>, Read<Visible>)>()
.next()
.map(|(i,v)| (i, v.is_visible()))
{
light_data.push(ibl.strength);
light_data.push(ibl.orientation);
}
if let Some((ambient, true)) = entities
.iter_for_mut::<(Write<AmbientLight>, Read<Visible>)>()
.next()
.map(|(i,v)| (i, v.is_visible()))
{
light_data.push(*ambient.diffuse());
light_data.push(*ambient.specular());
ambient.reset_changed();
}else{
light_data.push(vec3!(0f32));
light_data.push(vec4!(0f32));
}
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;
if prev_num_shadow_maps != lighting_textures.shadow_maps.len(){
lighting_textures.num_shadow_maps_changed = true;
}
}
if let Some(ibl) = entities.iter_for_mut::<Write<ImageBasedLight>>().next(){
if ibl.has_changed() {
let textures_pool = resources.get::<TexturesPool>().unwrap();
let diffuse = textures_pool.cubemap(ibl.diffuse).unwrap();
let specular = textures_pool.cubemap(ibl.specular).unwrap();
log::trace!("Recreating ibl uniforms");
lighting_textures.ibl = vec![
LightingTexture{
name: "diffmap".to_owned(),
texture: diffuse.id(),
target: gl::TEXTURE_CUBE_MAP
},
LightingTexture{
name: "envmap".to_owned(),
texture: specular.id(),
target: gl::TEXTURE_CUBE_MAP
},
];
lighting_textures.has_changed = true;
ibl.reset_changed();
}
}else{
if !lighting_textures.ibl.is_empty(){
lighting_textures.ibl.clear();
lighting_textures.has_changed = true;
}
}
}