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}