use std::fs::File; use std::io::Read; use std::path::Path; use std::ptr::{null, null_mut}; use std::str::from_utf8; use crate::{ error::Error, matrix::Matrix4f, misc::{AsEnum, Bindable}, }; /* Programm */ #[derive(Default)] pub struct Program { id: gl::GLuint, } impl Program { pub fn from_shaders(iter: I) -> Result where I: IntoIterator, { Self::from_shaders_result(iter.into_iter().map(Ok)) } pub fn from_shaders_result(iter: I) -> Result where I: IntoIterator>, E: From, { let id = gl::create_program(); let id = Error::err_if(&0, id)?; let shaders = iter.into_iter().collect::, _>>()?; for shader in &shaders { gl::attach_shader(id, shader.id()); } gl::link_program(id); for shader in &shaders { gl::detach_shader(id, shader.id()); } let mut success = 1; gl::get_program_iv(id, gl::LINK_STATUS, &mut success); if success != 1 { let mut len = 0; gl::get_program_iv(id, gl::INFO_LOG_LENGTH, &mut len); let mut buffer = Vec::::with_capacity(len as usize + 1); buffer.resize(len as usize, 0); gl::get_program_info_log( id, len, null_mut(), buffer.as_mut_ptr() as *mut gl::types::GLchar, ); let msg = from_utf8(&buffer).map_err(Error::Utf8Error)?; return Err(Error::ShaderLink(msg.into()).into()); } Ok(Program { id }) } pub fn id(&self) -> gl::GLuint { self.id } pub fn uniform(&self, location: gl::GLint, uniform: Uniform<'_>) { match uniform { Uniform::Matrix4f(v) => gl::uniform_matrix_4fv(location, 1, gl::FALSE, v.as_ptr()), } } } impl Drop for Program { fn drop(&mut self) { gl::delete_program(self.id); } } impl Bindable for Program { fn bind(&self) { gl::use_program(self.id); } fn unbind(&self) { gl::use_program(0); } } /* Shader */ pub struct Shader { id: gl::GLuint, } impl Shader { pub fn from_string(type_: Type, mut source: String) -> Result { let id = gl::create_shader(type_.as_enum()); let id = Error::err_if(&0, id)?; source.push('\0'); let source_ptr = source.as_ptr() as *const i8; let source_ptr: *const *const i8 = &source_ptr; gl::shader_source(id, 1, source_ptr, null()); gl::compile_shader(id); let mut success = 1; gl::get_shader_iv(id, gl::COMPILE_STATUS, &mut success); if success != 1 { let mut len = 0; gl::get_shader_iv(id, gl::INFO_LOG_LENGTH, &mut len); let mut buffer = Vec::::with_capacity(len as usize + 1); buffer.resize(len as usize, 0); gl::get_shader_info_log( id, len, null_mut(), buffer.as_mut_ptr() as *mut gl::types::GLchar, ); let msg = from_utf8(&buffer)?; return Err(Error::ShaderCompile { code: source, error: msg.into(), }); } Ok(Self { id }) } pub fn from_reader(type_: Type, reader: &mut R) -> Result where R: Read, { let mut source = String::new(); reader.read_to_string(&mut source)?; Self::from_string(type_, source) } pub fn from_file

(type_: Type, path: P) -> Result where P: AsRef, { let mut file = File::open(&path)?; match Self::from_reader(type_, &mut file) { Ok(v) => Ok(v), Err(Error::ShaderCompile { code, error }) => Err(Error::ShaderCompile { code: format!("{}\n{}", path.as_ref().display(), code), error, }), Err(err) => Err(err), } } pub fn id(&self) -> gl::GLuint { self.id } } impl Drop for Shader { fn drop(&mut self) { gl::delete_shader(self.id); } } /* Type */ #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Type { Vertex, Compute, Fragment, Geometry, TesslationControl, TesslationEvaluation, } impl AsEnum for Type { fn as_enum(&self) -> gl::GLenum { match self { Self::Vertex => gl::VERTEX_SHADER, Self::Compute => gl::COMPUTE_SHADER, Self::Fragment => gl::FRAGMENT_SHADER, Self::Geometry => gl::GEOMETRY_SHADER, Self::TesslationControl => gl::TESS_CONTROL_SHADER, Self::TesslationEvaluation => gl::TESS_EVALUATION_SHADER, } } } /* Uniform */ pub enum Uniform<'a> { Matrix4f(&'a Matrix4f), }