Skip to main content

gust_render/
shader.rs

1//! Shader module
2
3use gl;
4use gl::types::*;
5use nalgebra::{Matrix2, Matrix3, Matrix4, Vector2, Vector3, Vector4};
6use std;
7use std::ffi::CString;
8use std::fs::File;
9use std::io;
10use std::io::Read;
11use std::ptr;
12
13lazy_static! {
14    pub static ref DEFAULT_SHADER: Shader = Shader::default();
15}
16
17lazy_static! {
18    pub static ref SPRITE_SHADER: Shader = {
19        let (vert, frag, id);
20        unsafe {
21            let (v, f, i) = Shader::do_shader(
22                &CString::new(SPRITE_VS.as_bytes()).unwrap(),
23                &CString::new(SPRITE_FS.as_bytes()).unwrap(),
24            )
25            .unwrap();
26            frag = f;
27            vert = v;
28            id = i;
29        }
30        Shader { id, frag, vert }
31    };
32}
33
34lazy_static! {
35    pub static ref BATCH_SHADER: Shader = {
36        let (vert, frag, id);
37        unsafe {
38            let (v, f, i) = Shader::do_shader(
39                &CString::new(BATCH_VS.as_bytes()).unwrap(),
40                &CString::new(FS.as_bytes()).unwrap(),
41            )
42            .unwrap();
43            vert = v;
44            frag = f;
45            id = i;
46        }
47        Shader {
48            id: id,
49            frag: frag,
50            vert: vert,
51        }
52    };
53}
54
55lazy_static! {
56    pub static ref NO_TEXTURE_SHADER: Shader = {
57        let (vert, frag, id);
58        unsafe {
59            let (v, f, i) = Shader::do_shader(
60                &CString::new(VS.as_bytes()).unwrap(),
61                &CString::new(NO_TEXTURE_FS.as_bytes()).unwrap(),
62            )
63            .unwrap();
64            vert = v;
65            frag = f;
66            id = i;
67        }
68        Shader {
69            id: id,
70            frag: frag,
71            vert: vert,
72        }
73    };
74}
75
76/// Shader object that abstract openGl type
77#[derive(Debug)]
78pub struct Shader {
79    id: u32,
80    vert: u32,
81    frag: u32,
82}
83
84static SPRITE_VS: &'static str = "#version 330 core
85layout (location = 0) in vec2 aPos;
86layout (location = 1) in vec2 aTexCoord;
87layout (location = 2) in vec3 aColor;
88out vec3 ourColor;
89out vec2 TexCoord;
90uniform mat4 transform;
91uniform mat4 model;
92uniform mat4 projection;
93
94void main()
95{
96    gl_Position = projection * transform * vec4(aPos.xy, 0.0, 1.0);
97    ourColor = aColor;
98    TexCoord = aTexCoord;
99}";
100
101static SPRITE_FS: &'static str = "#version 330 core
102out vec4 FragColor;
103in vec3 ourColor;
104in vec2 TexCoord;
105uniform sampler2D ourTexture;
106
107void main()
108{
109   FragColor = texture(ourTexture, TexCoord) * vec4(ourColor, 1.0);
110}";
111
112static BATCH_VS: &'static str = "#version 330 core
113layout (location = 0) in vec2 aPos;
114layout (location = 1) in vec2 aTexCoord;
115layout (location = 2) in vec3 aColor;
116out vec3 ourColor;
117out vec2 TexCoord;
118uniform mat4 glob_model;
119uniform mat4 projection;
120
121void main()
122{
123   gl_Position = projection * glob_model * vec4(aPos.xy, 0.0, 1.0);
124   ourColor = aColor;
125   TexCoord = aTexCoord;
126}
127";
128
129static VS: &'static str = "#version 330 core
130layout (location = 0) in vec2 aPos;
131layout (location = 1) in vec2 aTexCoord;
132layout (location = 2) in vec3 aColor;
133out vec3 ourColor;
134out vec2 TexCoord;
135uniform mat4 transform;
136uniform mat4 projection;
137
138void main()
139{
140   gl_Position = projection * transform * vec4(aPos.xy, 0.0, 1.0);
141   ourColor = aColor;
142   TexCoord = aTexCoord;
143}";
144
145static FS: &'static str = "#version 330 core
146out vec4 FragColor;
147in vec3 ourColor;
148in vec2 TexCoord;
149uniform sampler2D ourTexture;
150
151void main()
152{
153   FragColor = texture(ourTexture, TexCoord) * vec4(ourColor, 1.0);
154}";
155
156static NO_TEXTURE_FS: &'static str = "#version 330 core
157out vec4 FragColor;
158in vec3 ourColor;
159
160void main()
161{
162   FragColor = vec4(ourColor, 1.0);
163}";
164
165/// Return a string from a filename
166pub fn file_to_cstring(name: &str) -> Result<CString, io::Error> {
167    let mut content = String::new();
168    File::open(name)?.read_to_string(&mut content)?;
169    Ok(CString::new(content.as_bytes()).unwrap())
170}
171
172impl Shader {
173    // Constructors ---------------------------------------------------------------
174
175    /// Create a new Shader from a filename of vertex and frag
176    pub fn new(vert: &str, frag: &str) -> Result<Shader, io::Error> {
177        let vert_source = file_to_cstring(vert)?;
178        let frag_source = file_to_cstring(frag)?;
179        let (vert_id, frag_id, id);
180
181        unsafe {
182            let (v, f, i) = Shader::do_shader(&vert_source, &frag_source)?;
183            vert_id = v;
184            frag_id = f;
185            id = i;
186        }
187
188        Ok(Shader {
189            id,
190            frag: frag_id,
191            vert: vert_id,
192        })
193    }
194
195    /// Do all everything needed for shaders
196    unsafe fn do_shader(
197        vert_code: &CString,
198        frag_code: &CString,
199    ) -> Result<(u32, u32, u32), io::Error> {
200        let id = gl::CreateProgram();
201        let vert_id = Shader::compile_shader(&vert_code, gl::VERTEX_SHADER);
202        let frag_id = Shader::compile_shader(&frag_code, gl::FRAGMENT_SHADER);
203
204        gl::AttachShader(id, vert_id);
205        gl::AttachShader(id, frag_id);
206        gl::LinkProgram(id);
207
208        let mut status: i32 = 0;
209        let s: String = std::iter::repeat(' ').take(512).collect();
210        let info_log: CString = CString::new(s.as_bytes()).unwrap();
211        gl::GetProgramiv(id, gl::LINK_STATUS, &mut status);
212        if status == 0 {
213            gl::GetProgramInfoLog(id, 512, ptr::null_mut(), info_log.as_ptr() as *mut _);
214            println!("Could not link shaders {}", info_log.into_string().unwrap());
215        }
216        gl::DeleteShader(vert_id);
217        gl::DeleteShader(frag_id);
218        Ok((vert_id, frag_id, id))
219    }
220
221    /// Compile all shaders
222    unsafe fn compile_shader(code: &CString, gl_type: GLenum) -> u32 {
223        let id = gl::CreateShader(gl_type);
224        gl::ShaderSource(id, 1, &code.as_ptr(), ptr::null());
225        gl::CompileShader(id);
226        let mut success: i32 = 0;
227        let s: String = std::iter::repeat(' ').take(512).collect();
228        let info_log: CString = CString::new(s.as_bytes()).unwrap();
229        gl::GetShaderiv(id, gl::COMPILE_STATUS, &mut success);
230        if success == 0 {
231            gl::GetShaderInfoLog(id, 512, ptr::null_mut(), info_log.as_ptr() as *mut _);
232            println!(
233                "Could not compile shaders {}",
234                info_log.into_string().unwrap()
235            );
236        };
237        id
238    }
239
240    // Usable ---------------------------------------------------------------------
241
242    /// Activate the program
243    pub fn activate(&self) {
244        unsafe {
245            gl::UseProgram(self.id);
246        }
247    }
248
249    // Uniform setter Vector
250
251    pub fn uniform_f4(&mut self, name: &str, value: Vector4<f32>) {
252        unsafe {
253            let pos = gl::GetUniformLocation(self.id, name.as_ptr() as *const _);
254            gl::Uniform4f(pos, value.x, value.y, value.z, value.w);
255        }
256    }
257
258    pub fn uniform_f3(&mut self, name: &str, value: Vector3<f32>) {
259        unsafe {
260            let pos = gl::GetUniformLocation(self.id, name.as_ptr() as *const _);
261            gl::Uniform3f(pos, value.x, value.y, value.z);
262        }
263    }
264
265    pub fn uniform_f2(&mut self, name: &str, value: Vector2<f32>) {
266        unsafe {
267            let pos = gl::GetUniformLocation(self.id, name.as_ptr() as *const _);
268            gl::Uniform2f(pos, value.x, value.y);
269        }
270    }
271
272    pub fn uniform_f(&mut self, name: &str, value: f32) {
273        unsafe {
274            let pos = gl::GetUniformLocation(self.id, name.as_ptr() as *const _);
275            gl::Uniform1f(pos, value);
276        }
277    }
278
279    // Uniform setter integer
280
281    pub fn uniform_bool(&mut self, name: &str, value: bool) {
282        self.uniform_int(name, value as i32);
283    }
284
285    pub fn uniform_int(&mut self, name: &str, value: i32) {
286        unsafe {
287            let pos = gl::GetUniformLocation(self.id, name.as_ptr() as *const _);
288            gl::Uniform1i(pos, value);
289        }
290    }
291
292    pub fn uniform_int2(&mut self, name: &str, value: Vector2<i32>) {
293        unsafe {
294            let pos = gl::GetUniformLocation(self.id, name.as_ptr() as *const _);
295            gl::Uniform2i(pos, value.x, value.y);
296        }
297    }
298
299    pub fn uniform_int3(&mut self, name: &str, value: Vector3<i32>) {
300        unsafe {
301            let pos = gl::GetUniformLocation(self.id, name.as_ptr() as *const _);
302            gl::Uniform3i(pos, value.x, value.y, value.z);
303        }
304    }
305
306    pub fn uniform_int4(&mut self, name: &str, value: Vector4<i32>) {
307        unsafe {
308            let pos = gl::GetUniformLocation(self.id, name.as_ptr() as *const _);
309            gl::Uniform4i(pos, value.x, value.y, value.z, value.w);
310        }
311    }
312
313    // Uniform setter for matrix
314
315    pub fn uniform_mat4f(&self, name: &str, value: &Matrix4<f32>) {
316        unsafe {
317            let string = CString::new(name.as_bytes()).unwrap();
318            let pos = gl::GetUniformLocation(self.id, string.as_ptr());
319            gl::UniformMatrix4fv(pos, 1, gl::FALSE, value.as_slice().as_ptr());
320        }
321    }
322
323    pub fn uniform_mat3f(&mut self, name: &str, value: Matrix3<f32>) {
324        unsafe {
325            let pos = gl::GetUniformLocation(self.id, name.as_ptr() as *const _);
326            gl::UniformMatrix3fv(pos, 1, gl::FALSE, value.as_slice().as_ptr());
327        }
328    }
329
330    pub fn uniform_mat2f(&mut self, name: &str, value: Matrix2<f32>) {
331        unsafe {
332            let pos = gl::GetUniformLocation(self.id, name.as_ptr() as *const _);
333            gl::UniformMatrix2fv(pos, 1, gl::FALSE, value.as_slice().as_ptr());
334        }
335    }
336}
337
338impl Default for Shader {
339    /// Default shader mode
340    fn default() -> Shader {
341        let vert_source = CString::new(VS.as_bytes()).unwrap();
342        let frag_source = CString::new(FS.as_bytes()).unwrap();
343        let (vert_id, frag_id, id);
344
345        unsafe {
346            let (v, f, i) = Shader::do_shader(&vert_source, &frag_source).unwrap();
347            vert_id = v;
348            frag_id = f;
349            id = i;
350        }
351
352        Shader {
353            id,
354            frag: frag_id,
355            vert: vert_id,
356        }
357    }
358}
359
360impl Drop for Shader {
361    fn drop(&mut self) {
362        unsafe {
363            gl::DeleteShader(self.vert);
364            gl::DeleteShader(self.frag);
365            gl::DeleteProgram(self.id);
366        }
367        println!("Shaders {} deleted.", self.id);
368    }
369}