use crate::err::Error; use crate::GlContext; use std::ffi::CString; use std::ops::{Deref, DerefMut}; use std::os::raw::c_char; use std::ptr::null; use std::str::from_utf8; pub trait ShaderData { fn init(&mut self, gl: &mut GlContext, program_id: u32); fn apply(&self, gl: &GlContext, program_id: u32); } impl ShaderData for () { fn init(&mut self, _gl: &mut GlContext, _program_id: u32) {} fn apply(&self, _gl: &GlContext, _program_id: u32) {} } pub struct ShaderProgram { pub program_id: u32, data: D, } impl ShaderProgram { pub fn init(&mut self, gl: &mut GlContext) { self.data.init(gl, self.program_id); } pub fn apply(&self, gl: &GlContext) { self.data.apply(gl, self.program_id); } } impl Deref for ShaderProgram { type Target = D; fn deref(&self) -> &Self::Target { &self.data } } impl DerefMut for ShaderProgram { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.data } } pub struct Shader(pub u32); impl Drop for Shader { fn drop(&mut self) { unsafe { gl::DeleteShader(self.0) } } } #[repr(u32)] pub enum ShaderType { Vertex = gl::VERTEX_SHADER, Fragment = gl::FRAGMENT_SHADER, } impl Shader { pub fn compile(shader_type: ShaderType, source: &str) -> Result { let shader_id = unsafe { gl::CreateShader(shader_type as u32) }; let source = CString::new(source.as_bytes())?; unsafe { gl::ShaderSource(shader_id, 1, &source.as_ptr(), null()); gl::CompileShader(shader_id); let mut success = 0; gl::GetShaderiv(shader_id, gl::COMPILE_STATUS, &mut success); if success != 1 { let mut log_length = 0; gl::GetShaderiv(shader_id, gl::INFO_LOG_LENGTH, &mut log_length); let mut log = Vec::with_capacity(log_length as usize); let mut len = 0; gl::GetShaderInfoLog( shader_id, log_length, &mut len, log.as_mut_ptr() as *mut c_char, ); log.set_len(len as usize); return Err(Error::ShaderCompilationFailure( from_utf8(&log)?.to_string(), )); } } Ok(Shader(shader_id)) } } impl ShaderProgram<()> { pub fn create(shader0: Shader, shader1: Shader) -> Result, Error> { ShaderProgram::create_with_data((), shader0, shader1) } } impl ShaderProgram { pub fn enable(&self) { unsafe { gl::UseProgram(self.program_id) } } pub fn create_with_data( data: D, shader0: Shader, shader1: Shader, ) -> Result, Error> { unsafe { let program_id = gl::CreateProgram(); gl::AttachShader(program_id, shader0.0); gl::AttachShader(program_id, shader1.0); gl::LinkProgram(program_id); let mut status = 0; gl::GetProgramiv(program_id, gl::LINK_STATUS, &mut status); if status != 1 { let mut log_length = 0; gl::GetProgramiv(program_id, gl::INFO_LOG_LENGTH, &mut log_length); let mut log = Vec::with_capacity(log_length as usize); let mut len = 0; gl::GetProgramInfoLog( program_id, log_length, &mut len, log.as_mut_ptr() as *mut c_char, ); log.set_len(len as usize); return Err(Error::ShaderProgramLinkFailure( from_utf8(&log)?.to_string(), )); } gl::DeleteShader(shader0.0); gl::DeleteShader(shader1.0); Ok(ShaderProgram { data, program_id }) } } }