use rinecs::{
Read, Write, Not, ReadOption, Entities, Resources,
EntitiesThreadLocal, ResourcesThreadLocal, Has, system, system_thread_local
};
use crate::{
components::Visible,
light::shadow,
};
use rin_material::{MaterialRef, MaterialMultiRef};
#[cfg(feature="debug_geometry")]
use crate::geometry::DebugGeometryRef;
use super::resources;
use super::geometry::{ SubmeshBuffers, GpuGeometryRef };
#[cfg(feature="debug_geometry")]
use super::geometry::GpuDebugGeometryRef;
use super::material::MaterialCache;
use rin_graphics::{Node, node::DynamicTransformation};
use rin_gl as gl;
#[system(name = "model matrices")]
#[needs(
"MaterialCache",
"Node",
"MaterialRef",
"MaterialMultiRef",
"Visible",
"GpuGeometryRef",
"resources::OpaqueSortedGeometry",
"resources::TranslucentSortedGeometry"
)]
#[cfg_attr(feature="debug_geometry", needs(
"DebugGeometryRef",
"GpuDebugGeometryRef",
"resources::DebugSortedGeometry"
))]
#[updates("resources::ModelMatricesData")]
pub fn generate_model_matrices_data(entities: Entities, resources: Resources) {
let materials_cache = resources.get::<MaterialCache>().unwrap();
let any_model_changed = entities.changed_iter_for::<(
Read<Node>,
Read<GpuGeometryRef>,
ReadOption<MaterialRef>,
ReadOption<MaterialMultiRef>,
Read<Visible>)>()
.any(|(_, _, materials, multimaterials, visible)| {
visible.is_visible() && if let Some(materials) = materials {
materials.iter().any(|m| materials_cache[*m].visible)
}else if let Some(multimaterials) = multimaterials {
multimaterials.iter()
.any(|materials| materials.iter().any(|m| materials_cache[*m].visible))
}else{
false
}
});
#[cfg(feature="debug_geometry")]
let any_debug_model_changed = entities.changed_iter_for::<(
Read<Node>,
Read<GpuDebugGeometryRef>,
Read<DebugGeometryRef>,
)>().any(|(_, _, debug_geom)|{
debug_geom.is_visible() && materials_cache[*debug_geom.material()].visible
});
#[cfg(not(feature="debug_geometry"))]
let any_debug_model_changed = false;
let mut model_data = resources.get_mut::<resources::ModelMatricesData>().unwrap();
let opaque_geometry_sorted = resources
.get::<resources::OpaqueSortedGeometry>()
.unwrap();
let translucent_geometry_sorted = resources
.get::<resources::TranslucentSortedGeometry>()
.unwrap();
#[cfg(feature="debug_geometry")]
let debug_geometry_sorted = resources
.get::<resources::DebugSortedGeometry>()
.unwrap();
let opaque_changed = opaque_geometry_sorted.has_changed();
let translucent_changed = translucent_geometry_sorted.has_changed();
#[cfg(feature="debug_geometry")]
let debug_changed = debug_geometry_sorted.has_changed();
#[cfg(not(feature="debug_geometry"))]
let debug_changed = false;
let was_empty = model_data.is_empty();
if any_model_changed
|| any_debug_model_changed
|| opaque_changed
|| translucent_changed
|| debug_changed
|| model_data.is_empty()
{
let geometries = opaque_geometry_sorted.iter().chain(translucent_geometry_sorted.iter());
#[cfg(feature="debug_geometry")]
let geometries = geometries.chain(debug_geometry_sorted.iter());
model_data.clear();
let geom_entities = geometries.flat_map(|geom| geom.entities.iter());
let models = entities.iter_for_entities::<Read<Node>,_>(geom_entities)
.map(|trafo| {
let model = trafo.global_transformation();
let normal = trafo.inv_global_transformation().transpose();
(model, normal)
});
model_data.extend(models);
if !(was_empty && model_data.is_empty()){
model_data.has_changed = true;
}
}
}
#[system(name = "generate model mats static")]
#[needs(
"MaterialCache",
"resources::StaticShadowsSortedGeometry",
"Node",
"Visible",
"GpuGeometryRef",
"MaterialRef",
"MaterialMultiRef",
)]
#[updates("resources::StaticModelMatricesData")]
#[cond(components(has("shadow::StaticMap")))]
pub fn generate_static_model_matrices_data(entities: Entities, resources: Resources) {
let materials_cache = resources.get::<MaterialCache>().unwrap();
let geometries = resources.get::<resources::StaticShadowsSortedGeometry>().unwrap();
let mut model_data = resources.get_mut::<resources::StaticModelMatricesData>().unwrap();
let was_empty = model_data.is_empty();
let any_model_changed = geometries.has_changed() || entities.changed_iter_for::<(
Read<Node>,
Not<DynamicTransformation>,
Read<GpuGeometryRef>,
ReadOption<MaterialRef>,
ReadOption<MaterialMultiRef>,
Read<Visible>)>()
.any(|(_, _, _, materials, multimaterials, visible)| {
visible.is_visible() && 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
}
});
if any_model_changed || model_data.is_empty() {
if !model_data.is_empty(){
log::trace!("Regenerating static model data");
}
model_data.clear();
let geom_entities = geometries.iter().flat_map(|geom| &geom.entities);
let models = entities.iter_for_entities::<Read<Node>,_>(geom_entities)
.map(|trafo| trafo.global_transformation());
model_data.extend(models);
if !(was_empty && model_data.is_empty()){
model_data.has_changed = true;
}
}
}
#[system(name = "generate model mats dynamic")]
#[needs(
"MaterialCache",
"resources::DynamicShadowsSortedGeometry",
"Node",
"Visible",
"GpuGeometryRef",
"MaterialRef",
"MaterialMultiRef",
)]
#[updates("resources::DynamicModelMatricesData")]
#[cond(components(has("shadow::Map"), has("shadow::StaticMap")))]
pub fn generate_dynamic_model_matrices_data(entities: Entities, resources: Resources) {
let materials_cache = resources.get::<MaterialCache>().unwrap();
let geometries = resources.get::<resources::DynamicShadowsSortedGeometry>().unwrap();
let any_model_changed = geometries.has_changed() || entities.changed_iter_for::<(
Read<Node>,
Has<DynamicTransformation>,
Read<GpuGeometryRef>,
ReadOption<MaterialRef>,
ReadOption<MaterialMultiRef>,
Read<Visible>)>()
.any(|(_, _, _, materials, multimaterials, visible)| {
visible.is_visible() && 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
}
});
let mut model_data = resources.get_mut::<resources::DynamicModelMatricesData>().unwrap();
let was_empty = model_data.is_empty();
if any_model_changed || model_data.is_empty() {
model_data.clear();
let geom_entities = geometries.iter().flat_map(|geom| &geom.entities);
let models = entities.iter_for_entities::<Read<Node>,_>(geom_entities)
.map(|trafo| trafo.global_transformation());
model_data.extend(models);
if !(was_empty && model_data.is_empty()){
model_data.has_changed = true;
}
}
}
#[system(name = "generate model mats all")]
#[needs(
"MaterialCache",
"resources::AllShadowsSortedGeometry",
"Node",
"Visible",
"GpuGeometryRef",
"MaterialRef",
"MaterialMultiRef",
)]
#[updates("resources::AllModelMatricesData")]
#[cond(any(
components(has_not("shadow::Map", "shadow::StaticMap")),
has_resource("resources::DepthPrepass")
))]
#[reads("shadow::Map", "shadow::StaticMap")]
pub fn generate_all_model_matrices_data(entities: Entities, resources: Resources) {
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();
if !(any_has_only_dynamic_shadows || depth_prepass) {
return;
}
let materials_cache = resources.get::<MaterialCache>().unwrap();
let geometries = resources.get::<resources::AllShadowsSortedGeometry>().unwrap();
let any_model_changed = geometries.has_changed() || entities.changed_iter_for::<(
Read<Node>,
Read<GpuGeometryRef>,
ReadOption<MaterialRef>,
ReadOption<MaterialMultiRef>,
Read<Visible>)>()
.any(|(_, _, materials, multimaterials, visible)| {
let changed = visible.is_visible();
changed && 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
}
});
let mut model_data = resources.get_mut::<resources::AllModelMatricesData>().unwrap();
let was_empty = model_data.is_empty();
if any_model_changed || model_data.is_empty() {
model_data.clear();
let geom_entities = geometries.iter().flat_map(|geom| &geom.entities);
let models = entities.iter_for_entities::<Read<Node>,_>(geom_entities)
.map(|trafo| trafo.global_transformation());
model_data.extend(models);
if !(was_empty && model_data.is_empty()){
model_data.has_changed = true;
}
}
}
#[system_thread_local(name = "upload m. matrices")]
#[needs(resources::ModelMatricesData, SubmeshBuffers)]
#[updates(resources::ModelMatricesBuffer)]
#[writes(SubmeshBuffers, resources::ModelMatricesData)]
#[gpu_stats]
pub fn update_model_matrices_gpu(mut entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let mut model_data = resources.get_mut::<resources::ModelMatricesData>().unwrap();
let vaos_changed = entities.iter_for::<Read<SubmeshBuffers>>()
.any(|s| s.iter().any(|s| s.has_changed()));
if model_data.has_changed || vaos_changed {
let mut model_data_gpu = resources.get_mut::<resources::ModelMatricesBuffer>().unwrap();
if model_data_gpu.capacity() >= model_data.len() {
model_data_gpu.update(&model_data.data);
}else{
model_data_gpu.load(&model_data.data, gl::DYNAMIC_DRAW);
}
model_data.has_changed = false;
if vaos_changed {
for submeshes in entities.iter_for_mut::<Write<SubmeshBuffers>>(){
for submesh in submeshes {
submesh.reset_has_changed();
}
}
}
}
}
#[system_thread_local(name = "upload static m. matrices")]
#[needs(resources::StaticModelMatricesData)]
#[updates(resources::StaticModelMatricesBuffer)]
#[writes(resources::StaticModelMatricesData)]
#[gpu_stats]
pub fn update_static_model_matrices_gpu(_entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let mut model_data = resources.get_mut::<resources::StaticModelMatricesData>().unwrap();
if model_data.has_changed {
log::trace!("Updating gpu model data for static geometries");
let mut model_data_gpu = resources.get_mut::<resources::StaticModelMatricesBuffer>().unwrap();
if model_data_gpu.capacity() >= model_data.len() {
model_data_gpu.update(&model_data.data);
}else{
model_data_gpu.load(&model_data.data, gl::DYNAMIC_DRAW);
}
model_data.has_changed = false;
}
}
#[system_thread_local(name = "upload dynamic m. matrices")]
#[needs(resources::DynamicModelMatricesData)]
#[updates(resources::DynamicModelMatricesBuffer)]
#[writes(resources::DynamicModelMatricesData)]
#[gpu_stats]
pub fn update_dynamic_model_matrices_gpu(_entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let mut model_data = resources.get_mut::<resources::DynamicModelMatricesData>().unwrap();
if model_data.has_changed {
let mut model_data_gpu = resources.get_mut::<resources::DynamicModelMatricesBuffer>().unwrap();
if model_data_gpu.capacity() >= model_data.len() {
model_data_gpu.update(&model_data.data);
}else{
model_data_gpu.load(&model_data.data, gl::DYNAMIC_DRAW);
}
model_data.has_changed = false;
}
}
#[system_thread_local(name = "upload all m. matrices")]
#[needs(resources::AllModelMatricesData)]
#[updates(resources::AllModelMatricesBuffer)]
#[writes(resources::AllModelMatricesData)]
#[gpu_stats]
pub fn update_all_model_matrices_gpu(_entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let mut model_data = resources.get_mut::<resources::AllModelMatricesData>().unwrap();
if model_data.has_changed {
let mut model_data_gpu = resources.get_mut::<resources::AllModelMatricesBuffer>().unwrap();
if model_data_gpu.capacity() >= model_data.len() {
model_data_gpu.update(&model_data.data);
}else{
model_data_gpu.load(&model_data.data, gl::DYNAMIC_DRAW);
}
model_data.has_changed = false;
}
}