oxide_engine/light/
uniform.rs1use bytemuck::{Pod, Zeroable};
4
5use super::{AmbientLight, DirectionalLight};
6
7pub const MAX_DIRECTIONAL_LIGHTS: usize = 4;
8pub const MAX_POINT_LIGHTS: usize = 4096;
9
10#[repr(C, align(16))]
11#[derive(Copy, Clone, Debug, Pod, Zeroable)]
12pub struct GpuDirectionalLight {
13 pub direction: [f32; 4],
14 pub color_intensity: [f32; 4],
15}
16
17impl From<&DirectionalLight> for GpuDirectionalLight {
18 fn from(light: &DirectionalLight) -> Self {
19 Self {
20 direction: [light.direction.x, light.direction.y, light.direction.z, 0.0],
21 color_intensity: [light.color.x, light.color.y, light.color.z, light.intensity],
22 }
23 }
24}
25
26#[repr(C, align(16))]
28#[derive(Copy, Clone, Debug, Pod, Zeroable)]
29pub struct GpuPointLight {
30 pub position_radius: [f32; 4],
31 pub color_intensity: [f32; 4],
32 pub _padding: [f32; 4],
33}
34
35impl GpuPointLight {
36 pub fn from_values(
37 position: glam::Vec3,
38 color: glam::Vec3,
39 intensity: f32,
40 radius: f32,
41 ) -> Self {
42 Self {
43 position_radius: [position.x, position.y, position.z, radius],
44 color_intensity: [color.x, color.y, color.z, intensity],
45 _padding: [0.0; 4],
46 }
47 }
48}
49
50#[repr(C, align(16))]
51#[derive(Copy, Clone, Debug, Pod, Zeroable)]
52pub struct LightUniform {
53 pub ambient_color_intensity: [f32; 4],
54 pub directional_count: u32,
55 pub point_count: u32,
56 pub _padding: [u32; 2],
57 pub directional_lights: [GpuDirectionalLight; MAX_DIRECTIONAL_LIGHTS],
58}
59
60impl Default for LightUniform {
61 fn default() -> Self {
62 Self {
63 ambient_color_intensity: [0.2, 0.2, 0.2, 0.2],
64 directional_count: 0,
65 point_count: 0,
66 _padding: [0; 2],
67 directional_lights: [GpuDirectionalLight::zeroed(); MAX_DIRECTIONAL_LIGHTS],
68 }
69 }
70}
71
72impl LightUniform {
73 pub fn new(
74 ambient: Option<&AmbientLight>,
75 directional_lights: &[&DirectionalLight],
76 point_count: usize,
77 ) -> Self {
78 let mut uniform = Self::default();
79
80 if let Some(ambient) = ambient {
81 uniform.ambient_color_intensity = [
82 ambient.color.x,
83 ambient.color.y,
84 ambient.color.z,
85 ambient.intensity,
86 ];
87 }
88
89 uniform.directional_count =
90 (directional_lights.len() as u32).min(MAX_DIRECTIONAL_LIGHTS as u32);
91 for (i, light) in directional_lights
92 .iter()
93 .take(MAX_DIRECTIONAL_LIGHTS)
94 .enumerate()
95 {
96 uniform.directional_lights[i] = GpuDirectionalLight::from(*light);
97 }
98
99 uniform.point_count = (point_count as u32).min(MAX_POINT_LIGHTS as u32);
100 uniform
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 fn assert_pod<T: Pod>() {}
109
110 #[test]
111 fn storage_and_uniform_alignment_contracts() {
112 assert_pod::<GpuDirectionalLight>();
113 assert_pod::<GpuPointLight>();
114 assert_pod::<LightUniform>();
115
116 assert_eq!(std::mem::size_of::<GpuDirectionalLight>() % 16, 0);
117 assert_eq!(std::mem::size_of::<GpuPointLight>() % 16, 0);
118 assert_eq!(std::mem::size_of::<LightUniform>() % 16, 0);
119 assert_eq!(std::mem::size_of::<GpuPointLight>(), 48);
120
121 assert_eq!(std::mem::align_of::<GpuDirectionalLight>(), 16);
122 assert_eq!(std::mem::align_of::<GpuPointLight>(), 16);
123 assert_eq!(std::mem::align_of::<LightUniform>(), 16);
124 }
125}