heavyli_engine 0.0.7

A game engine based on 'OpenGL'.
Documentation
use std::ffi::CString;
use std::fs::File;
use std::io::Read;
use std::ptr;
use std::str;

use gl;

/// This enum is made for easy shader value setting.
pub enum ShaderValue {
    Bool(bool),
    Int(i32),
    Float(f32),
    Vec3(glm::Vec3),
    Mat4(glm::Mat4),
}

/// Compiles fragment and vertex shader code and loads it to memory.
///
/// @ Return: the id of the shader that contains the code.
pub fn compile(vertex_path: &str, fragment_path: &str) -> u32 {
    // 1. Retrieve the vertex/fragment source code from filesystem:
    let vertex_shader_code = open_code_file(vertex_path);
    let fragment_shader_code = open_code_file(fragment_path);

    // 2. Compile shaders
    unsafe {
        // Vertex shader
        let vertex = compile_source(gl::VERTEX_SHADER, vertex_shader_code, "VERTEX");

        // Fragment Shader
        let fragment = compile_source(gl::FRAGMENT_SHADER, fragment_shader_code, "FRAGMENT");

        // Shader Program
        let id = generate_program_id(&vertex, &fragment);

        // delete the shaders as they're linked into our program now and no longer necessary
        gl::DeleteShader(vertex);
        gl::DeleteShader(fragment);

        return id;
    }
}

/// Deletes a shader program.
pub unsafe fn delete_shader_program(shader_id: u32) {
    gl::DeleteProgram(shader_id);
}

/// Generates a Program ID using `OpenGL`, the `Vertex` shader and the `Fragment` shader.
pub unsafe fn generate_program_id(vertex: &u32, fragment: &u32) -> u32 {
    let id = gl::CreateProgram();
    gl::AttachShader(id, *vertex);
    gl::AttachShader(id, *fragment);
    gl::LinkProgram(id);

    check_compile_errors(id, "PROGRAM");

    return id;
}

/// Compiles a shader source.
///
/// @ Return: The ID of the shader.
pub unsafe fn compile_source(shader_type: u32, shader_code: CString, shader_name: &str) -> u32 {
    let shader_id = gl::CreateShader(shader_type);

    gl::ShaderSource(shader_id, 1, &shader_code.as_ptr(), ptr::null());
    gl::CompileShader(shader_id);

    check_compile_errors(shader_id, shader_name);

    return shader_id;
}

/// Loads shader code from a file.
pub fn open_code_file(shader_path: &str) -> CString {
    // Read the file:
    let mut shader_file =
        File::open(shader_path).unwrap_or_else(|_| panic!("Failed to open file {}", shader_path));

    // Read the file data:
    let mut shader_code = String::new();
    shader_file
        .read_to_string(&mut shader_code)
        .expect("Failed to read shader");

    // Convert the data to CString:
    return CString::new(shader_code.as_bytes()).unwrap();
}

pub unsafe fn use_program(shader_id: u32) {
    gl::UseProgram(shader_id);
}

/// Sets a shader uniform.
pub unsafe fn set_value(shader_id: u32, name: &'static str, value: ShaderValue) {
    let value_name = CString::new(name).unwrap();

    match value {
        ShaderValue::Bool(value) => gl::Uniform1i(
            gl::GetUniformLocation(shader_id, value_name.as_ptr()),
            value as i32,
        ),
        ShaderValue::Int(value) => gl::Uniform1i(
            gl::GetUniformLocation(shader_id, value_name.as_ptr()),
            value,
        ),
        ShaderValue::Float(value) => gl::Uniform1f(
            gl::GetUniformLocation(shader_id, value_name.as_ptr()),
            value,
        ),
        ShaderValue::Vec3(value) => gl::Uniform3fv(
            gl::GetUniformLocation(shader_id, value_name.as_ptr()),
            1,
            value.as_ptr(),
        ),
        ShaderValue::Mat4(value) => gl::UniformMatrix4fv(
            gl::GetUniformLocation(shader_id, value_name.as_ptr()),
            1,
            gl::FALSE,
            value.as_ptr(),
        ),
    }
}

unsafe fn check_compile_errors(shader: u32, type_: &str) {
    let mut success = gl::FALSE as gl::types::GLint;
    let mut info_log = Vec::with_capacity(1024);

    info_log.set_len(1024 - 1); // Subtract 1 to skip the trailing null character.

    if type_ != "PROGRAM" {
        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut success);

        if success != gl::TRUE as gl::types::GLint {
            gl::GetShaderInfoLog(
                shader,
                1024,
                ptr::null_mut(),
                info_log.as_mut_ptr() as *mut gl::types::GLchar,
            );
            println!(
                "ERROR::SHADER_COMPILATION_ERROR of type: {}\n{}\n \
                        -- --------------------------------------------------- -- ",
                type_,
                String::from_utf8_lossy(&info_log)
            );
        }
    } else {
        gl::GetProgramiv(shader, gl::LINK_STATUS, &mut success);

        if success != gl::TRUE as gl::types::GLint {
            gl::GetProgramInfoLog(
                shader,
                1024,
                ptr::null_mut(),
                info_log.as_mut_ptr() as *mut gl::types::GLchar,
            );
            println!(
                "ERROR::PROGRAM_LINKING_ERROR of type: {}\n{}\n \
                        -- --------------------------------------------------- -- ",
                type_,
                String::from_utf8_lossy(&info_log)
            );
        }
    }
}