1use crate::core::*;
2use crate::renderer::light::*;
3use crate::renderer::*;
4
5pub struct DirectionalLight {
10 context: Context,
11 shadow_texture: Option<DepthTexture2D>,
12 shadow_matrix: Mat4,
13 pub intensity: f32,
15 pub color: Srgba,
17 pub direction: Vec3,
19}
20
21impl DirectionalLight {
22 pub fn new(
24 context: &Context,
25 intensity: f32,
26 color: Srgba,
27 direction: Vec3,
28 ) -> DirectionalLight {
29 DirectionalLight {
30 context: context.clone(),
31 shadow_matrix: Mat4::identity(),
32 shadow_texture: None,
33 intensity,
34 color,
35 direction,
36 }
37 }
38
39 pub fn clear_shadow_map(&mut self) {
44 self.shadow_texture = None;
45 self.shadow_matrix = Mat4::identity();
46 }
47
48 pub fn generate_shadow_map(
55 &mut self,
56 texture_size: u32,
57 geometries: impl IntoIterator<Item = impl Geometry> + Clone,
58 ) {
59 let up = compute_up_direction(self.direction);
60
61 let viewport = Viewport::new_at_origo(texture_size, texture_size);
62 let mut aabb = AxisAlignedBoundingBox::EMPTY;
63 for geometry in geometries.clone() {
64 aabb.expand_with_aabb(geometry.aabb());
65 }
66 if aabb.is_empty() {
67 return;
68 }
69 let position = aabb.center();
70 let target = position + self.direction.normalize();
71 let z_far = aabb.distance_max(position);
72 let z_near = -z_far;
73 let frustum_height = aabb.max().distance(aabb.min()); let shadow_camera = Camera::new_orthographic(
75 viewport,
76 position,
77 target,
78 up,
79 frustum_height,
80 z_near,
81 z_far,
82 );
83 let mut shadow_texture = DepthTexture2D::new::<f32>(
84 &self.context,
85 texture_size,
86 texture_size,
87 Wrapping::ClampToEdge,
88 Wrapping::ClampToEdge,
89 );
90 let depth_material = DepthMaterial {
91 render_states: RenderStates {
92 write_mask: WriteMask::DEPTH,
93 ..Default::default()
94 },
95 ..Default::default()
96 };
97 let frustum = shadow_camera.frustum();
98 shadow_texture
99 .as_depth_target()
100 .clear(ClearState::default())
101 .write::<RendererError>(|| {
102 for geometry in geometries
103 .into_iter()
104 .filter(|g| frustum.contains(g.aabb()))
105 {
106 render_with_material(
107 &self.context,
108 &shadow_camera,
109 &geometry,
110 &depth_material,
111 &[],
112 );
113 }
114 Ok(())
115 })
116 .unwrap();
117 self.shadow_texture = Some(shadow_texture);
118 self.shadow_matrix = shadow_matrix(&shadow_camera);
119 }
120
121 pub fn shadow_map(&self) -> Option<&DepthTexture2D> {
125 self.shadow_texture.as_ref()
126 }
127}
128
129impl Light for DirectionalLight {
130 fn shader_source(&self, i: u32) -> String {
131 if self.shadow_texture.is_some() {
132 format!(
133 "
134 uniform sampler2D shadowMap{};
135 uniform mat4 shadowMVP{};
136
137 uniform vec3 color{};
138 uniform vec3 direction{};
139
140 vec3 calculate_lighting{}(vec3 surface_color, vec3 position, vec3 normal, vec3 view_direction, float metallic, float roughness, float occlusion)
141 {{
142 return calculate_light(color{}, -direction{}, surface_color, view_direction, normal, metallic, roughness)
143 * calculate_shadow(-direction{}, normal, shadowMap{}, shadowMVP{}, position);
144 }}
145
146 ", i, i, i, i, i, i, i, i, i, i)
147 } else {
148 format!(
149 "
150 uniform vec3 color{};
151 uniform vec3 direction{};
152
153 vec3 calculate_lighting{}(vec3 surface_color, vec3 position, vec3 normal, vec3 view_direction, float metallic, float roughness, float occlusion)
154 {{
155 return calculate_light(color{}, -direction{}, surface_color, view_direction, normal, metallic, roughness);
156 }}
157
158 ", i, i, i, i, i)
159 }
160 }
161 fn use_uniforms(&self, program: &Program, i: u32) {
162 if let Some(ref tex) = self.shadow_texture {
163 program.use_depth_texture(&format!("shadowMap{}", i), tex);
164 program.use_uniform(&format!("shadowMVP{}", i), self.shadow_matrix);
165 }
166 program.use_uniform(
167 &format!("color{}", i),
168 self.color.to_linear_srgb().truncate() * self.intensity,
169 );
170 program.use_uniform(&format!("direction{}", i), self.direction.normalize());
171 }
172
173 fn id(&self) -> LightId {
174 LightId::DirectionalLight(self.shadow_texture.is_some())
175 }
176}