use crate::scene::api_material_name;
use nightshade::ecs::material::resources::material_registry_mutate;
use nightshade::prelude::*;
use nightshade::render::material::AlphaMode;
use nightshade::render::texture_data::{SamplerSettings, TextureUsage};
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 set_texture_tiling(world: &mut World, entity: Entity, repeats: f32) {
mutate_material(world, entity, |material| {
material.base_texture_transform.scale = [repeats, repeats];
});
}
pub fn set_normal_texture(world: &mut World, entity: Entity, texture_name: &str) {
let name = texture_name.to_string();
mutate_material(world, entity, move |material| {
material.normal_texture = Some(name);
});
}
pub fn set_metallic_roughness_texture(world: &mut World, entity: Entity, texture_name: &str) {
let name = texture_name.to_string();
mutate_material(world, entity, move |material| {
material.metallic_roughness_texture = Some(name);
});
}
pub fn set_emissive_texture(world: &mut World, entity: Entity, texture_name: &str) {
let name = texture_name.to_string();
mutate_material(world, entity, move |material| {
material.emissive_texture = Some(name);
});
}
pub fn set_occlusion_texture(world: &mut World, entity: Entity, texture_name: &str) {
let name = texture_name.to_string();
mutate_material(world, entity, move |material| {
material.occlusion_texture = Some(name);
});
}
pub fn load_texture(world: &mut World, name: &str, image_bytes: &[u8]) {
load_texture_with_usage(world, name, image_bytes, TextureUsage::Color);
}
pub fn load_texture_linear(world: &mut World, name: &str, image_bytes: &[u8]) {
load_texture_with_usage(world, name, image_bytes, TextureUsage::Linear);
}
fn load_texture_with_usage(world: &mut World, name: &str, image_bytes: &[u8], usage: TextureUsage) {
nightshade::ecs::loading::queue_encoded_texture(
world,
name.to_string(),
image_bytes.to_vec(),
usage,
SamplerSettings::DEFAULT,
);
texture_cache_acquire(
&mut world.resources.texture_cache,
TextureOwner::Named(name.to_string()),
vec![name.to_string()],
);
}
pub fn register_texture(world: &mut World, name: &str, width: u32, height: u32, rgba: &[u8]) {
nightshade::ecs::loading::queue_decoded_texture(
world,
name.to_string(),
rgba.to_vec(),
width,
height,
TextureUsage::Color,
SamplerSettings::DEFAULT,
);
texture_cache_acquire(
&mut world.resources.texture_cache,
TextureOwner::Named(name.to_string()),
vec![name.to_string()],
);
}
pub fn set_alpha_blend(world: &mut World, entity: Entity, enabled: bool) {
mutate_material(world, entity, move |material| {
material.alpha_mode = if enabled {
AlphaMode::Blend
} else {
AlphaMode::Opaque
};
});
}
pub fn set_alpha_cutoff(world: &mut World, entity: Entity, cutoff: f32) {
mutate_material(world, entity, move |material| {
material.alpha_mode = AlphaMode::Mask;
material.alpha_cutoff = cutoff;
});
}
pub fn set_double_sided(world: &mut World, entity: Entity, double_sided: bool) {
mutate_material(world, entity, move |material| {
material.double_sided = double_sided;
});
}
pub fn set_ior(world: &mut World, entity: Entity, ior: f32) {
mutate_material(world, entity, move |material| {
material.ior = ior;
});
}
pub fn set_transmission(world: &mut World, entity: Entity, factor: f32) {
mutate_material(world, entity, move |material| {
material.transmission_factor = factor;
});
}
pub fn set_clearcoat(world: &mut World, entity: Entity, factor: f32, roughness: f32) {
mutate_material(world, entity, move |material| {
material.clearcoat_factor = factor;
material.clearcoat_roughness_factor = roughness;
});
}
pub fn set_anisotropy(world: &mut World, entity: Entity, strength: f32, rotation: f32) {
mutate_material(world, entity, move |material| {
material.anisotropy_strength = strength;
material.anisotropy_rotation = rotation;
});
}
pub fn set_uv_transform(
world: &mut World,
entity: Entity,
offset: [f32; 2],
scale: [f32; 2],
rotation: f32,
) {
mutate_material(world, entity, move |material| {
material.base_texture_transform.offset = offset;
material.base_texture_transform.scale = scale;
material.base_texture_transform.rotation = rotation;
});
}
pub fn set_sheen(world: &mut World, entity: Entity, color: [f32; 3], roughness: f32) {
mutate_material(world, entity, move |material| {
material.sheen_color_factor = color;
material.sheen_roughness_factor = roughness;
});
}
pub fn set_iridescence(world: &mut World, entity: Entity, factor: f32, ior: f32) {
mutate_material(world, entity, move |material| {
material.iridescence_factor = factor;
material.iridescence_ior = ior;
});
}
pub fn set_specular(world: &mut World, entity: Entity, factor: f32, color: [f32; 3]) {
mutate_material(world, entity, move |material| {
material.specular_factor = factor;
material.specular_color_factor = color;
});
}
pub fn set_normal_scale(world: &mut World, entity: Entity, scale: f32) {
mutate_material(world, entity, move |material| {
material.normal_scale = scale;
});
}
pub fn set_occlusion_strength(world: &mut World, entity: Entity, strength: f32) {
mutate_material(world, entity, move |material| {
material.occlusion_strength = strength;
});
}
pub fn set_emissive_strength(world: &mut World, entity: Entity, strength: f32) {
mutate_material(world, entity, move |material| {
material.emissive_strength = strength;
});
}
pub fn set_thickness(world: &mut World, entity: Entity, thickness: f32) {
mutate_material(world, entity, move |material| {
material.thickness = thickness;
});
}
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
.get::<nightshade::ecs::material::components::MaterialRef>(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
.get::<nightshade::ecs::material::components::MaterialRef>(entity)
.cloned()
else {
return;
};
if owns_material(&material_ref.name, entity) {
if !material_registry_mutate(
&mut world.resources.assets.material_registry,
&material_ref.name,
apply,
) {
return;
}
let new_textures: Vec<String> = registry_entry_by_name(
&world.resources.assets.material_registry.registry,
&material_ref.name,
)
.map(|material| material.texture_names().map(str::to_string).collect())
.unwrap_or_default();
texture_cache_acquire(
&mut world.resources.texture_cache,
TextureOwner::EntityMaterial(render_entity(entity)),
new_textures,
);
world
.resources
.mesh_render_state
.mark_material_dirty(render_entity(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();
texture_cache_acquire(
&mut world.resources.texture_cache,
TextureOwner::EntityMaterial(render_entity(entity)),
textures,
);
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);
}
}