use crate::{
components::{Name, Ty},
transformation::{Transformation, DynamicTransformation},
};
use na::{Mat4, Orthographic3, Perspective3, FastMul, ToMat, one};
use rin::{
color::*,
graphics::{Node, NodeRef},
};
use angle::Angle;
use std::mem::{self, Discriminant};
use rinecs::{Entity, Entities, Resources, Read, ReadOption};
#[cfg(any(feature="gl", feature="gles", feature="webgl"))]
use crate::renderer::{
light::ImageBasedLight
};
pub mod shadow;
#[derive(Clone,Copy,Debug,Component, Serialize, Deserialize)]
pub struct Light;
#[derive(Clone,Copy,Debug)]
pub struct LightAsCameraMatrices{
projection: Mat4,
projection_view: Mat4,
view: Mat4,
}
#[derive(Clone,Copy,Debug,Component, Serialize, Deserialize)]
pub struct DirectionalLight{
pub color: Rgb<f32>,
pub radius: f32,
pub strength: f32,
}
impl DirectionalLight{
pub fn matrices_for(trafo: &Transformation, shadow_map: &shadow::Parameters) -> DirectionalLightMatrices{
let view = trafo.inv_global_transformation();
let frustum_size = shadow_map.frustum_size;
let left = -frustum_size / 2.;
let right = frustum_size / 2.;
let top = frustum_size / 2.;
let bottom = -frustum_size / 2.;
let znear = shadow_map.near_clip;
let zfar = shadow_map.far_clip;
let projection = Orthographic3::new(left, right, bottom, top, znear, zfar).to_mat();
let projection_view = projection.fast_mul(&view);
let biased_projection_view = Mat4::new(
0.5, 0.0, 0.0, 0.5,
0.0, 0.5, 0.0, 0.5,
0.0, 0.0, 0.5, 0.5,
0.0, 0.0, 0.0, 1.0
).fast_mul(&projection_view);
DirectionalLightMatrices{
projection,
projection_view,
view,
biased_projection_view,
}
}
}
#[derive(Clone,Copy,Debug,OneToNComponent, Serialize, Deserialize)]
pub struct DirectionalLightMatrices{
projection: Mat4,
projection_view: Mat4,
view: Mat4,
biased_projection_view: Mat4,
}
impl DirectionalLightMatrices{
pub fn projection(&self) -> &Mat4{
&self.projection
}
pub fn view(&self) -> &Mat4{
&self.view
}
pub fn projection_view(&self) -> &Mat4{
&self.projection_view
}
pub fn biased_projection_view(&self) -> &Mat4{
&self.biased_projection_view
}
pub fn as_camera_matrices(&self) -> &LightAsCameraMatrices{
unsafe{ mem::transmute(self) }
}
}
pub struct DirectionalLightBuilder<'a, C: 'a>{
world: &'a mut C,
light: DirectionalLight,
trafo: Node,
parent: Option<Entity>,
dynamic: bool,
shadow_map: Option<shadow::Map>,
static_shadow_map: Option<shadow::StaticMap>,
shadow_type: Option<shadow::Type>,
name: &'a str,
}
impl<'a, C: rinecs::CreationContext> DirectionalLightBuilder<'a, C>{
pub(crate) fn new(world: &'a mut C, name: &'a str) -> DirectionalLightBuilder<'a, C>{
DirectionalLightBuilder{
world,
light: DirectionalLight{
color: rgb!(1.0, 1.0, 1.0),
strength: 1.0,
radius: 0.1,
},
trafo: one(),
parent: None,
dynamic: false,
shadow_map: None,
static_shadow_map: None,
shadow_type: None,
name,
}
}
pub fn transformation<T: Into<Node>>(mut self, trafo: T) -> DirectionalLightBuilder<'a,C>{
self.trafo = trafo.into();
self
}
pub fn transformation_as_child<T: Into<Node>>(mut self, trafo: T, parent: Entity) -> DirectionalLightBuilder<'a,C>{
self.trafo = trafo.into();
self.parent = Some(parent);
self
}
pub fn dynamic_transformation<T: Into<Node>>(mut self, trafo: T) -> DirectionalLightBuilder<'a,C>{
self.trafo = trafo.into();
self.dynamic = true;
self
}
pub fn dynamic_transformation_as_child<T: Into<Node>>(mut self, trafo: T, parent: Entity) -> DirectionalLightBuilder<'a,C>{
self.trafo = trafo.into();
self.parent = Some(parent);
self.dynamic = true;
self
}
pub fn color<CC: ToRgb>(mut self, color: CC) -> DirectionalLightBuilder<'a,C>{
self.light.color = color.to_rgb();
self
}
pub fn strength(mut self, strength: f32) -> DirectionalLightBuilder<'a,C>{
self.light.strength = strength;
self
}
pub fn radius(mut self, radius: f32) -> DirectionalLightBuilder<'a,C>{
self.light.radius = radius;
self
}
pub fn shadow_map(mut self, shadow_map: shadow::Parameters) -> DirectionalLightBuilder<'a,C>{
self.shadow_map = Some(shadow::Map(shadow_map));
self
}
pub fn static_shadow_map(mut self, shadow_map: shadow::Parameters) -> DirectionalLightBuilder<'a,C>{
self.static_shadow_map = Some(shadow::StaticMap(shadow_map));
self
}
pub fn shadow_type(mut self, shadow_type: shadow::Type) -> DirectionalLightBuilder<'a,C>{
self.shadow_type = Some(shadow_type);
self
}
pub fn build(self) -> Entity{
let builder = self.world.new_entity()
.add(Name(self.name.to_owned()))
.add(Light)
.add(Ty::Light)
.add(self.light);
let builder = if let Some(parent) = self.parent {
builder.add_child(&parent, Transformation::from_node(self.trafo))
}else{
builder.add(Transformation::from_node(self.trafo))
};
let builder = if self.shadow_map.is_some() || self.static_shadow_map.is_some(){
builder
.add(self.shadow_type.unwrap_or(shadow::Type::Gaussian))
}else{
builder
};
let builder = if let Some(shadow_map) = self.shadow_map {
builder
.add(shadow_map)
}else{
builder
};
let builder = if let Some(shadow_map) = self.static_shadow_map {
builder
.add(shadow_map)
}else{
builder
};
if self.dynamic{
builder.add(DynamicTransformation).build()
}else{
builder.build()
}
}
}
#[derive(Clone,Copy,Debug,Component, Serialize, Deserialize)]
pub struct AmbientLight{
pub diffuse: Rgb<f32>,
pub specular: Rgb<f32>,
}
#[derive(Clone,Copy,Debug,Component, Serialize, Deserialize)]
pub struct Attenuation{
pub attenuation_constant: f32,
pub attenuation_linear: f32,
pub attenuation_quadratic: f32,
}
#[derive(Clone,Copy,Debug,Component, Serialize, Deserialize)]
pub struct PointLight{
pub radius: f32,
pub color: Rgb<f32>,
pub strength: f32,
}
pub struct PointLightBuilder<'a, C: 'a>{
world: &'a mut C,
light: PointLight,
trafo: Node,
parent: Option<Entity>,
dynamic: bool,
name: &'a str,
}
impl<'a, C: rinecs::CreationContext> PointLightBuilder<'a,C>{
pub(crate) fn new(world: &'a mut C, name: &'a str) -> PointLightBuilder<'a,C>{
PointLightBuilder{
world,
light: PointLight{
color: rgb!(1.0, 1.0, 1.0),
strength: 1.0,
radius: 0.1,
},
trafo: one(),
parent: None,
dynamic: false,
name,
}
}
pub fn transformation<T: Into<Node>>(mut self, trafo: T) -> PointLightBuilder<'a,C>{
self.trafo = trafo.into();
self
}
pub fn transformation_as_child<T: Into<Node>>(mut self, trafo: T, parent: Entity) -> PointLightBuilder<'a,C>{
self.trafo = trafo.into();
self.parent = Some(parent);
self
}
pub fn dynamic_transformation<T: Into<Node>>(mut self, trafo: T) -> PointLightBuilder<'a,C>{
self.trafo = trafo.into();
self.dynamic = true;
self
}
pub fn dynamic_transformation_as_child<T: Into<Node>>(mut self, trafo: T, parent: Entity) -> PointLightBuilder<'a,C>{
self.trafo = trafo.into();
self.parent = Some(parent);
self.dynamic = true;
self
}
pub fn color<CC: ToRgb>(&mut self, color: CC) -> &mut PointLightBuilder<'a,C>{
self.light.color = color.to_rgb();
self
}
pub fn strength(&mut self, strength: f32) -> &mut PointLightBuilder<'a,C>{
self.light.strength = strength;
self
}
pub fn radius(&mut self, radius: f32) -> &mut PointLightBuilder<'a,C>{
self.light.radius = radius;
self
}
pub fn build(&mut self) -> Entity{
let builder = self.world.new_entity()
.add(Name(self.name.to_owned()))
.add(Light)
.add(Ty::Light)
.add(self.light);
let builder = if let Some(parent) = self.parent {
builder.add_child(&parent, Transformation::from_node(self.trafo))
}else{
builder.add(Transformation::from_node(self.trafo))
};
if self.dynamic{
builder.add(DynamicTransformation).build()
}else{
builder.build()
}
}
}
#[derive(Clone,Copy,Debug,Component, Serialize, Deserialize)]
pub struct SpotLight{
pub radius: f32,
pub color: Rgb<f32>,
pub strength: f32,
pub spot_angle: Deg<f32>,
pub spot_cos_cutoff: f32,
pub spot_cos_inner_cutoff: f32,
pub spot_blend: f32,
}
impl SpotLight{
pub fn recalculate_cos(&mut self){
self.spot_cos_cutoff = self.spot_angle.cos();
self.spot_cos_inner_cutoff = (self.spot_angle * (1. - self.spot_blend)).cos();
}
pub fn matrices_for(&self, trafo: &Transformation, shadow_map: &shadow::Parameters) -> SpotLightMatrices{
let ratio = 1.;
let view = trafo.inv_global_transformation();
let projection = *Perspective3::new(
ratio,
self.spot_angle.to_rad().value() * 2.,
shadow_map.near_clip,
shadow_map.far_clip)
.as_matrix();
let projection_view = projection.fast_mul(&view);
SpotLightMatrices{
projection,
projection_view,
view,
}
}
}
#[derive(Clone,Copy,Debug,OneToNComponent, Serialize, Deserialize)]
pub struct SpotLightMatrices{
projection: Mat4,
projection_view: Mat4,
view: Mat4,
}
impl SpotLightMatrices{
pub fn projection(&self) -> &Mat4{
&self.projection
}
pub fn view(&self) -> &Mat4{
&self.view
}
pub fn projection_view(&self) -> &Mat4{
&self.projection_view
}
pub fn as_camera_matrices(&self) -> &LightAsCameraMatrices{
unsafe{ mem::transmute(self) }
}
}
pub struct SpotLightBuilder<'a,C: 'a>{
world: &'a mut C,
light: SpotLight,
trafo: Node,
parent: Option<Entity>,
dynamic: bool,
shadow_map: Option<shadow::Map>,
static_shadow_map: Option<shadow::StaticMap>,
shadow_type: Option<shadow::Type>,
name: &'a str,
}
impl<'a, C: rinecs::CreationContext> SpotLightBuilder<'a,C>{
pub(crate) fn new(world: &'a mut C, name: &'a str) -> SpotLightBuilder<'a,C>{
let spot_angle = Deg(45.);
let spot_cos_cutoff = spot_angle.cos();
let spot_blend = 1.;
let spot_cos_inner_cutoff = (spot_angle * (1. - spot_blend)).cos();
SpotLightBuilder{
world,
light: SpotLight{
color: rgb!(1.0, 1.0, 1.0),
radius: 0.1,
strength: 1.0,
spot_angle,
spot_cos_cutoff,
spot_cos_inner_cutoff,
spot_blend: 1.,
},
trafo: one(),
parent: None,
dynamic: false,
shadow_map: None,
static_shadow_map: None,
shadow_type: None,
name,
}
}
pub fn transformation<T: Into<Node>>(mut self, trafo: T) -> SpotLightBuilder<'a,C>{
self.trafo = trafo.into();
self
}
pub fn transformation_as_child<T: Into<Node>>(mut self, trafo: T, parent: Entity) -> SpotLightBuilder<'a,C>{
self.trafo = trafo.into();
self.parent = Some(parent);
self
}
pub fn dynamic_transformation<T: Into<Node>>(mut self, trafo: T) -> SpotLightBuilder<'a,C>{
self.trafo = trafo.into();
self.dynamic = true;
self
}
pub fn dynamic_transformation_as_child<T: Into<Node>>(mut self, trafo: T, parent: Entity) -> SpotLightBuilder<'a,C>{
self.trafo = trafo.into();
self.parent = Some(parent);
self.dynamic = true;
self
}
pub fn color<CC: ToRgb>(mut self, color: CC) -> SpotLightBuilder<'a,C>{
self.light.color = color.to_rgb();
self
}
pub fn strength(mut self, strength: f32) -> SpotLightBuilder<'a,C>{
self.light.strength = strength;
self
}
pub fn radius(mut self, radius: f32) -> SpotLightBuilder<'a,C>{
self.light.radius = radius;
self
}
pub fn spot_angle(mut self, angle: Deg<f32>) -> SpotLightBuilder<'a,C>{
self.light.spot_angle = angle;
self
}
pub fn spot_blend(mut self, spot_blend: f32) -> SpotLightBuilder<'a,C>{
self.light.spot_blend = spot_blend;
self
}
pub fn shadow_map(mut self, shadow_map: shadow::Parameters) -> SpotLightBuilder<'a,C>{
self.shadow_map = Some(shadow::Map(shadow_map));
self
}
pub fn static_shadow_map(mut self, shadow_map: shadow::Parameters) -> SpotLightBuilder<'a,C>{
self.static_shadow_map = Some(shadow::StaticMap(shadow_map));
self
}
pub fn shadow_type(mut self, shadow_type: shadow::Type) -> SpotLightBuilder<'a,C>{
self.shadow_type = Some(shadow_type);
self
}
pub fn build(mut self) -> Entity{
self.light.recalculate_cos();
let builder = self.world.new_entity()
.add(Name(self.name.to_owned()))
.add(Light)
.add(Ty::Light)
.add(self.light);
let builder = if let Some(parent) = self.parent {
builder.add_child(&parent, Transformation::from_node(self.trafo))
}else{
builder.add(Transformation::from_node(self.trafo))
};
let builder = if self.shadow_map.is_some() || self.static_shadow_map.is_some(){
builder
.add(self.shadow_type.unwrap_or(shadow::Type::Gaussian))
}else{
builder
};
let builder = if let Some(shadow_map) = self.shadow_map {
builder
.add(shadow_map)
}else{
builder
};
let builder = if let Some(shadow_map) = self.static_shadow_map {
builder
.add(shadow_map)
}else{
builder
};
if self.dynamic{
builder.add(DynamicTransformation).build()
}else{
builder.build()
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
pub struct LightInfo{
pub num_directional_lights: usize,
pub num_spot_lights: usize,
pub num_point_lights: usize,
pub num_shadow_maps: usize,
pub max_num_shadow_maps_directional: usize,
pub max_num_shadow_maps_point: usize,
pub max_num_shadow_maps_spot: usize,
pub use_ibl: bool,
pub shadow_map_type: Option<Discriminant<shadow::Type>>,
has_changed: bool,
}
impl LightInfo{
fn update(&mut self, mut light_info: LightInfo){
self.has_changed = light_info.has_changed;
let changed = self != &light_info;
light_info.has_changed = changed;
*self = light_info;
}
pub fn has_changed(&self) -> bool{
self.has_changed
}
pub fn shadow_map_type(&self) -> Option<&'static str>{
self.shadow_map_type.map(|ty| shadow::Type::discriminant_to_str(ty))
}
}
pub fn check_lights_changed_system(entities: Entities, resources: Resources){
let num_directional_lights = entities.iter_for::<Read<DirectionalLight>>().count();
let num_spot_lights = entities.iter_for::<Read<SpotLight>>().count();
let num_point_lights = entities.iter_for::<Read<PointLight>>().count();
let max_num_shadow_maps_directional = entities
.iter_for::<(Read<DirectionalLight>, ReadOption<shadow::Map>, ReadOption<shadow::StaticMap>)>()
.map(|(_,s,ss)| s.is_some() as u8 + ss.is_some() as u8)
.max()
.unwrap_or(0) as usize;
let max_num_shadow_maps_spot = entities
.iter_for::<(Read<SpotLight>, ReadOption<shadow::Map>, ReadOption<shadow::StaticMap>)>()
.map(|(_,s,ss)| s.is_some() as u8 + ss.is_some() as u8)
.max()
.unwrap_or(0) as usize;
let max_num_shadow_maps_point = entities
.iter_for::<(Read<PointLight>, ReadOption<shadow::Map>, ReadOption<shadow::StaticMap>)>()
.map(|(_,s,ss)| s.is_some() as u8 + ss.is_some() as u8)
.max()
.unwrap_or(0) as usize;
let num_shadow_maps = entities.iter_for::<Read<shadow::Map>>().count() +
entities.iter_for::<Read<shadow::StaticMap>>().count();
#[cfg(any(feature="gl", feature="gles", feature="webgl"))]
let use_ibl = entities.iter_for::<Read<ImageBasedLight>>().next().is_some();
#[cfg(not(any(feature="gl", feature="gles", feature="webgl")))]
let use_ibl = false;
let shadow_map_type = entities.iter_for::<Read<shadow::Type>>().next().map(|ty| mem::discriminant(ty));
let light_info = LightInfo{
num_directional_lights,
num_spot_lights,
num_point_lights,
max_num_shadow_maps_directional,
max_num_shadow_maps_spot,
max_num_shadow_maps_point,
num_shadow_maps,
use_ibl,
shadow_map_type,
has_changed: false,
};
resources.get_mut::<LightInfo>().unwrap().update(light_info)
}