use std::fmt;
use web_sys::WebGlProgram;
use web_sys::WebGlShader;
use web_sys::WebGlUniformLocation;
use crate::{gl, GL};
#[repr(u32)]
pub enum ShaderType {
VERTEX = GL::VERTEX_SHADER,
FRAGMENT = GL::FRAGMENT_SHADER,
}
#[derive(Debug)]
pub struct Shader {
pub name: &'static str,
program: Option<WebGlProgram>,
}
impl Default for Shader {
fn default() -> Self {
Self {
name: "Uninitialized Shader",
program: None,
}
}
}
impl fmt::Display for Shader {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.program.is_some() {
write!(f, "{}", self.name)
} else {
write!(f, "{} (SHADER PROGRAM MISSING)", self.name)
}
}
}
impl gl::Bind for Shader {
fn bind(&self, gl: &GL) {
gl.use_program(self.program.as_ref());
}
fn unbind(&self, gl: &GL) {
gl.use_program(None);
}
}
impl Drop for Shader {
fn drop(&mut self) {
let gl = gl::get_context();
gl.delete_program(self.program.as_ref());
}
}
impl Shader {
pub fn new(gl: &GL) -> Self {
let name = "Default Shader";
let vertex_shader =
Shader::create_vertex(gl, include_str!("../res/shader/default.vert.glsl"))
.expect("Could not create Vertex Shader!");
let fragment_shader =
Shader::create_fragment(gl, include_str!("../res/shader/default.frag.glsl"))
.expect("Could not create Fragment Shader!");
let program =
Shader::program_with_vertex_and_fragment(gl, &vertex_shader, &fragment_shader).ok();
Self { name, program }
}
pub fn new_with_vertex(
gl: &GL,
vertex_shader: WebGlShader,
name: Option<&'static str>,
) -> Self {
let name = name.unwrap_or("Custom Vertex Shader");
let fragment_shader =
Shader::create_fragment(gl, include_str!("../res/shader/default.frag.glsl"))
.expect("Could not create Fragment Shader!");
let program =
Shader::program_with_vertex_and_fragment(gl, &vertex_shader, &fragment_shader).ok();
Self { name, program }
}
pub fn create_fragment(gl: &GL, source: &str) -> Result<WebGlShader, String> {
Self::create_with_type(gl, ShaderType::FRAGMENT, source)
}
pub fn create_vertex(gl: &GL, source: &str) -> Result<WebGlShader, String> {
Self::create_with_type(gl, ShaderType::VERTEX, source)
}
pub fn create_with_type(
gl: &GL,
shader_type: ShaderType,
source: &str,
) -> Result<WebGlShader, String> {
let shader = gl
.create_shader(shader_type as u32)
.ok_or_else(|| String::from("Unable to create Shader object."))?;
gl.shader_source(&shader, source);
gl.compile_shader(&shader);
if gl
.get_shader_parameter(&shader, GL::COMPILE_STATUS)
.as_bool()
.unwrap_or(false)
{
Ok(shader)
} else {
Err(gl
.get_shader_info_log(&shader)
.unwrap_or_else(|| String::from("Could not compile shader.")))
}
}
pub fn program_with_vertex_and_fragment(
gl: &GL,
vertex_shader: &WebGlShader,
fragment_shader: &WebGlShader,
) -> Result<WebGlProgram, String> {
let program = gl
.create_program()
.ok_or_else(|| String::from("Unable to create Program object."))?;
gl.attach_shader(&program, vertex_shader);
gl.attach_shader(&program, fragment_shader);
gl.link_program(&program);
if gl
.get_program_parameter(&program, GL::LINK_STATUS)
.as_bool()
.unwrap_or(false)
{
gl.delete_shader(Some(vertex_shader));
gl.delete_shader(Some(fragment_shader));
Ok(program)
} else {
Err(gl
.get_program_info_log(&program)
.unwrap_or_else(|| String::from("Could not link program.")))
}
}
pub fn get_uniform_location(&self, gl: &GL, name: &str) -> Option<WebGlUniformLocation> {
self.program
.as_ref()
.map(|program| gl.get_uniform_location(program, name))
.unwrap()
}
pub fn get_attrib_location(&self, gl: &GL, name: &str) -> Option<i32> {
self.program
.as_ref()
.map(|program| gl.get_attrib_location(program, name))
}
}