use glin;
use super::{Material, Renderer};
use color::{Rgba,ToRgba};
use color::consts::*;
use std::borrow::Borrow;
use glin::RenderSurface;
use std::cell::UnsafeCell;
use glin::uniforms;
use std::rc::Rc;
pub struct BasicMaterial<T = glin::Texture>{
color: Option<Rgba<f32>>,
texture: Option<T>,
properties: Vec<glin::Property>,
uniforms: UnsafeCell<Vec<glin::program::Uniform>>,
force_color_from_mesh: bool,
}
pub struct Builder{
properties: Vec<glin::Property>,
color: Option<Rgba<f32>>,
force_color_from_mesh: bool,
}
pub struct BuilderWithTexture<T = glin::Texture>{
properties: Vec<glin::Property>,
color: Option<Rgba<f32>>,
texture: T,
force_color_from_mesh: bool,
}
impl Builder{
pub fn new() -> Builder{
Builder{
properties: vec![],
color: None,
force_color_from_mesh: false,
}
}
pub fn color<C: ToRgba>(mut self, color: &C) -> Builder{
self.color = Some(color.to_rgba().to_standard());
self
}
pub fn texture<T: TextureUniformValue>(self, texture: T) -> BuilderWithTexture<T>{
BuilderWithTexture{
properties: self.properties,
color: self.color,
texture: texture,
force_color_from_mesh: self.force_color_from_mesh,
}
}
pub fn properties(mut self, properties: Vec<glin::Property>) -> Builder{
self.properties = properties;
self
}
pub fn force_color_from_mesh(mut self) -> Builder{
self.force_color_from_mesh = true;
self
}
pub fn build(self) -> BasicMaterial{
BasicMaterial{
color: self.color,
texture: None,
properties: self.properties,
uniforms: UnsafeCell::new(vec![]),
force_color_from_mesh: self.force_color_from_mesh,
}
}
}
impl<T: TextureUniformValue> BuilderWithTexture<T>{
pub fn color<C: ToRgba>(mut self, color: &C) -> BuilderWithTexture<T>{
self.color = Some(color.to_rgba().to_standard());
self
}
pub fn texture(self, texture: T) -> BuilderWithTexture<T>{
BuilderWithTexture{
properties: self.properties,
color: self.color,
texture: texture,
force_color_from_mesh: self.force_color_from_mesh,
}
}
pub fn properties(mut self, properties: Vec<glin::Property>) -> BuilderWithTexture<T>{
self.properties = properties;
self
}
pub fn force_color_from_mesh(mut self) -> BuilderWithTexture<T>{
self.force_color_from_mesh = true;
self
}
pub fn build(self) -> BasicMaterial<T>{
BasicMaterial{
color: self.color,
texture: Some(self.texture),
properties: self.properties,
uniforms: UnsafeCell::new(vec![]),
force_color_from_mesh: self.force_color_from_mesh,
}
}
}
impl Default for BasicMaterial{
fn default() -> BasicMaterial{
Builder::new().build()
}
}
impl<T> BasicMaterial<T>{
pub fn color(&self) -> Rgba<f32>{
self.color.unwrap_or(WHITE.to_rgba())
}
fn uniforms_cache(&self) -> &mut Vec<glin::program::Uniform>{
unsafe{ &mut *self.uniforms.get() }
}
}
impl<T: Borrow<glin::Texture>> BasicMaterial<T>{
pub fn texture(&self) -> Option<&glin::Texture>{
self.texture.as_ref().map(|t| t.borrow())
}
}
impl<'a> BasicMaterial<glin::TextureSampler<'a>>{
pub fn texture_sampler(&self) -> Option<glin::TextureSampler<'a>>{
self.texture
}
}
impl<T: TextureUniformValue> Material for BasicMaterial<T>{
fn program<R: RenderSurface>(&self, renderer: &Renderer<R>) -> &glin::Program{
if renderer.model_matrices_as_attributes(){
if self.force_color_from_mesh && self.texture.is_some() && self.color.is_none() {
shaders::get_texture_force_mesh_color_model_attr_program(renderer)
}else if self.texture.is_some() && self.color.is_none() {
shaders::get_texture_model_attr_program(renderer)
}else if self.force_color_from_mesh && self.texture.is_some() && self.color.is_some() {
shaders::get_texture_color_force_mesh_color_model_attr_program(renderer)
}else if self.texture.is_some() && self.color.is_some() {
shaders::get_texture_color_model_attr_program(renderer)
}else if self.force_color_from_mesh && self.color.is_some(){
shaders::get_color_force_mesh_color_model_attr_program(renderer)
}else if self.color.is_some(){
shaders::get_color_model_attr_program(renderer)
}else{
shaders::get_color_coords_model_attr_program(renderer)
}
}else{
if self.force_color_from_mesh && self.texture.is_some() && self.color.is_none() {
shaders::get_texture_force_mesh_color_program(renderer)
}else if self.texture.is_some() && self.color.is_none() {
shaders::get_texture_program(renderer)
}else if self.force_color_from_mesh && self.texture.is_some() && self.color.is_some() {
shaders::get_texture_color_force_mesh_color_program(renderer)
}else if self.texture.is_some() && self.color.is_some() {
shaders::get_texture_color_program(renderer)
}else if self.force_color_from_mesh && self.color.is_some(){
shaders::get_color_force_mesh_color_program(renderer)
}else if self.color.is_some(){
shaders::get_color_program(renderer)
}else{
shaders::get_color_coords_program(renderer)
}
}
}
fn uniforms<R: RenderSurface>(&self, _renderer: &Renderer<R>) -> &[glin::program::Uniform]{
let uniforms = self.uniforms_cache();
if uniforms.is_empty() {
*uniforms = if let Some(t) = self.texture.as_ref(){
uniforms!{
color: self.color(),
tex: t.as_texture_uniform(0),
}
}else{
uniforms!{
color: self.color(),
}
};
}
uniforms
}
fn properties(&self) -> &[glin::Property]{
&self.properties
}
}
mod shaders{
use glin;
macro_rules! impl_program {
{
USE_GLOBAL_COLOR: $global_color: expr,
HAS_COLOR_TEXTURE: $color_texture: expr,
USE_BGR: $use_bgr: expr,
FORCE_COLOR_FROM_MESH: $force_color_from_mesh: expr,
USE_MODEL_ATTR: $use_model_attr: expr,
$fn_name: ident,
$obj_name: ident
} => {
program_cache!(glin::program::Settings{
version: crate::default_glsl_version(),
extensions: vec![],
precision: glin::program::ShaderPrecision::High,
defines: vec![
("USE_GLOBAL_COLOR", $global_color),
("HAS_COLOR_TEXTURE", $color_texture),
("USE_BGR", $use_bgr),
("FORCE_COLOR_FROM_MESH", $force_color_from_mesh),
("USE_MODEL_ATTR", $use_model_attr),
],
shaders: vec![
(glin::gl::VERTEX_SHADER, include_str!("shaders/basic_material.vs.glsl")),
(glin::gl::FRAGMENT_SHADER, include_str!("shaders/basic_material.fs.glsl")),
],
bindings: crate::default_attribute_bindings().clone(),
base_path: "",
includes: glin::hash_map!{
"mvp_uniforms.glsl" => {include_str!("shaders/mvp_uniforms.glsl")}
},
}, $fn_name, $obj_name);
}
}
impl_program!{
USE_GLOBAL_COLOR: "0",
HAS_COLOR_TEXTURE: "0",
USE_BGR: "0",
FORCE_COLOR_FROM_MESH: "0",
USE_MODEL_ATTR: "0",
get_color_coords_program,
SHADER
}
impl_program!{
USE_GLOBAL_COLOR: "1",
HAS_COLOR_TEXTURE: "0",
USE_BGR: "0",
FORCE_COLOR_FROM_MESH: "0",
USE_MODEL_ATTR: "0",
get_color_program,
COLOR_SHADER
}
impl_program!{
USE_GLOBAL_COLOR: "1",
HAS_COLOR_TEXTURE: "0",
USE_BGR: "0",
FORCE_COLOR_FROM_MESH: "1",
USE_MODEL_ATTR: "0",
get_color_force_mesh_color_program,
COLOR_FORCE_MESH_COLOR_SHADER
}
impl_program!{
USE_GLOBAL_COLOR: "0",
HAS_COLOR_TEXTURE: "1",
USE_BGR: "0",
FORCE_COLOR_FROM_MESH: "0",
USE_MODEL_ATTR: "0",
get_texture_program,
TEXTURE_SHADER
}
impl_program!{
USE_GLOBAL_COLOR: "0",
HAS_COLOR_TEXTURE: "1",
USE_BGR: "0",
FORCE_COLOR_FROM_MESH: "1",
USE_MODEL_ATTR: "0",
get_texture_force_mesh_color_program,
TEXTURE_FORCE_MESH_COLOR_SHADER
}
impl_program!{
USE_GLOBAL_COLOR: "1",
HAS_COLOR_TEXTURE: "1",
USE_BGR: "0",
FORCE_COLOR_FROM_MESH: "0",
USE_MODEL_ATTR: "0",
get_texture_color_program,
TEXTURE_COLOR_SHADER
}
impl_program!{
USE_GLOBAL_COLOR: "1",
HAS_COLOR_TEXTURE: "1",
USE_BGR: "0",
FORCE_COLOR_FROM_MESH: "1",
USE_MODEL_ATTR: "0",
get_texture_color_force_mesh_color_program,
TEXTURE_COLOR_FORCE_MESH_COLOR_SHADER
}
impl_program!{
USE_GLOBAL_COLOR: "0",
HAS_COLOR_TEXTURE: "0",
USE_BGR: "0",
FORCE_COLOR_FROM_MESH: "0",
USE_MODEL_ATTR: "1",
get_color_coords_model_attr_program,
SHADER_MODEL_ATTR
}
impl_program!{
USE_GLOBAL_COLOR: "1",
HAS_COLOR_TEXTURE: "0",
USE_BGR: "0",
FORCE_COLOR_FROM_MESH: "0",
USE_MODEL_ATTR: "1",
get_color_model_attr_program,
COLOR_SHADER_MODEL_ATTR
}
impl_program!{
USE_GLOBAL_COLOR: "1",
HAS_COLOR_TEXTURE: "0",
USE_BGR: "0",
FORCE_COLOR_FROM_MESH: "1",
USE_MODEL_ATTR: "1",
get_color_force_mesh_color_model_attr_program,
COLOR_FORCE_MESH_COLOR_SHADER_MODEL_ATTR
}
impl_program!{
USE_GLOBAL_COLOR: "0",
HAS_COLOR_TEXTURE: "1",
USE_BGR: "0",
FORCE_COLOR_FROM_MESH: "0",
USE_MODEL_ATTR: "1",
get_texture_model_attr_program,
TEXTURE_SHADER_MODEL_ATTR
}
impl_program!{
USE_GLOBAL_COLOR: "0",
HAS_COLOR_TEXTURE: "1",
USE_BGR: "0",
FORCE_COLOR_FROM_MESH: "1",
USE_MODEL_ATTR: "1",
get_texture_force_mesh_color_model_attr_program,
TEXTURE_FORCE_MESH_COLOR_SHADER_MODEL_ATTR
}
impl_program!{
USE_GLOBAL_COLOR: "1",
HAS_COLOR_TEXTURE: "1",
USE_BGR: "0",
FORCE_COLOR_FROM_MESH: "0",
USE_MODEL_ATTR: "1",
get_texture_color_model_attr_program,
TEXTURE_COLOR_SHADER_MODEL_ATTR
}
impl_program!{
USE_GLOBAL_COLOR: "1",
HAS_COLOR_TEXTURE: "1",
USE_BGR: "0",
FORCE_COLOR_FROM_MESH: "1",
USE_MODEL_ATTR: "1",
get_texture_color_force_mesh_color_model_attr_program,
TEXTURE_COLOR_FORCE_MESH_COLOR_SHADER_MODEL_ATTR
}
}
pub trait TextureUniformValue{
fn as_texture_uniform(&self, binding: u32) -> glin::program::UniformValue;
}
impl<'a> TextureUniformValue for glin::TextureSampler<'a>{
fn as_texture_uniform(&self, binding: u32) -> glin::program::UniformValue{
glin::program::UniformValue::TextureSampler(self.texture_id(), binding, self.target(), self.sampler_id())
}
}
impl TextureUniformValue for glin::Texture{
fn as_texture_uniform(&self, binding: u32) -> glin::program::UniformValue{
glin::program::UniformValue::Texture(self.id(), binding, self.target())
}
}
impl<'a> TextureUniformValue for &'a glin::Texture{
fn as_texture_uniform(&self, binding: u32) -> glin::program::UniformValue{
glin::program::UniformValue::Texture(self.id(), binding, self.target())
}
}
impl TextureUniformValue for Rc<glin::Texture>{
fn as_texture_uniform(&self, binding: u32) -> glin::program::UniformValue{
glin::program::UniformValue::Texture(self.id(), binding, self.target())
}
}