You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
338 lines
12 KiB
338 lines
12 KiB
using System;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using UnityEditor.VFX.Block;
|
|
using UnityEngine;
|
|
|
|
namespace UnityEditor.VFX
|
|
{
|
|
static class VFXSubgraphUtility
|
|
{
|
|
public static bool IsSubgraphModel(VFXModel model) => model is VFXSubgraphBlock or VFXSubgraphContext or VFXSubgraphOperator;
|
|
|
|
public static int TransferExpressionToParameters(IList<VFXExpression> inputExpression, IEnumerable<VFXParameter> parameters, List<VFXExpression> backedUpExpressions = null)
|
|
{
|
|
int cptSlot = 0;
|
|
foreach (var param in parameters)
|
|
{
|
|
VFXSlot outputSlot = param.outputSlots[0];
|
|
|
|
param.subgraphMode = true;
|
|
if (inputExpression.Count <= cptSlot)
|
|
continue;
|
|
|
|
foreach (var slot in outputSlot.GetExpressionSlots())
|
|
{
|
|
if (backedUpExpressions != null)
|
|
backedUpExpressions.Add(slot.GetExpression());
|
|
slot.SetExpression(inputExpression[cptSlot]);
|
|
cptSlot += 1;
|
|
}
|
|
}
|
|
|
|
return cptSlot;
|
|
}
|
|
|
|
public static VFXPropertyWithValue GetPropertyFromInputParameter(VFXParameter param)
|
|
{
|
|
List<object> attributes = new List<object>();
|
|
if (!string.IsNullOrEmpty(param.tooltip))
|
|
attributes.Add(new TooltipAttribute(param.tooltip));
|
|
|
|
if (param.valueFilter == VFXValueFilter.Range)
|
|
attributes.Add(new RangeAttribute((float)VFXConverter.ConvertTo(param.min, typeof(float)), (float)VFXConverter.ConvertTo(param.max, typeof(float))));
|
|
else if (param.valueFilter == VFXValueFilter.Enum)
|
|
attributes.Add(new EnumAttribute(param.enumValues.ToArray()));
|
|
|
|
return new VFXPropertyWithValue(new VFXProperty(param.type, param.exposedName, attributes.ToArray()), param.value);
|
|
}
|
|
|
|
public static bool InputPredicate(VFXParameter param)
|
|
{
|
|
return param.exposed && !param.isOutput;
|
|
}
|
|
|
|
public static bool OutputPredicate(VFXParameter param)
|
|
{
|
|
return param.isOutput;
|
|
}
|
|
|
|
public static IEnumerable<VFXParameter> GetParameters(IEnumerable<VFXModel> models, Func<VFXParameter, bool> predicate)
|
|
{
|
|
return models.OfType<VFXParameter>().Where(predicate).OrderBy(t => t.order);
|
|
}
|
|
}
|
|
|
|
[VFXHelpURL("Subgraph")]
|
|
[VFXInfo(name = "Empty Subgraph Operator")]
|
|
class VFXSubgraphOperator : VFXOperator, IVFXAttributeUsage
|
|
{
|
|
bool m_IsMissing;
|
|
|
|
[VFXSetting(VFXSettingAttribute.VisibleFlags.InInspector), SerializeField]
|
|
protected VisualEffectSubgraphOperator m_Subgraph;
|
|
|
|
public VFXSubgraphOperator()
|
|
{
|
|
// This allow to detect when a resource is deleted or restored
|
|
EditorApplication.projectChanged += OnProjectOrHierarchyChanged;
|
|
}
|
|
|
|
public void OnDestroy()
|
|
{
|
|
EditorApplication.projectChanged -= OnProjectOrHierarchyChanged;
|
|
}
|
|
|
|
public VisualEffectSubgraphOperator subgraph
|
|
{
|
|
get
|
|
{
|
|
if (m_Subgraph == null && !object.ReferenceEquals(m_Subgraph, null))
|
|
{
|
|
string assetPath = AssetDatabase.GetAssetPath(m_Subgraph.GetInstanceID());
|
|
|
|
var newSubgraph = AssetDatabase.LoadAssetAtPath<VisualEffectSubgraphOperator>(assetPath);
|
|
if (newSubgraph != null)
|
|
{
|
|
m_Subgraph = newSubgraph;
|
|
}
|
|
|
|
m_IsMissing = m_Subgraph == null;
|
|
}
|
|
return m_Subgraph;
|
|
}
|
|
}
|
|
|
|
[NonSerialized]
|
|
VFXModel[] m_SubChildren;
|
|
|
|
public void RecreateCopy()
|
|
{
|
|
ClearCopy();
|
|
|
|
if (subgraph == null)
|
|
{
|
|
m_SubChildren = null;
|
|
return;
|
|
}
|
|
|
|
var graph = m_Subgraph.GetResource().GetOrCreateGraph();
|
|
HashSet<ScriptableObject> dependencies = new HashSet<ScriptableObject>();
|
|
|
|
foreach (var child in graph.children.Where(t => t is VFXOperator || t is VFXParameter))
|
|
{
|
|
dependencies.Add(child);
|
|
child.CollectDependencies(dependencies);
|
|
}
|
|
|
|
var copy = VFXMemorySerializer.DuplicateObjects(dependencies.ToArray());
|
|
m_SubChildren = copy.OfType<VFXModel>().Where(t => t is VFXOperator || t is VFXParameter).ToArray();
|
|
|
|
foreach (var child in copy)
|
|
{
|
|
child.hideFlags = HideFlags.HideAndDontSave;
|
|
}
|
|
|
|
var usedSubgraph = m_Subgraph.GetResource().GetOrCreateGraph();
|
|
usedSubgraph.SyncCustomAttributes();
|
|
if (GetGraph() is { } mainGraph)
|
|
{
|
|
mainGraph.SyncCustomAttributes();
|
|
}
|
|
ResyncCustomAttributes();
|
|
}
|
|
|
|
private void ClearCopy()
|
|
{
|
|
if (m_SubChildren != null)
|
|
{
|
|
foreach (var child in m_SubChildren)
|
|
{
|
|
if (child != null)
|
|
{
|
|
ScriptableObject.DestroyImmediate(child, true);
|
|
}
|
|
}
|
|
m_SubChildren = null;
|
|
}
|
|
}
|
|
|
|
public sealed override string name => m_Subgraph != null ? ObjectNames.NicifyVariableName(m_Subgraph.name) : "Empty Subgraph Operator";
|
|
|
|
protected override IEnumerable<VFXPropertyWithValue> inputProperties
|
|
{
|
|
get
|
|
{
|
|
if (m_SubChildren == null)
|
|
RecreateCopy();
|
|
|
|
return GetParameters(VFXSubgraphUtility.InputPredicate)
|
|
.OrderBy(x => x.order)
|
|
.Select(VFXSubgraphUtility.GetPropertyFromInputParameter);
|
|
}
|
|
}
|
|
|
|
protected override IEnumerable<VFXPropertyWithValue> outputProperties
|
|
{
|
|
get
|
|
{
|
|
foreach (var param in GetParameters(VFXSubgraphUtility.OutputPredicate).OrderBy(t => t.order))
|
|
{
|
|
if (!string.IsNullOrEmpty(param.tooltip))
|
|
yield return new VFXPropertyWithValue(new VFXProperty(param.type, param.exposedName, new TooltipAttribute(param.tooltip)));
|
|
else
|
|
yield return new VFXPropertyWithValue(new VFXProperty(param.type, param.exposedName));
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void GetImportDependentAssets(HashSet<int> dependencies)
|
|
{
|
|
base.GetImportDependentAssets(dependencies);
|
|
if (!object.ReferenceEquals(m_Subgraph, null))
|
|
{
|
|
dependencies.Add(m_Subgraph.GetInstanceID());
|
|
}
|
|
}
|
|
|
|
protected internal override void Invalidate(VFXModel model, InvalidationCause cause)
|
|
{
|
|
if (cause == InvalidationCause.kSettingChanged)
|
|
{
|
|
var graph = GetGraph();
|
|
|
|
if (graph != null && m_Subgraph != null && m_Subgraph.GetResource() is {} resource)
|
|
{
|
|
var otherGraph = resource.GetOrCreateGraph();
|
|
if (otherGraph == graph || otherGraph.subgraphDependencies.Contains(graph.GetResource().visualEffectObject))
|
|
m_Subgraph = null; // prevent cyclic dependencies.
|
|
|
|
if (graph.GetResource().isSubgraph) // BuildSubgraphDependencies is called for vfx by recompilation, but in subgraph we must call it explicitly
|
|
graph.BuildSubgraphDependencies();
|
|
|
|
RecreateCopy();
|
|
}
|
|
}
|
|
|
|
base.Invalidate(model, cause);
|
|
}
|
|
|
|
IEnumerable<VFXParameter> GetParameters(Func<VFXParameter, bool> predicate)
|
|
{
|
|
return m_Subgraph != null
|
|
? VFXSubgraphUtility.GetParameters(m_SubChildren, predicate)
|
|
: Enumerable.Empty<VFXParameter>();
|
|
}
|
|
|
|
public override void CollectDependencies(HashSet<ScriptableObject> objs, bool ownedOnly = true)
|
|
{
|
|
base.CollectDependencies(objs, ownedOnly);
|
|
|
|
if (ownedOnly || m_Subgraph == null)
|
|
return;
|
|
|
|
m_Subgraph.GetResource().GetOrCreateGraph().CollectDependencies(objs, false);
|
|
}
|
|
|
|
public override void CheckGraphBeforeImport()
|
|
{
|
|
base.CheckGraphBeforeImport();
|
|
|
|
// If the graph is reimported it can be because one of its dependency such as the subgraphs, has been changed.
|
|
if (!VFXGraph.explicitCompile)
|
|
{
|
|
MarkOutputExpressionsAsOutOfDate();
|
|
ResyncSlots(true);
|
|
ResyncCustomAttributes();
|
|
}
|
|
}
|
|
|
|
protected override void OnAdded()
|
|
{
|
|
base.OnAdded();
|
|
ResyncCustomAttributes();
|
|
}
|
|
|
|
protected override VFXExpression[] BuildExpression(VFXExpression[] inputExpression)
|
|
{
|
|
if (subgraph == null)
|
|
return Array.Empty<VFXExpression>();
|
|
|
|
if (m_SubChildren == null)
|
|
RecreateCopy();
|
|
|
|
// Change all the inputExpressions of the parameters.
|
|
var parameters = GetParameters(VFXSubgraphUtility.InputPredicate).OrderBy(t => t.order);
|
|
|
|
var backedUpExpressions = new List<VFXExpression>();
|
|
|
|
VFXSubgraphUtility.TransferExpressionToParameters(inputExpression, parameters, backedUpExpressions);
|
|
|
|
List<VFXExpression> outputExpressions = new List<VFXExpression>();
|
|
foreach (var param in GetParameters(VFXSubgraphUtility.OutputPredicate))
|
|
{
|
|
outputExpressions.AddRange(param.inputSlots[0].GetExpressionSlots().Select(t => t.GetExpression()));
|
|
}
|
|
|
|
return outputExpressions.ToArray();
|
|
}
|
|
|
|
void OnProjectOrHierarchyChanged()
|
|
{
|
|
var wasMissing = m_IsMissing;
|
|
var temp = subgraph;
|
|
if (wasMissing != m_IsMissing)
|
|
{
|
|
Invalidate(InvalidationCause.kExpressionGraphChanged);
|
|
}
|
|
}
|
|
|
|
private void ResyncCustomAttributes()
|
|
{
|
|
var graph = GetGraph();
|
|
if (graph == null || m_Subgraph == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var usedSubgraph = m_Subgraph.GetResource().GetOrCreateGraph();
|
|
|
|
foreach (var customAttribute in usedSubgraph.customAttributes)
|
|
{
|
|
if (!graph.attributesManager.Exist(customAttribute.attributeName))
|
|
{
|
|
graph.TryAddCustomAttribute(customAttribute.attributeName, CustomAttributeUtility.GetValueType(customAttribute.type), customAttribute.description, true, out _);
|
|
graph.Invalidate(InvalidationCause.kExpressionGraphChanged);
|
|
}
|
|
else
|
|
{
|
|
graph.TryUpdateCustomAttribute(customAttribute.attributeName, customAttribute.type, customAttribute.description, true);
|
|
}
|
|
}
|
|
graph.SetCustomAttributeDirty();
|
|
}
|
|
|
|
public IEnumerable<VFXAttribute> usedAttributes
|
|
{
|
|
get
|
|
{
|
|
if (m_Subgraph != null)
|
|
{
|
|
var usedSubgraph = m_Subgraph.GetResource().GetOrCreateGraph();
|
|
foreach (var customAttribute in usedSubgraph.customAttributes)
|
|
{
|
|
if (usedSubgraph.attributesManager.TryFind(customAttribute.attributeName, out var attribute))
|
|
{
|
|
yield return attribute;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Rename(string oldName, string newName)
|
|
{
|
|
throw new NotSupportedException("The subgraph operator can use attributes, but cannot rename them");
|
|
}
|
|
}
|
|
}
|