using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using UnityEditor.Rendering.Converter;
using UnityEngine;
namespace UnityEditor.Rendering.Universal
{
/// <summary>
/// Filter for the list of converters used in batch mode.
/// </summary>
/// <seealso cref="Converters.RunInBatchMode(UnityEditor.Rendering.Universal.ConverterContainerId, List{UnityEditor.Rendering.Universal.ConverterId}, UnityEditor.Rendering.Universal.ConverterFilter)"/>
public enum ConverterFilter
{
/// <summary>
/// Use this to include converters matching the filter.
/// </summary>
Inclusive,
/// <summary>
/// Use this to exclude converters matching the filter.
/// </summary>
Exclusive
}
/// <summary>
/// The container to run in batch mode.
/// </summary>
/// <seealso cref="Converters.RunInBatchMode(UnityEditor.Rendering.Universal.ConverterContainerId)"/>
public enum ConverterContainerId
{
/// <summary>
/// Use this for Built-in to URP converter.
/// </summary>
BuiltInToURP,
/// <summary>
/// Use this for Built-in to 2D (URP) converter.
/// </summary>
BuiltInToURP2D,
/// <summary>
/// Use this for Built-in and 3D URP to 2D (URP) converter.
/// </summary>
BuiltInAndURPToURP2D,
/// <summary>
/// Use this to upgrade 2D (URP) assets.
/// </summary>
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;
}
}
/// <summary>
/// The converter to run in batch mode.
/// </summary>
/// <seealso cref="Converters.RunInBatchMode(UnityEditor.Rendering.Universal.ConverterContainerId, List{UnityEditor.Rendering.Universal.ConverterId}, UnityEditor.Rendering.Universal.ConverterFilter)"/>
public enum ConverterId
{
/// <summary>
/// Use this for the material converters.
/// </summary>
[BatchModeConverterInfo(ConverterContainerId.BuiltInToURP,typeof(BuiltInToURP3DMaterialUpgrader))]
Material,
/// <summary>
/// Use this for the render settings converters.
/// </summary>
[BatchModeConverterInfo(ConverterContainerId.BuiltInToURP, typeof(RenderSettingsConverter))]
RenderSettings,
/// <summary>
/// Use this for the animation clip converters.
/// </summary>
[BatchModeConverterInfo(ConverterContainerId.BuiltInToURP, typeof(AnimationClipConverter))]
AnimationClip,
/// <summary>
/// Use this for readonly material converters.
/// </summary>
[BatchModeConverterInfo(ConverterContainerId.BuiltInToURP, typeof(ReadonlyMaterialConverter))]
ReadonlyMaterial,
/// <summary>
/// Use this for 2D material conversion
/// </summary>
[BatchModeConverterInfo(ConverterContainerId.BuiltInToURP2D, typeof(BuiltInToURP2DMaterialUpgrader))]
ReadonlyMaterial2D,
/// <summary>
/// Use this for 3D URP material conversion
/// </summary>
[BatchModeConverterInfo(ConverterContainerId.UpgradeURP2DAssets, typeof(BuiltInAndURP3DTo2DMaterialUpgrader))]
URPToReadonlyMaterial2D,
#if PPV2_EXISTS
/// <summary>
/// Use this for post processing V2 converters.
/// </summary>
[BatchModeConverterInfo(ConverterContainerId.BuiltInToURP, typeof(PPv2Converter))]
PPv2,
#endif
/// <summary>
/// Use this for parametric to freeform light converters.
/// </summary>
[BatchModeConverterInfo(ConverterContainerId.UpgradeURP2DAssets, typeof(ParametricToFreeformLightUpgrader))]
ParametricToFreeformLight,
}
/// <summary>
/// Class for the converter framework.
/// </summary>
public static class Converters
{
private static void DumpAvailableConverters()
{
StringBuilder sb = new();
foreach (var converter in TypeCache.GetTypesDerivedFrom<IRenderPipelineConverter>())
{
if (converter.IsAbstract || converter.IsInterface)
continue;
sb.AppendLine(converter.AssemblyQualifiedName);
}
Debug.Log(sb.ToString());
}
/// <summary>
/// Call this method to run all the converters in a specific container in batch mode.
/// </summary>
/// <param name="containerName">The name of the container which will be batched. All Converters in this Container will run if prerequisites are met.</param>
public static void RunInBatchMode(ConverterContainerId containerName)
{
RunInBatchMode(containerName, new List<ConverterId>() { }, 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<BatchModeConverterInfo>();
if (attr != null)
{
if(attr.containerName == containerName)
type = attr.converterType;
}
}
return type != null;
}
/// <summary>
/// Call this method to run a specific list of converters in a specific container in batch mode.
/// </summary>
/// <param name="containerName">The name of the container which will be batched.</param>
/// <param name="converterList">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.</param>
/// <param name="converterFilter">The enum that decide if the list of converters will be included or excluded when batching.</param>
public static void RunInBatchMode(ConverterContainerId containerName, List<ConverterId> converterList, ConverterFilter converterFilter)
{
var types = FilterConverters(containerName, converterList, converterFilter);
RunInBatchMode(types);
}
internal static List<Type> FilterConverters(ConverterContainerId containerName, List<ConverterId> converterList, ConverterFilter converterFilter)
{
Array converters = Enum.GetValues(typeof(ConverterId));
List<Type> 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;
}
/// <summary>
/// Call this method to run a specific list of converters in batch mode.
/// </summary>
/// <param name="converterTypes">The list of converters to run</param>
/// <returns>False if there were errors.</returns>
internal static bool RunInBatchMode(List<Type> 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<IRenderPipelineConverter> 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<IRenderPipelineConverter> converters)
{
foreach (var converter in converters)
{
var sb = new StringBuilder($"Conversion results for item: {converter}:{Environment.NewLine}");
converter.Scan(OnConverterCompleteDataCollection);
void OnConverterCompleteDataCollection(List<IRenderPipelineConverterItem> 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();
}
}
}