dzahui/simulation/
shader.rs

1// Internal dependencies
2use crate::Error;
3
4// External dependencies
5use std::{ffi::CString, fs::File, ptr};
6use cgmath::{Matrix, Matrix4};
7use gl::types::GLint;
8use std::io::Read;
9use gl;
10
11
12/// # General information
13///
14/// A shader instance stores the id representing a combination of a vertex and fragment shader compiled (OpenGL Shading Language)
15/// and attached to an OpenGL program. Later on, an instance of such shader can be attached to an OpenGL context to use in conjunction with a series
16/// of vertices to draw to screen.
17///
18/// # Fields
19///
20/// * `id` - An id field setup by OpenGL to uniquely identify shaders being passed.
21///
22#[derive(Debug, PartialEq, Eq)]
23pub(crate) struct Shader {
24    pub(crate) id: u32,
25}
26
27impl Shader {
28    /// # General information
29    ///
30    /// Creates a new shader program composed of both a vertex and a fragment shader. Since it uses `gl` crate, it's necessary that an openGL context has
31    /// been initialized. Unsafe part stops rust from caching errors while compiling shaders, therefore print statements will be sent to the terminal containing
32    /// a message in case an error has happened. Later use of faulty shaders will stop the program from running, but debbuging becomes hard since little
33    /// information is provided by the `gl` crate. Should enable logging errors at a later date.
34    /// **Regarding the steps the function uses**: first it opens and read files to strings. Then, shaders are casted to CStrings. After that, each shader is sent
35    /// to be compiled and linked to a u32 variable. Finally, the u32 varaibles are linked to an OpenGL program with an id and cache is erased (compiled programs
36    /// are already associated to a program, therefore can be safely erased). This last id is returned inside Shader structure.
37    ///
38    /// # Parameters
39    ///
40    /// * `vertex_path` - Path to a vertex shader file.
41    /// * `fragment_path` - Path to a fragment shader file.
42    ///
43    pub fn new(
44        vertex_path: impl AsRef<str>,
45        fragment_path: impl AsRef<str>,
46    ) -> Result<Self, Error> {
47        // Opening files.
48        let mut vertex_shader = File::open(vertex_path.as_ref()).map_err(|e| Error::Io(e))?;
49        let mut fragment_shader = File::open(fragment_path.as_ref()).map_err(|e| Error::Io(e))?;
50
51        // Reading files.
52        let mut vertex_shader_read = String::new();
53        let mut fragment_shader_read = String::new();
54        vertex_shader
55            .read_to_string(&mut vertex_shader_read)
56            .map_err(|e| Error::Io(e))?;
57        fragment_shader
58            .read_to_string(&mut fragment_shader_read)
59            .map_err(|e| Error::Io(e))?;
60
61        // Casting shaders.
62        let vertex_shader_read = CString::new(vertex_shader_read.as_bytes())
63            .map_err(|e| Error::Custom(e.to_string()))?;
64        let fragment_shader_read = CString::new(fragment_shader_read.as_bytes())
65            .map_err(|e| Error::Custom(e.to_string()))?;
66
67        // Compiling shaders with GLSL.
68        // Vertex shader.
69        let vertex_shader: u32;
70        unsafe {
71            vertex_shader = gl::CreateShader(gl::VERTEX_SHADER);
72            gl::ShaderSource(vertex_shader, 1, &vertex_shader_read.as_ptr(), ptr::null());
73            gl::CompileShader(vertex_shader);
74            let mut success = gl::FALSE as GLint;
75            gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success);
76            if success == gl::FALSE as GLint {
77                // log does not serve
78                return Err(Error::custom("Error while compiling vertex shader!"));
79            }
80        };
81        // Fragment shader.
82        let fragment_shader: u32;
83        unsafe {
84            fragment_shader = gl::CreateShader(gl::FRAGMENT_SHADER);
85            gl::ShaderSource(
86                fragment_shader,
87                1,
88                &fragment_shader_read.as_ptr(),
89                ptr::null(),
90            );
91            gl::CompileShader(fragment_shader);
92            let mut success = gl::FALSE as GLint;
93            gl::GetShaderiv(fragment_shader, gl::COMPILE_STATUS, &mut success);
94            if success == gl::FALSE as GLint {
95                // log does not serve
96                return Err(Error::custom("Error while compiling fragment shader!"));
97            }
98        }
99
100        // Linkage to OpenGL program.
101        let id: u32;
102        unsafe {
103            id = gl::CreateProgram();
104            gl::AttachShader(id, vertex_shader);
105            gl::AttachShader(id, fragment_shader);
106            gl::LinkProgram(id);
107            let mut success = gl::FALSE as GLint;
108            gl::GetProgramiv(id, gl::LINK_STATUS, &mut success);
109            if success == gl::FALSE as GLint {
110                return Err(Error::custom("Error while linking program shader!"));
111            }
112            gl::DeleteShader(vertex_shader);
113            gl::DeleteShader(fragment_shader);
114        };
115
116        Ok(Shader { id })
117    }
118
119    /// Use a certain pair of shaders identified by id. Program can have multiple shaders at once, but only one can be used at a time.
120    pub fn use_shader(&self) {
121        unsafe {
122            gl::UseProgram(self.id);
123        }
124    }
125
126    /// Send a 4x4 matrix variable to vertex shader. Matrix variable has to be declared as a uniform in shader and it's name must be known for this to work.
127    pub fn set_mat4(&self, opengl_variable_name: &str, mat4_value: &Matrix4<f32>) -> Result<(),Error> {
128        let c_str_name = CString::new(opengl_variable_name.as_bytes())?;
129        unsafe {
130            gl::UniformMatrix4fv(
131                gl::GetUniformLocation(self.id, c_str_name.as_ptr()),
132                1,
133                gl::FALSE,
134                mat4_value.as_ptr(),
135            );
136        }
137        Ok(())
138    }
139}