|
| 1 | +use std::sync::{Mutex, OnceLock}; |
| 2 | + |
| 3 | +use gloog_core::types::{ |
| 4 | + BufferTarget, |
| 5 | + BufferUsage, |
| 6 | + DrawMode, |
| 7 | + ProgramID, |
| 8 | + UniformLocation, |
| 9 | + VertexArrayID, |
| 10 | + VertexAttribType, |
| 11 | +}; |
| 12 | +use gloog_core::GLContext; |
| 13 | +use gloog_math::{Mat4, Vec3, Vec4}; |
| 14 | + |
| 15 | +use crate::{scale_matrix, trans_matrix}; |
| 16 | + |
| 17 | +#[allow(unused)] |
| 18 | +const MAX_LIGHTS: u32 = 16; |
| 19 | + |
| 20 | + |
| 21 | +pub struct Light<'gl> { |
| 22 | + gl: &'gl GLContext, |
| 23 | + id: usize, |
| 24 | + pub ambient: Vec4, |
| 25 | + pub diffuse: Vec4, |
| 26 | + pub specular: Vec4, |
| 27 | + pub position: Vec3, |
| 28 | + pub draw_color: Vec4, |
| 29 | + info: &'static StaticLightInfo, |
| 30 | + uniforms: LightUniforms, |
| 31 | +} |
| 32 | + |
| 33 | +struct StaticLightInfo { |
| 34 | + vao: VertexArrayID, |
| 35 | + vertex_count: usize, |
| 36 | + program: ProgramID, |
| 37 | + u_color: UniformLocation, |
| 38 | + u_model_view_matrix: UniformLocation, |
| 39 | + u_projection_matrix: UniformLocation, |
| 40 | +} |
| 41 | + |
| 42 | +struct LightUniforms { |
| 43 | + diffuse: UniformLocation, |
| 44 | + ambient: UniformLocation, |
| 45 | + specular: UniformLocation, |
| 46 | + position: UniformLocation, |
| 47 | +} |
| 48 | + |
| 49 | + |
| 50 | +static LIGHT_INFO: OnceLock<StaticLightInfo> = OnceLock::new(); |
| 51 | +static NEXT_LIGHT_ID: OnceLock<Mutex<usize>> = OnceLock::new(); |
| 52 | + |
| 53 | + |
| 54 | +impl<'gl> Light<'gl> { |
| 55 | + #[allow(unused)] |
| 56 | + pub fn id(&self) -> usize { |
| 57 | + self.id |
| 58 | + } |
| 59 | + |
| 60 | + pub fn new( |
| 61 | + gl: &'gl GLContext, |
| 62 | + program: ProgramID, |
| 63 | + position: Vec3, |
| 64 | + diffuse: Vec4, |
| 65 | + ambient: Vec4, |
| 66 | + specular: Vec4, |
| 67 | + draw_color_override: Option<Vec4>, |
| 68 | + ) -> Self { |
| 69 | + let info = LIGHT_INFO.get_or_init(|| Self::init(gl)); |
| 70 | + let id = Self::next_id(); |
| 71 | + let uniforms = Self::get_uniforms(gl, id, program); |
| 72 | + |
| 73 | + Self { |
| 74 | + gl, |
| 75 | + id, |
| 76 | + ambient, |
| 77 | + diffuse, |
| 78 | + specular, |
| 79 | + position, |
| 80 | + uniforms, |
| 81 | + draw_color: draw_color_override.unwrap_or(diffuse), |
| 82 | + info, |
| 83 | + } |
| 84 | + } |
| 85 | + |
| 86 | + |
| 87 | + fn get_uniforms(gl: &GLContext, id: usize, program: ProgramID) -> LightUniforms { |
| 88 | + let diffuse = gl.get_uniform_location(program, &format!("lights[{id}].diffuse")).unwrap(); |
| 89 | + let ambient = gl.get_uniform_location(program, &format!("lights[{id}].ambient")).unwrap(); |
| 90 | + let specular = gl.get_uniform_location(program, &format!("lights[{id}].specular")).unwrap(); |
| 91 | + let position = gl.get_uniform_location(program, &format!("lights[{id}].position")).unwrap(); |
| 92 | + |
| 93 | + LightUniforms { |
| 94 | + diffuse, |
| 95 | + ambient, |
| 96 | + specular, |
| 97 | + position, |
| 98 | + } |
| 99 | + } |
| 100 | + |
| 101 | + |
| 102 | + fn next_id() -> usize { |
| 103 | + let id = NEXT_LIGHT_ID.get_or_init(|| Mutex::new(0)); |
| 104 | + let mut id = id.lock().expect("mutex poisoned"); |
| 105 | + let out = *id; |
| 106 | + *id += 1; |
| 107 | + out |
| 108 | + } |
| 109 | + |
| 110 | + |
| 111 | + fn init(gl: &GLContext) -> StaticLightInfo { |
| 112 | + let vertex_data = { |
| 113 | + let verts = [ |
| 114 | + Vec3::new(-0.5, -0.5, 0.5), |
| 115 | + Vec3::new(-0.5, 0.5, 0.5), |
| 116 | + Vec3::new(0.5, 0.5, 0.5), |
| 117 | + Vec3::new(0.5, -0.5, 0.5), |
| 118 | + Vec3::new(-0.5, -0.5, -0.5), |
| 119 | + Vec3::new(-0.5, 0.5, -0.5), |
| 120 | + Vec3::new(0.5, 0.5, -0.5), |
| 121 | + Vec3::new(0.5, -0.5, -0.5), |
| 122 | + ]; |
| 123 | + |
| 124 | + let mut idx = 0; |
| 125 | + let mut buf = [Vec3::default(); 36]; |
| 126 | + |
| 127 | + let mut inc = || { |
| 128 | + let i = idx; |
| 129 | + idx += 1; |
| 130 | + i |
| 131 | + }; |
| 132 | + |
| 133 | + let mut push_quad = |a: usize, b: usize, c: usize, d: usize| { |
| 134 | + let tl = verts[a]; |
| 135 | + let bl = verts[b]; |
| 136 | + let br = verts[c]; |
| 137 | + let tr = verts[d]; |
| 138 | + |
| 139 | + buf[inc()] = tl; |
| 140 | + buf[inc()] = bl; |
| 141 | + buf[inc()] = br; |
| 142 | + |
| 143 | + buf[inc()] = tl; |
| 144 | + buf[inc()] = br; |
| 145 | + buf[inc()] = tr; |
| 146 | + }; |
| 147 | + |
| 148 | + push_quad(1, 0, 3, 2); |
| 149 | + push_quad(5, 4, 0, 1); |
| 150 | + push_quad(2, 3, 7, 6); |
| 151 | + push_quad(6, 7, 4, 5); |
| 152 | + push_quad(5, 1, 2, 6); |
| 153 | + push_quad(7, 3, 0, 4); |
| 154 | + |
| 155 | + buf |
| 156 | + }; |
| 157 | + |
| 158 | + const VERT_SRC: &str = include_str!("./shaders/vert.glsl"); |
| 159 | + const FRAG_SRC: &str = include_str!("./shaders/frag-light.glsl"); |
| 160 | + let program = super::setup_program(gl, VERT_SRC, FRAG_SRC); |
| 161 | + |
| 162 | + let u_color = gl.get_uniform_location(program, "uColor").unwrap(); |
| 163 | + let u_model_view_matrix = gl.get_uniform_location(program, "uModelViewMatrix").unwrap(); |
| 164 | + let u_projection_matrix = gl.get_uniform_location(program, "uProjectionMatrix").unwrap(); |
| 165 | + |
| 166 | + let vao = gl.create_vertex_array(); |
| 167 | + gl.bind_vertex_array(vao); |
| 168 | + |
| 169 | + let vbo = gl.create_buffer(); |
| 170 | + gl.bind_buffer(BufferTarget::ArrayBuffer, vbo); |
| 171 | + gl.buffer_data(BufferTarget::ArrayBuffer, bytemuck::cast_slice(&vertex_data[..]), BufferUsage::StaticDraw); |
| 172 | + gl.vertex_attrib_pointer(0, 3, VertexAttribType::Float, false, 0, 0); |
| 173 | + gl.enable_vertex_attrib_array(0); |
| 174 | + |
| 175 | + gl.unbind_vertex_array(); |
| 176 | + |
| 177 | + StaticLightInfo { |
| 178 | + vao, |
| 179 | + vertex_count: vertex_data.len(), |
| 180 | + program, |
| 181 | + u_color, |
| 182 | + u_model_view_matrix, |
| 183 | + u_projection_matrix, |
| 184 | + } |
| 185 | + } |
| 186 | + |
| 187 | + pub fn set_uniforms(&self, view_matrix: &Mat4) { |
| 188 | + let &Self { gl, ref uniforms, .. } = self; |
| 189 | + |
| 190 | + gl.uniform_4fv(uniforms.diffuse, &[self.diffuse.into()]); |
| 191 | + gl.uniform_4fv(uniforms.ambient, &[self.ambient.into()]); |
| 192 | + gl.uniform_4fv(uniforms.specular, &[self.specular.into()]); |
| 193 | + |
| 194 | + let position4 = Vec4::from3(self.position, 1.0); |
| 195 | + let vs_position = view_matrix * position4; |
| 196 | + let vs_position = Vec3::new(vs_position.x, vs_position.y, vs_position.z); |
| 197 | + gl.uniform_3fv(uniforms.position, &[vs_position.into()]); |
| 198 | + } |
| 199 | + |
| 200 | + pub fn draw(&self, view_matrix: &Mat4, proj_matrix: &Mat4) { |
| 201 | + let &Self { gl, info, .. } = self; |
| 202 | + |
| 203 | + gl.use_program(info.program); |
| 204 | + gl.bind_vertex_array(info.vao); |
| 205 | + |
| 206 | + let model_view = view_matrix * trans_matrix(self.position) * scale_matrix(Vec3::new(0.2, 0.2, 0.2)); |
| 207 | + |
| 208 | + gl.uniform_4fv(info.u_color, &[self.draw_color.into()]); |
| 209 | + gl.uniform_matrix_4fv(info.u_model_view_matrix, false, &[model_view.into()]); |
| 210 | + gl.uniform_matrix_4fv(info.u_projection_matrix, false, &[(*proj_matrix).into()]); |
| 211 | + |
| 212 | + gl.draw_arrays(DrawMode::Triangles, 0, info.vertex_count); |
| 213 | + } |
| 214 | +} |
0 commit comments