Rasagar/Library/PackageCache/com.unity.visualeffectgraph/Editor/Models/VFXSlotContainerModel.cs
2024-08-26 23:07:20 +03:00

493 lines
18 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.VFX;
using Object = UnityEngine.Object;
namespace UnityEditor.VFX
{
interface IVFXSlotContainer
{
VFXSlot activationSlot { get; } // Return the activation slot if any, null otherwise
ReadOnlyCollection<VFXSlot> inputSlots { get; }
ReadOnlyCollection<VFXSlot> outputSlots { get; }
int GetNbInputSlots();
int GetNbOutputSlots();
VFXSlot GetInputSlot(int index);
VFXSlot GetOutputSlot(int index);
void AddSlot(VFXSlot slot, int index = -1);
void RemoveSlot(VFXSlot slot);
void ReplaceSlot(VFXSlot prevSlot, VFXSlot newSlot);
int GetSlotIndex(VFXSlot slot);
void UpdateOutputExpressionsIfNeeded();
void ClearSlots();
bool ResyncSlots(bool notify);
void Invalidate(VFXModel.InvalidationCause cause);
void Invalidate(VFXModel model, VFXModel.InvalidationCause cause);
void SetSettingValue(string name, object value);
object GetSettingValue(string name);
VFXSetting GetSetting(string name);
void OnCopyLinksOtherSlot(VFXSlot mySlot, VFXSlot prevOtherSlot, VFXSlot newOtherSlot);
void OnCopyLinksMySlot(VFXSlot myPrevSlot, VFXSlot myNewSlot, VFXSlot otherSlot);
bool collapsed { get; set; }
VFXSpace GetOutputSpaceFromSlot(VFXSlot slot);
}
abstract class VFXSlotContainerModel<ParentType, ChildrenType> : VFXModel<ParentType, ChildrenType>, IVFXSlotContainer
where ParentType : VFXModel
where ChildrenType : VFXModel
{
public virtual VFXSlot activationSlot => null;
public virtual ReadOnlyCollection<VFXSlot> inputSlots { get { return m_InputSlots.AsReadOnly(); } }
public virtual ReadOnlyCollection<VFXSlot> outputSlots { get { return m_OutputSlots.AsReadOnly(); } }
public virtual int GetNbInputSlots() { return m_InputSlots.Count; }
public virtual int GetNbOutputSlots() { return m_OutputSlots.Count; }
public virtual VFXSlot GetInputSlot(int index) { return m_InputSlots[index]; }
public virtual VFXSlot GetOutputSlot(int index) { return m_OutputSlots[index]; }
protected virtual IEnumerable<VFXPropertyWithValue> inputProperties { get { return PropertiesFromType(GetInputPropertiesTypeName()); } }
protected virtual IEnumerable<VFXPropertyWithValue> outputProperties { get { return PropertiesFromType(GetOutputPropertiesTypeName()); } }
// Get properties with value from nested class fields
protected IEnumerable<VFXPropertyWithValue> PropertiesFromType(string typeName)
{
//using are own GetRecursiveNestedType is needed for .net 4.0 compability
return PropertiesFromType(GetType().GetRecursiveNestedType(typeName));
}
// Get properties with value from type fields
public static IEnumerable<VFXPropertyWithValue> PropertiesFromType(Type type)
{
if (type == null)
return Enumerable.Empty<VFXPropertyWithValue>();
var instance = System.Activator.CreateInstance(type);
return type.GetFields()
.Where(f => !f.IsStatic)
.Select(f =>
{
var p = new VFXPropertyWithValue();
p.property = new VFXProperty(f);
p.value = f.GetValue(instance);
return p;
});
}
// Get properties with values from slots
protected static IEnumerable<VFXPropertyWithValue> PropertiesFromSlots(IEnumerable<VFXSlot> slots)
{
return slots.Select(s =>
{
var p = new VFXPropertyWithValue();
p.property = s.property;
p.value = s.value;
return p;
});
}
// Get properties with values from slots if any or initialize from default inner class name
protected IEnumerable<VFXPropertyWithValue> PropertiesFromSlotsOrDefaultFromClass(VFXSlot.Direction direction)
{
bool isInput = direction == VFXSlot.Direction.kInput;
var slots = isInput ? inputSlots : outputSlots;
if (slots.Count() == 0)
return PropertiesFromType(isInput ? GetInputPropertiesTypeName() : GetOutputPropertiesTypeName());
else
return PropertiesFromSlots(slots);
}
protected static string GetInputPropertiesTypeName()
{
return "InputProperties";
}
protected static string GetOutputPropertiesTypeName()
{
return "OutputProperties";
}
public virtual void AddSlot(VFXSlot slot, int index = -1) { InnerAddSlot(slot, index, true); }
private void InnerAddSlot(VFXSlot slot, int index, bool notify)
{
var slotList = slot.direction == VFXSlot.Direction.kInput ? m_InputSlots : m_OutputSlots;
if (!slot.IsMasterSlot())
throw new ArgumentException("InnerAddSlot expect only a masterSlot");
if (slot.owner != this as IVFXSlotContainer)
{
if (slot.owner != null)
slot.owner.RemoveSlot(slot);
int realIndex = index == -1 ? slotList.Count : index;
slotList.Insert(realIndex, slot);
slot.SetOwner(this);
if (notify)
Invalidate(InvalidationCause.kStructureChanged);
}
}
void IVFXSlotContainer.Invalidate(VFXModel model, InvalidationCause cause)
{
Invalidate(model, cause);
}
public virtual void OnCopyLinksOtherSlot(VFXSlot mySlot, VFXSlot prevOtherSlot, VFXSlot newOtherSlot)
{
}
public virtual void OnCopyLinksMySlot(VFXSlot myPrevSlot, VFXSlot myNewSlot, VFXSlot otherSlot)
{
}
public virtual void RemoveSlot(VFXSlot slot) { InnerRemoveSlot(slot, true); }
private void InnerRemoveSlot(VFXSlot slot, bool notify)
{
var slotList = slot.direction == VFXSlot.Direction.kInput ? m_InputSlots : m_OutputSlots;
if (!slot.IsMasterSlot())
throw new ArgumentException();
if (slot.owner == this as IVFXSlotContainer)
{
slotList.Remove(slot);
slot.SetOwner(null);
if (notify)
Invalidate(InvalidationCause.kStructureChanged);
}
}
public virtual void ReplaceSlot(VFXSlot prevSlot, VFXSlot newSlot) { InnerReplaceSlot(prevSlot, newSlot, true); }
private void InnerReplaceSlot(VFXSlot prevSlot, VFXSlot newSlot, bool notify)
{
int index = GetSlotIndex(prevSlot);
InnerRemoveSlot(prevSlot, false);
InnerAddSlot(newSlot, index, false);
if (notify)
Invalidate(InvalidationCause.kStructureChanged);
}
public int GetSlotIndex(VFXSlot slot)
{
var slotList = slot.direction == VFXSlot.Direction.kInput ? m_InputSlots : m_OutputSlots;
return slotList.IndexOf(slot);
}
protected VFXSlotContainerModel()
{ }
public override void OnEnable()
{
base.OnEnable();
if (m_InputSlots == null)
{
m_InputSlots = new List<VFXSlot>();
SyncSlots(VFXSlot.Direction.kInput, false); // Initial slot creation
}
else
{
int nbRemoved = m_InputSlots.RemoveAll(c => c == null);// Remove bad references if any
if (nbRemoved > 0)
Debug.LogWarning(String.Format("Remove {0} input slot(s) that couldnt be deserialized from {1} of type {2}", nbRemoved, name, GetType()));
}
if (m_OutputSlots == null)
{
m_OutputSlots = new List<VFXSlot>();
SyncSlots(VFXSlot.Direction.kOutput, false); // Initial slot creation
}
else
{
int nbRemoved = m_OutputSlots.RemoveAll(c => c == null);// Remove bad references if any
if (nbRemoved > 0)
Debug.LogWarning(String.Format("Remove {0} output slot(s) that couldnt be deserialized from {1} of type {2}", nbRemoved, name, GetType()));
}
}
public override void Sanitize(int version)
{
base.Sanitize(version);
if (ResyncSlots(true))
Debug.Log(string.Format("Slots have been resynced in {0} of type {1}", name, GetType()));
}
public override void OnUnknownChange()
{
base.OnUnknownChange();
m_OutputExpressionsUpToDate = false;
ResyncSlots(false);
}
public override void CollectDependencies(HashSet<ScriptableObject> objs, bool ownedOnly = true)
{
base.CollectDependencies(objs, ownedOnly);
foreach (var slot in m_InputSlots.Concat(m_OutputSlots))
{
objs.Add(slot);
slot.CollectDependencies(objs, ownedOnly);
}
}
public virtual bool ResyncSlots(bool notify)
{
bool changed = false;
changed |= SyncSlots(VFXSlot.Direction.kInput, notify);
changed |= SyncSlots(VFXSlot.Direction.kOutput, notify);
return changed;
}
//Specific helper for VFXLibrary, doesn't notify, used before applying variants.
public void ClearSlots()
{
while (m_InputSlots.Count > 0)
InnerRemoveSlot(m_InputSlots.First(), false);
while (m_OutputSlots.Count > 0)
InnerRemoveSlot(m_OutputSlots.First(), false);
}
public void MoveSlots(VFXSlot.Direction direction, int movedIndex, int targetIndex)
{
VFXSlot movedSlot = m_InputSlots[movedIndex];
if (movedIndex < targetIndex)
{
m_InputSlots.Insert(targetIndex, movedSlot);
m_InputSlots.RemoveAt(movedIndex);
}
else
{
m_InputSlots.RemoveAt(movedIndex);
m_InputSlots.Insert(targetIndex, movedSlot);
}
}
protected override void OnInvalidate(VFXModel model, InvalidationCause cause)
{
if (model == this && cause == InvalidationCause.kSettingChanged)
ResyncSlots(true);
base.OnInvalidate(model, cause);
}
static public IEnumerable<VFXNamedExpression> GetExpressionsFromSlots(IVFXSlotContainer slotContainer)
{
foreach (var master in slotContainer.inputSlots)
{
foreach (var slot in master.GetExpressionSlots())
{
var expression = slot.GetExpression();
yield return new VFXNamedExpression(expression, slot.fullName);
}
}
}
protected void InitSlotsFromProperties(IEnumerable<VFXPropertyWithValue> properties, VFXSlot.Direction direction)
{
foreach (var p in properties)
{
var slot = VFXSlot.Create(p, direction);
InnerAddSlot(slot, -1, false);
}
}
public VFXSlot GetSlotByPath(bool input, string path)
{
string[] elements = path.Split('_');
IEnumerable<VFXSlot> slots = input ? m_InputSlots : m_OutputSlots;
VFXSlot slot = null;
for (int i = 0; i < elements.Length; ++i)
{
slot = slots.FirstOrDefault(t => t.name == elements[i]);
if (slot == null) break;
slots = slot.children;
}
return slot;
}
protected bool SyncSlots(VFXSlot.Direction direction, bool notify)
{
bool isInput = direction == VFXSlot.Direction.kInput;
var expectedProperties = (isInput ? inputProperties : outputProperties).ToArray();
int nbSlots = isInput ? GetNbInputSlots() : GetNbOutputSlots();
var currentSlots = isInput ? inputSlots : outputSlots;
// check all slots owner (TODO Still useful?)
for (int i = 0; i < nbSlots; ++i)
{
VFXSlot slot = currentSlots[i];
var slotOwner = slot.owner as VFXSlotContainerModel<ParentType, ChildrenType>;
if (slotOwner != this)
{
Debug.LogError("Slot :" + slot.name + " of Container" + name + "Has a wrong owner.");
slot.SetOwner(this); // make sure everything works even if the owner was lost for some reason.
}
}
bool recreate = false;
if (nbSlots != expectedProperties.Length)
recreate = true;
else
{
for (int i = 0; i < nbSlots; ++i)
if (!currentSlots[i].property.Equals(expectedProperties[i].property))
{
recreate = true;
break;
}
}
if (recreate)
{
var existingSlots = new List<VFXSlot>(currentSlots);
// Remove all slots
for (int i = nbSlots - 1; i >= 0; --i)
InnerRemoveSlot(currentSlots[i], false);
var newSlotCount = expectedProperties.Length;
var newSlots = new VFXSlot[newSlotCount];
var createdSlots = new List<VFXSlot>(newSlotCount);
// Reuse slots that already exists or create a new one if not
for (int i = 0; i < newSlotCount; ++i)
{
var p = expectedProperties[i];
var slot = existingSlots.Find(s => p.property.Equals(s.property));
if (slot != null)
{
slot.UpdateAttributes(p.property.attributes, notify);
existingSlots.Remove(slot);
}
else
{
slot = VFXSlot.Create(p, direction);
createdSlots.Add(slot);
}
newSlots[i] = slot;
}
for (int i = 0; i < createdSlots.Count; ++i)
{
var dstSlot = createdSlots[i];
// Try to keep links and value for slots of same name and compatible types
var srcSlot = existingSlots.FirstOrDefault(s => s.property.name == dstSlot.property.name);
// Find the first slot with same type (should perform a more clever selection based on name distance)
if (srcSlot == null)
srcSlot = existingSlots.FirstOrDefault(s => s.property.type == dstSlot.property.type);
// Try to find a slot that can be implicitely converted
if (srcSlot == null)
srcSlot = existingSlots.FirstOrDefault(s => VFXConverter.CanConvertTo(s.property.type, dstSlot.property.type));
if (srcSlot != null)
{
VFXSlot.CopyLinksAndValue(dstSlot, srcSlot, notify);
srcSlot.UnlinkAll(true, notify);
existingSlots.Remove(srcSlot);
}
}
// Remove all remaining links
foreach (var slot in existingSlots)
slot.UnlinkAll(true, notify);
// Add all slots
foreach (var s in newSlots)
InnerAddSlot(s, -1, false);
if (notify)
Invalidate(InvalidationCause.kStructureChanged);
}
else
{
// Update properties
for (int i = 0; i < nbSlots; ++i)
currentSlots[i].UpdateAttributes(expectedProperties[i].property.attributes, notify);
}
return recreate;
}
public void ExpandPath(string fieldPath)
{
m_expandedPaths.Add(fieldPath);
Invalidate(InvalidationCause.kParamChanged);
}
public void RetractPath(string fieldPath)
{
m_expandedPaths.Remove(fieldPath);
Invalidate(InvalidationCause.kParamChanged);
}
public bool IsPathExpanded(string fieldPath)
{
return m_expandedPaths.Contains(fieldPath);
}
protected virtual void UpdateOutputExpressions() { }
[NonSerialized]
private bool m_OutputExpressionsUpToDate = false;
public void UpdateOutputExpressionsIfNeeded()
{
if (!m_OutputExpressionsUpToDate)
{
UpdateOutputExpressions();
m_OutputExpressionsUpToDate = true;
}
}
public void MarkOutputExpressionsAsOutOfDate()
{
m_OutputExpressionsUpToDate = false;
}
public virtual VFXSpace GetOutputSpaceFromSlot(VFXSlot slot)
{
return VFXSpace.None;
}
public override void CheckGraphBeforeImport()
{
// The cache is here to avoid multiple output recomputation when updating the expression graph
// So It's marked as out of date before compilation just to minimize risk of cache missed invalidation between different compilation
MarkOutputExpressionsAsOutOfDate();
base.CheckGraphBeforeImport();
}
//[SerializeField]
HashSet<string> m_expandedPaths = new HashSet<string>();
[SerializeField]
List<VFXSlot> m_InputSlots;
[SerializeField]
List<VFXSlot> m_OutputSlots;
}
}