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
28pub 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}