use crate::{
components::{Name, Ty, Visible},
};
#[cfg(feature="debug_geometry")]
use crate::geometry::{Geometry, DebugGeometryRef};
#[cfg(feature="debug_geometry")]
use rin_material::MaterialRef;
#[cfg(feature="debug_geometry")]
use color::consts::WHITE;
#[cfg(feature="debug_geometry")]
use rin_graphics::{self, Mesh, vertex3d, PrimitiveType, Vertex3D, vertex3dcolor, Vertex3DColor};
use color::{Rgb, ToRgb, rgb_linear, color_space::LinearRgb};
#[cfg(any(feature="show_debug_dynamic_cascades", feature="show_debug_static_cascades"))]
use color::rgb;
use rin_graphics::{Node, node::DynamicTransformation};
use rin_math::{Mat4, Orthographic3, Perspective3, ToMat, one, vec3, Deg, Rad, Vec3, Angle, clamp};
#[cfg(feature="debug_geometry")]
use rin_math::pnt2;
#[cfg(any(feature="show_debug_dynamic_cascades", feature="show_debug_static_cascades"))]
use rin_math::vec4;
use std::mem::{self, Discriminant};
use rinecs::{Entity, Entities, Resources, Read, Write};
#[cfg(feature="debug_geometry")]
use rinecs::ReadOption;
#[cfg(feature="gl_forward_renderer")]
pub use crate::renderer::{
light::ImageBasedLight
};
use rinecs::{Component, OneToNComponent, system};
use serde_derive::{Serialize, Deserialize};
use std140_data::{Std140, WriteStd140, Data};
pub mod shadow;
#[derive(Clone, Copy, Debug, Component, Serialize, Deserialize)]
pub struct Light;
#[derive(Clone, Copy, Debug, Component, Serialize, Deserialize)]
#[autochanges]
pub struct DirectionalLight{
color: Rgb<f32, LinearRgb>,
radius: f32,
strength: f32,
changed: bool,
}
impl rinecs::Changes for DirectionalLight{
fn has_changed(&self) -> bool{
self.changed
}
fn reset_changed(&mut self){
self.changed = false
}
}
impl DirectionalLight{
pub fn from_components(color: Rgb<f32, LinearRgb>, radius: f32, strength: f32) -> DirectionalLight {
DirectionalLight{
color,
radius,
strength,
changed: true,
}
}
pub fn color(&self) -> &Rgb<f32, LinearRgb>{
&self.color
}
pub fn radius(&self) -> f32{
self.radius
}
pub fn strength(&self) -> f32{
self.strength
}
pub fn set_color<C: ToRgb>(&mut self, color: &C){
let color = color.to_rgb().to_linear();
self.changed |= self.color != color;
self.color = color;
}
pub fn set_strength(&mut self, strength: f32) {
self.changed |= self.strength != strength;
self.strength = strength
}
pub fn set_radius(&mut self, radius: f32){
self.changed |= self.radius != radius;
self.radius = radius
}
pub fn matrices_for(trafo: &Node, shadow_map: &shadow::Parameters) -> DirectionalLightMatrices {
let view = trafo.inv_global_transformation();
let left = shadow_map.left;
let right = shadow_map.right;
let bottom = shadow_map.bottom;
let top = shadow_map.top;
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 * 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
) * projection_view;
DirectionalLightMatrices{
projection,
projection_view,
view,
biased_projection_view,
}
}
#[cfg(feature="debug_geometry")]
pub fn debug_geometry<'a, I1, I2>(
&self,
shadow_cascades: Option<I1>,
static_shadow_cascades: Option<I2>) -> Mesh<Vertex3DColor>
where
I1: IntoIterator<Item = &'a shadow::Parameters>,
I2: IntoIterator<Item = &'a shadow::Parameters>,
{
let max_distance = 30.;
let vertices = (-3..3).flat_map(|i| vec![
vec3(i as f32 * 0.2, 0., 0.),
vec3(i as f32 * 0.2, 0., -max_distance),
vec3(i as f32 * 0.2 -0.025, -0.025, -max_distance + 0.05),
vec3(i as f32 * 0.2 + 0.025, 0.025, -max_distance + 0.05),
]).map(|v| vertex3dcolor(v, &self.color));
let mut debug_geom = Mesh::from_iter_and_type(vertices, PrimitiveType::Lines);
debug_geom.set_indices((0..6).flat_map(|i| {
let start = i * 4;
vec![
0 + start, 1 + start, 1 + start, 2 + start, 1 + start, 3 + start,
]
}).collect());
#[cfg(any(feature="show_debug_dynamic_cascades", feature="show_debug_static_cascades"))]
const TINTS: [Rgb<u8>; 4] = [
rgb!(243u8, 0, 73),
rgb!(82, 204, 106),
rgb!(46, 120, 153),
rgb!(255, 238, 25)
];
#[cfg(any(feature="show_debug_dynamic_cascades", feature="show_debug_static_cascades"))]
fn shadow_cascades_debug_geometry<'a, I>(
shadow_cascades: I,
debug_geom: &mut Mesh<Vertex3DColor>)
where I: IntoIterator<Item = &'a shadow::Parameters>
{
let cuboid = rin_graphics::cuboid_wireframe(vec3!(2.));
for (shadow_map, tint) in shadow_cascades.into_iter().zip(TINTS.iter()) {
let matrices = DirectionalLight::matrices_for(&Node::identity(), shadow_map);
let inv_projection = matrices.projection.try_inverse().unwrap();
let frustum_verts = cuboid.iter().map(|v|{
let light_space_position = inv_projection * vec4!(v.position, 1.);
let pos = light_space_position.xyz() / light_space_position.w;
vertex3dcolor(pos, tint)
}).collect();
let frustum_indices = cuboid.indices().to_vec();
let frustum = Mesh::new(frustum_verts, frustum_indices, cuboid.primitive_type());
debug_geom.extend_from_mesh(&frustum);
}
}
#[cfg(feature="show_debug_dynamic_cascades")]
{
if let Some(shadow_cascades) = shadow_cascades {
shadow_cascades_debug_geometry(shadow_cascades, &mut debug_geom);
}
}
#[cfg(feature="show_debug_static_cascades")]
{
if let Some(shadow_cascades) = static_shadow_cascades {
shadow_cascades_debug_geometry(shadow_cascades, &mut debug_geom);
}
}
debug_geom
}
#[cfg(feature="debug_geometry")]
pub fn debug_material<'a, C: crate::CreationContext<'a>>(world: &mut C) -> MaterialRef {
world.find_material("LightDebugMaterial").unwrap_or_else(||{
let material = rin_material::BasicMaterialBuilder::default()
.build();
world.register_material("DirectionalLightMaterial", material)
})
}
}
#[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 struct DirectionalLightBuilder<'a, C: 'a>{
world: &'a mut C,
light: DirectionalLight,
trafo: Node,
parent: Option<Entity>,
dynamic: bool,
shadow_maps: Vec<shadow::Map>,
static_shadow_maps: Vec<shadow::StaticMap>,
cascades: Option<shadow::Cascades>,
static_cascades: Option<shadow::StaticCascades>,
shadow_type: Option<shadow::Type>,
visible: bool,
name: &'a str,
}
impl<'a, 'c, C: crate::CreationContext<'c>> DirectionalLightBuilder<'a, C>{
pub(crate) fn new(world: &'a mut C, name: &'a str) -> DirectionalLightBuilder<'a, C>{
DirectionalLightBuilder{
world,
light: DirectionalLight{
color: rgb_linear!(1.0, 1.0, 1.0),
strength: 1.0,
radius: 0.1,
changed: true,
},
trafo: one(),
parent: None,
dynamic: false,
shadow_maps: vec![],
static_shadow_maps: vec![],
cascades: None,
static_cascades: None,
shadow_type: None,
visible: true,
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 invisible(mut self) -> DirectionalLightBuilder<'a,C>{
self.visible = false;
self
}
pub fn color<CC: ToRgb>(mut self, color: CC) -> DirectionalLightBuilder<'a,C>{
self.light.color = color.to_rgb().to_linear();
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_maps = vec![shadow::Map::new(shadow_map)];
self
}
pub fn shadow_map_cascades<P>(mut self, maps: P, cascades: shadow::Cascades) -> DirectionalLightBuilder<'a,C>
where P: IntoIterator<Item = shadow::Parameters>
{
self.shadow_maps = maps.into_iter().map(|p| shadow::Map::new(p)).collect();
self.cascades = Some(cascades);
self
}
pub fn static_shadow_map(mut self, shadow_map: shadow::Parameters) -> DirectionalLightBuilder<'a,C>{
self.static_shadow_maps = vec![shadow::StaticMap::new(shadow_map)];
self
}
pub fn static_shadow_map_cascades<P>(mut self, cascades: P) -> DirectionalLightBuilder<'a,C>
where P: IntoIterator<Item = shadow::Parameters>
{
self.static_shadow_maps = cascades.into_iter().map(|p| shadow::StaticMap::new(p)).collect();
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{
#[cfg(feature="debug_geometry")]
let debug_geom = {
let shadow_params = if !self.shadow_maps.is_empty(){
Some(self.shadow_maps.iter().map(|s| s.parameters()))
}else{
None
};
let static_shadow_params = if !self.static_shadow_maps.is_empty(){
Some(self.static_shadow_maps.iter().map(|s| s.parameters()))
}else{
None
};
let debug_geom = self.light.debug_geometry(shadow_params, static_shadow_params);
let debug_geom = self.world.register_mesh(debug_geom);
let material = DirectionalLight::debug_material(self.world);
DebugGeometryRef::new(debug_geom, material)
};
let has_shadow = !self.shadow_maps.is_empty() || !self.static_shadow_maps.is_empty();
let mut builder = self.world.new_entity()
.add(Name(self.name.to_owned()))
.add(Light)
.add(Visible::new(self.visible))
.add(Ty::Light)
.add(self.light);
if !self.shadow_maps.is_empty() {
builder = builder.add_slice(self.shadow_maps)
}
if !self.static_shadow_maps.is_empty() {
builder = builder.add_slice(self.static_shadow_maps)
}
builder = builder
.add_option(self.cascades)
.add_option(self.static_cascades);
#[cfg(feature="debug_geometry")]
{
builder = builder.add(debug_geom);
}
builder = if let Some(parent) = self.parent {
builder.add_child(&parent, self.trafo)
}else{
builder.add(self.trafo)
};
if has_shadow {
builder = builder.add(self.shadow_type.unwrap_or(shadow::Type::Gaussian5))
}
if self.dynamic{
builder.add_tag::<DynamicTransformation>().build()
}else{
builder.build()
}
}
}
#[derive(Clone, Copy, Debug, Component, Serialize, Deserialize)]
pub struct AmbientLight{
diffuse: Rgb<f32, LinearRgb>,
specular: Rgb<f32, LinearRgb>,
changed: bool,
}
impl AmbientLight{
pub fn new<'a, C: rinecs::EntitiesCreationExt<'a>>(
world: &mut C,
diffuse: Rgb<f32, LinearRgb>,
specular: Rgb<f32, LinearRgb>) -> rinecs::Entity
{
let ambient_light = AmbientLight{
diffuse,
specular,
changed: true,
};
world.new_entity()
.add(ambient_light)
.add(Ty::Light)
.add(Visible::new(true))
.build()
}
pub fn diffuse(&self) -> &Rgb<f32, LinearRgb>{
&self.diffuse
}
pub fn specular(&self) -> &Rgb<f32, LinearRgb>{
&self.specular
}
pub fn set_diffuse<C: ToRgb>(&mut self, diffuse: C){
let new = diffuse.to_rgb().to_linear();
self.changed |= self.diffuse != new;
self.diffuse = new
}
pub fn set_specular<C: ToRgb>(&mut self, specular: C){
let new = specular.to_rgb().to_linear();
self.changed |= self.specular != new;
self.specular = new
}
pub fn has_changed(&self) -> bool{
self.changed
}
pub fn reset_changed(&mut self){
self.changed = false;
}
}
#[derive(Clone, Copy, Debug, Component, Serialize, Deserialize)]
#[autochanges]
pub struct AreaLight{
half_width: f32,
half_height: f32,
strength: f32,
color: Rgb<f32, LinearRgb>,
changed: bool,
}
impl AreaLight{
pub fn half_width(&self, trafo: &Node) -> Vec3 {
trafo.global_orientation() * vec3(self.half_width, 0., 0.)
}
pub fn half_height(&self, trafo: &Node) -> Vec3 {
trafo.global_orientation() * vec3(0., self.half_height, 0.)
}
pub fn color(&self) -> &Rgb<f32, LinearRgb>{
&self.color
}
pub fn strength(&self) -> f32{
self.strength
}
pub fn width(&self) -> f32{
self.half_width * 2.
}
pub fn height(&self) -> f32{
self.half_height * 2.
}
pub fn set_color<C: ToRgb>(&mut self, color: &C){
let color = color.to_rgb().to_linear();
self.changed |= self.color != color;
self.color = color;
}
pub fn set_width(&mut self, width: f32){
let half_width = width / 2.;
self.changed |= self.half_width != half_width;
self.half_width = half_width
}
pub fn set_height(&mut self, height: f32){
let half_height = height / 2.;
self.changed |= self.half_height != half_height;
self.half_height = half_height
}
pub fn set_strength(&mut self, strength: f32){
self.changed |= self.strength != strength;
self.strength = strength
}
#[cfg(feature="debug_geometry")]
pub fn debug_geometry(&self) -> Mesh<Vertex3D> {
let mesh = rin_graphics::rectangle(
&pnt2(-self.half_width, -self.half_height),
self.half_width * 2.,
self.half_height * 2.);
let primitive_type = mesh.primitive_type();
let (vertices, indices) = mesh.into();
let vertices = vertices.into_iter().map(|v| vertex3d(vec3!(v, 0.))).collect();
Mesh::new(vertices, indices, primitive_type)
}
#[cfg(feature="debug_geometry")]
pub fn debug_material<'a, C: crate::CreationContext<'a>>(world: &mut C) -> MaterialRef {
world.find_material("LightDebugMaterial").unwrap_or_else(||{
let material = rin_material::BasicMaterialBuilder::default()
.base_color(&WHITE)
.build();
world.register_material("DirectionalLightMaterial", material)
})
}
}
impl rinecs::Changes for AreaLight{
fn has_changed(&self) -> bool{
self.changed
}
fn reset_changed(&mut self){
self.changed = false
}
}
pub struct AreaLightBuilder<'a, C: 'a>{
world: &'a mut C,
light: AreaLight,
trafo: Node,
parent: Option<Entity>,
dynamic: bool,
visible: bool,
name: &'a str,
}
impl<'a, 'c, C: crate::CreationContext<'c>> AreaLightBuilder<'a, C>{
pub(crate) fn new(world: &'a mut C, name: &'a str) -> AreaLightBuilder<'a, C>{
AreaLightBuilder{
world,
light: AreaLight{
color: rgb_linear!(1.0, 1.0, 1.0),
half_width: 0.5,
half_height: 0.5,
strength: 200.,
changed: true,
},
trafo: one(),
parent: None,
visible: true,
dynamic: false,
name,
}
}
pub fn transformation<T: Into<Node>>(mut self, trafo: T) -> AreaLightBuilder<'a,C>{
self.trafo = trafo.into();
self
}
pub fn transformation_as_child<T: Into<Node>>(mut self, trafo: T, parent: Entity) -> AreaLightBuilder<'a,C>{
self.trafo = trafo.into();
self.parent = Some(parent);
self
}
pub fn dynamic_transformation<T: Into<Node>>(mut self, trafo: T) -> AreaLightBuilder<'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) -> AreaLightBuilder<'a,C>{
self.trafo = trafo.into();
self.parent = Some(parent);
self.dynamic = true;
self
}
pub fn invisible(mut self) -> AreaLightBuilder<'a,C>{
self.visible = false;
self
}
pub fn color<CC: ToRgb>(mut self, color: CC) -> AreaLightBuilder<'a,C>{
self.light.color = color.to_rgb().to_linear();
self
}
pub fn width(mut self, width: f32) -> AreaLightBuilder<'a,C>{
self.light.half_width = width / 2.;
self
}
pub fn height(mut self, height: f32) -> AreaLightBuilder<'a,C>{
self.light.half_height = height / 2.;
self
}
pub fn strength(mut self, strength: f32) -> AreaLightBuilder<'a,C>{
self.light.strength = strength;
self
}
pub fn build(self) -> Entity{
#[cfg(feature="debug_geometry")]
let debug_geom = {
let debug_geom = self.light.debug_geometry();
let debug_geom = self.world.register_mesh(debug_geom);
let material = AreaLight::debug_material(self.world);
DebugGeometryRef::new(debug_geom, material)
};
let mut builder = self.world.new_entity()
.add(Name(self.name.to_owned()))
.add(Light)
.add(Visible::new(self.visible))
.add(Ty::Light)
.add(self.light);
#[cfg(feature="debug_geometry")]
{
builder = builder.add(debug_geom);
}
builder = if let Some(parent) = self.parent {
builder.add_child(&parent, self.trafo)
}else{
builder.add(self.trafo)
};
if self.dynamic{
builder.add_tag::<DynamicTransformation>().build()
}else{
builder.build()
}
}
}
#[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)]
#[autochanges]
pub struct PointLight{
radius: f32,
color: Rgb<f32, LinearRgb>,
strength: f32,
changed: bool,
}
impl PointLight{
pub fn from_components(color: Rgb<f32, LinearRgb>, radius: f32, strength: f32) -> PointLight {
PointLight{
color,
radius,
strength,
changed: true,
}
}
pub fn color(&self) -> &Rgb<f32, LinearRgb>{
&self.color
}
pub fn radius(&self) -> f32{
self.radius
}
pub fn strength(&self) -> f32{
self.strength
}
pub fn set_color<C: ToRgb>(&mut self, color: &C){
let color = color.to_rgb().to_linear();
self.changed |= self.color != color;
self.color = color;
}
pub fn set_strength(&mut self, strength: f32) {
self.changed |= self.strength != strength;
self.strength = strength
}
pub fn set_radius(&mut self, radius: f32){
self.changed |= self.radius != radius;
self.radius = radius
}
}
impl rinecs::Changes for PointLight{
fn has_changed(&self) -> bool{
self.changed
}
fn reset_changed(&mut self){
self.changed = false
}
}
pub struct PointLightBuilder<'a, C: 'a>{
world: &'a mut C,
light: PointLight,
trafo: Node,
parent: Option<Entity>,
dynamic: bool,
visible: bool,
name: &'a str,
}
impl<'a, 'c, C: rinecs::EntitiesCreationExt<'c>> PointLightBuilder<'a,C>{
pub(crate) fn new(world: &'a mut C, name: &'a str) -> PointLightBuilder<'a,C>{
PointLightBuilder{
world,
light: PointLight{
color: rgb_linear!(1.0, 1.0, 1.0),
strength: 1.0,
radius: 0.1,
changed: true,
},
trafo: one(),
parent: None,
dynamic: false,
visible: true,
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 invisible(mut self) -> PointLightBuilder<'a,C>{
self.visible = false;
self
}
pub fn color<CC: ToRgb>(&mut self, color: CC) -> &mut PointLightBuilder<'a,C>{
self.light.color = color.to_rgb().to_linear();
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(Visible::new(self.visible))
.add(self.light);
let builder = if let Some(parent) = self.parent {
builder.add_child(&parent, self.trafo)
}else{
builder.add(self.trafo)
};
if self.dynamic{
builder.add_tag::<DynamicTransformation>().build()
}else{
builder.build()
}
}
}
#[derive(Clone, Copy, Debug, Component, Serialize, Deserialize)]
#[autochanges]
pub struct SpotLight{
radius: f32,
color: Rgb<f32, LinearRgb>,
strength: f32,
spot_angle: Rad<f32>,
spot_cos_cutoff: f32,
spot_cos_inner_cutoff: f32,
spot_blend: f32,
changed: bool,
}
impl SpotLight{
pub fn from_components(
radius: f32,
color: Rgb<f32, LinearRgb>,
strength: f32,
spot_angle: Deg<f32>,
spot_blend: f32) -> SpotLight
{
let mut spot_light = SpotLight{
radius,
color,
strength,
spot_angle: spot_angle.to_rad(),
spot_blend,
spot_cos_cutoff: 0.,
spot_cos_inner_cutoff: 0.,
changed: true,
};
spot_light.recalculate_cos();
spot_light
}
pub fn color(&self) -> &Rgb<f32, LinearRgb>{
&self.color
}
pub fn radius(&self) -> f32{
self.radius
}
pub fn strength(&self) -> f32{
self.strength
}
pub fn spot_angle(&self) -> Deg<f32>{
self.spot_angle.to_deg()
}
pub fn spot_blend(&self) -> f32{
self.spot_blend
}
pub fn set_color<C: ToRgb>(&mut self, color: &C){
let color = color.to_rgb().to_linear();
self.changed |= self.color != color;
self.color = color;
}
pub fn set_strength(&mut self, strength: f32) {
self.changed |= self.strength != strength;
self.strength = strength
}
pub fn set_radius(&mut self, radius: f32){
self.changed |= self.radius != radius;
self.radius = radius
}
pub fn set_spot_angle(&mut self, angle: Deg<f32>){
self.changed |= self.spot_angle != angle.to_rad();
self.spot_angle = angle.to_rad();
self.recalculate_cos();
}
pub fn set_spot_blend(&mut self, blend: f32){
self.changed |= self.spot_blend != blend;
self.spot_blend = blend;
}
pub fn spot_cos_cutoff(&self) -> f32 {
self.spot_cos_cutoff
}
pub fn spot_cos_inner_cutoff(&self) -> f32 {
self.spot_cos_inner_cutoff
}
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: &Node, 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 * view;
SpotLightMatrices{
projection,
projection_view,
view,
}
}
}
impl rinecs::Changes for SpotLight{
fn has_changed(&self) -> bool{
self.changed
}
fn reset_changed(&mut self){
self.changed = false
}
}
#[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 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>,
visible: bool,
name: &'a str,
}
impl<'a, 'c, C: rinecs::EntitiesCreationExt<'c>> SpotLightBuilder<'a,C>{
pub(crate) fn new(world: &'a mut C, name: &'a str) -> SpotLightBuilder<'a,C>{
let spot_angle = Deg(45.).to_rad();
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_linear!(1.0, 1.0, 1.0),
radius: 0.1,
strength: 1.0,
spot_angle,
spot_cos_cutoff,
spot_cos_inner_cutoff,
spot_blend: 1.,
changed: true,
},
trafo: one(),
parent: None,
dynamic: false,
shadow_map: None,
static_shadow_map: None,
shadow_type: None,
visible: true,
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 invisible(mut self) -> SpotLightBuilder<'a,C>{
self.visible = false;
self
}
pub fn color<CC: ToRgb>(mut self, color: CC) -> SpotLightBuilder<'a,C>{
self.light.color = color.to_rgb().to_linear();
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.to_rad();
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::new(shadow_map));
self
}
pub fn static_shadow_map(mut self, shadow_map: shadow::Parameters) -> SpotLightBuilder<'a,C>{
self.static_shadow_map = Some(shadow::StaticMap::new(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(Visible::new(self.visible))
.add(self.light);
let builder = if let Some(parent) = self.parent {
builder.add_child(&parent, self.trafo)
}else{
builder.add(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::Gaussian5))
}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_tag::<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_area_lights: usize,
pub num_shadow_maps: usize,
pub has_dynamic_shadow_maps_directional: bool,
pub has_dynamic_shadow_maps_point: bool,
pub has_dynamic_shadow_maps_spot: bool,
pub has_static_shadow_maps_directional: bool,
pub has_static_shadow_maps_point: bool,
pub has_static_shadow_maps_spot: bool,
pub use_ibl: bool,
pub has_ambient: bool,
pub shadow_map_type: Option<Discriminant<shadow::Type>>,
pub debug_cascades: bool,
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;
if changed {
log::trace!("Light info changed");
}
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 reset_changed(&mut self){
self.has_changed = false;
}
pub fn shadow_gaussian_kernel_size(&self) -> Option<u32>{
self.shadow_map_type.and_then(|shadow_map_type| {
if shadow_map_type == mem::discriminant(&shadow::Type::Gaussian3) { Some(3) }
else if shadow_map_type == mem::discriminant(&shadow::Type::Gaussian5) { Some(5) }
else if shadow_map_type == mem::discriminant(&shadow::Type::Gaussian7) { Some(7) }
else { None }
})
}
}
#[system(name = "check lights changed")]
#[needs(
"DirectionalLight",
"SpotLight",
"PointLight",
"AmbientLight",
"shadow::Map",
"shadow::StaticMap"
)]
#[cfg_attr(feature="gl_forward_renderer", needs("ImageBasedLight"))]
#[cfg_attr(feature="debug_geometry", updates("Geometry<Vertex3DColor>"))]
#[cfg_attr(feature="debug_geometry", updates("Geometry<Vertex3D>"))]
#[cfg_attr(feature="debug_geometry", reads("DebugGeometryRef"))]
#[updates("LightInfo")]
#[reads("Visible", "shadow::Type", "shadow::Cascades", "shadow::StaticCascades")]
#[writes("shadow::Map", "shadow::StaticMap", "DirectionalLight", "SpotLight", "PointLight", "AreaLight")]
pub fn check_lights_changed_system(mut entities: Entities, resources: Resources){
for cascades in entities.iter_for_mut::<Write<shadow::Map>>() {
for cascade in cascades {
cascade.update();
}
}
entities.update_changed::<shadow::Map>();
entities.update_changed::<shadow::StaticMap>();
entities.update_changed::<DirectionalLight>();
entities.update_changed::<SpotLight>();
entities.update_changed::<PointLight>();
entities.update_changed::<AreaLight>();
let num_directional_lights = entities.iter_for::<(Read<DirectionalLight>, Read<Visible>)>()
.filter(|(_,visible)| visible.is_visible())
.count();
let num_spot_lights = entities.iter_for::<(Read<SpotLight>, Read<Visible>)>()
.filter(|(_,visible)| visible.is_visible())
.count();
let num_point_lights = entities.iter_for::<(Read<PointLight>, Read<Visible>)>()
.filter(|(_,visible)| visible.is_visible())
.count();
let num_area_lights = entities.iter_for::<(Read<AreaLight>, Read<Visible>)>()
.filter(|(_,visible)| visible.is_visible())
.count();
let has_dynamic_shadow_maps_directional = entities
.iter_for::<(Read<DirectionalLight>, Read<Visible>, Read<shadow::Map>)>()
.filter(|(_,v,_)| v.is_visible())
.next()
.is_some();
let has_dynamic_shadow_maps_spot = entities
.iter_for::<(Read<SpotLight>, Read<Visible>, Read<shadow::Map>)>()
.filter(|(_,v,_)| v.is_visible())
.next()
.is_some();
let has_dynamic_shadow_maps_point = entities
.iter_for::<(Read<PointLight>, Read<Visible>, Read<shadow::Map>)>()
.filter(|(_,v,_)| v.is_visible())
.next()
.is_some();
let has_static_shadow_maps_directional = entities
.iter_for::<(Read<DirectionalLight>, Read<Visible>, Read<shadow::StaticMap>)>()
.filter(|(_,v,_)| v.is_visible())
.next()
.is_some();
let has_static_shadow_maps_spot = entities
.iter_for::<(Read<SpotLight>, Read<Visible>, Read<shadow::StaticMap>)>()
.filter(|(_,v,_)| v.is_visible())
.next()
.is_some();
let has_static_shadow_maps_point = entities
.iter_for::<(Read<PointLight>, Read<Visible>, Read<shadow::StaticMap>)>()
.filter(|(_,v,_)| v.is_visible())
.next()
.is_some();
let num_shadow_maps = entities
.iter_for::<(Read<Visible>, Read<shadow::Map>)>()
.filter(|(v,_)| v.is_visible())
.map(|(_, cascades)| cascades.iter().count())
.sum::<usize>()
+ entities
.iter_for::<(Read<Visible>, Read<shadow::StaticMap>)>()
.filter(|(v,_)| v.is_visible())
.map(|(_, cascades)| cascades.iter().count())
.sum::<usize>();
#[cfg(feature="gl_forward_renderer")]
let use_ibl = entities.iter_for::<(Read<ImageBasedLight>, Read<Visible>)>()
.next()
.map(|(_, v)| v.is_visible())
.unwrap_or(false);
let has_ambient = entities.iter_for::<(Read<AmbientLight>, Read<Visible>)>()
.next()
.map(|(_, v)| v.is_visible())
.unwrap_or(false);
#[cfg(not(feature="gl_forward_renderer"))]
let use_ibl = false;
let shadow_map_type = entities.iter_for::<(Read<shadow::Type>, Read<Visible>)>()
.filter(|(_, v)| v.is_visible())
.next()
.map(|(ty, _)| mem::discriminant(ty));
let debug_cascades = entities.iter_for::<(Read<shadow::Cascades>, Read<Visible>)>()
.filter(|(c, v)| c.debug_enabled() && v.is_visible())
.next()
.is_some();
let static_debug_cascades = entities.iter_for::<(Read<shadow::StaticCascades>, Read<Visible>)>()
.filter(|(c, v)| c.debug_enabled() && v.is_visible())
.next()
.is_some();
let light_info = LightInfo{
num_directional_lights,
num_spot_lights,
num_point_lights,
num_area_lights,
has_static_shadow_maps_directional,
has_static_shadow_maps_spot,
has_static_shadow_maps_point,
has_dynamic_shadow_maps_directional,
has_dynamic_shadow_maps_spot,
has_dynamic_shadow_maps_point,
num_shadow_maps,
use_ibl,
has_ambient,
shadow_map_type,
has_changed: false,
debug_cascades: debug_cascades || static_debug_cascades
};
resources.get_mut::<LightInfo>().unwrap().update(light_info);
#[cfg(feature="debug_geometry")]
{
for (light, shadow_map, static_shadow_map, visible, debug_geometry_ref) in entities
.iter_for::<(
Read<DirectionalLight>,
ReadOption<shadow::Map>,
ReadOption<shadow::StaticMap>,
Read<Visible>,
Read<DebugGeometryRef>)>()
.filter(|(_, _, _, visible, _)| visible.is_visible())
{
if debug_geometry_ref.is_visible(){
let shadow_params = shadow_map
.as_ref()
.map(|s| s.iter().map(|s| s.parameters()));
let static_shadow_params = static_shadow_map
.as_ref()
.map(|s| s.iter().map(|s| s.parameters()));
let debug_geometry = light.debug_geometry(shadow_params, static_shadow_params);
let mut geometry = entities
.component_for_mut::<Geometry<Vertex3DColor>>(debug_geometry_ref.geometry())
.unwrap();
geometry.set(debug_geometry);
}
}
for (light, visible, debug_geometry_ref) in entities
.changed_iter_for::<(
Read<AreaLight>,
Read<Visible>,
Read<DebugGeometryRef>)>()
.filter(|(light, visible, _)| visible.is_visible())
{
let debug_geometry = light.debug_geometry();
let mut geometry = entities
.component_for_mut::<Geometry<Vertex3D>>(debug_geometry_ref.geometry())
.unwrap();
geometry.set(debug_geometry);
}
}
}
pub fn blackbody(kelvin: f32) -> Rgb<f32, LinearRgb>{
let temp = kelvin / 100.;
let r = if temp <= 66.{
1.0
}else{
let x = temp - 55.;
let a = 351.97690566805693;
let b = 0.114206453784165;
let c = -40.25366309332127;
let r = a + b * x + c * x.ln();
clamp( r / 255., 0.0, 1.0 )
};
let g = if temp <= 10.{
0.0
}else if temp <= 66.{
let a = -155.25485562709179;
let b = -0.44596950469579133;
let c = 104.49216199393888;
let x = temp - 2.;
let g = a + b * x + c * x.ln();
clamp( g / 255., 0.0, 1.0 )
}else{
let a = 325.4494125711974;
let b = 0.07943456536662342;
let c = -28.0852963507957;
let x = temp - 50.;
let g = a + b * x + c * x.ln();
clamp( g / 255., 0.0, 1.0 )
};
let b = if temp >= 66.{
1.0
}else if temp <= 19.{
0.0
}else{
let a = -254.76935184120902;
let b = 0.8274096064007395;
let c = 115.67994401066147;
let x = temp - 10.;
let b = a + b * x + c * x.ln();
clamp( b / 255., 0.0, 1.0 )
};
rgb_linear!(r,g,b)
}