2021-32-bit-holiday-jam/src/shader.rs

196 lines
3.7 KiB
Rust

use std::convert::TryInto;
use std::ffi::{CStr, CString};
pub struct ShaderObject {
id: u32,
}
impl ShaderObject {
pub fn id (&self) -> u32 {
self.id
}
pub fn from_source (shader_type: u32, source: &[u8])
-> Result <ShaderObject, String>
{
let id = unsafe {
gl::CreateShader (shader_type)
};
let sources = [
source.as_ptr () as *const i8,
];
let lengths = [
source.len ().try_into ().unwrap (),
];
let success = unsafe {
gl::ShaderSource (id, sources.len ().try_into ().unwrap (), sources.as_ptr (), lengths.as_ptr ());
gl::CompileShader (id);
let mut success = 0;
gl::GetShaderiv (id, gl::COMPILE_STATUS, &mut success);
success == 1
};
if success {
Ok (ShaderObject {
id,
})
}
else {
let mut info_log = vec! [0u8; 4096];
let mut log_length = 0;
unsafe {
gl::GetShaderInfoLog (id, (info_log.len () - 1).try_into ().unwrap (), &mut log_length, info_log.as_mut_ptr () as *mut i8);
}
info_log.truncate (log_length.try_into ().unwrap ());
let info = String::from_utf8 (info_log).unwrap ();
Err (info)
}
}
pub fn from_file <P> (shader_type: u32, filename: P)
-> Result <ShaderObject, String>
where P: AsRef <std::path::Path>
{
let src = crate::file::load_small_file (filename, 1024 * 1024).unwrap ();
Self::from_source (shader_type, &src)
}
}
impl Drop for ShaderObject {
fn drop (&mut self) {
unsafe {
gl::DeleteShader (self.id);
}
}
}
#[derive (PartialEq, Eq)]
pub struct ShaderProgram {
id: u32,
}
impl ShaderProgram {
pub fn new (vert: &ShaderObject, frag: &ShaderObject)
-> Result <ShaderProgram, String>
{
let id = unsafe {
gl::CreateProgram ()
};
unsafe {
gl::AttachShader (id, vert.id ());
gl::AttachShader (id, frag.id ());
gl::LinkProgram (id);
}
let success = unsafe {
let mut success = 0;
gl::GetProgramiv (id, gl::LINK_STATUS, &mut success);
success == 1
};
if success {
Ok (ShaderProgram {
id,
})
}
else {
let mut info_log = vec! [0u8; 4096];
let mut log_length = 0;
unsafe {
gl::GetProgramInfoLog (id, (info_log.len () - 1).try_into ().unwrap (), &mut log_length, info_log.as_mut_ptr () as *mut i8);
}
info_log.truncate (log_length.try_into ().unwrap ());
let info = String::from_utf8 (info_log).unwrap ();
Err (info)
}
}
// 'use' is a keyword
pub fn use_program (&self) {
unsafe {
gl::UseProgram (self.id);
}
}
pub fn get_uniform_location (&self, name: &CStr) -> i32 {
self.use_program ();
unsafe {
gl::GetUniformLocation (self.id, name.as_ptr ())
}
}
pub fn get_uniform_locations <'a, I> (&self, names: I)
-> Vec <i32>
where I: Iterator <Item = &'a str>
{
self.use_program ();
names
.map (|name| {
let c_str = CString::new (name.as_bytes ()).unwrap ();
let loc = unsafe {
gl::GetUniformLocation (self.id, c_str.as_ptr ())
};
loc
})
.collect ()
}
pub fn get_attribute_location (&self, name: &CStr) -> i32 {
self.use_program ();
unsafe {
gl::GetAttribLocation (self.id, name.as_ptr ())
}
}
// This could be an iterator adapter but the code is too long
// and it's only called during startup so I don't care.
pub fn get_attribute_locations <'a, I> (&self, names: I)
-> Vec <Option <u32>>
where I: Iterator <Item = &'a str>
{
self.use_program ();
names
.map (|name| {
let c_str = CString::new (name.as_bytes ()).unwrap ();
let loc = unsafe {
gl::GetAttribLocation (self.id, c_str.as_ptr ())
};
match loc.try_into () {
Ok (loc) => Some (loc),
_ => None,
}
})
.collect ()
}
}
impl Drop for ShaderProgram {
fn drop (&mut self) {
unsafe {
gl::DeleteProgram (self.id);
}
}
}