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::shader::{Candela, Lux};
27pub use super::shadow_map::ShadowMap;
28
29#[derive(Debug, Snafu)]
30#[snafu(visibility(pub(crate)))]
31pub enum LightingError {
32 #[snafu(display("{source}"))]
33 Atlas { source: AtlasError },
34
35 #[snafu(display("Driver poll error: {source}"))]
36 Poll { source: wgpu::PollError },
37}
38
39impl From<AtlasError> for LightingError {
40 fn from(source: AtlasError) -> Self {
41 LightingError::Atlas { source }
42 }
43}
44
45pub trait IsLight: Clone {
47 fn style(&self) -> LightStyle;
49
50 fn light_space_transforms(
51 &self,
52 parent_transform: &TransformDescriptor,
54 z_near: f32,
60 z_far: f32,
62 ) -> Vec<Mat4>;
63}
64
65#[derive(Clone, Debug)]
69pub struct DirectionalLight {
70 descriptor: Hybrid<DirectionalLightDescriptor>,
71}
72
73impl IsLight for DirectionalLight {
74 fn style(&self) -> LightStyle {
75 LightStyle::Directional
76 }
77
78 fn light_space_transforms(
79 &self,
80 parent_transform: &TransformDescriptor,
81 z_near: f32,
82 z_far: f32,
83 ) -> Vec<Mat4> {
84 let m = Mat4::from(*parent_transform);
85 vec![{
86 let (p, v) = self
87 .descriptor()
88 .shadow_mapping_projection_and_view(&m, z_near, z_far);
89 p * v
90 }]
91 }
92}
93
94impl DirectionalLight {
95 pub fn id(&self) -> Id<DirectionalLightDescriptor> {
97 self.descriptor.id()
98 }
99
100 pub fn descriptor(&self) -> DirectionalLightDescriptor {
102 self.descriptor.get()
103 }
104}
105
106impl AnalyticalLight<DirectionalLight> {
110 pub fn set_direction(&self, direction: Vec3) -> &Self {
112 self.inner.descriptor.modify(|d| d.direction = direction);
113 self
114 }
115
116 pub fn with_direction(self, direction: Vec3) -> Self {
118 self.set_direction(direction);
119 self
120 }
121
122 pub fn modify_direction<T: 'static>(&self, f: impl FnOnce(&mut Vec3) -> T) -> T {
124 self.inner.descriptor.modify(|d| f(&mut d.direction))
125 }
126
127 pub fn direction(&self) -> Vec3 {
129 self.inner.descriptor.get().direction
130 }
131
132 pub fn set_color(&self, color: Vec4) -> &Self {
134 self.inner.descriptor.modify(|d| d.color = color);
135 self
136 }
137
138 pub fn with_color(self, color: Vec4) -> Self {
140 self.set_color(color);
141 self
142 }
143
144 pub fn modify_color<T: 'static>(&self, f: impl FnOnce(&mut Vec4) -> T) -> T {
146 self.inner.descriptor.modify(|d| f(&mut d.color))
147 }
148
149 pub fn color(&self) -> Vec4 {
151 self.inner.descriptor.get().color
152 }
153
154 pub fn set_intensity(&self, intensity: Lux) -> &Self {
156 self.inner.descriptor.modify(|d| d.intensity = intensity);
157 self
158 }
159
160 pub fn with_intensity(self, intensity: Lux) -> Self {
162 self.set_intensity(intensity);
163 self
164 }
165
166 pub fn modify_intensity<T: 'static>(&self, f: impl FnOnce(&mut Lux) -> T) -> T {
168 self.inner.descriptor.modify(|d| f(&mut d.intensity))
169 }
170
171 pub fn intensity(&self) -> Lux {
173 self.inner.descriptor.get().intensity
174 }
175}
176
177#[derive(Clone, Debug)]
181pub struct PointLight {
182 descriptor: Hybrid<PointLightDescriptor>,
183}
184
185impl IsLight for PointLight {
186 fn style(&self) -> LightStyle {
187 LightStyle::Point
188 }
189
190 fn light_space_transforms(
191 &self,
192 t: &TransformDescriptor,
193 z_near: f32,
197 z_far: f32,
199 ) -> Vec<Mat4> {
200 let m = Mat4::from(*t);
201 let (p, vs) = self
202 .descriptor()
203 .shadow_mapping_projection_and_view_matrices(&m, z_near, z_far);
204 vs.into_iter().map(|v| p * v).collect()
205 }
206}
207
208impl PointLight {
209 pub fn id(&self) -> Id<PointLightDescriptor> {
211 self.descriptor.id()
212 }
213
214 pub fn descriptor(&self) -> PointLightDescriptor {
216 self.descriptor.get()
217 }
218}
219
220impl AnalyticalLight<PointLight> {
225 pub fn set_position(&self, position: Vec3) -> &Self {
227 self.inner.descriptor.modify(|d| d.position = position);
228 self
229 }
230
231 pub fn with_position(self, position: Vec3) -> Self {
233 self.set_position(position);
234 self
235 }
236
237 pub fn modify_position<T: 'static>(&self, f: impl FnOnce(&mut Vec3) -> T) -> T {
239 self.inner.descriptor.modify(|d| f(&mut d.position))
240 }
241
242 pub fn position(&self) -> Vec3 {
244 self.inner.descriptor.get().position
245 }
246
247 pub fn set_color(&self, color: Vec4) -> &Self {
249 self.inner.descriptor.modify(|d| d.color = color);
250 self
251 }
252
253 pub fn with_color(self, color: Vec4) -> Self {
255 self.set_color(color);
256 self
257 }
258
259 pub fn modify_color<T: 'static>(&self, f: impl FnOnce(&mut Vec4) -> T) -> T {
261 self.inner.descriptor.modify(|d| f(&mut d.color))
262 }
263
264 pub fn color(&self) -> Vec4 {
266 self.inner.descriptor.get().color
267 }
268
269 pub fn set_intensity(&self, intensity: Candela) -> &Self {
271 self.inner.descriptor.modify(|d| d.intensity = intensity);
272 self
273 }
274
275 pub fn with_intensity(self, intensity: Candela) -> Self {
277 self.set_intensity(intensity);
278 self
279 }
280
281 pub fn modify_intensity<T: 'static>(&self, f: impl FnOnce(&mut Candela) -> T) -> T {
283 self.inner.descriptor.modify(|d| f(&mut d.intensity))
284 }
285
286 pub fn intensity(&self) -> Candela {
288 self.inner.descriptor.get().intensity
289 }
290}
291
292#[derive(Clone, Debug)]
296pub struct SpotLight {
297 descriptor: Hybrid<SpotLightDescriptor>,
298}
299
300impl IsLight for SpotLight {
301 fn style(&self) -> LightStyle {
302 LightStyle::Spot
303 }
304
305 fn light_space_transforms(
306 &self,
307 t: &TransformDescriptor,
308 z_near: f32,
312 z_far: f32,
314 ) -> Vec<Mat4> {
315 let m = Mat4::from(*t);
316 vec![{
317 let (p, v) = self
318 .descriptor()
319 .shadow_mapping_projection_and_view(&m, z_near, z_far);
320 p * v
321 }]
322 }
323}
324
325impl SpotLight {
326 pub fn id(&self) -> Id<SpotLightDescriptor> {
328 self.descriptor.id()
329 }
330
331 pub fn descriptor(&self) -> SpotLightDescriptor {
333 self.descriptor.get()
334 }
335}
336
337impl AnalyticalLight<SpotLight> {
342 pub fn set_position(&self, position: Vec3) -> &Self {
344 self.inner.descriptor.modify(|d| d.position = position);
345 self
346 }
347
348 pub fn with_position(self, position: Vec3) -> Self {
350 self.set_position(position);
351 self
352 }
353
354 pub fn modify_position<T: 'static>(&self, f: impl FnOnce(&mut Vec3) -> T) -> T {
356 self.inner.descriptor.modify(|d| f(&mut d.position))
357 }
358
359 pub fn position(&self) -> Vec3 {
361 self.inner.descriptor.get().position
362 }
363
364 pub fn set_direction(&self, direction: Vec3) -> &Self {
366 self.inner.descriptor.modify(|d| d.direction = direction);
367 self
368 }
369
370 pub fn with_direction(self, direction: Vec3) -> Self {
372 self.set_direction(direction);
373 self
374 }
375
376 pub fn modify_direction<T: 'static>(&self, f: impl FnOnce(&mut Vec3) -> T) -> T {
378 self.inner.descriptor.modify(|d| f(&mut d.direction))
379 }
380
381 pub fn direction(&self) -> Vec3 {
383 self.inner.descriptor.get().direction
384 }
385
386 pub fn set_inner_cutoff(&self, inner_cutoff: f32) -> &Self {
388 self.inner
389 .descriptor
390 .modify(|d| d.inner_cutoff = inner_cutoff);
391 self
392 }
393
394 pub fn with_inner_cutoff(self, inner_cutoff: f32) -> Self {
396 self.set_inner_cutoff(inner_cutoff);
397 self
398 }
399
400 pub fn modify_inner_cutoff<T: 'static>(&self, f: impl FnOnce(&mut f32) -> T) -> T {
402 self.inner.descriptor.modify(|d| f(&mut d.inner_cutoff))
403 }
404
405 pub fn inner_cutoff(&self) -> f32 {
407 self.inner.descriptor.get().inner_cutoff
408 }
409
410 pub fn set_outer_cutoff(&self, outer_cutoff: f32) -> &Self {
412 self.inner
413 .descriptor
414 .modify(|d| d.outer_cutoff = outer_cutoff);
415 self
416 }
417
418 pub fn with_outer_cutoff(self, outer_cutoff: f32) -> Self {
420 self.set_outer_cutoff(outer_cutoff);
421 self
422 }
423
424 pub fn modify_outer_cutoff<T: 'static>(&self, f: impl FnOnce(&mut f32) -> T) -> T {
426 self.inner.descriptor.modify(|d| f(&mut d.outer_cutoff))
427 }
428
429 pub fn outer_cutoff(&self) -> f32 {
431 self.inner.descriptor.get().outer_cutoff
432 }
433
434 pub fn set_color(&self, color: Vec4) -> &Self {
436 self.inner.descriptor.modify(|d| d.color = color);
437 self
438 }
439
440 pub fn with_color(self, color: Vec4) -> Self {
442 self.set_color(color);
443 self
444 }
445
446 pub fn modify_color<T: 'static>(&self, f: impl FnOnce(&mut Vec4) -> T) -> T {
448 self.inner.descriptor.modify(|d| f(&mut d.color))
449 }
450
451 pub fn color(&self) -> Vec4 {
453 self.inner.descriptor.get().color
454 }
455
456 pub fn set_intensity(&self, intensity: Candela) -> &Self {
458 self.inner.descriptor.modify(|d| d.intensity = intensity);
459 self
460 }
461
462 pub fn with_intensity(self, intensity: Candela) -> Self {
464 self.set_intensity(intensity);
465 self
466 }
467
468 pub fn modify_intensity<T: 'static>(&self, f: impl FnOnce(&mut Candela) -> T) -> T {
470 self.inner.descriptor.modify(|d| f(&mut d.intensity))
471 }
472
473 pub fn intensity(&self) -> Candela {
475 self.inner.descriptor.get().intensity
476 }
477}
478
479#[derive(Clone)]
480pub enum Light {
481 Directional(DirectionalLight),
482 Point(PointLight),
483 Spot(SpotLight),
484}
485
486impl From<DirectionalLight> for Light {
487 fn from(light: DirectionalLight) -> Self {
488 Light::Directional(light)
489 }
490}
491
492impl From<PointLight> for Light {
493 fn from(light: PointLight) -> Self {
494 Light::Point(light)
495 }
496}
497
498impl From<SpotLight> for Light {
499 fn from(light: SpotLight) -> Self {
500 Light::Spot(light)
501 }
502}
503
504impl IsLight for Light {
505 fn style(&self) -> LightStyle {
506 match self {
507 Light::Directional(light) => light.style(),
508 Light::Point(light) => light.style(),
509 Light::Spot(light) => light.style(),
510 }
511 }
512
513 fn light_space_transforms(
514 &self,
515 parent_transform: &TransformDescriptor,
517 z_near: f32,
521 z_far: f32,
523 ) -> Vec<Mat4> {
524 match self {
525 Light::Directional(light) => {
526 light.light_space_transforms(parent_transform, z_near, z_far)
527 }
528 Light::Point(light) => light.light_space_transforms(parent_transform, z_near, z_far),
529 Light::Spot(light) => light.light_space_transforms(parent_transform, z_near, z_far),
530 }
531 }
532}
533
534#[derive(Clone)]
546pub struct AnalyticalLight<T = Light> {
547 pub(crate) light_descriptor: Hybrid<LightDescriptor>,
549 inner: T,
551 transform: Transform,
555 node_transform: Arc<RwLock<Option<NestedTransform>>>,
562}
563
564impl<T: IsLight> core::fmt::Display for AnalyticalLight<T> {
565 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
566 f.write_fmt(format_args!(
567 "AnalyticalLight type={} light-id={:?} node-nested-transform-global-id:{:?}",
568 self.inner.style(),
569 self.light_descriptor.id(),
570 self.node_transform
571 .read()
572 .unwrap()
573 .as_ref()
574 .map(|h| h.global_id())
575 ))
576 }
577}
578
579impl<T: IsLight> IsLight for AnalyticalLight<T> {
580 fn style(&self) -> LightStyle {
581 self.inner.style()
582 }
583
584 fn light_space_transforms(
585 &self,
586 parent_transform: &TransformDescriptor,
588 z_near: f32,
594 z_far: f32,
596 ) -> Vec<Mat4> {
597 self.inner
598 .light_space_transforms(parent_transform, z_near, z_far)
599 }
600}
601
602impl AnalyticalLight {
603 pub fn as_directional(&self) -> Option<&DirectionalLight> {
605 match &self.inner {
606 Light::Directional(light) => Some(light),
607 _ => None,
608 }
609 }
610
611 pub fn as_point(&self) -> Option<&PointLight> {
613 match &self.inner {
614 Light::Point(light) => Some(light),
615 _ => None,
616 }
617 }
618
619 pub fn as_spot(&self) -> Option<&SpotLight> {
621 match &self.inner {
622 Light::Spot(light) => Some(light),
623 _ => None,
624 }
625 }
626}
627
628impl<T: IsLight> AnalyticalLight<T> {
629 pub fn id(&self) -> Id<LightDescriptor> {
631 self.light_descriptor.id()
632 }
633
634 pub fn descriptor(&self) -> LightDescriptor {
636 self.light_descriptor.get()
637 }
638
639 pub fn link_node_transform(&self, transform: &NestedTransform) {
641 *self.node_transform.write().unwrap() = Some(transform.clone());
642 }
643
644 pub fn inner(&self) -> &T {
646 &self.inner
647 }
648
649 pub fn transform(&self) -> &Transform {
658 &self.transform
659 }
660
661 pub fn linked_node_transform(&self) -> Option<NestedTransform> {
668 self.node_transform
669 .read()
670 .unwrap()
671 .as_ref()
672 .map(|t| t.clone())
673 }
674
675 pub fn into_generic(self) -> AnalyticalLight
679 where
680 Light: From<T>,
681 {
682 let AnalyticalLight {
683 light_descriptor,
684 inner,
685 transform,
686 node_transform,
687 } = self;
688 let inner = Light::from(inner);
689 AnalyticalLight {
690 light_descriptor,
691 inner,
692 transform,
693 node_transform,
694 }
695 }
696}
697
698#[derive(Clone)]
700pub struct Lighting {
701 pub(crate) geometry_slab: SlabAllocator<WgpuRuntime>,
702 pub(crate) light_slab: SlabAllocator<WgpuRuntime>,
703 pub(crate) light_slab_buffer: Arc<RwLock<SlabBuffer<wgpu::Buffer>>>,
704 pub(crate) geometry_slab_buffer: Arc<RwLock<SlabBuffer<wgpu::Buffer>>>,
705 pub(crate) lighting_descriptor: Hybrid<LightingDescriptor>,
706 pub(crate) analytical_lights: Arc<RwLock<Vec<AnalyticalLight>>>,
707 pub(crate) analytical_lights_array: Arc<RwLock<Option<HybridArray<Id<LightDescriptor>>>>>,
708 pub(crate) shadow_map_update_pipeline: Arc<wgpu::RenderPipeline>,
709 pub(crate) shadow_map_update_bindgroup_layout: Arc<wgpu::BindGroupLayout>,
710 pub(crate) shadow_map_update_blitter: AtlasBlitter,
711 pub(crate) shadow_map_atlas: Atlas,
712}
713
714pub struct LightingBindGroupLayoutEntries {
715 pub light_slab: wgpu::BindGroupLayoutEntry,
716 pub shadow_map_image: wgpu::BindGroupLayoutEntry,
717 pub shadow_map_sampler: wgpu::BindGroupLayoutEntry,
718}
719
720impl LightingBindGroupLayoutEntries {
721 pub fn new(starting_binding: u32) -> Self {
722 Self {
723 light_slab: wgpu::BindGroupLayoutEntry {
724 binding: starting_binding,
725 visibility: wgpu::ShaderStages::FRAGMENT,
726 ty: wgpu::BindingType::Buffer {
727 ty: wgpu::BufferBindingType::Storage { read_only: true },
728 has_dynamic_offset: false,
729 min_binding_size: None,
730 },
731 count: None,
732 },
733 shadow_map_image: wgpu::BindGroupLayoutEntry {
734 binding: starting_binding + 1,
735 visibility: wgpu::ShaderStages::FRAGMENT,
736 ty: wgpu::BindingType::Texture {
737 sample_type: wgpu::TextureSampleType::Float { filterable: false },
738 view_dimension: wgpu::TextureViewDimension::D2Array,
739 multisampled: false,
740 },
741 count: None,
742 },
743 shadow_map_sampler: wgpu::BindGroupLayoutEntry {
744 binding: starting_binding + 2,
745 visibility: wgpu::ShaderStages::FRAGMENT,
746 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
747 count: None,
748 },
749 }
750 }
751}
752
753impl Lighting {
754 fn create_shadow_map_atlas(
756 light_slab: &SlabAllocator<WgpuRuntime>,
757 size: wgpu::Extent3d,
758 ) -> Atlas {
759 let usage = wgpu::TextureUsages::RENDER_ATTACHMENT
760 | wgpu::TextureUsages::TEXTURE_BINDING
761 | wgpu::TextureUsages::COPY_SRC;
762 Atlas::new(
763 light_slab,
764 size,
765 Some(wgpu::TextureFormat::R32Float),
766 Some("shadow-map-atlas"),
767 Some(usage),
768 )
769 }
770
771 pub fn new(atlas_size: wgpu::Extent3d, geometry: &Geometry) -> Self {
773 let runtime = geometry.runtime();
774 let light_slab = SlabAllocator::new(runtime, "light-slab", wgpu::BufferUsages::empty());
775 let lighting_descriptor = light_slab.new_value(LightingDescriptor::default());
776 let light_slab_buffer = light_slab.commit();
777 let shadow_map_update_bindgroup_layout: Arc<_> =
778 ShadowMap::create_update_bindgroup_layout(&runtime.device).into();
779 let shadow_map_update_pipeline =
780 ShadowMap::create_update_pipeline(&runtime.device, &shadow_map_update_bindgroup_layout)
781 .into();
782 Self {
783 shadow_map_atlas: Self::create_shadow_map_atlas(&light_slab, atlas_size),
784 analytical_lights: Default::default(),
785 analytical_lights_array: Default::default(),
786 geometry_slab: geometry.slab_allocator().clone(),
787 light_slab,
788 light_slab_buffer: Arc::new(RwLock::new(light_slab_buffer)),
789 lighting_descriptor,
790 geometry_slab_buffer: Arc::new(RwLock::new(geometry.slab_allocator().commit())),
791 shadow_map_update_pipeline,
792 shadow_map_update_bindgroup_layout,
793 shadow_map_update_blitter: AtlasBlitter::new(
794 &runtime.device,
795 wgpu::TextureFormat::R32Float,
796 wgpu::FilterMode::Nearest,
797 ),
798 }
799 }
800
801 pub fn slab_allocator(&self) -> &SlabAllocator<WgpuRuntime> {
802 &self.light_slab
803 }
804
805 pub fn add_light<T>(&self, bundle: &AnalyticalLight<T>)
816 where
817 T: IsLight,
818 Light: From<T>,
819 {
820 log::trace!(
821 "adding light {:?} ({})",
822 bundle.light_descriptor.id(),
823 bundle.inner.style()
824 );
825 self.analytical_lights
827 .write()
828 .unwrap()
829 .push(bundle.clone().into_generic());
830 *self.analytical_lights_array.write().unwrap() = None;
832 }
833
834 pub fn remove_light<T: IsLight>(&self, bundle: &AnalyticalLight<T>) {
840 log::trace!(
841 "removing light {:?} ({})",
842 bundle.light_descriptor.id(),
843 bundle.inner.style()
844 );
845 let mut guard = self.analytical_lights.write().unwrap();
847 guard.retain(|stored_light| {
848 stored_light.light_descriptor.id() != bundle.light_descriptor.id()
849 });
850 *self.analytical_lights_array.write().unwrap() = None;
851 }
852
853 pub fn lights(&self) -> Vec<AnalyticalLight> {
855 self.analytical_lights.read().unwrap().clone()
856 }
857
858 pub fn new_directional_light(&self) -> AnalyticalLight<DirectionalLight> {
862 let descriptor = self
863 .light_slab
864 .new_value(DirectionalLightDescriptor::default());
865 let transform = Transform::new(&self.light_slab);
866 let light_descriptor = self.light_slab.new_value({
867 let mut light = LightDescriptor::from(descriptor.id());
868 light.transform_id = transform.id();
869 light
870 });
871
872 let bundle = AnalyticalLight {
873 light_descriptor,
874 inner: DirectionalLight { descriptor },
875 transform,
876 node_transform: Default::default(),
877 };
878 self.add_light(&bundle);
879
880 bundle
881 }
882
883 pub fn new_point_light(&self) -> AnalyticalLight<PointLight> {
887 let descriptor = self.light_slab.new_value(PointLightDescriptor::default());
888 let transform = Transform::new(&self.light_slab);
889 let light_descriptor = self.light_slab.new_value({
890 let mut light = LightDescriptor::from(descriptor.id());
891 light.transform_id = transform.id();
892 light
893 });
894
895 let bundle = AnalyticalLight {
896 light_descriptor,
897 inner: PointLight { descriptor },
898 transform,
899 node_transform: Default::default(),
900 };
901 self.add_light(&bundle);
902
903 bundle
904 }
905
906 pub fn new_spot_light(&self) -> AnalyticalLight<SpotLight> {
910 let descriptor = self.light_slab.new_value(SpotLightDescriptor::default());
911 let transform = Transform::new(&self.light_slab);
912 let light_descriptor = self.light_slab.new_value({
913 let mut light = LightDescriptor::from(descriptor.id());
914 light.transform_id = transform.id();
915 light
916 });
917
918 let bundle = AnalyticalLight {
919 light_descriptor,
920 inner: SpotLight { descriptor },
921 transform,
922 node_transform: Default::default(),
923 };
924 self.add_light(&bundle);
925
926 bundle
927 }
928
929 pub fn new_shadow_map<T>(
932 &self,
933 analytical_light_bundle: &AnalyticalLight<T>,
934 size: UVec2,
936 z_near: f32,
940 z_far: f32,
944 ) -> Result<ShadowMap, LightingError>
945 where
946 T: IsLight,
947 Light: From<T>,
948 {
949 ShadowMap::new(self, analytical_light_bundle, size, z_near, z_far)
950 }
951
952 #[must_use]
953 pub fn commit(&self) -> SlabBuffer<wgpu::Buffer> {
954 log::trace!("committing lights");
955
956 for light in self.analytical_lights.read().unwrap().iter() {
958 if let Some(node_transform) = light.node_transform.read().unwrap().as_ref() {
959 let global_node_transform = node_transform.global_descriptor();
960 if node_transform.global_descriptor() != light.transform.descriptor() {
961 light.transform.set_descriptor(global_node_transform);
962 }
963 }
964 }
965
966 let lights_array = {
967 let mut array_guard = self.analytical_lights_array.write().unwrap();
968
969 array_guard
972 .get_or_insert_with(|| {
973 log::trace!(" analytical lights array was invalidated");
974 let lights_guard = self.analytical_lights.read().unwrap();
975 let new_lights = lights_guard
976 .iter()
977 .map(|bundle| bundle.light_descriptor.id());
978 let array = self.light_slab.new_array(new_lights);
979 log::trace!(" lights array is now: {:?}", array.array());
980 array
981 })
982 .array()
983 };
984
985 self.lighting_descriptor.modify(
986 |LightingDescriptor {
987 analytical_lights_array,
988 shadow_map_atlas_descriptor_id,
989 update_shadow_map_id,
990 update_shadow_map_texture_index,
991 light_tiling_descriptor_id: _,
993 }| {
994 *analytical_lights_array = lights_array;
995 *shadow_map_atlas_descriptor_id = self.shadow_map_atlas.descriptor_id();
996 *update_shadow_map_id = Id::NONE;
997 *update_shadow_map_texture_index = 0;
998 },
999 );
1000
1001 let buffer = self.light_slab.commit();
1002 log::trace!(" light slab creation time: {}", buffer.creation_time());
1003 buffer
1004 }
1005}
1006
1007#[cfg(test)]
1008mod test;