1use crate::scene::api_material_name;
5use nightshade::ecs::material::resources::material_registry_mutate;
6use nightshade::prelude::*;
7use nightshade::render::material::AlphaMode;
8use nightshade::render::texture_data::{SamplerSettings, TextureUsage};
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 set_normal_texture(world: &mut World, entity: Entity, texture_name: &str) {
65 let name = texture_name.to_string();
66 mutate_material(world, entity, move |material| {
67 material.normal_texture = Some(name);
68 });
69}
70
71pub fn set_metallic_roughness_texture(world: &mut World, entity: Entity, texture_name: &str) {
75 let name = texture_name.to_string();
76 mutate_material(world, entity, move |material| {
77 material.metallic_roughness_texture = Some(name);
78 });
79}
80
81pub fn set_emissive_texture(world: &mut World, entity: Entity, texture_name: &str) {
84 let name = texture_name.to_string();
85 mutate_material(world, entity, move |material| {
86 material.emissive_texture = Some(name);
87 });
88}
89
90pub fn set_occlusion_texture(world: &mut World, entity: Entity, texture_name: &str) {
93 let name = texture_name.to_string();
94 mutate_material(world, entity, move |material| {
95 material.occlusion_texture = Some(name);
96 });
97}
98
99pub fn load_texture(world: &mut World, name: &str, image_bytes: &[u8]) {
103 load_texture_with_usage(world, name, image_bytes, TextureUsage::Color);
104}
105
106pub fn load_texture_linear(world: &mut World, name: &str, image_bytes: &[u8]) {
110 load_texture_with_usage(world, name, image_bytes, TextureUsage::Linear);
111}
112
113fn load_texture_with_usage(world: &mut World, name: &str, image_bytes: &[u8], usage: TextureUsage) {
114 nightshade::ecs::loading::queue_encoded_texture(
115 world,
116 name.to_string(),
117 image_bytes.to_vec(),
118 usage,
119 SamplerSettings::DEFAULT,
120 );
121 texture_cache_acquire(
122 world.res_mut::<nightshade::render::wgpu::texture_cache::TextureCache>(),
123 TextureOwner::Named(name.to_string()),
124 vec![name.to_string()],
125 );
126}
127
128pub fn register_texture(world: &mut World, name: &str, width: u32, height: u32, rgba: &[u8]) {
133 nightshade::ecs::loading::queue_decoded_texture(
134 world,
135 name.to_string(),
136 rgba.to_vec(),
137 width,
138 height,
139 TextureUsage::Color,
140 SamplerSettings::DEFAULT,
141 );
142 texture_cache_acquire(
143 world.res_mut::<nightshade::render::wgpu::texture_cache::TextureCache>(),
144 TextureOwner::Named(name.to_string()),
145 vec![name.to_string()],
146 );
147}
148
149pub fn set_alpha_blend(world: &mut World, entity: Entity, enabled: bool) {
153 mutate_material(world, entity, move |material| {
154 material.alpha_mode = if enabled {
155 AlphaMode::Blend
156 } else {
157 AlphaMode::Opaque
158 };
159 });
160}
161
162pub fn set_alpha_cutoff(world: &mut World, entity: Entity, cutoff: f32) {
166 mutate_material(world, entity, move |material| {
167 material.alpha_mode = AlphaMode::Mask;
168 material.alpha_cutoff = cutoff;
169 });
170}
171
172pub fn set_double_sided(world: &mut World, entity: Entity, double_sided: bool) {
175 mutate_material(world, entity, move |material| {
176 material.double_sided = double_sided;
177 });
178}
179
180pub fn set_ior(world: &mut World, entity: Entity, ior: f32) {
183 mutate_material(world, entity, move |material| {
184 material.ior = ior;
185 });
186}
187
188pub fn set_transmission(world: &mut World, entity: Entity, factor: f32) {
191 mutate_material(world, entity, move |material| {
192 material.transmission_factor = factor;
193 });
194}
195
196pub fn set_clearcoat(world: &mut World, entity: Entity, factor: f32, roughness: f32) {
199 mutate_material(world, entity, move |material| {
200 material.clearcoat_factor = factor;
201 material.clearcoat_roughness_factor = roughness;
202 });
203}
204
205pub fn set_anisotropy(world: &mut World, entity: Entity, strength: f32, rotation: f32) {
208 mutate_material(world, entity, move |material| {
209 material.anisotropy_strength = strength;
210 material.anisotropy_rotation = rotation;
211 });
212}
213
214pub fn set_uv_transform(
218 world: &mut World,
219 entity: Entity,
220 offset: [f32; 2],
221 scale: [f32; 2],
222 rotation: f32,
223) {
224 mutate_material(world, entity, move |material| {
225 material.base_texture_transform.offset = offset;
226 material.base_texture_transform.scale = scale;
227 material.base_texture_transform.rotation = rotation;
228 });
229}
230
231pub fn set_sheen(world: &mut World, entity: Entity, color: [f32; 3], roughness: f32) {
234 mutate_material(world, entity, move |material| {
235 material.sheen_color_factor = color;
236 material.sheen_roughness_factor = roughness;
237 });
238}
239
240pub fn set_iridescence(world: &mut World, entity: Entity, factor: f32, ior: f32) {
243 mutate_material(world, entity, move |material| {
244 material.iridescence_factor = factor;
245 material.iridescence_ior = ior;
246 });
247}
248
249pub fn set_specular(world: &mut World, entity: Entity, factor: f32, color: [f32; 3]) {
252 mutate_material(world, entity, move |material| {
253 material.specular_factor = factor;
254 material.specular_color_factor = color;
255 });
256}
257
258pub fn set_normal_scale(world: &mut World, entity: Entity, scale: f32) {
261 mutate_material(world, entity, move |material| {
262 material.normal_scale = scale;
263 });
264}
265
266pub fn set_occlusion_strength(world: &mut World, entity: Entity, strength: f32) {
268 mutate_material(world, entity, move |material| {
269 material.occlusion_strength = strength;
270 });
271}
272
273pub fn set_emissive_strength(world: &mut World, entity: Entity, strength: f32) {
276 mutate_material(world, entity, move |material| {
277 material.emissive_strength = strength;
278 });
279}
280
281pub fn set_thickness(world: &mut World, entity: Entity, thickness: f32) {
285 mutate_material(world, entity, move |material| {
286 material.thickness = thickness;
287 });
288}
289
290fn owns_material(material_name: &str, entity: Entity) -> bool {
291 material_name
292 .strip_prefix(crate::runner::MATERIAL_PREFIX)
293 .and_then(|suffix| suffix.parse().ok())
294 == Some(entity.id)
295}
296
297pub(crate) fn owned_color(world: &mut World, entity: Entity) -> Option<[f32; 4]> {
298 let material_ref = world
299 .get::<nightshade::ecs::material::components::MaterialRef>(entity)
300 .cloned()?;
301 if !owns_material(&material_ref.name, entity) {
302 let current = registry_entry_by_name(
303 &world
304 .res::<nightshade::ecs::asset_state::AssetState>()
305 .material_registry
306 .registry,
307 &material_ref.name,
308 )
309 .map(|material| material.base_color)
310 .unwrap_or([1.0, 1.0, 1.0, 1.0]);
311 set_color(world, entity, current);
312 return Some(current);
313 }
314 registry_entry_by_name(
315 &world
316 .res::<nightshade::ecs::asset_state::AssetState>()
317 .material_registry
318 .registry,
319 &material_ref.name,
320 )
321 .map(|material| material.base_color)
322}
323
324fn mutate_material(world: &mut World, entity: Entity, apply: impl FnOnce(&mut Material)) {
325 let Some(material_ref) = world
326 .get::<nightshade::ecs::material::components::MaterialRef>(entity)
327 .cloned()
328 else {
329 return;
330 };
331
332 if owns_material(&material_ref.name, entity) {
333 if !material_registry_mutate(
334 &mut world
335 .res_mut::<nightshade::ecs::asset_state::AssetState>()
336 .material_registry,
337 &material_ref.name,
338 apply,
339 ) {
340 return;
341 }
342 let new_textures: Vec<String> = registry_entry_by_name(
343 &world
344 .res::<nightshade::ecs::asset_state::AssetState>()
345 .material_registry
346 .registry,
347 &material_ref.name,
348 )
349 .map(|material| material.texture_names().map(str::to_string).collect())
350 .unwrap_or_default();
351 texture_cache_acquire(
352 world.res_mut::<nightshade::render::wgpu::texture_cache::TextureCache>(),
353 TextureOwner::EntityMaterial(render_entity(entity)),
354 new_textures,
355 );
356 world
357 .res_mut::<nightshade::render::mesh_state::MeshRenderState>()
358 .mark_material_dirty(render_entity(entity));
359 } else {
360 let mut material = registry_entry_by_name(
361 &world
362 .res::<nightshade::ecs::asset_state::AssetState>()
363 .material_registry
364 .registry,
365 &material_ref.name,
366 )
367 .cloned()
368 .unwrap_or_default();
369 apply(&mut material);
370 let textures: Vec<String> = material.texture_names().map(str::to_string).collect();
371 texture_cache_acquire(
372 world.res_mut::<nightshade::render::wgpu::texture_cache::TextureCache>(),
373 TextureOwner::EntityMaterial(render_entity(entity)),
374 textures,
375 );
376 if let Some((index, _)) = registry_lookup_index(
377 &world
378 .res::<nightshade::ecs::asset_state::AssetState>()
379 .material_registry
380 .registry,
381 &material_ref.name,
382 ) {
383 registry_remove_reference(
384 &mut world
385 .res_mut::<nightshade::ecs::asset_state::AssetState>()
386 .material_registry
387 .registry,
388 index,
389 );
390 }
391 register_material(world, entity, api_material_name(entity), material);
392 }
393}