use crate::ecs::world::World;
use crate::mcp::McpResponse;
fn spawn_mesh_by_type(world: &mut World, mesh_type: &str) -> Option<freecs::Entity> {
match mesh_type.to_lowercase().as_str() {
"cube" => Some(crate::ecs::world::spawn_cube_at(
world,
nalgebra_glm::Vec3::zeros(),
)),
"sphere" => Some(crate::ecs::world::spawn_sphere_at(
world,
nalgebra_glm::Vec3::zeros(),
)),
"cylinder" => Some(crate::ecs::world::spawn_cylinder_at(
world,
nalgebra_glm::Vec3::zeros(),
)),
"cone" => Some(crate::ecs::world::spawn_cone_at(
world,
nalgebra_glm::Vec3::zeros(),
)),
"plane" => Some(crate::ecs::world::spawn_plane_at(
world,
nalgebra_glm::Vec3::zeros(),
)),
"torus" => Some(crate::ecs::world::spawn_torus_at(
world,
nalgebra_glm::Vec3::zeros(),
)),
_ => None,
}
}
pub(crate) fn mcp_spawn_entity(
world: &mut World,
request: crate::mcp::SpawnEntityRequest,
) -> McpResponse {
let crate::mcp::SpawnEntityRequest {
name,
mesh,
position,
scale,
color,
emissive,
parent: parent_name,
alpha: alpha_mode,
} = request;
let scale = scale.unwrap_or([1.0, 1.0, 1.0]);
use crate::ecs::transform::components::Parent;
use crate::ecs::world::{GLOBAL_TRANSFORM, LOCAL_TRANSFORM, NAME, PARENT};
if world.resources.entity_names.contains_key(&name) {
return McpResponse::Error(format!("Entity '{}' already exists", name));
}
let parent_entity = if let Some(ref pname) = parent_name {
let Some(&parent) = world.resources.entity_names.get(pname) else {
return McpResponse::Error(format!("Parent entity '{}' not found", pname));
};
Some(parent)
} else {
None
};
let pivot = world.spawn_entities(LOCAL_TRANSFORM | GLOBAL_TRANSFORM | PARENT, 1)[0];
if let Some(transform) = world.get_local_transform_mut(pivot) {
transform.translation = nalgebra_glm::Vec3::new(position[0], position[1], position[2]);
}
crate::ecs::transform::commands::mark_local_transform_dirty(world, pivot);
if let Some(parent) = parent_entity {
world.update_parent(pivot, Some(Parent(Some(parent))));
}
let Some(entity) = spawn_mesh_by_type(world, &mesh) else {
crate::ecs::world::commands::despawn_recursive_immediate(world, pivot);
return McpResponse::Error(format!(
"Unknown mesh type '{}'. Valid types: Cube, Sphere, Cylinder, Cone, Plane, Torus",
mesh
));
};
world.update_parent(entity, Some(Parent(Some(pivot))));
if scale != [1.0, 1.0, 1.0] {
if let Some(transform) = world.get_local_transform_mut(pivot) {
transform.scale = nalgebra_glm::Vec3::new(scale[0], scale[1], scale[2]);
}
crate::ecs::transform::commands::mark_local_transform_dirty(world, pivot);
}
let material_name = name.clone();
let mut material = crate::ecs::material::components::Material::default();
if let Some(c) = color {
material.base_color = c;
}
if let Some(e) = emissive {
material.emissive_factor = e;
}
if let Some(ref alpha) = alpha_mode {
material.alpha_mode = match alpha.to_lowercase().as_str() {
"opaque" => crate::ecs::material::components::AlphaMode::Opaque,
"mask" => crate::ecs::material::components::AlphaMode::Mask,
"blend" => crate::ecs::material::components::AlphaMode::Blend,
_ => crate::ecs::material::components::AlphaMode::Opaque,
};
}
crate::ecs::material::resources::material_registry_insert(
&mut world.resources.material_registry,
material_name.clone(),
material,
);
if let Some(&index) = world
.resources
.material_registry
.registry
.name_to_index
.get(&material_name)
{
world
.resources
.material_registry
.registry
.add_reference(index);
}
world.set_material_ref(
entity,
crate::ecs::material::components::MaterialRef::new(material_name),
);
world.add_components(pivot, NAME);
world.set_name(pivot, crate::ecs::name::components::Name(name.clone()));
world.resources.entity_names.insert(name.clone(), pivot);
let parent_info = parent_name
.map(|p| format!(" (child of '{}')", p))
.unwrap_or_default();
McpResponse::Success(format!(
"Spawned entity '{}' with {} mesh at {:?}{}",
name, mesh, position, parent_info
))
}
pub(crate) fn mcp_despawn_entity(world: &mut World, name: String) -> McpResponse {
if let Some(entity) = world.resources.entity_names.remove(&name) {
crate::ecs::world::commands::despawn_recursive_immediate(world, entity);
McpResponse::Success(format!("Despawned entity '{}'", name))
} else {
McpResponse::Error(format!("Entity '{}' not found", name))
}
}
pub(crate) fn mcp_query_entity(
world: &World,
request: crate::mcp::QueryEntityRequest,
) -> Result<McpResponse, McpResponse> {
let entity = super::resolve_entity(world, &request.name)?;
let Some(transform) = world.get_local_transform(entity) else {
return Err(McpResponse::Error(format!(
"Entity '{}' has no transform",
request.name
)));
};
Ok(McpResponse::EntityInfo {
name: request.name,
position: [
transform.translation.x,
transform.translation.y,
transform.translation.z,
],
rotation: [
transform.rotation.i,
transform.rotation.j,
transform.rotation.k,
transform.rotation.w,
],
scale: [transform.scale.x, transform.scale.y, transform.scale.z],
})
}
pub(crate) fn mcp_clear_scene(world: &mut World) -> McpResponse {
let entities_to_despawn: Vec<_> = world.resources.entity_names.values().copied().collect();
let count = entities_to_despawn.len();
for entity in entities_to_despawn {
crate::ecs::world::commands::despawn_recursive_immediate(world, entity);
}
world.resources.entity_names.clear();
McpResponse::Success(format!("Cleared {} entities from scene", count))
}
pub(crate) fn mcp_set_visibility(
world: &mut World,
request: crate::mcp::SetVisibilityRequest,
) -> Result<McpResponse, McpResponse> {
use crate::ecs::visibility::components::Visibility;
let entity = super::resolve_entity(world, &request.name)?;
world.set_visibility(
entity,
Visibility {
visible: request.visible,
},
);
Ok(McpResponse::Success(format!(
"Visibility set to {} for '{}'",
request.visible, request.name
)))
}