use crate::scene::api_material_name;
use nightshade::prelude::*;
use nightshade::render::wgpu::texture_cache::{
SamplerSettings, TextureUsage, texture_cache_remove_reference,
};
pub fn set_color(world: &mut World, entity: Entity, color: [f32; 4]) {
mutate_material(world, entity, |material| {
material.base_color = color;
});
}
pub fn set_metallic_roughness(world: &mut World, entity: Entity, metallic: f32, roughness: f32) {
mutate_material(world, entity, |material| {
material.metallic = metallic;
material.roughness = roughness;
});
}
pub fn set_emissive(world: &mut World, entity: Entity, color: [f32; 3], strength: f32) {
mutate_material(world, entity, |material| {
material.emissive_factor = color;
material.emissive_strength = strength;
});
}
pub fn set_unlit(world: &mut World, entity: Entity, unlit: bool) {
mutate_material(world, entity, |material| {
material.unlit = unlit;
});
}
pub fn set_texture(world: &mut World, entity: Entity, texture_name: &str) {
let name = texture_name.to_string();
mutate_material(world, entity, move |material| {
material.base_texture = Some(name);
});
}
pub fn load_texture(world: &mut World, name: &str, image_bytes: &[u8]) {
nightshade::ecs::loading::queue_encoded_texture(
world,
name.to_string(),
image_bytes.to_vec(),
TextureUsage::Color,
SamplerSettings::DEFAULT,
);
texture_cache_add_reference(&mut world.resources.texture_cache, name);
}
fn owns_material(material_name: &str, entity: Entity) -> bool {
material_name
.strip_prefix(crate::runner::MATERIAL_PREFIX)
.and_then(|suffix| suffix.parse().ok())
== Some(entity.id)
}
pub(crate) fn owned_color(world: &mut World, entity: Entity) -> Option<[f32; 4]> {
let material_ref = world.core.get_material_ref(entity).cloned()?;
if !owns_material(&material_ref.name, entity) {
let current = registry_entry_by_name(
&world.resources.assets.material_registry.registry,
&material_ref.name,
)
.map(|material| material.base_color)
.unwrap_or([1.0, 1.0, 1.0, 1.0]);
set_color(world, entity, current);
return Some(current);
}
registry_entry_by_name(
&world.resources.assets.material_registry.registry,
&material_ref.name,
)
.map(|material| material.base_color)
}
fn mutate_material(world: &mut World, entity: Entity, apply: impl FnOnce(&mut Material)) {
let Some(material_ref) = world.core.get_material_ref(entity).cloned() else {
return;
};
if owns_material(&material_ref.name, entity) {
let Some(mut material) = registry_entry_by_name(
&world.resources.assets.material_registry.registry,
&material_ref.name,
)
.cloned() else {
return;
};
let old_textures: Vec<String> = material.texture_names().map(str::to_string).collect();
apply(&mut material);
let new_textures: Vec<String> = material.texture_names().map(str::to_string).collect();
swap_texture_references(world, &old_textures, &new_textures);
if let Some(existing) = registry_entry_by_name_mut(
&mut world.resources.assets.material_registry.registry,
&material_ref.name,
) {
*existing = material;
}
world
.resources
.mesh_render_state
.mark_material_dirty(entity);
} else {
let mut material = registry_entry_by_name(
&world.resources.assets.material_registry.registry,
&material_ref.name,
)
.cloned()
.unwrap_or_default();
apply(&mut material);
let textures: Vec<String> = material.texture_names().map(str::to_string).collect();
for texture in &textures {
texture_cache_add_reference(&mut world.resources.texture_cache, texture);
}
if let Some((index, _)) = registry_lookup_index(
&world.resources.assets.material_registry.registry,
&material_ref.name,
) {
registry_remove_reference(
&mut world.resources.assets.material_registry.registry,
index,
);
}
register_material(world, entity, api_material_name(entity), material);
}
}
fn swap_texture_references(world: &mut World, old_textures: &[String], new_textures: &[String]) {
for texture in old_textures {
if !new_textures.contains(texture) {
texture_cache_remove_reference(&mut world.resources.texture_cache, texture);
}
}
for texture in new_textures {
if !old_textures.contains(texture) {
texture_cache_add_reference(&mut world.resources.texture_cache, texture);
}
}
}