opengl_graphics/
shader_utils.rs

1//! Helper functions for dealing with shaders.
2
3// External crates.
4use gl;
5use gl::types::{GLboolean, GLenum, GLint, GLsizeiptr, GLuint};
6use std::ffi::CString;
7use std::{mem, ptr};
8
9#[cfg(not(feature = "glow"))]
10use gl::types::GLchar;
11
12/// Describes a shader attribute.
13pub struct DynamicAttribute {
14    /// The vertex buffer object.
15    vbo: GLuint,
16    /// The number of components.
17    size: i32,
18    /// The location of the attribute in shader.
19    location: GLuint,
20    /// Whether to normalize when sending to GPU.
21    normalize: GLboolean,
22    /// The type, for example gl::FLOAT.
23    ty: GLenum,
24}
25
26impl Drop for DynamicAttribute {
27    fn drop(&mut self) {
28        unsafe {
29            gl::DeleteBuffers(1, &self.vbo);
30        }
31    }
32}
33
34impl DynamicAttribute {
35    /// Binds to a vertex array object.
36    ///
37    /// The vertex array object remembers the format for later.
38    fn bind_vao(&self, vao: GLuint) {
39        let stride = 0;
40        unsafe {
41            gl::BindVertexArray(vao);
42            gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
43            gl::VertexAttribPointer(
44                self.location,
45                self.size,
46                self.ty,
47                self.normalize,
48                stride,
49                ptr::null(),
50            );
51        }
52    }
53
54    fn new(
55        program: GLuint,
56        name: &str,
57        size: i32,
58        normalize: GLboolean,
59        ty: GLenum,
60        vao: GLuint,
61    ) -> Result<Self, String> {
62        let location = attribute_location(program, name)?;
63        let mut vbo = 0;
64        unsafe {
65            gl::GenBuffers(1, &mut vbo);
66        }
67        let res = DynamicAttribute {
68            vbo,
69            size,
70            location,
71            normalize,
72            ty,
73        };
74        res.bind_vao(vao);
75        Ok(res)
76    }
77
78    /// Create XYZ vertex attribute.
79    pub fn xyz(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
80        DynamicAttribute::new(program, name, 3, gl::FALSE, gl::FLOAT, vao)
81    }
82
83    /// Create XY vertex attribute.
84    pub fn xy(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
85        DynamicAttribute::new(program, name, 2, gl::FALSE, gl::FLOAT, vao)
86    }
87
88    /// Create RGB color attribute.
89    pub fn rgb(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
90        DynamicAttribute::new(program, name, 3, gl::FALSE, gl::FLOAT, vao)
91    }
92
93    /// Create RGBA color attribute.
94    pub fn rgba(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
95        DynamicAttribute::new(program, name, 4, gl::FALSE, gl::FLOAT, vao)
96    }
97
98    /// Create texture coordinate attribute.
99    pub fn uv(program: GLuint, name: &str, vao: GLuint) -> Result<DynamicAttribute, String> {
100        DynamicAttribute::new(program, name, 2, gl::FALSE, gl::FLOAT, vao)
101    }
102
103    /// Sets attribute data.
104    pub unsafe fn set<T>(&self, data: &[T]) {
105        gl::EnableVertexAttribArray(self.location);
106        gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
107        gl::BufferData(
108            gl::ARRAY_BUFFER,
109            data.len() as GLsizeiptr * mem::size_of::<T>() as GLsizeiptr,
110            mem::transmute(data.as_ptr()),
111            gl::DYNAMIC_DRAW,
112        );
113    }
114}
115
116/// Compiles a shader.
117///
118/// Returns a shader or a message with the error.
119pub fn compile_shader(shader_type: GLenum, source: &str) -> Result<GLuint, String> {
120    unsafe {
121        let shader = gl::CreateShader(shader_type);
122        let c_source = match CString::new(source) {
123            Ok(x) => x,
124            Err(err) => return Err(format!("compile_shader: {}", err)),
125        };
126        gl::ShaderSource(shader, 1, &c_source.as_ptr(), ptr::null());
127        drop(c_source);
128        gl::CompileShader(shader);
129        let mut status = gl::FALSE as GLint;
130
131        #[cfg(feature = "glow")]
132        {
133            gl::GetCompleStatus(shader, &mut status);
134        }
135        #[cfg(not(feature = "glow"))]
136        {
137            gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);
138        }
139
140        if status == (gl::TRUE as GLint) {
141            Ok(shader)
142        } else {
143            #[cfg(feature = "glow")]
144            {
145                Err(gl::GetShaderInfoLog(shader))
146            }
147            #[cfg(not(feature = "glow"))]
148            {
149                let mut len = 0;
150                gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
151
152                if len == 0 {
153                    Err("Compilation failed with no log. \
154                       The OpenGL context might have been created on another thread, \
155                       or not have been created."
156                        .to_string())
157                } else {
158                    // Subtract 1 to skip the trailing null character.
159                    let mut buf = vec![0; len as usize - 1];
160                    gl::GetShaderInfoLog(
161                        shader,
162                        len,
163                        ptr::null_mut(),
164                        buf.as_mut_ptr() as *mut GLchar,
165                    );
166
167                    gl::DeleteShader(shader);
168
169                    Err(String::from_utf8(buf).expect("ShaderInfoLog not valid utf8"))
170                }
171            }
172        }
173    }
174}
175
176/// Finds attribute location from a program.
177///
178/// Returns `Err` if there is no attribute with such name.
179pub fn attribute_location(program: GLuint, name: &str) -> Result<GLuint, String> {
180    unsafe {
181        let c_name = match CString::new(name) {
182            Ok(x) => x,
183            Err(err) => return Err(format!("attribute_location: {}", err)),
184        };
185        let id = gl::GetAttribLocation(program, c_name.as_ptr());
186        drop(c_name);
187        if id < 0 {
188            Err(format!("Attribute '{}' does not exists in shader", name))
189        } else {
190            Ok(id as GLuint)
191        }
192    }
193}
194
195/// Finds uniform location from a program.
196///
197/// Returns `Err` if there is no uniform with such name.
198pub fn uniform_location(program: GLuint, name: &str) -> Result<GLuint, String> {
199    unsafe {
200        let c_name = match CString::new(name) {
201            Ok(x) => x,
202            Err(err) => return Err(format!("uniform_location: {}", err)),
203        };
204        let id = gl::GetUniformLocation(program, c_name.as_ptr());
205        drop(c_name);
206        if id < 0 {
207            Err(format!("Uniform '{}' does not exists in shader", name))
208        } else {
209            Ok(id as GLuint)
210        }
211    }
212}