use super::helpers::material_properties_ui;
use super::{ComponentInspector, InspectorContext};
use crate::ecs::generational_registry::{registry_entry_by_name, registry_entry_by_name_mut};
use crate::mosaic::ToastKind;
use crate::prelude::*;
#[derive(Default)]
pub struct MaterialInspector {
editing: bool,
}
fn color_swatch(ui: &mut egui::Ui, color: egui::Color32) {
let (rect, _) = ui.allocate_exact_size(egui::vec2(48.0, 18.0), egui::Sense::hover());
ui.painter().rect_filled(rect, 2.0, color);
ui.painter().rect_stroke(
rect,
2.0,
egui::Stroke::new(1.0, egui::Color32::GRAY),
egui::StrokeKind::Inside,
);
}
fn material_display_section(
ui: &mut egui::Ui,
material: &Material,
material_name: &str,
context: &mut InspectorContext,
ref_count: usize,
editing: &mut bool,
) {
ui.horizontal(|ui| {
ui.label("Base Color:");
let color = egui::Color32::from_rgba_unmultiplied(
(material.base_color[0] * 255.0) as u8,
(material.base_color[1] * 255.0) as u8,
(material.base_color[2] * 255.0) as u8,
(material.base_color[3] * 255.0) as u8,
);
color_swatch(ui, color);
});
ui.horizontal(|ui| {
ui.label("Alpha Mode:");
ui.label(match material.alpha_mode {
AlphaMode::Opaque => "Opaque",
AlphaMode::Mask => "Mask",
AlphaMode::Blend => "Blend",
});
});
if material.alpha_mode == AlphaMode::Mask {
ui.horizontal(|ui| {
ui.label("Alpha Cutoff:");
ui.label(format!("{:.2}", material.alpha_cutoff));
});
}
ui.horizontal(|ui| {
ui.label("Roughness:");
ui.label(format!("{:.2}", material.roughness));
ui.label("Metallic:");
ui.label(format!("{:.2}", material.metallic));
});
if material.unlit {
ui.label("Unlit: Yes");
}
if material.emissive_factor[0] > 0.0
|| material.emissive_factor[1] > 0.0
|| material.emissive_factor[2] > 0.0
{
ui.horizontal(|ui| {
ui.label("Emissive:");
let color = egui::Color32::from_rgb(
(material.emissive_factor[0] * 255.0) as u8,
(material.emissive_factor[1] * 255.0) as u8,
(material.emissive_factor[2] * 255.0) as u8,
);
color_swatch(ui, color);
});
}
if let Some(texture) = &material.base_texture {
ui.horizontal(|ui| {
ui.label("Base Texture:");
ui.label(texture);
});
}
if let Some(texture) = &material.emissive_texture {
ui.horizontal(|ui| {
ui.label("Emissive Texture:");
ui.label(texture);
});
}
ui.separator();
ui.small(format!("Used by {} entities", ref_count));
ui.horizontal(|ui| {
if ui.button("Lookup Material").clicked() {
context.actions.push(super::InspectorAction::LookupMaterial(
material_name.to_string(),
));
}
if ui.button("Edit Material").clicked() {
*editing = true;
}
});
}
impl ComponentInspector for MaterialInspector {
fn name(&self) -> &str {
"Material"
}
fn has_component(&self, world: &World, entity: Entity) -> bool {
world.get_material_ref(entity).is_some()
}
fn add_component(&self, world: &mut World, entity: Entity) {
if let Some(&index) = world
.resources
.material_registry
.registry
.name_to_index
.get("Default")
{
world
.resources
.material_registry
.registry
.add_reference(index);
}
world.set_material_ref(entity, MaterialRef::new("Default".to_string()));
}
fn remove_component(&self, world: &mut World, entity: Entity) {
let material_name = world.get_material_ref(entity).map(|r| r.name.clone());
if let Some(name) = material_name
&& let Some(&index) = world
.resources
.material_registry
.registry
.name_to_index
.get(&name)
{
world
.resources
.material_registry
.registry
.remove_reference(index);
}
world.remove_material_ref(entity);
}
fn ui(
&mut self,
world: &mut World,
entity: Entity,
ui: &mut egui::Ui,
context: &mut InspectorContext,
) {
ui.label("Material");
let material_name = if let Some(material_ref) = world.get_material_ref(entity) {
material_ref.name.clone()
} else {
return;
};
let available_materials: Vec<String> = world
.resources
.material_registry
.registry
.index_to_name
.iter()
.filter_map(|opt| opt.clone())
.collect();
let mut selected_name = material_name.clone();
ui.horizontal(|ui| {
ui.label("Material:");
egui::ComboBox::from_id_salt("material_selector")
.selected_text(&selected_name)
.show_ui(ui, |ui| {
for name in &available_materials {
ui.selectable_value(&mut selected_name, name.clone(), name);
}
});
});
if selected_name != material_name {
if let Some(&index) = world
.resources
.material_registry
.registry
.name_to_index
.get(&material_name)
{
world
.resources
.material_registry
.registry
.remove_reference(index);
}
if let Some(&index) = world
.resources
.material_registry
.registry
.name_to_index
.get(&selected_name)
{
world
.resources
.material_registry
.registry
.add_reference(index);
}
world.set_material_ref(entity, MaterialRef::new(selected_name.clone()));
self.editing = false;
return;
}
ui.separator();
if self.editing {
if let Some(material) = registry_entry_by_name_mut(
&mut world.resources.material_registry.registry,
&material_name,
) {
let changed = material_properties_ui(ui, material, "inspector");
if changed {
context.pending_notifications.push((
ToastKind::Success,
format!("Material '{}' modified", material_name),
));
}
ui.separator();
if ui.button("Done Editing").clicked() {
self.editing = false;
}
}
} else if let Some(material) =
registry_entry_by_name(&world.resources.material_registry.registry, &material_name)
{
let ref_count = world
.resources
.material_registry
.registry
.name_to_index
.get(&material_name)
.map(|&index| {
world.resources.material_registry.registry.reference_counts[index as usize]
})
.unwrap_or(0);
material_display_section(
ui,
material,
&material_name,
context,
ref_count,
&mut self.editing,
);
}
}
}