using System; using System.Collections.Generic; using System.Text; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; using Object = UnityEngine.Object; namespace UnityEditor.Rendering.Universal { enum IdentifierType { kNullIdentifier = 0, kImportedAsset = 1, kSceneObject = 2, kSourceAsset = 3, kBuiltInAsset = 4 }; internal static class ReadonlyMaterialMap { public static bool TryGetMappingMaterial(Material material, out Material mappingMaterial) { mappingMaterial = material; if (GraphicsSettings.currentRenderPipeline is UniversalRenderPipelineAsset) { if (s_BuiltInMaterialsToURPMaterialsMappings.TryGetValue(material.name, out var mapping)) mappingMaterial = mapping(); } return mappingMaterial != null; } public static int count => s_BuiltInMaterialsToURPMaterialsMappings.Count; public static IEnumerable Keys => s_BuiltInMaterialsToURPMaterialsMappings.Keys; static Dictionary> s_BuiltInMaterialsToURPMaterialsMappings = new() { ["Default-Diffuse"] = () => GraphicsSettings.GetRenderPipelineSettings().defaultMaterial, ["Default-Material"] = () => GraphicsSettings.GetRenderPipelineSettings().defaultMaterial, ["Default-ParticleSystem"] = () => GraphicsSettings.GetRenderPipelineSettings().defaultParticleUnlitMaterial, ["Default-Particle"] = () => GraphicsSettings.GetRenderPipelineSettings().defaultParticleUnlitMaterial, ["Default-Terrain-Diffuse"] = () => GraphicsSettings.GetRenderPipelineSettings().defaultTerrainLitMaterial, ["Default-Terrain-Specular"] = () => GraphicsSettings.GetRenderPipelineSettings().defaultTerrainLitMaterial, ["Default-Terrain-Standard"] = () => GraphicsSettings.GetRenderPipelineSettings().defaultTerrainLitMaterial, ["Sprites-Default"] = () => GraphicsSettings.GetRenderPipelineSettings().defaultLitMaterial, ["Sprites-Mask"] = () => GraphicsSettings.GetRenderPipelineSettings().defaultLitMaterial, ["SpatialMappingOcclusion"] = () => AssetDatabase.LoadAssetAtPath("Packages/com.unity.render-pipelines.universal/Runtime/Materials/SpatialMappingOcclusion.mat"), ["SpatialMappingWireframe"] = () => AssetDatabase.LoadAssetAtPath("Packages/com.unity.render-pipelines.universal/Runtime/Materials/SpatialMappingWireframe.mat"), }; public static List<(string materialName, string searchQuery)> GetMaterialSearchList() { List<(string materialName, string searchQuery)> list = new(); foreach (var mat in GetBuiltInMaterials()) { string formattedId = $"<$object:{GlobalObjectId.GetGlobalObjectIdSlow(mat)},UnityEngine.Object$>"; list.Add(($"p: ref={formattedId}", $"{mat.name} is being referenced")); } return list; } public static Material[] GetBuiltInMaterials() { using(ListPool.Get(out var tmp)) { foreach (var materialName in Keys) { var name = materialName + ".mat"; Material mat = null; foreach (var material in AssetDatabaseHelper.FindAssets()) { if (material.name == materialName) { mat = material; break; } } if (mat == null) { mat = AssetDatabase.GetBuiltinExtraResource(name); if (mat == null) { mat = Resources.GetBuiltinResource(name); if (mat == null) { mat = Resources.Load(name); } } } if (mat == null) { Debug.LogError($"Material '{materialName}' not found in built-in resources or project assets."); continue; } tmp.Add(mat); } return tmp.ToArray(); } } } internal class ReadonlyMaterialConverter : RenderPipelineConverter { private static bool s_HasShownWarning = false; public override bool isEnabled { get { if (GraphicsSettings.currentRenderPipeline is not UniversalRenderPipelineAsset) { if (!s_HasShownWarning) Debug.LogWarning("[Render Pipeline Converter] Readonly Material Converter requires URP. Convert your project to URP to use this converter."); s_HasShownWarning = true; return false; } return true; } } public override string name => "Readonly Material Converter"; public override string info => "Converts references to Built-In readonly materials to URP readonly materials. This will create temporarily a .index file and that can take a long time."; public override Type container => typeof(BuiltInToURPConverterContainer); List guids = new(); List assetPaths = new (); internal void Add(string guid, string assetPath) { guids.Add(guid); assetPaths.Add(assetPath); } public override void OnInitialize(InitializeConverterContext ctx, Action callback) { SearchServiceUtils.RunQueuedSearch ( SearchServiceUtils.IndexingOptions.DeepSearch, ReadonlyMaterialMap.GetMaterialSearchList(), (item, description) => { if (GlobalObjectId.TryParse(item.id, out var gid)) { var assetPath = AssetDatabase.GUIDToAssetPath(gid.assetGUID); var itemDescriptor = new ConverterItemDescriptor() { name = assetPath, info = description, }; Add(gid.ToString(), assetPath); ctx.AddAssetToConvert(itemDescriptor); } }, callback ); } internal MaterialReferenceChanger m_MaterialReferenceChanger; public override void OnPreRun() { m_MaterialReferenceChanger = new MaterialReferenceChanger(); } public override void OnPostRun() { m_MaterialReferenceChanger?.Dispose(); m_MaterialReferenceChanger = null; } public override void OnRun(ref RunItemContext ctx) { var errorString = new StringBuilder(); var obj = LoadObject(ref ctx, errorString); if (!m_MaterialReferenceChanger.ReassignUnityObjectMaterials(obj, errorString)) { ctx.didFail = true; ctx.info = errorString.ToString(); } } public override void OnClicked(int index) { EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath(assetPaths[index])); } private Object LoadObject(ref RunItemContext ctx, StringBuilder sb) { var item = ctx.item; var guid = guids[item.index]; if (GlobalObjectId.TryParse(guid, out var gid)) { var obj = GlobalObjectId.GlobalObjectIdentifierToObjectSlow(gid); if (!obj) { // Open container scene if (gid.identifierType == (int)IdentifierType.kSceneObject) { var containerPath = AssetDatabase.GUIDToAssetPath(gid.assetGUID); var mainInstanceID = AssetDatabase.LoadAssetAtPath(containerPath); AssetDatabase.OpenAsset(mainInstanceID); // if we have a prefab open, then we already have the object we need to update var prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); if (prefabStage != null) { obj = mainInstanceID; } // Reload object if it is still null if (obj == null) { obj = GlobalObjectId.GlobalObjectIdentifierToObjectSlow(gid); if (!obj) { sb.AppendLine($"Object {gid.assetGUID} failed to load..."); } } } } return obj; } sb.AppendLine($"Failed to parse Global ID {item.descriptor.info}..."); return null; } } }