#[cfg(not(feature="webgl"))]
use std::ptr;
use std::{cell::UnsafeCell, hash::Hasher, mem};
use std::str;
use regex::{Regex, Captures};
use std::usize;
use std::str::FromStr;
use indexmap::IndexMap as HashMap;
use std::fs;
use std::io::Read;
use std::borrow::Borrow;
use std::cell::Cell;
use std::path::Path;
use std::hash::Hash;
use gl::types::*;
use gl;
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
use gl::Gl;
#[cfg(feature="gles")]
use gl::Gles2 as Gl;
#[cfg(feature="webgl")]
use gl::GlinContext as Gl;
use texture::{Texture, TextureSampler};
use cubemap::{CubeMap, CubeMapSampler};
use attributes;
use state::{State, StateRef};
use ::Result;
use std::fmt::Debug;
#[derive(Debug)]
pub struct Program{
unique_id: usize,
backend: Box<dyn Backend>,
attributes: UnsafeCell<Option<HashMap<String, u32>>>,
attributes_hash: Cell<Option<u64>>,
uniforms: UnsafeCell<Option<HashMap<String, u32>>>,
}
impl PartialEq for Program {
fn eq(&self, other: &Self) -> bool {
self.id() == other.id()
}
}
impl Eq for Program {}
#[derive(Clone,Copy,PartialEq,Eq,Hash,Debug)]
pub enum ShaderPrecision{
Low,
Medium,
High,
}
impl Default for ShaderPrecision{
fn default() -> ShaderPrecision{
ShaderPrecision::Medium
}
}
impl ToString for ShaderPrecision{
fn to_string(&self) -> String{
match self{
ShaderPrecision::Low => "lowp".to_string(),
ShaderPrecision::Medium => "mediump".to_string(),
ShaderPrecision::High => "highp".to_string(),
}
}
}
#[derive(Clone,Default,PartialEq,Eq,Debug)]
pub struct Settings<S: Eq + Hash + Debug>{
pub version: usize,
pub extensions: Vec<S>,
pub precision: ShaderPrecision,
pub defines: Vec<(S, S)>,
pub shaders: Vec<(GLenum, S)>,
pub bindings: HashMap<String, u32>,
pub base_path: S,
pub includes: HashMap<S, S>,
}
struct FileLineTracker {
new_lines: Vec<Option<usize>>
}
impl FileLineTracker {
fn new(lines: usize) -> FileLineTracker {
FileLineTracker{
new_lines: (1..=lines).map(|i| Some(i)).collect(),
}
}
fn new_line(&mut self, line: usize) {
if self.new_lines.len() < line + 2 {
self.new_lines.resize(line + 2, None)
}else{
self.new_lines.insert(line + 1, None);
}
}
fn original_line(&self, line: usize) -> Option<usize> {
if line > 0 {
self.new_lines.get(line - 1).copied().flatten()
}else{
None
}
}
fn max_line(&self) -> Option<usize> {
self.new_lines.iter().rev().find(|l| l.is_some()).copied().flatten()
}
}
type Found = bool;
fn substitute_define(shader_src: &str, define: &str, value: &str) -> (String, Found){
let search = r"(?m)^\s*#define\s*".to_string() + define + ".*";
let replace = "#define ".to_string() + define + " " + value;
let re = Regex::new(&search).unwrap();
if re.is_match(shader_src){
(re.replace(shader_src, replace.as_str()).to_string(), true)
}else{
(replace + "\n" + shader_src, false)
}
}
fn substitute_extension(shader_src: &str, extension: &str) -> (String, Found){
let search = r"(?m)^\s*#extension\s*".to_string() + extension + ":.*";
let replace = "#extension ".to_string() + extension + ": enable";
let re = Regex::new(&search).unwrap();
if re.is_match(shader_src){
(re.replace(shader_src, replace.as_str()).to_string(), true)
}else{
(replace + "\n" + shader_src, false)
}
}
lazy_static! {
static ref RE_INCLUDE: Regex = Regex::new(concat!(r"(?m)^\s*#\s*include\s*", "\"(.*)\"")).unwrap();
static ref RE_LINE: Regex = Regex::new(r"^#line ([0-9])+").unwrap();
}
fn parse_includes(file: usize, shader_src: &str, base_path: &str, includes: &mut Vec<(String, String)>) -> Result<String>{
let mut errors = vec![];
let parsed_src = RE_INCLUDE.replace_all(shader_src, |captures: &Captures|{
let include_path = Path::new(&captures[1]);
let start = captures.get(1).unwrap().start();
let include_next_line = shader_src[..start].lines().count() + 1;
let file_name = if include_path.is_absolute() {
captures[1].to_owned()
}else{
Path::new(base_path).join(include_path).to_str().unwrap().to_owned()
};
if let Ok(mut include) = fs::File::open(&file_name){
let mut include_src = String::new();
include.read_to_string(&mut include_src).unwrap();
includes.push((file_name, include_src.clone()));
let include_file = includes.len();
match parse_includes(include_file, &include_src, base_path, includes){
Ok(include_src) => {
format!("#line 1 {}\n{}\n#line {} {}",
include_file,
include_src,
include_next_line,
file)
}
Err(err) => {
errors.push(format!("{}", err));
"".to_owned()
}
}
}else{
errors.push(format!("glsl include not found: {}", file_name));
String::new()
}
}).to_string();
if errors.is_empty() {
Ok(parsed_src)
}else{
Err(::Error::new(::ErrorKind::CompileError, errors.join("\n").as_str()))
}
}
fn parse_includes_dictionary<'a, S>(file: usize, file_name: &str, shader_src: &str, includes: &[(S, S)])
-> Result<String>
where S: AsRef<str> + From<&'a str> + Eq + Hash
{
let mut errors = vec![];
let parsed_src = RE_INCLUDE.replace_all(shader_src, |captures: &Captures|{
let include_file_name: &'a str = unsafe{ mem::transmute(&captures[1]) };
let start = captures.get(1).unwrap().start();
let include_next_line = shader_src[..start].lines().count() + 1;
if let Some((include_file, (_, include_src))) = includes.iter().enumerate()
.find(|(_, (include, _))| include == &include_file_name.into())
{
let include_file = include_file + 1;
match parse_includes_dictionary(include_file, include_file_name, include_src.as_ref(), includes){
Ok(include_src) =>
format!("// {}\n#line 1 {}\n{}\n// {}\n#line {} {}",
include_file_name,
include_file,
include_src,
file_name,
include_next_line,
file),
Err(err) => {
errors.push(format!("{}", err));
"".to_owned()
}
}
}else{
errors.push(format!("glsl include not found: {}", include_file_name));
"".to_owned()
}
}).to_string();
if errors.is_empty() {
Ok(parsed_src)
}else{
Err(::Error::new(::ErrorKind::CompileError, errors.join("\n").as_str()))
}
}
fn is_full_line_comment(line: &str) -> bool {
let line = line.trim();
if line.starts_with("//") {
return true
}
if !line.starts_with("/*") {
return false
}
let mut found_bar = false;
let mut open_comment = false;
let mut found_closing_asterisk = false;
for c in line.chars() {
if !found_bar && !open_comment && !c.is_whitespace() && c != '/' {
return false
}else if !found_bar && !open_comment && c == '/' {
found_bar = true;
}else if found_bar && !open_comment && c == '/' {
return true;
}else if found_bar && !open_comment && c == '*' {
open_comment = true;
found_bar = false;
}else if found_bar && !open_comment && !c.is_whitespace() && c != '*' {
return false;
}else if open_comment && c == '*' {
found_closing_asterisk = true;
}else if open_comment && found_closing_asterisk && c == '/' {
found_closing_asterisk = false;
open_comment = false
}
}
!open_comment
}
fn split_shader_header(shader_src: &str) -> (String, String){
lazy_static!{
static ref RE_VERSION: Regex = Regex::new(r"(?m)^\s*\#version\s+[0-9]*").unwrap();
static ref RE_PRECISION: Regex = Regex::new(r"(?m)^\s*precision\s+(lowp|mediump|highp)\s+(float|int);")
.unwrap();
}
let mut header = String::new();
let mut source = String::new();
let mut lines = shader_src.lines().enumerate();
let mut block_comment_start = false;
for (i, line) in &mut lines {
if RE_VERSION.is_match(line) || RE_PRECISION.is_match(line){
header.push_str(line);
header.push('\n');
}else if line.trim().is_empty(){
header.push_str(" \n");
}else if is_full_line_comment(line){
header.push_str(line);
header.push('\n');
}else if line.trim_start().starts_with("/*") {
block_comment_start = true;
header.push_str(line);
header.push('\n');
}else if block_comment_start && !line.contains("*/"){
header.push_str(line);
header.push('\n');
}else if block_comment_start && line.contains("*/") {
if let Some(next_block_comment) = line.find("/*") {
if next_block_comment < line.find("*/").unwrap() {
block_comment_start = false;
}
}else{
block_comment_start = false
}
header.push_str(line);
header.push('\n');
}else{
source.push_str(line);
source.push('\n');
break;
}
}
for (_, line) in lines{
source.push_str(line);
source.push('\n');
}
(header, source)
}
#[cfg(any(feature="gles", feature="webgl"))]
fn version_str(version: usize) -> String{
if version < 300{
"".to_string()
}else{
"#version ".to_string() + &version.to_string() + " es"
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
fn version_str(version: usize) -> String{
"#version ".to_string() + &version.to_string()
}
fn replace_version_precision_extensions(shader_header: &str, version: usize, precision: ShaderPrecision, extensions: &str, lines_tracker: &mut FileLineTracker) -> String{
lazy_static!{
static ref RE_VERSION: Regex = Regex::new(r"(?m)^\s*\#version\s+[0-9]*").unwrap();
static ref RE_PRECISION_FLOAT: Regex = Regex::new(r"(?m)^\s*precision\s+(lowp|mediump|highp)\s+float;")
.unwrap();
static ref RE_PRECISION_INT: Regex = Regex::new(r"(?m)^\s*precision\s+(lowp|mediump|highp)\s+int;")
.unwrap();
}
let version_str = version_str(version);
let precision_float_str = format!("precision {} float;", precision.to_string());
let precision_int_str = format!("precision {} int;", precision.to_string());
let shader_header = if RE_VERSION.find(shader_header).is_some() {
let replaced = RE_VERSION.replace(shader_header, "").to_string();
replaced
}else{
lines_tracker.new_line(0);
shader_header.to_string()
};
let shader_header = if !extensions.is_empty(){
for (i, _) in extensions.lines().enumerate() {
lines_tracker.new_line(i + 1)
}
version_str + "\n" + extensions + "\n" + &shader_header
}else{
version_str + "\n" + &shader_header
};
let mut next_line = shader_header.lines().count();
let shader_header = if RE_PRECISION_FLOAT.find(&shader_header).is_some() {
RE_PRECISION_FLOAT.replace(&shader_header, precision_float_str.as_str()).to_string()
}else{
lines_tracker.new_line(next_line);
next_line += 1;
shader_header + &precision_float_str + "\n"
};
let shader_header = if RE_PRECISION_INT.find(&shader_header).is_some() {
RE_PRECISION_INT.replace(&shader_header, precision_int_str.as_str()).to_string()
}else{
lines_tracker.new_line(next_line);
shader_header + &precision_int_str + "\n"
};
shader_header
}
#[cfg(feature="gles")]
pub fn translate_es3_to_es2(src: &str, shader_ty: GLenum) -> String{
lazy_static!{
static ref INS: Regex = Regex::new(r"(?m)^\s*in\s+([^\s]+)\s([^\s]+)\s*;").unwrap();
static ref OUTS: Regex = Regex::new(r"(?m)^\s*out\s+(?P<type>[^\s]+)\s+(?P<name>[^\s]+)\s*;")
.unwrap();
static ref TEXTURE: Regex = Regex::new(r"([^a-zA-z0-9_]{1})texture\s*\(").unwrap();
}
match shader_ty {
gl::VERTEX_SHADER => {
let src = INS.replace_all(src, "attribute $1 $2;");
let src = OUTS.replace_all(&src, "varying $1 $2;");
let src = TEXTURE.replace_all(&src, "$1 texture2D(");
(*src).to_owned()
}
gl::FRAGMENT_SHADER => {
let src = INS.replace_all(src, "varying $1 $2;");
let out = OUTS.captures_iter(&src).map(|c| c["name"].to_owned()).next().unwrap();
let frag_out = Regex::new(&format!(r"([^a-zA-z0-9_]{{1}}){}([^a-zA-z0-9_]{{1}})", out)).unwrap();
let src = frag_out.replace_all(&src, "$1 gl_FragColor $2");
let src = OUTS.replace_all(&src, "");
let src = TEXTURE.replace_all(&src, r"$1 texture2D(");
(*src).to_owned()
}
_=> unimplemented!()
}
}
thread_local!(static NEXT_UNIQUE_ID: Cell<usize> = Cell::new(1));
pub struct Builder<'a>(pub(crate) &'a StateRef);
impl<'a> Builder<'a>{
pub fn from_src(&self, src: &[(GLenum,&str)]) -> Result<Program>{
let mut program = Program::new(self.0)?;
for &(ty, src) in src{
program.backend.compile_shader(ty, src, &[], src.as_ref(), None)?;
}
program.backend.link()?;
Ok(program)
}
pub fn from_src_bindings<S,B>(&self, src: &[(GLenum, S)], bindings: &B) -> Result<Program>
where S: AsRef<str>,
B: attributes::Bindings
{
let mut program = Program::new(self.0)?;
for &(ty, ref src) in src{
program.backend.compile_shader(ty, src.as_ref(), &[], src.as_ref(), None)?;
}
program.backend.bind_attributes(bindings);
program.backend.link()?;
Ok(program)
}
pub fn from_settings<S>(&self, settings: Settings<S>) -> Result<Program>
where S: AsRef<str> + From<&'a str> + Eq + Hash + Debug
{
let mut includes = settings.includes.iter()
.map(|(i,p)| (i.as_ref().to_owned(), p.as_ref().to_owned()))
.collect::<Vec<_>>();
let shaders = settings.shaders.iter().map(|&(ty, ref shader_src)| {
let num_lines_before_include = shader_src.as_ref()
.lines()
.take_while(|line| !RE_INCLUDE.is_match(line))
.count();
let mut root_file_line_tracker = FileLineTracker::new(num_lines_before_include);
let shader_src = if !settings.includes.is_empty() {
parse_includes_dictionary(0, "main", shader_src.as_ref(), &includes)?
}else{
parse_includes(0, shader_src.as_ref(), settings.base_path.as_ref(), &mut includes)?
};
let (header, src) = split_shader_header(&shader_src);
let extensions = settings.extensions.iter()
.map(|e| format!("#extension {}: enable\n", e.as_ref()))
.collect::<String>();
let header = replace_version_precision_extensions(
&header,
settings.version,
settings.precision,
&extensions,
&mut root_file_line_tracker,
);
let first_source_line = header.lines().count() + 1;
let shader_src = settings.defines.iter().fold(src,
|shader_src, (define, value)|{
let (src, found) = substitute_define(
&shader_src,
define.as_ref(),
value.as_ref()
);
if !found {
root_file_line_tracker.new_line(first_source_line)
}
src
});
let shader_src = header + &shader_src;
Ok((ty, shader_src, root_file_line_tracker))
}).collect::<Result<Vec<_>>>()?;
let mut program = Program::new(self.0)?;
for (ty, src, root_file_line_tracker) in shaders.iter() {
let original_source = settings.shaders.iter()
.find(|(shader_ty, _)| shader_ty == ty)
.map(|(_, src)| src.as_ref())
.unwrap();
program.backend.compile_shader(*ty, src, &includes, original_source, Some(root_file_line_tracker))?;
}
program.backend.bind_attributes(&settings.bindings);
program.backend.link()
.map_err(|err| ::Error::with_cause(
::ErrorKind::LinkError,
format!(
"{}",
shaders.into_iter().map(|(ty,src,_)| ty.to_string() + ": " + &src).collect::<String>()
).as_str(),
err
))?;
Ok(program)
}
}
impl Program{
fn new(gl: &StateRef) -> Result<Program>{
let id = unsafe{ gl.CreateProgram() };
if id > 0 {
let shaders = Vec::new();
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
let backend = if gl.capabilities().supports_dsa() {
Box::new(BackendDsa{
id,
shaders,
gl: gl.clone(),
}) as Box<dyn Backend>
}else{
Box::new(BackendBind{
id,
shaders,
gl: gl.clone(),
}) as Box<dyn Backend>
};
#[cfg(any(feature = "gles", feature="webgl"))]
let backend = Box::new(BackendBind{id, shaders, gl: gl.clone()}) as Box<dyn Backend>;
let unique_id = NEXT_UNIQUE_ID.with(|id| {
let next_id = id.get();
id.set(next_id + 1);
next_id
});
Ok(Program{
unique_id,
backend,
attributes: UnsafeCell::new(None),
uniforms: UnsafeCell::new(None),
attributes_hash: Cell::new(None),
})
}else{
Err(::Error::with_gl_code(::ErrorKind::ProgramCreationError,
"glCreateProgram returned 0", unsafe{ gl.GetError() }))
}
}
pub fn id(&self) -> GLuint{
self.backend.id()
}
pub fn unique_id(&self) -> usize{
self.unique_id
}
fn gl(&self) -> &Gl{
self.backend.gl()
}
fn state_mut(&self) -> &mut State<'static>{
self.backend.state_mut()
}
pub fn attribute_location(&self, attr: &str) -> Option<u32>{
let loc = unsafe{
self.gl().GetAttribLocation(self.id(), to_c_str!(attr))
};
if loc == -1{
None
}else{
Some(loc as u32)
}
}
pub fn uniform_location(&self, name: &str) -> Option<u32>{
let loc = unsafe{
self.gl().GetUniformLocation(self.id(), to_c_str!(name))
};
if loc == -1{
None
}else{
Some(loc as u32)
}
}
pub fn uniform_block_index(&self, name: &str) -> Option<u32>{
let loc = unsafe{
self.gl().GetUniformBlockIndex(self.id(), to_c_str!(name))
};
if loc == gl::INVALID_INDEX{
None
}else{
Some(loc as u32)
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
pub fn shader_storage_block_index(&self, name: &str) -> Option<u32>{
let loc = unsafe{
self.gl().GetProgramResourceIndex(self.id(), gl::SHADER_STORAGE_BLOCK, to_c_str!(name))
};
if loc == gl::INVALID_INDEX{
None
}else{
Some(loc as u32)
}
}
#[cfg(not(feature="webgl"))]
pub fn attribute_bindings(&self) -> &HashMap<String, u32>{
unsafe{
(*self.attributes.get()).get_or_insert_with(|| {
let mut total_attrs = 0;
let mut namebuffer = [0u8;1024];
self.gl().GetProgramiv(self.id(), gl::ACTIVE_ATTRIBUTES, &mut total_attrs);
(0..total_attrs).map(|i| {
let mut length = 0;
let mut attr_size = 0;
let mut attr_type = 0;
self.gl().GetActiveAttrib(self.id(), i as u32,
1024,
&mut length,
&mut attr_size,
&mut attr_type,
namebuffer.as_mut_ptr() as *mut i8);
let name = from_c_str!(namebuffer.as_ptr() as *const i8).to_owned();
let location = self.attribute_location(&name).unwrap();
(name, location)
}).collect()
})
}
}
pub fn attribute_bindings_hash(&self) -> u64 {
if let Some(hash) = self.attributes_hash.get() {
hash
}else{
let mut hasher = fxhash::FxHasher::default();
for (name, loc) in self.attribute_bindings() {
name.hash(&mut hasher);
loc.hash(&mut hasher);
}
let hash = hasher.finish();
self.attributes_hash.set(Some(hash));
hash
}
}
#[cfg(not(feature="webgl"))]
pub fn uniform_locations(&self) -> &HashMap<String, u32>{
unsafe{
(*self.uniforms.get()).get_or_insert_with(|| {
let mut num_uniforms = 0;
self.gl().GetProgramiv(self.id(), gl::ACTIVE_UNIFORMS, &mut num_uniforms);
let mut namebuffer = [0u8;1024];
(0..num_uniforms).filter_map(|i|{
let mut length = 0;
let mut uniform_size = 0;
let mut uniform_type = 0;
self.gl().GetActiveUniform(self.id(), i as u32,
1024,
&mut length,
&mut uniform_size,
&mut uniform_type,
namebuffer.as_mut_ptr() as *mut i8);
let name = from_c_str!(namebuffer.as_ptr() as *const i8).to_owned();
self.uniform_location(&name).map(|location| (name.to_owned(), location))
}).collect()
})
}
}
#[cfg(feature="webgl")]
pub fn attribute_bindings(&self) -> &HashMap<String, u32>{
self.attributes.get_or_insert_with(|| unsafe{
let mut total_attrs = 0;
self.gl().GetProgramiv(self.id(), gl::ACTIVE_ATTRIBUTES, &mut total_attrs);
(0..total_attrs).map(|i| {
let attrib_info = self.gl().GetActiveAttrib(self.id(), i as u32).unwrap();
let name = attrib_info.Name();
let location = self.attribute_location(&name).unwrap();
(name, location)
}).collect()
})
}
#[cfg(feature="webgl")]
pub fn uniform_locations(&self) -> &HashMap<String, u32>{
self.uniforms.get_or_insert_with(|| unsafe{
let mut num_uniforms = 0;
self.gl().GetProgramiv(self.id(), gl::ACTIVE_UNIFORMS, &mut num_uniforms);
(0..num_uniforms).map(|i|{
let uniform_info = self.gl().GetActiveUniform(self.id(), i as u32).unwrap();
let name = uniform_info.Name();
let location = self.uniform_location(&name).unwrap();
(name, location)
}).collect()
})
}
pub fn uniform_1f(&self, uniform: &str, value: f32) -> Result<()>{
self.uniform_location(uniform).map(|loc|
self.backend.uniform_1f(loc, value)
).ok_or_else(|| uniform_not_found_error(uniform))
}
pub fn uniform_2f<T: AsRef<[f32;2]>>(&self, uniform: &str, value: T) -> Result<()>{
self.uniform_location(uniform).map(|loc|
self.backend.uniform_2f(loc, value.as_ref())
).ok_or_else(|| uniform_not_found_error(uniform))
}
pub fn uniform_3f<T: AsRef<[f32;3]>>(&self, uniform: &str, value: T) -> Result<()>{
self.uniform_location(uniform).map(|loc|
self.backend.uniform_3f(loc, value.as_ref())
).ok_or_else(|| uniform_not_found_error(uniform))
}
pub fn uniform_4f<T: AsRef<[f32;4]>>(&self, uniform: &str, value: T) -> Result<()>{
self.uniform_location(uniform).map(|loc|
self.backend.uniform_4f(loc, value.as_ref())
).ok_or_else(|| uniform_not_found_error(uniform))
}
pub fn uniform_rgba<T: AsRef<[f32;4]>>(&self, uniform: &str, value: T) -> Result<()>{
self.uniform_location(uniform).map(|loc|
self.backend.uniform_rgba(loc, value.as_ref())
).ok_or_else(|| uniform_not_found_error(uniform))
}
pub fn uniform_1i(&self, uniform: &str, value: i32) -> Result<()>{
self.uniform_location(uniform).map(|loc|
self.backend.uniform_1i(loc, value)
).ok_or_else(|| uniform_not_found_error(uniform))
}
pub fn uniform_2i(&self, uniform: &str, value: &[i32;2]) -> Result<()>{
self.uniform_location(uniform).map(|loc|
self.backend.uniform_2i(loc, value)
).ok_or_else(|| uniform_not_found_error(uniform))
}
pub fn uniform_3i(&self, uniform: &str, value: &[i32;3]) -> Result<()>{
self.uniform_location(uniform).map(|loc|
self.backend.uniform_3i(loc, value)
).ok_or_else(|| uniform_not_found_error(uniform))
}
pub fn uniform_4i(&self, uniform: &str, value: &[i32;4]) -> Result<()>{
self.uniform_location(uniform).map(|loc|
self.backend.uniform_4i(loc, value)
).ok_or_else(|| uniform_not_found_error(uniform))
}
pub fn uniform_matrix_4f(&self, uniform: &str, value: &[f32;16]) -> Result<()>{
self.uniform_location(uniform).map(|loc|
self.backend.uniform_matrix_4f(loc, value)
).ok_or_else(|| uniform_not_found_error(uniform))
}
pub fn uniform_tex(&self, uniform: &str, tex: &Texture, location: u32) -> Result<()>{
self.uniform_location(uniform).map(|loc|
self.backend.uniform_tex(loc, tex, location)
).ok_or_else(|| uniform_not_found_error(uniform))
}
pub fn uniform(&self, location: u32, value: UniformValue){
self.backend.uniform(location, value)
}
pub fn set_uniform(&self, uniform: &Uniform) -> Result<()>{
match uniform.to_location(self){
Some(Uniform::Name(_, _)) => unreachable!(),
Some(Uniform::Location(loc, value)) => {
self.backend.uniform(loc, value);
Ok(())
},
None => Err(uniform_not_found_error("from location")),
}
}
#[cfg(debug_assertions)]
pub fn set_uniforms<U: Borrow<Uniform>, I: IntoIterator<Item = U>>(&self, uniforms: I) -> Result<()>{
let errors = uniforms.into_iter().filter_map(|uniform| {
self.set_uniform(uniform.borrow()).err()
}).collect::<Vec<_>>();
if errors.is_empty(){
Ok(())
}else{
Err(::Error::new(::ErrorKind::UniformNotFound,
errors[1..].iter().fold(format!("{}", errors[0]),
|acc, e| acc + "\n" + &format!("{}", e)).as_ref()))
}
}
#[cfg(not(debug_assertions))]
pub fn set_uniforms<U: Borrow<Uniform>, I: IntoIterator<Item = U>>(&self, uniforms: I) -> Result<()>{
let errors = uniforms.into_iter().filter_map(|uniform| {
self.set_uniform(uniform.borrow()).err()
}).count();
if errors == 0{
Ok(())
}else{
Err(::Error::new(::ErrorKind::UniformNotFound, None))
}
}
pub fn set_uniform_at(&self, array_name: &str, idx: usize, uniform: &Uniform) -> Result<()>{
match uniform{
Uniform::Name(name, value) => {
let uniform_name = array_name.to_string() + "[" + &idx.to_string() + "]." + name;
match self.uniform_location(&uniform_name){
Some(loc) =>{
self.uniform(loc, value.clone());
Ok(())
},
None => Err(uniform_not_found_error(&uniform_name))
}
},
Uniform::Location(loc, value) => {
self.backend.uniform(*loc, value.clone());
Ok(())
}
}
}
pub fn set_ubo<T, B: ::BufferRange<T>>(&self, name: &str, buffer: &B, binding_point: u32) -> Result<()>{
unsafe{
let block_index = self.gl().GetUniformBlockIndex(self.id(), to_c_str!(name));
if block_index != gl::INVALID_INDEX {
let offset = buffer.start() * mem::size_of::<T>();
let size = buffer.len() * mem::size_of::<T>();
self.state_mut().bind_base_buffer(
gl::UNIFORM_BUFFER,
binding_point,
buffer.id(),
offset as isize,
size as isize);
self.gl().UniformBlockBinding(self.id(), block_index, binding_point);
Ok(())
}else{
Err(uniform_not_found_error(&name))
}
}
}
pub fn uniform_block_size(&self, name: &str) -> Result<usize>{
unsafe{
let block_index = self.gl().GetUniformBlockIndex(self.id(), to_c_str!(name));
if block_index != gl::INVALID_INDEX {
let mut ubo_size = 0;
self.gl().GetActiveUniformBlockiv(self.id(), block_index,
gl::UNIFORM_BLOCK_DATA_SIZE, &mut ubo_size);
Ok(ubo_size as usize)
}else{
Err(uniform_not_found_error(&name))
}
}
}
}
fn parse_compiler_error(
error: &str,
shader_type: GLenum,
includes: &[(String, String)],
original_source: &str,
full_src: &str,
line_tracker: Option<&FileLineTracker>) -> ::Error
{
#[cfg(not(feature = "webgl"))]
let re_one_error = Regex::new(r"([0-9]+)\(([0-9]+)\)\s:\s(.*)$").unwrap();
#[cfg(feature = "webgl")]
let re_one_error = Regex::new(r"ERROR: ([0-9]+):([0-9]+):\s(.*)$").unwrap();
let mut error_msg = format!("failed to compile {}:\n", shader_type_to_str(shader_type));
let mut first_error = true;
for line in error.lines(){
let captures = re_one_error.captures(line);
if !first_error {
error_msg += "\n";
}else{
first_error = false;
}
if captures.is_some() && captures.as_ref().unwrap().len() >= 4{
let captures = captures.as_ref().unwrap();
let file = usize::from_str(&captures[1]).unwrap();
let line = usize::from_str(&captures[2]).unwrap();
let mapped_line = if file == 0 {
if let Some(line_tracker) = line_tracker {
if let Some(last_line) = line_tracker.max_line() {
if line > last_line {
Some(line)
}else{
line_tracker.original_line(line)
}
}else{
Some(line)
}
}else{
Some(line)
}
}else{
Some(line)
};
let error = captures[3].to_string();
let src = if file == 0 {
original_source
}else{
&includes[file - 1].1
};
if let Some(line) = mapped_line {
let first_line = if line >= 5 { line - 5 } else { 0 };
let take = if line >= 5 { 4 } else { 1 };
let line_num_spaces = line.to_string().len();
let line_num_spaces = " ".repeat(line_num_spaces);
let mut source_slice = format!("{} | \n", line_num_spaces);
let mut lines = src.lines();
source_slice.extend(lines.by_ref()
.skip(first_line)
.take(take)
.map(|line| format!("{} | {}\n", line_num_spaces, line)));
source_slice.extend(lines.next().map(|line_str| format!("{} | {}\n", line, line_str)));
source_slice.extend(lines
.take(4)
.map(|line| format!("{} | {}\n", line_num_spaces, line)));
source_slice += &format!("{} |", line_num_spaces);
error_msg += &format!("{}\n{}--> {}{}{}\n{}\n",
error,
line_num_spaces,
if file == 0 { "" } else { &includes[file - 1].0 },
if file == 0 { "" } else { ":" },
line,
source_slice);
}else{
let first_line = if line >= 5 { line - 5 } else { 0 };
let take = if line >= 5 { 4 } else { 1 };
let line_num_spaces = line.to_string().len();
let line_num_spaces = " ".repeat(line_num_spaces);
let mut source_slice = format!("{} | \n", line_num_spaces);
let mut lines = full_src.lines();
source_slice.extend(lines.by_ref()
.skip(first_line)
.take(take)
.map(|line| format!("{} | {}\n", line_num_spaces, line)));
source_slice.extend(lines.next().map(|line_str| format!("{} | {}\n", line, line_str)));
source_slice.extend(lines
.take(4)
.map(|line| format!("{} | {}\n", line_num_spaces, line)));
source_slice += &format!("{} |", line_num_spaces);
error_msg += &format!("Couldn't find line in original file (probably added from settings defines), showing lines from final full src: {}\n{}--> {}{}{}\n{}\n",
error,
line_num_spaces,
if file == 0 { "" } else { &includes[file - 1].0 },
if file == 0 { "" } else { ":" },
line,
source_slice);
}
}else{
error_msg += line;
}
}
::Error::new(::ErrorKind::CompileError, error_msg.as_ref())
}
trait Backend: ::std::fmt::Debug {
fn id(&self) -> GLuint;
fn gl(&self) -> &Gl;
fn state(&self) -> &State<'static>;
fn state_mut(&self) -> &mut State<'static>;
#[cfg(not(feature="webgl"))]
fn compile_shader(&mut self,
ty: GLenum,
src: &str,
includes: &[(String, String)],
original_source: &str,
line_trackers: Option<&FileLineTracker>) -> Result<GLuint>
{
unsafe {
let shader = self.gl().CreateShader(ty);
let shader_source = std::ffi::CString::new(src).unwrap();
self.gl().ShaderSource(shader, 1, &shader_source.as_ptr(), ptr::null());
self.gl().CompileShader(shader);
let mut status = gl::FALSE as GLint;
self.gl().GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);
if status != (gl::TRUE as GLint) {
let mut len = 0;
self.gl().GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
if len > 1 {
let mut buf = Vec::with_capacity((len-1) as usize);
buf.set_len((len-1) as usize);
self.gl().GetShaderInfoLog(shader, len-1, ptr::null_mut(), buf.as_mut_ptr() as *mut i8);
let error = from_c_str!(buf.as_ptr());
Err(parse_compiler_error(error, ty, includes, original_source, src, line_trackers))
}else{
Err(::Error::new(::ErrorKind::CompileError,
format!("failed to compile {} but no log
full shader:\n {}", shader_type_to_str(ty), src).as_ref()))
}
}else{
self.gl().AttachShader(self.id(), shader);
self.shaders_mut().push(shader);
Ok(shader)
}
}
}
#[cfg(feature="webgl")]
fn compile_shader(&mut self,
ty: GLenum,
src: &str,
includes: &[(String, String)],
original_source: &str,
line_trackers: Option<&FileLineTracker>) -> Result<GLuint>
{
unsafe {
let shader = self.gl().CreateShader(ty);
self.gl().ShaderSource(shader, src);
self.gl().CompileShader(shader);
let mut status = gl::FALSE as GLint;
self.gl().GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);
if status != (gl::TRUE as GLint) {
let error = self.gl().GetShaderInfoLog(shader);
if let Some(error) = error {
Err(parse_compiler_error(&error, ty, includes, original_source, src, line_trackers))
}else{
Err(::Error::new(::ErrorKind::CompileError,
format!("failed to compile {} but no log
full shader:\n {}", shader_type_to_str(ty), src).as_ref()))
}
}else{
self.gl().AttachShader(self.id(), shader);
self.shaders_mut().push(shader);
Ok(shader)
}
}
}
fn bind_attributes(&self, bindings: &dyn attributes::Bindings){
unsafe{
for (name, location) in bindings.attribute_bindings() {
#[cfg(not(feature="webgl"))]
self.gl().BindAttribLocation(self.id(), *location, to_c_str!(name.clone()));
#[cfg(feature="webgl")]
self.gl().BindAttribLocation(self.id(), *location, name);
}
}
}
#[cfg(not(feature="webgl"))]
fn link(&self) -> Result<GLuint> {
let mut status = gl::FALSE as GLint;
unsafe {
self.gl().LinkProgram(self.id());
self.gl().GetProgramiv(self.id(), gl::LINK_STATUS, &mut status);
if status != (gl::TRUE as GLint) {
let mut len = 0;
self.gl().GetProgramiv(self.id(), gl::INFO_LOG_LENGTH, &mut len);
if len>1{
let mut buf = Vec::with_capacity((len) as usize);
buf.set_len((len) as usize);
self.gl().GetProgramInfoLog(self.id(), len, ptr::null_mut(), buf.as_mut_ptr() as *mut i8);
Err(::Error::new(::ErrorKind::LinkError,
format!("glsl program failed to link: {}\n", str::from_utf8(&buf).unwrap()).as_ref()))
}else{
Err(::Error::new(::ErrorKind::LinkError,
"glsl program failed linking but no log"))
}
}else{
Ok(self.id())
}
}
}
#[cfg(feature="webgl")]
fn link(&self) -> Result<GLuint> {
let mut status = gl::FALSE as GLint;
unsafe {
self.gl().LinkProgram(self.id());
self.gl().GetProgramiv(self.id(), gl::LINK_STATUS, &mut status);
if status != (gl::TRUE as GLint) {
let error = self.gl().GetProgramInfoLog(self.id());
if let Some(error) = error {
Err(::Error::new(::ErrorKind::LinkError,
format!("glsl program failed to link: {}\n", error).as_ref()))
}else{
Err(::Error::new(::ErrorKind::LinkError,
"glsl program failed linking but no log"))
}
}else{
Ok(self.id())
}
}
}
fn shaders(&self) -> &Vec<GLuint>;
fn shaders_mut(&mut self) -> &mut Vec<GLuint>;
fn uniform_1f(&self, loc: u32, value: f32);
fn uniform_2f(&self, loc: u32, value: &[f32;2]);
fn uniform_3f(&self, loc: u32, value: &[f32;3]);
fn uniform_4f(&self, loc: u32, value: &[f32;4]);
fn uniform_rgba(&self, loc: u32, c: &[f32;4]);
fn uniform_1i(&self, loc: u32, value: i32);
fn uniform_2i(&self, loc: u32, value: &[i32;2]);
fn uniform_3i(&self, loc: u32, value: &[i32;3]);
fn uniform_4i(&self, loc: u32, value: &[i32;4]);
fn uniform_matrix_4f(&self, loc: u32, value: &[f32;16]);
fn uniform_tex(&self, loc: u32, tex: &Texture, location: u32);
fn uniform(&self, loc: u32, value: UniformValue);
}
#[derive(Debug)]
struct BackendBind {
id: GLuint,
shaders: Vec<GLuint>,
gl: StateRef,
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
#[derive(Debug)]
struct BackendDsa {
id: GLuint,
shaders: Vec<GLuint>,
gl: StateRef,
}
impl BackendBind {
fn bind(&self) {
if self.gl.state().program().id() != self.id {
self.gl.state_mut().bind_program(self.id);
}
}
}
impl Drop for BackendBind {
fn drop(&mut self){
unsafe{
for s in self.shaders.iter(){
self.gl.DetachShader(self.id, *s);
self.gl.DeleteShader(*s);
}
self.gl.DeleteProgram(self.id);
}
}
}
impl Backend for BackendBind {
fn id(&self) -> GLuint{
self.id
}
fn gl(&self) -> &Gl{
&self.gl
}
fn state(&self) -> &State<'static>{
self.gl.state()
}
fn state_mut(&self) -> &mut State<'static>{
self.gl.state_mut()
}
fn shaders(&self) -> &Vec<GLuint>{
&self.shaders
}
fn shaders_mut(&mut self) -> &mut Vec<GLuint>{
&mut self.shaders
}
fn uniform_1f(&self, loc: u32, value: f32) {
self.bind();
unsafe{
self.gl.Uniform1f( loc as i32, value );
}
}
fn uniform_2f(&self, loc: u32, value: &[f32;2]) {
self.bind();
unsafe{
self.gl.Uniform2fv( loc as i32, 1, &value[0] );
}
}
fn uniform_3f(&self, loc: u32, value: &[f32;3]) {
self.bind();
unsafe{
self.gl.Uniform3fv( loc as i32, 1, &value[0] );
}
}
fn uniform_4f(&self, loc: u32, value: &[f32;4]) {
self.bind();
unsafe{
self.gl.Uniform4fv( loc as i32, 1, &value[0] );
}
}
fn uniform_rgba(&self, loc: u32, c: &[f32;4]) {
self.bind();
unsafe{
self.gl.Uniform4fv( loc as i32, 1, &c[0] );
}
}
fn uniform_1i(&self, loc: u32, value: i32) {
self.bind();
unsafe{
self.gl.Uniform1i( loc as i32, value );
}
}
fn uniform_2i(&self, loc: u32, value: &[i32;2]) {
self.bind();
unsafe{
self.gl.Uniform2iv( loc as i32, 1, &value[0] );
}
}
fn uniform_3i(&self, loc: u32, value: &[i32;3]) {
self.bind();
unsafe{
self.gl.Uniform3iv( loc as i32, 1, &value[0] );
}
}
fn uniform_4i(&self, loc: u32, value: &[i32;4]) {
self.bind();
unsafe{
self.gl.Uniform4iv( loc as i32, 1, &value[0] );
}
}
fn uniform_matrix_4f(&self, loc: u32, value: &[f32;16]) {
self.bind();
unsafe{
self.gl.UniformMatrix4fv( loc as i32, 1, gl::FALSE, &value[0] );
}
}
fn uniform_tex(&self, loc: u32, tex: &Texture, location: u32) {
self.bind();
unsafe{
self.gl.ActiveTexture(gl::TEXTURE0 + location);
self.gl.BindTexture(tex.target(), tex.id());
self.uniform_1i(loc, location as i32);
self.gl.ActiveTexture(gl::TEXTURE0);
self.gl.BindSampler(location, 0);
}
}
fn uniform(&self, location: u32, value: UniformValue){
self.bind();
unsafe{
match value{
UniformValue::Float(v) => self.gl.Uniform1f( location as i32, v ),
UniformValue::Int(v) => self.gl.Uniform1i( location as i32, v ),
UniformValue::Bool(v) => self.gl.Uniform1i( location as i32, v as i32 ),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
UniformValue::UInt(v) => self.gl.Uniform1ui( location as i32, v ),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
UniformValue::UVec2(v0,v1) => self.gl.Uniform2ui( location as i32, v0, v1 ),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
UniformValue::UVec3(v0,v1,v2) => self.gl.Uniform3ui( location as i32, v0, v1, v2 ),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
UniformValue::UVec4(v0,v1,v2,v3) => self.gl.Uniform4ui( location as i32, v0, v1, v2, v3 ),
UniformValue::Vec2(v0,v1) => self.gl.Uniform2f( location as i32, v0, v1 ),
UniformValue::Vec3(v0,v1,v2) => self.gl.Uniform3f( location as i32, v0, v1, v2 ),
UniformValue::Vec4(v0,v1,v2,v3) => self.gl.Uniform4f( location as i32, v0, v1, v2, v3 ),
UniformValue::IVec2(v0,v1) => self.gl.Uniform2i( location as i32, v0, v1 ),
UniformValue::IVec3(v0,v1,v2) => self.gl.Uniform3i( location as i32, v0, v1, v2 ),
UniformValue::IVec4(v0,v1,v2,v3) => self.gl.Uniform4i( location as i32, v0, v1, v2, v3 ),
UniformValue::Mat2(v) => self.gl.UniformMatrix2fv( location as i32, 1, gl::FALSE, v.as_ptr() as *const f32 ),
UniformValue::Mat3(v) => self.gl.UniformMatrix3fv( location as i32, 1, gl::FALSE, v.as_ptr() as *const f32 ),
UniformValue::Mat4(v) => self.gl.UniformMatrix4fv( location as i32, 1, gl::FALSE, v.as_ptr() as *const f32 ),
UniformValue::ArrayFloat(v) =>
self.gl.Uniform1fv(location as i32, v.len() as i32, v.as_ptr() as *const f32),
UniformValue::ArrayVec2(v) =>
self.gl.Uniform2fv(location as i32, v.len() as i32, v.as_ptr() as *const f32),
UniformValue::ArrayVec3(v) =>
self.gl.Uniform2fv(location as i32, v.len() as i32, v.as_ptr() as *const f32),
UniformValue::ArrayVec4(v) =>
self.gl.Uniform4fv(location as i32, v.len() as i32, v.as_ptr() as *const f32),
UniformValue::ArrayInt(v) =>
self.gl.Uniform1iv(location as i32, v.len() as i32, v.as_ptr() as *const i32),
UniformValue::ArrayIVec2(v) =>
self.gl.Uniform2iv(location as i32, v.len() as i32, v.as_ptr() as *const i32),
UniformValue::ArrayIVec3(v) =>
self.gl.Uniform3iv(location as i32, v.len() as i32, v.as_ptr() as *const i32),
UniformValue::ArrayIVec4(v) =>
self.gl.Uniform4iv(location as i32, v.len() as i32, v.as_ptr() as *const i32),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
UniformValue::ArrayUInt(v) =>
self.gl.Uniform1uiv(location as i32, v.len() as i32, v.as_ptr() as *const u32),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
UniformValue::ArrayUVec2(v) =>
self.gl.Uniform2uiv(location as i32, v.len() as i32, v.as_ptr() as *const u32),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
UniformValue::ArrayUVec3(v) =>
self.gl.Uniform3uiv(location as i32, v.len() as i32, v.as_ptr() as *const u32),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
UniformValue::ArrayUVec4(v) =>
self.gl.Uniform4uiv(location as i32, v.len() as i32, v.as_ptr() as *const u32),
UniformValue::Texture(tex, tex_location, target) => {
self.gl.ActiveTexture(gl::TEXTURE0 + tex_location);
self.gl.BindTexture(target, tex);
self.gl.Uniform1i(location as i32, tex_location as i32 );
self.gl.ActiveTexture(gl::TEXTURE0);
self.gl.BindSampler(tex_location, 0);
},
UniformValue::TextureSampler(tex, tex_location, target, sampler) => {
self.gl.ActiveTexture(gl::TEXTURE0 + tex_location);
self.gl.BindTexture(target, tex);
self.gl.Uniform1i(location as i32, tex_location as i32 );
self.gl.ActiveTexture(gl::TEXTURE0);
self.gl.BindSampler(tex_location, sampler);
},
UniformValue::UniformBlockBinding(binding_point) => {
let block_index = location;
self.gl().UniformBlockBinding(self.id(), block_index, binding_point);
},
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
UniformValue::ShaderStorageBlockBinding(binding_point) => {
let block_index = location;
self.gl().ShaderStorageBlockBinding(self.id(), block_index, binding_point);
},
}
}
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
impl Drop for BackendDsa {
fn drop(&mut self){
unsafe{
for s in self.shaders.iter(){
self.gl.DetachShader(self.id, *s);
self.gl.DeleteShader(*s);
}
self.gl.DeleteProgram(self.id);
}
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
impl Backend for BackendDsa {
fn id(&self) -> GLuint{
self.id
}
fn gl(&self) -> &Gl{
&self.gl
}
fn state(&self) -> &State<'static>{
self.gl.state()
}
fn state_mut(&self) -> &mut State<'static>{
self.gl.state_mut()
}
fn shaders(&self) -> &Vec<GLuint>{
&self.shaders
}
fn shaders_mut(&mut self) -> &mut Vec<GLuint>{
&mut self.shaders
}
fn uniform_1f(&self, loc: u32, value: f32) {
unsafe{
self.gl.ProgramUniform1f(self.id, loc as i32, value );
}
}
fn uniform_2f(&self, loc: u32, value: &[f32;2]) {
unsafe{
self.gl.ProgramUniform2fv(self.id, loc as i32, 1, &value[0] );
}
}
fn uniform_3f(&self, loc: u32, value: &[f32;3]) {
unsafe{
self.gl.ProgramUniform3fv(self.id, loc as i32, 1, &value[0] );
}
}
fn uniform_4f(&self, loc: u32, value: &[f32;4]) {
unsafe{
self.gl.ProgramUniform4fv(self.id, loc as i32, 1, &value[0] );
}
}
fn uniform_rgba(&self, loc: u32, c: &[f32;4]) {
unsafe{
self.gl.ProgramUniform4fv(self.id, loc as i32, 1, &c[0] );
}
}
fn uniform_1i(&self, loc: u32, value: i32) {
unsafe{
self.gl.ProgramUniform1i(self.id, loc as i32, value );
}
}
fn uniform_2i(&self, loc: u32, value: &[i32;2]) {
unsafe{
self.gl.ProgramUniform2iv(self.id, loc as i32, 1, &value[0] );
}
}
fn uniform_3i(&self, loc: u32, value: &[i32;3]) {
unsafe{
self.gl.ProgramUniform3iv(self.id, loc as i32, 1, &value[0] );
}
}
fn uniform_4i(&self, loc: u32, value: &[i32;4]) {
unsafe{
self.gl.ProgramUniform4iv(self.id, loc as i32, 1, &value[0] );
}
}
fn uniform_matrix_4f(&self, loc: u32, value: &[f32;16]) {
unsafe{
self.gl.ProgramUniformMatrix4fv(self.id, loc as i32, 1, gl::FALSE, &value[0] );
}
}
fn uniform_tex(&self, loc: u32, tex: &Texture, location: u32) {
unsafe{
self.gl.BindTextures(location, 1, &tex.id());
self.uniform_1i(loc, location as i32);
self.gl.BindSampler(location, 0);
}
}
fn uniform(&self, location: u32, value: UniformValue){
unsafe{
match value{
UniformValue::Float(v) => self.gl.ProgramUniform1f( self.id, location as i32, v ),
UniformValue::Int(v) => self.gl.ProgramUniform1i( self.id, location as i32, v ),
UniformValue::Bool(v) => self.gl.ProgramUniform1i( self.id, location as i32, v as i32 ),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
UniformValue::UInt(v) => self.gl.ProgramUniform1ui( self.id, location as i32, v ),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
UniformValue::UVec2(v0,v1) => self.gl.ProgramUniform2ui( self.id, location as i32, v0, v1 ),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
UniformValue::UVec3(v0,v1,v2) => self.gl.ProgramUniform3ui( self.id, location as i32, v0, v1, v2 ),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
UniformValue::UVec4(v0,v1,v2,v3) => self.gl.ProgramUniform4ui( self.id, location as i32, v0, v1, v2, v3 ),
UniformValue::Vec2(v0,v1) => self.gl.ProgramUniform2f( self.id, location as i32, v0, v1 ),
UniformValue::Vec3(v0,v1,v2) => self.gl.ProgramUniform3f( self.id, location as i32, v0, v1, v2 ),
UniformValue::Vec4(v0,v1,v2,v3) => self.gl.ProgramUniform4f( self.id, location as i32, v0, v1, v2, v3 ),
UniformValue::IVec2(v0,v1) => self.gl.ProgramUniform2i( self.id, location as i32, v0, v1 ),
UniformValue::IVec3(v0,v1,v2) => self.gl.ProgramUniform3i( self.id, location as i32, v0, v1, v2 ),
UniformValue::IVec4(v0,v1,v2,v3) => self.gl.ProgramUniform4i( self.id, location as i32, v0, v1, v2, v3 ),
UniformValue::Mat2(v) =>
self.gl.ProgramUniformMatrix2fv( self.id, location as i32, 1, gl::FALSE, v.as_ptr() as *const f32 ),
UniformValue::Mat3(v) =>
self.gl.ProgramUniformMatrix3fv( self.id, location as i32, 1, gl::FALSE, v.as_ptr() as *const f32 ),
UniformValue::Mat4(v) =>
self.gl.ProgramUniformMatrix4fv( self.id, location as i32, 1, gl::FALSE, v.as_ptr() as *const f32 ),
UniformValue::ArrayFloat(v) =>
self.gl.ProgramUniform1fv(self.id, location as i32, v.len() as i32, v.as_ptr() as *const f32),
UniformValue::ArrayVec2(v) =>
self.gl.ProgramUniform2fv(self.id, location as i32, v.len() as i32, v.as_ptr() as *const f32),
UniformValue::ArrayVec3(v) =>
self.gl.ProgramUniform3fv(self.id, location as i32, v.len() as i32, v.as_ptr() as *const f32),
UniformValue::ArrayVec4(v) =>
self.gl.ProgramUniform4fv(self.id, location as i32, v.len() as i32, v.as_ptr() as *const f32),
UniformValue::ArrayInt(v) =>
self.gl.ProgramUniform1iv(self.id, location as i32, v.len() as i32, v.as_ptr() as *const i32),
UniformValue::ArrayIVec2(v) =>
self.gl.ProgramUniform2iv(self.id, location as i32, v.len() as i32, v.as_ptr() as *const i32),
UniformValue::ArrayIVec3(v) =>
self.gl.ProgramUniform3iv(self.id, location as i32, v.len() as i32, v.as_ptr() as *const i32),
UniformValue::ArrayIVec4(v) =>
self.gl.ProgramUniform4iv(self.id, location as i32, v.len() as i32, v.as_ptr() as *const i32),
UniformValue::ArrayUInt(v) =>
self.gl.ProgramUniform1uiv(self.id, location as i32, v.len() as i32, v.as_ptr() as *const u32),
UniformValue::ArrayUVec2(v) =>
self.gl.ProgramUniform2uiv(self.id, location as i32, v.len() as i32, v.as_ptr() as *const u32),
UniformValue::ArrayUVec3(v) =>
self.gl.ProgramUniform3uiv(self.id, location as i32, v.len() as i32, v.as_ptr() as *const u32),
UniformValue::ArrayUVec4(v) =>
self.gl.ProgramUniform4uiv(self.id, location as i32, v.len() as i32, v.as_ptr() as *const u32),
UniformValue::Texture(tex, tex_location, _target) => {
self.gl.BindTextures(tex_location, 1, &tex);
self.gl.ProgramUniform1i(self.id, location as i32, tex_location as i32 );
self.gl.BindSampler(tex_location, 0);
},
UniformValue::TextureSampler(tex, tex_location, _target, sampler) => {
self.gl.BindTextures(tex_location, 1, &tex);
self.gl.ProgramUniform1i(self.id, location as i32, tex_location as i32 );
self.gl.BindSampler(tex_location, sampler);
},
UniformValue::UniformBlockBinding(binding_point) => {
let block_index = location;
self.gl().UniformBlockBinding(self.id(), block_index, binding_point);
},
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
UniformValue::ShaderStorageBlockBinding(binding_point) => {
let block_index = location;
self.gl().ShaderStorageBlockBinding(self.id(), block_index, binding_point);
},
}
}
}
}
fn shader_type_to_str(shader_ty: GLenum) -> String{
match shader_ty{
gl::VERTEX_SHADER => "vertex shader",
gl::FRAGMENT_SHADER => "fragment shader",
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
gl::GEOMETRY_SHADER => "geometry shader",
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
gl::COMPUTE_SHADER => "compute shader",
_ => "unkown shader",
}.to_string()
}
#[cfg(debug_assertions)]
fn uniform_not_found_error(uniform: &str) -> ::Error{
::Error::new(::ErrorKind::UniformNotFound,
format!("Uniform {} not found", uniform).as_ref())
}
#[cfg(not(debug_assertions))]
fn uniform_not_found_error(_uniform: &str) -> ::Error{
::Error::new(::ErrorKind::UniformNotFound, None)
}
#[derive(Clone, Debug, PartialEq)]
pub enum UniformValue{
Float(f32),
Int(i32),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
UInt(u32),
Bool(bool),
Mat2([[f32;2];2]),
Mat3([[f32;3];3]),
Mat4([[f32;4];4]),
Vec2(f32,f32),
Vec3(f32,f32,f32),
Vec4(f32,f32,f32,f32),
IVec2(i32,i32),
IVec3(i32,i32,i32),
IVec4(i32,i32,i32,i32),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
UVec2(u32,u32),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
UVec3(u32,u32,u32),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
UVec4(u32,u32,u32,u32),
ArrayFloat(Vec<f32>),
ArrayVec2(Vec<[f32;2]>),
ArrayVec3(Vec<[f32;3]>),
ArrayVec4(Vec<[f32;4]>),
ArrayInt(Vec<i32>),
ArrayIVec2(Vec<[i32;2]>),
ArrayIVec3(Vec<[i32;3]>),
ArrayIVec4(Vec<[i32;4]>),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
ArrayUInt(Vec<u32>),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
ArrayUVec2(Vec<[u32;2]>),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
ArrayUVec3(Vec<[u32;3]>),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
ArrayUVec4(Vec<[u32;4]>),
Texture(u32, u32, u32),
TextureSampler(u32, u32, u32, u32),
UniformBlockBinding(u32),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
ShaderStorageBlockBinding(u32),
}
#[derive(Clone, Debug, PartialEq)]
pub enum Uniform{
Name(String, UniformValue),
Location(u32, UniformValue),
}
impl Uniform{
pub fn to_location(&self, program: &Program) -> Option<Uniform> {
match self {
Uniform::Name(name, value) => match value {
UniformValue::UniformBlockBinding(_binding_point) =>
program.uniform_block_index(name)
.map(|loc| Uniform::Location(loc, value.clone())),
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
UniformValue::ShaderStorageBlockBinding(_binding_point) =>
program.shader_storage_block_index(name)
.map(|loc| Uniform::Location(loc, value.clone())),
_ => program.uniform_location(name)
.map(|loc| Uniform::Location(loc, value.clone())),
},
Uniform::Location(_loc, _v) => Some(self.clone()),
}
}
}
pub fn uniform<Str: AsRef<str>, T: AsUniform<U>, U: ?Sized>(name: Str, value: &T) -> Uniform{
Uniform::Name(
name.as_ref().to_string(),
value.as_uniform(),
)
}
pub fn uniform_location<T: AsUniform<U>, U: ?Sized>(location: u32, value: &T) -> Uniform{
Uniform::Location (
location,
value.as_uniform(),
)
}
pub trait AsUniform<T: ?Sized>{
fn as_uniform(&self) -> UniformValue;
}
impl AsUniform<UniformValue> for UniformValue{
fn as_uniform(&self) -> UniformValue{
self.clone()
}
}
impl AsUniform<f32> for f32{
fn as_uniform(&self) -> UniformValue{
UniformValue::Float(*self)
}
}
impl AsUniform<i32> for i32{
fn as_uniform(&self) -> UniformValue{
UniformValue::Int(*self)
}
}
impl AsUniform<bool> for bool{
fn as_uniform(&self) -> UniformValue{
UniformValue::Bool(*self)
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
impl AsUniform<u32> for u32{
fn as_uniform(&self) -> UniformValue{
UniformValue::UInt(*self)
}
}
impl<A: AsRef<f32>> AsUniform<dyn AsRef<f32>> for A{
fn as_uniform(&self) -> UniformValue{
UniformValue::Float(*self.as_ref())
}
}
impl<A: AsRef<i32>> AsUniform<dyn AsRef<i32>> for A{
fn as_uniform(&self) -> UniformValue{
UniformValue::Int(*self.as_ref())
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
impl<A: AsRef<u32>> AsUniform<dyn AsRef<u32>> for A{
fn as_uniform(&self) -> UniformValue{
UniformValue::UInt(*self.as_ref())
}
}
impl AsUniform<[[f32;4];4]> for [[f32;4];4]{
fn as_uniform(&self) -> UniformValue{
UniformValue::Mat4(*self)
}
}
impl<'a> AsUniform<&'a [[f32;4];4]> for &'a [[f32;4];4]{
fn as_uniform(&self) -> UniformValue{
UniformValue::Mat4(**self)
}
}
impl<T:AsRef<[[f32;4];4]>> AsUniform<dyn AsRef<[[f32;4];4]>> for T{
fn as_uniform(&self) -> UniformValue{
UniformValue::Mat4(*self.as_ref())
}
}
impl AsUniform<[[f32;3];3]> for [[f32;3];3]{
fn as_uniform(&self) -> UniformValue{
UniformValue::Mat3(*self)
}
}
impl<'a> AsUniform<&'a [[f32;3];3]> for &'a [[f32;3];3]{
fn as_uniform(&self) -> UniformValue{
UniformValue::Mat3(**self)
}
}
impl AsUniform<[f32;9]> for [f32;9]{
fn as_uniform(&self) -> UniformValue{
unsafe{
UniformValue::Mat3(mem::transmute(*self))
}
}
}
impl<'a> AsUniform<&'a [f32;9]> for &'a [f32;9]{
fn as_uniform(&self) -> UniformValue{
unsafe{
UniformValue::Mat3(mem::transmute(**self))
}
}
}
impl<T:AsRef<[[f32;3];3]>> AsUniform<dyn AsRef<[[f32;3];3]>> for T{
fn as_uniform(&self) -> UniformValue{
UniformValue::Mat3(*self.as_ref())
}
}
impl<T:AsRef<[f32;9]>> AsUniform<dyn AsRef<[f32;9]>> for T{
fn as_uniform(&self) -> UniformValue{
unsafe{
UniformValue::Mat3(mem::transmute(*self.as_ref()))
}
}
}
impl AsUniform<[f32;2]> for [f32;2]{
fn as_uniform(&self) -> UniformValue{
UniformValue::Vec2(self[0], self[1])
}
}
impl<'a> AsUniform<&'a [f32;2]> for &'a [f32;2]{
fn as_uniform(&self) -> UniformValue{
UniformValue::Vec2(self[0], self[1])
}
}
impl<T:AsRef<[f32;2]>> AsUniform<dyn AsRef<[f32;2]>> for T{
fn as_uniform(&self) -> UniformValue{
UniformValue::Vec2(self.as_ref()[0], self.as_ref()[1])
}
}
impl AsUniform<[f32;3]> for [f32;3]{
fn as_uniform(&self) -> UniformValue{
UniformValue::Vec3(self[0], self[1], self[2])
}
}
impl<'a> AsUniform<&'a [f32;3]> for &'a [f32;3]{
fn as_uniform(&self) -> UniformValue{
UniformValue::Vec3(self[0], self[1], self[2])
}
}
impl<T:AsRef<[f32;3]>> AsUniform<dyn AsRef<[f32;3]>> for T{
fn as_uniform(&self) -> UniformValue{
UniformValue::Vec3(self.as_ref()[0], self.as_ref()[1], self.as_ref()[2])
}
}
impl AsUniform<[f32;4]> for [f32;4]{
fn as_uniform(&self) -> UniformValue{
UniformValue::Vec4(self[0], self[1], self[2], self[3])
}
}
impl<'a> AsUniform<&'a [f32;4]> for &'a [f32;4]{
fn as_uniform(&self) -> UniformValue{
UniformValue::Vec4(self[0], self[1], self[2], self[3])
}
}
impl<T:AsRef<[f32;4]>> AsUniform<dyn AsRef<[f32;4]>> for T{
fn as_uniform(&self) -> UniformValue{
UniformValue::Vec4(self.as_ref()[0], self.as_ref()[1], self.as_ref()[2], self.as_ref()[3])
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
impl AsUniform<[u32;2]> for [u32;2]{
fn as_uniform(&self) -> UniformValue{
UniformValue::UVec2(self[0], self[1])
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
impl<'a> AsUniform<&'a [u32;2]> for &'a [u32;2]{
fn as_uniform(&self) -> UniformValue{
UniformValue::UVec2(self[0], self[1])
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
impl<T:AsRef<[u32;2]>> AsUniform<dyn AsRef<[u32;2]>> for T{
fn as_uniform(&self) -> UniformValue{
UniformValue::UVec2(self.as_ref()[0], self.as_ref()[1])
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
impl AsUniform<[u32;3]> for [u32;3]{
fn as_uniform(&self) -> UniformValue{
UniformValue::UVec3(self[0], self[1], self[2])
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
impl<'a> AsUniform<&'a [u32;3]> for &'a [u32;3]{
fn as_uniform(&self) -> UniformValue{
UniformValue::UVec3(self[0], self[1], self[2])
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
impl<T:AsRef<[u32;3]>> AsUniform<dyn AsRef<[u32;3]>> for T{
fn as_uniform(&self) -> UniformValue{
UniformValue::UVec3(self.as_ref()[0], self.as_ref()[1], self.as_ref()[2])
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
impl AsUniform<[u32;4]> for [u32;4]{
fn as_uniform(&self) -> UniformValue{
UniformValue::UVec4(self[0], self[1], self[2], self[3])
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
impl<'a> AsUniform<&'a [u32;4]> for &'a [u32;4]{
fn as_uniform(&self) -> UniformValue{
UniformValue::UVec4(self[0], self[1], self[2], self[3])
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
impl<T:AsRef<[u32;4]>> AsUniform<dyn AsRef<[u32;4]>> for T{
fn as_uniform(&self) -> UniformValue{
UniformValue::UVec4(self.as_ref()[0], self.as_ref()[1], self.as_ref()[2], self.as_ref()[3])
}
}
impl AsUniform<[i32;2]> for [i32;2]{
fn as_uniform(&self) -> UniformValue{
UniformValue::IVec2(self[0], self[1])
}
}
impl<'a> AsUniform<&'a [i32;2]> for &'a [i32;2]{
fn as_uniform(&self) -> UniformValue{
UniformValue::IVec2(self[0], self[1])
}
}
impl<T:AsRef<[i32;2]>> AsUniform<dyn AsRef<[i32;2]>> for T{
fn as_uniform(&self) -> UniformValue{
UniformValue::IVec2(self.as_ref()[0], self.as_ref()[1])
}
}
impl AsUniform<[i32;3]> for [i32;3]{
fn as_uniform(&self) -> UniformValue{
UniformValue::IVec3(self[0], self[1], self[2])
}
}
impl<'a> AsUniform<&'a [i32;3]> for &'a [i32;3]{
fn as_uniform(&self) -> UniformValue{
UniformValue::IVec3(self[0], self[1], self[2])
}
}
impl<T:AsRef<[i32;3]>> AsUniform<dyn AsRef<[i32;3]>> for T{
fn as_uniform(&self) -> UniformValue{
UniformValue::IVec3(self.as_ref()[0], self.as_ref()[1], self.as_ref()[2])
}
}
impl AsUniform<[i32;4]> for [i32;4]{
fn as_uniform(&self) -> UniformValue{
UniformValue::IVec4(self[0], self[1], self[2], self[3])
}
}
impl<'a> AsUniform<&'a [i32;4]> for &'a [i32;4]{
fn as_uniform(&self) -> UniformValue{
UniformValue::IVec4(self[0], self[1], self[2], self[3])
}
}
impl<T:AsRef<[i32;4]>> AsUniform<dyn AsRef<[i32;4]>> for T{
fn as_uniform(&self) -> UniformValue{
UniformValue::IVec4(self.as_ref()[0], self.as_ref()[1], self.as_ref()[2], self.as_ref()[3])
}
}
impl<T: Borrow<Texture>> AsUniform<Texture> for T{
fn as_uniform(&self) -> UniformValue{
UniformValue::Texture(self.borrow().id(), 0, self.borrow().target())
}
}
impl<T: Borrow<Texture>> AsUniform<(Texture, u32)> for (T, u32){
fn as_uniform(&self) -> UniformValue{
UniformValue::Texture(self.0.borrow().id(), self.1, self.0.borrow().target())
}
}
impl<C: Borrow<CubeMap>> AsUniform<CubeMap> for C{
fn as_uniform(&self) -> UniformValue{
UniformValue::Texture(self.borrow().id(), 0, gl::TEXTURE_CUBE_MAP)
}
}
impl<C: Borrow<CubeMap>> AsUniform<(CubeMap,u32)> for (C,u32){
fn as_uniform(&self) -> UniformValue{
UniformValue::Texture(self.0.borrow().id(), self.1, gl::TEXTURE_CUBE_MAP)
}
}
impl<'a, T: Borrow<TextureSampler<'a>>> AsUniform<(TextureSampler<'a>,u32)> for (T,u32){
fn as_uniform(&self) -> UniformValue{
UniformValue::TextureSampler(self.0.borrow().texture_id(), self.1, self.0.borrow().target(), self.0.borrow().sampler_id())
}
}
impl<'a, T: Borrow<TextureSampler<'a>>> AsUniform<TextureSampler<'a>> for T{
fn as_uniform(&self) -> UniformValue{
UniformValue::TextureSampler(self.borrow().texture_id(), 0, self.borrow().target(), self.borrow().sampler_id())
}
}
impl<'a, C: Borrow<CubeMapSampler<'a>>> AsUniform<(CubeMapSampler<'a>,u32)> for (C,u32){
fn as_uniform(&self) -> UniformValue{
UniformValue::TextureSampler(self.0.borrow().cubemap_id(), self.1, gl::TEXTURE_CUBE_MAP, self.0.borrow().sampler_id())
}
}
impl<'a, C: Borrow<CubeMapSampler<'a>>> AsUniform<CubeMapSampler<'a>> for C{
fn as_uniform(&self) -> UniformValue{
UniformValue::TextureSampler(self.borrow().cubemap_id(), 0, gl::TEXTURE_CUBE_MAP, self.borrow().sampler_id())
}
}
use std::ops::Deref;
impl<U: Deref<Target=T>, T: AsUniform<dyn AsRef<f32>>> AsUniform<dyn Deref<Target=dyn AsRef<f32>>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<[f32;2]>> AsUniform<dyn Deref<Target=[f32;2]>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<dyn AsRef<[f32;2]>>> AsUniform<dyn Deref<Target=dyn AsRef<[f32;2]>>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<[f32;3]>> AsUniform<dyn Deref<Target=[f32;3]>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<dyn AsRef<[f32;3]>>> AsUniform<dyn Deref<Target=dyn AsRef<[f32;3]>>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<dyn AsRef<[f32;4]>>> AsUniform<dyn Deref<Target=[f32;4]>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<[[f32;3];3]>> AsUniform<dyn Deref<Target=[[f32;3];3]>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<dyn AsRef<[[f32;3];3]>>> AsUniform<dyn Deref<Target=dyn AsRef<[[f32;3];3]>>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<[[f32;4];4]>> AsUniform<dyn Deref<Target=[[f32;4];4]>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<dyn AsRef<[[f32;4];4]>>> AsUniform<dyn Deref<Target=dyn AsRef<[[f32;4];4]>>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<i32>> AsUniform<dyn Deref<Target=i32>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<dyn AsRef<i32>>> AsUniform<dyn Deref<Target=dyn AsRef<i32>>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<[i32;2]>> AsUniform<dyn Deref<Target=[i32;2]>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<dyn AsRef<[i32;2]>>> AsUniform<dyn Deref<Target=dyn AsRef<[i32;2]>>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<[i32;3]>> AsUniform<dyn Deref<Target=[i32;3]>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<dyn AsRef<[i32;3]>>> AsUniform<dyn Deref<Target=dyn AsRef<[i32;3]>>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<dyn AsRef<[i32;4]>>> AsUniform<dyn Deref<Target=[i32;4]>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<u32>> AsUniform<dyn Deref<Target=u32>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<dyn AsRef<u32>>> AsUniform<dyn Deref<Target=dyn AsRef<u32>>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<[u32;2]>> AsUniform<dyn Deref<Target=[u32;2]>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<dyn AsRef<[u32;2]>>> AsUniform<dyn Deref<Target=dyn AsRef<[u32;2]>>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<[u32;3]>> AsUniform<dyn Deref<Target=[u32;3]>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<dyn AsRef<[u32;3]>>> AsUniform<dyn Deref<Target=dyn AsRef<[u32;3]>>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl<U: Deref<Target=T>, T: AsUniform<dyn AsRef<[u32;4]>>> AsUniform<dyn Deref<Target=[u32;4]>> for U {
fn as_uniform(&self) -> UniformValue {
self.deref().as_uniform()
}
}
impl AsUniform<Vec<f32>> for Vec<f32>{
fn as_uniform(&self) -> UniformValue{
UniformValue::ArrayFloat(self.clone())
}
}
impl<U: AsRef<[f32;2]>> AsUniform<Vec<[f32;2]>> for Vec<U>{
fn as_uniform(&self) -> UniformValue{
UniformValue::ArrayVec2(self.iter().map(|v| *v.as_ref()).collect())
}
}
impl<U: AsRef<[f32;3]>> AsUniform<Vec<[f32;3]>> for Vec<U>{
fn as_uniform(&self) -> UniformValue{
UniformValue::ArrayVec3(self.iter().map(|v| *v.as_ref()).collect())
}
}
impl<U: AsRef<[f32;4]>> AsUniform<Vec<[f32;4]>> for Vec<U>{
fn as_uniform(&self) -> UniformValue{
UniformValue::ArrayVec4(self.iter().map(|v| *v.as_ref()).collect())
}
}
impl AsUniform<Vec<i32>> for Vec<i32>{
fn as_uniform(&self) -> UniformValue{
UniformValue::ArrayInt(self.clone())
}
}
impl<U: AsRef<[i32;2]>> AsUniform<Vec<[i32;2]>> for Vec<U>{
fn as_uniform(&self) -> UniformValue{
UniformValue::ArrayIVec2(self.iter().map(|v| *v.as_ref()).collect())
}
}
impl<U: AsRef<[i32;3]>> AsUniform<Vec<[i32;3]>> for Vec<U>{
fn as_uniform(&self) -> UniformValue{
UniformValue::ArrayIVec3(self.iter().map(|v| *v.as_ref()).collect())
}
}
impl<U: AsRef<[i32;4]>> AsUniform<Vec<[i32;4]>> for Vec<U>{
fn as_uniform(&self) -> UniformValue{
UniformValue::ArrayIVec4(self.iter().map(|v| *v.as_ref()).collect())
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
impl AsUniform<Vec<u32>> for Vec<u32>{
fn as_uniform(&self) -> UniformValue{
UniformValue::ArrayUInt(self.clone())
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
impl<U: AsRef<[u32;2]>> AsUniform<Vec<[u32;2]>> for Vec<U>{
fn as_uniform(&self) -> UniformValue{
UniformValue::ArrayUVec2(self.iter().map(|v| *v.as_ref()).collect())
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
impl<U: AsRef<[u32;3]>> AsUniform<Vec<[u32;3]>> for Vec<U>{
fn as_uniform(&self) -> UniformValue{
UniformValue::ArrayUVec3(self.iter().map(|v| *v.as_ref()).collect())
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
impl<U: AsRef<[u32;4]>> AsUniform<Vec<[u32;4]>> for Vec<U>{
fn as_uniform(&self) -> UniformValue{
UniformValue::ArrayUVec4(self.iter().map(|v| *v.as_ref()).collect())
}
}