Нет описания
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. #if PPV2_EXISTS
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Text;
  8. using UnityEditor;
  9. using UnityEditor.Rendering;
  10. using UnityEditor.SceneManagement;
  11. using UnityEditor.Search;
  12. using UnityEngine;
  13. using UnityEngine.Rendering;
  14. using UnityEngine.Rendering.Universal;
  15. using UnityEngine.SceneManagement;
  16. using BIRPRendering = UnityEngine.Rendering.PostProcessing;
  17. using Object = UnityEngine.Object;
  18. using URPRenderingEditor = UnityEditor.Rendering.Universal;
  19. using URPRendering = UnityEngine.Rendering.Universal;
  20. namespace UnityEditor.Rendering.Universal
  21. {
  22. internal class PPv2Converter : RenderPipelineConverter
  23. {
  24. public override string name => "Post-Processing Stack v2 Converter";
  25. public override string info =>
  26. "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.";
  27. public override Type container => typeof(BuiltInToURPConverterContainer);
  28. public override bool needsIndexing => true;
  29. private IEnumerable<PostProcessEffectSettingsConverter> effectConverters = null;
  30. private List<Object> postConversionDestroyables = null;
  31. List<string> guids = new List<string>();
  32. public override void OnInitialize(InitializeConverterContext context, Action callback)
  33. {
  34. // Converters should already be set to null on domain reload,
  35. // but we're doing it here just in case anything somehow lingers.
  36. effectConverters = null;
  37. postConversionDestroyables = new List<Object>();
  38. // We are using separate searchContexts here and Adding them in this order:
  39. // - Components from Prefabs & Scenes (Volumes & Layers)
  40. // - ScriptableObjects (Profiles)
  41. //
  42. // This allows the old objects to be both re-referenced and deleted safely as they are converted in OnRun.
  43. // The process of converting Volumes will convert Profiles as-needed, and then the explicit followup Profile
  44. // conversion step will convert any non-referenced assets and delete all old Profiles.
  45. // Components First
  46. using var componentContext =
  47. Search.SearchService.CreateContext("asset", "urp=convert-ppv2component a=URPConverterIndex");
  48. using var componentItems = Search.SearchService.Request(componentContext);
  49. {
  50. AddSearchItemsAsConverterAssetEntries(componentItems, context);
  51. }
  52. // Then ScriptableObjects
  53. using var scriptableObjectContext =
  54. Search.SearchService.CreateContext("asset", "urp=convert-ppv2scriptableobject a=URPConverterIndex ");
  55. using var scriptableObjectItems = Search.SearchService.Request(scriptableObjectContext);
  56. {
  57. AddSearchItemsAsConverterAssetEntries(scriptableObjectItems, context);
  58. }
  59. callback.Invoke();
  60. }
  61. public override void OnRun(ref RunItemContext context)
  62. {
  63. var obj = GetContextObject(ref context);
  64. if (!obj)
  65. {
  66. context.didFail = true;
  67. context.info = "Could not be converted because the target object was lost.";
  68. return;
  69. }
  70. BIRPRendering.PostProcessVolume[] oldVolumes = null;
  71. BIRPRendering.PostProcessLayer[] oldLayers = null;
  72. // TODO: Upcoming changes to GlobalObjectIdentifierToObjectSlow will allow
  73. // this to be inverted, and the else to be deleted.
  74. #if false
  75. if (obj is GameObject go)
  76. {
  77. oldVolumes = go.GetComponents<BIRPRendering.PostProcessVolume>();
  78. oldLayers = go.GetComponents<BIRPRendering.PostProcessLayer>();
  79. }
  80. else if (obj is MonoBehaviour mb)
  81. {
  82. oldVolumes = mb.GetComponents<BIRPRendering.PostProcessVolume>();
  83. oldLayers = mb.GetComponents<BIRPRendering.PostProcessLayer>();
  84. }
  85. #else
  86. if (obj is GameObject go)
  87. {
  88. oldVolumes = go.GetComponentsInChildren<BIRPRendering.PostProcessVolume>();
  89. oldLayers = go.GetComponentsInChildren<BIRPRendering.PostProcessLayer>();
  90. }
  91. else if (obj is MonoBehaviour mb)
  92. {
  93. oldVolumes = mb.GetComponentsInChildren<BIRPRendering.PostProcessVolume>();
  94. oldLayers = mb.GetComponentsInChildren<BIRPRendering.PostProcessLayer>();
  95. }
  96. #endif
  97. // Note: even if nothing needs to be converted, that should still count as success,
  98. // though it shouldn't ever actually occur.
  99. var succeeded = true;
  100. var errorString = new StringBuilder();
  101. if (effectConverters == null ||
  102. effectConverters.Count() == 0 ||
  103. effectConverters.Any(converter => converter == null))
  104. {
  105. effectConverters = GetAllBIRPConverters();
  106. }
  107. if (oldVolumes != null)
  108. {
  109. foreach (var oldVolume in oldVolumes)
  110. {
  111. ConvertVolume(oldVolume, ref succeeded, errorString);
  112. }
  113. }
  114. if (oldLayers != null)
  115. {
  116. foreach (var oldLayer in oldLayers)
  117. {
  118. ConvertLayer(oldLayer, ref succeeded, errorString);
  119. }
  120. }
  121. if (obj is BIRPRendering.PostProcessProfile oldProfile)
  122. {
  123. ConvertProfile(oldProfile, ref succeeded, errorString);
  124. }
  125. if (!succeeded)
  126. {
  127. context.didFail = true;
  128. context.info = errorString.ToString();
  129. }
  130. else
  131. {
  132. var currentScene = SceneManager.GetActiveScene();
  133. EditorSceneManager.SaveScene(currentScene);
  134. }
  135. }
  136. public override void OnPostRun()
  137. {
  138. for (var i = 0; i < postConversionDestroyables.Count; i++)
  139. {
  140. Object.DestroyImmediate(postConversionDestroyables[i], allowDestroyingAssets: true);
  141. }
  142. postConversionDestroyables.Clear();
  143. }
  144. public override void OnClicked(int index)
  145. {
  146. if (GlobalObjectId.TryParse(guids[index], out var gid))
  147. {
  148. var containerPath = AssetDatabase.GUIDToAssetPath(gid.assetGUID);
  149. EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath<Object>(containerPath));
  150. }
  151. }
  152. private void AddSearchItemsAsConverterAssetEntries(ISearchList searchItems, InitializeConverterContext context)
  153. {
  154. foreach (var searchItem in searchItems)
  155. {
  156. if (searchItem == null || !GlobalObjectId.TryParse(searchItem.id, out var globalId))
  157. {
  158. continue;
  159. }
  160. var description = searchItem.provider.fetchDescription(searchItem, searchItem.context);
  161. var item = new ConverterItemDescriptor()
  162. {
  163. name = description.Split('/').Last().Split('.').First(),
  164. info = $"{ReturnType(globalId)}",
  165. };
  166. guids.Add(globalId.ToString());
  167. context.AddAssetToConvert(item);
  168. }
  169. }
  170. string ReturnType(GlobalObjectId gid)
  171. {
  172. var containerPath = AssetDatabase.GUIDToAssetPath(gid.assetGUID);
  173. return AssetDatabase.LoadAssetAtPath<Object>(containerPath).GetType().ToString().Split('.').Last();
  174. }
  175. private Object GetContextObject(ref RunItemContext ctx)
  176. {
  177. var item = ctx.item;
  178. var guid = guids[item.index];
  179. if (GlobalObjectId.TryParse(guid, out var globalId))
  180. {
  181. // Try loading the object
  182. // TODO: Upcoming changes to GlobalObjectIdentifierToObjectSlow will allow it
  183. // to return direct references to prefabs and their children.
  184. // Once that change happens there are several items which should be adjusted.
  185. var obj = GlobalObjectId.GlobalObjectIdentifierToObjectSlow(globalId);
  186. // If the object was not loaded, it is probably part of an unopened scene;
  187. // if so, then the solution is to first load the scene here.
  188. var objIsInSceneOrPrefab = globalId.identifierType == 2; // 2 is IdentifierType.kSceneObject
  189. if (!obj &&
  190. objIsInSceneOrPrefab)
  191. {
  192. // Open the Containing Scene Asset in the Hierarchy so the Object can be manipulated
  193. var mainAssetPath = AssetDatabase.GUIDToAssetPath(globalId.assetGUID);
  194. var mainAsset = AssetDatabase.LoadAssetAtPath<Object>(mainAssetPath);
  195. AssetDatabase.OpenAsset(mainAsset);
  196. // If a prefab stage was opened, then mainAsset is the root of the
  197. // prefab that contains the target object, so reference that for now,
  198. // until GlobalObjectIdentifierToObjectSlow is updated
  199. if (PrefabStageUtility.GetCurrentPrefabStage() != null)
  200. {
  201. obj = mainAsset;
  202. }
  203. // Reload object if it is still null (because it's in a previously unopened scene)
  204. if (!obj)
  205. {
  206. obj = GlobalObjectId.GlobalObjectIdentifierToObjectSlow(globalId);
  207. if (!obj)
  208. {
  209. ctx.didFail = true;
  210. ctx.info = $"Object {globalId.assetGUID} failed to load...";
  211. }
  212. }
  213. }
  214. return obj;
  215. }
  216. ctx.didFail = true;
  217. ctx.info = $"Failed to parse Global ID {item.descriptor.info}...";
  218. return null;
  219. }
  220. #region Conversion_Entry_Points
  221. private void ConvertVolume(BIRPRendering.PostProcessVolume oldVolume, ref bool succeeded,
  222. StringBuilder errorString)
  223. {
  224. if (!succeeded)
  225. {
  226. return;
  227. }
  228. if (!oldVolume)
  229. {
  230. // TODO: unless there's good way to tell the if the object is just missing because it was already
  231. // converted as part of an earlier conversion object, then these two lines should be commented
  232. // out or removed. It should still return though.
  233. // succeeded = false;
  234. // errorString.AppendLine("PPv2 PostProcessVolume failed to be converted because the original asset reference was lost during conversion.");
  235. return;
  236. }
  237. if (PrefabUtility.IsPartOfPrefabInstance(oldVolume) &&
  238. !PrefabUtility.IsAddedComponentOverride(oldVolume))
  239. {
  240. // This is a property override on an instance of the component,
  241. // so override the component instance with the modifications.
  242. succeeded = ConvertVolumeInstance(oldVolume, errorString);
  243. }
  244. else
  245. {
  246. // The entire component is unique, so just convert it
  247. succeeded = ConvertVolumeComponent(oldVolume, errorString);
  248. }
  249. }
  250. private void ConvertLayer(BIRPRendering.PostProcessLayer oldLayer, ref bool succeeded,
  251. StringBuilder errorString)
  252. {
  253. if (!succeeded)
  254. {
  255. return;
  256. }
  257. if (!oldLayer)
  258. {
  259. // TODO: unless there's good way to tell the if the object is just missing because it was already
  260. // converted as part of an earlier conversion object, then these two lines should be commented
  261. // out or removed. It should still return though.
  262. // succeeded = false;
  263. // errorString.AppendLine("PPv2 PostProcessLayer failed to be converted because the original asset reference was lost during conversion.");
  264. return;
  265. }
  266. if (PrefabUtility.IsPartOfPrefabInstance(oldLayer) &&
  267. !PrefabUtility.IsAddedComponentOverride(oldLayer))
  268. {
  269. // This is a property override on an instance of the component,
  270. // so override the component instance with the modifications.
  271. succeeded = ConvertLayerInstance(oldLayer, errorString);
  272. }
  273. else
  274. {
  275. // The entire component is unique, so just convert it
  276. succeeded = ConvertLayerComponent(oldLayer, errorString);
  277. }
  278. }
  279. private void ConvertProfile(BIRPRendering.PostProcessProfile oldProfile, ref bool succeeded,
  280. StringBuilder errorString)
  281. {
  282. if (!succeeded)
  283. {
  284. return;
  285. }
  286. if (!oldProfile)
  287. {
  288. errorString.AppendLine(
  289. "PPv2 PostProcessProfile failed to be converted because the original asset reference was lost during conversion.");
  290. return;
  291. }
  292. ConvertVolumeProfileAsset(oldProfile, errorString, ref succeeded);
  293. // TODO:
  294. // - Perhaps old Profiles should only be deleted if they actually no longer have references,
  295. // just in case some some Volume conversions are skipped and still need references for future conversion.
  296. // - Alternatively, leave deletion of Profiles entirely to the user. (I think this is preferred)
  297. }
  298. #endregion Conversion_Entry_Points
  299. private bool ConvertVolumeComponent(BIRPRendering.PostProcessVolume oldVolume, StringBuilder errorString)
  300. {
  301. // Don't convert if it appears to already have been converted.
  302. if (oldVolume.GetComponent<Volume>()) return true;
  303. var gameObject = oldVolume.gameObject;
  304. var newVolume = gameObject.AddComponent<Volume>();
  305. newVolume.priority = oldVolume.priority;
  306. newVolume.weight = oldVolume.weight;
  307. newVolume.blendDistance = oldVolume.blendDistance;
  308. newVolume.isGlobal = oldVolume.isGlobal;
  309. newVolume.enabled = oldVolume.enabled;
  310. var success = true;
  311. newVolume.sharedProfile = ConvertVolumeProfileAsset(oldVolume.sharedProfile, errorString, ref success);
  312. if (PrefabUtility.IsPartOfPrefabAsset(oldVolume))
  313. {
  314. postConversionDestroyables.Add(oldVolume);
  315. }
  316. else
  317. {
  318. Object.DestroyImmediate(oldVolume, allowDestroyingAssets: true);
  319. }
  320. EditorUtility.SetDirty(gameObject);
  321. return success;
  322. }
  323. private bool ConvertVolumeInstance(BIRPRendering.PostProcessVolume oldVolume, StringBuilder errorString)
  324. {
  325. // First get a reference to the local instance of the converted component
  326. // which may require immediately converting it at its origin location first.
  327. var newVolumeInstance = oldVolume.GetComponent<Volume>();
  328. if (!newVolumeInstance)
  329. {
  330. var oldVolumeOrigin = PrefabUtility.GetCorrespondingObjectFromSource(oldVolume);
  331. if (!ConvertVolumeComponent(oldVolumeOrigin, errorString))
  332. {
  333. return false;
  334. }
  335. PrefabUtility.SavePrefabAsset(oldVolumeOrigin.gameObject);
  336. newVolumeInstance = oldVolume.GetComponent<Volume>();
  337. if (!newVolumeInstance)
  338. {
  339. errorString.AppendLine(
  340. "PPv2 PostProcessVolume failed to be converted because the instance object did not inherit the converted Prefab source.");
  341. return false;
  342. }
  343. }
  344. bool success = true;
  345. var oldModifications = PrefabUtility.GetPropertyModifications(oldVolume);
  346. foreach (var oldModification in oldModifications)
  347. {
  348. if (oldModification.target is BIRPRendering.PostProcessVolume)
  349. {
  350. if (oldModification.propertyPath.EndsWith("priority", StringComparison.InvariantCultureIgnoreCase))
  351. newVolumeInstance.priority = oldVolume.priority;
  352. else if (oldModification.propertyPath.EndsWith("weight",
  353. StringComparison.InvariantCultureIgnoreCase))
  354. newVolumeInstance.weight = oldVolume.weight;
  355. else if (oldModification.propertyPath.EndsWith("blendDistance",
  356. StringComparison.InvariantCultureIgnoreCase))
  357. newVolumeInstance.blendDistance = oldVolume.blendDistance;
  358. else if (oldModification.propertyPath.EndsWith("isGlobal",
  359. StringComparison.InvariantCultureIgnoreCase))
  360. newVolumeInstance.isGlobal = oldVolume.isGlobal;
  361. else if (oldModification.propertyPath.EndsWith("enabled",
  362. StringComparison.InvariantCultureIgnoreCase))
  363. newVolumeInstance.enabled = oldVolume.enabled;
  364. else if (oldModification.propertyPath.EndsWith("sharedProfile",
  365. StringComparison.InvariantCultureIgnoreCase))
  366. newVolumeInstance.sharedProfile =
  367. ConvertVolumeProfileAsset(oldVolume.sharedProfile, errorString, ref success);
  368. EditorUtility.SetDirty(newVolumeInstance);
  369. }
  370. }
  371. return success;
  372. }
  373. private bool ConvertLayerComponent(BIRPRendering.PostProcessLayer oldLayer, StringBuilder errorString)
  374. {
  375. var siblingCamera = oldLayer.GetComponent<Camera>().GetUniversalAdditionalCameraData();
  376. // PostProcessLayer requires a sibling Camera component, but
  377. // we check it here just in case something weird went happened.
  378. if (!siblingCamera)
  379. {
  380. errorString.AppendLine(
  381. "PPv2 PostProcessLayer failed to be converted because the instance object was missing a required sibling Camera component.");
  382. return false;
  383. }
  384. // The presence of a PostProcessLayer implies the Camera should render post-processes
  385. siblingCamera.renderPostProcessing = true;
  386. siblingCamera.volumeLayerMask = oldLayer.volumeLayer;
  387. siblingCamera.volumeTrigger = oldLayer.volumeTrigger;
  388. siblingCamera.stopNaN = oldLayer.stopNaNPropagation;
  389. siblingCamera.antialiasingQuality =
  390. (URPRendering.AntialiasingQuality)oldLayer.subpixelMorphologicalAntialiasing.quality;
  391. switch (oldLayer.antialiasingMode)
  392. {
  393. case BIRPRendering.PostProcessLayer.Antialiasing.None:
  394. siblingCamera.antialiasing = URPRendering.AntialiasingMode.None;
  395. break;
  396. case BIRPRendering.PostProcessLayer.Antialiasing.FastApproximateAntialiasing:
  397. siblingCamera.antialiasing = URPRendering.AntialiasingMode.FastApproximateAntialiasing;
  398. break;
  399. case BIRPRendering.PostProcessLayer.Antialiasing.SubpixelMorphologicalAntialiasing:
  400. siblingCamera.antialiasing = URPRendering.AntialiasingMode.SubpixelMorphologicalAntiAliasing;
  401. break;
  402. default:
  403. // Default to the the most performant mode, since "None" is an explicit option.
  404. siblingCamera.antialiasing = URPRendering.AntialiasingMode.FastApproximateAntialiasing;
  405. break;
  406. }
  407. if (PrefabUtility.IsPartOfPrefabAsset(oldLayer))
  408. {
  409. postConversionDestroyables.Add(oldLayer);
  410. }
  411. else
  412. {
  413. Object.DestroyImmediate(oldLayer, allowDestroyingAssets: true);
  414. }
  415. EditorUtility.SetDirty(siblingCamera.gameObject);
  416. return true;
  417. }
  418. private bool ConvertLayerInstance(BIRPRendering.PostProcessLayer oldLayer, StringBuilder errorString)
  419. {
  420. // First get a reference to the local instance of the camera (which is required by PostProcessingLayer)
  421. var siblingCamera = oldLayer.GetComponent<Camera>().GetUniversalAdditionalCameraData();
  422. if (!siblingCamera)
  423. {
  424. errorString.AppendLine(
  425. "PPv2 PostProcessLayer failed to be converted because the instance object was missing a required sibling Camera component.");
  426. return false;
  427. }
  428. var oldModifications = PrefabUtility.GetPropertyModifications(oldLayer);
  429. foreach (var oldModification in oldModifications)
  430. {
  431. if (oldModification.target is BIRPRendering.PostProcessLayer)
  432. {
  433. if (oldModification.propertyPath.EndsWith("volumeLayer",
  434. StringComparison.InvariantCultureIgnoreCase))
  435. siblingCamera.volumeLayerMask = oldLayer.volumeLayer;
  436. else if (oldModification.propertyPath.EndsWith("volumeTrigger",
  437. StringComparison.InvariantCultureIgnoreCase))
  438. siblingCamera.volumeTrigger = oldLayer.volumeTrigger;
  439. else if (oldModification.propertyPath.EndsWith("stopNaNPropagation",
  440. StringComparison.InvariantCultureIgnoreCase))
  441. siblingCamera.stopNaN = oldLayer.stopNaNPropagation;
  442. else if (oldModification.propertyPath.EndsWith("quality",
  443. StringComparison.InvariantCultureIgnoreCase))
  444. siblingCamera.antialiasingQuality =
  445. (URPRendering.AntialiasingQuality)oldLayer.subpixelMorphologicalAntialiasing.quality;
  446. else if (oldModification.propertyPath.EndsWith("antialiasingMode",
  447. StringComparison.InvariantCultureIgnoreCase))
  448. {
  449. switch (oldLayer.antialiasingMode)
  450. {
  451. case BIRPRendering.PostProcessLayer.Antialiasing.None:
  452. siblingCamera.antialiasing = URPRendering.AntialiasingMode.None;
  453. break;
  454. case BIRPRendering.PostProcessLayer.Antialiasing.FastApproximateAntialiasing:
  455. siblingCamera.antialiasing = URPRendering.AntialiasingMode.FastApproximateAntialiasing;
  456. break;
  457. case BIRPRendering.PostProcessLayer.Antialiasing.SubpixelMorphologicalAntialiasing:
  458. siblingCamera.antialiasing =
  459. URPRendering.AntialiasingMode.SubpixelMorphologicalAntiAliasing;
  460. break;
  461. default:
  462. // Default to the the most performant mode, since "None" is an explicit option.
  463. siblingCamera.antialiasing = URPRendering.AntialiasingMode.FastApproximateAntialiasing;
  464. break;
  465. }
  466. }
  467. EditorUtility.SetDirty(siblingCamera);
  468. }
  469. }
  470. return true;
  471. }
  472. private VolumeProfile ConvertVolumeProfileAsset(BIRPRendering.PostProcessProfile oldProfile,
  473. StringBuilder errorString, ref bool success)
  474. {
  475. // Don't convert if it appears to already have been converted.
  476. if (!oldProfile) return null;
  477. var oldPath = AssetDatabase.GetAssetPath(oldProfile);
  478. var oldDirectory = Path.GetDirectoryName(oldPath);
  479. var oldName = Path.GetFileNameWithoutExtension(oldPath);
  480. var newPath = Path.Combine(oldDirectory, $"{oldName}(URP).asset");
  481. if (File.Exists(newPath))
  482. {
  483. return AssetDatabase.LoadAssetAtPath<VolumeProfile>(newPath);
  484. }
  485. var newProfile = ScriptableObject.CreateInstance<VolumeProfile>();
  486. try
  487. {
  488. AssetDatabase.CreateAsset(newProfile, newPath);
  489. }
  490. catch (Exception e)
  491. {
  492. errorString.AppendLine($"PPv2 PostProcessLayer failed to be converted with exception:\n{e}");
  493. success = false;
  494. if (!newProfile) return null;
  495. }
  496. foreach (var oldSettings in oldProfile.settings)
  497. {
  498. foreach (var effectConverter in effectConverters)
  499. {
  500. effectConverter.AddConvertedProfileSettingsToProfile(oldSettings, newProfile);
  501. }
  502. }
  503. EditorUtility.SetDirty(newProfile);
  504. return newProfile;
  505. }
  506. public IEnumerable<PostProcessEffectSettingsConverter> GetAllBIRPConverters()
  507. {
  508. var baseType = typeof(PostProcessEffectSettingsConverter);
  509. var assembly = Assembly.GetAssembly(baseType);
  510. var derivedTypes = assembly
  511. .GetTypes()
  512. .Where(t =>
  513. t.BaseType != null &&
  514. t.BaseType == baseType)
  515. .Select(t => ScriptableObject.CreateInstance(t) as PostProcessEffectSettingsConverter);
  516. return derivedTypes;
  517. }
  518. }
  519. }
  520. #endif