use crate::ecs::asset_id::MaterialId;
use crate::ecs::generational_registry::GenerationalRegistry;
use crate::ecs::material::components::Material;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
#[derive(Clone)]
pub struct MaterialRegistry {
pub registry: GenerationalRegistry<Material>,
pub protected_indices: Vec<u32>,
pub content_hash_to_name: HashMap<u64, String>,
}
impl Default for MaterialRegistry {
fn default() -> Self {
let mut instance = Self {
registry: GenerationalRegistry::new(),
protected_indices: Vec::new(),
content_hash_to_name: HashMap::new(),
};
let default_materials = [
(
"Default",
Material {
base_color: [0.7, 0.7, 0.7, 1.0],
..Default::default()
},
),
(
"Red",
Material {
base_color: [1.0, 0.3, 0.3, 1.0],
..Default::default()
},
),
(
"Green",
Material {
base_color: [0.3, 1.0, 0.3, 1.0],
..Default::default()
},
),
(
"Blue",
Material {
base_color: [0.3, 0.3, 1.0, 1.0],
..Default::default()
},
),
(
"Yellow",
Material {
base_color: [1.0, 1.0, 0.3, 1.0],
..Default::default()
},
),
(
"Magenta",
Material {
base_color: [1.0, 0.3, 1.0, 1.0],
..Default::default()
},
),
(
"Cyan",
Material {
base_color: [0.3, 1.0, 1.0, 1.0],
..Default::default()
},
),
(
"White",
Material {
base_color: [1.0, 1.0, 1.0, 1.0],
..Default::default()
},
),
(
"Black",
Material {
base_color: [0.1, 0.1, 0.1, 1.0],
..Default::default()
},
),
];
for (name, material) in default_materials {
let (index, _generation) = instance.registry.insert(name.to_string(), material);
instance.protected_indices.push(index);
}
instance
}
}
pub fn material_registry_insert(
registry: &mut MaterialRegistry,
name: String,
material: Material,
) -> MaterialId {
let (index, generation) = registry.registry.insert(name, material);
MaterialId { index, generation }
}
pub fn material_registry_lookup_id(registry: &MaterialRegistry, name: &str) -> Option<MaterialId> {
registry
.registry
.lookup_index(name)
.map(|(index, generation)| MaterialId { index, generation })
}
pub fn material_registry_remove_unused(registry: &mut MaterialRegistry) -> Vec<String> {
let mut removed = Vec::new();
for index in 0..registry.registry.entries.len() {
if registry.protected_indices.contains(&(index as u32)) {
continue;
}
if registry.registry.reference_counts[index] == 0
&& registry.registry.entries[index].is_some()
{
if let Some(name) = registry.registry.index_to_name[index].take() {
registry.registry.name_to_index.remove(&name);
removed.push(name);
}
registry.registry.entries[index] = None;
registry.registry.free_indices.push(index as u32);
}
}
removed
}
pub fn material_registry_iter(
registry: &MaterialRegistry,
) -> impl Iterator<Item = (&String, &Material)> {
registry
.registry
.index_to_name
.iter()
.enumerate()
.filter_map(|(index, name_opt)| {
name_opt.as_ref().and_then(|name| {
registry.registry.entries[index]
.as_ref()
.map(|material| (name, material))
})
})
}
fn compute_material_content_hash(material: &Material) -> u64 {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
for component in &material.base_color {
component.to_bits().hash(&mut hasher);
}
for component in &material.emissive_factor {
component.to_bits().hash(&mut hasher);
}
material.alpha_mode.hash(&mut hasher);
material.alpha_cutoff.to_bits().hash(&mut hasher);
material.base_texture.hash(&mut hasher);
material.base_texture_uv_set.hash(&mut hasher);
material.emissive_texture.hash(&mut hasher);
material.emissive_texture_uv_set.hash(&mut hasher);
material.normal_texture.hash(&mut hasher);
material.normal_texture_uv_set.hash(&mut hasher);
material.normal_scale.to_bits().hash(&mut hasher);
material.normal_map_flip_y.hash(&mut hasher);
material.normal_map_two_component.hash(&mut hasher);
material.metallic_roughness_texture.hash(&mut hasher);
material.metallic_roughness_texture_uv_set.hash(&mut hasher);
material.occlusion_texture.hash(&mut hasher);
material.occlusion_texture_uv_set.hash(&mut hasher);
material.occlusion_strength.to_bits().hash(&mut hasher);
material.roughness.to_bits().hash(&mut hasher);
material.metallic.to_bits().hash(&mut hasher);
material.unlit.hash(&mut hasher);
material.double_sided.hash(&mut hasher);
for component in &material.uv_scale {
component.to_bits().hash(&mut hasher);
}
material.transmission_factor.to_bits().hash(&mut hasher);
material.transmission_texture.hash(&mut hasher);
material.transmission_texture_uv_set.hash(&mut hasher);
material.thickness.to_bits().hash(&mut hasher);
material.thickness_texture.hash(&mut hasher);
material.thickness_texture_uv_set.hash(&mut hasher);
for component in &material.attenuation_color {
component.to_bits().hash(&mut hasher);
}
material.attenuation_distance.to_bits().hash(&mut hasher);
material.ior.to_bits().hash(&mut hasher);
material.specular_factor.to_bits().hash(&mut hasher);
for component in &material.specular_color_factor {
component.to_bits().hash(&mut hasher);
}
material.specular_texture.hash(&mut hasher);
material.specular_texture_uv_set.hash(&mut hasher);
material.specular_color_texture.hash(&mut hasher);
material.specular_color_texture_uv_set.hash(&mut hasher);
material.emissive_strength.to_bits().hash(&mut hasher);
hasher.finish()
}
pub fn material_registry_find_or_insert(
registry: &mut MaterialRegistry,
fallback_name: String,
material: Material,
) -> String {
let content_hash = compute_material_content_hash(&material);
if let Some(existing_name) = registry.content_hash_to_name.get(&content_hash) {
return existing_name.clone();
}
registry
.content_hash_to_name
.insert(content_hash, fallback_name.clone());
material_registry_insert(registry, fallback_name.clone(), material);
fallback_name
}