wde_pbr/deferred/lights/
lights_binding.rs1use wde_logger::prelude::*;
2
3use bevy::{ecs::system::SystemParamItem, prelude::*};
4use wde_renderer::prelude::*;
5
6use crate::deferred::lights::*;
7use crate::prelude::{AtmosphereParamsUniform, AtmosphereSettings, PbrParamsUniform, PbrSettings};
8
9pub const MAX_LIGHTS: usize = 64;
11
12pub(crate) struct LightsBindingPlugin;
13impl Plugin for LightsBindingPlugin {
14 fn build(&self, app: &mut App) {
15 app.init_resource::<AtmosphereSettings>()
17 .register_type::<AtmosphereSettings>()
18 .init_resource::<PbrSettings>()
19 .register_type::<PbrSettings>()
20 .add_plugins(RenderDataRegisterPlugin::<LightsData>::default());
21
22 app.get_sub_app_mut(RenderApp)
24 .unwrap()
25 .init_resource::<ExtractedLights>()
26 .init_resource::<AtmosphereParamsUniform>()
27 .init_resource::<PbrParamsUniform>()
28 .add_systems(Extract, extract)
29 .add_systems(Extract, extract_atmosphere_params)
30 .add_systems(Extract, extract_pbr_params)
31 .add_systems(Render, update_lights_buffer.in_set(RenderSet::Prepare));
32 }
33}
34
35#[derive(Asset, Clone, TypePath, Default)]
36pub struct LightsData;
37impl LightsData {
38 pub const LIGHTS_BUFFER_IDX: u32 = 0;
39 pub const LIGHTS_BUFFER_STAGING_IDX: u32 = 1;
40 pub const ATMOSPHERE_PARAMS_BUFFER_IDX: u32 = 2;
41 pub const PBR_PARAMS_BUFFER_IDX: u32 = 3;
42}
43impl RenderData for LightsData {
44 type Params = ();
45
46 fn describe(_params: &mut SystemParamItem<Self::Params>, builder: &mut RenderDataBuilder) {
47 builder
48 .add_buffer(
49 Self::LIGHTS_BUFFER_IDX,
50 Buffer {
51 label: "lights-buffer-gpu".to_string(),
52 size: std::mem::size_of::<LightsStorageElement>() * MAX_LIGHTS,
53 usage: BufferUsage::STORAGE | BufferUsage::COPY_DST,
54 content: None
55 }
56 )
57 .add_buffer(
58 Self::LIGHTS_BUFFER_STAGING_IDX,
59 Buffer {
60 label: "lights-buffer-staging".to_string(),
61 size: std::mem::size_of::<LightsStorageElement>() * MAX_LIGHTS,
62 usage: BufferUsage::COPY_SRC | BufferUsage::COPY_DST,
63 content: None
64 }
65 )
66 .add_buffer(
67 Self::ATMOSPHERE_PARAMS_BUFFER_IDX,
68 Buffer {
69 label: "atmosphere-params".to_string(),
70 size: std::mem::size_of::<AtmosphereParamsUniform>(),
71 usage: BufferUsage::UNIFORM | BufferUsage::COPY_DST,
72 content: Some(
73 bytemuck::cast_slice(&[AtmosphereParamsUniform::default()]).to_vec()
74 )
75 }
76 )
77 .add_buffer(
78 Self::PBR_PARAMS_BUFFER_IDX,
79 Buffer {
80 label: "pbr-params".to_string(),
81 size: std::mem::size_of::<PbrParamsUniform>(),
82 usage: BufferUsage::UNIFORM | BufferUsage::COPY_DST,
83 content: Some(bytemuck::cast_slice(&[PbrParamsUniform::default()]).to_vec())
84 }
85 );
86 }
87}
88
89#[derive(Resource, Default)]
90struct ExtractedLights {
91 pub directional_lights: Vec<DirectionalLight>,
92 pub point_lights: Vec<PointLight>,
93 pub spot_lights: Vec<SpotLight>
94}
95
96fn extract(
97 lights_directional: ExtractWorld<Query<&DirectionalLight>>,
98 lights_point: ExtractWorld<Query<&PointLight>>,
99 lights_spot: ExtractWorld<Query<&SpotLight>>,
100 mut extracted_lights: ResMut<ExtractedLights>
101) {
102 extracted_lights.directional_lights = lights_directional.iter().copied().collect();
104 extracted_lights.point_lights = lights_point.iter().copied().collect();
105 extracted_lights.spot_lights = lights_spot.iter().copied().collect();
106}
107
108fn extract_atmosphere_params(
109 settings: ExtractWorld<Res<AtmosphereSettings>>,
110 time: ExtractWorld<Res<Time>>,
111 mut uniform: ResMut<AtmosphereParamsUniform>
112) {
113 let day_length = settings.day_length_seconds.max(1.0);
114 let time_of_day = if settings.animate_time {
115 (time.elapsed_secs() / day_length).fract()
116 } else {
117 settings.time_of_day.fract()
118 };
119 *uniform = AtmosphereParamsUniform::from_settings(&settings, time_of_day);
120}
121
122fn extract_pbr_params(
123 settings: ExtractWorld<Res<PbrSettings>>,
124 mut uniform: ResMut<PbrParamsUniform>
125) {
126 *uniform = PbrParamsUniform::from_settings(&settings);
127}
128
129fn update_lights_buffer(
130 lights_buffer: ResRenderData<LightsData>,
131 buffers: Res<RenderAssets<GpuBuffer>>,
132 render_instance: Res<RenderInstance>,
133 extracted_lights: Res<ExtractedLights>,
134 atmosphere_uniform: Res<AtmosphereParamsUniform>,
135 pbr_uniform: Res<PbrParamsUniform>
136) {
137 let lights_buffer_cpu = match lights_buffer.iter().next() {
139 Some((_, buffer)) => match buffers.get(
140 &buffer
141 .get_buffer(LightsData::LIGHTS_BUFFER_STAGING_IDX)
142 .unwrap()
143 ) {
144 Some(lights_buffer) => lights_buffer,
145 None => return
146 },
147 None => return
148 };
149
150 let render_instance = render_instance.0.read().unwrap();
151 let mut offset = 0;
152 for light in extracted_lights.directional_lights.iter() {
153 let data = LightsStorageElement::from_directional(light);
154 lights_buffer_cpu.buffer.write(
155 &render_instance,
156 bytemuck::bytes_of(&data),
157 offset * std::mem::size_of::<LightsStorageElement>()
158 );
159 offset += 1;
160 }
161 for light in extracted_lights.point_lights.iter() {
162 let data = LightsStorageElement::from_point(light);
163 lights_buffer_cpu.buffer.write(
164 &render_instance,
165 bytemuck::bytes_of(&data),
166 offset * std::mem::size_of::<LightsStorageElement>()
167 );
168 offset += 1;
169 }
170 for light in extracted_lights.spot_lights.iter() {
171 let data = LightsStorageElement::from_spot(light);
172 lights_buffer_cpu.buffer.write(
173 &render_instance,
174 bytemuck::bytes_of(&data),
175 offset * std::mem::size_of::<LightsStorageElement>()
176 );
177 offset += 1;
178 }
179 if offset > MAX_LIGHTS {
180 warn!(
181 "Number of lights exceeded the maximum of {}. Some lights will be ignored in rendering.",
182 MAX_LIGHTS
183 );
184 }
185
186 let lights_buffer_gpu = match buffers.get(
188 &lights_buffer
189 .iter()
190 .next()
191 .unwrap()
192 .1
193 .get_buffer(LightsData::LIGHTS_BUFFER_IDX)
194 .unwrap()
195 ) {
196 Some(buffer) => buffer,
197 None => return
198 };
199 lights_buffer_gpu
200 .buffer
201 .copy_from_buffer(&render_instance, &lights_buffer_cpu.buffer);
202
203 let atmosphere_buffer = match buffers.get(
205 &lights_buffer
206 .iter()
207 .next()
208 .unwrap()
209 .1
210 .get_buffer(LightsData::ATMOSPHERE_PARAMS_BUFFER_IDX)
211 .unwrap()
212 ) {
213 Some(buffer) => buffer,
214 None => return
215 };
216 atmosphere_buffer.buffer.write(
217 &render_instance,
218 bytemuck::cast_slice(&[*atmosphere_uniform]),
219 0
220 );
221
222 let pbr_buffer = match buffers.get(
223 &lights_buffer
224 .iter()
225 .next()
226 .unwrap()
227 .1
228 .get_buffer(LightsData::PBR_PARAMS_BUFFER_IDX)
229 .unwrap()
230 ) {
231 Some(buffer) => buffer,
232 None => return
233 };
234 pbr_buffer
235 .buffer
236 .write(&render_instance, bytemuck::cast_slice(&[*pbr_uniform]), 0);
237}