Files
2026-07-13 12:49:17 +08:00

109 lines
4.5 KiB
C#

using System;
using System.IO;
using MCPForUnity.Editor.Helpers;
using MCPForUnity.Editor.Security;
using MCPForUnity.Editor.Services.AssetGen;
using MCPForUnity.Editor.Services.AssetGen.Import;
using Newtonsoft.Json.Linq;
using UnityEditor;
namespace MCPForUnity.Editor.Tools.AssetGen
{
/// <summary>
/// Import a local 3D model file (already on disk — e.g. exported from Blender/Maya) into the
/// Unity project. DCC-agnostic and key-free: the file is copied under Assets/ and run through
/// the shared ModelImportPipeline (glTFast/FBX/OBJ/zip handling, scale-normalize, material
/// settings). Placement into the scene is the caller's job (kept single-purpose).
/// </summary>
[McpForUnityTool("import_model_file", AutoRegister = false, Group = "asset_gen")]
public static class ImportModelFile
{
private static readonly string[] SupportedExt = { ".fbx", ".obj", ".glb", ".gltf", ".zip" };
public static object HandleCommand(JObject @params)
{
if (@params == null) return new ErrorResponse("Parameters cannot be null.");
var p = new ToolParams(@params);
try
{
string source = p.Get("sourcePath");
if (string.IsNullOrWhiteSpace(source))
return new ErrorResponse("'source_path' is required.");
string srcAbs = ResolveSource(source);
if (!File.Exists(srcAbs))
return new ErrorResponse($"Source file not found: {source}");
string ext = Path.GetExtension(srcAbs).ToLowerInvariant();
if (Array.IndexOf(SupportedExt, ext) < 0)
return new ErrorResponse(
$"Unsupported model extension '{ext}'. Supported: .fbx, .obj, .glb, .gltf, .zip.");
string baseName = p.Get("name");
if (string.IsNullOrWhiteSpace(baseName))
baseName = Path.GetFileNameWithoutExtension(srcAbs);
string destRel = StageUnderAssets(srcAbs, baseName, ext, p.Get("outputFolder"));
AssetDatabase.Refresh();
var job = new AssetGenJob
{
TargetSize = p.GetFloat("targetSize", 1f) ?? 1f,
AnimationType = p.Get("animationType"),
};
AssetGenJob result = ModelImportPipeline.ImportInto(job, destRel);
if (result == null || result.State == AssetGenJobState.Failed)
return new ErrorResponse(result?.Error ?? "Import failed.");
return new SuccessResponse(
$"Imported model: {result.AssetPath}",
new { asset_path = result.AssetPath, asset_guid = result.AssetGuid });
}
catch (Exception e)
{
return new ErrorResponse(SecretRedactor.Scrub(e.Message));
}
}
private static string ResolveSource(string source)
{
string s = source.Replace('\\', '/');
if (s == "Assets" || s.StartsWith("Assets/")) return AssetGenPaths.ToAbsolute(s);
return s; // absolute path on disk
}
private static string StageUnderAssets(string srcAbs, string baseName, string ext, string outputFolder)
{
string root = !string.IsNullOrWhiteSpace(outputFolder)
? outputFolder
: AssetGenPrefs.OutputRoot + "/Imported";
if (!AssetGenPaths.TryGetAssetsFolder(root, out root))
{
if (!string.IsNullOrWhiteSpace(outputFolder))
throw new ArgumentException("'output_folder' must resolve under the project's Assets folder.");
root = AssetGenPrefs.DefaultOutputRoot + "/Imported";
}
string absRoot = AssetGenPaths.ToAbsolute(root);
Directory.CreateDirectory(absRoot);
string safe = SanitizeName(baseName);
string fileName = safe + ext;
string abs = Path.Combine(absRoot, fileName);
int n = 1;
while (File.Exists(abs)) { fileName = safe + "_" + n++ + ext; abs = Path.Combine(absRoot, fileName); }
File.Copy(srcAbs, abs);
return (root.TrimEnd('/') + "/" + fileName).Replace('\\', '/');
}
private static string SanitizeName(string raw)
{
if (string.IsNullOrWhiteSpace(raw)) return "model";
foreach (char c in Path.GetInvalidFileNameChars()) raw = raw.Replace(c, '_');
return raw.Trim();
}
}
}