using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using MCPForUnity.Editor.Helpers; using MCPForUnity.Runtime.Helpers; using Newtonsoft.Json.Linq; using UnityEditor; using UnityEngine; namespace MCPForUnity.Editor.Tools.Graphics { internal static class GraphicsHelpers { private static bool? _hasVolumeSystem; private static Type _volumeType; private static Type _volumeProfileType; private static Type _volumeComponentType; private static Type _volumeParameterType; internal static bool HasVolumeSystem { get { if (_hasVolumeSystem == null) DetectPackages(); return _hasVolumeSystem.Value; } } internal static bool HasURP => RenderPipelineUtility.GetActivePipeline() == RenderPipelineUtility.PipelineKind.Universal; internal static bool HasHDRP => RenderPipelineUtility.GetActivePipeline() == RenderPipelineUtility.PipelineKind.HighDefinition; internal static Type VolumeType { get { if (_hasVolumeSystem == null) DetectPackages(); return _volumeType; } } internal static Type VolumeProfileType { get { if (_hasVolumeSystem == null) DetectPackages(); return _volumeProfileType; } } internal static Type VolumeComponentType { get { if (_hasVolumeSystem == null) DetectPackages(); return _volumeComponentType; } } internal static Type VolumeParameterType { get { if (_hasVolumeSystem == null) DetectPackages(); return _volumeParameterType; } } private static void DetectPackages() { _volumeType = Type.GetType("UnityEngine.Rendering.Volume, Unity.RenderPipelines.Core.Runtime"); _volumeProfileType = Type.GetType("UnityEngine.Rendering.VolumeProfile, Unity.RenderPipelines.Core.Runtime"); _volumeComponentType = Type.GetType("UnityEngine.Rendering.VolumeComponent, Unity.RenderPipelines.Core.Runtime"); _volumeParameterType = Type.GetType("UnityEngine.Rendering.VolumeParameter, Unity.RenderPipelines.Core.Runtime"); _hasVolumeSystem = _volumeType != null && _volumeProfileType != null; } internal static Type ResolveVolumeComponentType(string effectName) { if (string.IsNullOrEmpty(effectName) || VolumeComponentType == null) return null; var derivedTypes = TypeCache.GetTypesDerivedFrom(VolumeComponentType); foreach (var t in derivedTypes) { if (t.IsAbstract) continue; if (string.Equals(t.Name, effectName, StringComparison.OrdinalIgnoreCase)) return t; } return null; } internal static List GetAvailableEffectTypes() { if (VolumeComponentType == null) return new List(); var derivedTypes = TypeCache.GetTypesDerivedFrom(VolumeComponentType); return derivedTypes .Where(t => !t.IsAbstract && !t.IsGenericType) .OrderBy(t => t.Name) .ToList(); } internal static Component FindVolume(JObject @params) { var p = new ToolParams(@params); string target = p.Get("target"); if (string.IsNullOrEmpty(target)) { var allVolumes = UnityFindObjectsCompat.FindAll(VolumeType); return allVolumes.Length > 0 ? allVolumes[0] as Component : null; } if (int.TryParse(target, out int instanceId)) { var byId = GameObjectLookup.ResolveInstanceID(instanceId) as GameObject; if (byId != null) return byId.GetComponent(VolumeType); } var go = GameObject.Find(target); if (go != null) return go.GetComponent(VolumeType); return null; } internal static string GetPipelineName() { return RenderPipelineUtility.GetActivePipeline() switch { RenderPipelineUtility.PipelineKind.Universal => "Universal (URP)", RenderPipelineUtility.PipelineKind.HighDefinition => "High Definition (HDRP)", RenderPipelineUtility.PipelineKind.BuiltIn => "Built-in", RenderPipelineUtility.PipelineKind.Custom => "Custom", _ => "Unknown" }; } internal static object ReadSerializedValue(SerializedProperty prop) { return prop.propertyType switch { SerializedPropertyType.Boolean => prop.boolValue, SerializedPropertyType.Integer => prop.type == "long" ? prop.longValue : (object)prop.intValue, SerializedPropertyType.Float => prop.floatValue, SerializedPropertyType.String => prop.stringValue, SerializedPropertyType.Enum => prop.enumValueIndex < prop.enumNames.Length ? prop.enumNames[prop.enumValueIndex] : (object)prop.enumValueIndex, SerializedPropertyType.ObjectReference => prop.objectReferenceValue != null ? (object)new { name = prop.objectReferenceValue.name, path = AssetDatabase.GetAssetPath(prop.objectReferenceValue) } : null, SerializedPropertyType.Color => new[] { prop.colorValue.r, prop.colorValue.g, prop.colorValue.b, prop.colorValue.a }, SerializedPropertyType.Vector2 => new[] { prop.vector2Value.x, prop.vector2Value.y }, SerializedPropertyType.Vector3 => new[] { prop.vector3Value.x, prop.vector3Value.y, prop.vector3Value.z }, SerializedPropertyType.LayerMask => prop.intValue, _ => prop.propertyType.ToString() }; } internal static bool SetSerializedValue(SerializedProperty prop, JToken value) { try { switch (prop.propertyType) { case SerializedPropertyType.Boolean: prop.boolValue = ParamCoercion.CoerceBool(value, false); return true; case SerializedPropertyType.Integer: if (prop.type == "long") prop.longValue = ParamCoercion.CoerceLong(value, 0); else prop.intValue = ParamCoercion.CoerceInt(value, 0); return true; case SerializedPropertyType.Float: prop.floatValue = ParamCoercion.CoerceFloat(value, 0f); return true; case SerializedPropertyType.String: prop.stringValue = value.ToString(); return true; case SerializedPropertyType.Enum: if (value.Type == JTokenType.String) { for (int i = 0; i < prop.enumNames.Length; i++) { if (string.Equals(prop.enumNames[i], value.ToString(), StringComparison.OrdinalIgnoreCase)) { prop.enumValueIndex = i; return true; } } } prop.enumValueIndex = ParamCoercion.CoerceInt(value, 0); return true; case SerializedPropertyType.ObjectReference: if (value.Type == JTokenType.String) { string path = value.ToString(); var asset = AssetDatabase.LoadAssetAtPath(path); if (asset != null) { prop.objectReferenceValue = asset; return true; } } else if (value.Type == JTokenType.Object) { string path = value["path"]?.ToString(); if (!string.IsNullOrEmpty(path)) { var asset = AssetDatabase.LoadAssetAtPath(path); if (asset != null) { prop.objectReferenceValue = asset; return true; } } } else if (value.Type == JTokenType.Null) { prop.objectReferenceValue = null; return true; } return false; case SerializedPropertyType.Color: if (value is JArray colorArr && colorArr.Count >= 3) { prop.colorValue = new Color( (float)colorArr[0], (float)colorArr[1], (float)colorArr[2], colorArr.Count >= 4 ? (float)colorArr[3] : 1f); return true; } return false; case SerializedPropertyType.Vector2: if (value is JArray v2Arr && v2Arr.Count >= 2) { prop.vector2Value = new Vector2((float)v2Arr[0], (float)v2Arr[1]); return true; } return false; case SerializedPropertyType.Vector3: if (value is JArray v3Arr && v3Arr.Count >= 3) { prop.vector3Value = new Vector3((float)v3Arr[0], (float)v3Arr[1], (float)v3Arr[2]); return true; } return false; case SerializedPropertyType.LayerMask: prop.intValue = ParamCoercion.CoerceInt(value, 0); return true; default: return false; } } catch { return false; } } internal static void MarkDirty(UnityEngine.Object obj) { if (obj == null) return; EditorUtility.SetDirty(obj); if (obj is Component comp) { var prefabStage = UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage(); if (prefabStage != null) UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(prefabStage.scene); else UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(comp.gameObject.scene); } } } }