升级XR插件版本
This commit is contained in:
568
Packages/MCPForUnity/Editor/Tools/Vfx/VfxGraphAssets.cs
Normal file
568
Packages/MCPForUnity/Editor/Tools/Vfx/VfxGraphAssets.cs
Normal file
@@ -0,0 +1,568 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_VFX_GRAPH
|
||||
using UnityEngine.VFX;
|
||||
#endif
|
||||
|
||||
namespace MCPForUnity.Editor.Tools.Vfx
|
||||
{
|
||||
/// <summary>
|
||||
/// Asset management operations for VFX Graph.
|
||||
/// Handles creating, assigning, and listing VFX assets.
|
||||
/// Requires com.unity.visualeffectgraph package and UNITY_VFX_GRAPH symbol.
|
||||
/// </summary>
|
||||
internal static class VfxGraphAssets
|
||||
{
|
||||
#if !UNITY_VFX_GRAPH
|
||||
public static object CreateAsset(JObject @params)
|
||||
{
|
||||
return new { success = false, message = "VFX Graph package (com.unity.visualeffectgraph) not installed" };
|
||||
}
|
||||
|
||||
public static object AssignAsset(JObject @params)
|
||||
{
|
||||
return new { success = false, message = "VFX Graph package (com.unity.visualeffectgraph) not installed" };
|
||||
}
|
||||
|
||||
public static object ListTemplates(JObject @params)
|
||||
{
|
||||
return new { success = false, message = "VFX Graph package (com.unity.visualeffectgraph) not installed" };
|
||||
}
|
||||
|
||||
public static object ListAssets(JObject @params)
|
||||
{
|
||||
return new { success = false, message = "VFX Graph package (com.unity.visualeffectgraph) not installed" };
|
||||
}
|
||||
#else
|
||||
private static readonly string[] SupportedVfxGraphVersions = { "12.1" };
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new VFX Graph asset file from a template.
|
||||
/// </summary>
|
||||
public static object CreateAsset(JObject @params)
|
||||
{
|
||||
string assetName = @params["assetName"]?.ToString();
|
||||
string folderPath = @params["folderPath"]?.ToString() ?? "Assets/VFX";
|
||||
string template = @params["template"]?.ToString() ?? "empty";
|
||||
|
||||
if (string.IsNullOrEmpty(assetName))
|
||||
{
|
||||
return new { success = false, message = "assetName is required" };
|
||||
}
|
||||
|
||||
string versionError = ValidateVfxGraphVersion();
|
||||
if (!string.IsNullOrEmpty(versionError))
|
||||
{
|
||||
return new { success = false, message = versionError };
|
||||
}
|
||||
|
||||
// Ensure folder exists
|
||||
if (!AssetDatabase.IsValidFolder(folderPath))
|
||||
{
|
||||
string[] folders = folderPath.Split('/');
|
||||
string currentPath = folders[0];
|
||||
for (int i = 1; i < folders.Length; i++)
|
||||
{
|
||||
string newPath = currentPath + "/" + folders[i];
|
||||
if (!AssetDatabase.IsValidFolder(newPath))
|
||||
{
|
||||
AssetDatabase.CreateFolder(currentPath, folders[i]);
|
||||
}
|
||||
currentPath = newPath;
|
||||
}
|
||||
}
|
||||
|
||||
string assetPath = $"{folderPath}/{assetName}.vfx";
|
||||
|
||||
// Check if asset already exists
|
||||
if (AssetDatabase.LoadAssetAtPath<VisualEffectAsset>(assetPath) != null)
|
||||
{
|
||||
bool overwrite = @params["overwrite"]?.ToObject<bool>() ?? false;
|
||||
if (!overwrite)
|
||||
{
|
||||
return new { success = false, message = $"Asset already exists at {assetPath}. Set overwrite=true to replace." };
|
||||
}
|
||||
AssetDatabase.DeleteAsset(assetPath);
|
||||
}
|
||||
|
||||
// Find template asset and copy it
|
||||
string templatePath = FindTemplate(template);
|
||||
string templateAssetPath = TryGetAssetPathFromFileSystem(templatePath);
|
||||
VisualEffectAsset newAsset = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(templateAssetPath))
|
||||
{
|
||||
// Copy the asset to create a new VFX Graph asset
|
||||
if (!AssetDatabase.CopyAsset(templateAssetPath, assetPath))
|
||||
{
|
||||
return new { success = false, message = $"Failed to copy VFX template from {templateAssetPath}" };
|
||||
}
|
||||
AssetDatabase.Refresh();
|
||||
newAsset = AssetDatabase.LoadAssetAtPath<VisualEffectAsset>(assetPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new { success = false, message = "VFX template not found. Add a .vfx template asset or install VFX Graph templates." };
|
||||
}
|
||||
|
||||
if (newAsset == null)
|
||||
{
|
||||
return new { success = false, message = "Failed to create VFX asset. Try using a template from list_templates." };
|
||||
}
|
||||
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
message = $"Created VFX asset: {assetPath}",
|
||||
data = new
|
||||
{
|
||||
assetPath = assetPath,
|
||||
assetName = newAsset.name,
|
||||
template = template
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds VFX template path by name.
|
||||
/// </summary>
|
||||
private static string FindTemplate(string templateName)
|
||||
{
|
||||
// Get the actual filesystem path for the VFX Graph package using PackageManager API
|
||||
var packageInfo = UnityEditor.PackageManager.PackageInfo.FindForAssetPath("Packages/com.unity.visualeffectgraph");
|
||||
|
||||
var searchPaths = new List<string>();
|
||||
|
||||
if (packageInfo != null)
|
||||
{
|
||||
// Use the resolved path from PackageManager (handles Library/PackageCache paths)
|
||||
searchPaths.Add(System.IO.Path.Combine(packageInfo.resolvedPath, "Editor/Templates"));
|
||||
searchPaths.Add(System.IO.Path.Combine(packageInfo.resolvedPath, "Samples"));
|
||||
}
|
||||
|
||||
// Also search project-local paths
|
||||
searchPaths.Add("Assets/VFX/Templates");
|
||||
|
||||
string[] templatePatterns = new[]
|
||||
{
|
||||
$"{templateName}.vfx",
|
||||
$"VFX{templateName}.vfx",
|
||||
$"Simple{templateName}.vfx",
|
||||
$"{templateName}VFX.vfx"
|
||||
};
|
||||
|
||||
foreach (string basePath in searchPaths)
|
||||
{
|
||||
string searchRoot = basePath;
|
||||
if (basePath.StartsWith("Assets/"))
|
||||
{
|
||||
searchRoot = System.IO.Path.Combine(UnityEngine.Application.dataPath, basePath.Substring("Assets/".Length));
|
||||
}
|
||||
|
||||
if (!System.IO.Directory.Exists(searchRoot))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (string pattern in templatePatterns)
|
||||
{
|
||||
string[] files = System.IO.Directory.GetFiles(searchRoot, pattern, System.IO.SearchOption.AllDirectories);
|
||||
if (files.Length > 0)
|
||||
{
|
||||
return files[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Also search by partial match
|
||||
try
|
||||
{
|
||||
string[] allVfxFiles = System.IO.Directory.GetFiles(searchRoot, "*.vfx", System.IO.SearchOption.AllDirectories);
|
||||
foreach (string file in allVfxFiles)
|
||||
{
|
||||
if (System.IO.Path.GetFileNameWithoutExtension(file).ToLower().Contains(templateName.ToLower()))
|
||||
{
|
||||
return file;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"Failed to search VFX templates under '{searchRoot}': {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// Search in project assets
|
||||
string[] guids = AssetDatabase.FindAssets("t:VisualEffectAsset " + templateName);
|
||||
if (guids.Length > 0)
|
||||
{
|
||||
string assetPath = AssetDatabase.GUIDToAssetPath(guids[0]);
|
||||
// Convert asset path (e.g., "Assets/...") to absolute filesystem path
|
||||
if (!string.IsNullOrEmpty(assetPath) && assetPath.StartsWith("Assets/"))
|
||||
{
|
||||
return System.IO.Path.Combine(UnityEngine.Application.dataPath, assetPath.Substring("Assets/".Length));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(assetPath) && assetPath.StartsWith("Packages/"))
|
||||
{
|
||||
var info = UnityEditor.PackageManager.PackageInfo.FindForAssetPath(assetPath);
|
||||
if (info != null)
|
||||
{
|
||||
string relPath = assetPath.Substring(("Packages/" + info.name + "/").Length);
|
||||
return System.IO.Path.Combine(info.resolvedPath, relPath);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns a VFX asset to a VisualEffect component.
|
||||
/// </summary>
|
||||
public static object AssignAsset(JObject @params)
|
||||
{
|
||||
VisualEffect vfx = VfxGraphCommon.FindVisualEffect(@params);
|
||||
if (vfx == null)
|
||||
{
|
||||
return new { success = false, message = "VisualEffect component not found" };
|
||||
}
|
||||
|
||||
string assetPath = @params["assetPath"]?.ToString();
|
||||
if (string.IsNullOrEmpty(assetPath))
|
||||
{
|
||||
return new { success = false, message = "assetPath is required" };
|
||||
}
|
||||
|
||||
// Validate and normalize path
|
||||
// Reject absolute paths, parent directory traversal, and backslashes
|
||||
if (assetPath.Contains("\\") || assetPath.Contains("..") || System.IO.Path.IsPathRooted(assetPath))
|
||||
{
|
||||
return new { success = false, message = "Invalid assetPath: traversal and absolute paths are not allowed" };
|
||||
}
|
||||
|
||||
if (assetPath.StartsWith("Packages/"))
|
||||
{
|
||||
return new { success = false, message = "Invalid assetPath: VFX assets must live under Assets/." };
|
||||
}
|
||||
|
||||
if (!assetPath.StartsWith("Assets/"))
|
||||
{
|
||||
assetPath = "Assets/" + assetPath;
|
||||
}
|
||||
if (!assetPath.EndsWith(".vfx"))
|
||||
{
|
||||
assetPath += ".vfx";
|
||||
}
|
||||
|
||||
// Verify the normalized path doesn't escape the project
|
||||
string fullPath = System.IO.Path.Combine(UnityEngine.Application.dataPath, assetPath.Substring("Assets/".Length));
|
||||
string canonicalProjectRoot = System.IO.Path.GetFullPath(UnityEngine.Application.dataPath);
|
||||
string canonicalAssetPath = System.IO.Path.GetFullPath(fullPath);
|
||||
if (!canonicalAssetPath.StartsWith(canonicalProjectRoot + System.IO.Path.DirectorySeparatorChar) &&
|
||||
canonicalAssetPath != canonicalProjectRoot)
|
||||
{
|
||||
return new { success = false, message = "Invalid assetPath: would escape project directory" };
|
||||
}
|
||||
|
||||
var asset = AssetDatabase.LoadAssetAtPath<VisualEffectAsset>(assetPath);
|
||||
if (asset == null)
|
||||
{
|
||||
// Try searching by name
|
||||
string searchName = System.IO.Path.GetFileNameWithoutExtension(assetPath);
|
||||
string[] guids = AssetDatabase.FindAssets($"t:VisualEffectAsset {searchName}");
|
||||
if (guids.Length > 0)
|
||||
{
|
||||
assetPath = AssetDatabase.GUIDToAssetPath(guids[0]);
|
||||
asset = AssetDatabase.LoadAssetAtPath<VisualEffectAsset>(assetPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (asset == null)
|
||||
{
|
||||
return new { success = false, message = $"VFX asset not found: {assetPath}" };
|
||||
}
|
||||
|
||||
Undo.RecordObject(vfx, "Assign VFX Asset");
|
||||
vfx.visualEffectAsset = asset;
|
||||
EditorUtility.SetDirty(vfx);
|
||||
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
message = $"Assigned VFX asset '{asset.name}' to {vfx.gameObject.name}",
|
||||
data = new
|
||||
{
|
||||
gameObject = vfx.gameObject.name,
|
||||
assetName = asset.name,
|
||||
assetPath = assetPath
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists available VFX templates.
|
||||
/// </summary>
|
||||
public static object ListTemplates(JObject @params)
|
||||
{
|
||||
var templates = new List<object>();
|
||||
var seenPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// Get the actual filesystem path for the VFX Graph package using PackageManager API
|
||||
var packageInfo = UnityEditor.PackageManager.PackageInfo.FindForAssetPath("Packages/com.unity.visualeffectgraph");
|
||||
|
||||
var searchPaths = new List<string>();
|
||||
|
||||
if (packageInfo != null)
|
||||
{
|
||||
// Use the resolved path from PackageManager (handles Library/PackageCache paths)
|
||||
searchPaths.Add(System.IO.Path.Combine(packageInfo.resolvedPath, "Editor/Templates"));
|
||||
searchPaths.Add(System.IO.Path.Combine(packageInfo.resolvedPath, "Samples"));
|
||||
}
|
||||
|
||||
// Also search project-local paths
|
||||
searchPaths.Add("Assets/VFX/Templates");
|
||||
searchPaths.Add("Assets/VFX");
|
||||
|
||||
// Precompute normalized package path for comparison
|
||||
string normalizedPackagePath = null;
|
||||
if (packageInfo != null)
|
||||
{
|
||||
normalizedPackagePath = packageInfo.resolvedPath.Replace("\\", "/");
|
||||
}
|
||||
|
||||
// Precompute the Assets base path for converting absolute paths to project-relative
|
||||
string assetsBasePath = Application.dataPath.Replace("\\", "/");
|
||||
|
||||
foreach (string basePath in searchPaths)
|
||||
{
|
||||
if (!System.IO.Directory.Exists(basePath))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string[] vfxFiles = System.IO.Directory.GetFiles(basePath, "*.vfx", System.IO.SearchOption.AllDirectories);
|
||||
foreach (string file in vfxFiles)
|
||||
{
|
||||
string absolutePath = file.Replace("\\", "/");
|
||||
string name = System.IO.Path.GetFileNameWithoutExtension(file);
|
||||
bool isPackage = normalizedPackagePath != null && absolutePath.StartsWith(normalizedPackagePath);
|
||||
|
||||
// Convert absolute path to project-relative path
|
||||
string projectRelativePath;
|
||||
if (isPackage)
|
||||
{
|
||||
// For package paths, convert to Packages/... format
|
||||
projectRelativePath = "Packages/" + packageInfo.name + absolutePath.Substring(normalizedPackagePath.Length);
|
||||
}
|
||||
else if (absolutePath.StartsWith(assetsBasePath))
|
||||
{
|
||||
// For project assets, convert to Assets/... format
|
||||
projectRelativePath = "Assets" + absolutePath.Substring(assetsBasePath.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback: use the absolute path if we can't determine the relative path
|
||||
projectRelativePath = absolutePath;
|
||||
}
|
||||
|
||||
string normalizedPath = projectRelativePath.Replace("\\", "/");
|
||||
if (seenPaths.Add(normalizedPath))
|
||||
{
|
||||
templates.Add(new { name = name, path = projectRelativePath, source = isPackage ? "package" : "project" });
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"Failed to list VFX templates under '{basePath}': {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// Also search project assets
|
||||
string[] guids = AssetDatabase.FindAssets("t:VisualEffectAsset");
|
||||
foreach (string guid in guids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
string normalizedPath = path.Replace("\\", "/");
|
||||
if (seenPaths.Add(normalizedPath))
|
||||
{
|
||||
string name = System.IO.Path.GetFileNameWithoutExtension(path);
|
||||
templates.Add(new { name = name, path = path, source = "project" });
|
||||
}
|
||||
}
|
||||
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
data = new
|
||||
{
|
||||
count = templates.Count,
|
||||
templates = templates
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists all VFX assets in the project.
|
||||
/// </summary>
|
||||
public static object ListAssets(JObject @params)
|
||||
{
|
||||
string searchFolder = @params["folder"]?.ToString();
|
||||
string searchPattern = @params["search"]?.ToString();
|
||||
|
||||
string filter = "t:VisualEffectAsset";
|
||||
if (!string.IsNullOrEmpty(searchPattern))
|
||||
{
|
||||
filter += " " + searchPattern;
|
||||
}
|
||||
|
||||
string[] guids;
|
||||
if (!string.IsNullOrEmpty(searchFolder))
|
||||
{
|
||||
if (searchFolder.Contains("\\") || searchFolder.Contains("..") || System.IO.Path.IsPathRooted(searchFolder))
|
||||
{
|
||||
return new { success = false, message = "Invalid folder: traversal and absolute paths are not allowed" };
|
||||
}
|
||||
|
||||
if (searchFolder.StartsWith("Packages/"))
|
||||
{
|
||||
return new { success = false, message = "Invalid folder: VFX assets must live under Assets/." };
|
||||
}
|
||||
|
||||
if (!searchFolder.StartsWith("Assets/"))
|
||||
{
|
||||
searchFolder = "Assets/" + searchFolder;
|
||||
}
|
||||
|
||||
string fullPath = System.IO.Path.Combine(UnityEngine.Application.dataPath, searchFolder.Substring("Assets/".Length));
|
||||
string canonicalProjectRoot = System.IO.Path.GetFullPath(UnityEngine.Application.dataPath);
|
||||
string canonicalSearchFolder = System.IO.Path.GetFullPath(fullPath);
|
||||
if (!canonicalSearchFolder.StartsWith(canonicalProjectRoot + System.IO.Path.DirectorySeparatorChar) &&
|
||||
canonicalSearchFolder != canonicalProjectRoot)
|
||||
{
|
||||
return new { success = false, message = "Invalid folder: would escape project directory" };
|
||||
}
|
||||
|
||||
guids = AssetDatabase.FindAssets(filter, new[] { searchFolder });
|
||||
}
|
||||
else
|
||||
{
|
||||
guids = AssetDatabase.FindAssets(filter);
|
||||
}
|
||||
|
||||
var assets = new List<object>();
|
||||
foreach (string guid in guids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var asset = AssetDatabase.LoadAssetAtPath<VisualEffectAsset>(path);
|
||||
if (asset != null)
|
||||
{
|
||||
assets.Add(new
|
||||
{
|
||||
name = asset.name,
|
||||
path = path,
|
||||
guid = guid
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return new
|
||||
{
|
||||
success = true,
|
||||
data = new
|
||||
{
|
||||
count = assets.Count,
|
||||
assets = assets
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static string ValidateVfxGraphVersion()
|
||||
{
|
||||
var info = UnityEditor.PackageManager.PackageInfo.FindForAssetPath("Packages/com.unity.visualeffectgraph");
|
||||
if (info == null)
|
||||
{
|
||||
return "VFX Graph package (com.unity.visualeffectgraph) not installed";
|
||||
}
|
||||
|
||||
if (IsVersionSupported(info.version))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string supported = string.Join(", ", SupportedVfxGraphVersions.Select(version => $"{version}.x"));
|
||||
return $"Unsupported VFX Graph version {info.version}. Supported versions: {supported}.";
|
||||
}
|
||||
|
||||
private static bool IsVersionSupported(string installedVersion)
|
||||
{
|
||||
if (string.IsNullOrEmpty(installedVersion))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string normalized = installedVersion;
|
||||
int suffixIndex = normalized.IndexOfAny(new[] { '-', '+' });
|
||||
if (suffixIndex >= 0)
|
||||
{
|
||||
normalized = normalized.Substring(0, suffixIndex);
|
||||
}
|
||||
|
||||
if (!Version.TryParse(normalized, out Version installed))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (string supported in SupportedVfxGraphVersions)
|
||||
{
|
||||
if (!Version.TryParse(supported, out Version target))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (installed.Major == target.Major && installed.Minor == target.Minor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string TryGetAssetPathFromFileSystem(string templatePath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(templatePath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string normalized = templatePath.Replace("\\", "/");
|
||||
string assetsRoot = Application.dataPath.Replace("\\", "/");
|
||||
|
||||
if (normalized.StartsWith(assetsRoot + "/"))
|
||||
{
|
||||
return "Assets/" + normalized.Substring(assetsRoot.Length + 1);
|
||||
}
|
||||
|
||||
var packageInfo = UnityEditor.PackageManager.PackageInfo.FindForAssetPath("Packages/com.unity.visualeffectgraph");
|
||||
if (packageInfo != null)
|
||||
{
|
||||
string packageRoot = packageInfo.resolvedPath.Replace("\\", "/");
|
||||
if (normalized.StartsWith(packageRoot + "/"))
|
||||
{
|
||||
return "Packages/" + packageInfo.name + "/" + normalized.Substring(packageRoot.Length + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user