nightshade_api/
appearance.rs1use crate::scene::api_material_name;
5use nightshade::prelude::*;
6use nightshade::render::wgpu::texture_cache::{
7 SamplerSettings, TextureUsage, texture_cache_remove_reference,
8};
9
10pub fn set_color(world: &mut World, entity: Entity, color: [f32; 4]) {
12 mutate_material(world, entity, |material| {
13 material.base_color = color;
14 });
15}
16
17pub fn set_metallic_roughness(world: &mut World, entity: Entity, metallic: f32, roughness: f32) {
19 mutate_material(world, entity, |material| {
20 material.metallic = metallic;
21 material.roughness = roughness;
22 });
23}
24
25pub fn set_emissive(world: &mut World, entity: Entity, color: [f32; 3], strength: f32) {
28 mutate_material(world, entity, |material| {
29 material.emissive_factor = color;
30 material.emissive_strength = strength;
31 });
32}
33
34pub fn set_unlit(world: &mut World, entity: Entity, unlit: bool) {
36 mutate_material(world, entity, |material| {
37 material.unlit = unlit;
38 });
39}
40
41pub fn set_texture(world: &mut World, entity: Entity, texture_name: &str) {
45 let name = texture_name.to_string();
46 mutate_material(world, entity, move |material| {
47 material.base_texture = Some(name);
48 });
49}
50
51pub fn set_texture_tiling(world: &mut World, entity: Entity, repeats: f32) {
56 mutate_material(world, entity, |material| {
57 material.base_texture_transform.scale = [repeats, repeats];
58 });
59}
60
61pub fn load_texture(world: &mut World, name: &str, image_bytes: &[u8]) {
64 nightshade::ecs::loading::queue_encoded_texture(
65 world,
66 name.to_string(),
67 image_bytes.to_vec(),
68 TextureUsage::Color,
69 SamplerSettings::DEFAULT,
70 );
71 texture_cache_add_reference(&mut world.resources.texture_cache, name);
72}
73
74fn owns_material(material_name: &str, entity: Entity) -> bool {
75 material_name
76 .strip_prefix(crate::runner::MATERIAL_PREFIX)
77 .and_then(|suffix| suffix.parse().ok())
78 == Some(entity.id)
79}
80
81pub(crate) fn owned_color(world: &mut World, entity: Entity) -> Option<[f32; 4]> {
82 let material_ref = world.core.get_material_ref(entity).cloned()?;
83 if !owns_material(&material_ref.name, entity) {
84 let current = registry_entry_by_name(
85 &world.resources.assets.material_registry.registry,
86 &material_ref.name,
87 )
88 .map(|material| material.base_color)
89 .unwrap_or([1.0, 1.0, 1.0, 1.0]);
90 set_color(world, entity, current);
91 return Some(current);
92 }
93 registry_entry_by_name(
94 &world.resources.assets.material_registry.registry,
95 &material_ref.name,
96 )
97 .map(|material| material.base_color)
98}
99
100fn mutate_material(world: &mut World, entity: Entity, apply: impl FnOnce(&mut Material)) {
101 let Some(material_ref) = world.core.get_material_ref(entity).cloned() else {
102 return;
103 };
104
105 if owns_material(&material_ref.name, entity) {
106 let Some(mut material) = registry_entry_by_name(
107 &world.resources.assets.material_registry.registry,
108 &material_ref.name,
109 )
110 .cloned() else {
111 return;
112 };
113 let old_textures: Vec<String> = material.texture_names().map(str::to_string).collect();
114 apply(&mut material);
115 let new_textures: Vec<String> = material.texture_names().map(str::to_string).collect();
116 swap_texture_references(world, &old_textures, &new_textures);
117 if let Some(existing) = registry_entry_by_name_mut(
118 &mut world.resources.assets.material_registry.registry,
119 &material_ref.name,
120 ) {
121 *existing = material;
122 }
123 world
124 .resources
125 .mesh_render_state
126 .mark_material_dirty(entity);
127 } else {
128 let mut material = registry_entry_by_name(
129 &world.resources.assets.material_registry.registry,
130 &material_ref.name,
131 )
132 .cloned()
133 .unwrap_or_default();
134 apply(&mut material);
135 let textures: Vec<String> = material.texture_names().map(str::to_string).collect();
136 for texture in &textures {
137 texture_cache_add_reference(&mut world.resources.texture_cache, texture);
138 }
139 if let Some((index, _)) = registry_lookup_index(
140 &world.resources.assets.material_registry.registry,
141 &material_ref.name,
142 ) {
143 registry_remove_reference(
144 &mut world.resources.assets.material_registry.registry,
145 index,
146 );
147 }
148 register_material(world, entity, api_material_name(entity), material);
149 }
150}
151
152fn swap_texture_references(world: &mut World, old_textures: &[String], new_textures: &[String]) {
153 for texture in old_textures {
154 if !new_textures.contains(texture) {
155 texture_cache_remove_reference(&mut world.resources.texture_cache, texture);
156 }
157 }
158 for texture in new_textures {
159 if !old_textures.contains(texture) {
160 texture_cache_add_reference(&mut world.resources.texture_cache, texture);
161 }
162 }
163}