606 lines
26 KiB
C#
606 lines
26 KiB
C#
#if PPV2_EXISTS
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using UnityEditor;
|
|
using UnityEditor.Rendering;
|
|
using UnityEditor.SceneManagement;
|
|
using UnityEditor.Search;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.Rendering.Universal;
|
|
using UnityEngine.SceneManagement;
|
|
using BIRPRendering = UnityEngine.Rendering.PostProcessing;
|
|
using Object = UnityEngine.Object;
|
|
using URPRenderingEditor = UnityEditor.Rendering.Universal;
|
|
using URPRendering = UnityEngine.Rendering.Universal;
|
|
|
|
namespace UnityEditor.Rendering.Universal
|
|
{
|
|
internal class PPv2Converter : RenderPipelineConverter
|
|
{
|
|
public override string name => "Post-Processing Stack v2 Converter";
|
|
public override string info =>
|
|
"Converts PPv2 Volumes, Profiles, and Layers to URP Volumes, Profiles, and Cameras. This process creates a temporary .index file and might take a long time.";
|
|
public override Type container => typeof(BuiltInToURPConverterContainer);
|
|
public override bool needsIndexing => true;
|
|
|
|
private IEnumerable<PostProcessEffectSettingsConverter> effectConverters = null;
|
|
|
|
private List<Object> postConversionDestroyables = null;
|
|
|
|
List<string> guids = new List<string>();
|
|
public override void OnInitialize(InitializeConverterContext context, Action callback)
|
|
{
|
|
// Converters should already be set to null on domain reload,
|
|
// but we're doing it here just in case anything somehow lingers.
|
|
effectConverters = null;
|
|
|
|
postConversionDestroyables = new List<Object>();
|
|
|
|
// We are using separate searchContexts here and Adding them in this order:
|
|
// - Components from Prefabs & Scenes (Volumes & Layers)
|
|
// - ScriptableObjects (Profiles)
|
|
//
|
|
// This allows the old objects to be both re-referenced and deleted safely as they are converted in OnRun.
|
|
// The process of converting Volumes will convert Profiles as-needed, and then the explicit followup Profile
|
|
// conversion step will convert any non-referenced assets and delete all old Profiles.
|
|
|
|
// Components First
|
|
using var componentContext =
|
|
Search.SearchService.CreateContext("asset", "urp=convert-ppv2component a=URPConverterIndex");
|
|
using var componentItems = Search.SearchService.Request(componentContext);
|
|
{
|
|
AddSearchItemsAsConverterAssetEntries(componentItems, context);
|
|
}
|
|
|
|
// Then ScriptableObjects
|
|
using var scriptableObjectContext =
|
|
Search.SearchService.CreateContext("asset", "urp=convert-ppv2scriptableobject a=URPConverterIndex ");
|
|
using var scriptableObjectItems = Search.SearchService.Request(scriptableObjectContext);
|
|
{
|
|
AddSearchItemsAsConverterAssetEntries(scriptableObjectItems, context);
|
|
}
|
|
|
|
callback.Invoke();
|
|
}
|
|
|
|
public override void OnRun(ref RunItemContext context)
|
|
{
|
|
var obj = GetContextObject(ref context);
|
|
|
|
if (!obj)
|
|
{
|
|
context.didFail = true;
|
|
context.info = "Could not be converted because the target object was lost.";
|
|
return;
|
|
}
|
|
|
|
BIRPRendering.PostProcessVolume[] oldVolumes = null;
|
|
BIRPRendering.PostProcessLayer[] oldLayers = null;
|
|
|
|
// TODO: Upcoming changes to GlobalObjectIdentifierToObjectSlow will allow
|
|
// this to be inverted, and the else to be deleted.
|
|
#if false
|
|
if (obj is GameObject go)
|
|
{
|
|
oldVolumes = go.GetComponents<BIRPRendering.PostProcessVolume>();
|
|
oldLayers = go.GetComponents<BIRPRendering.PostProcessLayer>();
|
|
}
|
|
else if (obj is MonoBehaviour mb)
|
|
{
|
|
oldVolumes = mb.GetComponents<BIRPRendering.PostProcessVolume>();
|
|
oldLayers = mb.GetComponents<BIRPRendering.PostProcessLayer>();
|
|
}
|
|
#else
|
|
if (obj is GameObject go)
|
|
{
|
|
oldVolumes = go.GetComponentsInChildren<BIRPRendering.PostProcessVolume>();
|
|
oldLayers = go.GetComponentsInChildren<BIRPRendering.PostProcessLayer>();
|
|
}
|
|
else if (obj is MonoBehaviour mb)
|
|
{
|
|
oldVolumes = mb.GetComponentsInChildren<BIRPRendering.PostProcessVolume>();
|
|
oldLayers = mb.GetComponentsInChildren<BIRPRendering.PostProcessLayer>();
|
|
}
|
|
#endif
|
|
|
|
// Note: even if nothing needs to be converted, that should still count as success,
|
|
// though it shouldn't ever actually occur.
|
|
var succeeded = true;
|
|
var errorString = new StringBuilder();
|
|
|
|
if (effectConverters == null ||
|
|
effectConverters.Count() == 0 ||
|
|
effectConverters.Any(converter => converter == null))
|
|
{
|
|
effectConverters = GetAllBIRPConverters();
|
|
}
|
|
|
|
if (oldVolumes != null)
|
|
{
|
|
foreach (var oldVolume in oldVolumes)
|
|
{
|
|
ConvertVolume(oldVolume, ref succeeded, errorString);
|
|
}
|
|
}
|
|
|
|
if (oldLayers != null)
|
|
{
|
|
foreach (var oldLayer in oldLayers)
|
|
{
|
|
ConvertLayer(oldLayer, ref succeeded, errorString);
|
|
}
|
|
}
|
|
|
|
if (obj is BIRPRendering.PostProcessProfile oldProfile)
|
|
{
|
|
ConvertProfile(oldProfile, ref succeeded, errorString);
|
|
}
|
|
|
|
if (!succeeded)
|
|
{
|
|
context.didFail = true;
|
|
context.info = errorString.ToString();
|
|
}
|
|
else
|
|
{
|
|
var currentScene = SceneManager.GetActiveScene();
|
|
EditorSceneManager.SaveScene(currentScene);
|
|
}
|
|
}
|
|
|
|
public override void OnPostRun()
|
|
{
|
|
for (var i = 0; i < postConversionDestroyables.Count; i++)
|
|
{
|
|
Object.DestroyImmediate(postConversionDestroyables[i], allowDestroyingAssets: true);
|
|
}
|
|
|
|
postConversionDestroyables.Clear();
|
|
}
|
|
|
|
public override void OnClicked(int index)
|
|
{
|
|
if (GlobalObjectId.TryParse(guids[index], out var gid))
|
|
{
|
|
var containerPath = AssetDatabase.GUIDToAssetPath(gid.assetGUID);
|
|
EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath<Object>(containerPath));
|
|
}
|
|
}
|
|
|
|
private void AddSearchItemsAsConverterAssetEntries(ISearchList searchItems, InitializeConverterContext context)
|
|
{
|
|
foreach (var searchItem in searchItems)
|
|
{
|
|
if (searchItem == null || !GlobalObjectId.TryParse(searchItem.id, out var globalId))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var description = searchItem.provider.fetchDescription(searchItem, searchItem.context);
|
|
|
|
var item = new ConverterItemDescriptor()
|
|
{
|
|
name = description.Split('/').Last().Split('.').First(),
|
|
info = $"{ReturnType(globalId)}",
|
|
};
|
|
|
|
guids.Add(globalId.ToString());
|
|
context.AddAssetToConvert(item);
|
|
}
|
|
}
|
|
|
|
string ReturnType(GlobalObjectId gid)
|
|
{
|
|
var containerPath = AssetDatabase.GUIDToAssetPath(gid.assetGUID);
|
|
return AssetDatabase.LoadAssetAtPath<Object>(containerPath).GetType().ToString().Split('.').Last();
|
|
}
|
|
|
|
private Object GetContextObject(ref RunItemContext ctx)
|
|
{
|
|
var item = ctx.item;
|
|
var guid = guids[item.index];
|
|
|
|
if (GlobalObjectId.TryParse(guid, out var globalId))
|
|
{
|
|
// Try loading the object
|
|
// TODO: Upcoming changes to GlobalObjectIdentifierToObjectSlow will allow it
|
|
// to return direct references to prefabs and their children.
|
|
// Once that change happens there are several items which should be adjusted.
|
|
var obj = GlobalObjectId.GlobalObjectIdentifierToObjectSlow(globalId);
|
|
|
|
// If the object was not loaded, it is probably part of an unopened scene;
|
|
// if so, then the solution is to first load the scene here.
|
|
var objIsInSceneOrPrefab = globalId.identifierType == 2; // 2 is IdentifierType.kSceneObject
|
|
if (!obj &&
|
|
objIsInSceneOrPrefab)
|
|
{
|
|
// Open the Containing Scene Asset in the Hierarchy so the Object can be manipulated
|
|
var mainAssetPath = AssetDatabase.GUIDToAssetPath(globalId.assetGUID);
|
|
var mainAsset = AssetDatabase.LoadAssetAtPath<Object>(mainAssetPath);
|
|
AssetDatabase.OpenAsset(mainAsset);
|
|
|
|
// If a prefab stage was opened, then mainAsset is the root of the
|
|
// prefab that contains the target object, so reference that for now,
|
|
// until GlobalObjectIdentifierToObjectSlow is updated
|
|
if (PrefabStageUtility.GetCurrentPrefabStage() != null)
|
|
{
|
|
obj = mainAsset;
|
|
}
|
|
|
|
// Reload object if it is still null (because it's in a previously unopened scene)
|
|
if (!obj)
|
|
{
|
|
obj = GlobalObjectId.GlobalObjectIdentifierToObjectSlow(globalId);
|
|
if (!obj)
|
|
{
|
|
ctx.didFail = true;
|
|
ctx.info = $"Object {globalId.assetGUID} failed to load...";
|
|
}
|
|
}
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
ctx.didFail = true;
|
|
ctx.info = $"Failed to parse Global ID {item.descriptor.info}...";
|
|
|
|
return null;
|
|
}
|
|
|
|
#region Conversion_Entry_Points
|
|
|
|
private void ConvertVolume(BIRPRendering.PostProcessVolume oldVolume, ref bool succeeded,
|
|
StringBuilder errorString)
|
|
{
|
|
if (!succeeded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!oldVolume)
|
|
{
|
|
// TODO: unless there's good way to tell the if the object is just missing because it was already
|
|
// converted as part of an earlier conversion object, then these two lines should be commented
|
|
// out or removed. It should still return though.
|
|
// succeeded = false;
|
|
// errorString.AppendLine("PPv2 PostProcessVolume failed to be converted because the original asset reference was lost during conversion.");
|
|
return;
|
|
}
|
|
|
|
if (PrefabUtility.IsPartOfPrefabInstance(oldVolume) &&
|
|
!PrefabUtility.IsAddedComponentOverride(oldVolume))
|
|
{
|
|
// This is a property override on an instance of the component,
|
|
// so override the component instance with the modifications.
|
|
succeeded = ConvertVolumeInstance(oldVolume, errorString);
|
|
}
|
|
else
|
|
{
|
|
// The entire component is unique, so just convert it
|
|
succeeded = ConvertVolumeComponent(oldVolume, errorString);
|
|
}
|
|
}
|
|
|
|
private void ConvertLayer(BIRPRendering.PostProcessLayer oldLayer, ref bool succeeded,
|
|
StringBuilder errorString)
|
|
{
|
|
if (!succeeded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!oldLayer)
|
|
{
|
|
// TODO: unless there's good way to tell the if the object is just missing because it was already
|
|
// converted as part of an earlier conversion object, then these two lines should be commented
|
|
// out or removed. It should still return though.
|
|
// succeeded = false;
|
|
// errorString.AppendLine("PPv2 PostProcessLayer failed to be converted because the original asset reference was lost during conversion.");
|
|
return;
|
|
}
|
|
|
|
if (PrefabUtility.IsPartOfPrefabInstance(oldLayer) &&
|
|
!PrefabUtility.IsAddedComponentOverride(oldLayer))
|
|
{
|
|
// This is a property override on an instance of the component,
|
|
// so override the component instance with the modifications.
|
|
succeeded = ConvertLayerInstance(oldLayer, errorString);
|
|
}
|
|
else
|
|
{
|
|
// The entire component is unique, so just convert it
|
|
succeeded = ConvertLayerComponent(oldLayer, errorString);
|
|
}
|
|
}
|
|
|
|
private void ConvertProfile(BIRPRendering.PostProcessProfile oldProfile, ref bool succeeded,
|
|
StringBuilder errorString)
|
|
{
|
|
if (!succeeded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!oldProfile)
|
|
{
|
|
errorString.AppendLine(
|
|
"PPv2 PostProcessProfile failed to be converted because the original asset reference was lost during conversion.");
|
|
return;
|
|
}
|
|
|
|
ConvertVolumeProfileAsset(oldProfile, errorString, ref succeeded);
|
|
|
|
// TODO:
|
|
// - Perhaps old Profiles should only be deleted if they actually no longer have references,
|
|
// just in case some some Volume conversions are skipped and still need references for future conversion.
|
|
// - Alternatively, leave deletion of Profiles entirely to the user. (I think this is preferred)
|
|
}
|
|
|
|
#endregion Conversion_Entry_Points
|
|
|
|
private bool ConvertVolumeComponent(BIRPRendering.PostProcessVolume oldVolume, StringBuilder errorString)
|
|
{
|
|
// Don't convert if it appears to already have been converted.
|
|
if (oldVolume.GetComponent<Volume>()) return true;
|
|
|
|
var gameObject = oldVolume.gameObject;
|
|
var newVolume = gameObject.AddComponent<Volume>();
|
|
|
|
newVolume.priority = oldVolume.priority;
|
|
newVolume.weight = oldVolume.weight;
|
|
newVolume.blendDistance = oldVolume.blendDistance;
|
|
newVolume.isGlobal = oldVolume.isGlobal;
|
|
newVolume.enabled = oldVolume.enabled;
|
|
|
|
var success = true;
|
|
newVolume.sharedProfile = ConvertVolumeProfileAsset(oldVolume.sharedProfile, errorString, ref success);
|
|
|
|
if (PrefabUtility.IsPartOfPrefabAsset(oldVolume))
|
|
{
|
|
postConversionDestroyables.Add(oldVolume);
|
|
}
|
|
else
|
|
{
|
|
Object.DestroyImmediate(oldVolume, allowDestroyingAssets: true);
|
|
}
|
|
|
|
EditorUtility.SetDirty(gameObject);
|
|
|
|
return success;
|
|
}
|
|
|
|
private bool ConvertVolumeInstance(BIRPRendering.PostProcessVolume oldVolume, StringBuilder errorString)
|
|
{
|
|
// First get a reference to the local instance of the converted component
|
|
// which may require immediately converting it at its origin location first.
|
|
var newVolumeInstance = oldVolume.GetComponent<Volume>();
|
|
if (!newVolumeInstance)
|
|
{
|
|
var oldVolumeOrigin = PrefabUtility.GetCorrespondingObjectFromSource(oldVolume);
|
|
|
|
if (!ConvertVolumeComponent(oldVolumeOrigin, errorString))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
PrefabUtility.SavePrefabAsset(oldVolumeOrigin.gameObject);
|
|
|
|
newVolumeInstance = oldVolume.GetComponent<Volume>();
|
|
|
|
if (!newVolumeInstance)
|
|
{
|
|
errorString.AppendLine(
|
|
"PPv2 PostProcessVolume failed to be converted because the instance object did not inherit the converted Prefab source.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool success = true;
|
|
var oldModifications = PrefabUtility.GetPropertyModifications(oldVolume);
|
|
foreach (var oldModification in oldModifications)
|
|
{
|
|
if (oldModification.target is BIRPRendering.PostProcessVolume)
|
|
{
|
|
if (oldModification.propertyPath.EndsWith("priority", StringComparison.InvariantCultureIgnoreCase))
|
|
newVolumeInstance.priority = oldVolume.priority;
|
|
else if (oldModification.propertyPath.EndsWith("weight",
|
|
StringComparison.InvariantCultureIgnoreCase))
|
|
newVolumeInstance.weight = oldVolume.weight;
|
|
else if (oldModification.propertyPath.EndsWith("blendDistance",
|
|
StringComparison.InvariantCultureIgnoreCase))
|
|
newVolumeInstance.blendDistance = oldVolume.blendDistance;
|
|
else if (oldModification.propertyPath.EndsWith("isGlobal",
|
|
StringComparison.InvariantCultureIgnoreCase))
|
|
newVolumeInstance.isGlobal = oldVolume.isGlobal;
|
|
else if (oldModification.propertyPath.EndsWith("enabled",
|
|
StringComparison.InvariantCultureIgnoreCase))
|
|
newVolumeInstance.enabled = oldVolume.enabled;
|
|
else if (oldModification.propertyPath.EndsWith("sharedProfile",
|
|
StringComparison.InvariantCultureIgnoreCase))
|
|
newVolumeInstance.sharedProfile =
|
|
ConvertVolumeProfileAsset(oldVolume.sharedProfile, errorString, ref success);
|
|
|
|
EditorUtility.SetDirty(newVolumeInstance);
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
private bool ConvertLayerComponent(BIRPRendering.PostProcessLayer oldLayer, StringBuilder errorString)
|
|
{
|
|
var siblingCamera = oldLayer.GetComponent<Camera>().GetUniversalAdditionalCameraData();
|
|
|
|
// PostProcessLayer requires a sibling Camera component, but
|
|
// we check it here just in case something weird went happened.
|
|
if (!siblingCamera)
|
|
{
|
|
errorString.AppendLine(
|
|
"PPv2 PostProcessLayer failed to be converted because the instance object was missing a required sibling Camera component.");
|
|
return false;
|
|
}
|
|
|
|
// The presence of a PostProcessLayer implies the Camera should render post-processes
|
|
siblingCamera.renderPostProcessing = true;
|
|
|
|
siblingCamera.volumeLayerMask = oldLayer.volumeLayer;
|
|
siblingCamera.volumeTrigger = oldLayer.volumeTrigger;
|
|
siblingCamera.stopNaN = oldLayer.stopNaNPropagation;
|
|
|
|
siblingCamera.antialiasingQuality =
|
|
(URPRendering.AntialiasingQuality)oldLayer.subpixelMorphologicalAntialiasing.quality;
|
|
|
|
switch (oldLayer.antialiasingMode)
|
|
{
|
|
case BIRPRendering.PostProcessLayer.Antialiasing.None:
|
|
siblingCamera.antialiasing = URPRendering.AntialiasingMode.None;
|
|
break;
|
|
case BIRPRendering.PostProcessLayer.Antialiasing.FastApproximateAntialiasing:
|
|
siblingCamera.antialiasing = URPRendering.AntialiasingMode.FastApproximateAntialiasing;
|
|
break;
|
|
case BIRPRendering.PostProcessLayer.Antialiasing.SubpixelMorphologicalAntialiasing:
|
|
siblingCamera.antialiasing = URPRendering.AntialiasingMode.SubpixelMorphologicalAntiAliasing;
|
|
break;
|
|
default:
|
|
// Default to the the most performant mode, since "None" is an explicit option.
|
|
siblingCamera.antialiasing = URPRendering.AntialiasingMode.FastApproximateAntialiasing;
|
|
break;
|
|
}
|
|
|
|
if (PrefabUtility.IsPartOfPrefabAsset(oldLayer))
|
|
{
|
|
postConversionDestroyables.Add(oldLayer);
|
|
}
|
|
else
|
|
{
|
|
Object.DestroyImmediate(oldLayer, allowDestroyingAssets: true);
|
|
}
|
|
|
|
EditorUtility.SetDirty(siblingCamera.gameObject);
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool ConvertLayerInstance(BIRPRendering.PostProcessLayer oldLayer, StringBuilder errorString)
|
|
{
|
|
// First get a reference to the local instance of the camera (which is required by PostProcessingLayer)
|
|
var siblingCamera = oldLayer.GetComponent<Camera>().GetUniversalAdditionalCameraData();
|
|
if (!siblingCamera)
|
|
{
|
|
errorString.AppendLine(
|
|
"PPv2 PostProcessLayer failed to be converted because the instance object was missing a required sibling Camera component.");
|
|
return false;
|
|
}
|
|
|
|
var oldModifications = PrefabUtility.GetPropertyModifications(oldLayer);
|
|
foreach (var oldModification in oldModifications)
|
|
{
|
|
if (oldModification.target is BIRPRendering.PostProcessLayer)
|
|
{
|
|
if (oldModification.propertyPath.EndsWith("volumeLayer",
|
|
StringComparison.InvariantCultureIgnoreCase))
|
|
siblingCamera.volumeLayerMask = oldLayer.volumeLayer;
|
|
else if (oldModification.propertyPath.EndsWith("volumeTrigger",
|
|
StringComparison.InvariantCultureIgnoreCase))
|
|
siblingCamera.volumeTrigger = oldLayer.volumeTrigger;
|
|
else if (oldModification.propertyPath.EndsWith("stopNaNPropagation",
|
|
StringComparison.InvariantCultureIgnoreCase))
|
|
siblingCamera.stopNaN = oldLayer.stopNaNPropagation;
|
|
else if (oldModification.propertyPath.EndsWith("quality",
|
|
StringComparison.InvariantCultureIgnoreCase))
|
|
siblingCamera.antialiasingQuality =
|
|
(URPRendering.AntialiasingQuality)oldLayer.subpixelMorphologicalAntialiasing.quality;
|
|
else if (oldModification.propertyPath.EndsWith("antialiasingMode",
|
|
StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
switch (oldLayer.antialiasingMode)
|
|
{
|
|
case BIRPRendering.PostProcessLayer.Antialiasing.None:
|
|
siblingCamera.antialiasing = URPRendering.AntialiasingMode.None;
|
|
break;
|
|
case BIRPRendering.PostProcessLayer.Antialiasing.FastApproximateAntialiasing:
|
|
siblingCamera.antialiasing = URPRendering.AntialiasingMode.FastApproximateAntialiasing;
|
|
break;
|
|
case BIRPRendering.PostProcessLayer.Antialiasing.SubpixelMorphologicalAntialiasing:
|
|
siblingCamera.antialiasing =
|
|
URPRendering.AntialiasingMode.SubpixelMorphologicalAntiAliasing;
|
|
break;
|
|
default:
|
|
// Default to the the most performant mode, since "None" is an explicit option.
|
|
siblingCamera.antialiasing = URPRendering.AntialiasingMode.FastApproximateAntialiasing;
|
|
break;
|
|
}
|
|
}
|
|
|
|
EditorUtility.SetDirty(siblingCamera);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private VolumeProfile ConvertVolumeProfileAsset(BIRPRendering.PostProcessProfile oldProfile,
|
|
StringBuilder errorString, ref bool success)
|
|
{
|
|
// Don't convert if it appears to already have been converted.
|
|
if (!oldProfile) return null;
|
|
|
|
var oldPath = AssetDatabase.GetAssetPath(oldProfile);
|
|
var oldDirectory = Path.GetDirectoryName(oldPath);
|
|
var oldName = Path.GetFileNameWithoutExtension(oldPath);
|
|
var newPath = Path.Combine(oldDirectory, $"{oldName}(URP).asset");
|
|
if (File.Exists(newPath))
|
|
{
|
|
return AssetDatabase.LoadAssetAtPath<VolumeProfile>(newPath);
|
|
}
|
|
|
|
var newProfile = ScriptableObject.CreateInstance<VolumeProfile>();
|
|
try
|
|
{
|
|
AssetDatabase.CreateAsset(newProfile, newPath);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
errorString.AppendLine($"PPv2 PostProcessLayer failed to be converted with exception:\n{e}");
|
|
success = false;
|
|
|
|
if (!newProfile) return null;
|
|
}
|
|
|
|
foreach (var oldSettings in oldProfile.settings)
|
|
{
|
|
foreach (var effectConverter in effectConverters)
|
|
{
|
|
effectConverter.AddConvertedProfileSettingsToProfile(oldSettings, newProfile);
|
|
}
|
|
}
|
|
|
|
EditorUtility.SetDirty(newProfile);
|
|
|
|
return newProfile;
|
|
}
|
|
|
|
public IEnumerable<PostProcessEffectSettingsConverter> GetAllBIRPConverters()
|
|
{
|
|
var baseType = typeof(PostProcessEffectSettingsConverter);
|
|
|
|
var assembly = Assembly.GetAssembly(baseType);
|
|
var derivedTypes = assembly
|
|
.GetTypes()
|
|
.Where(t =>
|
|
t.BaseType != null &&
|
|
t.BaseType == baseType)
|
|
.Select(t => ScriptableObject.CreateInstance(t) as PostProcessEffectSettingsConverter);
|
|
|
|
return derivedTypes;
|
|
}
|
|
}
|
|
}
|
|
#endif
|