142 lines
4.3 KiB
Rust
142 lines
4.3 KiB
Rust
use gl;
|
|
use std::{
|
|
ptr,
|
|
str,
|
|
ffi::CString,
|
|
path::Path,
|
|
};
|
|
|
|
pub struct Shader {
|
|
pub program_id: u32,
|
|
}
|
|
|
|
pub struct ShaderBuilder {
|
|
program_id: u32,
|
|
shaders: Vec::<u32>,
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub enum ShaderType {
|
|
Vertex,
|
|
Fragment,
|
|
TessellationControl,
|
|
TessellationEvaluation,
|
|
Geometry,
|
|
}
|
|
|
|
impl Into<gl::types::GLenum> for ShaderType {
|
|
fn into(self) -> gl::types::GLenum {
|
|
match self {
|
|
ShaderType::Vertex => { gl::VERTEX_SHADER },
|
|
ShaderType::Fragment => { gl::FRAGMENT_SHADER },
|
|
ShaderType::TessellationControl => { gl::TESS_CONTROL_SHADER },
|
|
ShaderType::TessellationEvaluation => { gl::TESS_EVALUATION_SHADER } ,
|
|
ShaderType::Geometry => { gl::GEOMETRY_SHADER },
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ShaderType {
|
|
fn from_ext(ext: &std::ffi::OsStr) -> Result<ShaderType, String> {
|
|
match ext.to_str().expect("Failed to read extension") {
|
|
"vert" => { Ok(ShaderType::Vertex) },
|
|
"frag" => { Ok(ShaderType::Fragment) },
|
|
"tcs" => { Ok(ShaderType::TessellationControl) },
|
|
"tes" => { Ok(ShaderType::TessellationEvaluation) },
|
|
"geom" => { Ok(ShaderType::Geometry) },
|
|
e => { Err(e.to_string()) },
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ShaderBuilder {
|
|
pub unsafe fn new() -> ShaderBuilder {
|
|
ShaderBuilder {
|
|
program_id: gl::CreateProgram(),
|
|
shaders: vec![],
|
|
}
|
|
}
|
|
|
|
pub unsafe fn attach_file(self, shader_path: &str) -> ShaderBuilder {
|
|
let path = Path::new(shader_path);
|
|
if let Some(extension) = path.extension() {
|
|
let shader_type = ShaderType::from_ext(extension)
|
|
.expect("Failed to parse file extension.");
|
|
let shader_src = std::fs::read_to_string(path)
|
|
.expect(&format!("Failed to read shader source. {}", shader_path));
|
|
self.compile_shader(&shader_src, shader_type)
|
|
} else {
|
|
panic!("Failed to read extension of file with path: {}", shader_path);
|
|
}
|
|
}
|
|
|
|
pub unsafe fn compile_shader(mut self, shader_src: &str, shader_type: ShaderType) -> ShaderBuilder {
|
|
let shader = gl::CreateShader(shader_type.into());
|
|
let c_str_shader = CString::new(shader_src.as_bytes()).unwrap();
|
|
gl::ShaderSource(shader, 1, &c_str_shader.as_ptr(), ptr::null());
|
|
gl::CompileShader(shader);
|
|
|
|
if !self.check_shader_errors(shader) {
|
|
panic!("Shader failed to compile.");
|
|
}
|
|
|
|
self.shaders.push(shader);
|
|
|
|
self
|
|
}
|
|
|
|
unsafe fn check_shader_errors(&self, shader_id: u32) -> bool {
|
|
let mut success = i32::from(gl::FALSE);
|
|
let mut info_log = Vec::with_capacity(512);
|
|
info_log.set_len(512 - 1);
|
|
gl::GetShaderiv(shader_id, gl::COMPILE_STATUS, &mut success);
|
|
if success != i32::from(gl::TRUE) {
|
|
gl::GetShaderInfoLog(
|
|
shader_id,
|
|
512,
|
|
ptr::null_mut(),
|
|
info_log.as_mut_ptr() as *mut gl::types::GLchar,
|
|
);
|
|
println!("ERROR::Shader Compilation Failed!\n{}", String::from_utf8_lossy(&info_log));
|
|
return false;
|
|
}
|
|
true
|
|
}
|
|
|
|
unsafe fn check_linker_errors(&self) -> bool {
|
|
let mut success = i32::from(gl::FALSE);
|
|
let mut info_log = Vec::with_capacity(512);
|
|
info_log.set_len(512 - 1);
|
|
gl::GetProgramiv(self.program_id, gl::LINK_STATUS, &mut success);
|
|
if success != i32::from(gl::TRUE) {
|
|
gl::GetProgramInfoLog(
|
|
self.program_id,
|
|
512,
|
|
ptr::null_mut(),
|
|
info_log.as_mut_ptr() as *mut gl::types::GLchar,
|
|
);
|
|
println!("ERROR::SHADER::PROGRAM::COMPILATION_FAILED\n{}", String::from_utf8_lossy(&info_log));
|
|
return false;
|
|
}
|
|
true
|
|
}
|
|
|
|
pub unsafe fn link(self) -> Shader {
|
|
for &shader in &self.shaders {
|
|
gl::AttachShader(self.program_id, shader);
|
|
}
|
|
gl::LinkProgram(self.program_id);
|
|
|
|
// todo:: use this to make safer abstraction
|
|
self.check_linker_errors();
|
|
|
|
for &shader in &self.shaders {
|
|
gl::DeleteShader(shader);
|
|
}
|
|
|
|
Shader {
|
|
program_id: self.program_id
|
|
}
|
|
}
|
|
}
|