Files
Fishing2/Packages/com.nwh.common/Editor/URPToBuiltInConverter.cs
2026-02-27 17:44:21 +08:00

419 lines
16 KiB
C#

using UnityEngine;
using UnityEditor;
using System.IO;
using System.Collections.Generic;
namespace NWH.Common.Editor
{
/// <summary>
/// Converts materials in a folder from URP shaders to Built-in render pipeline shaders.
/// </summary>
public static class URPToBuiltInConverter
{
private static readonly Dictionary<string, string> ShaderMapping = new Dictionary<string, string>
{
// 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<string, string> TexturePropertyMapping = new Dictionary<string, string>
{
{ "_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<string, string> ColorPropertyMapping = new Dictionary<string, string>
{
{ "_BaseColor", "_Color" },
{ "_EmissionColor", "_EmissionColor" },
{ "_SpecColor", "_SpecColor" },
};
private static readonly Dictionary<string, string> FloatPropertyMapping = new Dictionary<string, string>
{
{ "_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<string> warnings = new List<string>();
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<Material>(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<string, Texture>();
var cachedColors = new Dictionary<string, Color>();
var cachedFloats = new Dictionary<string, float>();
var cachedKeywords = new HashSet<string>(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;
}
}
}
}
}