macro_rules! hash_map(
{ $($key:expr => $value:expr),+ } => {
{
let mut m = ::std::collections::HashMap::new();
$(
m.insert($key, $value);
)+
m
}
};
);
#[macro_use] mod utils;
pub mod light;
pub mod pbr_material;
mod basic_material;
mod outline_material;
mod renderer;
pub mod components;
mod shader_data;
pub mod shadow;
pub mod geometry;
mod camera;
pub mod resources;
mod transformation;
pub mod water;
mod depth_prepass;
pub mod memory;
pub mod allocator;
use crate::{
material::MaterialRef,
transformation::Viewport,
};
#[cfg(feature="skinning")]
use crate::skinning;
pub use self::renderer::Renderer;
pub use self::pbr_material::{
Material, MaterialCache, MaterialPool, material_pool_updater,
};
#[cfg(feature="gl_material_unified_ubo")]
pub use self::pbr_material::material_offsets_updater;
pub use self::light::{
ImageBasedLightBuilder
};
pub use self::shadow::ShadowMapPool;
pub use self::resources::TexturesPool;
pub use self::memory::Allocator;
use std::collections::{HashMap, HashSet};
use glin::{self, UntypedOffscreenBuffer};
use rin::{
gl,
window::Window,
color::consts::GREEN,
prelude::*,
events::Stream,
math::Rect,
};
use na::Vec2;
use generational_arena::Arena;
use rinecs::World;
use std::rc::Rc;
use std::cell::RefCell;
use std::fmt::Debug;
use serde::{Serialize, Deserialize};
use std::any::TypeId;
pub fn default_attribute_bindings() -> HashMap<String, u32>{
vec![
("position".to_string(), 0),
("color".to_string(), 1),
("texcoord".to_string(), 2),
("normal".to_string(), 3),
("material_offset".to_string(), 4),
("modelMatrix".to_string(), 5),
("normalMatrix".to_string(), 9),
].into_iter().collect()
}
#[repr(u32)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum TextureBindingPoints {
BaseColor = 0,
Normal = 1,
Occlusion = 2,
MetallicRoughness = 3,
Emissive = 4,
Anisotropy = 5,
DiffuseIbl = 6,
SpecularIbl = 7,
IblBrdf = 8,
Shadow = 9,
ShadowPcf = 10,
}
#[cfg(feature="gles")]
pub(crate) const fn default_glsl_version() -> usize{
300
}
#[cfg(feature="webgl")]
pub(crate) const fn default_glsl_version() -> usize{
300
}
#[cfg(feature="gl")]
pub(crate) const fn default_glsl_version() -> usize{
410
}
pub(crate) const fn use_model_as_attribute() -> bool{
true
}
#[cfg(feature="gl_material_unified_ubo")]
pub(crate) const fn use_material_unified_ubo() -> bool{
true
}
#[cfg(not(feature="gl_material_unified_ubo"))]
pub(crate) const fn use_material_unified_ubo() -> bool{
false
}
pub struct Bundle{
settings: Settings,
material_pool: MaterialPool,
textures_pool: TexturesPool,
shadow_maps_pool: ShadowMapPool,
render_surface: Option<resources::ScreenRenderBuffer>,
}
pub struct Settings{
pub renderer: gl::Renderer<'static>,
pub window: Window,
}
#[cfg(feature="gl")]
fn create_indices_storage(gl: &gl::Renderer) -> Rc<RefCell<allocator::Allocator<glin::SharedBufferStorage<u8>>>>{
let indices_allocator = allocator::Allocator::new_storage(&*gl, 1024,
memory::storage_static_flags(),
gl::ELEMENT_ARRAY_BUFFER)
.unwrap();
Rc::new(RefCell::new(indices_allocator))
}
fn create_indices_buffer(gl: &gl::Renderer) -> Rc<RefCell<allocator::Allocator<glin::SharedBuffer<u8>>>>{
let indices_allocator = allocator::Allocator::new_buffer(&*gl, 1024,
memory::buffer_static_usage(),
gl::ELEMENT_ARRAY_BUFFER)
.unwrap();
Rc::new(RefCell::new(indices_allocator))
}
impl Bundle{
fn _new(settings: Settings, render_surface: Option<resources::ScreenRenderBuffer>) -> Bundle{
let material_pool = MaterialPool::new(&settings.renderer);
let textures_pool = TexturesPool::new();
let shadow_maps_pool = ShadowMapPool::new(settings.renderer.creation_proxy());
Bundle {
material_pool,
textures_pool,
shadow_maps_pool,
render_surface,
settings,
}
}
pub fn new(settings: Settings) -> Bundle{
Self::_new(settings, None)
}
pub fn new_with_render_surface(settings: Settings, render_surface: resources::ScreenRenderBuffer) -> Bundle{
Self::_new(settings, Some(render_surface))
}
pub fn viewport_size_stream(&mut self) -> Stream<'static, Vec2<i32>>{
if self.render_surface.is_some() {
Stream::never()
}else{
self.settings.window.event_stream().window().resized()
}
}
pub fn viewport(&self) -> Rect<i32>{
if let Some(render_surface) = self.render_surface.as_ref() {
render_surface.viewport().into()
}else{
self.settings.window.viewport()
}
}
pub fn add_systems(world: &mut World){
let debug_material = {
let mut material_pool = world.resource_mut::<MaterialPool>().unwrap();
let debug_material = crate::material::basic_material::Builder::new()
.color(&GREEN)
.build();
material_pool.register_material("__DEBUG_MATERIAL", debug_material)
};
#[cfg(any(feature="gles", feature="webgl"))]
create_update_geometry_buffer(world);
world.add_barrier();
world.add_system_with_name_thread_local_gpu(camera::camera_ubo_updater, "camera ubo updater");
world.add_system_with_name_thread_local_gpu(material_pool_updater, "material pool");
world.add_creation_system_with_name_gpu(light::create_missing_light_matrices, "create light matrices");
world.add_system_with_name_thread_local_gpu(light::update_lights_data, "update lights data");
world.add_system_with_name(geometry::geometry_sort, "geometry sort");
world.add_system_with_name_thread_local(geometry::dynamic_shadows_geometry_sort, "dynamic shadows geom sort");
world.add_system_with_name_thread_local(geometry::static_shadows_geometry_sort, "static shadows geom sort");
world.add_system_with_name_thread_local(geometry::all_shadows_geometry_sort, "all shadows geom sort");
world.add_barrier();
#[cfg(feature="gl_material_unified_ubo")]
world.add_system_with_name_thread_local_gpu(material_offsets_updater, "material offsets update");
#[cfg(feature="gl_multidraw_indirect")]
{
world.add_system_with_name_thread_local(geometry::update_command_buffer_data, "command buffer data");
}
world.add_system_with_name(transformation::generate_model_matrices_data, "model matrices");
world.add_system_with_name(transformation::generate_static_model_matrices_data, "static m. matrices");
world.add_system_with_name(transformation::generate_dynamic_model_matrices_data, "dynamic m. matrices");
world.add_system_with_name(transformation::generate_all_model_matrices_data, "all m. matrices");
world.add_barrier();
#[cfg(feature="gl_multidraw_indirect")]
{
world.add_system_with_name_thread_local_gpu(geometry::upload_command_buffer, "upload command buffer");
world.add_system_with_name_thread_local_gpu(geometry::upload_shadows_command_buffer, "command buffer shadows");
world.add_system_with_name_thread_local_gpu(geometry::upload_static_shadows_command_buffer, "command buffer static shadows");
world.add_system_with_name_thread_local_gpu(geometry::upload_all_shadows_command_buffer, "command buffer all shadows");
}
world.add_system_with_name_thread_local_gpu(transformation::update_model_matrices_gpu, "upload m. matrices");
world.add_system_with_name_thread_local_gpu(transformation::update_static_model_matrices_gpu, "upload static m. matrices");
world.add_system_with_name_thread_local_gpu(transformation::update_dynamic_model_matrices_gpu, "upload dynamic m. matrices");
world.add_system_with_name_thread_local_gpu(transformation::update_all_model_matrices_gpu, "upload all m. matrices");
world.add_system_with_name_thread_local_gpu(shadow::shadow_maps_updater, "shadow maps update");
if world.resource::<resources::DepthPrepass>().is_some(){
trace!("Initializing gl with depth prepass");
world.add_system_with_name_thread_local_gpu(depth_prepass::depth_prepass, "depth prepass");
}
world.add_system_with_name_thread_local_gpu(renderer::Renderer::new(debug_material), "renderer");
#[cfg(feature="gl_persistent_map")]
world.add_system_with_name_thread_local(geometry::update_regions::<crate::blender::Vertex>, "update dyn geometry regions");
}
pub fn into_resources(self, world: &mut World) {
world.add_resource(Viewport::new(self.viewport()));
let window = self.settings.window;
world.add_resource_thread_local(Arena::<glin::Program>::new());
world.add_resource_thread_local(MaterialCache::new());
world.add_resource_thread_local(resources::CameraUBO{
ubo: self.settings.renderer.new_buffer().empty_target(gl::UNIFORM_BUFFER).unwrap(),
dirty: true,
});
world.add_resource_thread_local(resources::LightingUBO{
ubo: self.settings.renderer.new_buffer().empty_target(gl::UNIFORM_BUFFER).unwrap(),
dirty: true,
});
world.add_resource_thread_local(resources::LightingUniforms{
uniforms: vec![],
dirty: true,
});
world.add_resource(resources::LightData(self::shader_data::Data::default()));
world.add_resource(resources::ModelMatricesData{
data: vec![],
has_changed: true,
});
world.add_resource(resources::StaticModelMatricesData{
data: vec![],
has_changed: true,
});
world.add_resource(resources::DynamicModelMatricesData{
data: vec![],
has_changed: true,
});
world.add_resource(resources::AllModelMatricesData{
data: vec![],
has_changed: true,
});
world.add_resource_thread_local(resources::ModelMatricesBuffer(
self.settings.renderer.new_shared_buffer().empty_target(gl::ARRAY_BUFFER).unwrap()
));
world.add_resource_thread_local(resources::StaticModelMatricesBuffer(
self.settings.renderer.new_shared_buffer().empty_target(gl::ARRAY_BUFFER).unwrap()
));
world.add_resource_thread_local(resources::DynamicModelMatricesBuffer(
self.settings.renderer.new_shared_buffer().empty_target(gl::ARRAY_BUFFER).unwrap()
));
world.add_resource_thread_local(resources::AllModelMatricesBuffer(
self.settings.renderer.new_shared_buffer().empty_target(gl::ARRAY_BUFFER).unwrap()
));
#[cfg(feature="gl_multidraw_indirect")]
{
world.add_resource(geometry::CommandBufferData::default());
world.add_resource(geometry::ShadowsCommandBufferData::default());
world.add_resource(geometry::StaticShadowsCommandBufferData::default());
world.add_resource(geometry::AllShadowsCommandBufferData::default());
world.add_resource_thread_local(geometry::CommandBuffer(
self.settings.renderer.new_buffer().empty_target(gl::DRAW_INDIRECT_BUFFER).unwrap()
));
world.add_resource_thread_local(geometry::ShadowsCommandBuffer(
self.settings.renderer.new_buffer().empty_target(gl::DRAW_INDIRECT_BUFFER).unwrap()
));
world.add_resource_thread_local(geometry::StaticShadowsCommandBuffer(
self.settings.renderer.new_buffer().empty_target(gl::DRAW_INDIRECT_BUFFER).unwrap()
));
world.add_resource_thread_local(geometry::AllShadowsCommandBuffer(
self.settings.renderer.new_buffer().empty_target(gl::DRAW_INDIRECT_BUFFER).unwrap()
));
}
world.add_resource(resources::OpaqueSortedGeometry::new());
world.add_resource(resources::TranslucentSortedGeometry::new());
world.add_resource(resources::DynamicShadowsSortedGeometry::new());
world.add_resource(resources::StaticShadowsSortedGeometry::new());
world.add_resource(resources::AllShadowsSortedGeometry::new());
world.add_resource_thread_local(DeferredVertexRegister::new());
world.add_resource_thread_local(self.textures_pool);
world.add_resource_thread_local(self.shadow_maps_pool);
world.add_resource_thread_local(self.settings.renderer);
world.add_resource_thread_local(self.material_pool);
if let Some(render_surface) = self.render_surface {
if render_surface.depth_prepass().is_some(){
world.add_resource(resources::DepthPrepass);
}
world.add_resource_thread_local(render_surface);
}
world.add_resource_thread_local(window);
world.add_resource_thread_local(resources::GeometryWithModelsIndex(vec![]));
}
pub fn vertex_register(&self) -> VertexRegister{
VertexRegister::new(&self.settings.renderer)
}
pub fn register_components(world: &mut World){
world.register::<geometry::SubmeshBuffers>();
world.register::<geometry::ShadowGeometry>();
world.register::<geometry::StaticShadowGeometry>();
world.register::<geometry::AllShadowGeometry>();
world.register::<geometry::VertexBuffer>();
world.register_thread_local::<geometry::IndicesBuffer>();
world.register_thread_local::<geometry::DebugNormals>();
world.register::<geometry::GpuGeometryRef>();
world.register_thread_local::<light::LightMatricesUBO>();
world.register::<shadow::Map>();
world.register::<shadow::StaticMap>();
world.register::<MaterialRef>();
world.register::<light::ImageBasedLight>();
world.register_thread_local::<components::RenderPlane>();
}
}
pub struct VertexRegister{
#[cfg(feature="gl")]
indices_allocator_storage: Option<Rc<RefCell<allocator::Allocator<glin::SharedBufferStorage<u8>>>>>,
indices_allocator_buffer: Option<Rc<RefCell<allocator::Allocator<glin::SharedBuffer<u8>>>>>,
}
impl VertexRegister{
pub fn new(gl: &gl::Renderer) -> VertexRegister {
#[cfg(feature="gl")]
{
let supports_storage =
unsafe{ gl.context().gl().BufferStorage.is_loaded() };
if supports_storage {
VertexRegister {
indices_allocator_buffer: None,
indices_allocator_storage: Some(create_indices_storage(gl)),
}
}else{
VertexRegister {
indices_allocator_buffer: Some(create_indices_buffer(gl)),
indices_allocator_storage: None,
}
}
}
#[cfg(any(feature="gles", feature="webgl"))]
VertexRegister {
indices_allocator_buffer: Some(create_indices_buffer(gl)),
indices_allocator_storage: None,
}
}
}
pub struct DeferredVertexRegister{
to_register: Vec<Box<FnMut(
&mut World,
&mut VertexRegister,
)>>,
registered_vertices: HashSet<TypeId>,
}
impl DeferredVertexRegister{
pub fn new() -> DeferredVertexRegister{
DeferredVertexRegister{
to_register: vec![],
registered_vertices: HashSet::default(),
}
}
pub fn deferred_register_vertex_type<T>(&mut self) -> bool
where T: glin::VertexFormat + Send + Clone + Copy + Debug + Serialize + Deserialize<'static>
+ 'static
{
if self.registered_vertices.contains(&TypeId::of::<T>()){
return false
}
#[cfg(feature="gl")]
let registerer = |
world: &mut World,
vertex_register: &mut VertexRegister,
|{
#[cfg(feature="skinning")]
{
world.register::<skinning::components::AnimatedGeometry<T>>();
}
#[cfg(not(feature="skinning"))]
{
world.register::<geometry::AnimatedGeometry<T>>();
}
#[cfg(feature="gl")]
{
if let Some(indices_allocator) = vertex_register.indices_allocator_storage.as_ref() {
let uploader = geometry::GeometryUploader::<T,_>::new(world, 1024, indices_allocator.clone());
world.add_creation_system_with_name_gpu(uploader, "create gpu vertices");
}else if let Some(indices_allocator) = vertex_register.indices_allocator_buffer.as_ref() {
let uploader = geometry::GeometryUploader::<T,_>::new(world, 1024, indices_allocator.clone());
world.add_creation_system_with_name_gpu(uploader, "create gpu vertices");
};
}
#[cfg(any(feature="gles", feature="webgl"))]
{
let indices_allocator = vertex_register.indices_allocator_buffer.clone().unwrap();
let uploader = geometry::GeometryUploader::<T,_>::new(world, 1024, indices_allocator);
world.add_creation_system_with_name_gpu(uploader, "create gpu vertices");
}
};
self.registered_vertices.insert(TypeId::of::<T>());
self.to_register.push(Box::new(registerer));
true
}
pub fn update_registers(&mut self, world: &mut World, vertex_register: &mut VertexRegister){
for mut registerer in self.to_register.drain(..){
(registerer)(world, vertex_register)
}
}
}
program_cache!(glin::program::Settings{
version: default_glsl_version(),
extensions: vec![],
precission: glin::program::ShaderPrecision::High,
defines: vec![
("USE_CAMERA_UBO", "1"),
("USE_MODEL_ATTR", "1"),
],
shaders: vec![
(glin::gl::VERTEX_SHADER, include_str!("shaders/depth_only.vs.glsl")),
(glin::gl::FRAGMENT_SHADER, include_str!("shaders/depth_only.fs.glsl")),
],
bindings: default_attribute_bindings(),
base_path: "",
includes: hash_map!{
"mvp_uniforms.glsl" => {include_str!("shaders/mvp_uniforms.glsl")}
},
}, get_depth_only_shader, SHADOW_MAP_SHADER);