use crate::custom_errors::Errors;
use cgmath::*;
use gl::types::*;
use std::collections::HashMap;
use std::ffi::CString;
use std::fs::File;
use std::io::Read;
use std::mem;
use std::os::raw::*;
pub struct Vao {
id: gl::types::GLuint,
}
impl Vao {
pub fn new() -> Result<Self, Errors> {
let mut id = 0;
unsafe {
gl::GenVertexArrays(1, &mut id);
}
if id == 0 {
return Err(Errors::OpenGlError(
"VAO creation failed".to_string(),
gl::INVALID_OPERATION,
));
}
Ok(Self { id })
}
pub fn bind(&self) {
unsafe {
gl::BindVertexArray(self.id);
}
}
pub fn unbind(&self) {
unsafe {
gl::BindVertexArray(0);
}
}
}
pub struct BufferObject {
id: gl::types::GLuint,
r#type: gl::types::GLenum,
usage: gl::types::GLenum,
}
impl BufferObject {
pub fn new(r#type: GLenum, usage: GLenum) -> Result<Self, Errors> {
let mut id = 0;
unsafe {
gl::GenBuffers(1, &mut id);
}
if id == 0 {
return Err(Errors::OpenGlError(
"Failed to generate buffer".to_string(),
gl::INVALID_OPERATION,
));
}
Ok(Self { id, r#type, usage })
}
pub fn bind(&self) {
unsafe {
gl::BindBuffer(self.r#type, self.id);
}
}
pub fn unbind(&self) {
unsafe {
gl::BindBuffer(self.r#type, 0);
}
}
pub fn store_i32_data(&self, data: &[i32]) {
unsafe {
gl::BufferData(
self.r#type,
(data.len() * mem::size_of::<gl::types::GLint>()) as gl::types::GLsizeiptr,
&data[0] as *const i32 as *const c_void,
self.usage,
)
}
}
pub fn store_f32_data(&self, data: &[f32]) {
unsafe {
gl::BufferData(
self.r#type,
(data.len() * mem::size_of::<gl::types::GLfloat>()) as gl::types::GLsizeiptr,
&data[0] as *const f32 as *const c_void,
self.usage,
)
}
}
pub fn store_u32_data(&self, data: &[u32]) {
unsafe {
gl::BufferData(
self.r#type,
(data.len() * mem::size_of::<gl::types::GLuint>()) as gl::types::GLsizeiptr,
&data[0] as *const u32 as *const c_void,
self.usage,
)
}
}
}
pub struct VertexAttribute {
index: gl::types::GLuint,
}
impl VertexAttribute {
pub fn new(
index: u32,
size: i32,
r#type: GLenum,
normalized: GLboolean,
stride: GLsizei,
pointer: *const c_void,
) -> VertexAttribute {
unsafe {
gl::VertexAttribPointer(index, size, r#type, normalized, stride, pointer);
}
VertexAttribute { index }
}
pub fn enable(&self) {
unsafe {
gl::EnableVertexAttribArray(self.index);
}
}
pub fn disable(&self) {
unsafe {
gl::DisableVertexAttribArray(self.index);
}
}
}
pub struct ShaderProgram {
program_handle: u32,
uniform_ids: HashMap<String, GLint>,
}
#[allow(temporary_cstring_as_ptr)]
impl ShaderProgram {
pub fn new(vertex_path: &str, fragment_path: &str) -> Result<Self, Errors> {
let vertex_shader = Self::compile_shader(vertex_path, gl::VERTEX_SHADER)?;
let fragment_shader = Self::compile_shader(fragment_path, gl::FRAGMENT_SHADER)?;
let program_handle = unsafe { gl::CreateProgram() };
unsafe {
gl::AttachShader(program_handle, vertex_shader);
gl::AttachShader(program_handle, fragment_shader);
gl::LinkProgram(program_handle);
gl::DeleteShader(vertex_shader);
gl::DeleteShader(fragment_shader);
}
let mut success = 0;
unsafe {
gl::GetProgramiv(program_handle, gl::LINK_STATUS, &mut success);
}
if success == 0 {
let mut log_len = 0;
unsafe {
gl::GetProgramiv(program_handle, gl::INFO_LOG_LENGTH, &mut log_len);
}
let mut log = vec![0; log_len as usize];
unsafe {
gl::GetProgramInfoLog(
program_handle,
log_len,
std::ptr::null_mut(),
log.as_mut_ptr() as *mut i8,
);
}
return Err(Errors::ShaderLinkError(
String::from_utf8_lossy(&log).to_string(),
));
}
Ok(Self {
program_handle,
uniform_ids: HashMap::new(),
})
}
fn compile_shader(path: &str, shader_type: GLenum) -> Result<GLuint, Errors> {
let mut shader_file = File::open(path).map_err(|e| Errors::FileLoadError(e.to_string()))?;
let mut shader_source = String::new();
shader_file
.read_to_string(&mut shader_source)
.map_err(|e| Errors::FileLoadError(e.to_string()))?;
let shader = unsafe { gl::CreateShader(shader_type) };
let c_str = CString::new(shader_source.as_bytes()).map_err(|e| {
Errors::ShaderCompilationError("Failed to create CString".to_string(), e.to_string())
})?;
unsafe {
gl::ShaderSource(shader, 1, &c_str.as_ptr(), std::ptr::null());
gl::CompileShader(shader);
}
let mut success = 0;
unsafe {
gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut success);
}
if success == 0 {
let mut log_len = 0;
unsafe {
gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut log_len);
}
let mut log = vec![0; log_len as usize];
unsafe {
gl::GetShaderInfoLog(
shader,
log_len,
std::ptr::null_mut(),
log.as_mut_ptr() as *mut i8,
);
}
return Err(Errors::ShaderCompilationError(
"Shader compilation failed".to_string(),
String::from_utf8_lossy(&log).to_string(),
));
}
Ok(shader)
}
pub fn bind(&self) {
unsafe {
gl::UseProgram(self.program_handle);
}
}
pub fn unbind() {
unsafe {
gl::UseProgram(0);
}
}
pub fn get_uniform_location(&mut self, name: &str) -> Result<GLint, Errors> {
if let Some(&location) = self.uniform_ids.get(name) {
Ok(location)
} else {
let c_name = CString::new(name)
.map_err(|e| Errors::OpenGlError(e.to_string(), gl::INVALID_VALUE))?;
let location = unsafe { gl::GetUniformLocation(self.program_handle, c_name.as_ptr()) };
if location < 0 {
Err(Errors::OpenGlError(
format!("Uniform '{}' not found", name,),
gl::UNIFORM,
))
} else {
self.uniform_ids.insert(name.to_string(), location);
Ok(location)
}
}
}
pub fn set_uniform_1f(&mut self, name: &str, value: f32) -> Result<(), Errors> {
let location = self.get_uniform_location(name)?;
unsafe {
gl::Uniform1f(location, value);
}
Ok(())
}
pub fn set_uniform_3f(&mut self, name: &str, x: f32, y: f32, z: f32) -> Result<(), Errors> {
let location = self.get_uniform_location(name)?;
unsafe {
gl::Uniform3f(location, x, y, z);
}
Ok(())
}
pub fn set_uniform_matrix4fv(
&mut self,
name: &str,
matrix: &cgmath::Matrix4<f32>,
) -> Result<(), Errors> {
let location = self.get_uniform_location(name)?;
unsafe {
gl::UniformMatrix4fv(location, 1, gl::FALSE, matrix.as_ptr());
}
Ok(())
}
}
pub struct Ebo {
id: gl::types::GLuint,
}
impl Ebo {
pub fn new() -> Result<Self, Errors> {
let mut id = 0;
unsafe {
gl::GenBuffers(1, &mut id);
}
if id == 0 {
return Err(Errors::OpenGlError(
"Failed to generate EBO".to_string(),
gl::INVALID_OPERATION,
));
}
Ok(Self { id })
}
pub fn bind(&self) {
unsafe {
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.id);
}
}
pub fn unbind(&self) {
unsafe {
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0);
}
}
pub fn store_indices(&self, indices: &[u32]) {
unsafe {
gl::BufferData(
gl::ELEMENT_ARRAY_BUFFER,
(indices.len() * mem::size_of::<u32>()) as gl::types::GLsizeiptr,
indices.as_ptr() as *const c_void,
gl::STATIC_DRAW,
);
}
}
}