"""
Import/Export tools for retopology workflows.
Provides safe, parameterized tools for importing reference meshes and exporting assets.
"""
from typing import Dict, Any, List
from mcp.server.fastmcp import Context
import logging
logger = logging.getLogger("BlenderMCPServer")
# ============================================================================
# IMPORT REFERENCE
# ============================================================================
def import_reference(
ctx: Context,
blender_connection,
file_paths: List[str],
collection_name: str = "_ref"
) -> str:
"""
Import reference meshes into a dedicated collection.
Imports one or more mesh files (OBJ, FBX, STL, PLY, etc.) and places
them in a reference collection for use as retopology targets.
Automatically creates the collection if it doesn't exist.
Parameters:
- file_paths: List of absolute file paths to import
- collection_name: Name of collection to organize references (default: '_ref')
Returns list of imported object names.
"""
try:
if not file_paths:
return "Error: file_paths list cannot be empty."
# Validate file extensions
valid_extensions = ['.obj', '.fbx', '.stl', '.ply', '.dae', '.abc', '.usd']
for path in file_paths:
ext = path.lower()
if not any(ext.endswith(e) for e in valid_extensions):
return f"Error: Unsupported file format for '{path}'. Supported: {valid_extensions}"
result = blender_connection.send_command("import_reference", {
"file_paths": file_paths,
"collection_name": collection_name
})
if "error" in result:
return f"Error: {result['error']}"
output = "Reference Meshes Imported!\n\n"
output += f"Collection: {collection_name}\n"
output += f"Files Imported: {len(file_paths)}\n\n"
if 'imported_objects' in result:
output += "Imported Objects:\n"
for obj in result['imported_objects']:
output += f" - {obj}\n"
if 'collection_created' in result and result['collection_created']:
output += f"\nā¹ Created new collection '{collection_name}'\n"
return output
except Exception as e:
logger.error(f"Error importing reference: {str(e)}")
return f"Error importing reference: {str(e)}"
# ============================================================================
# EXPORT ASSET
# ============================================================================
def export_asset(
ctx: Context,
blender_connection,
object_names: List[str],
file_path: str,
file_format: str = "GLTF",
apply_modifiers: bool = True,
scale: float = 1.0,
forward_axis: str = "Y",
up_axis: str = "Z"
) -> str:
"""
Export finalized meshes to various game-ready formats.
Exports selected objects to a file with options for applying modifiers,
scaling, and axis alignment. Supports multiple industry-standard formats.
Parameters:
- object_names: List of object names to export
- file_path: Absolute path for the exported file (including extension)
- file_format: Export format - 'GLTF', 'FBX', 'OBJ', 'STL' (default: 'GLTF')
- apply_modifiers: Apply modifiers before export (default: true)
- scale: Export scale multiplier (default: 1.0)
- forward_axis: Forward axis - 'X', 'Y', 'Z', '-X', '-Y', '-Z' (default: 'Y')
- up_axis: Up axis - 'X', 'Y', 'Z', '-X', '-Y', '-Z' (default: 'Z')
Returns export confirmation with file path.
"""
try:
if not object_names:
return "Error: object_names list cannot be empty."
valid_formats = ['GLTF', 'FBX', 'OBJ', 'STL', 'USD']
if file_format not in valid_formats:
return f"Error: Invalid file_format '{file_format}'. Must be one of {valid_formats}."
if scale <= 0:
return f"Error: scale must be greater than 0."
valid_axes = ['X', 'Y', 'Z', '-X', '-Y', '-Z']
if forward_axis not in valid_axes:
return f"Error: Invalid forward_axis '{forward_axis}'. Must be one of {valid_axes}."
if up_axis not in valid_axes:
return f"Error: Invalid up_axis '{up_axis}'. Must be one of {valid_axes}."
result = blender_connection.send_command("export_asset", {
"object_names": object_names,
"file_path": file_path,
"file_format": file_format,
"apply_modifiers": apply_modifiers,
"scale": scale,
"forward_axis": forward_axis,
"up_axis": up_axis
})
if "error" in result:
return f"Error: {result['error']}"
output = "Asset Exported Successfully!\n\n"
output += f"Format: {file_format}\n"
output += f"Objects Exported: {len(object_names)}\n"
for obj in object_names:
output += f" - {obj}\n"
output += f"\nApply Modifiers: {apply_modifiers}\n"
output += f"Scale: {scale}\n"
output += f"Forward Axis: {forward_axis}\n"
output += f"Up Axis: {up_axis}\n\n"
if 'file_path' in result:
output += f"š Saved to: {result['file_path']}\n"
if 'file_size_mb' in result:
output += f"š¾ File Size: {result['file_size_mb']:.2f} MB\n"
return output
except Exception as e:
logger.error(f"Error exporting asset: {str(e)}")
return f"Error exporting asset: {str(e)}"