ExposedParameterFieldFactory.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. using UnityEngine;
  2. using UnityEditor;
  3. using UnityEditor.UIElements;
  4. using UnityEngine.UIElements;
  5. using System;
  6. using System.Collections.Generic;
  7. namespace GraphProcessor
  8. {
  9. // So, this is a workaround class to add a wrapper around PropertyFields applied on [SerializeReference].
  10. // Because Property Fields binding being extremely slow (https://forum.unity.com/threads/propertyfield-extremely-slow.966191/)
  11. // and AppliedModifiedProperties() re-creating the ScriptableObject when called (which in NGP causes the graph to be re-built)
  12. // we can't use PropertyFields directly. This class provides a set of function to create PropertyFields for Exposed Parameters
  13. // but without being attached to the graph, so when we call AppliedModifiedProperties, the graph is not re-built.
  14. // The drawback is that we have to check ourselves for value changes and then apply them on the graph parameters,
  15. // but it's far better than having to re-create the graph every time a parameter or a setting is changed.
  16. public class ExposedParameterFieldFactory : IDisposable
  17. {
  18. BaseGraph graph;
  19. [SerializeField]
  20. ExposedParameterWorkaround exposedParameterObject;
  21. SerializedObject serializedObject;
  22. SerializedProperty serializedParameters;
  23. Dictionary<ExposedParameter, object> oldParameterValues = new Dictionary<ExposedParameter, object>();
  24. Dictionary<ExposedParameter, ExposedParameter.Settings> oldParameterSettings = new Dictionary<ExposedParameter, ExposedParameter.Settings>();
  25. public ExposedParameterFieldFactory(BaseGraph graph, List<ExposedParameter> customParameters = null)
  26. {
  27. this.graph = graph;
  28. exposedParameterObject = ScriptableObject.CreateInstance<ExposedParameterWorkaround>();
  29. exposedParameterObject.graph = graph;
  30. exposedParameterObject.hideFlags = HideFlags.HideAndDontSave ^ HideFlags.NotEditable;
  31. serializedObject = new SerializedObject(exposedParameterObject);
  32. UpdateSerializedProperties(customParameters);
  33. }
  34. public void UpdateSerializedProperties(List<ExposedParameter> parameters = null)
  35. {
  36. if (parameters != null)
  37. exposedParameterObject.parameters = parameters;
  38. else
  39. exposedParameterObject.parameters = graph.exposedParameters;
  40. serializedObject.Update();
  41. serializedParameters = serializedObject.FindProperty(nameof(ExposedParameterWorkaround.parameters));
  42. }
  43. public VisualElement GetParameterValueField(ExposedParameter parameter, Action<object> valueChangedCallback)
  44. {
  45. serializedObject.Update();
  46. int propIndex = FindPropertyIndex(parameter);
  47. var field = new PropertyField(serializedParameters.GetArrayElementAtIndex(propIndex));
  48. field.Bind(serializedObject);
  49. VisualElement view = new VisualElement();
  50. view.Add(field);
  51. oldParameterValues[parameter] = parameter.value;
  52. view.Add(new IMGUIContainer(() =>
  53. {
  54. if (oldParameterValues.TryGetValue(parameter, out var value))
  55. {
  56. if (parameter.value != null && !parameter.value.Equals(value))
  57. valueChangedCallback(parameter.value);
  58. }
  59. oldParameterValues[parameter] = parameter.value;
  60. }));
  61. // Disallow picking scene objects when the graph is not linked to a scene
  62. if (!this.graph.IsLinkedToScene())
  63. {
  64. var objectField = view.Q<ObjectField>();
  65. if (objectField != null)
  66. objectField.allowSceneObjects = false;
  67. }
  68. return view;
  69. }
  70. public VisualElement GetParameterSettingsField(ExposedParameter parameter, Action<object> valueChangedCallback)
  71. {
  72. serializedObject.Update();
  73. int propIndex = FindPropertyIndex(parameter);
  74. var serializedParameter = serializedParameters.GetArrayElementAtIndex(propIndex);
  75. serializedParameter.managedReferenceValue = exposedParameterObject.parameters[propIndex];
  76. var serializedSettings = serializedParameter.FindPropertyRelative(nameof(ExposedParameter.settings));
  77. serializedSettings.managedReferenceValue = exposedParameterObject.parameters[propIndex].settings;
  78. var settingsField = new PropertyField(serializedSettings);
  79. settingsField.Bind(serializedObject);
  80. VisualElement view = new VisualElement();
  81. view.Add(settingsField);
  82. // TODO: see if we can replace this with an event
  83. oldParameterSettings[parameter] = parameter.settings;
  84. view.Add(new IMGUIContainer(() =>
  85. {
  86. if (oldParameterSettings.TryGetValue(parameter, out var settings))
  87. {
  88. if (!settings.Equals(parameter.settings))
  89. valueChangedCallback(parameter.settings);
  90. }
  91. oldParameterSettings[parameter] = parameter.settings;
  92. }));
  93. return view;
  94. }
  95. public void ResetOldParameter(ExposedParameter parameter)
  96. {
  97. oldParameterValues.Remove(parameter);
  98. oldParameterSettings.Remove(parameter);
  99. }
  100. int FindPropertyIndex(ExposedParameter param) => exposedParameterObject.parameters.FindIndex(p => p == param);
  101. public void Dispose()
  102. {
  103. GameObject.DestroyImmediate(exposedParameterObject);
  104. }
  105. }
  106. }