1use wgpu::util::DeviceExt;
2use engvis_core::LightingEnvironment;
3
4pub const MAX_DIR_LIGHTS: usize = 4;
5pub const MAX_POINT_LIGHTS: usize = 16;
6
7#[repr(C)]
9#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
10pub struct LightingUniforms {
11 pub ambient_color: [f32; 4],
12 pub dir_light_count: u32,
13 pub point_light_count: u32,
14 pub _pad0: u32,
15 pub _pad1: u32,
16}
17
18#[repr(C)]
20#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
21pub struct DirectionalLightData {
22 pub direction: [f32; 4],
23 pub color: [f32; 4],
24}
25
26#[repr(C)]
28#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
29pub struct PointLightData {
30 pub position: [f32; 4],
31 pub color: [f32; 4],
32}
33
34pub struct LightingBuffer {
35 pub uniform_buffer: wgpu::Buffer,
36 pub dir_light_buffer: wgpu::Buffer,
37 pub point_light_buffer: wgpu::Buffer,
38 pub bind_group: wgpu::BindGroup,
39 pub bind_group_layout: wgpu::BindGroupLayout,
40}
41
42impl LightingBuffer {
43 pub fn new(device: &wgpu::Device, lighting: &LightingEnvironment) -> Self {
44 let uniform_data = Self::build_uniforms(lighting);
45 let dir_lights = Self::build_dir_lights(lighting);
46 let point_lights = Self::build_point_lights(lighting);
47
48 let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
49 label: Some("Lighting Uniform Buffer"),
50 contents: bytemuck::cast_slice(&[uniform_data]),
51 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
52 });
53
54 let dir_light_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
55 label: Some("Dir Light Storage Buffer"),
56 contents: bytemuck::cast_slice(&dir_lights),
57 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
58 });
59
60 let point_light_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
61 label: Some("Point Light Storage Buffer"),
62 contents: bytemuck::cast_slice(&point_lights),
63 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
64 });
65
66 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
67 label: Some("Lighting Bind Group Layout"),
68 entries: &[
69 wgpu::BindGroupLayoutEntry {
70 binding: 0,
71 visibility: wgpu::ShaderStages::FRAGMENT,
72 ty: wgpu::BindingType::Buffer {
73 ty: wgpu::BufferBindingType::Uniform,
74 has_dynamic_offset: false,
75 min_binding_size: None,
76 },
77 count: None,
78 },
79 wgpu::BindGroupLayoutEntry {
80 binding: 1,
81 visibility: wgpu::ShaderStages::FRAGMENT,
82 ty: wgpu::BindingType::Buffer {
83 ty: wgpu::BufferBindingType::Storage { read_only: true },
84 has_dynamic_offset: false,
85 min_binding_size: None,
86 },
87 count: None,
88 },
89 wgpu::BindGroupLayoutEntry {
90 binding: 2,
91 visibility: wgpu::ShaderStages::FRAGMENT,
92 ty: wgpu::BindingType::Buffer {
93 ty: wgpu::BufferBindingType::Storage { read_only: true },
94 has_dynamic_offset: false,
95 min_binding_size: None,
96 },
97 count: None,
98 },
99 ],
100 });
101
102 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
103 label: Some("Lighting Bind Group"),
104 layout: &bind_group_layout,
105 entries: &[
106 wgpu::BindGroupEntry {
107 binding: 0,
108 resource: uniform_buffer.as_entire_binding(),
109 },
110 wgpu::BindGroupEntry {
111 binding: 1,
112 resource: dir_light_buffer.as_entire_binding(),
113 },
114 wgpu::BindGroupEntry {
115 binding: 2,
116 resource: point_light_buffer.as_entire_binding(),
117 },
118 ],
119 });
120
121 Self {
122 uniform_buffer,
123 dir_light_buffer,
124 point_light_buffer,
125 bind_group,
126 bind_group_layout,
127 }
128 }
129
130 pub fn update(&self, queue: &wgpu::Queue, lighting: &LightingEnvironment) {
131 let uniforms = Self::build_uniforms(lighting);
132 queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
133
134 let dir_lights = Self::build_dir_lights(lighting);
135 queue.write_buffer(&self.dir_light_buffer, 0, bytemuck::cast_slice(&dir_lights));
136
137 let point_lights = Self::build_point_lights(lighting);
138 queue.write_buffer(
139 &self.point_light_buffer,
140 0,
141 bytemuck::cast_slice(&point_lights),
142 );
143 }
144
145 fn build_uniforms(lighting: &LightingEnvironment) -> LightingUniforms {
146 LightingUniforms {
147 ambient_color: [
148 lighting.ambient.color[0],
149 lighting.ambient.color[1],
150 lighting.ambient.color[2],
151 lighting.ambient.intensity,
152 ],
153 dir_light_count: lighting.directional_lights.len().min(MAX_DIR_LIGHTS) as u32,
154 point_light_count: lighting.point_lights.len().min(MAX_POINT_LIGHTS) as u32,
155 _pad0: 0,
156 _pad1: 0,
157 }
158 }
159
160 fn build_dir_lights(lighting: &LightingEnvironment) -> Vec<DirectionalLightData> {
161 let mut lights = vec![
162 DirectionalLightData {
163 direction: [0.0; 4],
164 color: [0.0; 4],
165 };
166 MAX_DIR_LIGHTS
167 ];
168 for (i, l) in lighting
169 .directional_lights
170 .iter()
171 .take(MAX_DIR_LIGHTS)
172 .enumerate()
173 {
174 lights[i] = DirectionalLightData {
175 direction: [l.direction.x, l.direction.y, l.direction.z, l.intensity],
176 color: [l.color[0], l.color[1], l.color[2], 0.0],
177 };
178 }
179 lights
180 }
181
182 fn build_point_lights(lighting: &LightingEnvironment) -> Vec<PointLightData> {
183 let mut lights = vec![
184 PointLightData {
185 position: [0.0; 4],
186 color: [0.0; 4],
187 };
188 MAX_POINT_LIGHTS
189 ];
190 for (i, l) in lighting
191 .point_lights
192 .iter()
193 .take(MAX_POINT_LIGHTS)
194 .enumerate()
195 {
196 lights[i] = PointLightData {
197 position: [l.position.x, l.position.y, l.position.z, l.range],
198 color: [l.color[0], l.color[1], l.color[2], l.intensity],
199 };
200 }
201 lights
202 }
203}