flatbox_render 0.1.0

Provides rendering functionality for Flatbox engine
Documentation
use std::fs::read_to_string;
use std::path::Path;
use std::ptr;
use std::string::FromUtf8Error;
use thiserror::Error;
use gl::types::{GLuint, GLint};
use flatbox_core::math::glm;

use crate::macros::*;

#[derive(Error, Debug)]
pub enum ShaderError {
    #[error("Shader compilation error")]
    CompilationError(String),
    #[error("Shader I/O error")]
    IoError(#[from] std::io::Error),
    #[error("Shader linking error")]
    LinkingError(String),
    #[error("Can't convert raw pointer to UTF-8 string")]
    Utf8Error(#[from] FromUtf8Error),
}

glenum_wrapper! {
    wrapper: ShaderType,
    variants: [
        VertexShader,
        FragmentShader
    ]
}

pub struct Shader {
    id: GLuint,
}

impl Shader {
    pub fn new(path: impl AsRef<Path>, shader_type: ShaderType) -> Result<Shader, ShaderError> {
        let source_code = read_to_string(path)?;
        
        Shader::new_from_source(&source_code, shader_type)
    }

    pub fn new_from_source(source_code: &str, shader_type: ShaderType) -> Result<Shader, ShaderError> {
        unsafe { Shader::new_internal(source_code, shader_type as u32) }
    }

    unsafe fn new_internal(source_code: &str, shader_type: GLuint) -> Result<Shader, ShaderError> {
        let source_code = c_string!(source_code);
        let shader = Shader {
            id: gl::CreateShader(shader_type),
        };

        gl::ShaderSource(shader.id, 1, &source_code.as_ptr(), ptr::null());
        gl::CompileShader(shader.id);

        let mut success: GLint = 0;
        gl::GetShaderiv(shader.id, gl::COMPILE_STATUS, &mut success);
        if success == 1 {
            Ok(shader)
        } else {
            let mut error_log_size: GLint = 0;
            gl::GetShaderiv(shader.id, gl::INFO_LOG_LENGTH, &mut error_log_size);

            let mut error_log: Vec<u8> = Vec::with_capacity(error_log_size as usize);
            gl::GetShaderInfoLog(
                shader.id,
                error_log_size,
                &mut error_log_size,
                error_log.as_mut_ptr() as *mut _,
            );

            error_log.set_len(error_log_size as usize);
            let log = String::from_utf8(error_log)?;

            Err(ShaderError::CompilationError(log))
        }
    }
}

impl Drop for Shader {
    fn drop(&mut self) {
        unsafe { gl::DeleteShader(self.id); }
    }
}

pub struct GraphicsPipeline {
    id: GLuint,
}

impl GraphicsPipeline {
    pub fn new(shaders: &[Shader]) -> Result<GraphicsPipeline, ShaderError> {
        unsafe { GraphicsPipeline::new_internal(shaders) }
    }

    pub fn apply(&self){
        unsafe { gl::UseProgram(self.id); }
    }

    pub fn set_bool(&self, name: &str, value: bool) {        
        let location = self.get_uniform_location(name);
        unsafe { gl::Uniform1i(location, value as i32); }
    }

    pub fn set_int(&self, name: &str, value: i32) {
        self.apply();
        let location = self.get_uniform_location(name);
        unsafe { gl::Uniform1i(location, value); }
    }

    pub fn set_float(&self, name: &str, value: f32) {        
        let location = self.get_uniform_location(name);
        unsafe { gl::Uniform1f(location, value); }
    }

    pub fn set_vec2(&self, name: &str, value: &glm::Vec2) {
        let location = self.get_uniform_location(name);
        unsafe { gl::Uniform2fv(location, 1, glm::value_ptr(value).as_ptr()); }
    }

    pub fn set_vec3(&self, name: &str, value: &glm::Vec3) {        
        let location = self.get_uniform_location(name);
        unsafe { gl::Uniform3fv(location, 1, glm::value_ptr(value).as_ptr()); }
    }

    pub fn set_vec4(&self, name: &str, value: &glm::Vec4) {        
        let location = self.get_uniform_location(name);
        unsafe { gl::Uniform4fv(location, 1, glm::value_ptr(value).as_ptr()); }
    }

    pub fn set_mat2(&self, name: &str, value: &glm::Mat2) {        
        let location = self.get_uniform_location(name);
        unsafe { gl::UniformMatrix2fv(location, 1, gl::FALSE, glm::value_ptr(value).as_ptr()); }
    }

    pub fn set_mat3(&self, name: &str, value: &glm::Mat3) {        
        let location = self.get_uniform_location(name);
        unsafe { gl::UniformMatrix3fv(location, 1, gl::FALSE, glm::value_ptr(value).as_ptr()); }
    }

    pub fn set_mat4(&self, name: &str, value: &glm::Mat4) {        
        let location = self.get_uniform_location(name);
        unsafe { gl::UniformMatrix4fv(location, 1, gl::FALSE, glm::value_ptr(value).as_ptr()); }
    }

    pub fn get_attribute_location(&self, attribute: &str) -> u32 {
        let attribute = c_string!(attribute);
        unsafe { gl::GetAttribLocation(self.id, attribute.as_ptr()) as GLuint }
    }

    pub fn get_uniform_location(&self, uniform: &str) -> i32 {
        let uniform = c_string!(uniform);
        unsafe { gl::GetUniformLocation(self.id, uniform.as_ptr()) as GLint }
    }

    unsafe fn new_internal(shaders: &[Shader]) -> Result<GraphicsPipeline, ShaderError> {
        let program = GraphicsPipeline {
            id: gl::CreateProgram()
        };

        for shader in shaders {
            gl::AttachShader(program.id, shader.id);
        }

        gl::LinkProgram(program.id);

        let mut success: GLint = 0;
        gl::GetProgramiv(program.id, gl::LINK_STATUS, &mut success);

        if success == 1 {
            Ok(program)
        } else {
            let mut error_log_size: GLint = 0;
            gl::GetProgramiv(program.id, gl::INFO_LOG_LENGTH, &mut error_log_size);

            let mut error_log: Vec<u8> = Vec::with_capacity(error_log_size as usize);
            gl::GetProgramInfoLog(
                program.id,
                error_log_size,
                &mut error_log_size,
                error_log.as_mut_ptr() as *mut _,
            );

            error_log.set_len(error_log_size as usize);
            let log = String::from_utf8(error_log)?;

            Err(ShaderError::LinkingError(log))
        }
    }
}