Files
coplaydev--unity-mcp/MCPForUnity/Editor/Tools/Graphics/RenderPipelineOps.cs
T
2026-07-13 12:49:17 +08:00

287 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Reflection;
using MCPForUnity.Editor.Helpers;
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
namespace MCPForUnity.Editor.Tools.Graphics
{
internal static class RenderPipelineOps
{
// === pipeline_get_info ===
// Returns: active pipeline, quality level, renderer info, key settings
internal static object GetInfo(JObject @params)
{
var pipeline = RenderPipelineUtility.GetActivePipeline();
var pipelineAsset = GraphicsSettings.currentRenderPipeline;
// Quality level info
int currentQuality = QualitySettings.GetQualityLevel();
string[] qualityNames = QualitySettings.names;
var data = new Dictionary<string, object>
{
["pipeline"] = pipeline.ToString(),
["pipelineName"] = GraphicsHelpers.GetPipelineName(),
["qualityLevel"] = currentQuality,
["qualityLevelName"] = currentQuality < qualityNames.Length ? qualityNames[currentQuality] : "Unknown",
["qualityLevels"] = qualityNames,
["colorSpace"] = QualitySettings.activeColorSpace.ToString(),
["hasVolumeSystem"] = GraphicsHelpers.HasVolumeSystem,
};
// If SRP, add pipeline asset info
if (pipelineAsset != null)
{
data["pipelineAsset"] = pipelineAsset.name;
data["pipelineAssetPath"] = AssetDatabase.GetAssetPath(pipelineAsset);
data["pipelineAssetType"] = pipelineAsset.GetType().Name;
// Read common public properties via reflection
var settings = new Dictionary<string, object>();
TryReadProperty(pipelineAsset, "renderScale", settings);
TryReadProperty(pipelineAsset, "supportsHDR", settings);
TryReadProperty(pipelineAsset, "msaaSampleCount", settings);
TryReadProperty(pipelineAsset, "shadowDistance", settings);
TryReadProperty(pipelineAsset, "shadowCascadeCount", settings);
TryReadProperty(pipelineAsset, "maxAdditionalLightsCount", settings);
TryReadProperty(pipelineAsset, "supportsSoftShadows", settings);
TryReadProperty(pipelineAsset, "colorGradingMode", settings);
if (settings.Count > 0)
data["settings"] = settings;
}
return new
{
success = true,
message = $"Pipeline: {GraphicsHelpers.GetPipelineName()}, Quality: {(currentQuality < qualityNames.Length ? qualityNames[currentQuality] : "?")}",
data
};
}
// === pipeline_set_quality ===
// Params: level (int or string name)
internal static object SetQuality(JObject @params)
{
var p = new ToolParams(@params);
string levelName = p.Get("level");
int? levelIndex = p.GetInt("level");
string[] names = QualitySettings.names;
int targetIndex = -1;
if (levelIndex.HasValue)
{
targetIndex = levelIndex.Value;
}
else if (!string.IsNullOrEmpty(levelName))
{
// Try exact match first
for (int i = 0; i < names.Length; i++)
{
if (string.Equals(names[i], levelName, StringComparison.OrdinalIgnoreCase))
{
targetIndex = i;
break;
}
}
// Try parse as int
if (targetIndex < 0 && int.TryParse(levelName, out int parsed))
targetIndex = parsed;
}
else
{
return new ErrorResponse($"'level' parameter required. Available: {string.Join(", ", names)}");
}
if (targetIndex < 0 || targetIndex >= names.Length)
return new ErrorResponse(
$"Invalid quality level. Available: {string.Join(", ", names)} (0-{names.Length - 1})");
QualitySettings.SetQualityLevel(targetIndex, true);
return new
{
success = true,
message = $"Quality level set to '{names[targetIndex]}' (index {targetIndex}).",
data = new
{
level = targetIndex,
name = names[targetIndex],
allLevels = names
}
};
}
// === pipeline_get_settings ===
// Detailed read of pipeline asset settings via public properties + SerializedObject fallback
internal static object GetSettings(JObject @params)
{
var pipelineAsset = GraphicsSettings.currentRenderPipeline;
if (pipelineAsset == null)
return new ErrorResponse("No render pipeline asset found (Built-in pipeline has no asset).");
var settings = new Dictionary<string, object>();
// Public properties (URP)
string[] publicProps = {
"renderScale", "supportsHDR", "msaaSampleCount", "shadowDistance",
"shadowCascadeCount", "mainLightShadowmapResolution",
"additionalLightsShadowmapResolution", "maxAdditionalLightsCount",
"supportsSoftShadows", "colorGradingMode", "colorGradingLutSize"
};
foreach (var propName in publicProps)
TryReadProperty(pipelineAsset, propName, settings);
// SerializedObject for non-public settings
var serializedSettings = new Dictionary<string, object>();
string[] serializedPaths = {
"m_DefaultRendererIndex", "m_MainLightRenderingMode",
"m_AdditionalLightsRenderingMode", "m_SupportsOpaqueTexture",
"m_SupportsDepthTexture"
};
using (var so = new SerializedObject(pipelineAsset))
{
foreach (var path in serializedPaths)
{
var prop = so.FindProperty(path);
if (prop != null)
serializedSettings[path] = GraphicsHelpers.ReadSerializedValue(prop);
}
}
if (serializedSettings.Count > 0)
settings["_serialized"] = serializedSettings;
return new
{
success = true,
message = $"Pipeline settings for '{pipelineAsset.name}'.",
data = new
{
assetName = pipelineAsset.name,
assetPath = AssetDatabase.GetAssetPath(pipelineAsset),
assetType = pipelineAsset.GetType().Name,
settings
}
};
}
// === pipeline_set_settings ===
// Write pipeline asset settings via public properties + SerializedObject fallback
internal static object SetSettings(JObject @params)
{
var p = new ToolParams(@params);
var settingsToken = p.GetRaw("settings") as JObject;
if (settingsToken == null)
return new ErrorResponse("'settings' dict is required.");
var pipelineAsset = GraphicsSettings.currentRenderPipeline;
if (pipelineAsset == null)
return new ErrorResponse("No render pipeline asset found.");
var changed = new List<string>();
var failed = new List<string>();
using (var so = new SerializedObject(pipelineAsset))
{
foreach (var prop in settingsToken.Properties())
{
string propName = prop.Name;
JToken value = prop.Value;
// Try public property first
var publicProp = pipelineAsset.GetType().GetProperty(propName,
BindingFlags.Public | BindingFlags.Instance);
if (publicProp != null && publicProp.CanWrite)
{
try
{
object converted = ConvertPropertyValue(value, publicProp.PropertyType);
publicProp.SetValue(pipelineAsset, converted);
changed.Add(propName);
continue;
}
catch (Exception ex)
{
McpLog.Warn($"[RenderPipelineOps] Failed to set '{propName}' via property: {ex.Message}");
}
}
// Try SerializedObject fallback (for m_ prefixed properties)
string serializedPath = propName.StartsWith("m_") ? propName : $"m_{char.ToUpper(propName[0])}{propName.Substring(1)}";
var sProp = so.FindProperty(serializedPath);
if (sProp == null && !propName.StartsWith("m_"))
sProp = so.FindProperty(propName);
if (sProp != null)
{
if (GraphicsHelpers.SetSerializedValue(sProp, value))
{
changed.Add(propName);
continue;
}
}
failed.Add(propName);
}
so.ApplyModifiedProperties();
}
EditorUtility.SetDirty(pipelineAsset);
AssetDatabase.SaveAssets();
var msg = $"Updated {changed.Count} pipeline setting(s)";
if (failed.Count > 0)
msg += $". Failed: {string.Join(", ", failed)}";
return new
{
success = true,
message = msg,
data = new { changed, failed }
};
}
// --- Helper: Convert JToken to target property type ---
private static object ConvertPropertyValue(JToken value, Type targetType)
{
if (targetType == typeof(bool)) return ParamCoercion.CoerceBool(value, false);
if (targetType == typeof(int)) return ParamCoercion.CoerceInt(value, 0);
if (targetType == typeof(float)) return ParamCoercion.CoerceFloat(value, 0f);
if (targetType == typeof(string)) return value.ToString();
if (targetType.IsEnum)
{
string str = value.ToString();
if (Enum.TryParse(targetType, str, true, out object enumVal))
return enumVal;
if (int.TryParse(str, out int intVal))
return Enum.ToObject(targetType, intVal);
}
return Convert.ChangeType(value.ToObject<object>(), targetType);
}
// --- Helper: Try to read a property value via reflection ---
private static void TryReadProperty(object obj, string propertyName, Dictionary<string, object> target)
{
if (obj == null) return;
var prop = obj.GetType().GetProperty(propertyName,
BindingFlags.Public | BindingFlags.Instance);
if (prop != null)
{
try
{
var val = prop.GetValue(obj);
target[propertyName] = val is Enum e ? e.ToString() : val;
}
catch { /* skip unreadable properties */ }
}
}
}
}