use crate::{
components::{Name, Visible, SourcePath},
geometry::{
Geometry, Submesh, GeometryRef,
},
transformation::{DynamicTransformation, RenderPlane},
material::{MaterialRef, AlphaType},
};
#[cfg(feature="skinning")]
use crate::skinning::components::AnimatedGeometry;
use crate::renderer::{
components::{
ProgramRef,
},
resources,
pbr_material::MaterialCache,
shadow,
memory::*,
allocator,
};
#[cfg(feature="gl_material_unified_ubo")]
use crate::renderer::pbr_material::MaterialPool;
use rinecs::{
CreationProxy, CreationSystem, Entities, Resources, World,
EntitiesThreadLocal, ResourcesThreadLocal,
Entity, ReadNot, Read, Write, ReadOption, ReadRef,
SystemThreadLocal
};
use rin::{
graphics::{ self, vertex, primitive_type_to_gl, Vertex, PrimitiveType},
gl, gl::types::*,
events::{Property, Parameter, PropertyT},
};
use glin::VertexFormat;
use std::marker::PhantomData;
use itertools::{Itertools, Either};
use na::{Vec4, Mat4};
use std::mem;
use std::fmt::{self, Debug};
use std::rc::Rc;
use std::cell::RefCell;
use serde::{Serialize, Deserialize};
use rayon::prelude::*;
use hashbrown::HashMap;
#[derive(Component, Clone, Copy, Eq, PartialEq, Debug, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub struct GpuGeometryRef(pub Entity);
#[cfg(not(feature="skinning"))]
#[derive(Component, Debug, Serialize, Deserialize)]
#[debug_as_string]
pub struct AnimatedGeometry<V: Debug + 'static>{
marker: PhantomData<V>,
}
#[derive(OneToNComponent)]
#[debug_as_string]
pub struct SubmeshBuffers{
has_changed: bool,
primitive_type: GLenum,
vao_range: VaoRange,
original_vao_range: VaoRange,
get_vao: Box<Fn(
&Rc<glin::CreationProxy>,
&ResourcesThreadLocal,
&glin::SharedBuffer<(Mat4, Mat4)>) -> glin::Result<&'static mut glin::Vao>>,
get_shadow_vao: Box<Fn(
&Rc<glin::CreationProxy>,
&ResourcesThreadLocal,
&glin::SharedBuffer<Mat4>) -> glin::Result<&'static mut glin::Vao>>,
get_vao_range: Box<Fn(&ResourcesThreadLocal, &VaoRange) -> VaoRangeInfo>,
get_command: Box<Fn(&ResourcesThreadLocal, &VaoRange, u32, u32) -> glin::DrawElementsIndirectCommand>,
}
impl Debug for SubmeshBuffers{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result{
fmt.debug_struct("SubmeshBuffers")
.field("has_changed", &self.has_changed)
.finish()
}
}
unsafe impl Send for SubmeshBuffers{}
impl SubmeshBuffers{
pub fn new<T,B>(vao_range: VaoRange, primitive_type: PrimitiveType) ->
SubmeshBuffers
where T: VertexFormat + Clone + 'static,
B: 'static + Clone + BufferExt<u8> +
glin::buffer::Cast<glin::IndexT> +
glin::buffer::Cast<T>,
<B as glin::buffer::Cast<glin::IndexT>>::CastTo: 'static +
glin::BufferRangeMut<glin::IndexT> +
Clone,
<B as glin::buffer::Cast<T>>::CastTo: 'static +
glin::BufferRangeMut<T> +
Clone,
allocator::Allocator<B>: allocator::InternalCreation<B> +
allocator::Creation<B> +
allocator::Updater,
Allocator<T,B>: AllocatorFlags,
{
let primitive_type = primitive_type_to_gl(primitive_type);
let vao_id = vao_range.vao_id();
#[cfg(feature="gl_material_unified_ubo")]
let get_vao = move |
gl: &Rc<glin::CreationProxy>,
resources: &ResourcesThreadLocal,
model_buffer: &glin::SharedBuffer<(Mat4, Mat4)>|
{
let mut allocator = resources.get_mut::<Allocator<T,B>>().unwrap();
let material_pool = resources.get::<MaterialPool>().unwrap();
let material_offsets = material_pool.offsets_buffer();
unsafe{ mem::transmute(allocator.vao(gl, vao_id, model_buffer, Some(&material_offsets))) }
};
#[cfg(not(feature="gl_material_unified_ubo"))]
let get_vao = move |
gl: &Rc<glin::CreationProxy>,
resources: &ResourcesThreadLocal,
model_buffer: &glin::SharedBuffer<(Mat4, Mat4)>|
{
let mut allocator = resources.get_mut::<Allocator<T,B>>().unwrap();
unsafe{ mem::transmute(allocator.vao(gl, vao_id, model_buffer, None)) }
};
let get_shadow_vao = move |
gl: &Rc<glin::CreationProxy>,
resources: &ResourcesThreadLocal,
model_buffer: &glin::SharedBuffer<Mat4>|
{
let mut allocator = resources.get_mut::<Allocator<T,B>>().unwrap();
unsafe{ mem::transmute(allocator.shadow_vao(gl, vao_id, model_buffer)) }
};
let get_command = move |
resources: &ResourcesThreadLocal,
range: &VaoRange,
base_instance: u32,
instance_count: u32|
{
let allocator = resources.get::<Allocator<T,B>>().unwrap();
unsafe{ mem::transmute(allocator.command(range, base_instance, instance_count)) }
};
let get_vao_range = move |resources: &ResourcesThreadLocal, range: &VaoRange|{
let allocator = resources.get::<Allocator<T,B>>().unwrap();
unsafe{ mem::transmute(allocator.vao_range(range)) }
};
SubmeshBuffers{
original_vao_range: vao_range.clone(),
vao_range,
primitive_type,
has_changed: true,
get_vao: Box::new(get_vao),
get_shadow_vao: Box::new(get_shadow_vao),
get_command: Box::new(get_command),
get_vao_range: Box::new(get_vao_range),
}
}
pub fn vao_base_instance<C: glin::CreationContext>(&self,
gl: &C,
resources: &ResourcesThreadLocal,
model_buffer: &glin::SharedBuffer<(Mat4, Mat4)>,
base_instance: u32) -> glin::Result<glin::vao::Range>
{
let vao = (self.get_vao)(gl.creation_proxy(), resources, model_buffer)?;
let vao_range = (self.get_vao_range)(resources, &self.vao_range);
if let Some(base_vertex) = vao_range.base_vertex{
Ok(vao.range_base_vertex_base_instance(
vao_range.range.clone(),
base_vertex,
base_instance,
self.primitive_type))
}else{
Ok(vao.range_base_instance(
vao_range.range.clone(),
base_instance,
self.primitive_type))
}
}
pub fn shadow_vao_base_instance<C: glin::CreationContext>(&self,
gl: &C,
resources: &ResourcesThreadLocal,
model_buffer: &glin::SharedBuffer<Mat4>,
base_instance: u32) -> glin::Result<glin::vao::Range>
{
let vao = (self.get_shadow_vao)(gl.creation_proxy(), resources, model_buffer)?;
let vao_range = (self.get_vao_range)(resources, &self.vao_range);
if let Some(base_vertex) = vao_range.base_vertex{
Ok(vao.range_base_vertex_base_instance(
vao_range.range.clone(),
base_vertex,
base_instance,
self.primitive_type))
}else{
Ok(vao.range_base_instance(
vao_range.range.clone(),
base_instance,
self.primitive_type))
}
}
pub fn has_indices(&self) -> bool{
self.vao_range.has_indices()
}
pub fn command(&self, resources: &ResourcesThreadLocal, base_instance: u32, instance_count: u32) -> glin::DrawElementsIndirectCommand {
(self.get_command)(resources, &self.vao_range, base_instance, instance_count)
}
pub fn full_vao<C: glin::CreationContext>(&self,
gl: &C,
resources: &ResourcesThreadLocal,
model_buffer: &glin::SharedBuffer<(Mat4, Mat4)>) -> glin::Result<&mut glin::Vao>
{
(self.get_vao)(gl.creation_proxy(), resources, model_buffer)
}
pub fn full_shadow_vao<C: glin::CreationContext>(&self,
gl: &C,
resources: &ResourcesThreadLocal,
model_buffer: &glin::SharedBuffer<Mat4>) -> glin::Result<&mut glin::Vao>
{
(self.get_shadow_vao)(gl.creation_proxy(), resources, model_buffer)
}
pub fn range_vao<'v>(&self, resources: &ResourcesThreadLocal, vao: &'v glin::Vao) -> glin::vao::Range<'v>{
let vao_range = (self.get_vao_range)(resources, &self.vao_range);
if let Some(base_vertex) = vao_range.base_vertex{
vao.range_base_vertex(vao_range.range.clone(), base_vertex, self.primitive_type)
}else{
vao.range(vao_range.range.clone(), self.primitive_type)
}
}
pub fn vao_range(&self) -> &VaoRange{
&self.vao_range
}
pub fn primitive_type(&self) -> GLenum{
self.primitive_type
}
pub fn has_changed(&self) -> bool{
self.has_changed
}
pub fn reset_has_changed(&mut self){
self.has_changed = false;
}
}
#[derive(Component, Debug)]
#[debug_as_string]
pub struct VertexBuffer(pub BufferRef);
#[derive(Component, Debug)]
#[debug_as_string]
pub struct IndicesBuffer(pub BufferRef);
#[derive(Component, Debug)]
#[debug_as_string]
pub struct DebugNormals(pub gl::SimpleVao<graphics::Vertex3D>);
#[derive(Component, Debug)]
#[debug_as_string]
pub struct ShadowGeometry(SubmeshBuffers);
#[derive(Component, Debug)]
#[debug_as_string]
pub struct StaticShadowGeometry(SubmeshBuffers);
#[derive(Component, Debug)]
#[debug_as_string]
pub struct AllShadowGeometry(SubmeshBuffers);
pub struct GeometryUploader<T, B>{
marker: PhantomData<T>,
marker_buff: PhantomData<B>,
}
impl<T: 'static + Clone + VertexFormat, B: 'static> GeometryUploader<T, B>{
pub fn new(
world: &mut World,
vertex_bytes: usize,
indices_allocator: Rc<RefCell<IndexAllocator<B>>>) -> GeometryUploader<T, B>
{
let allocator = super::memory::Allocator::<T,B>::new(vertex_bytes, indices_allocator);
world.add_resource_thread_local(allocator);
GeometryUploader{
marker: PhantomData,
marker_buff: PhantomData,
}
}
}
impl<'a, V, B> CreationSystem<'a> for GeometryUploader<V, B>
where V: VertexFormat + Clone + Copy + Debug +
Serialize + Deserialize<'static> + 'static,
B: 'static + Clone + BufferExt<u8> +
glin::buffer::Cast<glin::IndexT> +
glin::buffer::Cast<V>,
<B as glin::buffer::Cast<glin::IndexT>>::CastTo: 'static +
glin::BufferRangeMut<glin::IndexT> +
Clone,
<B as glin::buffer::Cast<V>>::CastTo: 'static +
glin::BufferRangeMut<V> +
Clone,
allocator::Allocator<B>: allocator::InternalCreation<B> +
allocator::Creation<B> +
allocator::Updater,
Allocator<V,B>: AllocatorFlags,
{
fn run(&mut self, mut entities: CreationProxy, resources: ResourcesThreadLocal) {
fn build_geometry<V,B>(
entities: &CreationProxy,
allocator: &mut Allocator<V,B>,
name: Name,
geometryref: GeometryRef,
entities_vec: Vec<Entity>,
glin: &gl::Renderer,
dynamic: bool) -> AddGpuGeometry
where V: gl::VertexFormat + Clone + Copy + 'static,
B: 'static + Clone + BufferExt<u8> +
glin::buffer::Cast<glin::IndexT> +
glin::buffer::Cast<V>,
<B as glin::buffer::Cast<glin::IndexT>>::CastTo: 'static +
glin::BufferRangeMut<glin::IndexT> +
Clone,
<B as glin::buffer::Cast<V>>::CastTo: 'static +
glin::BufferRangeMut<V> +
Clone,
allocator::Allocator<B>: allocator::InternalCreation<B> +
allocator::Creation<B> +
allocator::Updater,
Allocator<V,B>: AllocatorFlags,
{
let geom = entities.component_for::<Geometry<V>>(&geometryref).unwrap();
let submeshes = entities.component_for::<Submesh>(&geometryref);
let submeshes = submeshes.map(|s| &s as &[Submesh]);
let source_path = entities.component_for::<SourcePath>(&geometryref);
let bytes = geom.len() * mem::size_of::<V>() +
geom.indices().len() * mem::size_of::<glin::IndexT>();
let size = allocator::human(bytes);
trace!("Creating gpu geometries for {:?}::{:?} {}", source_path, name, size);
let has_submeshes = submeshes.is_some();
let (vao_ranges, full_range, vertices, indices) = allocator.submesh_vaos(glin, &geom, submeshes, dynamic);
if has_submeshes {
trace!("Creating {} gpu geometries for {:?}::{:?}, indices: {}, submeshes {}",
vao_ranges.len(), source_path, name, indices.is_some(), vao_ranges.len());
}else{
trace!("Creating 1 gpu geometries (no submeshes) for {:?}::{:?}", source_path, name);
}
let submesh_buffers = vao_ranges.into_iter().map(|vao_range|{
SubmeshBuffers::new::<V,B>(vao_range, geom.primitive_type())
}).collect::<Vec<_>>();
let shadow_geom = ShadowGeometry(SubmeshBuffers::new::<V,B>(
full_range.clone(),
geom.primitive_type()));
let static_shadow_geom = StaticShadowGeometry(SubmeshBuffers::new::<V,B>(
full_range.clone(),
geom.primitive_type()));
let all_shadow_geom = AllShadowGeometry(SubmeshBuffers::new::<V,B>(
full_range,
geom.primitive_type()));
AddGpuGeometry::New{
entities_vec,
name,
vertices,
indices,
submesh_buffers,
shadow_geom,
static_shadow_geom,
all_shadow_geom,
}
}
let glin = resources.get::<gl::Renderer<'static>>().unwrap();
let mut allocator = resources.get_mut::<Allocator<V,B>>().unwrap();
enum AddGpuGeometry{
Reuse(Vec<Entity>, GpuGeometryRef),
New{
entities_vec: Vec<Entity>,
vertices: BufferRef,
indices: Option<BufferRef>,
submesh_buffers: Vec<SubmeshBuffers>,
shadow_geom: ShadowGeometry,
static_shadow_geom: StaticShadowGeometry,
all_shadow_geom: AllShadowGeometry,
name: Name,
}
}
let to_add_index = entities.iter_for::<(
Entity,
Read<Visible>,
ReadNot<GeometryRef, GpuGeometryRef>,
ReadRef<GeometryRef, Geometry<V>>,
ReadOption<AnimatedGeometry<V>>
)>().filter_map(|(entity, visible, geometryref, geom, animated_geom)|{
if !visible.is_visible() || geom.is_none() {
None
}else{
Some((*geometryref, (entity, animated_geom.is_some())))
}
}).into_group_map();
let to_add = to_add_index.into_iter().flat_map(|(geometryref, toadd)| {
let (animated, nonanimated) : (Vec<_>, Vec<_>) = toadd.into_iter()
.partition_map(|(entity, animated)| if animated{
Either::Left(entity)
}else{
Either::Right(entity)
});
let name = entities.component_for::<Name>(&geometryref);
let non_animated = if !nonanimated.is_empty(){
if let Some((gpugeom, _geom)) = entities.iter_for::<(Read<GpuGeometryRef>, Read<GeometryRef>)>()
.find(|(_, geom)| **geom == geometryref)
{
trace!("Reusing gpu geometry");
Some(AddGpuGeometry::Reuse(nonanimated, *gpugeom))
}else{
Some(build_geometry::<V,B>(
&entities,
&mut allocator,
name.as_ref()
.map(|n| Name(format!("{} gpu geometry", **n)))
.unwrap_or_else(|| Name("Gpu geometry".to_owned())),
geometryref,
nonanimated,
&glin,
false))
}
}else{
None
};
let animated = animated.into_iter().map(|entity| build_geometry(
&entities,
&mut allocator,
name.as_ref()
.map(|name| Name(format!("{} animated gpu geometry for {}", **name, entity.guid())))
.unwrap_or_else(|| Name(format!("Animated gpu geometry for {}", entity.guid()))),
geometryref,
vec![entity],
&glin,
true)).collect::<Vec<_>>();
non_animated.into_iter().chain(animated)
}).collect::<Vec<_>>();
for gpuadd in to_add{
match gpuadd{
AddGpuGeometry::Reuse(entities_vec, gpugeom) => {
for entity in entities_vec {
entities.add_component_to(&entity, gpugeom);
}
}
AddGpuGeometry::New{
entities_vec,
name,
vertices,
indices,
submesh_buffers,
shadow_geom,
static_shadow_geom,
all_shadow_geom} =>
{
let gpugeom = entities.new_entity()
.add(name)
.add_slice(submesh_buffers)
.add_thread_local(shadow_geom)
.add_thread_local(static_shadow_geom)
.add_thread_local(all_shadow_geom)
.add_thread_local(VertexBuffer(vertices))
.build();
if let Some(indices) = indices {
entities.add_component_to_thread_local(&gpugeom, IndicesBuffer(indices));
}
for entity in entities_vec {
entities.add_component_to(&entity, GpuGeometryRef(gpugeom));
}
}
}
}
let iter = entities.iter_for::<(
Read<GeometryRef>,
Read<GpuGeometryRef>,
)>().unique();
let gl = resources.get::<gl::Renderer>().unwrap();
for (geom, gpugeom) in iter {
if let Some(mut mesh) = entities.component_for_mut::<Geometry<V>>(&geom){
let vertices = entities.component_for_mut::<VertexBuffer>(&gpugeom).unwrap();
let indices = entities.component_for_mut::<IndicesBuffer>(&gpugeom);
if mesh.has_changed() {
allocator.update_vertex_buffer_range(&*gl, &vertices, mesh.vertices()).unwrap();
if let Some(indices) = indices {
allocator.update_index_buffer_range(&*gl, &indices, mesh.indices()).unwrap();
}
mesh.reset_has_changed();
}
}
}
}
}
#[derive(Ord, PartialOrd, PartialEq, Eq)]
#[repr(C)]
struct GeomKey<'a> {
priority: i32,
properties: &'a [glin::Property],
programref: ProgramRef,
textures_hash: u64,
materialref: MaterialRef,
vao_id: VaoId,
geometryref: GpuGeometryRef,
submesh: usize,
}
#[derive(Ord, PartialOrd, PartialEq, Eq)]
#[repr(C)]
struct DistanceSortGeomKey<'a> {
priority: i32,
properties: &'a [glin::Property],
programref: ProgramRef,
textures_hash: u64,
vao_id: VaoId,
}
pub fn geometry_sort(entities: Entities, resources: Resources){
let materials_cache = resources.get::<MaterialCache>().unwrap();
let mut opaque_geometry = resources.get_mut::<resources::OpaqueSortedGeometry>().unwrap();
let mut translucent_geometry = resources.get_mut::<resources::TranslucentSortedGeometry>().unwrap();
let any_visible_changed = entities.iter_for::<Read<Visible>>()
.any(|v| v.has_changed());
let opaque_models_count = entities.iter_for::<(
Read<MaterialRef>,
Read<Visible>)>()
.map(|(materials, visible)| {
materials.iter().filter(|materialref| {
let material = &materials_cache[**materialref];
visible.is_visible() && (material.alpha == 1. ||
(material.alpha > 0. && material.alpha_type != AlphaType::Blending) ||
material.alpha_type == AlphaType::None)
}).count()
})
.sum::<usize>();
let translucent_models_count = entities.iter_for::<(
Read<MaterialRef>,
Read<Visible>)>()
.map(|(materials, visible)| {
materials.iter().filter(|materialref| {
let material = &materials_cache[**materialref];
visible.is_visible() && material.alpha > 0. && material.alpha < 1. &&
material.alpha_type == AlphaType::Blending
}).count()
})
.sum::<usize>();
let opaque_index_count = opaque_geometry.iter().map(|g| g.num_instances()).sum::<usize>();
let translucent_index_count = translucent_geometry.iter().map(|g| g.num_instances()).sum::<usize>();
if !any_visible_changed &&
opaque_models_count == opaque_index_count &&
translucent_models_count == translucent_index_count
{
opaque_geometry.reset_has_changed();
translucent_geometry.reset_has_changed();
return;
}
trace!("Geometry count changed opaque: {} -> {}, translucent {} -> {}, resorting",
opaque_index_count, opaque_models_count,
translucent_index_count, translucent_models_count);
opaque_geometry.clear();
translucent_geometry.clear();
if opaque_geometry.is_empty() || translucent_geometry.is_empty() {
let entities_models = entities.iter_for::<(
Entity,
ReadOption<Name>,
Read<GpuGeometryRef>,
ReadOption<RenderPlane>,
Read<MaterialRef>,
ReadRef<GpuGeometryRef, SubmeshBuffers>,
Read<Visible>,)>()
.filter(|(_,_,_,_,_,_, visible)| visible.is_visible());
for (entity, name, geometryref, renderplane, materials, submesh_buffers, _) in entities_models {
for (submesh, materialref) in materials.iter().enumerate() {
let material = &materials_cache[*materialref];
let vao_id = submesh_buffers.unwrap()[submesh].vao_range().vao_id();
if material.alpha == 1. ||
(material.alpha > 0. && material.alpha_type != AlphaType::Blending) ||
material.alpha_type == AlphaType::None
{
let geomkey = GeomKey{
priority: material.priority,
materialref: *materialref,
programref: material.programref,
properties: &material.properties,
textures_hash: material.textures_hash,
geometryref: *geometryref,
submesh,
vao_id,
};
let insert_pos = opaque_geometry.binary_search_by_key(&geomkey, |geom| {
let material = &materials_cache[geom.materialref];
GeomKey{
priority: material.priority,
materialref: geom.materialref,
programref: material.programref,
properties: &material.properties,
textures_hash: material.textures_hash,
geometryref: geom.geometryref,
submesh: geom.submesh,
vao_id: geom.vao_id,
}
});
match insert_pos {
Ok(insert_pos) => opaque_geometry[insert_pos]
.entities.push(entity),
Err(insert_pos) => opaque_geometry.insert(insert_pos,
resources::GeometryIndex{
entities: vec![entity],
name: name.map(|n| n.0.as_str()).unwrap_or("").to_owned(),
geometryref: *geometryref,
materialref: *materialref,
additional_uniforms: vec![],
submesh,
vao_id,
renderplane: renderplane.map(|_| entity),
}),
}
}else if material.alpha > 0. && material.alpha < 1. && material.alpha_type == AlphaType::Blending{
let geomkey = GeomKey{
priority: material.priority,
materialref: *materialref,
programref: material.programref,
properties: &material.properties,
textures_hash: material.textures_hash,
geometryref: *geometryref,
submesh,
vao_id,
};
let insert_pos = translucent_geometry.binary_search_by_key(&geomkey, |geom| {
let material = &materials_cache[geom.materialref];
GeomKey{
priority: material.priority,
materialref: geom.materialref,
programref: material.programref,
properties: &material.properties,
textures_hash: material.textures_hash,
geometryref: geom.geometryref,
submesh: geom.submesh,
vao_id: geom.vao_id,
}
});
match insert_pos {
Ok(insert_pos) => translucent_geometry[insert_pos]
.entities.push(entity),
Err(insert_pos) => translucent_geometry.insert(insert_pos,
resources::GeometryIndex{
entities: vec![entity],
name: name.map(|n| n.0.as_str()).unwrap_or("").to_owned(),
materialref: *materialref,
additional_uniforms: vec![],
geometryref: *geometryref,
submesh,
vao_id,
renderplane: renderplane.map(|_| entity),
}),
}
}
}
}
}
}
#[derive(Default)]
pub struct CommandBufferData{
pub data: Vec<glin::DrawElementsIndirectCommand>,
pub index: HashMap<(GpuGeometryRef, usize), usize>,
pub segments_per_renderplane: HashMap<Option<Entity>, Vec<(usize, usize)>>,
pub changed: bool,
}
pub fn update_command_buffer_data(entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let opaque_geometry = resources.get::<resources::OpaqueSortedGeometry>().unwrap();
let translucent_geometry = resources.get::<resources::TranslucentSortedGeometry>().unwrap();
if opaque_geometry.has_changed() || translucent_geometry.has_changed(){
let mut commands = resources.get_mut::<CommandBufferData>().unwrap();
let material_cache = resources.get::<MaterialCache>().unwrap();
commands.data.clear();
commands.index.clear();
commands.segments_per_renderplane.entry(None).or_insert_with(|| vec![]).clear();
for (renderplane, _, _) in entities.iter_for::<(
Entity,
Read<Visible>,
Read<RenderPlane>)>().filter(|(_,v,_)| v.is_visible())
{
commands.segments_per_renderplane.entry(Some(renderplane))
.or_insert_with(|| vec![])
.clear();
}
let mut prev_vao_id = None;
let mut prev_program_ref = None;
let mut prev_primitive_type = None;
let mut prev_textures = None;
let mut prev_properties = None;
let mut segment_start = 0;
let mut base_instance = 0;
for (segment, geom_index) in opaque_geometry.iter()
.chain(translucent_geometry.iter())
.enumerate()
{
let num_instances = geom_index.num_instances();
if let Some(submeshes) = entities.component_for::<SubmeshBuffers>(&geom_index.geometryref){
let submesh = &submeshes[geom_index.submesh];
let material = &material_cache[geom_index.materialref];
if (prev_program_ref.is_some() && prev_program_ref != Some(material.programref)) ||
(prev_vao_id.is_some() && prev_vao_id != Some(geom_index.vao_id)) ||
(prev_primitive_type.is_some() && prev_primitive_type != Some(submesh.primitive_type())) ||
(prev_textures.is_some() && prev_textures != Some(material.textures_hash)) ||
(prev_properties.is_some() && prev_properties != Some(&material.properties))
{
commands.segments_per_renderplane.get_mut(&None)
.unwrap()
.push((segment_start, segment));
segment_start = segment;
}
prev_vao_id = Some(geom_index.vao_id);
prev_program_ref = Some(material.programref);
prev_primitive_type = Some(submesh.primitive_type());
prev_textures = Some(material.textures_hash);
prev_properties = Some(&material.properties);
let pos = commands.data.len();
commands.index.insert((geom_index.geometryref, geom_index.submesh), pos);
commands.data.push(submesh.command(&resources, base_instance, num_instances as u32));
}
base_instance += num_instances as u32;
}
let last_segment = opaque_geometry.len() + translucent_geometry.len();
if last_segment > segment_start{
commands.segments_per_renderplane.get_mut(&None)
.unwrap()
.push((segment_start, last_segment));
}
for (renderplane, _, _) in entities.iter_for::<(
Entity,
Read<Visible>,
Read<RenderPlane>)>().filter(|(_,v,_)| v.is_visible())
{
let mut prev_vao_id = None;
let mut prev_program_ref = None;
let mut prev_primitive_type = None;
let mut prev_textures = None;
let mut prev_properties = None;
let mut segment_start = 0;
for (segment, geom_index) in opaque_geometry.iter()
.chain(translucent_geometry.iter())
.enumerate()
{
if let Some(submeshes) = entities.component_for::<SubmeshBuffers>(&geom_index.geometryref){
let submesh = &submeshes[geom_index.submesh];
let material = &material_cache[geom_index.materialref];
if geom_index.renderplane == Some(renderplane) {
if segment > segment_start + 1 {
commands.segments_per_renderplane.get_mut(&None)
.unwrap()
.push((segment_start, segment));
}
segment_start = segment + 1;
continue;
}
if (prev_program_ref != Some(material.programref)) ||
(prev_vao_id != Some(geom_index.vao_id)) ||
(prev_primitive_type != Some(submesh.primitive_type())) ||
(prev_textures != Some(material.textures_hash)) ||
(prev_properties.is_some() && prev_properties != Some(&material.properties))
{
commands.segments_per_renderplane.get_mut(&Some(renderplane))
.unwrap()
.push((segment_start, segment));
segment_start = segment;
}
prev_vao_id = Some(geom_index.vao_id);
prev_program_ref = Some(material.programref);
prev_primitive_type = Some(submesh.primitive_type());
prev_textures = Some(material.textures_hash);
prev_properties = Some(&material.properties);
}
}
let last_segment = opaque_geometry.len() + translucent_geometry.len();
if last_segment > segment_start{
commands.segments_per_renderplane.get_mut(&Some(renderplane))
.unwrap()
.push((segment_start, last_segment));
}
}
commands.changed = true;
}
}
pub struct CommandBuffer(pub glin::Buffer<glin::DrawElementsIndirectCommand>);
pub fn upload_command_buffer(_: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let mut commands = resources.get_mut::<CommandBufferData>().unwrap();
if commands.changed {
let mut command_buffer = resources.get_mut::<CommandBuffer>().unwrap();
if command_buffer.0.len() < commands.data.len(){
command_buffer.0.load(&commands.data, gl::STREAM_DRAW);
}else{
command_buffer.0.update(&commands.data);
}
commands.changed = false;
}
}
#[derive(Default)]
pub struct ShadowsCommandBufferData{
pub data: Vec<glin::DrawElementsIndirectCommand>,
pub index: HashMap<GpuGeometryRef, usize>,
pub segments: Vec<usize>,
pub changed: bool,
}
#[cfg(feature="gl_multidraw_indirect")]
impl ShadowsCommandBufferData{
fn parts_mut(&mut self) -> (&mut Vec<glin::DrawElementsIndirectCommand>, &mut HashMap<GpuGeometryRef, usize>, &mut Vec<usize>){
(&mut self.data, &mut self.index, &mut self.segments)
}
}
pub fn dynamic_shadows_geometry_sort(entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let has_static_shadows = entities.iter_for::<Read<shadow::StaticMap>>()
.next()
.is_some();
let has_dynamic_shadows = entities.iter_for::<Read<shadow::Map>>()
.next()
.is_some();
let materials_cache = resources.get::<MaterialCache>().unwrap();
if has_dynamic_shadows && has_static_shadows {
let models_count = entities.iter_for::<(
Read<DynamicTransformation>,
Read<GpuGeometryRef>,
Read<MaterialRef>,
Read<Visible>)>()
.filter(|(_,_, materials, visible)| visible.is_visible() && materials.iter()
.any(|m| materials_cache[*m].alpha > 0. ||
materials_cache[*m].alpha_type == AlphaType::None))
.count();
let mut geometry_sorted = resources.get_mut::<resources::DynamicShadowsSortedGeometry>().unwrap();
let index_count = geometry_sorted.iter().map(|g| g.num_instances()).sum::<usize>();
if models_count == index_count {
return
}
trace!("Regenerating dynamic shadows geometry index {} -> {}", index_count, models_count);
geometry_sorted.clear();
let entities_models = entities.iter_for::<(
Entity,
Read<DynamicTransformation>,
Read<GpuGeometryRef>,
ReadRef<GpuGeometryRef, ShadowGeometry>,
Read<MaterialRef>,
Read<Visible>)>()
.filter(|(_,_,_,_, materials, visible)| visible.is_visible() && materials.iter()
.any(|m| materials_cache[*m].alpha > 0. ||
materials_cache[*m].alpha_type == AlphaType::None));
for (entity, _, geometryref, geom, _, _) in entities_models {
let geom = geom.unwrap();
let geomkey = ShadowGeomKey{
vao_id: geom.vao_range().vao_id(),
primitive_type: geom.primitive_type(),
geometryref: *geometryref,
};
let insert_pos = geometry_sorted.binary_search_by_key(&geomkey, |geom| ShadowGeomKey{
vao_id: geom.vao_id,
primitive_type: geom.primitive_type,
geometryref: geom.geometryref,
});
match insert_pos {
Ok(insert_pos) => geometry_sorted[insert_pos]
.entities.push(entity),
Err(insert_pos) => geometry_sorted.insert(insert_pos,
resources::ShadowGeometryIndex{
entities: vec![entity],
geometryref: *geometryref,
vao_id: geom.vao_range().vao_id(),
primitive_type: geom.primitive_type(),
}),
}
}
#[cfg(feature="gl_multidraw_indirect")]
{
let mut commands = resources.get_mut::<ShadowsCommandBufferData>().unwrap();
commands.data.clear();
commands.segments.clear();
commands.index.clear();
let mut base_instance = 0;
let mut prev_vao_id = None;
let mut prev_primitive_type = None;
let (data, index, segments) = commands.parts_mut();
let iter = entities
.iter_for_entities::<Read<ShadowGeometry>, _>(geometry_sorted.geometryrefs().map(|g| *g))
.zip(geometry_sorted.iter())
.enumerate();
for (segment, (geom, geom_index)) in iter {
let num_instances = geom_index.num_instances() as u32;
if (prev_vao_id != Some(geom_index.vao_id)) ||
(prev_primitive_type != Some(geom.primitive_type()))
{
segments.push(segment);
prev_vao_id = Some(geom_index.vao_id);
prev_primitive_type = Some(geom.primitive_type());
}
let command = geom.command(&resources, base_instance, num_instances);
let pos = data.len();
index.insert(geom_index.geometryref, pos);
data.push(command);
base_instance += num_instances;
}
if !segments.is_empty(){
segments.push(geometry_sorted.len())
}
commands.changed = true;
}
}
}
pub struct ShadowsCommandBuffer(pub glin::Buffer<glin::DrawElementsIndirectCommand>);
pub fn upload_shadows_command_buffer(_: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let mut commands = resources.get_mut::<ShadowsCommandBufferData>().unwrap();
if commands.changed {
let mut command_buffer = resources.get_mut::<ShadowsCommandBuffer>().unwrap();
if command_buffer.0.len() < commands.data.len(){
command_buffer.0.load(&commands.data, gl::STREAM_DRAW);
}else{
command_buffer.0.update(&commands.data);
}
commands.changed = false;
}
}
#[derive(Default)]
pub struct StaticShadowsCommandBufferData{
pub data: Vec<glin::DrawElementsIndirectCommand>,
pub segments: Vec<usize>,
}
#[cfg(feature="gl_multidraw_indirect")]
impl StaticShadowsCommandBufferData{
fn data_and_segments(&mut self) -> (&mut Vec<glin::DrawElementsIndirectCommand>, &mut Vec<usize>){
(&mut self.data, &mut self.segments)
}
}
pub fn static_shadows_geometry_sort(entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let has_static_shadows = entities.iter_for::<Read<shadow::StaticMap>>()
.next()
.is_some();
let materials_cache = resources.get::<MaterialCache>().unwrap();
if has_static_shadows {
let models_count = entities.iter_for::<(
ReadNot<GpuGeometryRef, DynamicTransformation>,
Read<MaterialRef>,
Read<Visible>)>()
.filter(|(_, materials, visible)| visible.is_visible() && materials.iter()
.any(|m| materials_cache[*m].alpha > 0. ||
materials_cache[*m].alpha_type == AlphaType::None))
.count();
let mut geometry_sorted = resources.get_mut::<resources::StaticShadowsSortedGeometry>().unwrap();
let index_count = geometry_sorted.iter().map(|g| g.num_instances()).sum::<usize>();
if models_count == index_count {
return
}
trace!("Regenerating static shadows geometry index {} -> {}", index_count, models_count);
geometry_sorted.clear();
let entities_models = entities.iter_for::<(
Entity,
ReadNot<GpuGeometryRef, DynamicTransformation>,
ReadRef<GpuGeometryRef, StaticShadowGeometry>,
Read<MaterialRef>,
Read<Visible>)>()
.filter(|(_,_,_, materials, visible)| visible.is_visible() && materials.iter()
.any(|m| materials_cache[*m].alpha > 0. ||
materials_cache[*m].alpha_type == AlphaType::None));
for (entity, geometryref, geom, _, _) in entities_models {
let geom = geom.unwrap();
let geomkey = ShadowGeomKey{
vao_id: geom.vao_range().vao_id(),
primitive_type: geom.primitive_type(),
geometryref: *geometryref,
};
let insert_pos = geometry_sorted.binary_search_by_key(&geomkey, |geom| ShadowGeomKey{
vao_id: geom.vao_id,
primitive_type: geom.primitive_type,
geometryref: geom.geometryref,
});
match insert_pos {
Ok(insert_pos) => geometry_sorted[insert_pos]
.entities.push(entity),
Err(insert_pos) => geometry_sorted.insert(insert_pos,
resources::ShadowGeometryIndex{
entities: vec![entity],
geometryref: *geometryref,
vao_id: geom.vao_range().vao_id(),
primitive_type: geom.primitive_type(),
}),
}
}
#[cfg(feature="gl_multidraw_indirect")]
{
let mut commands = resources.get_mut::<StaticShadowsCommandBufferData>().unwrap();
commands.data.clear();
commands.segments.clear();
let mut base_instance = 0;
let mut prev_vao_id = None;
let mut prev_primitive_type = None;
let (data, segments) = commands.data_and_segments();
let new_commands = entities
.iter_for_entities::<Read<StaticShadowGeometry>, _>(geometry_sorted.geometryrefs().map(|g| *g))
.zip(geometry_sorted.iter())
.enumerate()
.map(|(segment, (geom, geom_index))| {
let num_instances = geom_index.num_instances() as u32;
if (prev_vao_id != Some(geom_index.vao_id)) ||
(prev_primitive_type != Some(geom.primitive_type()))
{
segments.push(segment);
prev_vao_id = Some(geom_index.vao_id);
prev_primitive_type = Some(geom.primitive_type());
}
let command = geom.command(&resources, base_instance, num_instances);
base_instance += num_instances;
command
});
data.extend(new_commands);
if !segments.is_empty(){
segments.push(geometry_sorted.len())
}
}
}
}
pub struct StaticShadowsCommandBuffer(pub glin::Buffer<glin::DrawElementsIndirectCommand>);
pub fn upload_static_shadows_command_buffer(_: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let geometries = resources.get::<resources::StaticShadowsSortedGeometry>().unwrap();
if geometries.has_changed() {
let commands = resources.get::<StaticShadowsCommandBufferData>().unwrap();
let mut command_buffer = resources.get_mut::<StaticShadowsCommandBuffer>().unwrap();
command_buffer.0.load(&commands.data, gl::STATIC_DRAW);
}
}
#[derive(Default)]
pub struct AllShadowsCommandBufferData{
pub data: Vec<glin::DrawElementsIndirectCommand>,
pub index_per_range_start: HashMap<GpuGeometryRef, usize>,
pub segments: Vec<usize>,
pub changed: bool,
}
#[cfg(feature="gl_multidraw_indirect")]
impl AllShadowsCommandBufferData{
fn parts_mut(&mut self) -> (&mut Vec<glin::DrawElementsIndirectCommand>, &mut HashMap<GpuGeometryRef, usize>, &mut Vec<usize>){
(&mut self.data, &mut self.index_per_range_start, &mut self.segments)
}
}
#[repr(C)]
#[derive(PartialOrd, PartialEq, Ord, Eq)]
struct ShadowGeomKey{
vao_id: VaoId,
primitive_type: GLenum,
geometryref: GpuGeometryRef,
}
#[repr(C)]
#[derive(PartialOrd, PartialEq, Ord, Eq)]
struct ShadowDistanceSortGeomKey{
vao_id: VaoId,
primitive_type: GLenum,
}
pub fn all_shadows_geometry_sort(entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let any_has_only_dynamic_shadows = entities.iter_for::<(Read<shadow::Map>, ReadOption<shadow::StaticMap>)>()
.any(|(_, ss)| ss.is_none());
let materials_cache = resources.get::<MaterialCache>().unwrap();
let depth_prepass = resources.get::<resources::DepthPrepass>().is_some();
if any_has_only_dynamic_shadows || depth_prepass {
let models_count = entities.iter_for::<(
Read<GpuGeometryRef>,
Read<MaterialRef>,
Read<Visible>)>()
.filter(|(_, materials, visible)| visible.is_visible() && materials.iter()
.any(|m| materials_cache[*m].alpha > 0. ||
materials_cache[*m].alpha_type == AlphaType::None))
.count();
let mut geometry_sorted = resources.get_mut::<resources::AllShadowsSortedGeometry>().unwrap();
if models_count == geometry_sorted.iter().map(|g| g.num_instances()).sum::<usize>() {
return
}
trace!("Regenerating all shadows geometry index");
geometry_sorted.clear();
let entities_models = entities.iter_for::<(
Entity,
Read<GpuGeometryRef>,
ReadRef<GpuGeometryRef, AllShadowGeometry>,
Read<MaterialRef>,
Read<Visible>)>()
.filter(|(_,_,_, materials, visible)| visible.is_visible() && materials.iter()
.any(|m| materials_cache[*m].alpha > 0. ||
materials_cache[*m].alpha_type == AlphaType::None));
for (entity, geometryref, geom, _, _) in entities_models {
let geom = geom.unwrap();
let geomkey = ShadowGeomKey{
vao_id: geom.vao_range().vao_id(),
primitive_type: geom.primitive_type(),
geometryref: *geometryref,
};
let insert_pos = geometry_sorted.binary_search_by_key(&geomkey, |geom| ShadowGeomKey{
vao_id: geom.vao_id,
primitive_type: geom.primitive_type,
geometryref: geom.geometryref,
});
match insert_pos {
Ok(insert_pos) => geometry_sorted[insert_pos]
.entities.push(entity),
Err(insert_pos) => geometry_sorted.insert(insert_pos,
resources::ShadowGeometryIndex{
entities: vec![entity],
vao_id: geom.vao_range().vao_id(),
primitive_type: geom.primitive_type(),
geometryref: *geometryref,
}),
}
}
#[cfg(feature="gl_multidraw_indirect")]
{
let mut commands = resources.get_mut::<AllShadowsCommandBufferData>().unwrap();
commands.data.clear();
commands.index_per_range_start.clear();
commands.segments.clear();
let mut base_instance = 0;
let mut prev_vao_id = None;
let mut prev_primitive_type = None;
let (data, index, segments) = commands.parts_mut();
let iter = entities
.iter_for_entities::<Read<AllShadowGeometry>, _>(geometry_sorted.geometryrefs().map(|g| *g))
.zip(geometry_sorted.iter())
.enumerate();
for (segment, (geom, geom_index)) in iter {
let num_instances = geom_index.num_instances() as u32;
if (prev_vao_id != Some(geom_index.vao_id)) ||
(prev_primitive_type != Some(geom.primitive_type()))
{
segments.push(segment);
prev_vao_id = Some(geom_index.vao_id);
prev_primitive_type = Some(geom.primitive_type());
}
let command = geom.command(&resources, base_instance, num_instances);
let pos = data.len();
index.insert(geom_index.geometryref, pos);
data.push(command);
base_instance += num_instances;
}
if !segments.is_empty(){
segments.push(geometry_sorted.len())
}
commands.changed = true;
}
}
}
pub struct AllShadowsCommandBuffer(pub glin::Buffer<glin::DrawElementsIndirectCommand>);
pub fn upload_all_shadows_command_buffer(_: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let mut commands = resources.get_mut::<AllShadowsCommandBufferData>().unwrap();
if commands.changed {
let mut command_buffer = resources.get_mut::<AllShadowsCommandBuffer>().unwrap();
if command_buffer.0.len() < commands.data.len(){
command_buffer.0.load(&commands.data, gl::STREAM_DRAW);
}else{
command_buffer.0.update(&commands.data);
}
commands.changed = false;
}
}
#[cfg(feature="skinning")]
pub struct AnimatedGeometryGpuUpdater<V,B>{
update_debug_normals: Parameter<'static, bool>,
debug_normals_length: Parameter<'static, f32>,
_marker: PhantomData<V>,
_marker_buff: PhantomData<B>,
}
#[cfg(feature="skinning")]
pub struct AnimatedGeometryGpuUpdaterSettings{
pub update_debug_normals: Property<'static, bool>,
pub debug_normals_length: Property<'static, f32>,
}
#[cfg(feature="skinning")]
impl<V,B> AnimatedGeometryGpuUpdater<V,B>{
pub fn new(settings: AnimatedGeometryGpuUpdaterSettings) -> AnimatedGeometryGpuUpdater<V,B>{
AnimatedGeometryGpuUpdater{
update_debug_normals: settings.update_debug_normals.to_parameter(),
debug_normals_length: settings.debug_normals_length.to_parameter(),
_marker: PhantomData,
_marker_buff: PhantomData
}
}
}
#[cfg(feature="skinning")]
impl<V: 'static, B> AnimatedGeometryGpuUpdater<V, B>
where V: Vertex<Position = Vec4> + vertex::Normal + Debug + Clone + VertexFormat +
Serialize + Deserialize<'static> + Send + 'static,
B: 'static + Clone +
BufferExt<u8> +
glin::buffer::Cast<glin::IndexT> +
glin::buffer::Cast<V>,
<B as glin::buffer::Cast<glin::IndexT>>::CastTo: 'static +
glin::BufferRangeMut<glin::IndexT> +
Clone,
<B as glin::buffer::Cast<V>>::CastTo: 'static +
glin::BufferRangeMut<V> +
Clone,
allocator::Allocator<B>: allocator::InternalCreation<B> +
allocator::Creation<B> +
allocator::Updater,
Allocator<V,B>: AllocatorFlags,
{
fn update_thread_local(&mut self, entities: &EntitiesThreadLocal, resources: &ResourcesThreadLocal){
let mut allocator = resources.get_mut::<Allocator<V,B>>().unwrap();
#[cfg(feature="gl_multidraw_indirect")]
let mut command_buffer_data = resources.get_mut::<CommandBufferData>().unwrap();
#[cfg(feature="gl_multidraw_indirect")]
let mut shadow_command_buffer_data = resources.get_mut::<ShadowsCommandBufferData>()
.unwrap();
#[cfg(feature="gl_multidraw_indirect")]
let mut all_shadow_command_buffer_data = resources.get_mut::<AllShadowsCommandBufferData>()
.unwrap();
let any_has_only_dynamic_shadows = entities.iter_for::<(Read<shadow::Map>, ReadOption<shadow::StaticMap>)>()
.any(|(_, ss)| ss.is_none());
let depth_prepass = resources.get::<resources::DepthPrepass>().is_some();
let gl = resources.get::<gl::Renderer>().unwrap();
let iter = entities.iter_for::<(
Write<AnimatedGeometry<V>>,
Read<GpuGeometryRef>,
)>();
iter.filter(|(animated_geom, _,)| animated_geom.changed)
.for_each(|(animated_geom, gpu_geometry_ref)| {
if *self.update_debug_normals{
if let Some(mut debug_normals) = entities.component_for_mut::<DebugNormals>(gpu_geometry_ref){
let length = *self.debug_normals_length;
let normals_data: Vec<_> = animated_geom.geom.iter().flat_map(|v|{
vec![
graphics::vertex3d(v.position().xyz()),
graphics::vertex3d(v.position().xyz() + v.normal() * length),
]
}).collect();
let mut mesh = graphics::Mesh::from_vertices(normals_data);
mesh.set_primitive_type(graphics::PrimitiveType::Lines);
debug_normals.update_vertices(&mesh);
}
}
let mut vertex_buffer = entities
.component_for_mut::<VertexBuffer>(&gpu_geometry_ref)
.unwrap();
let mut submesh_buffers = entities
.component_for_mut::<SubmeshBuffers>(gpu_geometry_ref)
.unwrap();
allocator.update_vertex_buffer_range(&*gl, &vertex_buffer, &animated_geom.geom).unwrap();
animated_geom.changed = false;
});
}
}
#[cfg(feature="skinning")]
impl<'a, V: 'static, B> SystemThreadLocal<'a> for AnimatedGeometryGpuUpdater<V, B>
where V: Vertex<Position = Vec4> + vertex::Normal + Debug + Clone + VertexFormat +
Serialize + Deserialize<'static> + 'static + Send,
B: 'static +
BufferExt<u8> +
Clone +
glin::buffer::Cast<glin::IndexT> +
glin::buffer::Cast<V>,
<B as glin::buffer::Cast<glin::IndexT>>::CastTo: 'static +
glin::BufferRangeMut<glin::IndexT> +
Clone,
<B as glin::buffer::Cast<V>>::CastTo: 'static +
glin::BufferRangeMut<V> +
Clone,
allocator::Allocator<B>: allocator::InternalCreation<B> +
allocator::Creation<B> +
allocator::Updater,
Allocator<V,B>: AllocatorFlags,
{
fn run(&mut self, entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
self.update_thread_local(&entities, &resources);
}
}