diff options
Diffstat (limited to 'opengl-bindings/src/shader.rs')
| -rw-r--r-- | opengl-bindings/src/shader.rs | 366 | 
1 files changed, 366 insertions, 0 deletions
diff --git a/opengl-bindings/src/shader.rs b/opengl-bindings/src/shader.rs new file mode 100644 index 0000000..5ed66a2 --- /dev/null +++ b/opengl-bindings/src/shader.rs @@ -0,0 +1,366 @@ +use std::ffi::CStr; +use std::ptr::null_mut; + +use safer_ffi::layout::ReprC; + +use crate::data_types::{Matrix, Vec3}; +use crate::CurrentContextWithFns; + +#[derive(Debug)] +pub struct Shader +{ +    shader: crate::sys::types::GLuint, +} + +impl Shader +{ +    #[must_use] +    pub fn new(current_context: &CurrentContextWithFns<'_>, kind: Kind) -> Self +    { +        let shader = unsafe { +            current_context +                .fns() +                .CreateShader(kind as crate::sys::types::GLenum) +        }; + +        Self { shader } +    } + +    /// Sets the source code of this shader. +    /// +    /// # Errors +    /// Returns `Err` if `source` is not ASCII. +    pub fn set_source( +        &self, +        current_context: &CurrentContextWithFns<'_>, +        source: &str, +    ) -> Result<(), Error> +    { +        if !source.is_ascii() { +            return Err(Error::SourceNotAscii); +        } + +        let length: crate::sys::types::GLint = +            source.len().try_into().map_err(|_| Error::SourceTooLarge { +                length: source.len(), +                max_length: crate::sys::types::GLint::MAX as usize, +            })?; + +        unsafe { +            current_context.fns().ShaderSource( +                self.shader, +                1, +                &source.as_ptr().cast(), +                &raw const length, +            ); +        } + +        Ok(()) +    } + +    /// Compiles this shader. +    /// +    /// # Errors +    /// Returns `Err` if compiling fails. +    pub fn compile( +        &self, +        current_context: &CurrentContextWithFns<'_>, +    ) -> Result<(), Error> +    { +        unsafe { +            current_context.fns().CompileShader(self.shader); +        } + +        let mut compile_success = crate::sys::types::GLint::default(); + +        unsafe { +            current_context.fns().GetShaderiv( +                self.shader, +                crate::sys::COMPILE_STATUS, +                &raw mut compile_success, +            ); +        } + +        if compile_success == 0 { +            let info_log = self.get_info_log(current_context); + +            return Err(Error::CompileFailed { log: info_log }); +        } + +        Ok(()) +    } + +    pub fn delete(self, current_context: &CurrentContextWithFns<'_>) +    { +        unsafe { +            current_context.fns().DeleteShader(self.shader); +        } +    } + +    fn get_info_log(&self, current_context: &CurrentContextWithFns<'_>) -> String +    { +        const BUF_SIZE: crate::sys::types::GLsizei = 512; + +        let mut buf = vec![crate::sys::types::GLchar::default(); BUF_SIZE as usize]; + +        unsafe { +            current_context.fns().GetShaderInfoLog( +                self.shader, +                BUF_SIZE, +                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()) } +    } +} + +/// Shader kind. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(u32)] +pub enum Kind +{ +    Vertex = crate::sys::VERTEX_SHADER, +    Fragment = crate::sys::FRAGMENT_SHADER, +} + +/// Shader program +#[derive(Debug, PartialEq, Eq, Hash)] +pub struct Program +{ +    program: crate::sys::types::GLuint, +} + +impl Program +{ +    #[must_use] +    pub fn new(current_context: &CurrentContextWithFns<'_>) -> Self +    { +        let program = unsafe { current_context.fns().CreateProgram() }; + +        Self { program } +    } + +    pub fn attach(&self, current_context: &CurrentContextWithFns<'_>, shader: &Shader) +    { +        unsafe { +            current_context +                .fns() +                .AttachShader(self.program, shader.shader); +        } +    } + +    /// Links this program. +    /// +    /// # Errors +    /// Returns `Err` if linking fails. +    pub fn link(&self, current_context: &CurrentContextWithFns<'_>) -> Result<(), Error> +    { +        unsafe { +            current_context.fns().LinkProgram(self.program); +        } + +        let mut link_success = crate::sys::types::GLint::default(); + +        unsafe { +            current_context.fns().GetProgramiv( +                self.program, +                crate::sys::LINK_STATUS, +                &raw mut link_success, +            ); +        } + +        if link_success == 0 { +            let info_log = self.get_info_log(current_context); + +            return Err(Error::LinkFailed { log: info_log }); +        } + +        Ok(()) +    } + +    pub fn activate(&self, current_context: &CurrentContextWithFns<'_>) +    { +        unsafe { +            current_context.fns().UseProgram(self.program); +        } +    } + +    pub fn set_uniform( +        &self, +        current_context: &CurrentContextWithFns<'_>, +        name: &CStr, +        var: &impl UniformVariable, +    ) +    { +        let location = UniformLocation(unsafe { +            current_context +                .fns() +                .GetUniformLocation(self.program, name.as_ptr().cast()) +        }); + +        var.set(current_context, self, location); +    } + +    pub fn delete(self, current_context: &CurrentContextWithFns<'_>) +    { +        unsafe { +            current_context.fns().DeleteProgram(self.program); +        } +    } + +    fn get_info_log(&self, current_context: &CurrentContextWithFns<'_>) -> String +    { +        const BUF_SIZE: crate::sys::types::GLsizei = 512; + +        let mut buf = vec![crate::sys::types::GLchar::default(); BUF_SIZE as usize]; + +        unsafe { +            current_context.fns().GetProgramInfoLog( +                self.program, +                BUF_SIZE, +                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()) } +    } +} + +pub trait UniformVariable: ReprC + sealed::Sealed +{ +    fn set( +        &self, +        current_context: &CurrentContextWithFns<'_>, +        program: &Program, +        uniform_location: UniformLocation, +    ); +} + +impl UniformVariable for f32 +{ +    fn set( +        &self, +        current_context: &CurrentContextWithFns<'_>, +        program: &Program, +        uniform_location: UniformLocation, +    ) +    { +        unsafe { +            current_context.fns().ProgramUniform1f( +                program.program, +                uniform_location.0, +                *self, +            ); +        } +    } +} + +impl sealed::Sealed for f32 {} + +impl UniformVariable for i32 +{ +    fn set( +        &self, +        current_context: &CurrentContextWithFns<'_>, +        program: &Program, +        uniform_location: UniformLocation, +    ) +    { +        unsafe { +            current_context.fns().ProgramUniform1i( +                program.program, +                uniform_location.0, +                *self, +            ); +        } +    } +} + +impl sealed::Sealed for i32 {} + +impl UniformVariable for Vec3<f32> +{ +    fn set( +        &self, +        current_context: &CurrentContextWithFns<'_>, +        program: &Program, +        uniform_location: UniformLocation, +    ) +    { +        unsafe { +            current_context.fns().ProgramUniform3f( +                program.program, +                uniform_location.0, +                self.x, +                self.y, +                self.z, +            ); +        } +    } +} + +impl sealed::Sealed for Vec3<f32> {} + +impl UniformVariable for Matrix<f32, 4, 4> +{ +    fn set( +        &self, +        current_context: &CurrentContextWithFns<'_>, +        program: &Program, +        uniform_location: UniformLocation, +    ) +    { +        unsafe { +            current_context.fns().ProgramUniformMatrix4fv( +                program.program, +                uniform_location.0, +                1, +                crate::sys::FALSE, +                self.items.as_ptr().cast::<f32>(), +            ); +        } +    } +} + +impl sealed::Sealed for Matrix<f32, 4, 4> {} + +#[derive(Debug)] +pub struct UniformLocation(crate::sys::types::GLint); + +/// Shader error. +#[derive(Debug, thiserror::Error)] +pub enum Error +{ +    #[error("All characters in source are not within the ASCII range")] +    SourceNotAscii, + +    #[error("Source is too large. Length ({length}) must be < {max_length}")] +    SourceTooLarge +    { +        length: usize, max_length: usize +    }, + +    #[error("Failed to compile shader")] +    CompileFailed +    { +        log: String +    }, + +    #[error("Failed to link shader program")] +    LinkFailed +    { +        log: String +    }, +} + +mod sealed +{ +    pub trait Sealed {} +}  | 
