easy_opengl/
shader.rs

1use std::collections::HashMap;
2use std::ffi::CString;
3use std::fs::File;
4use std::io::prelude::*;
5use std::ptr;
6use std::str;
7
8use gl::types::*;
9
10pub enum UniformType {
11    U32(u32),
12    I32(i32),
13    Uv2(u32, u32),
14    Iv2(i32, i32),
15    Uv3(u32, u32, u32),
16    Iv3(i32, i32, i32),
17    Uv4(u32, u32, u32, u32),
18    Iv4(i32, i32, i32, i32),
19    F32(f32),
20    F64(f64),
21    Fv2(f32, f32),
22    Fv3(f32, f32, f32),
23    Fv4(f32, f32, f32, f32),
24    M3(*const f32),
25    M4(*const f32),
26}
27
28/// A abstract representation of a shader
29///  # Example
30/// ``` Rust
31///
32/// let mut shader1 = Shader::new();
33/// shader1.load_from_memory(VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE, None);
34///
35/// let mut shader2 = Shader::new();
36/// shader2.load_from_file("./shaders/vertext.glsl", "./shaders/fragment.glsl", None);
37///
38/// shader1.set_uniform_int("entity_id", 33);
39/// ```
40pub struct Shader {
41    pub program: u32,
42    pub uniforms_location: HashMap<String, i32>,
43}
44
45impl Shader {
46    pub fn new() -> Self {
47        Self {
48            program: 0,
49            uniforms_location: HashMap::new(),
50        }
51    }
52
53    pub fn bind(&self) {
54        unsafe { gl::UseProgram(self.program) }
55    }
56
57    pub fn unbind(&self) {
58        unsafe { gl::UseProgram(0) }
59    }
60
61    pub fn load_from_memory(
62        &mut self,
63        vertex_shader: &str,
64        fragment_shader: &str,
65        geo_shader: Option<&String>,
66    ) -> bool {
67        let mut fail = false;
68
69        let vertex = self.compile_shader(vertex_shader, 0, &mut fail);
70        let fragment = self.compile_shader(fragment_shader, 1, &mut fail);
71        let mut geo = None;
72        if let Some(geo_shader) = geo_shader {
73            geo = Some(self.compile_shader(geo_shader, 1, &mut fail));
74        }
75
76        let program = self.create_shader_program(&vertex, &fragment, &geo, &mut fail);
77        self.delete_shaders(&vertex, &fragment, &geo);
78
79        self.program = program;
80
81        fail
82    }
83
84    pub fn load_from_file(
85        &mut self,
86        vertex_shader: &str,
87        fragment_shader: &str,
88        geo_shader: Option<&String>,
89    ) -> bool {
90        static read_files: fn(filename: &str) -> String = |filename: &str| -> String {
91            let mut file = File::open(filename)
92                .expect(format!("Couldn't open the file {}", filename).as_str());
93            let mut source = String::new();
94            file.read_to_string(&mut source)
95                .expect("Couldn't read the file");
96            source
97        };
98
99        let mut fail = false;
100        let vertex_source = read_files(vertex_shader);
101        let vertex = self.compile_shader(vertex_source.as_str(), 0, &mut fail);
102
103        let fragment_source = read_files(fragment_shader);
104        let fragment = self.compile_shader(fragment_source.as_str(), 1, &mut fail);
105
106        let mut geo = None;
107        if let Some(geo_shader) = geo_shader {
108            let geo_source = read_files(geo_shader);
109            geo = Some(self.compile_shader(geo_source.as_str(), 1, &mut fail));
110        }
111
112        let program = self.create_shader_program(&vertex, &fragment, &geo, &mut fail);
113        self.delete_shaders(&vertex, &fragment, &geo);
114
115        self.program = program;
116
117        fail
118    }
119
120    pub fn set_uniform(&mut self, name: &str, v: UniformType) {
121        match v {
122            UniformType::U32(v) => unsafe {
123                gl::Uniform1ui(self.get_uniform_locacion(name), v);
124            },
125            UniformType::I32(v) => unsafe {
126                gl::Uniform1i(self.get_uniform_locacion(name), v);
127            },
128            UniformType::Uv2(x, y) => unsafe {
129                gl::Uniform2ui(self.get_uniform_locacion(name), x, y);
130            },
131            UniformType::Iv2(x, y) => unsafe {
132                gl::Uniform2i(self.get_uniform_locacion(name), x, y);
133            },
134            UniformType::Uv3(x, y, z) => unsafe {
135                gl::Uniform3ui(self.get_uniform_locacion(name), x, y, z);
136            },
137            UniformType::Iv3(x, y, z) => unsafe {
138                gl::Uniform3i(self.get_uniform_locacion(name), x, y, z);
139            },
140            UniformType::Uv4(x, y, z, w) => unsafe {
141                gl::Uniform4ui(self.get_uniform_locacion(name), x, y, z, w);
142            },
143            UniformType::Iv4(x, y, z, w) => unsafe {
144                gl::Uniform4i(self.get_uniform_locacion(name), x, y, z, w);
145            },
146            UniformType::M3(m) => unsafe {
147                gl::UniformMatrix3fv(self.get_uniform_locacion(name), 1, gl::FALSE, m)
148            },
149            UniformType::M4(m) => unsafe {
150                gl::UniformMatrix4fv(self.get_uniform_locacion(name), 1, gl::FALSE, m)
151            },
152            UniformType::F32(v) => unsafe {
153                gl::Uniform1f(self.get_uniform_locacion(name), v);
154            },
155            UniformType::F64(v) => unsafe {
156                gl::Uniform1d(self.get_uniform_locacion(name), v);
157            },
158            UniformType::Fv2(x, y) => unsafe {
159                gl::Uniform2f(self.get_uniform_locacion(name), x, y);
160            },
161            UniformType::Fv3(x, y, z) => unsafe {
162                gl::Uniform3f(self.get_uniform_locacion(name), x, y, z);
163            },
164            UniformType::Fv4(x, y, z, w) => unsafe {
165                gl::Uniform4f(self.get_uniform_locacion(name), x, y, z, w);
166            },
167        }
168    }
169
170    fn get_uniform_locacion(&mut self, name: &str) -> i32 {
171        if self.uniforms_location.contains_key(name) {
172            return self.uniforms_location[name];
173        }
174        unsafe {
175            let c_name = CString::new(name.as_bytes()).unwrap();
176            let location = gl::GetUniformLocation(self.program, c_name.as_ptr());
177            self.uniforms_location.insert(name.to_string(), location);
178        }
179        self.uniforms_location[name]
180    }
181
182    fn create_shader_program(
183        &self,
184        vertex: &u32,
185        fragment: &u32,
186        geo: &Option<u32>,
187        fail: &mut bool,
188    ) -> u32 {
189        unsafe {
190            let program = gl::CreateProgram();
191            gl::AttachShader(program, *vertex);
192            gl::AttachShader(program, *fragment);
193            if let Some(geo) = geo {
194                gl::AttachShader(program, *geo);
195            }
196            gl::LinkProgram(program);
197
198            let mut success = 0;
199            let mut info_log = Vec::with_capacity(512);
200            info_log.set_len(512 - 1);
201
202            gl::GetProgramiv(program, gl::LINK_STATUS, &mut success);
203            if success != gl::TRUE as i32 {
204                *fail |= true;
205                gl::GetProgramInfoLog(
206                    program,
207                    512,
208                    ptr::null_mut(),
209                    info_log.as_mut_ptr() as *mut GLchar,
210                );
211            }
212            program
213        }
214    }
215
216    fn compile_shader(&self, shader: &str, _type: u8, fail: &mut bool) -> u32 {
217        unsafe {
218            let id = gl::CreateShader(self.get_shader_type(&_type));
219
220            let c_str_shader = CString::new(shader.as_bytes()).unwrap();
221            gl::ShaderSource(id, 1, &c_str_shader.as_ptr(), ptr::null());
222            gl::CompileShader(id);
223
224            let mut success = 0;
225            let mut info_log = Vec::with_capacity(512);
226            info_log.set_len(512 - 1);
227
228            gl::GetShaderiv(id, gl::COMPILE_STATUS, &mut success);
229            if success != gl::TRUE as i32 {
230                *fail |= true;
231
232                gl::GetShaderInfoLog(
233                    id,
234                    512,
235                    ptr::null_mut(),
236                    info_log.as_mut_ptr() as *mut GLchar,
237                );
238
239                println!(
240                    "Compiling {} shader fail. Error: {}",
241                    self.get_shader_name(&_type),
242                    str::from_utf8(&info_log).unwrap()
243                );
244            }
245
246            id
247        }
248    }
249
250    fn delete_shaders(&self, vertex: &u32, fragment: &u32, geo: &Option<u32>) {
251        unsafe {
252            gl::DeleteShader(*vertex);
253            gl::DeleteShader(*fragment);
254            if let Some(geo) = geo {
255                gl::DeleteShader(*geo);
256            }
257        }
258    }
259
260    fn get_shader_type(&self, _type: &u8) -> u32 {
261        if *_type == 0 {
262            gl::VERTEX_SHADER
263        } else if *_type == 1 {
264            gl::FRAGMENT_SHADER
265        } else {
266            gl::GEOMETRY_SHADER
267        }
268    }
269
270    fn get_shader_name(&self, _type: &u8) -> &str {
271        if *_type == 0 {
272            "vertex"
273        } else if *_type == 1 {
274            "fragment"
275        } else {
276            "geometry"
277        }
278    }
279}
280
281impl Drop for Shader {
282    fn drop(&mut self) {
283        unsafe {
284            gl::DeleteProgram(self.program);
285        }
286    }
287}