use crate::ecs::world::World;
use crate::mcp::McpResponse;
fn find_mesh_child(world: &World, pivot: freecs::Entity) -> Option<freecs::Entity> {
use crate::ecs::world::{MATERIAL_REF, PARENT};
if world.core.entity_has_components(pivot, MATERIAL_REF) {
return Some(pivot);
}
world
.core
.query_entities(PARENT | MATERIAL_REF)
.find(|&child| {
world
.core
.get_parent(child)
.map(|p| p.0 == Some(pivot))
.unwrap_or(false)
})
}
fn ensure_unique_material_name(
world: &mut World,
pivot: freecs::Entity,
entity_name: &str,
) -> Result<String, McpResponse> {
use crate::ecs::generational_registry::registry_entry_by_name;
let Some(mesh) = find_mesh_child(world, pivot) else {
return Err(McpResponse::Error(format!(
"Entity '{}' has no mesh with material",
entity_name
)));
};
let Some(material_ref) = world.core.get_material_ref(mesh) else {
return Err(McpResponse::Error(format!(
"Entity '{}' has no material reference",
entity_name
)));
};
let current_material_name = material_ref.name.clone();
let ref_count = world
.resources
.material_registry
.registry
.name_to_index
.get(¤t_material_name)
.map(|&index| {
world
.resources
.material_registry
.registry
.reference_count(index)
})
.unwrap_or(0);
if ref_count > 1 {
let unique_name = entity_name.to_string();
if !world
.resources
.material_registry
.registry
.name_to_index
.contains_key(&unique_name)
{
let new_material = registry_entry_by_name(
&world.resources.material_registry.registry,
¤t_material_name,
)
.cloned()
.unwrap_or_default();
crate::ecs::material::resources::material_registry_insert(
&mut world.resources.material_registry,
unique_name.clone(),
new_material,
);
}
if let Some(&old_index) = world
.resources
.material_registry
.registry
.name_to_index
.get(¤t_material_name)
{
world
.resources
.material_registry
.registry
.remove_reference(old_index);
}
if let Some(&new_index) = world
.resources
.material_registry
.registry
.name_to_index
.get(&unique_name)
{
world
.resources
.material_registry
.registry
.add_reference(new_index);
}
world.core.set_material_ref(
mesh,
crate::ecs::material::components::MaterialRef::new(unique_name.clone()),
);
Ok(unique_name)
} else {
Ok(current_material_name)
}
}
pub(crate) fn mcp_set_material_color(
world: &mut World,
request: crate::mcp::SetMaterialColorRequest,
) -> Result<McpResponse, McpResponse> {
use crate::ecs::generational_registry::registry_entry_by_name_mut;
let pivot = super::resolve_entity(world, &request.name)?;
let target_material_name = ensure_unique_material_name(world, pivot, &request.name)?;
let Some(material) = registry_entry_by_name_mut(
&mut world.resources.material_registry.registry,
&target_material_name,
) else {
return Err(McpResponse::Error(format!(
"Material '{}' not found for entity '{}'",
target_material_name, request.name
)));
};
material.base_color = [
request.color[0],
request.color[1],
request.color[2],
request.color[3],
];
Ok(McpResponse::Success(format!(
"Set material color of '{}' to {:?}",
request.name, request.color
)))
}
pub(crate) fn mcp_set_emissive(
world: &mut World,
request: crate::mcp::SetEmissiveRequest,
) -> Result<McpResponse, McpResponse> {
use crate::ecs::generational_registry::registry_entry_by_name_mut;
let pivot = super::resolve_entity(world, &request.name)?;
let target_material_name = ensure_unique_material_name(world, pivot, &request.name)?;
let Some(material) = registry_entry_by_name_mut(
&mut world.resources.material_registry.registry,
&target_material_name,
) else {
return Err(McpResponse::Error(format!(
"Material '{}' not found for entity '{}'",
target_material_name, request.name
)));
};
material.emissive_factor = request.emissive;
Ok(McpResponse::Success(format!(
"Set emissive of '{}' to {:?}",
request.name, request.emissive
)))
}
pub(crate) fn mcp_set_material(
world: &mut World,
request: crate::mcp::SetMaterialRequest,
) -> Result<McpResponse, McpResponse> {
let crate::mcp::SetMaterialRequest {
name,
base_color,
emissive,
roughness,
metallic,
alpha_mode,
alpha_cutoff,
} = request;
use crate::ecs::generational_registry::registry_entry_by_name_mut;
use crate::ecs::material::components::AlphaMode;
let entity = super::resolve_entity(world, &name)?;
let Some(mat_ref) = world.core.get_material_ref(entity) else {
return Err(McpResponse::Error(format!(
"Entity '{}' has no material",
name
)));
};
let material_name = mat_ref.name.clone();
let Some(material) = registry_entry_by_name_mut(
&mut world.resources.material_registry.registry,
&material_name,
) else {
return Err(McpResponse::Error(format!(
"Material '{}' not found for '{}'",
material_name, name
)));
};
if let Some(color) = base_color {
material.base_color = color;
}
if let Some(em) = emissive {
material.emissive_factor = em;
}
if let Some(r) = roughness {
material.roughness = r;
}
if let Some(m) = metallic {
material.metallic = m;
}
if let Some(mode) = alpha_mode {
material.alpha_mode = match mode.to_lowercase().as_str() {
"opaque" => AlphaMode::Opaque,
"mask" => AlphaMode::Mask,
"blend" => AlphaMode::Blend,
_ => material.alpha_mode,
};
}
if let Some(cutoff) = alpha_cutoff {
material.alpha_cutoff = cutoff;
}
Ok(McpResponse::Success(format!(
"Material updated for '{}'",
name
)))
}
pub(crate) fn mcp_set_casts_shadow(
world: &mut World,
request: crate::mcp::SetCastsShadowRequest,
) -> Result<McpResponse, McpResponse> {
use crate::ecs::shadow::components::CastsShadow;
let entity = super::resolve_entity(world, &request.name)?;
if request.casts_shadow {
world.core.set_casts_shadow(entity, CastsShadow);
} else {
world.core.remove_casts_shadow(entity);
}
Ok(McpResponse::Success(format!(
"Shadow casting set to {} for '{}'",
request.casts_shadow, request.name
)))
}