use color::{Rgb, ToRgb, ToRgba};
use graphics::{Mesh, PrimitiveType, vertex3d, Vertex3D, Mvp, CoordinateOrigin, node, Node, NodeRef, NodeMut};
use na::*;
use super::{Renderer, Render3d, Renderer3d,
basic_material, ShadowMap, Light,
shadow_mapping::DirectionalShadowMap};
use glin::{self, RenderSurface};
use std::borrow::Borrow;
use std::marker::PhantomData;
use std::mem;
pub struct DirectionalLight{
node: Node,
color: Rgb<f32>,
strength: f32,
radius: f32,
}
#[repr(C)]
struct NonPaddedData{
world_direction: Vec4,
color: Rgb<f32>,
radius: f32,
strength: f32,
padding: [f32;3],
shadow_map_idx: [i32;4],
}
#[repr(C)]
pub struct Data{
data: NonPaddedData,
pad: Mat4,
}
#[repr(C)]
pub struct DataWithShadow{
data: NonPaddedData,
view_matrix: Mat4,
}
impl DirectionalLight{
pub fn new() -> DirectionalLight{
DirectionalLight{
node: Node::identity(),
color: rgb!(1.0f32,1.0f32,1.0f32),
strength: 1.0,
radius: 0.1,
}
}
pub fn color(&self) -> &Rgb<f32>{
&self.color
}
pub fn strength(&self) -> f32{
self.strength
}
pub fn radius(&self) -> f32{
self.radius
}
pub fn set_color<C: ToRgb + ToRgba>(&mut self, color: &C){
self.color = color.to_rgb();
}
pub fn set_strength(&mut self, strength: f32){
self.strength = strength;
}
pub fn direction(&self) -> Vec3{
normalize(&(self.global_orientation() * Vec3::z()))
}
pub fn set_radius(&mut self, radius: f32){
self.radius = radius;
}
fn eye_direction(&self, view_matrix: &Mat4) -> Vec3{
let direction = self.direction();
Vec3::from_homogeneous(*view_matrix * vec4(direction.x, direction.y, direction.z, 0.0)).unwrap()
}
fn non_padded_data(&self, shadow_map_idx: [i32;4]) -> NonPaddedData{
NonPaddedData{
world_direction: vec4!(self.direction(), 0.),
color: self.color,
radius: self.radius,
strength: self.strength,
padding: unsafe{ mem::uninitialized() },
shadow_map_idx,
}
}
pub fn data(&self) -> Data{
Data{
data: self.non_padded_data([-1;4]),
pad: unsafe{ mem::uninitialized() }
}
}
pub fn with_shadow_maps<'l, S: DirectionalShadowMap<G>, G, B: Borrow<S>>(&'l self, shadow_maps: Vec<B>) -> DirectionalLightWithShadowMap<'l, S, G, B>{
assert!(shadow_maps.len() <= 4);
let view = self.inv_global_transformation();
let mut projections = vec![];
let mut mvps = vec![];
let mut biased_mvps = vec![];
for shadow_map in shadow_maps.iter() {
let size = shadow_map.borrow().frustum_size();
let left = -size / 2.;
let right = size / 2.;
let top = size / 2.;
let bottom = -size / 2.;
let znear = shadow_map.borrow().near_clip();
let zfar = shadow_map.borrow().far_clip();
let projection = Orthographic3::new(left, right, bottom, top, znear, zfar).to_mat();
let mvp = projection.fast_mul(&view);
let biased_mvp = 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(&mvp);
projections.push(projection);
mvps.push(mvp);
biased_mvps.push(biased_mvp);
}
DirectionalLightWithShadowMap{
shadow_maps,
view,
projections,
mvps,
biased_mvps,
light: self,
marker: PhantomData,
marker2: PhantomData,
}
}
}
impl NodeRef for DirectionalLight {
fn node(&self) -> &Node{
&self.node
}
}
impl NodeMut for DirectionalLight {
fn node_mut(&mut self) -> &mut Node{
&mut self.node
}
fn set_scale(&mut self, _s: Vec3){
}
fn update_with_parent_flags(&mut self, parent: Option<&Node>, flags: node::Flags) -> bool{
self.node.update_with_parent_flags(parent, flags)
}
}
impl Light for DirectionalLight{
fn ty(&self) -> &str{
"DIRECTIONAL_LIGHT"
}
fn uniforms(&self, _shadow_map_idx_offset: &mut usize) -> Vec<glin::program::Uniform>{
uniforms!{
enabled: 1f32,
type: 1f32,
world_direction: vec4!(self.direction(), 1.0),
color: self.color,
strength: self.strength,
radius: self.radius,
}
}
fn shadow_maps(&self) -> Vec<&dyn ShadowMap>{
vec![]
}
}
pub struct DirectionalLightWithShadowMap<'l, S, G, B>{
light: &'l DirectionalLight,
shadow_maps: Vec<B>,
view: Mat4,
projections: Vec<Mat4>,
mvps: Vec<Mat4>,
biased_mvps: Vec<Mat4>,
marker: PhantomData<G>,
marker2: PhantomData<S>,
}
impl<'l, G, S: DirectionalShadowMap<G>, B: Borrow<S>> DirectionalLightWithShadowMap<'l, S, G, B>{
pub fn view(&self) -> &Mat4{
&self.view
}
pub fn projection(&self, index: usize) -> &Mat4{
&self.projections[index]
}
pub fn projection_view(&self, index: usize) -> &Mat4{
&self.mvps[index]
}
pub fn biased_projection_view(&self, index: usize) -> &Mat4{
&self.biased_mvps[index]
}
pub fn biased_projection_views(&self) -> &[Mat4]{
&self.biased_mvps
}
pub fn mvp(&self, index: usize) -> Mvp{
Mvp::from_parts(
*self.projection(index),
*self.view(),
self.global_position(),
self.shadow_maps[index].borrow().near_clip(),
self.shadow_maps[index].borrow().far_clip(),
vec4!(0.),
CoordinateOrigin::CenterUp,
self.shadow_maps[index].borrow().viewport())
}
fn shadow_map_idx(&self, offset: i32) -> [i32;4]{
let mut shadow_maps = self.shadow_maps.iter();
let idx0 = shadow_maps.next()
.map(|s| s.borrow().shadow_sampler().shadow_sampler_idx() + offset)
.unwrap_or(-1);
let idx1 = shadow_maps.next()
.map(|s| s.borrow().shadow_sampler().shadow_sampler_idx() + offset + 1)
.unwrap_or(-1);
let idx2 = shadow_maps.next()
.map(|s| s.borrow().shadow_sampler().shadow_sampler_idx() + offset + 2)
.unwrap_or(-1);
let idx3 = shadow_maps.next()
.map(|s| s.borrow().shadow_sampler().shadow_sampler_idx() + offset + 3)
.unwrap_or(-1);
[idx0, idx1, idx2, idx3]
}
pub fn data(&self, shadow_map_idx_offset: usize) -> DataWithShadow{
DataWithShadow {
data: self.light.non_padded_data(self.shadow_map_idx(shadow_map_idx_offset as i32)),
view_matrix: self.view,
}
}
}
impl<'l, S, G, B> NodeRef for DirectionalLightWithShadowMap<'l, S, G, B>{
fn node(&self) -> &Node{
&self.light.node
}
}
impl<'l, G, S: DirectionalShadowMap<G>, B: Borrow<S>> Light for DirectionalLightWithShadowMap<'l, S, G, B>{
fn ty(&self) -> &str{
"DIRECTIONAL_LIGHT_SHADOW"
}
fn uniforms(&self, shadow_map_idx_offset: &mut usize) -> Vec<glin::program::Uniform>{
let mut uniforms = self.light.uniforms(shadow_map_idx_offset);
uniforms.extend(uniforms!{
view_matrix: self.view,
shadow_map_idx: self.shadow_map_idx(*shadow_map_idx_offset as i32)
});
uniforms.extend(self.biased_mvps.iter().enumerate().map(|(i, biased_mvp)| {
let i = *shadow_map_idx_offset + i;
glin::program::uniform(format!("shadow_matrices[{}]", i), biased_mvp)
}));
uniforms.extend(self.shadow_maps.iter().enumerate().flat_map(|(i,s)|{
let i = *shadow_map_idx_offset + i;
s.borrow().uniforms(i)
}));
*shadow_map_idx_offset += self.shadow_maps.len();
uniforms
}
fn shadow_maps(&self) -> Vec<&dyn ShadowMap>{
self.shadow_maps.iter().map(|s| s.borrow() as &dyn ShadowMap).collect()
}
}
geometry_cache!(Vertex3D, {
let vertices = (-3..3).flat_map(|i| vec![
vec3(i as f32 * 0.2, 0., 0.),
vec3(i as f32 * 0.2, 0., -1.),
vec3(i as f32 * 0.2, 0., -1.),
vec3(i as f32 * 0.2 -0.025, -0.025, -1. + 0.05),
vec3(i as f32 * 0.2, 0., -1.),
vec3(i as f32 * 0.2 + 0.025, 0.025, -1. + 0.05),
]).map(|v| vertex3d(v));
Mesh::from_iter_and_type(vertices, PrimitiveType::Lines)
});
impl Render3d for DirectionalLight{
fn render<R: RenderSurface>(&self, gl: &Renderer<R>) where Self: Sized{
let geom = get_geometry(gl);
let material = basic_material::Builder::new()
.color(&(self.color * self.strength))
.create();
gl.with_model(self)
.draw_vao_with_material(geom.full_range(), &material);
}
}
impl<'l, G, S: DirectionalShadowMap<G>, B: Borrow<S>> Render3d for DirectionalLightWithShadowMap<'l, S, G, B>{
fn render<R: RenderSurface>(&self, gl: &Renderer<R>) where Self: Sized{
self.light.render(gl)
}
}