use crate::{
components::{Name, Visible, SourcePath, VisibleChanges},
geometry::{
Geometry, Submesh, GeometryRef, AnimatedGeometry
},
transformation::RenderPlane,
};
use rin_material::{MaterialRef, MaterialMultiRef, ShadowMaterialRef};
#[cfg(feature="debug_geometry")]
use crate::geometry::DebugGeometryRef;
use crate::renderer::{
components::{
ProgramRef,
},
resources,
material::{MaterialCache, ShadowMaterialCache},
shadow,
memory::*,
allocator,
};
use super::{material::{MaterialOffsets, ShadowMaterialOffsets}, memory};
use rinecs::{Component, CreationSystem, Entities, EntitiesCreation, EntitiesThreadLocal, Entity, Has, NToOneComponent, OneToNComponent, Read, ReadNot, ReadOption, ReadRef, Ref, RefN, Resources, ResourcesCreation, ResourcesThreadLocal, World, Write, creation_system, storage::SliceView, system, system_thread_local};
use rinecs::SystemThreadLocal;
use rin_graphics::{self as graphics, PrimitiveTypeToGl, PrimitiveType, Node, node::DynamicTransformation};
use rin_gl::{self as gl, gl::types::*};
use glin::VertexFormat;
use std::marker::PhantomData;
use itertools::{Itertools, Either};
use rin_math::Mat4;
use std::mem;
use std::fmt::{self, Debug};
use std::rc::Rc;
use std::cell::RefCell;
use hashbrown::HashMap;
use super::resources::{GeometryIndex, ShadowGeometryIndex};
use super::renderer::RendererType;
use serde_derive::{Serialize, Deserialize};
use rinecs::streaming_iterator;
#[derive(NToOneComponent, Clone, Copy, Eq, PartialEq, Debug, Ord, PartialOrd, Hash, Serialize, Deserialize)]
#[changes]
pub struct GpuGeometryRef(Entity);
#[cfg(feature="debug_geometry")]
#[derive(NToOneComponent, Clone, Copy, Eq, PartialEq, Debug, Ord, PartialOrd, Hash, Serialize, Deserialize)]
#[changes]
pub struct GpuDebugGeometryRef(Entity);
#[derive(OneToNComponent, Clone, Copy, Eq, PartialEq, Debug, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub struct GeomToGpuGeomRef(pub Entity);
type GpuGeometryReference = Entity;
#[derive(OneToNComponent)]
#[debug_as_string]
pub struct SubmeshBuffers{
has_changed: bool,
primitive_type: GLenum,
vao_range: VaoRange,
get_vao: Box<dyn Fn(
&Rc<glin::CreationProxy>,
&mut memory::AllocatorsIndex,
Option<&MaterialOffsets>,
&glin::SharedBuffer<(Mat4, Mat4)>) -> glin::Result<&'static mut glin::Vao>>,
get_shadow_vao: Box<dyn Fn(
&Rc<glin::CreationProxy>,
&mut memory::AllocatorsIndex,
&glin::SharedBuffer<Mat4>,
Option<&glin::SharedBuffer<u32>>) -> glin::Result<&'static mut glin::Vao>>,
#[cfg(not(any(feature="gles", feature="webgl")))]
get_vao_range: Box<dyn Fn(
&mut memory::AllocatorsIndex,
&VaoRange) -> VaoRangeInfo>,
#[cfg(any(feature="gles", feature="webgl"))]
get_vao_range: Box<dyn Fn(
&mut memory::AllocatorsIndex,
&VaoRange,
&mut glin::Vao,
GLenum) -> glin::vao::Range<'static>>,
get_command: Box<dyn Fn(
&mut memory::AllocatorsIndex,
&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, unified_material_ubo: bool, allocator_handle: AllocatorHandle<T,B>) ->
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>
+ glin::buffer::WithBackend
+ allocator::MapRange<T>
+ Clone,
allocator::Allocator<B>: allocator::InternalCreation<B> +
allocator::Creation<B> +
allocator::Updater,
Allocator<T,B>: AllocatorFlags,
{
let primitive_type = primitive_type.to_gl();
let vao_id = vao_range.vao_id();
let get_vao = if unified_material_ubo {
let get_vao = move |
gl: &Rc<glin::CreationProxy>,
allocators: &mut memory::AllocatorsIndex,
material_offsets: Option<&MaterialOffsets>,
model_buffer: &glin::SharedBuffer<(Mat4, Mat4)>|
{
let material_offsets = material_offsets.map(|m| m.buffer());
let allocator = &mut allocators[allocator_handle];
unsafe{ mem::transmute(allocator.vao(gl, vao_id, model_buffer, material_offsets)) }
};
Box::new(get_vao) as Box<dyn Fn(
&Rc<glin::CreationProxy>,
&mut memory::AllocatorsIndex,
Option<&MaterialOffsets>,
&glin::SharedBuffer<(Mat4, Mat4)>) -> glin::Result<&'static mut glin::Vao>>
}else{
let get_vao = move |
gl: &Rc<glin::CreationProxy>,
allocators: &mut memory::AllocatorsIndex,
material_offsets: Option<&MaterialOffsets>,
model_buffer: &glin::SharedBuffer<(Mat4, Mat4)>|
{
let allocator = &mut allocators[allocator_handle];
unsafe{ mem::transmute(allocator.vao(gl, vao_id, model_buffer, None)) }
};
Box::new(get_vao)
};
let get_shadow_vao = move |
gl: &Rc<glin::CreationProxy>,
allocators: &mut memory::AllocatorsIndex,
model_buffer: &glin::SharedBuffer<Mat4>,
material_offsets_buffer: Option<&glin::SharedBuffer<u32>>|
{
let allocator = &mut allocators[allocator_handle];
unsafe{ mem::transmute(allocator.shadow_vao(gl, vao_id, model_buffer, material_offsets_buffer)) }
};
let get_command = move |
allocators: &mut memory::AllocatorsIndex,
range: &VaoRange,
base_instance: u32,
instance_count: u32|
{
let allocator = &mut allocators[allocator_handle];
unsafe{ mem::transmute(allocator.command(range, base_instance, instance_count)) }
};
#[cfg(not(any(feature="gles", feature="webgl")))]
let get_vao_range = move |allocators: &mut memory::AllocatorsIndex, range: &VaoRange|{
let allocator = &mut allocators[allocator_handle];
unsafe{ mem::transmute(allocator.vao_range(range)) }
};
#[cfg(any(feature="gles", feature="webgl"))]
let get_vao_range = move |allocators: &mut memory::AllocatorsIndex, range: &VaoRange, vao: &mut glin::Vao, primitive_type: GLenum|{
let allocator = &mut allocators[allocator_handle];
unsafe{ mem::transmute(allocator.vao_range(range, vao, primitive_type)) }
};
SubmeshBuffers{
vao_range,
primitive_type,
has_changed: true,
get_vao,
get_shadow_vao: Box::new(get_shadow_vao),
get_command: Box::new(get_command),
get_vao_range: Box::new(get_vao_range),
}
}
#[cfg(not(any(feature="gles", feature="webgl")))]
pub fn vao_base_instance<C: glin::CreationContext>(&self,
gl: &C,
allocators: &mut memory::AllocatorsIndex,
material_offsets: Option<&MaterialOffsets>,
model_buffer: &glin::SharedBuffer<(Mat4, Mat4)>,
base_instance: u32) -> glin::Result<glin::vao::Range>
{
let vao = (self.get_vao)(gl.creation_proxy(), allocators, material_offsets, model_buffer)?;
let vao_range = (self.get_vao_range)(allocators, &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))
}
}
#[cfg(not(any(feature="gles", feature="webgl")))]
pub fn shadow_vao_base_instance<C: glin::CreationContext>(&self,
gl: &C,
allocators: &mut memory::AllocatorsIndex,
model_buffer: &glin::SharedBuffer<Mat4>,
material_offsets_buffer: Option<&glin::SharedBuffer<u32>>,
base_instance: u32) -> glin::Result<glin::vao::Range>
{
let vao = (self.get_shadow_vao)(
gl.creation_proxy(),
allocators,
model_buffer,
material_offsets_buffer)?;
let vao_range = (self.get_vao_range)(allocators, &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,
allocators: &mut memory::AllocatorsIndex,
base_instance: u32,
instance_count: u32) -> glin::DrawElementsIndirectCommand
{
(self.get_command)(allocators, &self.vao_range, base_instance, instance_count)
}
pub fn full_vao<C: glin::CreationContext>(&self,
gl: &C,
allocators: &mut memory::AllocatorsIndex,
material_offsets: Option<&MaterialOffsets>,
model_buffer: &glin::SharedBuffer<(Mat4, Mat4)>) -> glin::Result<&mut glin::Vao>
{
(self.get_vao)(gl.creation_proxy(), allocators, material_offsets, model_buffer)
}
pub fn full_shadow_vao<C: glin::CreationContext>(&self,
gl: &C,
allocators: &mut memory::AllocatorsIndex,
model_buffer: &glin::SharedBuffer<Mat4>,
material_offsets_buffer: Option<&glin::SharedBuffer<u32>>) -> glin::Result<&mut glin::Vao>
{
(self.get_shadow_vao)(gl.creation_proxy(), allocators, model_buffer, material_offsets_buffer)
}
#[cfg(not(any(feature="gles", feature="webgl")))]
pub fn range_vao<'v>(&self, allocators: &mut memory::AllocatorsIndex, vao: &'v glin::Vao) -> glin::vao::Range<'v>{
let vao_range = (self.get_vao_range)(allocators, &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)
}
}
#[cfg(any(feature="gles", feature="webgl"))]
pub fn range_vao<'v>(&self, allocators: &mut memory::AllocatorsIndex, vao: &'v mut glin::Vao) -> glin::vao::Range<'v>{
(self.get_vao_range)(allocators, &self.vao_range, vao, 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);
#[system(name = "geometryref_changed_updater")]
#[needs(GeometryRef)]
#[writes(GeometryRef)]
#[cfg_attr(feature="debug_geometry", needs("DebugGeometryRef"))]
#[cfg_attr(feature="debug_geometry", writes("DebugGeometryRef"))]
pub fn geometryref_changed_updater(entities: Entities, _: Resources){
entities.update_changed::<GeometryRef>();
#[cfg(feature="debug_geometry")]
entities.update_changed::<DebugGeometryRef>();
}
#[system(name = "geometry_changed_updater")]
#[needs(Geometry<V>)]
#[writes(Geometry<V>)]
pub fn geometry_changed_updater<V>(entities: Entities, _: Resources)
where V: Clone + Send + 'static
{
entities.update_changed::<Geometry<V>>();
}
#[system(name = "update_visible_changed")]
#[needs(Visible)]
#[updates(VisibleChanges)]
#[writes(Visible)]
#[cfg_attr(feature="debug_geometry", needs(DebugGeometryRef))]
#[cfg_attr(feature="debug_geometry", writes(DebugGeometryRef))]
pub fn update_visible_changed(entities: Entities, _: Resources){
entities.update_changed::<Visible>();
#[cfg(feature="debug_geometry")]
entities.update_changed::<DebugGeometryRef>();
}
#[system(name = "update_materialrefs_changed")]
#[needs(MaterialRef, MaterialMultiRef)]
#[writes(MaterialRef, MaterialMultiRef)]
pub fn update_materialrefs_changed(entities: Entities, _: Resources){
entities.update_changed::<MaterialRef>();
entities.update_changed::<MaterialMultiRef>();
}
pub struct GeometryUploader<T, B>{
marker: PhantomData<T>,
marker_buff: PhantomData<B>,
#[cfg(not(any(feature = "webgl", feature = "gles")))]
uses_command_buffers: bool,
unified_material_ubo: bool,
pub(super) allocator_handle: AllocatorHandle<T, 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>>>,
renderer_ty: RendererType,
unified_material_ubo: bool,) -> GeometryUploader<T, B>
{
let allocator = Allocator::<T,B>::new(vertex_bytes, indices_allocator);
let allocator_handle = world
.resource_mut::<memory::AllocatorsIndex>()
.unwrap()
.add_allocator(allocator);
GeometryUploader{
marker: PhantomData,
marker_buff: PhantomData,
#[cfg(not(any(feature = "webgl", feature = "gles")))]
uses_command_buffers: renderer_ty == RendererType::MultiDrawIndirect,
unified_material_ubo,
allocator_handle,
}
}
}
#[cfg(feature="debug_geometry")]
type EntitiesVec = Vec<(Entity, bool)>;
#[cfg(not(feature="debug_geometry"))]
type EntitiesVec = Vec<Entity>;
#[creation_system(name = "geometry uploader")]
#[cfg_attr(feature="debug_geometry", cond(any(
changed("crate::geometry::Geometry<V>"),
changed("crate::geometry::GeometryRef"),
changed("Visible"),
changed("crate::geometry::DebugGeometryRef")
)))]
#[cfg_attr(not(feature="debug_geometry"), cond(any(
changed("crate::geometry::Geometry<V>"),
changed("crate::geometry::GeometryRef"),
changed("Visible"),
)))]
#[after(geometry_changed_updater<V>, geometryref_changed_updater, update_visible_changed)]
#[creates(
GpuGeometryRef,
VertexBuffer,
IndicesBuffer,
GeomToGpuGeomRef,
ShadowGeometry,
)]
impl<V, B> CreationSystem for GeometryUploader<V, B>
where V: VertexFormat + Clone + Copy + Debug + 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>
+ glin::buffer::WithBackend
+ allocator::MapRange<V>
+ Clone,
allocator::Allocator<B>: allocator::InternalCreation<B> +
allocator::Creation<B> +
allocator::Updater,
Allocator<V,B>: AllocatorFlags,
{
fn run(&mut self, mut entities: EntitiesCreation, resources: ResourcesCreation) {
log::trace!("Running geometry creation system");
fn build_geometry<V,B>(
entities: &EntitiesCreation,
allocator: &mut Allocator<V,B>,
allocator_handle: AllocatorHandle<V,B>,
name: Name,
geometryref: GeometryRef,
entities_vec: EntitiesVec,
glin: &gl::Renderer,
dynamic: bool,
unified_material_ubo: 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>
+ glin::buffer::WithBackend
+ allocator::MapRange<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);
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);
log::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 {
log::trace!("Creating {} gpu geometries for {:?}::{:?}, indices: {}, submeshes {}",
vao_ranges.len(), source_path, name, indices.is_some(), vao_ranges.len());
}else{
log::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(),
unified_material_ubo,
allocator_handle
)
}).collect::<Vec<_>>();
let shadow_geom = ShadowGeometry(SubmeshBuffers::new::<V,B>(
full_range.clone(),
geom.primitive_type(),
unified_material_ubo,
allocator_handle
));
AddGpuGeometry::New{
entities_vec,
geometryref,
name,
vertices,
indices,
submesh_buffers,
shadow_geom,
}
}
let glin = resources.get::<gl::Renderer<'static>>().unwrap();
let mut allocators = resources.get_mut::<AllocatorsIndex>().unwrap();
let mut allocator = allocators.get_mut::<V,B>(self.allocator_handle).unwrap();
enum AddGpuGeometry{
Reuse(EntitiesVec, GeometryRef, GpuGeometryReference),
New{
entities_vec: EntitiesVec,
geometryref: GeometryRef,
vertices: BufferRef,
indices: Option<BufferRef>,
submesh_buffers: Vec<SubmeshBuffers>,
shadow_geom: ShadowGeometry,
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_empty() {
None
}else{
Some((*geometryref, (entity, animated_geom.is_some(), false)))
}
});
#[cfg(feature="debug_geometry")]
let to_add_index = to_add_index.chain(entities.iter_for::<(
Entity,
ReadNot<DebugGeometryRef, GpuDebugGeometryRef>,
)>().filter_map(|(entity, geometryref)|{
let geom = entities.component_for::<Geometry<V>>(geometryref.geometry());
if !geometryref.is_visible() || geom.is_none() || geom.unwrap().is_empty() {
None
}else{
Some((*geometryref.geometry(), (entity, false, true)))
}
}));
let to_add_index = to_add_index.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, _debug)| if animated{
Either::Left(entity)
}else{
#[cfg(feature="debug_geometry")]
{
Either::Right((entity, _debug))
}
#[cfg(not(feature="debug_geometry"))]
{
Either::Right(entity)
}
});
let name = entities.component_for::<Name>(&geometryref);
#[cfg(feature = "debug_geometry")]
let non_animated = if !nonanimated.is_empty(){
if let Some((gpugeom, _geom)) = entities.iter_for::<(Read<GpuGeometryRef>, Read<GeometryRef>)>()
.find(|(_, geom)| **geom == geometryref)
{
log::trace!("Reusing gpu geometry");
Some(AddGpuGeometry::Reuse(nonanimated, geometryref, gpugeom.0))
}else if let Some((gpugeom, _geom)) = entities.iter_for::<(Read<GpuDebugGeometryRef>, Read<DebugGeometryRef>)>()
.find(|(_, debug_geom)| *debug_geom.geometry() == geometryref)
{
log::trace!("Reusing debug gpu geometry");
Some(AddGpuGeometry::Reuse(nonanimated, geometryref, gpugeom.0))
}else{
log::trace!("Adding gpu geometry");
Some(build_geometry::<V,B>(
&entities,
&mut allocator,
self.allocator_handle,
name.as_ref()
.map(|n| Name(format!("{} gpu geometry", **n)))
.unwrap_or_else(|| Name("Gpu geometry".to_owned())),
geometryref,
nonanimated,
&glin,
false,
self.unified_material_ubo))
}
}else{
None
};
#[cfg(not(feature = "debug_geometry"))]
let non_animated = if !nonanimated.is_empty(){
if let Some((gpugeom, _geom)) = entities.iter_for::<(Read<GpuGeometryRef>, Read<GeometryRef>)>()
.find(|(_, geom)| **geom == geometryref)
{
log::trace!("Reusing gpu geometry");
Some(AddGpuGeometry::Reuse(nonanimated, geometryref, gpugeom.0))
}else{
Some(build_geometry::<V,B>(
&entities,
&mut allocator,
self.allocator_handle,
name.as_ref()
.map(|n| Name(format!("{} gpu geometry", **n)))
.unwrap_or_else(|| Name("Gpu geometry".to_owned())),
geometryref,
nonanimated,
&glin,
false,
self.unified_material_ubo))
}
}else{
None
};
let animated = animated.into_iter().map(|entity| {
#[cfg(not(feature = "debug_geometry"))]
let entities_vec = vec![entity];
#[cfg(feature = "debug_geometry")]
let entities_vec = vec![(entity, false)];
build_geometry(
&entities,
&mut allocator,
self.allocator_handle,
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,
entities_vec,
&glin,
true,
self.unified_material_ubo)
}).collect::<Vec<_>>();
non_animated.into_iter().chain(animated)
}).collect::<Vec<_>>();
for gpuadd in to_add{
match gpuadd{
#[cfg(feature="debug_geometry")]
AddGpuGeometry::Reuse(entities_vec, geometryref, gpugeom) => {
for (entity, debug) in entities_vec {
if debug {
entities.add_component_to(&entity, GpuDebugGeometryRef(gpugeom));
}else{
entities.add_component_to(&entity, GpuGeometryRef(gpugeom));
}
}
entities.add_component_to(&geometryref, GeomToGpuGeomRef(gpugeom));
}
#[cfg(not(feature="debug_geometry"))]
AddGpuGeometry::Reuse(entities_vec, geometryref, gpugeom) => {
for entity in entities_vec {
entities.add_component_to(&entity, GpuGeometryRef(gpugeom));
}
entities.add_component_to(&geometryref, GeomToGpuGeomRef(gpugeom));
}
AddGpuGeometry::New{
entities_vec,
geometryref,
name,
vertices,
indices,
submesh_buffers,
shadow_geom} =>
{
let gpugeom = entities.new_entity()
.add(name)
.add_slice(submesh_buffers)
.add(shadow_geom)
.add(VertexBuffer(vertices))
.build();
if let Some(indices) = indices {
entities.add_component_to_thread_local(&gpugeom, IndicesBuffer(indices));
}
#[cfg(feature="debug_geometry")]
for (entity, debug) in entities_vec {
if debug {
entities.add_component_to(&entity, GpuDebugGeometryRef(gpugeom));
}else{
entities.add_component_to(&entity, GpuGeometryRef(gpugeom));
}
}
#[cfg(not(feature="debug_geometry"))]
for entity in entities_vec {
entities.add_component_to(&entity, GpuGeometryRef(gpugeom));
}
entities.add_component_to(&geometryref, GeomToGpuGeomRef(gpugeom));
}
}
}
let gl = resources.get::<gl::Renderer>().unwrap();
let mut command_buffers_dirty = false;
let changed = entities.changed_iter_for_mut::<(
Read<Geometry<V>>,
RefN<GeomToGpuGeomRef, Write<VertexBuffer>>,
RefN<GeomToGpuGeomRef, Write<IndicesBuffer>>)>();
for (mesh, mut vertexbuffers, mut indicesbuffers) in changed {
let iter = unsafe{ vertexbuffers.unsafe_iter_mut().zip(indicesbuffers.unsafe_iter_opt_mut()) };
for (vertices, indices) in iter {
command_buffers_dirty |= allocator.vertex_buffer_range_len(&vertices) !=
mesh.len();
allocator.update_vertex_buffer_range(&*gl, &vertices, mesh.vertices()).unwrap();
if let Some(indices) = indices {
command_buffers_dirty |= allocator.index_buffer_range_len(&indices) !=
mesh.indices().len();
allocator.update_index_buffer_range(&*gl, &indices, mesh.indices()).unwrap();
}else if !mesh.indices().is_empty(){
unimplemented!("Trying to update mesh with indices that didn't have originally. Not supported yet")
}
}
}
#[cfg(not(any(feature = "webgl", feature = "gles")))]
if self.uses_command_buffers && command_buffers_dirty {
if let Some(mut commands) = resources.get_mut::<CommandBufferData>(){
commands.dirty = command_buffers_dirty;
}
if let Some(mut shadow_commands) = resources.get_mut::<ShadowsCommandBufferData>(){
shadow_commands.dirty = command_buffers_dirty;
}
if let Some(mut shadow_commands) = resources.get_mut::<StaticShadowsCommandBufferData>(){
shadow_commands.dirty = command_buffers_dirty;
}
if let Some(mut shadow_commands) = resources.get_mut::<AllShadowsCommandBufferData>(){
shadow_commands.dirty = command_buffers_dirty;
}
}
}
}
#[derive(Ord, PartialOrd, PartialEq, Eq)]
#[repr(C)]
struct GeomKey<'a> {
priority: i32,
properties: &'a [glin::Property],
programref: ProgramRef,
textures_hash: u64,
uniforms_hash: u64,
materialref: MaterialRef,
vao_id: VaoId,
geometryref: Entity,
submesh: usize,
}
pub fn reset_geometry_indices(_: Entities, resources: Resources){
resources.get_mut::<resources::OpaqueSortedGeometry>().unwrap().reset_has_changed();
resources.get_mut::<resources::TranslucentSortedGeometry>().unwrap().reset_has_changed();
#[cfg(feature = "debug_geometry")]
resources.get_mut::<resources::DebugSortedGeometry>().unwrap().reset_has_changed();
}
#[system(name = "geometry sort")]
#[needs(
MaterialRef,
MaterialMultiRef,
Visible,
MaterialCache,
GpuGeometryRef,
)]
#[cfg_attr(feature="debug_geometry", needs(DebugGeometryRef, GpuDebugGeometryRef))]
#[after(update_visible_changed, update_materialrefs_changed)]
#[updates(
resources::TranslucentSortedGeometry,
resources::OpaqueSortedGeometry
)]
#[cfg_attr(feature="debug_geometry", updates(resources::DebugSortedGeometry))]
#[cfg_attr(not(feature="debug_geometry"), cond(any(
changed("MaterialRef"),
changed("MaterialMultiRef"),
changed("Visible"),
changed("GpuGeometryRef"),
resource("MaterialCache::<MaterialRef>::any_material_transparency_changed"),
resource("MaterialCache::<MaterialRef>::any_texture_or_uniform_changed"),
)))]
#[cfg_attr(feature="debug_geometry", cond(any(
changed("MaterialRef"),
changed("MaterialMultiRef"),
changed("Visible"),
changed("GpuGeometryRef"),
changed("DebugGeometryRef"),
resource("MaterialCache::<MaterialRef>::any_material_transparency_changed"),
resource("MaterialCache::<MaterialRef>::any_texture_or_uniform_changed"),
)))]
#[else_run("reset_geometry_indices")]
#[writes(Node, SubmeshBuffers)]
#[reads(RenderPlane)]
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();
#[cfg(feature = "debug_geometry")]
let mut debug_geometry = resources.get_mut::<resources::DebugSortedGeometry>().unwrap();
opaque_geometry.clear();
translucent_geometry.clear();
#[cfg(feature = "debug_geometry")]
debug_geometry.clear();
let entities_models = entities.iter_for::<(
Entity,
Read<Node>,
Read<GpuGeometryRef>,
ReadOption<RenderPlane>,
ReadOption<MaterialRef>,
ReadOption<MaterialMultiRef>,
Ref<GpuGeometryRef, Read<SubmeshBuffers>>,
Read<Visible>,)>()
.filter_map(|(e, _, gpu_geom, renderplane, materialref, multimat_ref, submesh_buffers, visible)| {
if visible.is_visible() {
Some((e, gpu_geom.0, renderplane, materialref, multimat_ref, None, submesh_buffers))
}else{
None
}
});
#[cfg(feature="debug_geometry")]
let entities_models = entities_models.chain(entities.iter_for::<(
Entity,
Read<Node>,
Read<GpuDebugGeometryRef>,
Read<DebugGeometryRef>,
Ref<GpuDebugGeometryRef, Read<SubmeshBuffers>>,
)>().filter_map(|(e, _, gpu_geom, debug_geom, submesh_buffers)|{
if debug_geom.is_visible() && materials_cache[*debug_geom.material()].visible {
Some((e, gpu_geom.0, None, None, None, Some(*debug_geom.material()), submesh_buffers))
}else{
None
}
}));
let mut add_submesh_material = |
entity: Entity,
geometryref: Entity,
is_debug: bool,
renderplane: Option<SliceView<RenderPlane>>,
submesh_buffers: SliceView<SubmeshBuffers>,
submesh: usize,
materialref: &MaterialRef,
|{
let material = &materials_cache[*materialref];
if !material.visible{
return;
}
let vao_id = submesh_buffers[submesh].vao_range().vao_id();
let geomkey = GeomKey{
priority: material.priority,
materialref: *materialref,
programref: material.programref,
properties: &material.properties,
textures_hash: material.textures_hash,
uniforms_hash: material.uniforms_hash,
geometryref,
submesh,
vao_id,
};
if !material.translucent && !is_debug {
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,
uniforms_hash: material.uniforms_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],
geometryref,
materialref: *materialref,
submesh,
vao_id,
renderplane: renderplane.map(|_| entity),
}),
}
}else if !is_debug{
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,
uniforms_hash: material.uniforms_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],
materialref: *materialref,
geometryref,
submesh,
vao_id,
renderplane: renderplane.map(|_| entity),
}),
}
}else{
#[cfg(feature = "debug_geometry")]
{
let insert_pos = debug_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,
uniforms_hash: material.uniforms_hash,
geometryref: geom.geometryref,
submesh: geom.submesh,
vao_id: geom.vao_id,
}
});
match insert_pos {
Ok(insert_pos) => debug_geometry[insert_pos].entities.push(entity),
Err(insert_pos) => debug_geometry.insert(insert_pos,
resources::GeometryIndex{
entities: vec![entity],
geometryref,
materialref: *materialref,
submesh,
vao_id,
renderplane: renderplane.map(|_| entity),
}),
}
}
}
};
for (entity, geometryref, renderplane, materials, multi_materials, one_material, submesh_buffers) in entities_models {
if let Some(materials) = materials {
for (submesh, materialref) in materials.iter().enumerate() {
add_submesh_material(
entity,
geometryref,
false,
renderplane,
submesh_buffers,
submesh,
materialref)
}
}else if let Some(multi_materials) = multi_materials {
for (submesh, material_multiref) in multi_materials.iter().enumerate() {
for materialref in material_multiref.iter() {
add_submesh_material(
entity,
geometryref,
false,
renderplane,
submesh_buffers,
submesh,
materialref)
}
}
}else if let Some(materialref) = one_material {
let submesh = 0;
add_submesh_material(
entity,
geometryref,
true,
renderplane,
submesh_buffers,
submesh,
&materialref)
}
}
}
#[derive(Default, Clone)]
pub struct Segment{
pub start: usize,
pub end: usize,
pub vao_id: u32,
pub primitive_type: u32,
pub elements: bool,
}
type SubmeshIndex = usize;
type Posiiton = usize;
pub struct CommandBufferData{
pub data: Vec<glin::DrawElementsIndirectCommand>,
pub index: HashMap<(GpuGeometryReference, SubmeshIndex), Posiiton>,
pub segments_per_renderplane: HashMap<Option<Entity>, Vec<Segment>>,
pub translucent_starts: HashMap<Option<Entity>, Posiiton>,
pub debug_starts: HashMap<Option<Entity>, Posiiton>,
pub dirty: bool,
pub changed: bool,
}
impl Default for CommandBufferData{
fn default() -> CommandBufferData{
CommandBufferData{
data: Vec::default(),
index: HashMap::default(),
segments_per_renderplane: HashMap::default(),
translucent_starts: HashMap::default(),
debug_starts: HashMap::default(),
dirty: true,
changed: false,
}
}
}
#[system_thread_local(name = "command buffer data")]
#[needs(
resources::OpaqueSortedGeometry,
resources::TranslucentSortedGeometry,
resources::ModelMatricesBuffer,
MaterialCache,
SubmeshBuffers,
)]
#[cfg_attr(feature="debug_geometry", needs(resources::DebugSortedGeometry))]
#[updates(CommandBufferData)]
#[reads(Visible, RenderPlane, gl::Renderer, MaterialOffsets)]
#[writes(AllocatorsIndex)]
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();
#[cfg(feature = "debug_geometry")]
let debug_geometry = resources.get::<resources::DebugSortedGeometry>().unwrap();
let mut commands = resources.get_mut::<CommandBufferData>().unwrap();
let materials_cache = resources.get::<MaterialCache>().unwrap();
let willrun = commands.dirty
|| opaque_geometry.has_changed()
|| translucent_geometry.has_changed()
|| materials_cache.any_texture_or_uniform_changed();
#[cfg(feature = "debug_geometry")]
let willrun = willrun || debug_geometry.has_changed();
if !willrun {
return;
}
commands.data.clear();
commands.index.clear();
commands.segments_per_renderplane.entry(None).or_insert_with(|| vec![]).clear();
commands.translucent_starts.clear();
commands.debug_starts.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_data = Segment::default();
let mut prev_vao_id = None;
let mut prev_program_ref = None;
let mut prev_uniforms = 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;
let mut translucent_start = None;
let mut debug_start = None;
let geometries = opaque_geometry.iter().chain(translucent_geometry.iter());
#[cfg(feature = "debug_geometry")]
let geometries = geometries.chain(debug_geometry.iter());
let gl = resources.get::<gl::Renderer>().unwrap();
let model_data_gpu = resources.get::<resources::ModelMatricesBuffer>().unwrap();
let mut allocators = resources.get_mut::<AllocatorsIndex>().unwrap();
let material_offsets = resources.get::<MaterialOffsets>();
for (segment, geom_index) in geometries.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 = &materials_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))
|| (prev_uniforms.is_some() && prev_uniforms != Some(material.uniforms_hash))
|| segment == opaque_geometry.len() + translucent_geometry.len()
{
prev_vao_data.start = segment_start;
prev_vao_data.end = segment;
commands.segments_per_renderplane.get_mut(&None)
.unwrap()
.push(prev_vao_data.clone());
segment_start = segment;
if segment == opaque_geometry.len(){
translucent_start = Some(commands.segments_per_renderplane[&None].len())
}
if segment == opaque_geometry.len() + translucent_geometry.len() {
debug_start = Some(commands.segments_per_renderplane[&None].len())
}
}
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);
prev_uniforms = Some(material.uniforms_hash);
prev_vao_data.vao_id = submesh
.full_vao(&*gl, &mut allocators, material_offsets.as_deref(), &model_data_gpu)
.unwrap()
.id();
prev_vao_data.primitive_type = submesh.primitive_type();
prev_vao_data.elements = submesh.has_indices();
let pos = commands.data.len();
commands.index.insert((geom_index.geometryref, geom_index.submesh), pos);
commands.data.push(submesh.command(&mut allocators, base_instance, num_instances as u32));
}
base_instance += num_instances as u32;
}
let last_segment = opaque_geometry.len() + translucent_geometry.len();
#[cfg(feature="debug_geometry")]
let last_segment = last_segment + debug_geometry.len();
if last_segment > segment_start{
prev_vao_data.start = segment_start;
prev_vao_data.end = last_segment;
commands.segments_per_renderplane.get_mut(&None)
.unwrap()
.push(prev_vao_data);
}
if let Some(translucent_start) = translucent_start{
commands.translucent_starts.insert(None, translucent_start);
}
if let Some(debug_start) = debug_start{
commands.debug_starts.insert(None, debug_start);
}
fn fill_renderplane_segments<'a, I: Iterator<Item = &'a GeometryIndex>>(
geometries: I,
renderplane: Entity,
entities: &EntitiesThreadLocal,
allocators: &mut memory::AllocatorsIndex,
material_offsets: Option<&MaterialOffsets>,
gl: &gl::Renderer,
model_data_gpu: &resources::ModelMatricesBuffer,
material_cache: &MaterialCache,
commands: &mut CommandBufferData,
opaque_len: usize,
translucent_len: usize,
debug_len: usize)
{
let mut prev_vao_data = Segment::default();
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_uniforms = None;
let mut prev_properties = None;
let mut segment_start = 0;
let mut translucent_start = None;
let mut debug_start = None;
for (segment, geom_index) in geometries.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 {
prev_vao_data.start = segment_start;
prev_vao_data.end = segment;
commands.segments_per_renderplane.get_mut(&Some(renderplane))
.unwrap()
.push(prev_vao_data.clone());
}
segment_start = segment + 1;
continue;
}
if (segment_start != segment) && (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_uniforms.is_some() && prev_uniforms != Some(material.uniforms_hash))
|| (prev_properties.is_some() && prev_properties != Some(&material.properties)))
{
prev_vao_data.start = segment_start;
prev_vao_data.end = segment;
commands.segments_per_renderplane.get_mut(&Some(renderplane))
.unwrap()
.push(prev_vao_data.clone());
segment_start = segment;
if segment == opaque_len{
translucent_start = Some(commands.segments_per_renderplane[&Some(renderplane)].len())
}
if segment == opaque_len + translucent_len {
debug_start = Some(commands.segments_per_renderplane[&Some(renderplane)].len())
}
}
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_uniforms = Some(material.uniforms_hash);
prev_properties = Some(&material.properties);
prev_vao_data.vao_id = submesh
.full_vao(gl, allocators, material_offsets, model_data_gpu)
.unwrap()
.id();
prev_vao_data.primitive_type = submesh.primitive_type();
prev_vao_data.elements = submesh.has_indices();
}
}
let last_segment = opaque_len + translucent_len + debug_len;
if last_segment > segment_start{
prev_vao_data.start = segment_start;
prev_vao_data.end = last_segment;
commands.segments_per_renderplane.get_mut(&Some(renderplane))
.unwrap()
.push(prev_vao_data);
}
if let Some(translucent_start) = translucent_start{
commands.translucent_starts.insert(Some(renderplane), translucent_start);
}
if let Some(debug_start) = debug_start{
commands.debug_starts.insert(Some(renderplane), debug_start);
}
}
let opaque_len = opaque_geometry.len();
let translucent_len = translucent_geometry.len();
#[cfg(feature="debug_geometry")]
let debug_len = debug_geometry.len();
#[cfg(not(feature="debug_geometry"))]
let debug_len = 0;
for (renderplane_entity, _, renderplane) in entities.iter_for::<(
Entity,
Read<Visible>,
Read<RenderPlane>)>().filter(|(_,v,_)| v.is_visible())
{
if renderplane[0].draw_translucent() && renderplane[0].draw_debug(){
let geometries = opaque_geometry.iter().chain(translucent_geometry.iter());
#[cfg(feature = "debug_geometry")]
let geometries = geometries.chain(debug_geometry.iter());
fill_renderplane_segments(
geometries,
renderplane_entity,
&entities,
&mut allocators,
material_offsets.as_deref(),
&gl,
&model_data_gpu,
&materials_cache,
&mut commands,
opaque_len,
translucent_len,
debug_len);
}else if renderplane[0].draw_translucent(){
let geometries = opaque_geometry.iter().chain(translucent_geometry.iter());
fill_renderplane_segments(
geometries,
renderplane_entity,
&entities,
&mut allocators,
material_offsets.as_deref(),
&gl,
&model_data_gpu,
&materials_cache,
&mut commands,
opaque_len,
translucent_len,
debug_len);
}else{
let geometries = opaque_geometry.iter();
fill_renderplane_segments(
geometries,
renderplane_entity,
&entities,
&mut allocators,
material_offsets.as_deref(),
&gl,
&model_data_gpu,
&materials_cache,
&mut commands,
opaque_len,
translucent_len,
debug_len);
}
}
commands.changed = true;
commands.dirty = false;
}
pub struct CommandBuffer(pub glin::Buffer<glin::DrawElementsIndirectCommand>);
#[system_thread_local(name = "upload command buffer")]
#[needs(CommandBufferData)]
#[updates(CommandBuffer)]
#[writes(CommandBufferData)]
#[gpu_stats]
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;
}
}
#[repr(C)]
#[derive(PartialOrd, PartialEq, Ord, Eq)]
struct ShadowGeomKey<'a>{
priority: Option<i32>,
properties: Option<&'a [glin::Property]>,
programref: Option<ProgramRef>,
textures_hash: Option<u64>,
uniforms_hash: Option<u64>,
materialref: Option<ShadowMaterialRef>,
vao_id: VaoId,
primitive_type: GLenum,
geometryref: GpuGeometryRef,
submesh: Option<usize>,
}
#[repr(C)]
#[derive(PartialOrd, PartialEq, Ord, Eq)]
struct ShadowDistanceSortGeomKey{
vao_id: VaoId,
primitive_type: GLenum,
}
#[derive(Default)]
pub struct ShadowsCommandBufferData{
pub data: Vec<glin::DrawElementsIndirectCommand>,
pub index: HashMap<GpuGeometryRef, usize>,
pub segments: Vec<Segment>,
pub changed: bool,
pub dirty: bool,
}
impl ShadowsCommandBufferData{
fn parts_mut(&mut self) -> (&mut Vec<glin::DrawElementsIndirectCommand>, &mut HashMap<GpuGeometryRef, usize>, &mut Vec<Segment>){
(&mut self.data, &mut self.index, &mut self.segments)
}
}
fn add_shadow_geometry(
geometry_sorted: &mut Vec<ShadowGeometryIndex>,
shadow_materials_cache: &ShadowMaterialCache,
entity: Entity,
geometryref: GpuGeometryRef,
geom: &SubmeshBuffers,
shadowmaterial: Option<(usize, ShadowMaterialRef)>)
{
let material = shadowmaterial.map(|(_submesh, m)| &shadow_materials_cache[m]);
let geomkey = ShadowGeomKey {
priority: material.map(|m| m.priority),
materialref: shadowmaterial.map(|(_, materialref)| materialref),
programref: material.map(|m| m.programref),
properties: material.map(|m| m.properties.as_slice()),
textures_hash: material.map(|m| m.textures_hash),
uniforms_hash: material.map(|m| m.uniforms_hash),
vao_id: geom.vao_range().vao_id(),
primitive_type: geom.primitive_type(),
geometryref: geometryref,
submesh: shadowmaterial.map(|(submesh,_)| submesh),
};
let insert_pos = geometry_sorted.binary_search_by_key(&geomkey, |geom| {
let material = geom.materialref.map(|m| &shadow_materials_cache[m]);
ShadowGeomKey{
priority: material.map(|m| m.priority),
materialref: shadowmaterial.map(|(_, materialref)| materialref),
programref: material.map(|m| m.programref),
properties: material.map(|m| m.properties.as_slice()),
textures_hash: material.map(|m| m.textures_hash),
uniforms_hash: material.map(|m| m.uniforms_hash),
vao_id: geom.vao_id,
primitive_type: geom.primitive_type,
geometryref: geom.geometryref,
submesh: geom.submesh,
}
});
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(),
materialref: shadowmaterial.map(|(_, materialref)| materialref),
submesh: shadowmaterial.map(|(submesh,_)| submesh),
}),
}
}
#[system(name = "geometry sort dynamic")]
#[needs(
Visible,
MaterialRef,
MaterialMultiRef,
ShadowMaterialRef,
MaterialCache,
ShadowMaterialCache,
GpuGeometryRef,
)]
#[after(update_visible_changed, update_materialrefs_changed)]
#[updates(resources::DynamicShadowsSortedGeometry)]
#[cond(all(
components(has("shadow::Map"), has("shadow::StaticMap")),
any(
changed("Visible"),
changed("MaterialRef"),
changed("MaterialMultiRef"),
changed("ShadowMaterialRef"),
resource("MaterialCache::<MaterialRef>::any_material_transparency_changed"),
resource("ShadowMaterialCache::any_material_transparency_changed"),
resource("ShadowMaterialCache::any_texture_or_uniform_changed"),
resource("resources::DynamicShadowsSortedGeometry::is_empty")
)
))]
#[reads(ShadowGeometry)]
pub fn dynamic_shadows_geometry_sort(entities: Entities, resources: Resources){
let mut geometry_sorted = resources.get_mut::<resources::DynamicShadowsSortedGeometry>()
.unwrap();
let materials_cache = resources.get::<MaterialCache>().unwrap();
let shadow_materials_cache = resources.get::<ShadowMaterialCache>().unwrap();
let any_visible_changed =
entities.changed_iter_for::<(Read<Visible>, Has<DynamicTransformation>)>()
.next().is_some();
let materialref_changed =
entities.changed_iter_for::<(Read<MaterialRef>, Has<DynamicTransformation>)>()
.next().is_some();
let materialmultiref_changed =
entities.changed_iter_for::<(Read<MaterialMultiRef>, Has<DynamicTransformation>)>()
.next().is_some();
if !geometry_sorted.is_empty()
&& !materials_cache.any_material_transparency_changed()
&& !shadow_materials_cache.any_material_transparency_changed()
&& !shadow_materials_cache.any_texture_or_uniform_changed()
&& !any_visible_changed
&& !materialref_changed
&& !materialmultiref_changed
{
return;
}
geometry_sorted.clear();
let entities_models = entities.iter_for::<(
Entity,
Has<DynamicTransformation>,
Read<GpuGeometryRef>,
ReadRef<GpuGeometryRef, ShadowGeometry>,
ReadOption<MaterialRef>,
ReadOption<MaterialMultiRef>,
ReadOption<ShadowMaterialRef>,
Read<Visible>)>().filter(|(_,_,_,_, materials, multimaterials, shadowmaterials, visible)| {
visible.is_visible() && if let Some(shadowmaterials) = shadowmaterials{
shadowmaterials.iter()
.any(|m| shadow_materials_cache[*m].visible)
}else if let Some(materials) = materials {
materials.iter()
.any(|m| !materials_cache[*m].translucent && materials_cache[*m].visible)
}else if let Some(multimaterials) = multimaterials {
multimaterials.iter()
.any(|materials| materials.iter()
.any(|m| !materials_cache[*m].translucent && materials_cache[*m].visible))
}else{
false
}
});
for (entity, _, geometryref, geom, _, _, shadowmaterials, _) in entities_models {
if let Some(shadowmaterials) = shadowmaterials {
for (submesh, shadowmaterial) in shadowmaterials.iter().enumerate() {
add_shadow_geometry(
&mut geometry_sorted,
&shadow_materials_cache,
entity,
*geometryref,
geom,
Some((submesh, *shadowmaterial)))
}
}else{
add_shadow_geometry(
&mut geometry_sorted,
&shadow_materials_cache,
entity,
*geometryref,
geom,
None)
}
}
}
#[system_thread_local(name = "shadows command buffer data")]
#[needs(
resources::DynamicShadowsSortedGeometry,
resources::DynamicModelMatricesBuffer,
ShadowMaterialCache,
ShadowGeometry,
ShadowMaterialOffsets
)]
#[updates(ShadowsCommandBufferData)]
#[writes(AllocatorsIndex)]
#[reads(gl::Renderer)]
pub fn update_shadows_command_buffer_data(entities: EntitiesThreadLocal, resources: ResourcesThreadLocal) {
let geometry_sorted = resources.get::<resources::DynamicShadowsSortedGeometry>()
.unwrap();
let mut commands = resources.get_mut::<ShadowsCommandBufferData>().unwrap();
if !commands.dirty && !geometry_sorted.has_changed() {
return
}
commands.data.clear();
commands.segments.clear();
commands.index.clear();
let (data, index, segments) = commands.parts_mut();
let mut iter = entities
.iter_for_entities::<Read<ShadowGeometry>, _>(geometry_sorted.geometryrefs().map(|g| &**g))
.zip(geometry_sorted.iter())
.enumerate();
if let Some((segment, (geom, geom_index))) = iter.next() {
let mut allocators = resources.get_mut::<AllocatorsIndex>().unwrap();
let gl = resources.get::<gl::Renderer>().unwrap();
let shadow_materials_cache = resources.get::<ShadowMaterialCache>().unwrap();
let model_data_gpu = resources.get::<resources::DynamicModelMatricesBuffer>().unwrap();
let material_offsets = resources.get::<ShadowMaterialOffsets>();
let material_offsets = material_offsets
.as_ref()
.and_then(|offsets| offsets.dynamic_buffer());
let mut base_instance = 0;
let num_instances = geom_index.num_instances() as u32;
let material = geom_index.materialref
.map(|m| &shadow_materials_cache[m]);
let mut segment_start = segment;
let mut prev_vao_id = geom_index.vao_id;
let mut prev_primitive_type = geom.primitive_type();
let mut prev_program_ref = material.map(|m| m.programref);
let mut prev_materialref = geom_index.materialref;
let mut prev_textures = material.map(|m| m.textures_hash);
let mut prev_properties = material.map(|m| &m.properties);
let mut prev_uniforms = material.map(|m| m.uniforms_hash);
let mut prev_elements = geom.has_indices();
let mut prev_vao_gl_id = geom
.full_shadow_vao(&*gl, &mut allocators, &model_data_gpu, material_offsets)
.unwrap()
.id();
let command = geom.command(&mut allocators, base_instance, num_instances);
let pos = data.len();
index.insert(geom_index.geometryref, pos);
data.push(command);
base_instance += num_instances;
for (segment, (geom, geom_index)) in iter {
let num_instances = geom_index.num_instances() as u32;
let material = geom_index.materialref
.map(|m| &shadow_materials_cache[m]);
if prev_program_ref != material.map(|m| m.programref)
|| prev_vao_id != geom_index.vao_id
|| prev_primitive_type != geom.primitive_type()
|| prev_textures != material.map(|m| m.textures_hash)
|| prev_properties != material.map(|m| &m.properties)
|| prev_uniforms != material.map(|m| m.uniforms_hash)
|| prev_materialref != geom_index.materialref
|| segment == geometry_sorted.len()
{
segments.push(Segment {
start: segment_start,
end: segment,
vao_id: prev_vao_gl_id,
primitive_type: prev_primitive_type,
elements: prev_elements,
});
segment_start = segment;
prev_vao_id = geom_index.vao_id;
prev_primitive_type = geom.primitive_type();
prev_program_ref = material.map(|m| m.programref);
prev_materialref = geom_index.materialref;
prev_textures = material.map(|m| m.textures_hash);
prev_properties = material.map(|m| &m.properties);
prev_uniforms = material.map(|m| m.uniforms_hash);
prev_elements = geom.has_indices();
prev_vao_gl_id = geom
.full_shadow_vao(&*gl, &mut allocators, &model_data_gpu, material_offsets)
.unwrap()
.id();
}
let command = geom.command(&mut allocators, base_instance, num_instances);
let pos = data.len();
index.insert(geom_index.geometryref, pos);
data.push(command);
base_instance += num_instances;
}
segments.push(Segment {
start: segment_start,
end: geometry_sorted.len(),
vao_id: prev_vao_gl_id,
primitive_type: prev_primitive_type,
elements: prev_elements,
});
}
commands.changed = true;
commands.dirty = false;
}
pub struct ShadowsCommandBuffer(pub glin::Buffer<glin::DrawElementsIndirectCommand>);
#[system_thread_local(name = "upload shadows command buffer")]
#[needs(ShadowsCommandBufferData)]
#[updates(ShadowsCommandBuffer)]
#[writes(ShadowsCommandBufferData)]
#[gpu_stats]
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<Segment>,
pub dirty: bool,
}
impl StaticShadowsCommandBufferData{
fn data_and_segments(&mut self) -> (&mut Vec<glin::DrawElementsIndirectCommand>, &mut Vec<Segment>){
(&mut self.data, &mut self.segments)
}
}
#[system(name = "geometry sort static")]
#[needs(
Visible,
MaterialRef,
MaterialMultiRef,
ShadowMaterialRef,
MaterialCache,
ShadowMaterialCache,
GpuGeometryRef,
ShadowGeometry,
)]
#[after(update_visible_changed, update_materialrefs_changed)]
#[updates(resources::StaticShadowsSortedGeometry)]
#[cond(all(
components(has("shadow::StaticMap")),
any(
changed("Visible"),
changed("MaterialRef"),
changed("MaterialMultiRef"),
changed("ShadowMaterialRef"),
resource("MaterialCache::<MaterialRef>::any_material_transparency_changed"),
resource("ShadowMaterialCache::any_material_transparency_changed"),
resource("ShadowMaterialCache::any_texture_or_uniform_changed"),
resource("resources::StaticShadowsSortedGeometry::is_empty")
)
))]
#[writes(Node, AllocatorsIndex)]
pub fn static_shadows_geometry_sort(entities: Entities, resources: Resources){
let mut geometry_sorted = resources.get_mut::<resources::StaticShadowsSortedGeometry>()
.unwrap();
let materials_cache = resources.get::<MaterialCache>().unwrap();
let shadow_materials_cache = resources.get::<ShadowMaterialCache>().unwrap();
let any_visible_changed =
entities.changed_iter_for::<ReadNot<Visible, DynamicTransformation>>()
.next()
.is_some();
let materialref_changed =
entities.changed_iter_for::<ReadNot<MaterialRef, DynamicTransformation>>()
.next()
.is_some();
let materialmultiref_changed =
entities.changed_iter_for::<ReadNot<MaterialMultiRef, DynamicTransformation>>()
.next()
.is_some();
if !geometry_sorted.is_empty()
&& !materials_cache.any_material_transparency_changed()
&& !shadow_materials_cache.any_material_transparency_changed()
&& !shadow_materials_cache.any_texture_or_uniform_changed()
&& !any_visible_changed
&& !materialref_changed
&& !materialmultiref_changed
{
return;
}
log::trace!("Sorting static shadows index");
geometry_sorted.clear();
let entities_models = entities.iter_for::<(
Entity,
Read<Node>,
ReadNot<GpuGeometryRef, DynamicTransformation>,
ReadRef<GpuGeometryRef, ShadowGeometry>,
ReadOption<MaterialRef>,
ReadOption<MaterialMultiRef>,
ReadOption<ShadowMaterialRef>,
Read<Visible>)>().filter(|(_,_,_,_, materials, multimaterials, shadowmaterials, visible)| {
visible.is_visible() && if let Some(shadowmaterials) = shadowmaterials{
shadowmaterials.iter()
.any(|m| shadow_materials_cache[*m].visible)
}else if let Some(materials) = materials {
materials.iter()
.any(|m| !materials_cache[*m].translucent && materials_cache[*m].visible)
}else if let Some(multimaterials) = multimaterials {
multimaterials.iter()
.any(|materials| materials.iter()
.any(|m| !materials_cache[*m].translucent && materials_cache[*m].visible))
}else{
false
}
});
for (entity, _, geometryref, geom, _, _, shadowmaterials, _) in entities_models {
if let Some(shadowmaterials) = shadowmaterials {
for (submesh, shadowmaterial) in shadowmaterials.iter().enumerate() {
add_shadow_geometry(
&mut geometry_sorted,
&shadow_materials_cache,
entity,
*geometryref,
geom,
Some((submesh, *shadowmaterial)))
}
}else{
add_shadow_geometry(
&mut geometry_sorted,
&shadow_materials_cache,
entity,
*geometryref,
geom,
None)
}
}
}
#[system_thread_local(name = "static shadows command buffer data")]
#[needs(
resources::StaticShadowsSortedGeometry,
resources::StaticModelMatricesBuffer,
ShadowGeometry,
ShadowMaterialCache,
ShadowMaterialOffsets
)]
#[updates(StaticShadowsCommandBufferData)]
#[writes(AllocatorsIndex)]
#[reads(gl::Renderer)]
pub fn update_static_shadows_command_buffer_data(entities: EntitiesThreadLocal, resources: ResourcesThreadLocal) {
let geometry_sorted = resources.get::<resources::StaticShadowsSortedGeometry>()
.unwrap();
let mut commands = resources.get_mut::<StaticShadowsCommandBufferData>().unwrap();
if !commands.dirty && !geometry_sorted.has_changed() {
return
}
commands.data.clear();
commands.segments.clear();
let (data, segments) = commands.data_and_segments();
let mut iter = entities
.iter_for_entities::<Read<ShadowGeometry>, _>(geometry_sorted.geometryrefs().map(|g| &**g))
.zip(geometry_sorted.iter())
.enumerate();
if let Some((segment, (geom, geom_index))) = iter.next() {
let gl = resources.get::<gl::Renderer>().unwrap();
let shadow_materials_cache = resources.get::<ShadowMaterialCache>().unwrap();
let model_data_gpu = resources.get::<resources::StaticModelMatricesBuffer>().unwrap();
let mut allocators = resources.get_mut::<AllocatorsIndex>().unwrap();
let material_offsets = resources.get::<ShadowMaterialOffsets>();
let material_offsets = material_offsets
.as_ref()
.and_then(|offsets| offsets.static_buffer());
let mut base_instance = 0;
let num_instances = geom_index.num_instances() as u32;
let material = geom_index.materialref
.map(|m| &shadow_materials_cache[m]);
let mut segment_start = segment;
let mut prev_vao_id = geom_index.vao_id;
let mut prev_primitive_type = geom.primitive_type();
let mut prev_program_ref = material.map(|m| m.programref);
let mut prev_materialref = geom_index.materialref;
let mut prev_textures = material.map(|m| m.textures_hash);
let mut prev_properties = material.map(|m| &m.properties);
let mut prev_uniforms = material.map(|m| m.uniforms_hash);
let mut prev_elements = geom.has_indices();
let mut prev_vao_gl_id = geom
.full_shadow_vao(&*gl, &mut allocators, &model_data_gpu, material_offsets)
.unwrap()
.id();
let command = geom.command(&mut allocators, base_instance, num_instances);
data.push(command);
base_instance += num_instances;
let new_commands = iter
.map(|(segment, (geom, geom_index))| {
let num_instances = geom_index.num_instances() as u32;
let material = geom_index.materialref.map(|m| &shadow_materials_cache[m]);
if prev_program_ref != material.map(|m| m.programref)
|| prev_vao_id != geom_index.vao_id
|| prev_primitive_type != geom.primitive_type()
|| prev_textures != material.map(|m| m.textures_hash)
|| prev_properties != material.map(|m| &m.properties)
|| prev_uniforms != material.map(|m| m.uniforms_hash)
|| prev_materialref != geom_index.materialref
|| segment == geometry_sorted.len()
{
segments.push(Segment {
start: segment_start,
end: segment,
vao_id: prev_vao_gl_id,
primitive_type: prev_primitive_type,
elements: prev_elements,
});
segment_start = segment;
prev_vao_id = geom_index.vao_id;
prev_primitive_type = geom.primitive_type();
prev_program_ref = material.map(|m| m.programref);
prev_materialref = geom_index.materialref;
prev_textures = material.map(|m| m.textures_hash);
prev_properties = material.map(|m| &m.properties);
prev_uniforms = material.map(|m| m.uniforms_hash);
prev_elements = geom.has_indices();
prev_vao_gl_id = geom
.full_shadow_vao(&*gl, &mut allocators, &model_data_gpu, material_offsets)
.unwrap()
.id();
}
let command = geom.command(&mut allocators, base_instance, num_instances);
base_instance += num_instances;
command
});
data.extend(new_commands);
segments.push(Segment {
start: segment_start,
end: geometry_sorted.len(),
vao_id: prev_vao_gl_id,
primitive_type: prev_primitive_type,
elements: prev_elements,
});
}
commands.dirty = false;
}
pub struct StaticShadowsCommandBuffer(pub glin::Buffer<glin::DrawElementsIndirectCommand>);
#[system_thread_local(name = "upload static shadows command buffer")]
#[needs(StaticShadowsCommandBufferData)]
#[updates(StaticShadowsCommandBuffer)]
#[cond(resource("resources::StaticShadowsSortedGeometry::has_changed"))]
#[gpu_stats]
pub fn upload_static_shadows_command_buffer(_: EntitiesThreadLocal, resources: ResourcesThreadLocal){
log::trace!("Regenerating static shadows command buffer");
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<Segment>,
pub changed: bool,
pub dirty: bool,
}
impl AllShadowsCommandBufferData{
fn parts_mut(&mut self) -> (&mut Vec<glin::DrawElementsIndirectCommand>, &mut HashMap<GpuGeometryRef, usize>, &mut Vec<Segment>){
(&mut self.data, &mut self.index_per_range_start, &mut self.segments)
}
}
#[system(name = "geometry sort all")]
#[needs(
"Visible",
"MaterialRef",
"MaterialMultiRef",
"ShadowMaterialRef",
"MaterialCache",
"ShadowMaterialCache",
"GpuGeometryRef",
"ShadowGeometry"
)]
#[after("update_visible_changed", "update_materialrefs_changed")]
#[updates("resources::AllShadowsSortedGeometry")]
#[cond(all(
any(
components(has_not("shadow::Map", "shadow::StaticMap")),
has_resource("resources::DepthPrepass")
),
any(
changed("Visible"),
changed("MaterialRef"),
changed("ShadowMaterialRef"),
changed("MaterialMultiRef"),
resource("MaterialCache::<MaterialRef>::any_material_transparency_changed"),
resource("ShadowMaterialCache::any_material_transparency_changed"),
resource("resources::AllShadowsSortedGeometry::is_empty"),
)
))]
#[reads("Node")]
pub fn all_shadows_geometry_sort(entities: Entities, resources: Resources){
let materials_cache = resources.get::<MaterialCache>().unwrap();
let shadow_materials_cache = resources.get::<ShadowMaterialCache>().unwrap();
let mut geometry_sorted = resources.get_mut::<resources::AllShadowsSortedGeometry>().unwrap();
geometry_sorted.clear();
let entities_models = entities.iter_for::<(
Entity,
Read<Node>,
Read<GpuGeometryRef>,
ReadRef<GpuGeometryRef, ShadowGeometry>,
ReadOption<MaterialRef>,
ReadOption<MaterialMultiRef>,
ReadOption<ShadowMaterialRef>,
Read<Visible>)>().filter(|(_,_,_,_, materials, multimaterials, shadowmaterials, visible)| {
visible.is_visible() && if let Some(shadowmaterials) = shadowmaterials{
shadowmaterials.iter()
.any(|m| shadow_materials_cache[*m].visible)
}else if let Some(materials) = materials {
materials.iter()
.any(|m| !materials_cache[*m].translucent && materials_cache[*m].visible)
}else if let Some(multimaterials) = multimaterials {
multimaterials.iter()
.any(|materials| materials.iter()
.any(|m| !materials_cache[*m].translucent && materials_cache[*m].visible))
}else{
false
}
});
for (entity, _, geometryref, geom, _, _, shadowmaterials, _) in entities_models {
if let Some(shadowmaterials) = shadowmaterials {
for (submesh, shadowmaterial) in shadowmaterials.iter().enumerate() {
add_shadow_geometry(
&mut geometry_sorted,
&shadow_materials_cache,
entity,
*geometryref,
geom,
Some((submesh, *shadowmaterial)))
}
}else{
add_shadow_geometry(
&mut geometry_sorted,
&shadow_materials_cache,
entity,
*geometryref,
geom,
None)
}
}
}
#[system_thread_local(name = "all shadows command buffer data")]
#[needs(
resources::AllShadowsSortedGeometry,
resources::AllModelMatricesBuffer,
ShadowMaterialCache,
ShadowGeometry,
ShadowMaterialOffsets
)]
#[updates(AllShadowsCommandBufferData)]
#[reads(gl::Renderer)]
#[writes(AllocatorsIndex)]
pub fn update_all_shadows_command_buffer_data(entities: EntitiesThreadLocal, resources: ResourcesThreadLocal) {
let mut commands = resources.get_mut::<AllShadowsCommandBufferData>().unwrap();
let geometry_sorted = resources.get::<resources::AllShadowsSortedGeometry>().unwrap();
if !commands.dirty && !geometry_sorted.has_changed() {
return
}
commands.data.clear();
commands.index_per_range_start.clear();
commands.segments.clear();
let (data, index, segments) = commands.parts_mut();
let mut iter = entities
.iter_for_entities::<Read<ShadowGeometry>, _>(geometry_sorted.geometryrefs().map(|g| &**g))
.zip(geometry_sorted.iter())
.enumerate();
if let Some((segment, (geom, geom_index))) = iter.next() {
let mut allocators = resources.get_mut::<AllocatorsIndex>().unwrap();
let gl = resources.get::<gl::Renderer>().unwrap();
let shadow_materials_cache = resources.get::<ShadowMaterialCache>().unwrap();
let model_data_gpu = resources.get::<resources::AllModelMatricesBuffer>().unwrap();
let material_offsets = resources.get::<ShadowMaterialOffsets>();
let material_offsets = material_offsets
.as_ref()
.and_then(|offsets| offsets.all_buffer());
let mut base_instance = 0;
let num_instances = geom_index.num_instances() as u32;
let material = geom_index.materialref
.map(|m| &shadow_materials_cache[m]);
let mut segment_start = segment;
let mut prev_vao_id = geom_index.vao_id;
let mut prev_primitive_type = geom.primitive_type();
let mut prev_program_ref = material.map(|m| m.programref);
let mut prev_materialref = geom_index.materialref;
let mut prev_textures = material.map(|m| m.textures_hash);
let mut prev_properties = material.map(|m| &m.properties);
let mut prev_uniforms = material.map(|m| m.uniforms_hash);
let mut prev_elements = geom.has_indices();
let mut prev_vao_gl_id = geom
.full_shadow_vao(&*gl, &mut allocators, &model_data_gpu, material_offsets)
.unwrap()
.id();
let command = geom.command(&mut allocators, base_instance, num_instances);
let pos = data.len();
index.insert(geom_index.geometryref, pos);
data.push(command);
base_instance += num_instances;
for (segment, (geom, geom_index)) in iter {
let num_instances = geom_index.num_instances() as u32;
let material = geom_index.materialref
.map(|m| &shadow_materials_cache[m]);
if prev_program_ref != material.map(|m| m.programref)
|| prev_vao_id != geom_index.vao_id
|| prev_primitive_type != geom.primitive_type()
|| prev_textures != material.map(|m| m.textures_hash)
|| prev_properties != material.map(|m| &m.properties)
|| prev_uniforms != material.map(|m| m.uniforms_hash)
|| prev_materialref != geom_index.materialref
|| segment == geometry_sorted.len()
{
segments.push(Segment {
start: segment_start,
end: segment,
vao_id: prev_vao_gl_id,
primitive_type: prev_primitive_type,
elements: prev_elements,
});
segment_start = segment;
prev_vao_id = geom_index.vao_id;
prev_primitive_type = geom.primitive_type();
prev_program_ref = material.map(|m| m.programref);
prev_materialref = geom_index.materialref;
prev_textures = material.map(|m| m.textures_hash);
prev_properties = material.map(|m| &m.properties);
prev_uniforms = material.map(|m| m.uniforms_hash);
prev_elements = geom.has_indices();
prev_vao_gl_id = geom
.full_shadow_vao(&*gl, &mut allocators, &model_data_gpu, material_offsets)
.unwrap()
.id();
}
let command = geom.command(&mut allocators, base_instance, num_instances);
let pos = data.len();
index.insert(geom_index.geometryref, pos);
data.push(command);
base_instance += num_instances;
}
segments.push(Segment {
start: segment_start,
end: geometry_sorted.len(),
vao_id: prev_vao_gl_id,
primitive_type: prev_primitive_type,
elements: prev_elements,
});
}
commands.changed = true;
commands.dirty = false;
}
pub struct AllShadowsCommandBuffer(pub glin::Buffer<glin::DrawElementsIndirectCommand>);
#[system_thread_local(name = "upload all shadows command buffer")]
#[needs(AllShadowsCommandBufferData)]
#[updates(AllShadowsCommandBuffer)]
#[writes(AllShadowsCommandBufferData)]
#[gpu_stats]
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;
}
}
pub struct AnimatedGeometryGpuUpdater<V,B>{
allocator_handle: AllocatorHandle<V, B>,
}
impl<V,B> AnimatedGeometryGpuUpdater<V,B>{
pub fn new(allocator_handle: AllocatorHandle<V, B>) -> AnimatedGeometryGpuUpdater<V,B>{
AnimatedGeometryGpuUpdater{
allocator_handle
}
}
}
impl<V: 'static, B> AnimatedGeometryGpuUpdater<V, B>
where V: Debug + Clone + VertexFormat + 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: &mut EntitiesThreadLocal, resources: &ResourcesThreadLocal){
let mut allocator_index = resources.get_mut::<memory::AllocatorsIndex>().unwrap();
let allocator = allocator_index.get_mut(self.allocator_handle).unwrap();
let gl = resources.get::<gl::Renderer>().unwrap();
let iter = entities.iter_for_mut::<(
Write<AnimatedGeometry<V>>,
Ref<GpuGeometryRef, Read<VertexBuffer>>,
)>();
iter.filter(|(animated_geom, _,)| animated_geom.changed)
.for_each(|(animated_geom, vertex_buffer)|
{
allocator.update_vertex_buffer_range(&*gl, &vertex_buffer, &animated_geom.geom).unwrap();
animated_geom.changed = false;
});
}
}
#[system_thread_local(name = "animated geometry uploader")]
#[updates(VertexBuffer)]
#[needs(AnimatedGeometry<V>)]
#[reads(GpuGeometryRef, gl::Renderer)]
#[writes(
AnimatedGeometry<V>,
memory::AllocatorsIndex,
)]
#[cond(components(has("AnimatedGeometry<V>")))]
#[gpu_stats]
impl<V: 'static, B> SystemThreadLocal for AnimatedGeometryGpuUpdater<V, B>
where V: Debug + Clone + Send + VertexFormat + 'static,
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, mut entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
self.update_thread_local(&mut entities, &resources);
}
}