use crate::app::{App, Plugin, Stage};
use crate::ecs::world::CORE;
use crate::ecs::world::World;
use crate::render_driver::render_entity;
pub fn install(world: &mut World) {
use crate::schedule::{FramePhase, schedule_push};
let schedule = &mut world.resources.schedules.frame;
schedule_push(
schedule,
FramePhase::Render,
crate::ecs::graphics::scene_sync::render_sync_system,
);
schedule_push(schedule, FramePhase::Render, render_sync_materials_system);
schedule_push(
schedule,
FramePhase::Render,
crate::ecs::graphics::animation_sync::render_sync_animation_system,
);
schedule_push(
schedule,
FramePhase::Render,
crate::ecs::skin::systems::render_sync_skinning_system,
);
}
#[derive(Default)]
pub struct RenderPlugin {
pub vsync: Option<bool>,
pub frame_rate_limit: Option<f32>,
}
impl Plugin for RenderPlugin {
fn build(&self, app: &mut App) {
app.world.resources.window.create_renderer = true;
if let Some(vsync) = self.vsync {
app.world.resources.render_settings.vsync_enabled = vsync;
}
if let Some(limit) = self.frame_rate_limit {
app.world.resources.render_settings.frame_rate_limit = Some(limit);
}
app.add_system(Stage::Startup, install);
}
}
fn material_table_entry(
registry: &crate::ecs::material::resources::MaterialRegistry,
index: usize,
) -> crate::render::config::RenderMaterialEntry {
match registry.registry.entries[index].as_ref() {
Some(material) => crate::render::config::RenderMaterialEntry {
material: material.clone(),
texture_ids: registry.texture_ids.get(index).copied().unwrap_or_default(),
},
None => crate::render::config::RenderMaterialEntry::default(),
}
}
pub fn render_sync_materials_system(world: &mut World) {
let registry_version = world.resources.assets.material_registry.version;
let registry_names_version = world.resources.assets.material_registry.names_version;
let (rebuild_entity_maps, first_run) = {
let sync = &mut world.resources.render_scene_sync;
let entities_dirty = std::mem::take(&mut sync.materials_dirty);
let version_dirty = sync.materials_version_seen != registry_version;
let names_dirty = sync.materials_names_version_seen != registry_names_version;
let first_run = !sync.materials_initialized;
if !entities_dirty && !version_dirty && !first_run {
return;
}
sync.materials_initialized = true;
sync.materials_version_seen = registry_version;
sync.materials_names_version_seen = registry_names_version;
(entities_dirty || names_dirty || first_run, first_run)
};
let _span = tracing::info_span!("render_sync_materials").entered();
use crate::render::material::AlphaMode;
let mut dirty_indices =
std::mem::take(&mut world.resources.assets.material_registry.dirty_indices);
dirty_indices.sort_unstable();
dirty_indices.dedup();
{
let resources = &mut world.resources;
let registry = &resources.assets.material_registry;
let table = &mut resources.renderer_state.render_materials;
let entry_count = registry.registry.entries.len();
if first_run || table.entries.len() > entry_count {
table.entries.clear();
table.entries.reserve(entry_count);
for index in 0..entry_count {
table.entries.push(material_table_entry(registry, index));
}
} else {
if table.entries.len() < entry_count {
table.entries.resize_with(
entry_count,
crate::render::config::RenderMaterialEntry::default,
);
}
for &index in &dirty_indices {
let index = index as usize;
if index < entry_count {
table.entries[index] = material_table_entry(registry, index);
}
}
}
table.name_to_id.clear();
for (index, name_slot) in registry.registry.index_to_name.iter().enumerate() {
if let Some(name) = name_slot
&& registry.registry.entries[index].is_some()
{
table.name_to_id.insert(name.clone(), index as u32 + 1);
}
}
table.transparent_ids.clear();
table.mask_ids.clear();
table.double_sided_ids.clear();
for (index, entry) in table.entries.iter().enumerate() {
let material_id = index as u32 + 1;
let material = &entry.material;
if material.alpha_mode == AlphaMode::Blend || material.transmission_factor > 0.0 {
table.transparent_ids.insert(material_id);
}
if matches!(material.alpha_mode, AlphaMode::Mask | AlphaMode::Dither) {
table.mask_ids.insert(material_id);
}
if material.double_sided {
table.double_sided_ids.insert(material_id);
}
}
}
{
let table = &mut world.resources.renderer_state.render_materials;
table.generation = table.generation.wrapping_add(1);
}
if !rebuild_entity_maps {
return;
}
let mesh_entities: Vec<crate::ecs::world::Entity> = world.ecs.worlds[CORE]
.query_entities(crate::ecs::world::RENDER_MESH)
.filter(|entity| {
!world.ecs.worlds[CORE].entity_has_components(*entity, crate::ecs::world::SKIN)
})
.collect();
let skinned_entities: Vec<crate::ecs::world::Entity> = world.ecs.worlds[CORE]
.query_entities(crate::ecs::world::SKIN | crate::ecs::world::RENDER_MESH)
.collect();
let instanced_entities: Vec<crate::ecs::world::Entity> = world.ecs.worlds[CORE]
.query_entities(crate::ecs::world::INSTANCED_MESH)
.collect();
let registry = &world.resources.assets.material_registry;
let mut entity_to_id: std::collections::HashMap<crate::ecs::world::Entity, u32> =
std::collections::HashMap::new();
let mut entity_to_name: std::collections::HashMap<crate::ecs::world::Entity, String> =
std::collections::HashMap::new();
for &entity in mesh_entities
.iter()
.chain(skinned_entities.iter())
.chain(instanced_entities.iter())
{
use crate::render::generational_registry::{registry_entry, registry_lookup_index};
let Some(material_ref) = world.get::<crate::ecs::material::components::MaterialRef>(entity)
else {
continue;
};
entity_to_name.insert(entity, material_ref.name.clone());
let id = material_ref
.id
.and_then(|id| {
registry_entry(®istry.registry, id.index, id.generation).map(|_| id.index + 1)
})
.or_else(|| {
registry_lookup_index(®istry.registry, &material_ref.name).and_then(
|(index, _generation)| {
registry.registry.entries[index as usize]
.as_ref()
.map(|_| index + 1)
},
)
})
.unwrap_or(0);
entity_to_id.insert(entity, id);
}
let table = &mut world.resources.renderer_state.render_materials;
table.entity_to_id = entity_to_id
.into_iter()
.map(|(entity, id)| (render_entity(entity), id))
.collect();
table.entity_to_name = entity_to_name
.into_iter()
.map(|(entity, name)| (render_entity(entity), name))
.collect();
}