You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
3.9 KiB
Rust

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<D: ShaderData> {
pub program_id: u32,
data: D,
}
impl<D: ShaderData> ShaderProgram<D> {
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<D: ShaderData> Deref for ShaderProgram<D> {
type Target = D;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<D: ShaderData> DerefMut for ShaderProgram<D> {
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<Shader, Error> {
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<ShaderProgram<()>, Error> {
ShaderProgram::create_with_data((), shader0, shader1)
}
}
impl<D: ShaderData> ShaderProgram<D> {
pub fn enable(&self) {
unsafe { gl::UseProgram(self.program_id) }
}
pub fn create_with_data(
data: D,
shader0: Shader,
shader1: Shader,
) -> Result<ShaderProgram<D>, 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 })
}
}
}