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