using System; using System.Collections.Generic; using System.Reflection; using System.Text; using UnityEditor.Rendering.Converter; using UnityEngine; namespace UnityEditor.Rendering.Universal { /// /// Filter for the list of converters used in batch mode. /// /// public enum ConverterFilter { /// /// Use this to include converters matching the filter. /// Inclusive, /// /// Use this to exclude converters matching the filter. /// Exclusive } /// /// The container to run in batch mode. /// /// public enum ConverterContainerId { /// /// Use this for Built-in to URP converter. /// BuiltInToURP, /// /// Use this for Built-in to 2D (URP) converter. /// BuiltInToURP2D, /// /// Use this for Built-in and 3D URP to 2D (URP) converter. /// BuiltInAndURPToURP2D, /// /// Use this to upgrade 2D (URP) assets. /// UpgradeURP2DAssets, } [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] internal class BatchModeConverterInfo : Attribute { public Type converterType { get; } public ConverterContainerId containerName {get;} public BatchModeConverterInfo(ConverterContainerId containerName, Type converterType) { this.converterType = converterType; this.containerName = containerName; } } /// /// The converter to run in batch mode. /// /// public enum ConverterId { /// /// Use this for the material converters. /// [BatchModeConverterInfo(ConverterContainerId.BuiltInToURP,typeof(BuiltInToURP3DMaterialUpgrader))] Material, /// /// Use this for the render settings converters. /// [BatchModeConverterInfo(ConverterContainerId.BuiltInToURP, typeof(RenderSettingsConverter))] RenderSettings, /// /// Use this for the animation clip converters. /// [BatchModeConverterInfo(ConverterContainerId.BuiltInToURP, typeof(AnimationClipConverter))] AnimationClip, /// /// Use this for readonly material converters. /// [BatchModeConverterInfo(ConverterContainerId.BuiltInToURP, typeof(ReadonlyMaterialConverter))] ReadonlyMaterial, /// /// Use this for 2D material conversion /// [BatchModeConverterInfo(ConverterContainerId.BuiltInToURP2D, typeof(BuiltInToURP2DMaterialUpgrader))] ReadonlyMaterial2D, /// /// Use this for 3D URP material conversion /// [BatchModeConverterInfo(ConverterContainerId.UpgradeURP2DAssets, typeof(BuiltInAndURP3DTo2DMaterialUpgrader))] URPToReadonlyMaterial2D, #if PPV2_EXISTS /// /// Use this for post processing V2 converters. /// [BatchModeConverterInfo(ConverterContainerId.BuiltInToURP, typeof(PPv2Converter))] PPv2, #endif /// /// Use this for parametric to freeform light converters. /// [BatchModeConverterInfo(ConverterContainerId.UpgradeURP2DAssets, typeof(ParametricToFreeformLightUpgrader))] ParametricToFreeformLight, } /// /// Class for the converter framework. /// public static class Converters { private static void DumpAvailableConverters() { StringBuilder sb = new(); foreach (var converter in TypeCache.GetTypesDerivedFrom()) { if (converter.IsAbstract || converter.IsInterface) continue; sb.AppendLine(converter.AssemblyQualifiedName); } Debug.Log(sb.ToString()); } /// /// Call this method to run all the converters in a specific container in batch mode. /// /// The name of the container which will be batched. All Converters in this Container will run if prerequisites are met. public static void RunInBatchMode(ConverterContainerId containerName) { RunInBatchMode(containerName, new List() { }, ConverterFilter.Exclusive); } internal static bool TryGetTypeInContainer(ConverterId value, ConverterContainerId containerName, out Type type) { type = null; var memberInfo = typeof(ConverterId).GetMember(value.ToString()); if (memberInfo.Length > 0) { var attr = memberInfo[0].GetCustomAttribute(); if (attr != null) { if(attr.containerName == containerName) type = attr.converterType; } } return type != null; } /// /// Call this method to run a specific list of converters in a specific container in batch mode. /// /// The name of the container which will be batched. /// The list of converters that will be either included or excluded from batching. These converters need to be part of the passed in container for them to run. /// The enum that decide if the list of converters will be included or excluded when batching. public static void RunInBatchMode(ConverterContainerId containerName, List converterList, ConverterFilter converterFilter) { var types = FilterConverters(containerName, converterList, converterFilter); RunInBatchMode(types); } internal static List FilterConverters(ConverterContainerId containerName, List converterList, ConverterFilter converterFilter) { Array converters = Enum.GetValues(typeof(ConverterId)); List converterTypes = new(); foreach (object value in converters) { var converterEnum = (ConverterId)value; if (TryGetTypeInContainer(converterEnum, containerName, out var type)) { bool inFilter = converterList.Contains(converterEnum); if ((converterFilter == ConverterFilter.Inclusive) ^ !inFilter) converterTypes.Add(type); } } return converterTypes; } /// /// Call this method to run a specific list of converters in batch mode. /// /// The list of converters to run /// False if there were errors. internal static bool RunInBatchMode(List converterTypes) { Debug.LogWarning("Using this API can lead to incomplete or unpredictable conversion outcomes. For reliable results, please perform the conversion via the dedicated window: Window > Rendering > Render Pipeline Converter."); List convertersToExecute = new(); bool errors = false; foreach (var type in converterTypes) { try { var instance = Activator.CreateInstance(type) as IRenderPipelineConverter; if (instance == null) { Debug.LogWarning($"{type} is not a converter type."); errors = true; } else convertersToExecute.Add(instance); } catch { Debug.LogWarning($"Unable to create instance of type {type}."); errors = true; } } if (errors) { Debug.LogWarning($"Please use any of the given Converter Types."); DumpAvailableConverters(); } BatchConverters(convertersToExecute); return !errors; } internal static void BatchConverters(List converters) { foreach (var converter in converters) { var sb = new StringBuilder($"Conversion results for item: {converter}:{Environment.NewLine}"); converter.Scan(OnConverterCompleteDataCollection); void OnConverterCompleteDataCollection(List items) { converter.BeforeConvert(); foreach (var item in items) { var status = converter.Convert(item, out var message); switch (status) { case Status.Pending: throw new InvalidOperationException("Converter returned a pending status when converting. This is not supported."); case Status.Error: case Status.Warning: sb.AppendLine($"- {item.name} ({status}) ({message})"); break; case Status.Success: { sb.AppendLine($"- {item.name} ({status})"); message = "Conversion successful!"; } break; } } converter.AfterConvert(); Debug.Log(sb.ToString()); } } AssetDatabase.SaveAssets(); } } }