use gl; use std::fs; use std::ffi::{CString, CStr}; use std::str; use cgmath::prelude::*; /// Shader Struct for creating and using shaders in the Context of engine /// pub struct shader { pub ID: gl::types::GLuint, } impl shader { /// Shader Constructor, will read, Compile and Create GLSL Program for use /// /// # Example /// `new(String::from("Example"))` /// if the String given was "Example" the Program expects both shaders to be in the directory "resources/shaders/Example/" /// with the vertex shader being called "shader.vert" and fragment "shader.frag" pub fn new(path: &str) -> shader { //read file contents let VERT_SHADER = fs::read_to_string(format!("resources/shaders/{path}/shader.vert")).unwrap(); let FRAG_SHADER = std::fs::read_to_string(format!("resources/shaders/{path}/shader.frag")).unwrap(); //create vertex shader let vertex_shader = unsafe {gl::CreateShader(gl::VERTEX_SHADER) }; unsafe { gl::ShaderSource(vertex_shader, 1, &VERT_SHADER.as_bytes().as_ptr().cast(), &VERT_SHADER.len().try_into().unwrap()); gl::CompileShader(vertex_shader); } //error Checking let mut success = 0; unsafe { gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success); if success == 0 { let mut log_len = 0_i32; let mut v: Vec = Vec::with_capacity(1024); gl::GetShaderInfoLog(vertex_shader, 1024, &mut log_len, v.as_mut_ptr().cast()); v.set_len(log_len.try_into().unwrap()); panic!("Vertex Shader Compile Error: {}", String::from_utf8_lossy(&v)); } let fragment_shader = gl::CreateShader(gl::FRAGMENT_SHADER); gl::ShaderSource(fragment_shader, 1, &FRAG_SHADER.as_bytes().as_ptr().cast(), &FRAG_SHADER.len().try_into().unwrap()); gl::CompileShader(fragment_shader); let mut success = 0; gl::GetShaderiv(fragment_shader, gl::COMPILE_STATUS, &mut success); if success == 0 { let mut v: Vec = Vec::with_capacity(1024); let mut log_len = 0_i32; gl::GetShaderInfoLog(fragment_shader, 1024, &mut log_len, v.as_mut_ptr().cast()); v.set_len(log_len.try_into().unwrap()); panic!("Fragment Shader Compile Error: {}", String::from_utf8_lossy(&v)); } let shader_program = gl::CreateProgram(); gl::AttachShader(shader_program, vertex_shader); gl::AttachShader(shader_program, fragment_shader); gl::LinkProgram(shader_program); let mut success = 0; gl::GetProgramiv(shader_program, gl::LINK_STATUS, &mut success); if success == 0 { let mut v: Vec = Vec::with_capacity(1024); let mut log_len = 0_i32; gl::GetProgramInfoLog(shader_program, 1024, &mut log_len, v.as_mut_ptr().cast()); v.set_len(log_len.try_into().unwrap()); panic!("Program Link Error: {}", String::from_utf8_lossy(&v)); } gl::DetachShader(shader_program, vertex_shader); gl::DetachShader(shader_program, fragment_shader); gl::DeleteShader(vertex_shader); gl::DeleteShader(fragment_shader); // return program shader { ID: shader_program, } } } /// uses Shader pub fn Use(&self) { unsafe {gl::UseProgram(self.ID);} } // BOILERPLATE JUMPSCARE pub fn setBool(&self, name: &CStr, value: bool ) {unsafe {gl::Uniform1i(gl::GetUniformLocation(self.ID, name.as_ptr()), value as i32);}} pub fn setInt(&self, name: &CStr, value: u32 ) {unsafe {gl::Uniform1i(gl::GetUniformLocation(self.ID, name.as_ptr()), value as i32);}} pub fn setFloat(&self, name: &CStr, value: f32 ) {unsafe {gl::Uniform1f(gl::GetUniformLocation(self.ID, name.as_ptr()), value as f32);}} pub fn setVec2(&self, name: &CStr, value: &cgmath::Vector2 ) {unsafe {gl::Uniform2fv(gl::GetUniformLocation(self.ID, name.as_ptr()), 1, value.as_ptr());}} pub fn setVector2d(&self, name: &CStr, x: f32, y: f32) {unsafe {gl::Uniform2f(gl::GetUniformLocation(self.ID, name.as_ptr()), x, y);}} pub fn setVector3(&self, name: &CStr, value: cgmath::Vector3 ) {unsafe {gl::Uniform3fv(gl::GetUniformLocation(self.ID, name.as_ptr()), 1, value.as_ptr());}} pub fn setVector3d(&self, name: &CStr, x: f32, y: f32, z: f32 ) {unsafe {gl::Uniform3f(gl::GetUniformLocation(self.ID, name.as_ptr()), x, y, z);}} pub fn setVector4(&self, name: &CStr, value: &cgmath::Vector4 ) {unsafe {gl::Uniform4fv(gl::GetUniformLocation(self.ID, name.as_ptr()), 1, value.as_ptr());}} pub fn setVector4d(&self, name: &CStr, x: f32, y: f32, z: f32, w: f32) {unsafe {gl::Uniform4f(gl::GetUniformLocation(self.ID, name.as_ptr()), x, y, z, w);}} pub fn setMat2(&self, name: &CStr, value: &cgmath::Matrix2) {unsafe {gl::UniformMatrix2fv(gl::GetUniformLocation(self.ID, name.as_ptr()), 1, gl::FALSE, value.as_ptr());}} pub fn setMat3(&self, name: &CStr, value: &cgmath::Matrix3) {unsafe {gl::UniformMatrix3fv(gl::GetUniformLocation(self.ID, name.as_ptr()), 1, gl::FALSE, value.as_ptr());}} pub fn setMat4(&self, name: &CStr, value: &cgmath::Matrix4) {unsafe {gl::UniformMatrix4fv(gl::GetUniformLocation(self.ID, name.as_ptr()), 1, gl::FALSE, value.as_ptr());}} } #[cfg(test)] mod tests { use glfw; use glfw::Context; use gl; use super::*; #[test] fn ShaderLoading() { // initialize GLFW let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap(); // set window hints glfw.window_hint(glfw::WindowHint::ContextVersion(3, 3)); glfw.window_hint(glfw::WindowHint::OpenGlProfile(glfw::OpenGlProfileHint::Core)); glfw.window_hint(glfw::WindowHint::OpenGlForwardCompat(true)); glfw.window_hint(glfw::WindowHint::Resizable(false)); //create Window let (mut window, events) = glfw.create_window(480, 320, "Shader Test", glfw::WindowMode::Windowed).unwrap(); let (screen_width, screen_height) = window.get_framebuffer_size(); // setup window window.make_current(); window.set_key_polling(true); // initialize gl gl::load_with(|ptr| window.get_proc_address(ptr) as *const _); let Shader = shader::new("basic"); Shader.Use(); } }