1use std::sync::{Arc, RwLock};
3
4#[cfg(doc)]
5use crate::stage::Stage;
6use craballoc::{
7 prelude::{Hybrid, SlabAllocator, WgpuRuntime},
8 slab::SlabBuffer,
9 value::HybridArray,
10};
11use crabslab::Id;
12use glam::{Mat4, UVec2, Vec3, Vec4};
13use snafu::prelude::*;
14
15use crate::{
16 atlas::{Atlas, AtlasBlitter, AtlasError},
17 geometry::Geometry,
18 transform::{shader::TransformDescriptor, NestedTransform, Transform},
19};
20
21use super::shader::{
22 DirectionalLightDescriptor, LightDescriptor, LightStyle, LightingDescriptor,
23 PointLightDescriptor, SpotLightDescriptor,
24};
25
26pub use super::{
27 shader::{Candela, Lux},
28 shadow_map::ShadowMap,
29};
30
31#[derive(Debug, Snafu)]
32#[snafu(visibility(pub(crate)))]
33pub enum LightingError {
34 #[snafu(display("{source}"))]
35 Atlas { source: AtlasError },
36
37 #[snafu(display("Driver poll error: {source}"))]
38 Poll { source: wgpu::PollError },
39}
40
41impl From<AtlasError> for LightingError {
42 fn from(source: AtlasError) -> Self {
43 LightingError::Atlas { source }
44 }
45}
46
47pub trait IsLight: Clone {
49 fn style(&self) -> LightStyle;
51
52 fn light_space_transforms(
53 &self,
54 parent_transform: &TransformDescriptor,
56 z_near: f32,
63 z_far: f32,
65 ) -> Vec<Mat4>;
66}
67
68#[derive(Clone, Debug)]
72pub struct DirectionalLight {
73 descriptor: Hybrid<DirectionalLightDescriptor>,
74}
75
76impl IsLight for DirectionalLight {
77 fn style(&self) -> LightStyle {
78 LightStyle::Directional
79 }
80
81 fn light_space_transforms(
82 &self,
83 parent_transform: &TransformDescriptor,
84 z_near: f32,
85 z_far: f32,
86 ) -> Vec<Mat4> {
87 let m = Mat4::from(*parent_transform);
88 vec![{
89 let (p, v) = self
90 .descriptor()
91 .shadow_mapping_projection_and_view(&m, z_near, z_far);
92 p * v
93 }]
94 }
95}
96
97impl DirectionalLight {
98 pub fn id(&self) -> Id<DirectionalLightDescriptor> {
100 self.descriptor.id()
101 }
102
103 pub fn descriptor(&self) -> DirectionalLightDescriptor {
105 self.descriptor.get()
106 }
107}
108
109impl AnalyticalLight<DirectionalLight> {
113 pub fn set_direction(&self, direction: Vec3) -> &Self {
115 self.inner.descriptor.modify(|d| d.direction = direction);
116 self
117 }
118
119 pub fn with_direction(self, direction: Vec3) -> Self {
121 self.set_direction(direction);
122 self
123 }
124
125 pub fn modify_direction<T: 'static>(&self, f: impl FnOnce(&mut Vec3) -> T) -> T {
127 self.inner.descriptor.modify(|d| f(&mut d.direction))
128 }
129
130 pub fn direction(&self) -> Vec3 {
132 self.inner.descriptor.get().direction
133 }
134
135 pub fn set_color(&self, color: Vec4) -> &Self {
137 self.inner.descriptor.modify(|d| d.color = color);
138 self
139 }
140
141 pub fn with_color(self, color: Vec4) -> Self {
143 self.set_color(color);
144 self
145 }
146
147 pub fn modify_color<T: 'static>(&self, f: impl FnOnce(&mut Vec4) -> T) -> T {
149 self.inner.descriptor.modify(|d| f(&mut d.color))
150 }
151
152 pub fn color(&self) -> Vec4 {
154 self.inner.descriptor.get().color
155 }
156
157 pub fn set_intensity(&self, intensity: Lux) -> &Self {
159 self.inner.descriptor.modify(|d| d.intensity = intensity);
160 self
161 }
162
163 pub fn with_intensity(self, intensity: Lux) -> Self {
165 self.set_intensity(intensity);
166 self
167 }
168
169 pub fn modify_intensity<T: 'static>(&self, f: impl FnOnce(&mut Lux) -> T) -> T {
171 self.inner.descriptor.modify(|d| f(&mut d.intensity))
172 }
173
174 pub fn intensity(&self) -> Lux {
176 self.inner.descriptor.get().intensity
177 }
178}
179
180#[derive(Clone, Debug)]
184pub struct PointLight {
185 descriptor: Hybrid<PointLightDescriptor>,
186}
187
188impl IsLight for PointLight {
189 fn style(&self) -> LightStyle {
190 LightStyle::Point
191 }
192
193 fn light_space_transforms(
194 &self,
195 t: &TransformDescriptor,
196 z_near: f32,
200 z_far: f32,
202 ) -> Vec<Mat4> {
203 let m = Mat4::from(*t);
204 let (p, vs) = self
205 .descriptor()
206 .shadow_mapping_projection_and_view_matrices(&m, z_near, z_far);
207 vs.into_iter().map(|v| p * v).collect()
208 }
209}
210
211impl PointLight {
212 pub fn id(&self) -> Id<PointLightDescriptor> {
214 self.descriptor.id()
215 }
216
217 pub fn descriptor(&self) -> PointLightDescriptor {
219 self.descriptor.get()
220 }
221}
222
223impl AnalyticalLight<PointLight> {
228 pub fn set_position(&self, position: Vec3) -> &Self {
230 self.inner.descriptor.modify(|d| d.position = position);
231 self
232 }
233
234 pub fn with_position(self, position: Vec3) -> Self {
236 self.set_position(position);
237 self
238 }
239
240 pub fn modify_position<T: 'static>(&self, f: impl FnOnce(&mut Vec3) -> T) -> T {
242 self.inner.descriptor.modify(|d| f(&mut d.position))
243 }
244
245 pub fn position(&self) -> Vec3 {
247 self.inner.descriptor.get().position
248 }
249
250 pub fn set_color(&self, color: Vec4) -> &Self {
252 self.inner.descriptor.modify(|d| d.color = color);
253 self
254 }
255
256 pub fn with_color(self, color: Vec4) -> Self {
258 self.set_color(color);
259 self
260 }
261
262 pub fn modify_color<T: 'static>(&self, f: impl FnOnce(&mut Vec4) -> T) -> T {
264 self.inner.descriptor.modify(|d| f(&mut d.color))
265 }
266
267 pub fn color(&self) -> Vec4 {
269 self.inner.descriptor.get().color
270 }
271
272 pub fn set_intensity(&self, intensity: Candela) -> &Self {
274 self.inner.descriptor.modify(|d| d.intensity = intensity);
275 self
276 }
277
278 pub fn with_intensity(self, intensity: Candela) -> Self {
280 self.set_intensity(intensity);
281 self
282 }
283
284 pub fn modify_intensity<T: 'static>(&self, f: impl FnOnce(&mut Candela) -> T) -> T {
286 self.inner.descriptor.modify(|d| f(&mut d.intensity))
287 }
288
289 pub fn intensity(&self) -> Candela {
291 self.inner.descriptor.get().intensity
292 }
293}
294
295#[derive(Clone, Debug)]
299pub struct SpotLight {
300 descriptor: Hybrid<SpotLightDescriptor>,
301}
302
303impl IsLight for SpotLight {
304 fn style(&self) -> LightStyle {
305 LightStyle::Spot
306 }
307
308 fn light_space_transforms(
309 &self,
310 t: &TransformDescriptor,
311 z_near: f32,
315 z_far: f32,
317 ) -> Vec<Mat4> {
318 let m = Mat4::from(*t);
319 vec![{
320 let (p, v) = self
321 .descriptor()
322 .shadow_mapping_projection_and_view(&m, z_near, z_far);
323 p * v
324 }]
325 }
326}
327
328impl SpotLight {
329 pub fn id(&self) -> Id<SpotLightDescriptor> {
331 self.descriptor.id()
332 }
333
334 pub fn descriptor(&self) -> SpotLightDescriptor {
336 self.descriptor.get()
337 }
338}
339
340impl AnalyticalLight<SpotLight> {
345 pub fn set_position(&self, position: Vec3) -> &Self {
347 self.inner.descriptor.modify(|d| d.position = position);
348 self
349 }
350
351 pub fn with_position(self, position: Vec3) -> Self {
353 self.set_position(position);
354 self
355 }
356
357 pub fn modify_position<T: 'static>(&self, f: impl FnOnce(&mut Vec3) -> T) -> T {
359 self.inner.descriptor.modify(|d| f(&mut d.position))
360 }
361
362 pub fn position(&self) -> Vec3 {
364 self.inner.descriptor.get().position
365 }
366
367 pub fn set_direction(&self, direction: Vec3) -> &Self {
369 self.inner.descriptor.modify(|d| d.direction = direction);
370 self
371 }
372
373 pub fn with_direction(self, direction: Vec3) -> Self {
375 self.set_direction(direction);
376 self
377 }
378
379 pub fn modify_direction<T: 'static>(&self, f: impl FnOnce(&mut Vec3) -> T) -> T {
381 self.inner.descriptor.modify(|d| f(&mut d.direction))
382 }
383
384 pub fn direction(&self) -> Vec3 {
386 self.inner.descriptor.get().direction
387 }
388
389 pub fn set_inner_cutoff(&self, inner_cutoff: f32) -> &Self {
391 self.inner
392 .descriptor
393 .modify(|d| d.inner_cutoff = inner_cutoff);
394 self
395 }
396
397 pub fn with_inner_cutoff(self, inner_cutoff: f32) -> Self {
399 self.set_inner_cutoff(inner_cutoff);
400 self
401 }
402
403 pub fn modify_inner_cutoff<T: 'static>(&self, f: impl FnOnce(&mut f32) -> T) -> T {
405 self.inner.descriptor.modify(|d| f(&mut d.inner_cutoff))
406 }
407
408 pub fn inner_cutoff(&self) -> f32 {
410 self.inner.descriptor.get().inner_cutoff
411 }
412
413 pub fn set_outer_cutoff(&self, outer_cutoff: f32) -> &Self {
415 self.inner
416 .descriptor
417 .modify(|d| d.outer_cutoff = outer_cutoff);
418 self
419 }
420
421 pub fn with_outer_cutoff(self, outer_cutoff: f32) -> Self {
423 self.set_outer_cutoff(outer_cutoff);
424 self
425 }
426
427 pub fn modify_outer_cutoff<T: 'static>(&self, f: impl FnOnce(&mut f32) -> T) -> T {
429 self.inner.descriptor.modify(|d| f(&mut d.outer_cutoff))
430 }
431
432 pub fn outer_cutoff(&self) -> f32 {
434 self.inner.descriptor.get().outer_cutoff
435 }
436
437 pub fn set_color(&self, color: Vec4) -> &Self {
439 self.inner.descriptor.modify(|d| d.color = color);
440 self
441 }
442
443 pub fn with_color(self, color: Vec4) -> Self {
445 self.set_color(color);
446 self
447 }
448
449 pub fn modify_color<T: 'static>(&self, f: impl FnOnce(&mut Vec4) -> T) -> T {
451 self.inner.descriptor.modify(|d| f(&mut d.color))
452 }
453
454 pub fn color(&self) -> Vec4 {
456 self.inner.descriptor.get().color
457 }
458
459 pub fn set_intensity(&self, intensity: Candela) -> &Self {
461 self.inner.descriptor.modify(|d| d.intensity = intensity);
462 self
463 }
464
465 pub fn with_intensity(self, intensity: Candela) -> Self {
467 self.set_intensity(intensity);
468 self
469 }
470
471 pub fn modify_intensity<T: 'static>(&self, f: impl FnOnce(&mut Candela) -> T) -> T {
473 self.inner.descriptor.modify(|d| f(&mut d.intensity))
474 }
475
476 pub fn intensity(&self) -> Candela {
478 self.inner.descriptor.get().intensity
479 }
480}
481
482#[derive(Clone)]
483pub enum Light {
484 Directional(DirectionalLight),
485 Point(PointLight),
486 Spot(SpotLight),
487}
488
489impl From<DirectionalLight> for Light {
490 fn from(light: DirectionalLight) -> Self {
491 Light::Directional(light)
492 }
493}
494
495impl From<PointLight> for Light {
496 fn from(light: PointLight) -> Self {
497 Light::Point(light)
498 }
499}
500
501impl From<SpotLight> for Light {
502 fn from(light: SpotLight) -> Self {
503 Light::Spot(light)
504 }
505}
506
507impl IsLight for Light {
508 fn style(&self) -> LightStyle {
509 match self {
510 Light::Directional(light) => light.style(),
511 Light::Point(light) => light.style(),
512 Light::Spot(light) => light.style(),
513 }
514 }
515
516 fn light_space_transforms(
517 &self,
518 parent_transform: &TransformDescriptor,
520 z_near: f32,
524 z_far: f32,
526 ) -> Vec<Mat4> {
527 match self {
528 Light::Directional(light) => {
529 light.light_space_transforms(parent_transform, z_near, z_far)
530 }
531 Light::Point(light) => light.light_space_transforms(parent_transform, z_near, z_far),
532 Light::Spot(light) => light.light_space_transforms(parent_transform, z_near, z_far),
533 }
534 }
535}
536
537#[derive(Clone)]
549pub struct AnalyticalLight<T = Light> {
550 pub(crate) light_descriptor: Hybrid<LightDescriptor>,
552 inner: T,
554 transform: Transform,
558 node_transform: Arc<RwLock<Option<NestedTransform>>>,
565}
566
567impl<T: IsLight> core::fmt::Display for AnalyticalLight<T> {
568 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
569 f.write_fmt(format_args!(
570 "AnalyticalLight type={} light-id={:?} node-nested-transform-global-id:{:?}",
571 self.inner.style(),
572 self.light_descriptor.id(),
573 self.node_transform
574 .read()
575 .unwrap()
576 .as_ref()
577 .map(|h| h.global_id())
578 ))
579 }
580}
581
582impl<T: IsLight> IsLight for AnalyticalLight<T> {
583 fn style(&self) -> LightStyle {
584 self.inner.style()
585 }
586
587 fn light_space_transforms(
588 &self,
589 parent_transform: &TransformDescriptor,
591 z_near: f32,
598 z_far: f32,
600 ) -> Vec<Mat4> {
601 self.inner
602 .light_space_transforms(parent_transform, z_near, z_far)
603 }
604}
605
606impl AnalyticalLight {
607 pub fn as_directional(&self) -> Option<&DirectionalLight> {
610 match &self.inner {
611 Light::Directional(light) => Some(light),
612 _ => None,
613 }
614 }
615
616 pub fn as_point(&self) -> Option<&PointLight> {
619 match &self.inner {
620 Light::Point(light) => Some(light),
621 _ => None,
622 }
623 }
624
625 pub fn as_spot(&self) -> Option<&SpotLight> {
628 match &self.inner {
629 Light::Spot(light) => Some(light),
630 _ => None,
631 }
632 }
633}
634
635impl<T: IsLight> AnalyticalLight<T> {
636 pub fn id(&self) -> Id<LightDescriptor> {
638 self.light_descriptor.id()
639 }
640
641 pub fn descriptor(&self) -> LightDescriptor {
643 self.light_descriptor.get()
644 }
645
646 pub fn link_node_transform(&self, transform: &NestedTransform) {
648 *self
649 .node_transform
650 .write()
651 .expect("light node_transform write") = Some(transform.clone());
652 }
653
654 pub fn inner(&self) -> &T {
656 &self.inner
657 }
658
659 pub fn transform(&self) -> &Transform {
669 &self.transform
670 }
671
672 pub fn linked_node_transform(&self) -> Option<NestedTransform> {
679 self.node_transform
680 .read()
681 .unwrap()
682 .as_ref()
683 .map(|t| t.clone())
684 }
685
686 pub fn into_generic(self) -> AnalyticalLight
691 where
692 Light: From<T>,
693 {
694 let AnalyticalLight {
695 light_descriptor,
696 inner,
697 transform,
698 node_transform,
699 } = self;
700 let inner = Light::from(inner);
701 AnalyticalLight {
702 light_descriptor,
703 inner,
704 transform,
705 node_transform,
706 }
707 }
708}
709
710#[derive(Clone)]
712pub struct Lighting {
713 pub(crate) geometry_slab: SlabAllocator<WgpuRuntime>,
714 pub(crate) light_slab: SlabAllocator<WgpuRuntime>,
715 pub(crate) light_slab_buffer: Arc<RwLock<SlabBuffer<wgpu::Buffer>>>,
716 pub(crate) geometry_slab_buffer: Arc<RwLock<SlabBuffer<wgpu::Buffer>>>,
717 pub(crate) lighting_descriptor: Hybrid<LightingDescriptor>,
718 pub(crate) analytical_lights: Arc<RwLock<Vec<AnalyticalLight>>>,
719 pub(crate) analytical_lights_array: Arc<RwLock<Option<HybridArray<Id<LightDescriptor>>>>>,
720 pub(crate) shadow_map_update_pipeline: Arc<wgpu::RenderPipeline>,
721 pub(crate) shadow_map_update_bindgroup_layout: Arc<wgpu::BindGroupLayout>,
722 pub(crate) shadow_map_update_blitter: AtlasBlitter,
723 pub(crate) shadow_map_atlas: Atlas,
724}
725
726pub struct LightingBindGroupLayoutEntries {
727 pub light_slab: wgpu::BindGroupLayoutEntry,
728 pub shadow_map_image: wgpu::BindGroupLayoutEntry,
729 pub shadow_map_sampler: wgpu::BindGroupLayoutEntry,
730}
731
732impl LightingBindGroupLayoutEntries {
733 pub fn new(starting_binding: u32) -> Self {
734 Self {
735 light_slab: wgpu::BindGroupLayoutEntry {
736 binding: starting_binding,
737 visibility: wgpu::ShaderStages::FRAGMENT,
738 ty: wgpu::BindingType::Buffer {
739 ty: wgpu::BufferBindingType::Storage { read_only: true },
740 has_dynamic_offset: false,
741 min_binding_size: None,
742 },
743 count: None,
744 },
745 shadow_map_image: wgpu::BindGroupLayoutEntry {
746 binding: starting_binding + 1,
747 visibility: wgpu::ShaderStages::FRAGMENT,
748 ty: wgpu::BindingType::Texture {
749 sample_type: wgpu::TextureSampleType::Float { filterable: false },
750 view_dimension: wgpu::TextureViewDimension::D2Array,
751 multisampled: false,
752 },
753 count: None,
754 },
755 shadow_map_sampler: wgpu::BindGroupLayoutEntry {
756 binding: starting_binding + 2,
757 visibility: wgpu::ShaderStages::FRAGMENT,
758 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
759 count: None,
760 },
761 }
762 }
763}
764
765impl Lighting {
766 fn create_shadow_map_atlas(
768 light_slab: &SlabAllocator<WgpuRuntime>,
769 size: wgpu::Extent3d,
770 ) -> Atlas {
771 let usage = wgpu::TextureUsages::RENDER_ATTACHMENT
772 | wgpu::TextureUsages::TEXTURE_BINDING
773 | wgpu::TextureUsages::COPY_SRC;
774 Atlas::new(
775 light_slab,
776 size,
777 Some(wgpu::TextureFormat::R32Float),
778 Some("shadow-map-atlas"),
779 Some(usage),
780 )
781 }
782
783 pub fn new(atlas_size: wgpu::Extent3d, geometry: &Geometry) -> Self {
785 let runtime = geometry.runtime();
786 let light_slab = SlabAllocator::new(runtime, "light-slab", wgpu::BufferUsages::empty());
787 let lighting_descriptor = light_slab.new_value(LightingDescriptor::default());
788 let light_slab_buffer = light_slab.commit();
789 let shadow_map_update_bindgroup_layout: Arc<_> =
790 ShadowMap::create_update_bindgroup_layout(&runtime.device).into();
791 let shadow_map_update_pipeline =
792 ShadowMap::create_update_pipeline(&runtime.device, &shadow_map_update_bindgroup_layout)
793 .into();
794 Self {
795 shadow_map_atlas: Self::create_shadow_map_atlas(&light_slab, atlas_size),
796 analytical_lights: Default::default(),
797 analytical_lights_array: Default::default(),
798 geometry_slab: geometry.slab_allocator().clone(),
799 light_slab,
800 light_slab_buffer: Arc::new(RwLock::new(light_slab_buffer)),
801 lighting_descriptor,
802 geometry_slab_buffer: Arc::new(RwLock::new(geometry.slab_allocator().commit())),
803 shadow_map_update_pipeline,
804 shadow_map_update_bindgroup_layout,
805 shadow_map_update_blitter: AtlasBlitter::new(
806 &runtime.device,
807 wgpu::TextureFormat::R32Float,
808 wgpu::FilterMode::Nearest,
809 ),
810 }
811 }
812
813 pub fn slab_allocator(&self) -> &SlabAllocator<WgpuRuntime> {
814 &self.light_slab
815 }
816
817 pub fn add_light<T>(&self, bundle: &AnalyticalLight<T>)
828 where
829 T: IsLight,
830 Light: From<T>,
831 {
832 log::trace!(
833 "adding light {:?} ({})",
834 bundle.light_descriptor.id(),
835 bundle.inner.style()
836 );
837 self.analytical_lights
839 .write()
840 .expect("analytical_lights write")
841 .push(bundle.clone().into_generic());
842 *self
844 .analytical_lights_array
845 .write()
846 .expect("analytical_lights_array write") = None;
847 }
848
849 pub fn remove_light<T: IsLight>(&self, bundle: &AnalyticalLight<T>) {
856 log::trace!(
857 "removing light {:?} ({})",
858 bundle.light_descriptor.id(),
859 bundle.inner.style()
860 );
861 let mut guard = self
863 .analytical_lights
864 .write()
865 .expect("analytical_lights write");
866 guard.retain(|stored_light| {
867 stored_light.light_descriptor.id() != bundle.light_descriptor.id()
868 });
869 *self
870 .analytical_lights_array
871 .write()
872 .expect("analytical_lights_array write") = None;
873 }
874
875 pub fn lights(&self) -> Vec<AnalyticalLight> {
877 self.analytical_lights
878 .read()
879 .expect("analytical_lights read")
880 .clone()
881 }
882
883 pub fn new_directional_light(&self) -> AnalyticalLight<DirectionalLight> {
887 let descriptor = self
888 .light_slab
889 .new_value(DirectionalLightDescriptor::default());
890 let transform = Transform::new(&self.light_slab);
891 let light_descriptor = self.light_slab.new_value({
892 let mut light = LightDescriptor::from(descriptor.id());
893 light.transform_id = transform.id();
894 light
895 });
896
897 let bundle = AnalyticalLight {
898 light_descriptor,
899 inner: DirectionalLight { descriptor },
900 transform,
901 node_transform: Default::default(),
902 };
903 self.add_light(&bundle);
904
905 bundle
906 }
907
908 pub fn new_point_light(&self) -> AnalyticalLight<PointLight> {
912 let descriptor = self.light_slab.new_value(PointLightDescriptor::default());
913 let transform = Transform::new(&self.light_slab);
914 let light_descriptor = self.light_slab.new_value({
915 let mut light = LightDescriptor::from(descriptor.id());
916 light.transform_id = transform.id();
917 light
918 });
919
920 let bundle = AnalyticalLight {
921 light_descriptor,
922 inner: PointLight { descriptor },
923 transform,
924 node_transform: Default::default(),
925 };
926 self.add_light(&bundle);
927
928 bundle
929 }
930
931 pub fn new_spot_light(&self) -> AnalyticalLight<SpotLight> {
935 let descriptor = self.light_slab.new_value(SpotLightDescriptor::default());
936 let transform = Transform::new(&self.light_slab);
937 let light_descriptor = self.light_slab.new_value({
938 let mut light = LightDescriptor::from(descriptor.id());
939 light.transform_id = transform.id();
940 light
941 });
942
943 let bundle = AnalyticalLight {
944 light_descriptor,
945 inner: SpotLight { descriptor },
946 transform,
947 node_transform: Default::default(),
948 };
949 self.add_light(&bundle);
950
951 bundle
952 }
953
954 pub fn set_ambient_color(&self, color: Vec4) {
962 self.lighting_descriptor.modify(|d| d.ambient_color = color);
963 }
964
965 pub fn ambient_color(&self) -> Vec4 {
967 self.lighting_descriptor.get().ambient_color
968 }
969
970 pub fn new_shadow_map<T>(
973 &self,
974 analytical_light_bundle: &AnalyticalLight<T>,
975 size: UVec2,
977 z_near: f32,
981 z_far: f32,
985 ) -> Result<ShadowMap, LightingError>
986 where
987 T: IsLight,
988 Light: From<T>,
989 {
990 ShadowMap::new(self, analytical_light_bundle, size, z_near, z_far)
991 }
992
993 #[must_use]
994 pub fn commit(&self) -> SlabBuffer<wgpu::Buffer> {
995 log::trace!("committing lights");
996
997 for light in self
999 .analytical_lights
1000 .read()
1001 .expect("analytical_lights read")
1002 .iter()
1003 {
1004 if let Some(node_transform) = light
1005 .node_transform
1006 .read()
1007 .expect("light node_transform read")
1008 .as_ref()
1009 {
1010 let global_node_transform = node_transform.global_descriptor();
1011 if node_transform.global_descriptor() != light.transform.descriptor() {
1012 light.transform.set_descriptor(global_node_transform);
1013 }
1014 }
1015 }
1016
1017 let lights_array = {
1018 let mut array_guard = self
1019 .analytical_lights_array
1020 .write()
1021 .expect("analytical_lights_array write");
1022
1023 array_guard
1026 .get_or_insert_with(|| {
1027 log::trace!(" analytical lights array was invalidated");
1028 let lights_guard = self
1029 .analytical_lights
1030 .read()
1031 .expect("analytical_lights read");
1032 let new_lights = lights_guard
1033 .iter()
1034 .map(|bundle| bundle.light_descriptor.id());
1035 let array = self.light_slab.new_array(new_lights);
1036 log::trace!(" lights array is now: {:?}", array.array());
1037 array
1038 })
1039 .array()
1040 };
1041
1042 self.lighting_descriptor.modify(
1043 |LightingDescriptor {
1044 analytical_lights_array,
1045 shadow_map_atlas_descriptor_id,
1046 update_shadow_map_id,
1047 update_shadow_map_texture_index,
1048 light_tiling_descriptor_id: _,
1050 ambient_color: _,
1052 }| {
1053 *analytical_lights_array = lights_array;
1054 *shadow_map_atlas_descriptor_id = self.shadow_map_atlas.descriptor_id();
1055 *update_shadow_map_id = Id::NONE;
1056 *update_shadow_map_texture_index = 0;
1057 },
1058 );
1059
1060 let buffer = self.light_slab.commit();
1061 log::trace!(" light slab creation time: {}", buffer.creation_time());
1062 buffer
1063 }
1064}
1065
1066#[cfg(test)]
1067mod test;