wde_pbr/deferred/lights/
lights_binding.rs

1use wde_logger::prelude::*;
2
3use bevy::{ecs::system::SystemParamItem, prelude::*};
4use wde_renderer::prelude::*;
5
6use crate::deferred::lights::*;
7
8/// Maximum number of lights in the scene.
9pub const MAX_LIGHTS: usize = 64;
10
11pub(crate) struct LightsBindingPlugin;
12impl Plugin for LightsBindingPlugin {
13    fn build(&self, app: &mut App) {
14        // Add the render bindings
15        app.add_plugins(RenderDataRegisterPlugin::<LightsData>::default());
16
17        // Add the systems to extract the lights from the world, and to update the lights buffer
18        app.get_sub_app_mut(RenderApp)
19            .unwrap()
20            .init_resource::<ExtractedLights>()
21            .add_systems(Extract, extract)
22            .add_systems(Render, update_lights_buffer.in_set(RenderSet::Prepare));
23    }
24}
25
26#[derive(Asset, Clone, TypePath, Default)]
27pub struct LightsData;
28impl LightsData {
29    pub const LIGHTS_BUFFER_IDX: u32 = 0;
30    pub const LIGHTS_BUFFER_STAGING_IDX: u32 = 1;
31}
32impl RenderData for LightsData {
33    type Params = ();
34
35    fn describe(_params: &mut SystemParamItem<Self::Params>, builder: &mut RenderDataBuilder) {
36        builder
37            .add_buffer(
38                Self::LIGHTS_BUFFER_IDX,
39                Buffer {
40                    label: "lights-buffer-gpu".to_string(),
41                    size: std::mem::size_of::<LightsStorageElement>() * MAX_LIGHTS,
42                    usage: BufferUsage::STORAGE | BufferUsage::COPY_DST,
43                    content: None
44                }
45            )
46            .add_buffer(
47                Self::LIGHTS_BUFFER_STAGING_IDX,
48                Buffer {
49                    label: "lights-buffer-staging".to_string(),
50                    size: std::mem::size_of::<LightsStorageElement>() * MAX_LIGHTS,
51                    usage: BufferUsage::COPY_SRC | BufferUsage::COPY_DST,
52                    content: None
53                }
54            );
55    }
56}
57
58#[derive(Resource, Default)]
59struct ExtractedLights {
60    pub directional_lights: Vec<DirectionalLight>,
61    pub point_lights: Vec<PointLight>,
62    pub spot_lights: Vec<SpotLight>
63}
64
65fn extract(
66    lights_directional: ExtractWorld<Query<&DirectionalLight>>,
67    lights_point: ExtractWorld<Query<&PointLight>>,
68    lights_spot: ExtractWorld<Query<&SpotLight>>,
69    mut extracted_lights: ResMut<ExtractedLights>
70) {
71    // Extract lights each frame. This is necessary to keep the lights buffer up to date, and to handle dynamic lights.
72    extracted_lights.directional_lights = lights_directional.iter().copied().collect();
73    extracted_lights.point_lights = lights_point.iter().copied().collect();
74    extracted_lights.spot_lights = lights_spot.iter().copied().collect();
75}
76
77fn update_lights_buffer(
78    lights_buffer: ResRenderData<LightsData>,
79    buffers: Res<RenderAssets<GpuBuffer>>,
80    render_instance: Res<RenderInstance>,
81    extracted_lights: Res<ExtractedLights>
82) {
83    // Get the lights buffer
84    let lights_buffer_cpu = match lights_buffer.iter().next() {
85        Some((_, buffer)) => match buffers.get(
86            &buffer
87                .get_buffer(LightsData::LIGHTS_BUFFER_STAGING_IDX)
88                .unwrap()
89        ) {
90            Some(lights_buffer) => lights_buffer,
91            None => return
92        },
93        None => return
94    };
95
96    let render_instance = render_instance.0.read().unwrap();
97    let mut offset = 0;
98    for light in extracted_lights.directional_lights.iter() {
99        let data = LightsStorageElement::from_directional(light);
100        lights_buffer_cpu.buffer.write(
101            &render_instance,
102            bytemuck::bytes_of(&data),
103            offset * std::mem::size_of::<LightsStorageElement>()
104        );
105        offset += 1;
106    }
107    for light in extracted_lights.point_lights.iter() {
108        let data = LightsStorageElement::from_point(light);
109        lights_buffer_cpu.buffer.write(
110            &render_instance,
111            bytemuck::bytes_of(&data),
112            offset * std::mem::size_of::<LightsStorageElement>()
113        );
114        offset += 1;
115    }
116    for light in extracted_lights.spot_lights.iter() {
117        let data = LightsStorageElement::from_spot(light);
118        lights_buffer_cpu.buffer.write(
119            &render_instance,
120            bytemuck::bytes_of(&data),
121            offset * std::mem::size_of::<LightsStorageElement>()
122        );
123        offset += 1;
124    }
125    if offset > MAX_LIGHTS {
126        warn!(
127            "Number of lights exceeded the maximum of {}. Some lights will be ignored in rendering.",
128            MAX_LIGHTS
129        );
130    }
131
132    // Update the buffer
133    let lights_buffer_gpu = match buffers.get(
134        &lights_buffer
135            .iter()
136            .next()
137            .unwrap()
138            .1
139            .get_buffer(LightsData::LIGHTS_BUFFER_IDX)
140            .unwrap()
141    ) {
142        Some(buffer) => buffer,
143        None => return
144    };
145    lights_buffer_gpu
146        .buffer
147        .copy_from_buffer(&render_instance, &lights_buffer_cpu.buffer);
148}