using UnityEngine; using UnityEditor; using System.IO; using System.Collections.Generic; namespace NWH.Common.Editor { /// /// Converts materials in a folder from URP shaders to Built-in render pipeline shaders. /// public static class URPToBuiltInConverter { private static readonly Dictionary ShaderMapping = new Dictionary { // Lit shaders { "Universal Render Pipeline/Lit", "Standard" }, { "Universal Render Pipeline/Simple Lit", "Mobile/Diffuse" }, { "Universal Render Pipeline/Baked Lit", "Legacy Shaders/Lightmapped/Diffuse" }, { "Universal Render Pipeline/Unlit", "Unlit/Texture" }, // Particle shaders { "Universal Render Pipeline/Particles/Lit", "Particles/Standard Surface" }, { "Universal Render Pipeline/Particles/Unlit", "Particles/Standard Unlit" }, { "Universal Render Pipeline/Particles/Simple Lit", "Particles/Standard Surface" }, // Terrain shaders { "Universal Render Pipeline/Terrain/Lit", "Nature/Terrain/Standard" }, // Nature/SpeedTree shaders { "Universal Render Pipeline/Nature/SpeedTree7", "Nature/SpeedTree" }, { "Universal Render Pipeline/Nature/SpeedTree7 Billboard", "Nature/SpeedTree Billboard" }, { "Universal Render Pipeline/Nature/SpeedTree8", "Nature/SpeedTree8" }, }; // Property mappings from URP to Built-in private static readonly Dictionary TexturePropertyMapping = new Dictionary { { "_BaseMap", "_MainTex" }, { "_BumpMap", "_BumpMap" }, { "_NormalMap", "_BumpMap" }, { "_EmissionMap", "_EmissionMap" }, { "_OcclusionMap", "_OcclusionMap" }, { "_MetallicGlossMap", "_MetallicGlossMap" }, { "_SpecGlossMap", "_SpecGlossMap" }, { "_ParallaxMap", "_ParallaxMap" }, { "_DetailAlbedoMap", "_DetailAlbedoMap" }, { "_DetailNormalMap", "_DetailNormalMap" }, { "_DetailMask", "_DetailMask" }, }; private static readonly Dictionary ColorPropertyMapping = new Dictionary { { "_BaseColor", "_Color" }, { "_EmissionColor", "_EmissionColor" }, { "_SpecColor", "_SpecColor" }, }; private static readonly Dictionary FloatPropertyMapping = new Dictionary { { "_Smoothness", "_Glossiness" }, { "_Metallic", "_Metallic" }, { "_BumpScale", "_BumpScale" }, { "_OcclusionStrength", "_OcclusionStrength" }, { "_Cutoff", "_Cutoff" }, { "_Parallax", "_Parallax" }, { "_DetailNormalMapScale", "_DetailNormalMapScale" }, }; [MenuItem("Tools/NWH/Convert Folder URP to Built-in")] public static void ConvertFolder() { string folderPath = EditorUtility.OpenFolderPanel("Select Folder to Convert", "Assets", ""); if (string.IsNullOrEmpty(folderPath)) { return; } // Convert to relative path if inside project string projectPath = Application.dataPath; string relativePath; if (folderPath.StartsWith(projectPath)) { relativePath = "Assets" + folderPath.Substring(projectPath.Length); } else if (folderPath.Contains("Packages")) { int packagesIndex = folderPath.IndexOf("Packages"); relativePath = folderPath.Substring(packagesIndex); } else { Debug.LogError("[URPToBuiltIn] Selected folder must be inside the project."); return; } ConvertMaterialsInFolder(folderPath, relativePath); } public static void ConvertMaterialsInFolder(string absolutePath, string relativePath) { if (!Directory.Exists(absolutePath)) { Debug.LogError($"[URPToBuiltIn] Folder not found: {absolutePath}"); return; } string[] materialFiles = Directory.GetFiles(absolutePath, "*.mat", SearchOption.AllDirectories); if (materialFiles.Length == 0) { Debug.Log("[URPToBuiltIn] No material files found in the selected folder."); return; } int converted = 0; int skipped = 0; int failed = 0; List warnings = new List(); try { AssetDatabase.StartAssetEditing(); for (int i = 0; i < materialFiles.Length; i++) { string filePath = materialFiles[i].Replace("\\", "/"); float progress = (float)i / materialFiles.Length; if (EditorUtility.DisplayCancelableProgressBar( "Converting Materials", $"Processing {Path.GetFileName(filePath)} ({i + 1}/{materialFiles.Length})", progress)) { Debug.Log("[URPToBuiltIn] Conversion cancelled by user."); break; } // Convert absolute path to asset path string assetPath = ConvertToAssetPath(filePath); if (string.IsNullOrEmpty(assetPath)) { warnings.Add($"Could not resolve asset path: {filePath}"); failed++; continue; } Material material = AssetDatabase.LoadAssetAtPath(assetPath); if (material == null) { warnings.Add($"Could not load material: {assetPath}"); failed++; continue; } if (material.shader == null) { warnings.Add($"Material has no shader: {assetPath}"); failed++; continue; } string shaderName = material.shader.name; // Check if this is a URP shader if (!IsURPShader(shaderName)) { skipped++; continue; } // Find target shader string targetShaderName = GetTargetShaderName(shaderName); Shader targetShader = Shader.Find(targetShaderName); if (targetShader == null) { warnings.Add($"Target shader not found '{targetShaderName}' for material: {assetPath}"); failed++; continue; } // Convert the material if (ConvertMaterial(material, targetShader)) { EditorUtility.SetDirty(material); converted++; } else { failed++; } } } finally { AssetDatabase.StopAssetEditing(); AssetDatabase.SaveAssets(); EditorUtility.ClearProgressBar(); } // Log summary Debug.Log($"[URPToBuiltIn] Conversion complete:\n" + $" Converted: {converted}\n" + $" Skipped (not URP): {skipped}\n" + $" Failed: {failed}"); if (warnings.Count > 0) { Debug.LogWarning("[URPToBuiltIn] Warnings:\n" + string.Join("\n", warnings)); } } private static string ConvertToAssetPath(string absolutePath) { absolutePath = absolutePath.Replace("\\", "/"); // Check for Assets folder int assetsIndex = absolutePath.IndexOf("/Assets/"); if (assetsIndex >= 0) { return absolutePath.Substring(assetsIndex + 1); } // Check for Packages folder int packagesIndex = absolutePath.IndexOf("/Packages/"); if (packagesIndex >= 0) { return absolutePath.Substring(packagesIndex + 1); } return null; } private static bool IsURPShader(string shaderName) { return shaderName.StartsWith("Universal Render Pipeline") || shaderName.StartsWith("Shader Graphs/") || shaderName.Contains("URP") || shaderName.Contains("/Universal/"); } private static string GetTargetShaderName(string urpShaderName) { if (ShaderMapping.TryGetValue(urpShaderName, out string targetName)) { return targetName; } // Fallback to Standard for unknown URP shaders return "Standard"; } private static bool ConvertMaterial(Material material, Shader targetShader) { try { // Cache current properties before changing shader var cachedTextures = new Dictionary(); var cachedColors = new Dictionary(); var cachedFloats = new Dictionary(); var cachedKeywords = new HashSet(material.shaderKeywords); // Cache textures foreach (var mapping in TexturePropertyMapping) { if (material.HasProperty(mapping.Key)) { Texture tex = material.GetTexture(mapping.Key); if (tex != null) { cachedTextures[mapping.Value] = tex; } } } // Cache colors foreach (var mapping in ColorPropertyMapping) { if (material.HasProperty(mapping.Key)) { cachedColors[mapping.Value] = material.GetColor(mapping.Key); } } // Cache floats foreach (var mapping in FloatPropertyMapping) { if (material.HasProperty(mapping.Key)) { cachedFloats[mapping.Value] = material.GetFloat(mapping.Key); } } // Cache render mode settings float surface = material.HasProperty("_Surface") ? material.GetFloat("_Surface") : 0; float alphaClip = material.HasProperty("_AlphaClip") ? material.GetFloat("_AlphaClip") : 0; float blend = material.HasProperty("_Blend") ? material.GetFloat("_Blend") : 0; // Change shader material.shader = targetShader; // Apply cached textures foreach (var kvp in cachedTextures) { if (material.HasProperty(kvp.Key)) { material.SetTexture(kvp.Key, kvp.Value); } } // Apply cached colors foreach (var kvp in cachedColors) { if (material.HasProperty(kvp.Key)) { material.SetColor(kvp.Key, kvp.Value); } } // Apply cached floats foreach (var kvp in cachedFloats) { if (material.HasProperty(kvp.Key)) { material.SetFloat(kvp.Key, kvp.Value); } } // Handle emission if (cachedColors.TryGetValue("_EmissionColor", out Color emissionColor)) { if (emissionColor.r > 0 || emissionColor.g > 0 || emissionColor.b > 0) { material.EnableKeyword("_EMISSION"); material.globalIlluminationFlags = MaterialGlobalIlluminationFlags.RealtimeEmissive; } } // Set render mode for Standard shader if (targetShader.name == "Standard") { SetupStandardShaderRenderMode(material, surface, alphaClip, blend); } return true; } catch (System.Exception e) { Debug.LogError($"[URPToBuiltIn] Error converting material {material.name}: {e.Message}"); return false; } } private static void SetupStandardShaderRenderMode(Material material, float surface, float alphaClip, float blend) { // URP: _Surface 0=Opaque, 1=Transparent // URP: _AlphaClip 0=Off, 1=On // Standard modes: 0=Opaque, 1=Cutout, 2=Fade, 3=Transparent if (surface == 0) // Opaque { if (alphaClip > 0.5f) { // Cutout mode material.SetFloat("_Mode", 1); material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero); material.SetInt("_ZWrite", 1); material.EnableKeyword("_ALPHATEST_ON"); material.DisableKeyword("_ALPHABLEND_ON"); material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest; } else { // Opaque mode material.SetFloat("_Mode", 0); material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero); material.SetInt("_ZWrite", 1); material.DisableKeyword("_ALPHATEST_ON"); material.DisableKeyword("_ALPHABLEND_ON"); material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); material.renderQueue = -1; } } else // Transparent { if (blend == 0) // Alpha blend { // Fade mode material.SetFloat("_Mode", 2); material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); material.SetInt("_ZWrite", 0); material.DisableKeyword("_ALPHATEST_ON"); material.EnableKeyword("_ALPHABLEND_ON"); material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent; } else // Premultiply or Additive { // Transparent mode material.SetFloat("_Mode", 3); material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); material.SetInt("_ZWrite", 0); material.DisableKeyword("_ALPHATEST_ON"); material.DisableKeyword("_ALPHABLEND_ON"); material.EnableKeyword("_ALPHAPREMULTIPLY_ON"); material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent; } } } } }