use std::cell::UnsafeCell;
use na::{Rotation2, vec2, vec3};
use angle::Angle;
use rinecs::{EntitiesThreadLocal, ResourcesThreadLocal};
use crate::{
renderer::{
default_attribute_bindings,
default_glsl_version,
use_material_unified_ubo,
TextureBindingPoints,
components::{ProgramRef},
shader_data::Data,
resources::TexturesPool,
},
material::*,
material::PbrMaterial as MutinyPbrMaterial,
material::texture::TextureSampler,
light::LightInfo,
};
#[cfg(feature="gl_material_unified_ubo")]
use super::resources::{OpaqueSortedGeometry, TranslucentSortedGeometry};
#[cfg(feature="gl_material_unified_ubo")]
use std::iter;
use std::collections::HashMap;
use glin;
use generational_arena::Arena;
use rin::gl::{self, types::*};
use densevec::KeyedDenseVec;
use std::mem;
use rin::util::LogErr;
use std::ops::{Index, IndexMut};
use std::hash::{Hash, Hasher};
pub trait Material: crate::material::Material {
fn data(&self) -> Option<Data>;
fn uniforms(&self, entities: &EntitiesThreadLocal, textures: &TexturesPool) -> Vec<glin::program::Uniform>;
fn properties(&self) -> Vec<glin::Property>;
fn program(&self, entities: &EntitiesThreadLocal, programs: &mut Arena<glin::Program>, gl: &gl::Renderer) -> ProgramRef;
fn double_sided(&self) -> bool;
fn alpha(&self) -> f32;
fn alpha_type(&self) -> AlphaType;
fn update(&mut self, entities: &EntitiesThreadLocal, resources: &ResourcesThreadLocal) -> bool;
fn textures(&self) -> Vec<&TextureSampler>;
fn priority(&self) -> Option<i32>{
None
}
}
fn into_gl_face(face: Face) -> GLenum{
match face{
Face::Front => gl::FRONT,
Face::Back => gl::BACK,
}
}
pub(crate) fn into_gl_property(property: &Property) -> glin::Property{
match property{
Property::ClipDistance(clip_dist) => glin::Property::ClipDistance(clip_dist.clone()) ,
Property::CullFace(face) => glin::Property::CullFace(face.map(|f| into_gl_face(f))),
Property::FrontFace(face) => glin::Property::FrontFace(into_gl_face(*face)),
Property::DepthClamp(v) => glin::Property::DepthClamp(*v),
Property::DepthTest(v) => glin::Property::DepthTest(*v),
Property::Dither(v) => glin::Property::Dither(*v),
Property::LineSmooth(v) => glin::Property::LineSmooth(*v),
Property::Multisample(v) => glin::Property::Multisample(*v),
Property::PolygonOffsetFill(v) => glin::Property::PolygonOffsetFill(*v),
Property::PolygonOffsetLine(v) => glin::Property::PolygonOffsetLine(*v),
Property::PolygonOffsetPoint(v) => glin::Property::PolygonOffsetPoint(*v),
Property::PolygonSmooth(v) => glin::Property::PolygonSmooth(*v),
Property::RasterizerDiscard(v) => glin::Property::RasterizerDiscard(*v),
Property::StencilMask(v) => glin::Property::StencilMask(*v),
Property::Scissor(v) => glin::Property::Scissor(v.map(|v| glin::Rect{
bottom: v.pos.y, left: v.pos.x, width: v.width, height: v.height
})),
Property::StencilTest(v) => glin::Property::StencilTest(*v),
Property::TextureCubemapSeamless(v) => glin::Property::TextureCubemapSeamless(*v),
Property::DepthMask(v) => glin::Property::DepthMask(*v),
Property::ColorMask(r,g,b,a) => glin::Property::ColorMask([*r,*g,*b,*a]),
Property::DepthRange(v1, v2) => glin::Property::DepthRange(*v1, *v2),
Property::LineWidth(v) => glin::Property::LineWidth(*v),
Property::PointSize(v) => glin::Property::PointSize(*v),
}
}
pub trait PbrMaterial: Material + MutinyPbrMaterial{
fn pbr_data(&self) -> Data;
}
impl PbrMaterial for StandardMaterial{
fn pbr_data(&self) -> Data{
let mut data = Data::with_capacity(12 * mem::size_of::<f32>());
data.push(self.base_color);
data.push(self.emissive_color);
data.push(self.roughness);
data.push(self.metallic);
data.push(self.reflectance);
data.push(self.reflectance_tint);
data
}
}
impl PbrMaterial for LambertMaterial{
fn pbr_data(&self) -> Data{
let mut data = Data::with_capacity(8 * mem::size_of::<f32>());
data.push(self.base_color);
data.push(self.emissive_color);
data
}
}
impl PbrMaterial for AnisotropicMaterial{
fn pbr_data(&self) -> Data{
let mut data = Data::with_capacity(22 * mem::size_of::<f32>());
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.base_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);
data.pad::<[f32;2]>();
data
}
}
impl PbrMaterial for ClothMaterial{
fn pbr_data(&self) -> Data{
let mut data = Data::with_capacity(16 * mem::size_of::<f32>());
data.push(self.base_color);
data.push(self.emissive_color);
data.push(self.sheen_color);
data.push(self.roughness);
data.pad::<[f32;3]>();
data
}
}
impl PbrMaterial for ClothSubsurfaceMaterial{
fn pbr_data(&self) -> Data{
let mut data = Data::with_capacity(20 * mem::size_of::<f32>());
data.push(self.base_color);
data.push(self.emissive_color);
data.push(self.sheen_color);
data.push(self.subsurface_color);
data.push(self.roughness);
data.pad::<[f32;3]>();
data
}
}
impl PbrMaterial for SubsurfaceMaterial{
fn pbr_data(&self) -> Data{
let mut data = Data::with_capacity(20 * mem::size_of::<f32>());
data.push(self.base_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.pad::<[f32;2]>();
data
}
}
impl PbrMaterial for ClearcoatMaterial{
fn pbr_data(&self) -> Data{
let mut data = Data::with_capacity(16 * mem::size_of::<f32>());
data.push(self.base_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.pad::<[f32;2]>();
data
}
}
impl<M: PbrMaterial> Material for M{
fn uniforms(&self, _: &EntitiesThreadLocal, textures: &TexturesPool) -> Vec<glin::program::Uniform>{
let mut uniforms = vec![];
match self.base_color_map(){
Some(tex) => {
let texture = &textures[tex.texture];
if let Some(sampler) = tex.sampler.map(|s| &textures[s]){
let sampler = texture.texture_sampler(sampler);
uniforms.push(glin::program::uniform("tex0", &(sampler, gl::TextureBindingPoints::BaseColor as u32)));
}else{
uniforms.push(glin::program::uniform("tex0", &(texture, gl::TextureBindingPoints::BaseColor as u32)));
}
}
_ => ()
}
match self.normal_map(){
Some(tex) => {
let texture = &textures[tex.texture];
if let Some(sampler) = tex.sampler.map(|s| &textures[s]){
let sampler = texture.texture_sampler(sampler);
uniforms.push(glin::program::uniform("norm", &(sampler, gl::TextureBindingPoints::Normal as u32)));
}else{
uniforms.push(glin::program::uniform("norm", &(texture, gl::TextureBindingPoints::Normal as u32)));
}
uniforms.push(glin::program::uniform("u_NormalScale", &self.normal_scale()));
}
_ => ()
}
match self.occlusion_map(){
Some(tex) => {
let texture = &textures[tex.texture];
if let Some(sampler) = tex.sampler.map(|s| &textures[s]){
let sampler = texture.texture_sampler(sampler);
uniforms.push(glin::program::uniform("u_OcclusionSampler", &(sampler, gl::TextureBindingPoints::Occlusion as u32)));
}else{
uniforms.push(glin::program::uniform("u_OcclusionSampler", &(texture, gl::TextureBindingPoints::Occlusion as u32)));
}
}
_ => ()
}
match self.metallic_roughness_map(){
Some(tex) => {
let texture = &textures[tex.texture];
if let Some(sampler) = tex.sampler.map(|s| &textures[s]){
let sampler = texture.texture_sampler(sampler);
uniforms.push(glin::program::uniform("u_MetallicRoughnessSampler", &(sampler, gl::TextureBindingPoints::MetallicRoughness as u32)));
}else{
uniforms.push(glin::program::uniform("u_MetallicRoughnessSampler", &(texture, gl::TextureBindingPoints::MetallicRoughness as u32)));
}
}
_ => ()
}
match self.emissive_map(){
Some(tex) => {
let texture = &textures[tex.texture];
if let Some(sampler) = tex.sampler.map(|s| &textures[s]){
let sampler = texture.texture_sampler(sampler);
uniforms.push(glin::program::uniform("u_EmissiveSampler", &(sampler, gl::TextureBindingPoints::Emissive as u32)));
}else{
uniforms.push(glin::program::uniform("u_EmissiveSampler", &(texture, gl::TextureBindingPoints::Emissive as u32)));
}
}
_ => ()
}
match self.anisotropy_map(){
Some(tex) => {
let texture = &textures[tex.texture];
if let Some(sampler) = tex.sampler.map(|s| &textures[s]){
let sampler = texture.texture_sampler(sampler);
uniforms.push(glin::program::uniform("anisotropy_map", &(sampler, gl::TextureBindingPoints::Anisotropy as u32)));
}else{
uniforms.push(glin::program::uniform("anisotropy_map", &(texture, gl::TextureBindingPoints::Anisotropy as u32)));
}
}
_ => ()
}
uniforms
}
fn properties(&self) -> Vec<glin::Property>{
let mut props = if self.double_sided() {
vec![glin::Property::CullFace(None)]
}else{
vec![gl::Property::CullFace(Some(gl::BACK))]
};
match self.alpha_type() {
AlphaType::AlphaToCoverage => props.push(gl::Property::SampleAlphaToCoverage(true)),
AlphaType::Blending => {
props.push(gl::Property::Blend(true));
props.push(gl::Property::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA));
}
_ => ()
}
props
}
fn program(&self, _: &EntitiesThreadLocal, programs_arena: &mut Arena<glin::Program>, gl: &gl::Renderer) -> ProgramRef{
let id = self.program_id();
*PBR_SHADER.with(|programs| {
unsafe{ (*programs.get()).entry(*id).or_insert_with(||{
trace!("Compiling pbr program with {} dir lights, {} spots, {} points, ibl: {}",
id.light_info.num_directional_lights, id.light_info.num_spot_lights,
id.light_info.num_point_lights, id.light_info.use_ibl);
let num_directional_lights = id.light_info.num_directional_lights.to_string();
let num_spot_lights = id.light_info.num_spot_lights.to_string();
let num_point_lights = id.light_info.num_point_lights.to_string();
let use_ibl = (id.light_info.use_ibl as u8).to_string();
let has_metallic_map = (id.has_metallic_map as u8).to_string();
let has_base_color_tex = (id.has_base_color_map as u8).to_string();
let has_normal_map = (id.has_normal_map as u8).to_string();
let has_shadow_map = (id.light_info.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.light_info.shadow_map_type().unwrap_or("0");
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_lighting_ubo = (true as u8).to_string();
let use_material_ubo = (!use_material_unified_ubo() as u8).to_string();
let use_material_ssbo = (use_material_unified_ubo() as u8).to_string();
let use_camera_ubo = (true as u8).to_string();
let use_model_attr = (super::use_model_as_attribute() as u8).to_string();
let max_num_shadow_maps_directional = id.light_info.max_num_shadow_maps_directional.to_string();
let max_num_shadow_maps_spot = id.light_info.max_num_shadow_maps_spot.to_string();
let max_num_shadow_maps_point = id.light_info.max_num_shadow_maps_point.to_string();
let num_shadow_maps = id.light_info.num_shadow_maps.to_string();
#[cfg(feature="freeimage")]
let inverted_ibl_lut = "1".to_string();
#[cfg(feature="image")]
let inverted_ibl_lut = "1".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: 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),
("USE_IBL", &use_ibl),
("USE_UBOS", &use_lighting_ubo),
("USE_MATERIAL_UBO", &use_material_ubo),
("USE_MATERIAL_SSBO", &use_material_ssbo),
("USE_CAMERA_UBO", &use_camera_ubo),
("HAS_TEXTURE", &has_base_color_tex),
("HAS_NORMALMAP", &has_normal_map),
("HAS_SHADOWMAP", &has_shadow_map),
("HAS_METALROUGHNESSMAP", &has_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),
("NUM_SHADOW_MAPS", &num_shadow_maps),
],
shaders: vec![
(gl::VERTEX_SHADER, include_str!("shaders/pbr.vs.glsl")),
(gl::FRAGMENT_SHADER, include_str!("shaders/pbr.fs.glsl")),
],
bindings: 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")},
"shadow_poisson.glsl" => {include_str!("shaders/shadow_poisson.glsl")},
"shadow_gaussian.glsl" => {include_str!("shaders/shadow_gaussian.glsl")},
"shadow_pcss.glsl" => {include_str!("shaders/shadow_pcss.glsl")},
"shadow_hard.glsl" => {include_str!("shaders/shadow_hard.glsl")}
},
.. Default::default()
};
let program = gl.new_program()
.from_settings(settings)
.log_err("")
.map_err(|_| panic!())
.unwrap();
set_default_uniforms(&program, &id, gl);
let id = programs_arena.insert(program);
ProgramRef::new(id)
})}
})
}
fn textures(&self) -> Vec<&TextureSampler>{
::std::iter::empty()
.chain(self.base_color_map())
.chain(self.normal_map())
.chain(self.occlusion_map())
.chain(self.metallic_roughness_map())
.chain(self.emissive_map())
.chain(self.anisotropy_map())
.collect()
}
fn double_sided(&self) -> bool{
self.is_double_sided()
}
fn alpha(&self) -> f32{
self.base_color_alpha()
}
fn data(&self) -> Option<Data>{
Some(self.pbr_data())
}
fn alpha_type(&self) -> AlphaType{
self.alpha_ty()
}
fn update(&mut self, _: &EntitiesThreadLocal, resources: &ResourcesThreadLocal) -> bool{
let light_info = resources.get::<LightInfo>().unwrap();
let changed = self.update_program_id(&light_info) || self.changed();
self.reset_changed();
changed
}
}
struct MaterialOffset{
start: usize,
end: usize,
#[cfg(feature="gl_material_unified_ubo")]
index: usize,
}
struct Buffer{
data: Vec<u8>,
buffer: gl::Buffer<u8>,
offset_index: KeyedDenseVec<MaterialRef, MaterialOffset>,
last_offset: usize,
#[cfg(not(feature="gl_material_unified_ubo"))]
ubo_align_size: usize,
}
impl Buffer {
pub fn new<R: glin::RenderSurface>(gl: &gl::Renderer<R>) -> Buffer{
Buffer{
data: vec![],
buffer: gl.new_buffer().empty_target(gl::UNIFORM_BUFFER).unwrap(),
offset_index: KeyedDenseVec::default(),
last_offset: 0,
#[cfg(not(feature="gl_material_unified_ubo"))]
ubo_align_size: unsafe{
gl.context().state().get_int(gl::UNIFORM_BUFFER_OFFSET_ALIGNMENT) as usize
},
}
}
fn add(&mut self, materialref: MaterialRef, data: Data) -> glin::buffer::Range<u8, glin::Buffer<u8>, &glin::Buffer<u8>> {
if let Some(offset) = self.offset_index.get(materialref){
self.buffer.range_mut(offset.start .. offset.end).update(&data);
return self.buffer.range(offset.start .. offset.end);
}
let mut begin;
let end;
#[cfg(not(feature="gl_material_unified_ubo"))]
{
begin = self.last_offset;
end = begin + data.len();
let offset = MaterialOffset{
start: begin,
end,
};
self.offset_index.insert(materialref, offset);
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;
}
}
#[cfg(feature="gl_material_unified_ubo")]
{
begin = self.last_offset;
let rem = begin % data.len();
if rem != 0 {
let add = data.len() - rem;
self.data.extend(unsafe{ vec![mem::uninitialized::<u8>(); add] });
begin += add;
}
end = begin + data.len();
self.offset_index.insert(materialref, MaterialOffset{
start: begin,
end: end,
index: begin / data.len(),
});
self.data.extend_from_slice(&data);
self.last_offset = end;
}
self.buffer.load(&self.data, gl::DYNAMIC_DRAW);
self.buffer.range(begin .. end)
}
pub fn buffer(&self) -> &glin::Buffer<u8>{
&self.buffer
}
#[cfg(not(feature="gl_material_unified_ubo"))]
pub fn ubo(&self, material_ref: MaterialRef) -> Option<glin::buffer::Range<u8, glin::Buffer<u8>, &glin::Buffer<u8>>>{
self.offset_index.get(material_ref)
.map(|range| self.buffer.range(range.start .. range.end))
}
pub fn offset(&self, material_ref: MaterialRef) -> Option<&MaterialOffset>{
self.offset_index.get(material_ref)
}
}
pub struct MaterialPool{
materials: KeyedDenseVec<MaterialRef, Box<dyn Material>>,
names_index: HashMap<String, MaterialRef>,
reverse_name_index: KeyedDenseVec<MaterialRef, String>,
ubos: Buffer,
#[cfg(feature="gl_material_unified_ubo")]
offsets_data: Vec<u32>,
#[cfg(feature="gl_material_unified_ubo")]
offsets_buffer: glin::SharedBuffer<u32>,
}
impl MaterialPool{
pub fn new(gl: &gl::Renderer) -> MaterialPool{
MaterialPool{
materials: KeyedDenseVec::new(),
names_index: HashMap::new(),
reverse_name_index: KeyedDenseVec::new(),
ubos: Buffer::new(gl),
#[cfg(feature="gl_material_unified_ubo")]
offsets_data: vec![],
#[cfg(feature="gl_material_unified_ubo")]
offsets_buffer: gl.new_shared_buffer().empty_target(gl::ARRAY_BUFFER).unwrap(),
}
}
pub fn register_material<M: Material + 'static>(&mut self, name: &str, material: M) -> MaterialRef{
let id = self.materials.insert_key_gen(Box::new(material));
self.names_index.insert(name.to_owned(), id);
self.reverse_name_index.insert(id, name.to_owned());
id
}
pub fn find_material(&self, name: &str) -> Option<MaterialRef>{
self.names_index.get(name).cloned()
}
pub fn materials(&self) -> impl Iterator<Item = &Box<dyn Material>>{
self.materials.values()
}
pub fn iter(&self) -> impl Iterator<Item = (MaterialRef, &Box<dyn Material>)>{
self.materials.iter()
}
fn update(&mut self, entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let mut materials_cache = resources.get_mut::<MaterialCache>().unwrap();
let glin = resources.get::<gl::Renderer<'static>>().unwrap();
let mut programs_pool = resources.get_mut::<Arena<gl::Program>>().unwrap();
let textures_pool = resources.get::<TexturesPool>().unwrap();
let ubos = &mut self.ubos;
for (materialref, material) in self.materials.iter_mut(){
if material.update(&entities, &resources) {
trace!("changing material {}", self.reverse_name_index[materialref]);
let programref = material.program(&entities, &mut programs_pool, &glin);
let program = &programs_pool[*programref];
material.data().map(|data| ubos.add(materialref, data) );
let uniforms = material.uniforms(&entities, &textures_pool)
.into_iter()
.filter_map(|uniform| uniform.to_location(program))
.collect();
let mut properties = material.properties();
#[cfg(not(feature="gl_material_unified_ubo"))]
{
if let Some(ubo) = ubos.ubo(materialref) {
properties.push(gl::UBOBindingPoints::Material.to_buffer_binding(ubo));
}
}
let mut hasher = fxhash::FxHasher::default();
material.textures().hash(&mut hasher);
let textures_hash = hasher.finish();
materials_cache.insert(materialref, MaterialData{
programref,
uniforms,
properties,
double_sided: material.double_sided(),
alpha: material.alpha(),
alpha_type: material.alpha_type(),
priority: material.priority().unwrap_or(0),
textures_hash,
});
}
}
}
#[cfg(feature="gl_material_unified_ubo")]
fn update_material_offsets(&mut self, resources: ResourcesThreadLocal){
let opaque_geometry_sorted = resources
.get::<OpaqueSortedGeometry>()
.unwrap();
let translucent_geometry_sorted = resources
.get::<TranslucentSortedGeometry>()
.unwrap();
let opaque_changed = opaque_geometry_sorted.has_changed();
let translucent_changed = translucent_geometry_sorted.has_changed();
let was_empty = self.offsets_data.is_empty();
if opaque_changed || translucent_changed || was_empty {
let geometries = opaque_geometry_sorted.iter()
.chain(translucent_geometry_sorted.iter());
self.offsets_data.clear();
let offset_index = &self.ubos.offset_index;
let material_offsets = geometries.flat_map(|geom| {
let offset = offset_index.get(geom.materialref)
.map(|offset| offset.index as u32)
.unwrap_or(0);
iter::repeat(offset).take(geom.entities.len())
});
self.offsets_data.extend(material_offsets);
if !(was_empty && self.offsets_data.is_empty()){
self.offsets_buffer.load(&self.offsets_data, gl::STATIC_DRAW);
}
}
}
#[cfg(not(feature="gl_material_unified_ubo"))]
pub fn ubo(&self, material_ref: MaterialRef) -> Option<glin::buffer::Range<u8, glin::Buffer<u8>, &glin::Buffer<u8>>>{
self.ubos.offset_index.get(material_ref)
.map(|range| self.ubos.buffer.range(range.start .. range.end))
}
pub fn buffer(&self) -> &glin::Buffer<u8>{
&self.ubos.buffer
}
#[cfg(feature="gl_material_unified_ubo")]
pub fn offsets_buffer(&self) -> &glin::SharedBuffer<u32>{
&self.offsets_buffer
}
}
impl Index<MaterialRef> for MaterialPool{
type Output = Box<Material>;
fn index(&self, index: MaterialRef) -> &Box<dyn Material>{
&self.materials[index]
}
}
impl IndexMut<MaterialRef> for MaterialPool{
fn index_mut(&mut self, index: MaterialRef) -> &mut Box<dyn Material>{
&mut self.materials[index]
}
}
pub fn material_pool_updater(entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let mut pool = resources.get_mut::<MaterialPool>().unwrap();
pool.update(entities, resources);
}
#[cfg(feature="gl_material_unified_ubo")]
pub fn material_offsets_updater(_: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let mut pool = resources.get_mut::<MaterialPool>().unwrap();
pool.update_material_offsets(resources);
}
#[derive(Clone)]
pub struct MaterialData{
pub programref: ProgramRef,
pub uniforms: Vec<gl::program::Uniform>,
pub properties: Vec<gl::Property>,
pub double_sided: bool,
pub alpha: f32,
pub alpha_type: AlphaType,
pub priority: i32,
pub textures_hash: u64,
}
unsafe impl Send for MaterialData{}
pub struct MaterialCache{
cache: KeyedDenseVec<MaterialRef, MaterialData>,
}
impl MaterialCache{
pub fn new() -> MaterialCache{
MaterialCache{
cache: KeyedDenseVec::new()
}
}
fn insert(&mut self, materialref: MaterialRef, data: MaterialData) -> Option<MaterialData>{
self.cache.insert(materialref, data)
}
pub fn is_empty(&self) -> bool{
self.cache.is_empty()
}
}
impl Index<MaterialRef> for MaterialCache{
type Output = MaterialData;
fn index(&self, materialref: MaterialRef) -> &MaterialData{
&self.cache[materialref]
}
}
impl IndexMut<MaterialRef> for MaterialCache{
fn index_mut(&mut self, materialref: MaterialRef) -> &mut MaterialData{
&mut self.cache[materialref]
}
}
thread_local!(static PBR_SHADER: UnsafeCell<HashMap<ProgramId, ProgramRef>> = UnsafeCell::new(HashMap::new()));
fn set_default_uniforms<R: glin::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 {
MaterialType::Lambert => (),
MaterialType::Standard | MaterialType::Anisotropic |
MaterialType::Clearcoat | MaterialType::Subsurface |
MaterialType::DisneyPrincipled => {
program
.set_uniform(&glin::program::uniform_location(loc, &(textures::brdf_texture(gl), TextureBindingPoints::IblBrdf as u32)))
.is_ok();
}
MaterialType::Cloth | MaterialType::ClothSubsurface => {
program
.set_uniform(&glin::program::uniform_location(loc, &(textures::cloth_ashikhmin_brdf_texture(gl), TextureBindingPoints::IblBrdf as u32)))
.is_ok();
}
}
}
}
#[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");
}
program
.set_uniform(&gl::UBOBindingPoints::Lighting.to_uniform_block_binding())
.is_ok();
program
.set_uniform(&gl::UBOBindingPoints::Camera.to_uniform_block_binding())
.unwrap();
program
.set_uniform(&gl::UBOBindingPoints::Material.to_uniform_block_binding())
.unwrap();
}
#[cfg(any(feature="image", feature="freeimage"))]
mod textures{
use glin;
use rin::graphics;
use rin::gl;
use glin::RenderSurface;
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 gl::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 gl::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 gl::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)
}
}
}