diff options
Diffstat (limited to 'engine/src/opengl/shader.rs')
-rw-r--r-- | engine/src/opengl/shader.rs | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/engine/src/opengl/shader.rs b/engine/src/opengl/shader.rs new file mode 100644 index 0000000..0bbca77 --- /dev/null +++ b/engine/src/opengl/shader.rs @@ -0,0 +1,198 @@ +use std::ffi::CStr; +use std::ptr::null_mut; + +#[derive(Debug)] +pub struct Shader +{ + shader: gl::types::GLuint, +} + +impl Shader +{ + pub fn new(kind: Kind) -> Self + { + let shader = unsafe { gl::CreateShader(kind.into_gl()) }; + + Self { shader } + } + + pub fn set_source(&self, source: &str) -> Result<(), Error> + { + if !source.is_ascii() { + return Err(Error::SourceNotAscii); + } + + unsafe { + #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] + gl::ShaderSource( + self.shader, + 1, + &source.as_ptr().cast(), + &(source.len() as gl::types::GLint), + ); + } + + Ok(()) + } + + pub fn compile(&self) -> Result<(), Error> + { + unsafe { + gl::CompileShader(self.shader); + } + + let mut compile_success = gl::types::GLint::default(); + + unsafe { + gl::GetShaderiv(self.shader, gl::COMPILE_STATUS, &mut compile_success); + } + + if compile_success == 0 { + let info_log = self.get_info_log(); + + return Err(Error::CompileFailed(info_log)); + } + + Ok(()) + } + + fn get_info_log(&self) -> String + { + let mut buf = vec![gl::types::GLchar::default(); 512]; + + unsafe { + #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] + gl::GetShaderInfoLog( + self.shader, + buf.len() as gl::types::GLsizei, + null_mut(), + buf.as_mut_ptr(), + ); + } + + let info_log = unsafe { CStr::from_ptr(buf.as_ptr()) }; + + unsafe { String::from_utf8_unchecked(info_log.to_bytes().to_vec()) } + } +} + +impl Drop for Shader +{ + fn drop(&mut self) + { + unsafe { + gl::DeleteShader(self.shader); + } + } +} + +/// Shader kind. +#[derive(Debug, Clone, Copy)] +pub enum Kind +{ + Vertex, + Fragment, +} + +impl Kind +{ + fn into_gl(self) -> gl::types::GLenum + { + match self { + Self::Vertex => gl::VERTEX_SHADER, + Self::Fragment => gl::FRAGMENT_SHADER, + } + } +} + +/// Shader program +#[derive(Debug)] +pub struct Program +{ + program: gl::types::GLuint, +} + +impl Program +{ + pub fn new() -> Self + { + let program = unsafe { gl::CreateProgram() }; + + Self { program } + } + + pub fn attach(&self, shader: &Shader) + { + unsafe { + gl::AttachShader(self.program, shader.shader); + } + } + + pub fn link(&self) -> Result<(), Error> + { + unsafe { + gl::LinkProgram(self.program); + } + + let mut link_success = gl::types::GLint::default(); + + unsafe { + gl::GetProgramiv(self.program, gl::LINK_STATUS, &mut link_success); + } + + if link_success == 0 { + let info_log = self.get_info_log(); + + return Err(Error::CompileFailed(info_log)); + } + + Ok(()) + } + + pub fn activate(&self) + { + unsafe { + gl::UseProgram(self.program); + } + } + + fn get_info_log(&self) -> String + { + let mut buf = vec![gl::types::GLchar::default(); 512]; + + unsafe { + #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] + gl::GetProgramInfoLog( + self.program, + buf.len() as gl::types::GLsizei, + null_mut(), + buf.as_mut_ptr(), + ); + } + + let info_log = unsafe { CStr::from_ptr(buf.as_ptr()) }; + + unsafe { String::from_utf8_unchecked(info_log.to_bytes().to_vec()) } + } +} + +impl Drop for Program +{ + fn drop(&mut self) + { + unsafe { + gl::DeleteProgram(self.program); + } + } +} + +/// Shader error. +#[derive(Debug, thiserror::Error)] +pub enum Error +{ + #[error("All characters in source are not within the ASCII range")] + SourceNotAscii, + + #[error("Failed to compile: {0}")] + CompileFailed(String), +} |