use std::cell::Cell;
use web_sys::{WebGl2RenderingContext, WebGlProgram, WebGlUniformLocation};
pub struct Uniform<T> {
name: String,
data: Cell<T>,
}
impl<T> Uniform<T> {
pub fn new(name: String, data: T) -> Uniform<T> {
Uniform {
name,
data: Cell::new(data),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn set_data(&self, value: T) {
self.data.set(value)
}
}
impl<T: Copy> Uniform<T> {
pub fn get_data(&self) -> T {
self.data.get()
}
}
pub trait UniformValue {
fn set_uniform(&self, gl: &WebGl2RenderingContext, program: &WebGlProgram);
}
impl<T: UniformType + Copy> UniformValue for Uniform<T> {
fn set_uniform(&self, gl: &WebGl2RenderingContext, program: &WebGlProgram) {
if let Some(location) = gl.get_uniform_location(program, self.name()) {
self.get_data().uniform(gl, Some(&location))
}
}
}
pub trait UniformType {
fn uniform(&self, gl: &WebGl2RenderingContext, location: Option<&WebGlUniformLocation>);
}
macro_rules! impl_uniform {
($t:ty, $fun:ident, $sel:ident, $($things:expr),+) => {
#[doc = concat!("Uniform type corresponding to `", stringify!($fun), "`.")]
impl UniformType for $t {
fn uniform(&$sel, gl: &WebGl2RenderingContext, location: Option<&WebGlUniformLocation>) {
gl.$fun(location, $($things,)+)
}
}
}
}
impl_uniform!(f32, uniform1f, self, *self);
impl_uniform!(i32, uniform1i, self, *self);
impl_uniform!(u32, uniform1ui, self, *self);
impl_uniform!((f32, f32), uniform2f, self, self.0, self.1);
impl_uniform!((i32, i32), uniform2i, self, self.0, self.1);
impl_uniform!((u32, u32), uniform2ui, self, self.0, self.1);
impl_uniform!((f32, f32, f32), uniform3f, self, self.0, self.1, self.2);
impl_uniform!((i32, i32, i32), uniform3i, self, self.0, self.1, self.2);
impl_uniform!((u32, u32, u32), uniform3ui, self, self.0, self.1, self.2);
impl_uniform!(
(f32, f32, f32, f32),
uniform4f,
self,
self.0,
self.1,
self.2,
self.3
);
impl_uniform!(
(i32, i32, i32, i32),
uniform4i,
self,
self.0,
self.1,
self.2,
self.3
);
impl_uniform!(
(u32, u32, u32, u32),
uniform4ui,
self,
self.0,
self.1,
self.2,
self.3
);