1use crate::gl;
4use crate::gl::types::*;
5use crate::gl::Gl;
6
7use reverie_util::math::nalgebra;
8use std::collections::HashMap;
9use std::ffi::{CStr, CString};
10use std::fs::File;
11use std::io::Read;
12use std::str;
13
14#[derive(Debug)]
16pub struct Program {
17 gl: Gl,
18 id: GLuint,
19}
20
21impl Program {
22 pub fn default_uv(gl: Gl) -> Result<Self, String> {
23 let vert = Shader::from_vert_code(
24 Gl::clone(&gl),
25 &CString::new(include_str!("../resources/uv.vert")).unwrap(),
26 )?;
27 let frag = Shader::from_frag_code(
28 Gl::clone(&gl),
29 &CString::new(include_str!("../resources/uv.frag")).unwrap(),
30 )?;
31 Self::from_shaders(gl, &[vert, frag])
32 }
33
34 pub fn from_shaders(gl: Gl, shaders: &[Shader]) -> Result<Self, String> {
40 let program_id = unsafe { gl.CreateProgram() };
41 for shader in shaders {
42 unsafe {
43 gl.AttachShader(program_id, shader.raw_id());
44 }
45 }
46
47 unsafe {
48 gl.LinkProgram(program_id);
49 }
50
51 let mut success: GLint = 1;
52 unsafe {
53 gl.GetProgramiv(program_id, gl::LINK_STATUS, &mut success);
54 }
55 if success == 0 {
56 let mut len: GLint = 0;
57 unsafe { gl.GetProgramiv(program_id, gl::INFO_LOG_LENGTH, &mut len) }
58 let error = create_whitespace_cstring_with_len(len as usize);
59
60 unsafe {
61 #[allow(clippy::as_ptr_cast_mut)]
70 gl.GetProgramInfoLog(
71 program_id,
72 len,
73 std::ptr::null_mut(),
74 error.as_ptr() as *mut GLchar,
75 )
76 }
77
78 return Err(error.to_string_lossy().into_owned());
79 }
80
81 for shader in shaders {
82 unsafe { gl.DetachShader(program_id, shader.raw_id()) }
83 }
84 Ok(Self { gl, id: program_id })
85 }
86
87 pub const unsafe fn raw_id(&self) -> GLuint {
92 self.id
93 }
94
95 pub fn set_used(&self) {
97 unsafe {
98 self.gl.UseProgram(self.id);
99 }
100 }
101
102 #[allow(clippy::missing_safety_doc)]
103 pub unsafe fn set_uniforms(&self, uniforms: &UniformVariables<'_>) {
106 for (name, uniform) in uniforms.map.iter() {
107 self.set_uniform(name, uniform);
108 }
109 }
110
111 #[allow(clippy::missing_safety_doc)]
112 pub unsafe fn set_uniform(&self, name: &CStr, value: &Uniform<'_>) {
115 match *value {
116 Uniform::Bool(b) => self.set_bool(name, b),
117 Uniform::Int(i) => self.set_int(name, i),
118 Uniform::Float(f) => self.set_float(name, f),
119 Uniform::Vector3(v) => self.set_vector3(name, v),
120 Uniform::TripleFloat(f1, f2, f3) => self.set_vec3(name, f1, f2, f3),
121 Uniform::Matrix4(m) => self.set_mat4(name, m),
122 }
123 }
124
125 #[allow(clippy::missing_safety_doc)]
126 pub unsafe fn set_bool(&self, name: &CStr, value: bool) {
129 self.gl.Uniform1i(
130 self.gl.GetUniformLocation(self.id, name.as_ptr()),
131 value as i32,
132 );
133 }
134
135 #[allow(clippy::missing_safety_doc)]
136 pub unsafe fn set_int(&self, name: &CStr, value: i32) {
139 self.gl
140 .Uniform1i(self.gl.GetUniformLocation(self.id, name.as_ptr()), value);
141 }
142
143 #[allow(clippy::missing_safety_doc)]
144 pub unsafe fn set_float(&self, name: &CStr, value: f32) {
147 self.gl
148 .Uniform1f(self.gl.GetUniformLocation(self.id, name.as_ptr()), value);
149 }
150
151 #[allow(clippy::missing_safety_doc)]
152 pub unsafe fn set_vector3(&self, name: &CStr, value: &nalgebra::Vector3<f32>) {
155 self.gl.Uniform3fv(
156 self.gl.GetUniformLocation(self.id, name.as_ptr()),
157 1,
158 value.as_ptr(),
159 );
160 }
161
162 #[allow(clippy::missing_safety_doc)]
163 pub unsafe fn set_vec3(&self, name: &CStr, x: f32, y: f32, z: f32) {
166 self.gl
167 .Uniform3f(self.gl.GetUniformLocation(self.id, name.as_ptr()), x, y, z);
168 }
169
170 #[allow(clippy::missing_safety_doc)]
171 pub unsafe fn set_mat4(&self, name: &CStr, mat: &nalgebra::Matrix4<f32>) {
174 self.gl.UniformMatrix4fv(
175 self.gl.GetUniformLocation(self.id, name.as_ptr()),
176 1,
177 gl::FALSE,
178 mat.as_ptr(),
179 );
180 }
181}
182
183impl Drop for Program {
184 fn drop(&mut self) {
186 unsafe {
187 self.gl.DeleteProgram(self.id);
188 }
189 }
190}
191
192#[derive(Debug)]
194pub struct Shader {
195 gl: Gl,
196 id: GLuint,
197}
198
199impl Shader {
200 pub fn from_code(gl: Gl, code: &CStr, kind: GLenum) -> Result<Self, String> {
202 let id = unsafe { gl.CreateShader(kind) };
203
204 unsafe {
205 gl.ShaderSource(id, 1, &code.as_ptr(), std::ptr::null());
206 gl.CompileShader(id);
207 }
208 let mut success: GLint = 1;
209 unsafe {
210 gl.GetShaderiv(id, gl::COMPILE_STATUS, &mut success);
211 }
212
213 if success == 0 {
215 let mut len: GLint = 0;
216 unsafe {
217 gl.GetShaderiv(id, gl::INFO_LOG_LENGTH, &mut len);
218 }
219
220 let error = create_whitespace_cstring_with_len(len as usize);
221 unsafe {
222 #[allow(clippy::as_ptr_cast_mut)]
231 gl.GetShaderInfoLog(id, len, std::ptr::null_mut(), error.as_ptr() as *mut GLchar);
232 }
233
234 return Err(error.to_string_lossy().into_owned());
235 }
236 Ok(Self { gl, id })
237 }
238
239 pub fn from_file(gl: Gl, path: &str, kind: GLenum) -> Result<Self, String> {
245 let mut file = File::open(path)
246 .unwrap_or_else(|err| panic!("ERROR: {} : Failed to open file '{}'", err, path));
247 let mut code = String::new();
248 file.read_to_string(&mut code)
249 .unwrap_or_else(|err| panic!("ERROR: {} : Failed to read file '{}'", err, path));
250
251 let code = CString::new(code.as_bytes()).unwrap();
252
253 Self::from_code(gl, &code, kind)
254 }
255
256 pub fn from_vert_file(gl: Gl, path: &str) -> Result<Self, String> {
258 Self::from_file(gl, path, gl::VERTEX_SHADER)
259 }
260
261 pub fn from_frag_file(gl: Gl, path: &str) -> Result<Self, String> {
263 Self::from_file(gl, path, gl::FRAGMENT_SHADER)
264 }
265
266 pub fn from_vert_code(gl: Gl, code: &CStr) -> Result<Self, String> {
268 Self::from_code(gl, code, gl::VERTEX_SHADER)
269 }
270
271 pub fn from_frag_code(gl: Gl, code: &CStr) -> Result<Self, String> {
273 Self::from_code(gl, code, gl::FRAGMENT_SHADER)
274 }
275
276 pub const unsafe fn raw_id(&self) -> GLuint {
281 self.id
282 }
283}
284
285impl Drop for Shader {
286 fn drop(&mut self) {
288 unsafe {
289 self.gl.DeleteShader(self.id);
290 }
291 }
292}
293
294fn create_whitespace_cstring_with_len(len: usize) -> CString {
295 let mut buffer: Vec<u8> = Vec::with_capacity(len + 1);
296 buffer.extend(std::iter::once(b' ').cycle().take(len));
297 unsafe { CString::from_vec_unchecked(buffer) }
298}
299
300#[derive(Debug)]
302pub enum Uniform<'a> {
303 Bool(bool),
304 Int(i32),
305 Float(f32),
306 Vector3(&'a nalgebra::Vector3<f32>),
307 TripleFloat(f32, f32, f32),
308 Matrix4(&'a nalgebra::Matrix4<f32>),
309}
310
311pub struct UniformVariables<'a> {
313 map: HashMap<&'a CStr, Uniform<'a>>,
314}
315
316impl<'a> Default for UniformVariables<'a> {
317 fn default() -> Self {
318 Self::new()
319 }
320}
321
322impl<'a> UniformVariables<'a> {
323 pub fn new() -> Self {
325 Self {
326 map: HashMap::new(),
327 }
328 }
329
330 pub fn add(&mut self, name: &'a CStr, value: Uniform<'a>) -> &mut Self {
332 self.map.insert(name, value);
333 self
334 }
335}