use color::{Rgba,ToRgba};
use glin;
use gl;
use std::cell::{Cell, UnsafeCell};
use std::mem;
use na::{Mat4, Rotation2, vec2};
use angle::{Angle, Rad};
use std::borrow::Borrow;
use graphics;
use std::path::Path;
use util::{Result, Error};
use glin::CreationContext;
use glin::RenderSurface;
use super::{CreationProxy, Material, Renderer, Light};
use std::slice;
use std::ops::Deref;
use gl::densevec::DenseVec;
use super::UniformsCache;
#[cfg(debug_assertions)]
use std::collections::HashSet;
#[cfg(feature="image")]
use graphics::image::GenericImageView;
pub mod metals{
use color::Rgb;
pub const IRON: Rgb<f32> = Rgb{r: 0.560, g: 0.570, b: 0.580};
pub const SILVER: Rgb<f32> = Rgb{r: 0.972, g: 0.960, b: 0.915};
pub const ALUMINUM: Rgb<f32> = Rgb{r: 0.913, g: 0.921, b: 0.925};
pub const GOLD: Rgb<f32> = Rgb{r: 1.000, g: 0.766, b: 0.336};
pub const COPPER: Rgb<f32> = Rgb{r: 0.955, g: 0.637, b: 0.538};
pub const CHROMIUM: Rgb<f32> = Rgb{r: 0.550, g: 0.556, b: 0.554};
pub const NICKEL: Rgb<f32> = Rgb{r: 0.660, g: 0.609, b: 0.526};
pub const TITANIUM: Rgb<f32> = Rgb{r: 0.542, g: 0.497, b: 0.449};
pub const COBALT: Rgb<f32> = Rgb{r: 0.662, g: 0.655, b: 0.634};
pub const PLATINUM: Rgb<f32> = Rgb{r: 0.672, g: 0.637, b: 0.585};
}
mod uniforms{
use super::Type;
use glin;
#[derive(UniformsLocationCache,Debug,Clone)]
pub struct PbrUniformsLocationCache{
pub mat_diffuse: u32,
pub mat_emissive: u32,
pub mat_roughness: u32,
pub mat_metallic: u32,
pub mat_reflectance: u32,
pub mat_reflectance_tint: u32,
}
#[derive(UniformsLocationCache,Debug,Clone)]
pub struct AnisotropicUniformsLocationCache{
pub mat_diffuse: u32,
pub mat_emissive: u32,
pub mat_roughness: u32,
pub mat_metallic: u32,
pub mat_reflectance: u32,
pub mat_reflectance_tint: u32,
pub mat_anisotropy: u32,
pub mat_anisotropic_direction: u32,
}
#[derive(UniformsLocationCache,Debug,Clone)]
pub struct LambertUniformsLocationCache{
pub mat_diffuse: u32,
pub mat_emissive: u32,
}
#[derive(UniformsLocationCache,Debug,Clone)]
pub struct ClothUniformsLocationCache{
pub mat_diffuse: u32,
pub mat_emissive: u32,
pub mat_sheen: u32,
pub mat_roughness: u32,
}
#[derive(UniformsLocationCache,Debug,Clone)]
pub struct ClothSubsurfaceUniformsLocationCache{
pub mat_diffuse: u32,
pub mat_subsurface: u32,
pub mat_emissive: u32,
pub mat_sheen: u32,
pub mat_roughness: u32,
}
#[derive(UniformsLocationCache,Debug,Clone)]
pub struct SubsurfaceUniformsLocationCache{
pub mat_diffuse: u32,
pub mat_emissive: u32,
pub mat_roughness: u32,
pub mat_metallic: u32,
pub mat_reflectance: u32,
pub mat_reflectance_tint: u32,
pub mat_subsurface_power: u32,
pub mat_subsurface_color: u32,
pub mat_thickness: u32,
}
#[derive(UniformsLocationCache,Debug,Clone)]
pub struct ClearcoatUniformsLocationCache{
pub mat_diffuse: u32,
pub mat_emissive: u32,
pub mat_roughness: u32,
pub mat_metallic: u32,
pub mat_reflectance: u32,
pub mat_reflectance_tint: u32,
pub mat_clearcoat: u32,
pub mat_clearcoat_roughness: u32,
}
#[derive(Debug,Clone)]
pub enum UniformsLocationCache{
Pbr(PbrUniformsLocationCache),
Lambert(LambertUniformsLocationCache),
Cloth(ClothUniformsLocationCache),
ClothSubsurface(ClothSubsurfaceUniformsLocationCache),
Anisotropic(AnisotropicUniformsLocationCache),
Subsurface(SubsurfaceUniformsLocationCache),
Clearcoat(ClearcoatUniformsLocationCache)
}
pub fn locations_for_type(ty: Type, program: &glin::Program) -> UniformsLocationCache{
match ty {
Type::Lambert =>
UniformsLocationCache::Lambert(LambertUniformsLocationCache::from_program(&program)),
Type::Standard =>
UniformsLocationCache::Pbr(PbrUniformsLocationCache::from_program(&program)),
Type::Cloth =>
UniformsLocationCache::Cloth(ClothUniformsLocationCache::from_program(&program)),
Type::ClothSubsurface =>
UniformsLocationCache::ClothSubsurface(ClothSubsurfaceUniformsLocationCache::from_program(&program)),
Type::Anisotropic =>
UniformsLocationCache::Anisotropic(AnisotropicUniformsLocationCache::from_program(&program)),
Type::Subsurface =>
UniformsLocationCache::Subsurface(SubsurfaceUniformsLocationCache::from_program(&program)),
Type::Clearcoat =>
UniformsLocationCache::Clearcoat(ClearcoatUniformsLocationCache::from_program(&program)),
}
}
}
use self::uniforms::*;
#[derive(Debug,Clone,Copy,Eq,PartialEq,Hash)]
pub enum Type{
Lambert,
Standard,
Cloth,
ClothSubsurface,
Clearcoat,
Anisotropic,
Subsurface,
}
impl ToString for Type{
fn to_string(&self) -> String{
match self{
Type::Lambert => "LAMBERT_MATERIAL",
Type::Standard => "STANDARD_MATERIAL",
Type::Cloth => "CLOTH_MATERIAL",
Type::ClothSubsurface => "CLOTH_SUBSURFACE_MATERIAL",
Type::Clearcoat => "CLEARCOAT_MATERIAL",
Type::Anisotropic => "ANISOTROPIC_MATERIAL",
Type::Subsurface => "SUBSURFACE_MATERIAL",
}.to_owned()
}
}
#[derive(Debug,Clone,Copy,Eq,PartialEq,Hash)]
pub enum AlphaType{
None,
Blending,
AlphaToCoverage,
ScreenDoor,
Threshold,
}
impl ToString for AlphaType{
fn to_string(&self) -> String{
match self{
AlphaType::None => "ALPHA_NONE",
AlphaType::Blending => "ALPHA_BLENDING",
AlphaType::AlphaToCoverage => "ALPHA_TO_COVERAGE",
AlphaType::ScreenDoor => "ALPHA_SCREEN_DOOR",
AlphaType::Threshold => "ALPHA_THRESHOLD",
}.to_owned()
}
}
thread_local!(static ID: Cell<usize> = Cell::new(0));
#[derive(Debug)]
pub struct PbrMaterial<Program = glin::Program, Texture = glin::Texture>{
id: usize,
texture: Option<Texture>,
normal_map: Option<Texture>,
occlusion_map: Option<Texture>,
metallic_roughness_map: Option<Texture>,
emissive_map: Option<Texture>,
anisotropy_map: Option<Texture>,
texture_matrix: Option<Mat4>,
normal_map_matrix: Option<Mat4>,
occlusion_map_matrix: Option<Mat4>,
metallic_roughness_map_matrix: Option<Mat4>,
emissive_map_matrix: Option<Mat4>,
anisotropy_map_matrix: Option<Mat4>,
program: Option<Program>,
diffuse_color: Rgba<f32>,
sheen_color: Rgba<f32>,
subsurface_color: Rgba<f32>,
emissive_color: Rgba<f32>,
subsurface_power: f32,
thickness: f32,
roughness: f32,
metallic: f32,
reflectance: f32,
reflectance_tint: f32,
anisotropy: f32,
anisotropic_rotation: Rad<f32>,
clearcoat: f32,
clearcoat_roughness: f32,
double_sided: bool,
has_tangents: bool,
has_clip_plane: bool,
has_separate_alpha: bool,
normal_scale: f32,
ty: Type,
alpha_type: AlphaType,
locations: UnsafeCell<Option<UniformsLocationCache>>,
uniforms: UnsafeCell<UniformsCache>,
}
#[derive(Default)]
pub struct Maps<P: AsRef<Path>>{
pub base_color: Option<P>,
pub normal: Option<P>,
pub ambient_occlusion: Option<P>,
pub metallic: Option<P>,
pub roughness: Option<P>,
pub emissive: Option<P>,
pub anisotropy: Option<P>,
}
pub struct Builder{
pub(crate) gl: CreationProxy
}
impl Builder{
#[cfg(feature="freeimage")]
pub fn from_maps<P: AsRef<Path>>(&self, maps: Maps<P>) -> Result<PbrMaterial>{
let mut material = PbrMaterial::new();
if let (Some(roughness), Some(metallic)) = (maps.roughness, maps.metallic) {
let roughness = graphics::image::load(roughness)?;
let metallic = graphics::image::load(metallic)?;
let grey_roughness = roughness.to_greyscale()
.map_err(|e| Error::with_cause("Error converting roughness map to greyscale", e))?;
let grey_metallic = metallic.to_greyscale()
.map_err(|e| Error::with_cause("Error converting metallic map to greyscale", e))?;
let rough_met_map = grey_roughness.pixels().iter()
.zip(grey_metallic.pixels().iter())
.flat_map(|(r, s)| vec![*r, *s])
.collect::<Vec<u8>>();
let format = glin::texture::LoadDataFormat{
target: gl::TEXTURE_2D,
internal_format: gl::RG8,
format: gl::RG,
ty: gl::UNSIGNED_BYTE,
width: roughness.width() as u32,
height: roughness.height() as u32,
levels: 1,
#[cfg(feature="gl")]
samples: 0,
};
let mut rough_met_map_tex = self.gl.new_texture()
.from_data_format(format, &rough_met_map)
.map_err(|err| Error::with_cause(&format!("Error loading roughness/metallic maps into texture"), err))?;
material.set_metallic_roughness_map(rough_met_map_tex);
}
if let Some(base_color) = maps.base_color{
let base_color = graphics::image::load(base_color)?;
let base_color = self.gl.new_texture().from_image(&base_color, gl::TEXTURE_2D)
.map_err(|err| Error::with_cause(&format!("Error loading base color map into texture"), err))?;
material.set_texture(base_color);
}
if let Some(normal_map) = maps.normal{
let normal_map = graphics::image::load(normal_map)?;
let normal_map = self.gl.new_texture().from_image(&normal_map, gl::TEXTURE_2D)
.map_err(|err| Error::with_cause(&format!("Error loading normal map into texture"), err))?;
material.set_normal_map(normal_map);
}
if let Some(ao_map) = maps.ambient_occlusion{
let ao_map = graphics::image::load(ao_map)?;
let ao_map = self.gl.new_texture().from_image(&ao_map, gl::TEXTURE_2D)
.map_err(|err| Error::with_cause(&format!("Error loading ambient occlusion map into texture"), err))?;
material.set_occlusion_map(ao_map);
}
if let Some(emissive) = maps.emissive{
let emissive = graphics::image::load(emissive)?;
let emissive = self.gl.new_texture().from_image(&emissive, gl::TEXTURE_2D)
.map_err(|err| Error::with_cause(&format!("Error loading emissive map into texture"), err))?;
material.set_emissive_map(emissive);
}
if let Some(anisotropy) = maps.anisotropy{
let anisotropy = graphics::image::load(anisotropy)?;
let anisotropy = self.gl.new_texture().from_image(&anisotropy, gl::TEXTURE_2D)
.map_err(|err| Error::with_cause(&format!("Error loading emissive map into texture"), err))?;
material.set_anisotropy_map(anisotropy);
}
Ok(material)
}
#[cfg(feature="image")]
pub fn from_maps<P: AsRef<Path>>(&self, maps: Maps<P>) -> Result<PbrMaterial>{
let mut material = PbrMaterial::new();
if let (Some(roughness), Some(metallic)) = (maps.roughness, maps.metallic) {
let roughness = graphics::image::load(roughness)?;
let metallic = graphics::image::load(metallic)?;
let rough_met_map = roughness.to_luma().pixels()
.zip(metallic.to_luma().pixels())
.flat_map(|(r, s)| vec![r.data[0], s.data[0]])
.collect::<Vec<_>>();
let rough_met_map = graphics::image::GrayAlphaImage
::from_raw(roughness.width(), roughness.height(), rough_met_map)
.ok_or_else(|| Error::new("Error combining roughness and metallic maps"))?;
let rough_met_map = self.gl.new_texture()
.from_image(&rough_met_map, gl::TEXTURE_2D)
.map_err(|err| Error::with_cause(&format!("Error loading roughness/metallic maps into texture"), err))?;
material.set_metallic_roughness_map(rough_met_map);
}
if let Some(base_color) = maps.base_color{
let base_color = graphics::image::load(base_color)?;
let base_color = self.gl.new_texture().from_image(&base_color, gl::TEXTURE_2D)
.map_err(|err| Error::with_cause(&format!("Error loading base color map into texture"), err))?;
material.set_texture(base_color);
}
if let Some(normal_map) = maps.normal{
let normal_map = graphics::image::load(normal_map)?;
let normal_map = self.gl.new_texture().from_image(&normal_map, gl::TEXTURE_2D)
.map_err(|err| Error::with_cause(&format!("Error loading normal map into texture"), err))?;
material.set_normal_map(normal_map);
}
if let Some(ao_map) = maps.ambient_occlusion{
let ao_map = graphics::image::load(ao_map)?;
let ao_map = self.gl.new_texture().from_image(&ao_map, gl::TEXTURE_2D)
.map_err(|err| Error::with_cause(&format!("Error loading ambient occlusion map into texture"), err))?;
material.set_occlusion_map(ao_map);
}
if let Some(emissive) = maps.emissive{
let emissive = graphics::image::load(emissive)?;
let emissive = self.gl.new_texture().from_image(&emissive, gl::TEXTURE_2D)
.map_err(|err| Error::with_cause(&format!("Error loading emissive map into texture"), err))?;
material.set_occlusion_map(emissive);
}
Ok(material)
}
}
impl PbrMaterial{
pub fn new() -> PbrMaterial<glin::Program>{
PbrMaterial{
id: ID.with(|id| {
let next_id = id.get();
id.set(next_id + 1);
next_id
}),
texture: None,
normal_map: None,
occlusion_map: None,
metallic_roughness_map: None,
emissive_map: None,
texture_matrix: None,
anisotropy_map: None,
normal_map_matrix: None,
occlusion_map_matrix: None,
metallic_roughness_map_matrix: None,
emissive_map_matrix: None,
anisotropy_map_matrix: None,
program: None,
diffuse_color: rgba!(0.8f32, 0.8f32, 0.8f32, 1f32),
subsurface_color: rgba!(1f32, 1f32, 1f32, 1f32),
emissive_color: rgba!(0.0f32, 0.0f32, 0.0f32, 1f32),
sheen_color: rgba!(0.04f32, 0.04f32, 0.04f32, 1f32),
roughness: 0.2,
metallic: 0.0,
reflectance: 0.5,
reflectance_tint: 0.,
anisotropy: 0.0,
anisotropic_rotation: Rad(0.),
subsurface_power: 12.234,
thickness: 0.5,
clearcoat: 0.0,
clearcoat_roughness: 0.5,
double_sided: false,
has_tangents: false,
has_clip_plane: false,
has_separate_alpha: false,
normal_scale: 1.,
locations: UnsafeCell::new(None),
ty: Type::Standard,
alpha_type: AlphaType::None,
uniforms: UnsafeCell::new(UniformsCache::new()),
}
}
pub fn new_with_texture(tex: glin::Texture) -> PbrMaterial<glin::Program>{
PbrMaterial{
id: ID.with(|id| {
let next_id = id.get();
id.set(next_id + 1);
next_id
}),
texture: Some(tex),
normal_map: None,
occlusion_map: None,
metallic_roughness_map: None,
emissive_map: None,
texture_matrix: None,
anisotropy_map: None,
normal_map_matrix: None,
occlusion_map_matrix: None,
metallic_roughness_map_matrix: None,
emissive_map_matrix: None,
anisotropy_map_matrix: None,
program: None,
diffuse_color: rgba!(0.8f32, 0.8f32, 0.8f32, 1f32),
subsurface_color: rgba!(1f32, 1f32, 1f32, 1f32),
emissive_color: rgba!(0.0f32, 0.0f32, 0.0f32, 1f32),
sheen_color: rgba!(0.04f32, 0.04f32, 0.04f32, 1f32),
roughness: 0.2,
metallic: 0.0,
reflectance: 0.5,
reflectance_tint: 0.,
anisotropy: 0.0,
anisotropic_rotation: Rad(0.),
subsurface_power: 12.234,
thickness: 0.5,
clearcoat: 0.0,
clearcoat_roughness: 0.5,
double_sided: false,
has_tangents: false,
has_clip_plane: false,
has_separate_alpha: false,
normal_scale: 1.,
locations: UnsafeCell::new(None),
ty: Type::Standard,
alpha_type: AlphaType::None,
uniforms: UnsafeCell::new(UniformsCache::new()),
}
}
}
impl<P: Borrow<glin::Program>, T: Borrow<glin::Texture>> PbrMaterial<P, T>{
fn update_locations<R: RenderSurface>(&self, gl: &gl::Renderer<R>){
let locations = locations_for_type(self.ty, self.program(gl));
unsafe{
*self.locations.get() = Some(locations)
}
}
fn invalidate_locations(&mut self){
unsafe{
*self.locations.get() = None
}
}
pub fn ty(&self) -> Type{
self.ty
}
pub fn set_type(&mut self, ty: Type){
self.ty = ty
}
pub fn texture(&self) -> Option<&T>{
self.texture.as_ref()
}
pub fn normal_map(&self) -> Option<&T>{
self.normal_map.as_ref()
}
pub fn set_texture<TT: Into<Option<T>>>(&mut self, texture: TT) -> Option<T>{
self.invalidate_locations();
mem::replace(&mut self.texture, texture.into())
}
pub fn set_normal_map<TT: Into<Option<T>>>(&mut self, texture: TT) -> Option<T>{
self.invalidate_locations();
mem::replace(&mut self.normal_map, texture.into())
}
pub fn set_emissive_map<TT: Into<Option<T>>>(&mut self, texture: TT) -> Option<T>{
self.invalidate_locations();
mem::replace(&mut self.emissive_map, texture.into())
}
pub fn set_anisotropy_map<TT: Into<Option<T>>>(&mut self, texture: TT) -> Option<T>{
self.invalidate_locations();
mem::replace(&mut self.anisotropy_map, texture.into())
}
pub fn set_program(&mut self, program: P){
let locations = locations_for_type(self.ty, program.borrow());
unsafe{
*self.locations.get() = Some(locations)
}
self.program = Some(program);
}
pub fn set_color<C:ToRgba>(&mut self, color: &C){
self.diffuse_color = color.to_rgba();
}
pub fn color(&self) -> &Rgba<f32>{
&self.diffuse_color
}
pub fn emissive_color(&self) -> &Rgba<f32>{
&self.emissive_color
}
pub fn set_emissive_color<C: ToRgba>(&mut self, emissive: &C){
self.emissive_color = emissive.to_rgba()
}
pub fn set_subsurface_color<C:ToRgba>(&mut self, color: &C){
self.subsurface_color = color.to_rgba();
}
pub fn subsurface_color(&self) -> &Rgba<f32>{
&self.subsurface_color
}
pub fn set_sheen_color<C: ToRgba>(&mut self, color: &C){
self.sheen_color = color.to_rgba()
}
pub fn sheen_color(&self) -> &Rgba<f32>{
&self.sheen_color
}
pub fn metallic(&self) -> f32{
self.metallic
}
pub fn roughness(&self) -> f32{
self.roughness
}
pub fn anisotropy(&self) -> f32{
self.anisotropy
}
pub fn set_anisotropy(&mut self, anisotropy: f32){
self.anisotropy = anisotropy
}
pub fn anisotropic_rotation(&self) -> Rad<f32>{
self.anisotropic_rotation
}
pub fn set_anisotropic_rotation(&mut self, anisotropic_rotation: Rad<f32>){
self.anisotropic_rotation = anisotropic_rotation
}
pub fn clearcoat(&self) -> f32{
self.clearcoat
}
pub fn set_clearcoat(&mut self, clearcoat: f32){
self.clearcoat = clearcoat;
}
pub fn clearcoat_roughness(&self) -> f32{
self.clearcoat_roughness
}
pub fn set_clearcoat_roughness(&mut self, r: f32){
self.clearcoat_roughness = r;
}
pub fn double_sided(&self) -> bool{
self.double_sided
}
pub fn set_metallic(&mut self, metallic: f32){
self.invalidate_locations();
self.metallic = metallic;
}
pub fn reflectance(&self) -> f32{
self.reflectance
}
pub fn set_reflectance(&mut self, reflectance: f32){
self.reflectance = reflectance;
}
pub fn set_shininess(&mut self, shininess: f32){
self.roughness = (2.0 / (shininess + 2.0)).sqrt().sqrt();
}
pub fn set_roughness(&mut self, roughness: f32){
self.invalidate_locations();
self.roughness = roughness;
}
pub fn subsurface_power(&self) -> f32{
self.subsurface_power
}
pub fn set_subsurface_power(&mut self, power: f32){
self.subsurface_power = power
}
pub fn thickness(&self) -> f32{
self.thickness
}
pub fn set_thickness(&mut self, thickness: f32){
self.thickness = thickness
}
pub fn reflectance_tint(&self) -> f32{
self.reflectance_tint
}
pub fn set_reflectance_tint(&mut self, t: f32){
self.reflectance_tint = t;
}
pub fn texture_matrix(&self) -> Option<&Mat4>{
self.texture_matrix.as_ref()
}
pub fn set_texture_matrix<M: Into<Option<Mat4>>>(&mut self, mat: M){
self.texture_matrix = mat.into();
}
pub fn set_normal_map_matrix<M: Into<Option<Mat4>>>(&mut self, mat: M){
self.normal_map_matrix = mat.into();
}
pub fn set_emissive_map_matrix<M: Into<Option<Mat4>>>(&mut self, mat: M){
self.emissive_map_matrix = mat.into();
}
pub fn set_anisotropy_map_matrix<M: Into<Option<Mat4>>>(&mut self, mat: M){
self.anisotropy_map_matrix = mat.into();
}
pub fn set_double_sided(&mut self, double_sided: bool){
self.double_sided = double_sided
}
pub fn has_clip_plane(&self) -> bool{
self.has_clip_plane
}
pub fn set_has_clip_plane(&mut self, c: bool){
self.has_clip_plane = c;
}
pub fn with_double_sided(&self, double_sided: bool) -> PbrMaterial<&glin::Program, &glin::Texture>{
PbrMaterial{
id: ID.with(|id| {
let next_id = id.get();
id.set(next_id + 1);
next_id
}),
texture: self.texture.as_ref().map(|t| t.borrow()),
normal_map: self.normal_map.as_ref().map(|t| t.borrow()),
occlusion_map: self.occlusion_map.as_ref().map(|t| t.borrow()),
metallic_roughness_map: self.metallic_roughness_map.as_ref().map(|t| t.borrow()),
emissive_map: self.emissive_map.as_ref().map(|t| t.borrow()),
anisotropy_map: self.anisotropy_map.as_ref().map(|t| t.borrow()),
texture_matrix: self.texture_matrix.clone(),
normal_map_matrix: self.normal_map_matrix.clone(),
occlusion_map_matrix: self.occlusion_map_matrix.clone(),
metallic_roughness_map_matrix: self.metallic_roughness_map_matrix.clone(),
emissive_map_matrix: self.emissive_map_matrix.clone(),
anisotropy_map_matrix: self.anisotropy_map_matrix.clone(),
program: self.program.as_ref().map(|p| p.borrow()),
diffuse_color: self.diffuse_color.clone(),
subsurface_color: self.subsurface_color.clone(),
emissive_color: self.emissive_color.clone(),
sheen_color: self.sheen_color.clone(),
roughness: self.roughness,
metallic: self.metallic,
reflectance: self.reflectance,
reflectance_tint: self.reflectance_tint,
anisotropy: self.anisotropy,
subsurface_power: self.subsurface_power,
thickness: self.thickness,
clearcoat: self.clearcoat,
clearcoat_roughness: self.clearcoat_roughness,
anisotropic_rotation: self.anisotropic_rotation,
double_sided,
has_tangents: self.has_tangents,
has_clip_plane: self.has_clip_plane,
has_separate_alpha: self.has_separate_alpha,
normal_scale: self.normal_scale,
locations: unsafe{ UnsafeCell::new((*self.locations.get()).clone()) },
ty: self.ty,
alpha_type: self.alpha_type,
uniforms: UnsafeCell::new(UniformsCache::new()),
}
}
pub fn set_occlusion_map<TT: Into<Option<T>>>(&mut self, texture: TT) -> Option<T>{
self.invalidate_locations();
mem::replace(&mut self.occlusion_map, texture.into())
}
pub fn set_occlusion_map_matrix<M: Into<Option<Mat4>>>(&mut self, mat: M){
self.occlusion_map_matrix = mat.into();
}
pub fn set_metallic_roughness_matrix<M: Into<Option<Mat4>>>(&mut self, mat: M){
self.invalidate_locations();
self.metallic_roughness_map_matrix = mat.into();
}
pub fn set_metallic_roughness_map<TT: Into<Option<T>>>(&mut self, texture: TT) -> Option<T>{
self.invalidate_locations();
self.metallic = 1.;
self.roughness = 1.;
mem::replace(&mut self.metallic_roughness_map, texture.into())
}
pub fn occlusion_map(&self) -> Option<&T>{
self.occlusion_map.as_ref()
}
pub fn metallic_roughness_map(&self) -> Option<&T>{
self.metallic_roughness_map.as_ref()
}
pub fn normal_scale(&self) -> f32 {
self.normal_scale
}
pub fn set_normal_scale(&mut self, scale: f32){
self.normal_scale = scale;
}
pub fn set_alpha_type(&mut self, alpha_type: AlphaType){
self.alpha_type = alpha_type;
}
pub fn alpha_type(&self) -> AlphaType {
self.alpha_type
}
pub fn has_tangents(&self) -> bool{
self.has_tangents
}
pub fn set_has_tangents(&mut self, has_tangents: bool){
self.has_tangents = has_tangents;
}
pub fn has_separate_alpha(&self) -> bool{
self.has_separate_alpha
}
pub fn set_has_separate_alpha(&mut self, has_separate_alpha: bool){
self.has_separate_alpha = has_separate_alpha;
}
pub fn with_lights<'a, 'l, L, I>(&'a self, lights: I) -> PbrMaterialRef<'a, P, T>
where L: Borrow<&'l dyn Light>,
I: IntoIterator<Item = L>,
{
let mut num_directional_lights = 0;
let mut num_point_lights = 0;
let mut num_spot_lights = 0;
let mut num_shadow_maps = 0;
let mut max_num_shadow_maps_directional = 0;
let mut max_num_shadow_maps_spot = 0;
let mut max_num_shadow_maps_point = 0;
let mut shadow_map_type = None;
let mut use_ibl = false;
let mut shadow_maps_offset = 0;
let uniforms = lights.into_iter()
.flat_map(|l| {
let l_type = l.borrow().ty();
let uniforms = l.borrow().uniforms(&mut shadow_maps_offset).into_iter().map(move |u|{
match l_type{
"AMBIENT_LIGHT" | "IMAGE_BASED_LIGHT" => u,
"DIRECTIONAL_LIGHT" | "DIRECTIONAL_LIGHT_SHADOW" => if let glin::program::Uniform::Name(name, value) = u {
if name != "shadow_map_idx" && name.starts_with("shadow"){
glin::program::uniform(name, &value)
}else{
let uniform_indexed_name = format!("directionalLights[{}].{}", num_directional_lights, name);
glin::program::uniform(&uniform_indexed_name, &value)
}
}else{
panic!("can't use location uniforms for lights")
},
"SPOT_LIGHT" | "SPOT_LIGHT_SHADOW" => if let glin::program::Uniform::Name(name, value) = u {
if name != "shadow_map_idx" && name.starts_with("shadow"){
glin::program::uniform(name, &value)
}else{
let uniform_indexed_name = format!("spotLights[{}].{}", num_spot_lights, name);
glin::program::uniform(&uniform_indexed_name, &value)
}
}else{
panic!("can't use location uniforms for lights")
},
"POINT_LIGHT" | "POINT_LIGHT_SHADOW" => if let glin::program::Uniform::Name(name, value) = u {
if name != "shadow_map_idx" && name.starts_with("shadow"){
glin::program::uniform(name, &value)
}else{
let uniform_indexed_name = format!("pointLights[{}].{}", num_point_lights, name);
glin::program::uniform(&uniform_indexed_name, &value)
}
}else{
panic!("can't use location uniforms for lights")
},
ty => panic!("light type {} not supported yet", ty),
}
});
let shadow_maps = l.borrow().shadow_maps();
if let Some(shadow_map) = shadow_maps.first() {
shadow_map_type = Some(shadow_map.shadow_map_type());
}
num_shadow_maps += shadow_maps.len();
match l_type{
"DIRECTIONAL_LIGHT" | "DIRECTIONAL_LIGHT_SHADOW" => {
num_directional_lights+=1;
max_num_shadow_maps_directional = max_num_shadow_maps_directional
.max(shadow_maps.len())
},
"POINT_LIGHT" | "POINT_LIGHT_SHADOW" => {
num_point_lights+=1;
max_num_shadow_maps_point = max_num_shadow_maps_point
.max(shadow_maps.len())
},
"SPOT_LIGHT" | "SPOT_LIGHT_SHADOW" => {
num_spot_lights+=1;
max_num_shadow_maps_spot = max_num_shadow_maps_spot
.max(shadow_maps.len())
},
"IMAGE_BASED_LIGHT" => use_ibl = true,
_ => (),
}
uniforms
})
.collect();
#[cfg(feature="gl")]
let properties = self.properties().iter().cloned()
.chain(if use_ibl {
Some(glin::Property::TextureCubemapSeamless(true))
}else{
None
}).collect();
#[cfg(any(feature="gles", feature="webgl"))]
let properties = self.properties().to_vec();
PbrMaterialRef{
material: self,
lighting_uniforms: uniforms,
use_ibl,
num_directional_lights,
num_point_lights,
num_spot_lights,
num_shadow_maps,
max_num_shadow_maps_directional,
max_num_shadow_maps_point,
max_num_shadow_maps_spot,
shadow_map_type,
locations: UnsafeCell::new(None),
material_ubo: None,
lighting_ubo: None,
camera_ubo: None,
properties,
}
}
pub fn with_lighting_ubo<'l, B, L, I>(&self, lights: I, ubo: B) -> PbrMaterialRef<P, T>
where B: gl::BufferRange<u8> + Clone,
L: Borrow<&'l dyn Light>,
I: IntoIterator<Item = L>,
{
let mut num_directional_lights = 0;
let mut num_point_lights = 0;
let mut num_spot_lights = 0;
let mut num_shadow_maps = 0;
let mut max_num_shadow_maps_directional = 0;
let mut max_num_shadow_maps_spot = 0;
let mut max_num_shadow_maps_point = 0;
let mut shadow_map_type = None;
let mut use_ibl = false;
#[cfg(debug_assertions)]
let mut visited_shadow_map_indices = HashSet::new();
let uniforms = lights.into_iter().flat_map(|l|{
match l.borrow().ty(){
"DIRECTIONAL_LIGHT" | "DIRECTIONAL_LIGHT_SHADOW" => {
num_directional_lights+=1;
let shadow_maps = l.borrow().shadow_maps();
num_shadow_maps += shadow_maps.len();
max_num_shadow_maps_directional = max_num_shadow_maps_directional
.max(shadow_maps.len());
shadow_maps.iter().flat_map(|shadow_map| {
shadow_map_type = Some(shadow_map.shadow_map_type());
#[cfg(debug_assertions)]
{
let shadow_sampler_idx = shadow_map.shadow_sampler().shadow_sampler_idx();
debug_assert!(!visited_shadow_map_indices
.contains(&shadow_sampler_idx),
"Trying to use the same shadow sampler {} for more than one shadow map",
shadow_sampler_idx);
visited_shadow_map_indices.insert(shadow_sampler_idx);
}
shadow_map.shadow_sampler().uniforms()
}).collect()
}
"POINT_LIGHT" | "POINT_LIGHT_SHADOW" => {
num_point_lights+=1;
let shadow_maps = l.borrow().shadow_maps();
num_shadow_maps += shadow_maps.len();
max_num_shadow_maps_point = max_num_shadow_maps_point
.max(shadow_maps.len());
shadow_maps.iter().flat_map(|shadow_map| {
shadow_map_type = Some(shadow_map.shadow_map_type());
#[cfg(debug_assertions)]
{
let shadow_sampler_idx = shadow_map.shadow_sampler().shadow_sampler_idx();
debug_assert!(!visited_shadow_map_indices
.contains(&shadow_sampler_idx),
"Trying to use the same shadow sampler {} for more than one shadow map",
shadow_sampler_idx);
visited_shadow_map_indices.insert(shadow_sampler_idx);
}
shadow_map.shadow_sampler().uniforms()
}).collect()
}
"SPOT_LIGHT" | "SPOT_LIGHT_SHADOW" => {
num_spot_lights+=1;
let shadow_maps = l.borrow().shadow_maps();
num_shadow_maps += shadow_maps.len();
max_num_shadow_maps_spot = max_num_shadow_maps_spot
.max(shadow_maps.len());
shadow_maps.iter().flat_map(|shadow_map| {
shadow_map_type = Some(shadow_map.shadow_map_type());
#[cfg(debug_assertions)]
{
let shadow_sampler_idx = shadow_map.shadow_sampler().shadow_sampler_idx();
debug_assert!(!visited_shadow_map_indices
.contains(&shadow_sampler_idx),
"Trying to use the same shadow sampler {} for more than one shadow map",
shadow_sampler_idx);
visited_shadow_map_indices.insert(shadow_sampler_idx);
}
shadow_map.shadow_sampler().uniforms()
}).collect()
}
"IMAGE_BASED_LIGHT" => {
use_ibl = true;
let mut shadow_maps_offset = 0;
l.borrow().uniforms(&mut shadow_maps_offset)
}
_ => unreachable!(),
}
})
.collect::<Vec<_>>();
#[cfg(feature="gl")]
let properties = self.properties().iter().cloned()
.chain(Some(gl::UBOBindingPoints::Lighting.to_buffer_binding(ubo.clone())))
.chain(if use_ibl {
Some(glin::Property::TextureCubemapSeamless(true))
}else{
None
}).collect();
#[cfg(any(feature="gles", feature="webgl"))]
let properties = self.properties().iter().cloned()
.chain(Some(gl::UBOBindingPoints::Lighting.to_buffer_binding(ubo.clone())))
.collect();
PbrMaterialRef{
material: self,
lighting_uniforms: uniforms,
use_ibl,
num_directional_lights,
num_point_lights,
num_spot_lights,
num_shadow_maps,
max_num_shadow_maps_directional,
max_num_shadow_maps_point,
max_num_shadow_maps_spot,
shadow_map_type,
locations: UnsafeCell::new(None),
material_ubo: None,
lighting_ubo: Some(gl::UBOBindingPoints::Lighting.to_buffer_binding(ubo)),
camera_ubo: None,
properties,
}
}
pub fn material_ubo<R: RenderSurface>(&self, gl: &Renderer<R>) -> PbrMaterialRef<P,T>{
PbrMaterialRef::new_with_material_ubo(self, gl)
}
pub fn with_camera_ubo<'a>(&'a self) -> PbrMaterialRef<'a, P,T>{
PbrMaterialRef::new_with_camera_ubo(self)
}
fn id(&self) -> usize{
self.id
}
fn data(&self) -> Data{
let mut data = Data{ data: vec![] };
match self.ty {
Type::Standard => {
data.push(self.diffuse_color);
data.push(self.emissive_color);
data.push(self.roughness);
data.push(self.metallic);
data.push(self.reflectance);
data.push(self.reflectance_tint);
},
Type::Lambert => {
data.push(self.diffuse_color);
data.push(self.emissive_color);
},
Type::Cloth => {
data.push(self.diffuse_color);
data.push(self.emissive_color);
data.push(self.sheen_color);
data.push(self.roughness);
data.push(unsafe{ [mem::uninitialized::<f32>();3] });
},
Type::ClothSubsurface => {
data.push(self.diffuse_color);
data.push(self.emissive_color);
data.push(self.sheen_color);
data.push(self.subsurface_color);
data.push(self.roughness);
data.push(unsafe{ [mem::uninitialized::<f32>();3] });
},
Type::Anisotropic => {
let ani_direction = Rotation2::new(self.anisotropic_rotation.value()) * vec2(1.0, 0.0);
let ani_direction = vec3!(ani_direction, 0.0);
data.push(self.diffuse_color);
data.push(self.emissive_color);
data.push(self.roughness);
data.push(self.metallic);
data.push(self.reflectance);
data.push(self.reflectance_tint);
data.push(ani_direction);
data.push(self.anisotropy);
},
Type::Subsurface => {
data.push(self.diffuse_color);
data.push(self.emissive_color);
data.push(self.roughness);
data.push(self.metallic);
data.push(self.reflectance);
data.push(self.reflectance_tint);
data.push(self.subsurface_color);
data.push(self.subsurface_power);
data.push(self.thickness);
data.push(unsafe{ [mem::uninitialized::<f32>();2] });
},
Type::Clearcoat => {
data.push(self.diffuse_color);
data.push(self.emissive_color);
data.push(self.roughness);
data.push(self.metallic);
data.push(self.reflectance);
data.push(self.reflectance_tint);
data.push(self.clearcoat);
data.push(self.clearcoat_roughness);
data.push(unsafe{ [mem::uninitialized::<f32>();2] });
},
}
data
}
fn program_id<R:RenderSurface>(&self, renderer: &Renderer<R>) -> ProgramId{
ProgramId{
context_id: renderer.id(),
num_directional_lights: 0,
num_point_lights: 0,
num_spot_lights: 0,
num_shadow_maps: 0,
max_num_shadow_maps_directional: 0,
max_num_shadow_maps_point: 0,
max_num_shadow_maps_spot: 0,
use_ibl: false,
use_metallic_map: self.metallic_roughness_map.is_some(),
shadow_map_type: None,
ty: self.ty,
alpha_type: self.alpha_type,
has_base_color_texture: self.texture.is_some(),
has_normal_map: self.normal_map.is_some(),
has_emissive_map: self.emissive_map.is_some(),
has_anisotropy_map: self.anisotropy_map.is_some(),
has_tangents: self.has_tangents,
has_clip_plane: self.has_clip_plane,
has_separate_alpha: self.has_separate_alpha,
is_double_sided: self.double_sided,
use_lighting_ubo: false,
use_material_ubo: false,
use_camera_ubo: false,
use_model_matrices_as_attrs: renderer.model_matrices_as_attributes(),
}
}
}
impl<P: Borrow<glin::Program>, T: Borrow<glin::Texture>> Material for PbrMaterial<P,T>{
fn program<R: RenderSurface>(&self, renderer: &Renderer<R>) -> &glin::Program{
match self.program.as_ref() {
Some(program) => program.borrow(),
None => {
let program_id = self.program_id(renderer);
#[cfg(feature="glsl-debug")]
{
let (program, locations) = shaders::get_pbr_program(renderer, program_id);
unsafe{
(*self.locations.get()) = locations.cloned();
}
program
}
#[cfg(not(feature="glsl-debug"))]
shaders::get_pbr_program(renderer, program_id)
}
}
}
fn uniforms<R: RenderSurface>(&self, gl: &Renderer<R>) -> &[glin::program::Uniform]{
if unsafe{ (*self.locations.get()).is_none() }{
self.update_locations(gl);
}
let get_uniforms = ||{
let mut uniforms = match unsafe{ (&*self.locations.get()) } {
Some(UniformsLocationCache::Pbr(ref locations)) => {
uniforms!{
locations.mat_diffuse => self.diffuse_color,
locations.mat_emissive => self.emissive_color,
locations.mat_roughness => self.roughness,
locations.mat_metallic => self.metallic,
locations.mat_reflectance => self.reflectance,
locations.mat_reflectance_tint => self.reflectance_tint,
}
},
Some(UniformsLocationCache::Lambert(ref locations)) => uniforms!{
locations.mat_diffuse => self.diffuse_color,
locations.mat_emissive => self.emissive_color,
},
Some(UniformsLocationCache::Cloth(ref locations)) => uniforms!{
locations.mat_diffuse => self.diffuse_color,
locations.mat_emissive => self.emissive_color,
locations.mat_sheen => self.sheen_color,
locations.mat_roughness => self.roughness,
},
Some(UniformsLocationCache::ClothSubsurface(ref locations)) => uniforms!{
locations.mat_diffuse => self.diffuse_color,
locations.mat_subsurface => self.subsurface_color,
locations.mat_emissive => self.emissive_color,
locations.mat_sheen => self.sheen_color,
locations.mat_roughness => self.roughness,
},
Some(UniformsLocationCache::Anisotropic(ref locations)) => {
let ani_direction = Rotation2::new(self.anisotropic_rotation.value()) * vec2(1.0, 0.0);
let ani_direction = vec3!(ani_direction, 0.0);
uniforms!{
locations.mat_diffuse => self.diffuse_color,
locations.mat_emissive => self.emissive_color,
locations.mat_roughness => self.roughness,
locations.mat_metallic => self.metallic,
locations.mat_anisotropy => self.anisotropy,
locations.mat_anisotropic_direction => ani_direction,
locations.mat_reflectance => self.reflectance,
locations.mat_reflectance_tint => self.reflectance_tint,
}
},
Some(UniformsLocationCache::Subsurface(ref locations)) => {
uniforms!{
locations.mat_diffuse => self.diffuse_color,
locations.mat_emissive => self.emissive_color,
locations.mat_roughness => self.roughness,
locations.mat_metallic => self.metallic,
locations.mat_reflectance => self.reflectance,
locations.mat_reflectance_tint => self.reflectance_tint,
locations.mat_subsurface_power => self.subsurface_power,
locations.mat_subsurface_color => self.subsurface_color,
locations.mat_thickness => self.thickness,
}
},
Some(UniformsLocationCache::Clearcoat(ref locations)) => {
uniforms!{
locations.mat_diffuse => self.diffuse_color,
locations.mat_emissive => self.emissive_color,
locations.mat_roughness => self.roughness,
locations.mat_metallic => self.metallic,
locations.mat_reflectance => self.reflectance,
locations.mat_reflectance_tint => self.reflectance_tint,
locations.mat_clearcoat => self.clearcoat,
locations.mat_clearcoat_roughness => self.clearcoat_roughness,
}
},
_ => unimplemented!()
};
let program = self.program(gl);
match self.texture{
Some(ref tex) => {
if let Some(loc) = program.uniform_location("tex0"){
uniforms.push(glin::program::uniform_location(loc, &(tex.borrow(), gl::TextureBindingPoints::BaseColor as u32)));
}
}
_ => ()
}
match self.normal_map{
Some(ref tex) => {
if let Some(loc) = program.uniform_location("norm"){
uniforms.push(glin::program::uniform_location(loc, &(tex.borrow(), gl::TextureBindingPoints::Normal as u32)));
}
if let Some(loc) = program.uniform_location("u_NormalScale"){
uniforms.push(glin::program::uniform_location(loc, &self.normal_scale));
}
}
_ => ()
}
match self.occlusion_map{
Some(ref tex) => {
if let Some(loc) = program.uniform_location("u_OcclusionSampler"){
uniforms.push(glin::program::uniform_location(loc, &(tex.borrow(), gl::TextureBindingPoints::Occlusion as u32)));
}
}
_ => ()
}
match self.metallic_roughness_map{
Some(ref tex) => {
if let Some(loc) = program.uniform_location("u_MetallicRoughnessSampler"){
uniforms.push(glin::program::uniform_location(loc, &(tex.borrow(), gl::TextureBindingPoints::MetallicRoughness as u32)));
}
}
_ => ()
}
match self.emissive_map{
Some(ref tex) => {
if let Some(loc) = program.uniform_location("u_EmissiveSampler"){
uniforms.push(glin::program::uniform_location(loc, &(tex.borrow(), gl::TextureBindingPoints::Emissive as u32)));
}
}
_ => ()
}
match self.anisotropy_map{
Some(ref tex) => {
if let Some(loc) = program.uniform_location("anisotropy_map"){
uniforms.push(glin::program::uniform_location(loc, &(tex.borrow(), gl::TextureBindingPoints::Anisotropy as u32)));
}
}
_ => ()
}
uniforms
};
let uniforms_cache = unsafe{ &mut *self.uniforms.get() };
uniforms_cache.uniforms(gl, get_uniforms)
}
fn properties(&self) -> &[glin::Property]{
match self.alpha_type{
AlphaType::AlphaToCoverage if self.double_sided => &[
glin::Property::CullFace(None),
gl::Property::SampleAlphaToCoverage(true)
],
AlphaType::AlphaToCoverage => &[
gl::Property::SampleAlphaToCoverage(true)
],
AlphaType::Blending if self.double_sided => &[
glin::Property::CullFace(None),
gl::Property::Blend(true),
gl::Property::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA),
],
AlphaType::Blending => &[
gl::Property::Blend(true),
gl::Property::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA),
],
_ if self.double_sided => &[
glin::Property::CullFace(None),
],
_ => &[]
}
}
}
pub struct PbrMaterialRef<'a, P: 'a = glin::Program, T: 'a = glin::Texture>{
material: &'a PbrMaterial<P, T>,
lighting_uniforms: Vec<glin::program::Uniform>,
camera_ubo: Option<glin::program::Uniform>,
lighting_ubo: Option<glin::Property>,
material_ubo: Option<glin::Property>,
use_ibl: bool,
shadow_map_type: Option<&'static str>,
num_directional_lights: usize,
num_point_lights: usize,
num_spot_lights: usize,
num_shadow_maps: usize,
max_num_shadow_maps_directional: usize,
max_num_shadow_maps_point: usize,
max_num_shadow_maps_spot: usize,
locations: UnsafeCell<Option<UniformsLocationCache>>,
properties: Vec<glin::Property>,
}
impl <'a, P: Borrow<glin::Program>, T: Borrow<glin::Texture>> PbrMaterialRef<'a, P, T>{
pub fn material_ubo<R: RenderSurface>(mut self, gl: &Renderer<R>) -> PbrMaterialRef<'a, P, T>{
if self.material_ubo.is_none(){
let ubo = PBR_BUFFER.with(|buffers|{
let buffers = unsafe{ &mut *buffers.get() };
buffers.entry(gl.id())
.or_insert_with(|| Buffer::new(gl))
.add(self.material)
});
self.material_ubo = Some(gl::UBOBindingPoints::Material.to_buffer_binding(ubo));
}
self
}
fn update_locations<R: RenderSurface>(&self, gl: &gl::Renderer<R>){
let locations = locations_for_type(self.material.ty, self.program(gl));
unsafe{
*self.locations.get() = Some(locations)
}
}
fn new_with_material_ubo<R: RenderSurface>(material: &'a PbrMaterial<P,T>, gl: &Renderer<R>) -> PbrMaterialRef<'a, P, T>{
let ubo = PBR_BUFFER.with(|buffers|{
let buffers = unsafe{ &mut *buffers.get() };
buffers.entry(gl.id())
.or_insert_with(|| Buffer::new(gl))
.add(material)
});
let properties = material.properties().iter().cloned()
.chain(Some(gl::UBOBindingPoints::Material.to_buffer_binding(ubo.clone())))
.collect();
PbrMaterialRef{
material,
lighting_uniforms: vec![],
use_ibl: false,
shadow_map_type: None,
num_directional_lights: 0,
num_point_lights: 0,
num_spot_lights: 0,
num_shadow_maps: 0,
max_num_shadow_maps_directional: 0,
max_num_shadow_maps_point: 0,
max_num_shadow_maps_spot: 0,
locations: UnsafeCell::new(None),
lighting_ubo: None,
material_ubo: Some(gl::UBOBindingPoints::Material.to_buffer_binding(ubo)),
camera_ubo: None,
properties
}
}
fn new_with_camera_ubo(material: &'a PbrMaterial<P,T>) -> PbrMaterialRef<'a, P, T>{
PbrMaterialRef{
material,
lighting_uniforms: vec![],
use_ibl: false,
shadow_map_type: None,
num_directional_lights: 0,
num_point_lights: 0,
num_spot_lights: 0,
num_shadow_maps: 0,
max_num_shadow_maps_directional: 0,
max_num_shadow_maps_point: 0,
max_num_shadow_maps_spot: 0,
locations: UnsafeCell::new(None),
lighting_ubo: None,
material_ubo: None,
camera_ubo: Some(gl::UBOBindingPoints::Camera.to_uniform_block_binding()),
properties: material.properties().to_vec(),
}
}
pub fn with_camera_ubo(mut self) -> PbrMaterialRef<'a, P, T>{
self.camera_ubo = Some(gl::UBOBindingPoints::Camera.to_uniform_block_binding());
self
}
pub fn color(&self) -> &Rgba<f32>{
self.material.color()
}
pub fn sheen_color(&self) -> &Rgba<f32>{
self.material.sheen_color()
}
pub fn subsurface_color(&self) -> &Rgba<f32>{
self.material.subsurface_color()
}
pub fn emissive_color(&self) -> &Rgba<f32>{
self.material.emissive_color()
}
pub fn subsurface_power(&self) -> f32{
self.material.subsurface_power()
}
pub fn thickness(&self) -> f32{
self.material.thickness()
}
pub fn roughness(&self) -> f32{
self.material.roughness()
}
pub fn metallic(&self) -> f32{
self.material.metallic()
}
pub fn reflectance(&self) -> f32{
self.material.reflectance()
}
pub fn reflectance_tint(&self) -> f32{
self.material.reflectance_tint()
}
pub fn anisotropy(&self) -> f32{
self.material.anisotropy()
}
pub fn anisotropic_rotation(&self) -> Rad<f32>{
self.material.anisotropic_rotation()
}
pub fn clearcoat(&self) -> f32{
self.material.clearcoat()
}
pub fn clearcoat_roughness(&self) -> f32{
self.material.clearcoat_roughness()
}
pub fn num_directional_lights(&self) -> usize{
self.num_directional_lights
}
pub fn num_spot_lights(&self) -> usize{
self.num_spot_lights
}
pub fn num_point_lights(&self) -> usize{
self.num_point_lights
}
pub fn num_shadow_maps(&self) -> usize{
self.num_shadow_maps
}
pub fn double_sided(&self) -> bool{
self.material.double_sided()
}
pub fn has_tangents(&self) -> bool{
self.material.has_tangents()
}
pub fn normal_scale(&self) -> f32{
self.material.normal_scale()
}
pub fn ty(&self) -> Type{
self.material.ty()
}
pub fn alpha_type(&self) -> AlphaType{
self.material.alpha_type()
}
pub fn has_clip_plane(&self) -> bool{
self.material.has_clip_plane
}
pub fn has_separate_alpha(&self) -> bool{
self.material.has_separate_alpha
}
pub fn lighting_uniforms<R: RenderSurface>(&self, gl: &Renderer<R>) -> Vec<gl::program::Uniform>{
let program = self.program(gl);
self.lighting_uniforms.iter().cloned()
.chain(self.lighting_ubo.iter().map(|_| gl::UBOBindingPoints::Lighting.to_uniform_block_binding()))
.filter_map(|uniform| uniform.to_location(program))
.collect()
}
pub fn material_uniforms<R: RenderSurface>(&self, gl: &Renderer<R>) -> Vec<gl::program::Uniform>{
let program = self.program(gl);
let mut uniforms = if self.material_ubo.is_none() {
if unsafe{ (*self.locations.get()).is_none() }{
self.update_locations(gl);
}
let mut uniforms = match unsafe{ (*self.locations.get()).as_ref() } {
Some(UniformsLocationCache::Pbr(ref locations)) =>
uniforms!{
locations.mat_diffuse => self.material.diffuse_color,
locations.mat_emissive => self.material.emissive_color,
locations.mat_roughness => self.material.roughness,
locations.mat_metallic => self.material.metallic,
locations.mat_reflectance => self.material.reflectance,
locations.mat_reflectance_tint => self.material.reflectance_tint,
},
Some(UniformsLocationCache::Lambert(ref locations)) => uniforms!{
locations.mat_diffuse => self.material.diffuse_color,
locations.mat_emissive => self.material.emissive_color,
},
Some(UniformsLocationCache::Cloth(ref locations)) =>
uniforms!{
locations.mat_diffuse => self.material.diffuse_color,
locations.mat_emissive => self.material.emissive_color,
locations.mat_sheen => self.material.sheen_color,
locations.mat_roughness => self.material.roughness,
},
Some(UniformsLocationCache::ClothSubsurface(ref locations)) =>
uniforms!{
locations.mat_diffuse => self.material.diffuse_color,
locations.mat_subsurface => self.material.subsurface_color,
locations.mat_emissive => self.material.emissive_color,
locations.mat_sheen => self.material.sheen_color,
locations.mat_roughness => self.material.roughness,
},
Some(UniformsLocationCache::Anisotropic(ref locations)) => {
let ani_direction = Rotation2::new(self.material.anisotropic_rotation.value()) * vec2(1.0, 0.0);
let ani_direction = vec3!(ani_direction, 0.0);
uniforms!{
locations.mat_diffuse => self.material.diffuse_color,
locations.mat_emissive => self.material.emissive_color,
locations.mat_roughness => self.material.roughness,
locations.mat_metallic => self.material.metallic,
locations.mat_anisotropy => self.material.anisotropy,
locations.mat_anisotropic_direction => ani_direction,
locations.mat_reflectance => self.material.reflectance,
locations.mat_reflectance_tint => self.material.reflectance_tint,
}
},
Some(UniformsLocationCache::Subsurface(ref locations)) => {
uniforms!{
locations.mat_diffuse => self.material.diffuse_color,
locations.mat_emissive => self.material.emissive_color,
locations.mat_roughness => self.material.roughness,
locations.mat_metallic => self.material.metallic,
locations.mat_reflectance => self.material.reflectance,
locations.mat_reflectance_tint => self.material.reflectance_tint,
locations.mat_subsurface_power => self.material.subsurface_power,
locations.mat_subsurface_color => self.material.subsurface_color,
locations.mat_thickness => self.material.thickness,
}
},
Some(UniformsLocationCache::Clearcoat(ref locations)) => {
uniforms!{
locations.mat_diffuse => self.material.diffuse_color,
locations.mat_emissive => self.material.emissive_color,
locations.mat_roughness => self.material.roughness,
locations.mat_metallic => self.material.metallic,
locations.mat_reflectance => self.material.reflectance,
locations.mat_reflectance_tint => self.material.reflectance_tint,
locations.mat_clearcoat => self.material.clearcoat,
locations.mat_clearcoat_roughness => self.material.clearcoat_roughness,
}
},
_ => unimplemented!()
};
uniforms
}else{
self.material_ubo.iter()
.map(|_| gl::UBOBindingPoints::Material.to_uniform_block_binding())
.filter_map(|uniform| uniform.to_location(program))
.collect()
};
match self.material.texture{
Some(ref tex) => {
if let Some(loc) = program.uniform_location("tex0"){
uniforms.push(glin::program::uniform_location(loc, &(tex.borrow(), gl::TextureBindingPoints::BaseColor as u32)));
}
}
_ => ()
}
match self.material.normal_map{
Some(ref tex) => {
if let Some(loc) = program.uniform_location("norm"){
uniforms.push(glin::program::uniform_location(loc, &(tex.borrow(), gl::TextureBindingPoints::Normal as u32)));
}
if let Some(loc) = program.uniform_location("u_NormalScale"){
uniforms.push(glin::program::uniform_location(loc, &self.material.normal_scale));
}
}
_ => ()
}
match self.material.occlusion_map{
Some(ref tex) => {
if let Some(loc) = program.uniform_location("u_OcclusionSampler"){
uniforms.push(glin::program::uniform_location(loc, &(tex.borrow(), gl::TextureBindingPoints::Occlusion as u32)));
}
}
_ => ()
}
match self.material.metallic_roughness_map{
Some(ref tex) => {
if let Some(loc) = program.uniform_location("u_MetallicRoughnessSampler"){
uniforms.push(glin::program::uniform_location(loc, &(tex.borrow(), gl::TextureBindingPoints::MetallicRoughness as u32)));
}
}
_ => ()
}
match self.material.emissive_map{
Some(ref tex) => {
if let Some(loc) = program.uniform_location("u_EmissiveSampler"){
uniforms.push(glin::program::uniform_location(loc, &(tex.borrow(), gl::TextureBindingPoints::Emissive as u32)));
}
}
_ => ()
}
match self.material.anisotropy_map{
Some(ref tex) => {
if let Some(loc) = program.uniform_location("anisotropy_map"){
uniforms.push(glin::program::uniform_location(loc, &(tex.borrow(), gl::TextureBindingPoints::Anisotropy as u32)));
}
}
_ => ()
}
uniforms
}
fn program_id<R: RenderSurface>(&self, renderer: &Renderer<R>) -> ProgramId {
ProgramId{
context_id: renderer.id(),
num_directional_lights: self.num_directional_lights,
num_point_lights: self.num_point_lights,
num_spot_lights: self.num_spot_lights,
num_shadow_maps: self.num_shadow_maps,
max_num_shadow_maps_directional: self.max_num_shadow_maps_directional,
max_num_shadow_maps_point: self.max_num_shadow_maps_point,
max_num_shadow_maps_spot: self.max_num_shadow_maps_spot,
use_ibl: self.use_ibl,
use_metallic_map: self.material.metallic_roughness_map.is_some(),
shadow_map_type: self.shadow_map_type,
ty: self.material.ty,
alpha_type: self.material.alpha_type,
has_base_color_texture: self.material.texture.is_some(),
has_normal_map: self.material.normal_map.is_some(),
has_emissive_map: self.material.emissive_map.is_some(),
has_anisotropy_map: self.material.anisotropy_map.is_some(),
has_tangents: self.material.has_tangents,
has_separate_alpha: self.material.has_separate_alpha,
is_double_sided: self.material.double_sided,
has_clip_plane: self.material.has_clip_plane,
use_lighting_ubo: self.lighting_ubo.is_some(),
use_material_ubo: self.material_ubo.is_some(),
use_camera_ubo: self.camera_ubo.is_some(),
use_model_matrices_as_attrs: renderer.model_matrices_as_attributes(),
}
}
}
impl<'a, P: Borrow<glin::Program>, T: Borrow<glin::Texture>> Material for PbrMaterialRef<'a, P, T>{
fn program<R: RenderSurface>(&self, renderer: &Renderer<R>) -> &glin::Program{
match self.material.program.as_ref() {
Some(program) => program.borrow(),
None => {
let program_id = self.program_id(renderer);
#[cfg(feature="glsl-debug")]
{
let (program, locations) = shaders::get_pbr_program(renderer, program_id);
if self.material_ubo.is_none() {
unsafe{
(*self.locations.get()) = locations.cloned();
}
}
program
}
#[cfg(not(feature="glsl-debug"))]
shaders::get_pbr_program(renderer, program_id)
}
}
}
fn uniforms<R: RenderSurface>(&self, gl: &Renderer<R>) -> &[glin::program::Uniform]{
let program = self.program(gl);
let get_uniforms = || {
let mut uniforms = if self.material_ubo.is_none() {
if unsafe{ (*self.locations.get()).is_none() }{
self.update_locations(gl);
}
let mut uniforms = match unsafe{ (*self.locations.get()).as_ref() } {
Some(UniformsLocationCache::Pbr(ref locations)) =>
uniforms!{
locations.mat_diffuse => self.material.diffuse_color,
locations.mat_emissive => self.material.emissive_color,
locations.mat_roughness => self.material.roughness,
locations.mat_metallic => self.material.metallic,
locations.mat_reflectance => self.material.reflectance,
locations.mat_reflectance_tint => self.material.reflectance_tint,
},
Some(UniformsLocationCache::Lambert(ref locations)) => uniforms!{
locations.mat_diffuse => self.material.diffuse_color,
locations.mat_emissive => self.material.emissive_color,
},
Some(UniformsLocationCache::Cloth(ref locations)) =>
uniforms!{
locations.mat_diffuse => self.material.diffuse_color,
locations.mat_emissive => self.material.emissive_color,
locations.mat_sheen => self.material.sheen_color,
locations.mat_roughness => self.material.roughness,
},
Some(UniformsLocationCache::ClothSubsurface(ref locations)) =>
uniforms!{
locations.mat_diffuse => self.material.diffuse_color,
locations.mat_subsurface => self.material.subsurface_color,
locations.mat_emissive => self.material.emissive_color,
locations.mat_sheen => self.material.sheen_color,
locations.mat_roughness => self.material.roughness,
},
Some(UniformsLocationCache::Anisotropic(ref locations)) => {
let ani_direction = Rotation2::new(self.material.anisotropic_rotation.value()) * vec2(1.0, 0.0);
let ani_direction = vec3!(ani_direction, 0.0);
uniforms!{
locations.mat_diffuse => self.material.diffuse_color,
locations.mat_emissive => self.material.emissive_color,
locations.mat_roughness => self.material.roughness,
locations.mat_metallic => self.material.metallic,
locations.mat_anisotropy => self.material.anisotropy,
locations.mat_anisotropic_direction => ani_direction,
locations.mat_reflectance => self.material.reflectance,
locations.mat_reflectance_tint => self.material.reflectance_tint,
}
},
Some(UniformsLocationCache::Subsurface(ref locations)) => {
uniforms!{
locations.mat_diffuse => self.material.diffuse_color,
locations.mat_emissive => self.material.emissive_color,
locations.mat_roughness => self.material.roughness,
locations.mat_metallic => self.material.metallic,
locations.mat_reflectance => self.material.reflectance,
locations.mat_reflectance_tint => self.material.reflectance_tint,
locations.mat_subsurface_power => self.material.subsurface_power,
locations.mat_subsurface_color => self.material.subsurface_color,
locations.mat_thickness => self.material.thickness,
}
},
Some(UniformsLocationCache::Clearcoat(ref locations)) => {
uniforms!{
locations.mat_diffuse => self.material.diffuse_color,
locations.mat_emissive => self.material.emissive_color,
locations.mat_roughness => self.material.roughness,
locations.mat_metallic => self.material.metallic,
locations.mat_reflectance => self.material.reflectance,
locations.mat_reflectance_tint => self.material.reflectance_tint,
locations.mat_clearcoat => self.material.clearcoat,
locations.mat_clearcoat_roughness => self.material.clearcoat_roughness,
}
},
_ => unimplemented!()
};
let extra_uniforms = self.lighting_uniforms.iter().cloned()
.chain(self.lighting_ubo.iter()
.map(|_| gl::UBOBindingPoints::Lighting.to_uniform_block_binding()))
.chain(self.camera_ubo.clone().into_iter())
.filter_map(|uniform| uniform.to_location(program));
uniforms.extend(extra_uniforms);
uniforms
}else{
self.lighting_uniforms.iter().cloned()
.chain(self.lighting_ubo.iter()
.map(|_| gl::UBOBindingPoints::Lighting.to_uniform_block_binding()))
.chain(self.material_ubo.iter()
.map(|_| gl::UBOBindingPoints::Material.to_uniform_block_binding()))
.chain(self.camera_ubo.clone().into_iter())
.filter_map(|uniform| uniform.to_location(program))
.collect()
};
let program = self.program(gl);
match self.material.texture{
Some(ref tex) => {
if let Some(loc) = program.uniform_location("tex0"){
uniforms.push(glin::program::uniform_location(loc, &(tex.borrow(), gl::TextureBindingPoints::BaseColor as u32)));
}
}
_ => ()
}
match self.material.normal_map{
Some(ref tex) => {
if let Some(loc) = program.uniform_location("norm"){
uniforms.push(glin::program::uniform_location(loc, &(tex.borrow(), gl::TextureBindingPoints::Normal as u32)));
}
if let Some(loc) = program.uniform_location("u_NormalScale"){
uniforms.push(glin::program::uniform_location(loc, &self.material.normal_scale));
}
}
_ => ()
}
match self.material.occlusion_map{
Some(ref tex) => {
if let Some(loc) = program.uniform_location("u_OcclusionSampler"){
uniforms.push(glin::program::uniform_location(loc, &(tex.borrow(), gl::TextureBindingPoints::Occlusion as u32)));
}
}
_ => ()
}
match self.material.metallic_roughness_map{
Some(ref tex) => {
if let Some(loc) = program.uniform_location("u_MetallicRoughnessSampler"){
uniforms.push(glin::program::uniform_location(loc, &(tex.borrow(), gl::TextureBindingPoints::MetallicRoughness as u32)));
}
}
_ => ()
}
match self.material.emissive_map{
Some(ref tex) => {
if let Some(loc) = program.uniform_location("u_EmissiveSampler"){
uniforms.push(glin::program::uniform_location(loc, &(tex.borrow(), gl::TextureBindingPoints::Emissive as u32)));
}
}
_ => ()
}
match self.material.anisotropy_map{
Some(ref tex) => {
if let Some(loc) = program.uniform_location("anisotropy_map"){
uniforms.push(glin::program::uniform_location(loc, &(tex.borrow(), gl::TextureBindingPoints::Anisotropy as u32)));
}
}
_ => ()
}
uniforms
};
let uniforms_cache = unsafe{ &mut *self.material.uniforms.get() };
uniforms_cache.uniforms(gl, get_uniforms)
}
fn properties(&self) -> &[glin::Property]{
&self.properties
}
}
struct Buffer{
data: Vec<u8>,
buffer: gl::Buffer<u8>,
offset_index: DenseVec<(usize, usize)>,
ubo_align_size: usize,
last_offset: usize,
}
impl Buffer {
fn new<R: RenderSurface>(gl: &Renderer<R>) -> Buffer{
let mut ubo_align_size = 0;
unsafe{
gl.context().gl().GetIntegerv(gl::UNIFORM_BUFFER_OFFSET_ALIGNMENT, &mut ubo_align_size);
}
Buffer{
data: vec![],
buffer: gl.new_buffer().empty_target(gl::UNIFORM_BUFFER).unwrap(),
offset_index: DenseVec::default(),
ubo_align_size: ubo_align_size as usize,
last_offset: 0,
}
}
fn add<P, T>(&mut self, material: &PbrMaterial<P,T>) -> glin::buffer::Range<u8, glin::Buffer<u8>, &glin::Buffer<u8>>
where P: Borrow<glin::Program>,
T: Borrow<glin::Texture>
{
let data = material.data();
let id = material.id();
if let Some((begin, end)) = self.offset_index.get(id){
self.buffer.range_mut(*begin .. *end).update(&data);
return self.buffer.range(*begin .. *end);
}
let begin = self.last_offset;
let end = begin + data.len();
self.offset_index.insert(id, (begin, end));
self.data.extend_from_slice(&data);
let rem = data.len() % self.ubo_align_size;
if rem != 0 {
let add = self.ubo_align_size - rem;
self.data.extend(unsafe{ vec![mem::uninitialized::<u8>(); add] });
self.last_offset = end + add;
}else{
self.last_offset = end;
}
self.buffer.load(&self.data, gl::STATIC_DRAW);
self.buffer.range(begin .. end)
}
}
thread_local!(static PBR_BUFFER: UnsafeCell<DenseVec<Buffer>> = UnsafeCell::new(DenseVec::default()));
mod id{
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct ProgramId{
pub context_id: usize,
pub num_directional_lights: usize,
pub num_spot_lights: usize,
pub num_point_lights: usize,
pub num_shadow_maps: usize,
pub max_num_shadow_maps_directional: usize,
pub max_num_shadow_maps_point: usize,
pub max_num_shadow_maps_spot: usize,
pub use_ibl: bool,
pub use_metallic_map: bool,
pub shadow_map_type: Option<&'static str>,
pub has_base_color_texture: bool,
pub has_normal_map: bool,
pub has_emissive_map: bool,
pub has_anisotropy_map: bool,
pub has_tangents: bool,
pub has_separate_alpha: bool,
pub is_double_sided: bool,
pub has_clip_plane: bool,
pub ty: super::Type,
pub alpha_type: super::AlphaType,
pub use_lighting_ubo: bool,
pub use_material_ubo: bool,
pub use_camera_ubo: bool,
pub use_model_matrices_as_attrs: bool,
}
}
use self::id::ProgramId;
fn set_default_uniforms<R: RenderSurface>(program: &glin::Program, id: &ProgramId, gl: &gl::Renderer<R>){
#[cfg(any(feature="image", feature="freeimage"))]
{
if let Some(loc) = program.uniform_location("iblbrdf"){
match id.ty {
Type::Lambert => (),
Type::Standard | Type::Anisotropic | Type::Clearcoat | Type::Subsurface =>
program
.set_uniform(&glin::program::uniform_location(loc, &(textures::brdf_texture(gl), gl::TextureBindingPoints::IblBrdf as u32)))
.unwrap(),
Type::Cloth | Type::ClothSubsurface =>
program
.set_uniform(&glin::program::uniform_location(loc, &(textures::cloth_ashikhmin_brdf_texture(gl), gl::TextureBindingPoints::IblBrdf as u32)))
.unwrap(),
}
}
}
#[cfg(not(any(feature="image", feature="freeimage")))]
{
#[cfg(feature="stdweb_window")]
console!(log, "no support for image loading, can't load brdf lut for pbr materials. Add one of \"image\" or \"freeimage\" features to your Cargo.toml");
panic!("no support for image loading, can't load brdf lut for pbr materials. Add one of \"image\" or \"freeimage\" features to your Cargo.toml");
}
if id.use_camera_ubo {
program
.set_uniform(&gl::program::uniform("camera_data", &gl::program::UniformValue::UniformBlockBinding(2)))
.unwrap();
}
}
#[cfg(not(feature = "glsl-debug"))]
mod shaders{
use std::cell::UnsafeCell;
use std::collections::HashMap;
use super::ProgramId;
use glin;
use gl::{self, Renderer, RenderSurface};
use util::LogErr;
thread_local!(static PBR_SHADER: UnsafeCell<HashMap<ProgramId, glin::Program>> = UnsafeCell::new(HashMap::new()));
pub fn get_pbr_program<R: RenderSurface>(gl: &Renderer<R>, id: ProgramId) -> &'static glin::Program{
PBR_SHADER.with(|programs|{
unsafe{ (*programs.get()).entry(id).or_insert_with(||{
let num_directional_lights = id.num_directional_lights.to_string();
let num_spot_lights = id.num_spot_lights.to_string();
let num_point_lights = id.num_point_lights.to_string();
let num_shadow_maps = id.num_shadow_maps.to_string();
let use_ibl = (id.use_ibl as u8).to_string();
let use_metallic_map = (id.use_metallic_map as u8).to_string();
let use_lighting_ubo = (id.use_lighting_ubo as u8).to_string();
let use_material_ubo = (id.use_material_ubo as u8).to_string();
let use_camera_ubo = (id.use_camera_ubo as u8).to_string();
let has_base_color_tex = (id.has_base_color_texture as u8).to_string();
let has_normal_map = (id.has_normal_map as u8).to_string();
let has_shadow_map = (id.shadow_map_type.is_some() as u8).to_string();
let shadow_map_type = id.shadow_map_type.unwrap_or("0");
let has_emissive_map = (id.has_emissive_map as u8).to_string();
let has_anisotropy_map = (id.has_anisotropy_map as u8).to_string();
let has_tangents = (id.has_tangents as u8).to_string();
let ty = id.ty.to_string();
let alpha_type = id.alpha_type.to_string();
let is_double_sided = (id.is_double_sided as u8).to_string();
let has_clip_plane = (id.has_clip_plane as u8).to_string();
let has_separate_alpha = (id.has_separate_alpha as u8).to_string();
let use_model_attr = (id.use_model_matrices_as_attrs as u8).to_string();
let max_num_shadow_maps_directional = id.max_num_shadow_maps_directional.to_string();
let max_num_shadow_maps_spot = id.max_num_shadow_maps_spot.to_string();
let max_num_shadow_maps_point = id.max_num_shadow_maps_point.to_string();
#[cfg(feature="freeimage")]
let inverted_ibl_lut = "1".to_string();
#[cfg(feature="image")]
let inverted_ibl_lut = "0".to_string();
#[cfg(not(any(feature="image", feature="freeimage")))]
{
error!("no support for image loading, can't load brdf lut for pbr materials. Add one of \"image\" or \"freeimage\" features to your Cargo.toml");
panic!();
}
let settings = glin::program::Settings{
version: gl::default_glsl_version(),
precission: glin::program::ShaderPrecision::High,
extensions: vec![],
defines: vec![
("DEBUG_ENABLED", "0"),
("NUM_DIRECTIONAL_LIGHTS", &num_directional_lights),
("NUM_SPOT_LIGHTS", &num_spot_lights),
("NUM_POINT_LIGHTS", &num_point_lights),
("NUM_SHADOW_MAPS", &num_shadow_maps),
("USE_IBL", &use_ibl),
("USE_UBOS", &use_lighting_ubo),
("USE_MATERIAL_UBO", &use_material_ubo),
("USE_CAMERA_UBO", &use_camera_ubo),
("HAS_TEXTURE", &has_base_color_tex),
("HAS_NORMALMAP", &has_normal_map),
("HAS_SHADOWMAP", &has_shadow_map),
("HAS_METALROUGHNESSMAP", &use_metallic_map),
("HAS_EMISSIVEMAP", &has_emissive_map),
("HAS_ANISOTROPYMAP", &has_anisotropy_map),
("HAS_TANGENTS", &has_tangents),
("HAS_CLIP_PLANE", &has_clip_plane),
("HAS_SEPARATE_ALPHA", &has_separate_alpha),
("USE_MODEL_ATTR", &use_model_attr),
("DOUBLE_SIDED", &is_double_sided),
("SHADOW_MAP_TYPE", shadow_map_type),
("MANUAL_SRGB", "1"),
("SRGB_FAST_APPROXIMATION", "1"),
("MATERIAL_TYPE", &ty),
("ALPHA_TYPE", &alpha_type),
("TARGET_MOBILE", "0"),
#[cfg(any(feature="image", feature="freeimage"))]
("INVERTED_IBL_BRDF_LUT", &inverted_ibl_lut),
("MAX_NUM_SHADOW_MAPS_DIRECTIONAL", &max_num_shadow_maps_directional),
("MAX_NUM_SHADOW_MAPS_SPOT", &max_num_shadow_maps_spot),
("MAX_NUM_SHADOW_MAPS_POINT", &max_num_shadow_maps_point),
],
shaders: vec![
(gl::VERTEX_SHADER, include_str!("shaders/vertex_pbr.glsl")),
(gl::FRAGMENT_SHADER, include_str!("shaders/fragment_pbr.glsl")),
],
bindings: gl::default_attribute_bindings(),
includes: hash_map!{
"lighting_uniforms.glsl" => {include_str!("shaders/lighting_uniforms.glsl")},
"material_uniforms.glsl" => {include_str!("shaders/material_uniforms.glsl")},
"mvp_uniforms.glsl" => {include_str!("shaders/mvp_uniforms.glsl")},
"poisson_disk.glsl" => {include_str!("shaders/poisson_disk.glsl")},
"gaussian_shadow.glsl" => {include_str!("shaders/gaussian_shadow.glsl")},
"pcss.fs.glsl" => {include_str!("shaders/pcss.fs.glsl")},
"hard_shadows.glsl" => {include_str!("shaders/hard_shadows.glsl")}
},
.. Default::default()
};
let program = gl.new_program()
.from_settings(settings)
.log_err("")
.map_err(|_| panic!())
.unwrap();
super::set_default_uniforms(&program, &id, gl);
program
})}
})
}
}
#[cfg(feature = "glsl-debug")]
mod shaders{
use std::cell::UnsafeCell;
use std::collections::HashMap;
use super::{
ProgramId, UniformsLocationCache, Type,
locations_for_type,
};
use glin;
use gl::{self, Renderer, RenderSurface};
use util::{LogErr, AutoLoader};
use std::path::Path;
struct Program{
ty: Type,
loader: AutoLoader<glin::Program>,
program: glin::Program,
locations: Option<UniformsLocationCache>,
}
impl Program{
fn update(&mut self) -> bool{
match self.loader.update(){
Ok(Some(program)) => {
info!("{} program {} reloaded correctly", self.ty.to_string(), program.id());
if self.locations.is_some(){
self.locations = Some(locations_for_type(self.ty, &program));
}
self.program = program;
true
}
Err(err) => {
error!("Error reloading pbr shader {}", err);
false
}
_ => false
}
}
}
thread_local!(static PBR_SHADER: UnsafeCell<HashMap<ProgramId, Program>> = UnsafeCell::new(HashMap::new()));
pub fn get_pbr_program<R: RenderSurface>(gl: &Renderer<R>, id: ProgramId) -> (&'static glin::Program, Option<&'static UniformsLocationCache>){
let loader = PBR_SHADER.with(|programs|{
unsafe{ (*programs.get()).entry(id).or_insert_with(||{
let num_directional_lights = id.num_directional_lights.to_string();
let num_spot_lights = id.num_spot_lights.to_string();
let num_point_lights = id.num_point_lights.to_string();
let num_shadow_maps = id.num_shadow_maps.to_string();
let use_ibl = (id.use_ibl as u8).to_string();
let use_metallic_map = (id.use_metallic_map as u8).to_string();
let use_lighting_ubo = (id.use_lighting_ubo as u8).to_string();
let use_material_ubo = (id.use_material_ubo as u8).to_string();
let use_camera_ubo = (id.use_camera_ubo as u8).to_string();
let has_base_color_tex = (id.has_base_color_texture as u8).to_string();
let has_normal_map = (id.has_normal_map as u8).to_string();
let has_shadow_map = (id.shadow_map_type.is_some() as u8).to_string();
let has_emissive_map = (id.has_emissive_map as u8).to_string();
let has_anisotropy_map = (id.has_anisotropy_map as u8).to_string();
let shadow_map_type = id.shadow_map_type.unwrap_or("0").to_owned();
let has_tangents = (id.has_tangents as u8).to_string();
let ty = id.ty.to_string();
let alpha_type = id.alpha_type.to_string();
let is_double_sided = (id.is_double_sided as u8).to_string();
let has_clip_plane = (id.has_clip_plane as u8).to_string();
let has_separate_alpha = (id.has_separate_alpha as u8).to_string();
let use_model_attr = (id.use_model_matrices_as_attrs as u8).to_string();
let max_num_shadow_maps_directional = id.max_num_shadow_maps_directional.to_string();
let max_num_shadow_maps_spot = id.max_num_shadow_maps_spot.to_string();
let max_num_shadow_maps_point = id.max_num_shadow_maps_point.to_string();
#[cfg(feature="freeimage")]
let inverted_ibl_lut = "1".to_string();
#[cfg(feature="image")]
let inverted_ibl_lut = "0".to_string();
#[cfg(not(any(feature="image", feature="freeimage")))]
{
error!("no support for image loading, can't load brdf lut for pbr materials. Add one of \"image\" or \"freeimage\" features to your Cargo.toml");
panic!();
}
let src_path = Path::new(file!()).parent().unwrap();
let vertex_path = src_path.join("shaders/vertex_pbr.glsl");
let fragment_path = src_path.join("shaders/fragment_pbr.glsl");
let settings = gl::autoload::ProgramSettings{
version: gl::default_glsl_version(),
precision: glin::program::ShaderPrecision::High,
extensions: vec![],
defines: vec![
("DEBUG_ENABLED".to_owned(), "0".to_owned()),
("NUM_DIRECTIONAL_LIGHTS".to_owned(), num_directional_lights),
("NUM_SPOT_LIGHTS".to_owned(), num_spot_lights),
("NUM_POINT_LIGHTS".to_owned(), num_point_lights),
("NUM_SHADOW_MAPS".to_owned(), num_shadow_maps),
("USE_IBL".to_owned(), use_ibl),
("USE_UBOS".to_owned(), use_lighting_ubo),
("USE_MATERIAL_UBO".to_owned(), use_material_ubo),
("USE_CAMERA_UBO".to_owned(), use_camera_ubo),
("HAS_TEXTURE".to_owned(), has_base_color_tex),
("HAS_NORMALMAP".to_owned(), has_normal_map),
("HAS_SHADOWMAP".to_owned(), has_shadow_map),
("HAS_METALROUGHNESSMAP".to_owned(), use_metallic_map),
("HAS_EMISSIVEMAP".to_owned(), has_emissive_map),
("HAS_ANISOTROPYMAP".to_owned(), has_anisotropy_map),
("HAS_TANGENTS".to_owned(), has_tangents),
("HAS_CLIP_PLANE".to_owned(), has_clip_plane),
("HAS_SEPARATE_ALPHA".to_owned(), has_separate_alpha),
("USE_MODEL_ATTR".to_owned(), use_model_attr),
("DOUBLE_SIDED".to_owned(), is_double_sided),
("SHADOW_MAP_TYPE".to_owned(), shadow_map_type),
("MANUAL_SRGB".to_owned(), "1".to_owned()),
("SRGB_FAST_APPROXIMATION".to_owned(), "1".to_owned()),
("MATERIAL_TYPE".to_owned(), ty.clone()),
("ALPHA_TYPE".to_owned(), alpha_type.clone()),
("TARGET_MOBILE".to_owned(), "0".to_owned()),
#[cfg(any(feature="image", feature="freeimage"))]
("INVERTED_IBL_BRDF_LUT".to_owned(), inverted_ibl_lut),
("MAX_NUM_SHADOW_MAPS_DIRECTIONAL".to_owned(), max_num_shadow_maps_directional),
("MAX_NUM_SHADOW_MAPS_SPOT".to_owned(), max_num_shadow_maps_spot),
("MAX_NUM_SHADOW_MAPS_POINT".to_owned(), max_num_shadow_maps_point),
],
shaders: vec![
(gl::VERTEX_SHADER, vertex_path),
(gl::FRAGMENT_SHADER, fragment_path),
],
};
let mut loader = gl.new_auto_program(settings);
let program = loader.load()
.log_err("")
.map_err(|_| panic!())
.unwrap();
let locations = if !id.use_material_ubo{
Some(locations_for_type(id.ty, &program))
}else{
None
};
debug!("Compiling program {} for pbr shader", program.id());
super::set_default_uniforms(&program, &id, gl);
Program{
loader,
program,
locations,
ty: id.ty,
}
})}
});
if loader.update(){
super::set_default_uniforms(&loader.program, &id, &gl);
}
(&loader.program, loader.locations.as_ref())
}
}
#[cfg(any(feature="image", feature="freeimage"))]
mod textures{
use glin;
use graphics;
use glin::RenderSurface;
use gl::Renderer;
image_cache!(get_image_32bit, IMAGE32, {
let brdf_lut = include_bytes!("textures/brdfLUT.exr");
graphics::image::load_from_memory(brdf_lut).unwrap()
});
image_cache!(get_image_16bit, IMAGE16, {
let brdf_lut = include_bytes!("textures/brdfLUT16.png");
graphics::image::load_from_memory(brdf_lut).unwrap()
});
image_cache!(get_cloth_ashikhmin_image_32bit, CLOTH_ASHIKHMIN_IMAGE32, {
let brdf_lut = include_bytes!("textures/cloth_ashikhmin_brdfLUT.exr");
graphics::image::load_from_memory(brdf_lut).unwrap()
});
image_cache!(get_cloth_ashikhmin_image_16bit, CLOTH_ASHIKHMIN_IMAGE16, {
let brdf_lut = include_bytes!("textures/cloth_ashikhmin_brdfLUT16.png");
graphics::image::load_from_memory(brdf_lut).unwrap()
});
image_cache!(get_cloth_charlie_image_32bit, CLOTH_CHARLIE_IMAGE32, {
let brdf_lut = include_bytes!("textures/cloth_charlie_brdfLUT.exr");
graphics::image::load_from_memory(brdf_lut).unwrap()
});
image_cache!(get_cloth_charlie_image_16bit, CLOTH_CHARLIE_IMAGE16, {
let brdf_lut = include_bytes!("textures/cloth_charlie_brdfLUT16.png");
graphics::image::load_from_memory(brdf_lut).unwrap()
});
pub fn brdf_texture<'r, R: RenderSurface>(gl: &'r Renderer<R>) -> &'r glin::Texture{
if graphics::image::supports(graphics::image::Format::Exr){
get_image_32bit(gl)
}else{
get_image_16bit(gl)
}
}
pub fn cloth_ashikhmin_brdf_texture<'r, R: RenderSurface>(gl: &'r Renderer<R>) -> &'r glin::Texture{
if graphics::image::supports(graphics::image::Format::Exr){
get_cloth_ashikhmin_image_32bit(gl)
}else{
get_cloth_ashikhmin_image_16bit(gl)
}
}
pub fn cloth_charlie_brdf_texture<'r, R: RenderSurface>(gl: &'r Renderer<R>) -> &'r glin::Texture{
if graphics::image::supports(graphics::image::Format::Exr){
get_cloth_charlie_image_32bit(gl)
}else{
get_cloth_charlie_image_16bit(gl)
}
}
}
struct Data{
data: Vec<u8>
}
impl Data{
fn push<T>(&mut self, data: T){
unsafe{
self.data.extend_from_slice(slice::from_raw_parts(&data as *const T as *const u8, mem::size_of::<T>()));
}
}
}
impl Deref for Data{
type Target = [u8];
fn deref(&self) -> &[u8]{
&self.data
}
}