use std::cell::Cell;
use std::ffi::{CStr, CString};
use std::ptr;
use std::rc::Rc;
use internal::bitmap_like::BitmapLike;
use internal::display::{Display, register_shader};
use libc::*;
use ffi::*;
#[repr(u32)]
#[derive(Copy, Clone, Debug)]
pub enum ShaderPlatform
{
Auto = ALLEGRO_SHADER_AUTO,
GLSL = ALLEGRO_SHADER_GLSL,
HLSL = ALLEGRO_SHADER_HLSL,
}
impl ShaderPlatform
{
pub fn from_allegro(platform: ALLEGRO_SHADER_PLATFORM) -> ShaderPlatform
{
match platform
{
ALLEGRO_SHADER_AUTO => ShaderPlatform::Auto,
ALLEGRO_SHADER_GLSL => ShaderPlatform::GLSL,
ALLEGRO_SHADER_HLSL => ShaderPlatform::HLSL,
_ => panic!("Malformed platform: {}", platform)
}
}
}
#[repr(u32)]
#[derive(Copy, Clone, Debug)]
pub enum ShaderType
{
Pixel = ALLEGRO_PIXEL_SHADER,
Vertex = ALLEGRO_VERTEX_SHADER,
}
pub struct Shader
{
allegro_shader: *mut ALLEGRO_SHADER,
valid: Rc<Cell<bool>>,
}
impl Shader
{
pub fn new(display: &mut Display, platform: ShaderPlatform) -> Result<Shader, ()>
{
let shader;
unsafe
{
let old_target = al_get_target_bitmap();
al_set_target_bitmap(display.get_backbuffer().get_allegro_bitmap());
shader = al_create_shader(platform as ALLEGRO_SHADER_PLATFORM);
al_set_target_bitmap(old_target);
};
if !shader.is_null()
{
let valid = Rc::new(Cell::new(true));
register_shader(display, valid.clone(), shader);
Ok(Shader
{
allegro_shader: shader,
valid: valid,
})
}
else
{
Err(())
}
}
pub fn get_allegro_shader(&self) -> *mut ALLEGRO_SHADER
{
if self.is_valid()
{
self.allegro_shader
}
else
{
ptr::null_mut()
}
}
pub fn attach_shader_source(&mut self, shader_type: ShaderType, source: Option<&str>) -> Result<(), String>
{
if !self.is_valid()
{
return Err("Shader is not valid".to_string());
}
let shader_type = shader_type as ALLEGRO_SHADER_TYPE;
let ret = unsafe
{
match source
{
Some(source) =>
{
let source = CString::new(source.as_bytes()).unwrap();
al_attach_shader_source(self.allegro_shader, shader_type, source.as_ptr())
},
None => al_attach_shader_source(self.allegro_shader, shader_type, ptr::null())
}
};
if ret != 0
{
Ok(())
}
else
{
Err(self.get_log())
}
}
pub fn attach_shader_source_file(&mut self, shader_type: ShaderType, filename: &str) -> Result<(), String>
{
if !self.is_valid()
{
return Err("Shader is not valid".to_string())
}
let filename = CString::new(filename.as_bytes()).unwrap();
let ret = unsafe
{
al_attach_shader_source_file(self.allegro_shader, shader_type as ALLEGRO_SHADER_TYPE, filename.as_ptr())
};
if ret != 0
{
Ok(())
}
else
{
Err(self.get_log())
}
}
pub fn build(&mut self) -> Result<(), String>
{
if !self.is_valid()
{
return Err("Shader is not valid".to_string());
}
let ret = unsafe
{
al_build_shader(self.allegro_shader)
};
if ret != 0
{
Ok(())
}
else
{
Err(self.get_log())
}
}
pub fn get_log(&self) -> String
{
if !self.is_valid()
{
return "".to_string();
}
unsafe
{
let log = al_get_shader_log(self.allegro_shader);
CStr::from_ptr(log).to_string_lossy().into_owned()
}
}
pub fn get_platform(&self) -> Result<ShaderPlatform, ()>
{
if self.is_valid()
{
unsafe
{
Ok(ShaderPlatform::from_allegro(al_get_shader_platform(self.allegro_shader)))
}
}
else
{
return Err(())
}
}
pub fn is_valid(&self) -> bool
{
self.valid.get()
}
}
impl Drop for Shader
{
fn drop(&mut self)
{
if self.is_valid()
{
unsafe
{
al_destroy_shader(self.allegro_shader);
}
}
}
}
pub trait ShaderUniform
{
unsafe fn set_self_for_shader(&self, name: &str) -> Result<(), ()>;
}
macro_rules! impl_shader_vector
{
($rust_type: ty, $func: expr, $num_elems: expr, $c_type: ty) =>
{
impl ShaderUniform for $rust_type
{
unsafe fn set_self_for_shader(&self, name: &str) -> Result<(), ()>
{
let c_name = CString::new(name.as_bytes()).unwrap();
let ret = $func(c_name.as_ptr(), $num_elems, self.as_ptr() as *mut $c_type, self.len() as i32);
if ret != 0
{
Ok(())
}
else
{
Err(())
}
}
}
}
}
impl_shader_vector!([f32], al_set_shader_float_vector, 1, c_float);
impl_shader_vector!([[f32; 2]], al_set_shader_float_vector, 2, c_float);
impl_shader_vector!([[f32; 3]], al_set_shader_float_vector, 3, c_float);
impl_shader_vector!([[f32; 4]], al_set_shader_float_vector, 4, c_float);
impl_shader_vector!([i32], al_set_shader_int_vector, 1, c_int);
impl_shader_vector!([[i32; 2]], al_set_shader_int_vector, 1, c_int);
impl_shader_vector!([[i32; 3]], al_set_shader_int_vector, 3, c_int);
impl_shader_vector!([[i32; 4]], al_set_shader_int_vector, 4, c_int);