using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; using UnityEditor; namespace MCPForUnity.Editor.Helpers { internal static class RenderPipelineUtility { internal enum PipelineKind { BuiltIn, Universal, HighDefinition, Custom } internal enum VFXComponentType { ParticleSystem, LineRenderer, TrailRenderer } private static Dictionary s_DefaultVFXMaterials = new Dictionary(); private static Dictionary s_DefaultSceneMaterials = new Dictionary(); private static readonly string[] BuiltInLitShaders = { "Standard", "Legacy Shaders/Diffuse" }; private static readonly string[] BuiltInUnlitShaders = { "Unlit/Color", "Unlit/Texture" }; private static readonly string[] BuiltInParticleShaders = { "Particles/Standard Unlit", "Particles/Alpha Blended", "Particles/Additive" }; private static readonly string[] UrpLitShaders = { "Universal Render Pipeline/Lit", "Universal Render Pipeline/Simple Lit" }; private static readonly string[] UrpUnlitShaders = { "Universal Render Pipeline/Unlit" }; private static readonly string[] UrpParticleShaders = { "Universal Render Pipeline/Particles/Unlit", "Universal Render Pipeline/Particles/Simple Lit", "Universal Render Pipeline/Particles/Lit", }; private static readonly string[] HdrpLitShaders = { "HDRP/Lit", "High Definition Render Pipeline/Lit" }; private static readonly string[] HdrpUnlitShaders = { "HDRP/Unlit", "High Definition Render Pipeline/Unlit" }; internal static PipelineKind GetActivePipeline() { var asset = GraphicsSettings.currentRenderPipeline; if (asset == null) { return PipelineKind.BuiltIn; } var typeName = asset.GetType().FullName ?? string.Empty; if (typeName.IndexOf("HighDefinition", StringComparison.OrdinalIgnoreCase) >= 0 || typeName.IndexOf("HDRP", StringComparison.OrdinalIgnoreCase) >= 0) { return PipelineKind.HighDefinition; } if (typeName.IndexOf("Universal", StringComparison.OrdinalIgnoreCase) >= 0 || typeName.IndexOf("URP", StringComparison.OrdinalIgnoreCase) >= 0) { return PipelineKind.Universal; } return PipelineKind.Custom; } internal static Shader ResolveShader(string requestedNameOrAlias) { var pipeline = GetActivePipeline(); if (!string.IsNullOrWhiteSpace(requestedNameOrAlias)) { var alias = requestedNameOrAlias.Trim(); var aliasMatch = ResolveAlias(alias, pipeline); if (aliasMatch != null) { WarnIfPipelineMismatch(aliasMatch.name, pipeline); return aliasMatch; } var direct = Shader.Find(alias); if (direct != null) { WarnIfPipelineMismatch(direct.name, pipeline); return direct; } McpLog.Warn($"Shader '{alias}' not found. Falling back to {pipeline} defaults."); } var fallback = ResolveDefaultLitShader(pipeline) ?? ResolveDefaultLitShader(PipelineKind.BuiltIn) ?? Shader.Find("Unlit/Color"); if (fallback != null) { WarnIfPipelineMismatch(fallback.name, pipeline); } return fallback; } internal static Shader ResolveDefaultLitShader(PipelineKind pipeline) { return pipeline switch { PipelineKind.HighDefinition => TryFindShader(HdrpLitShaders) ?? TryFindShader(UrpLitShaders), PipelineKind.Universal => TryFindShader(UrpLitShaders) ?? TryFindShader(HdrpLitShaders), PipelineKind.Custom => TryFindShader(BuiltInLitShaders) ?? TryFindShader(UrpLitShaders) ?? TryFindShader(HdrpLitShaders), _ => TryFindShader(BuiltInLitShaders) ?? Shader.Find("Unlit/Color") }; } internal static Shader ResolveDefaultUnlitShader(PipelineKind pipeline) { return pipeline switch { PipelineKind.HighDefinition => TryFindShader(HdrpUnlitShaders) ?? TryFindShader(UrpUnlitShaders) ?? TryFindShader(BuiltInUnlitShaders), PipelineKind.Universal => TryFindShader(UrpUnlitShaders) ?? TryFindShader(HdrpUnlitShaders) ?? TryFindShader(BuiltInUnlitShaders), PipelineKind.Custom => TryFindShader(BuiltInUnlitShaders) ?? TryFindShader(UrpUnlitShaders) ?? TryFindShader(HdrpUnlitShaders), _ => TryFindShader(BuiltInUnlitShaders) }; } private static Shader ResolveAlias(string alias, PipelineKind pipeline) { if (string.Equals(alias, "lit", StringComparison.OrdinalIgnoreCase) || string.Equals(alias, "default", StringComparison.OrdinalIgnoreCase) || string.Equals(alias, "default_lit", StringComparison.OrdinalIgnoreCase) || string.Equals(alias, "standard", StringComparison.OrdinalIgnoreCase)) { return ResolveDefaultLitShader(pipeline); } if (string.Equals(alias, "unlit", StringComparison.OrdinalIgnoreCase)) { return ResolveDefaultUnlitShader(pipeline); } if (string.Equals(alias, "urp_lit", StringComparison.OrdinalIgnoreCase)) { return TryFindShader(UrpLitShaders); } if (string.Equals(alias, "hdrp_lit", StringComparison.OrdinalIgnoreCase)) { return TryFindShader(HdrpLitShaders); } if (string.Equals(alias, "built_in_lit", StringComparison.OrdinalIgnoreCase)) { return TryFindShader(BuiltInLitShaders); } return null; } private static Shader TryFindShader(params string[] shaderNames) { foreach (var shaderName in shaderNames) { var shader = Shader.Find(shaderName); if (shader != null) { return shader; } } return null; } private static void WarnIfPipelineMismatch(string shaderName, PipelineKind activePipeline) { if (string.IsNullOrEmpty(shaderName)) { return; } var lowerName = shaderName.ToLowerInvariant(); bool shaderLooksUrp = lowerName.Contains("universal render pipeline") || lowerName.Contains("urp/"); bool shaderLooksHdrp = lowerName.Contains("high definition render pipeline") || lowerName.Contains("hdrp/"); bool shaderLooksSrp = shaderLooksUrp || shaderLooksHdrp; bool shaderLooksBuiltin = LooksLikeBuiltInShader(lowerName, shaderLooksSrp); switch (activePipeline) { case PipelineKind.HighDefinition: if (shaderLooksUrp) { McpLog.Warn($"[RenderPipelineUtility] Active pipeline is HDRP but shader '{shaderName}' looks URP-based. Asset may appear incorrect."); } else if (shaderLooksBuiltin && !shaderLooksHdrp) { McpLog.Warn($"[RenderPipelineUtility] Active pipeline is HDRP but shader '{shaderName}' looks Built-in. Consider using an HDRP shader for correct results."); } break; case PipelineKind.Universal: if (shaderLooksHdrp) { McpLog.Warn($"[RenderPipelineUtility] Active pipeline is URP but shader '{shaderName}' looks HDRP-based. Asset may appear incorrect."); } else if (shaderLooksBuiltin && !shaderLooksUrp) { McpLog.Warn($"[RenderPipelineUtility] Active pipeline is URP but shader '{shaderName}' looks Built-in. Consider using a URP shader for correct results."); } break; case PipelineKind.BuiltIn: if (shaderLooksSrp) { McpLog.Warn($"[RenderPipelineUtility] Active pipeline is Built-in but shader '{shaderName}' targets URP/HDRP. Asset may not render as expected."); } break; } } internal static bool IsMaterialInvalidForActivePipeline(Material material, out string reason) { reason = null; if (material == null) { reason = "missing_material"; return true; } Shader shader = material.shader; if (shader == null) { reason = "missing_shader"; return true; } if (IsErrorShader(shader)) { reason = "error_shader"; return true; } var pipeline = GetActivePipeline(); if (IsPipelineMismatch(shader.name, pipeline)) { reason = "pipeline_mismatch"; return true; } return false; } private static bool IsErrorShader(Shader shader) { if (shader == null) { return true; } if (shader == Shader.Find("Hidden/InternalErrorShader")) { return true; } string shaderName = shader.name ?? string.Empty; return shaderName.IndexOf("InternalErrorShader", StringComparison.OrdinalIgnoreCase) >= 0; } private static bool IsPipelineMismatch(string shaderName, PipelineKind activePipeline) { if (string.IsNullOrEmpty(shaderName)) { return true; } string lowerName = shaderName.ToLowerInvariant(); bool shaderLooksUrp = lowerName.Contains("universal render pipeline") || lowerName.Contains("urp/"); bool shaderLooksHdrp = lowerName.Contains("high definition render pipeline") || lowerName.Contains("hdrp/"); bool shaderLooksSrp = shaderLooksUrp || shaderLooksHdrp; bool shaderLooksBuiltin = LooksLikeBuiltInShader(lowerName, shaderLooksSrp); return activePipeline switch { PipelineKind.HighDefinition => shaderLooksUrp || (shaderLooksBuiltin && !shaderLooksHdrp), PipelineKind.Universal => shaderLooksHdrp || (shaderLooksBuiltin && !shaderLooksUrp), PipelineKind.BuiltIn => shaderLooksSrp, _ => false, }; } internal static Material GetOrCreateDefaultVFXMaterial(VFXComponentType componentType) { var pipeline = GetActivePipeline(); string cacheKey = $"{pipeline}_{componentType}"; if (s_DefaultVFXMaterials.TryGetValue(cacheKey, out Material cachedMaterial) && cachedMaterial != null) { return cachedMaterial; } Material material = null; if (pipeline == PipelineKind.BuiltIn) { string builtinPath = componentType == VFXComponentType.ParticleSystem ? "Default-Particle.mat" : "Default-Line.mat"; material = AssetDatabase.GetBuiltinExtraResource(builtinPath); } if (material == null) { Shader shader = ResolveDefaultVFXShader(pipeline, componentType); if (shader == null) { shader = Shader.Find("Unlit/Color"); } if (shader != null) { material = new Material(shader); material.name = $"Auto_Default_{componentType}_{pipeline}"; // Set default color (white is standard for VFX) if (material.HasProperty("_Color")) { material.SetColor("_Color", Color.white); } if (material.HasProperty("_BaseColor")) { material.SetColor("_BaseColor", Color.white); } if (componentType == VFXComponentType.ParticleSystem) { material.renderQueue = 3000; if (material.HasProperty("_Mode")) { material.SetFloat("_Mode", 2); } if (material.HasProperty("_SrcBlend")) { material.SetFloat("_SrcBlend", (float)UnityEngine.Rendering.BlendMode.SrcAlpha); } if (material.HasProperty("_DstBlend")) { material.SetFloat("_DstBlend", (float)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); } if (material.HasProperty("_ZWrite")) { material.SetFloat("_ZWrite", 0); } } McpLog.Info($"[RenderPipelineUtility] Created default VFX material for {componentType} using {shader.name}"); } } if (material != null) { s_DefaultVFXMaterials[cacheKey] = material; } return material; } private static Shader ResolveDefaultVFXShader(PipelineKind pipeline, VFXComponentType componentType) { if (componentType == VFXComponentType.ParticleSystem) { return pipeline switch { PipelineKind.Universal => TryFindShader(UrpParticleShaders) ?? ResolveDefaultUnlitShader(pipeline), PipelineKind.HighDefinition => TryFindShader(HdrpUnlitShaders) ?? ResolveDefaultUnlitShader(pipeline), PipelineKind.BuiltIn => TryFindShader(BuiltInParticleShaders) ?? ResolveDefaultUnlitShader(pipeline), PipelineKind.Custom => TryFindShader(UrpParticleShaders) ?? TryFindShader(BuiltInParticleShaders) ?? TryFindShader(HdrpUnlitShaders) ?? ResolveDefaultUnlitShader(pipeline), _ => ResolveDefaultUnlitShader(pipeline), }; } return ResolveDefaultUnlitShader(pipeline); } private static bool LooksLikeBuiltInShader(string lowerName, bool shaderLooksSrp) { if (string.IsNullOrEmpty(lowerName)) { return false; } if (lowerName == "standard" || lowerName.StartsWith("legacy shaders/", StringComparison.Ordinal) || lowerName.StartsWith("mobile/", StringComparison.Ordinal)) { return true; } // Built-in non-SRP shader families commonly seen on particles/old content. if (!shaderLooksSrp && (lowerName.StartsWith("particles/", StringComparison.Ordinal) || lowerName.StartsWith("unlit/", StringComparison.Ordinal))) { return true; } return false; } internal static Material GetOrCreateDefaultSceneMaterial() { var pipeline = GetActivePipeline(); string cacheKey = $"{pipeline}_scene"; if (s_DefaultSceneMaterials.TryGetValue(cacheKey, out Material cached) && cached != null) { return cached; } Material material = null; Shader shader = ResolveDefaultLitShader(pipeline) ?? ResolveDefaultUnlitShader(pipeline); if (shader == null) { shader = Shader.Find("Unlit/Color"); } if (shader != null) { material = new Material(shader); material.name = $"Auto_Default_Scene_{pipeline}"; if (material.HasProperty("_Color")) { material.SetColor("_Color", Color.white); } if (material.HasProperty("_BaseColor")) { material.SetColor("_BaseColor", Color.white); } McpLog.Info($"[RenderPipelineUtility] Created default scene material using {shader.name}"); } if (material != null) { s_DefaultSceneMaterials[cacheKey] = material; } return material; } } }