Rasagar/Library/PackageCache/com.unity.visualeffectgraph/Editor/Models/Parameters/VFXAttributeParameter.cs

272 lines
10 KiB
C#
Raw Normal View History

2024-08-26 13:07:20 -07:00
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.VFX.Block;
using UnityEngine;
using UnityEngine.VFX;
namespace UnityEditor.VFX
{
// This derived variant is used to provide a documentation link based on the attribute name
class GetAttributeVariant : Variant
{
private string attribute;
public GetAttributeVariant(string name, string category)
: base("Get".AppendLiteral(ObjectNames.NicifyVariableName(name)),
$"Attribute/{category}",
typeof(VFXAttributeParameter),
new[] { new KeyValuePair<string, object>("attribute", name) })
{
this.attribute = char.ToUpper(name[0]) + name.Substring(1);
}
public override string GetDocumentationLink()
{
return string.Format(base.GetDocumentationLink(), this.attribute);
}
}
class AttributeProvider : IVFXModelStringProvider
{
public string[] GetAvailableString(VFXModel model)
{
return model.GetGraph().attributesManager.GetAllNamesOrCombination(true, false, true, false).ToArray();
}
}
class ReadWritableAttributeProvider : IVFXModelStringProvider
{
public string[] GetAvailableString(VFXModel model)
{
return model.GetGraph().attributesManager.GetAllNamesOrCombination(true, false, false, false).ToArray();
}
}
class AttributeVariant : VariantProvider
{
public override IEnumerable<Variant> GetVariants()
{
var groups = VFXAttributesManager
.GetBuiltInAttributesOrCombination(true, false, true, false)
.GroupBy(x => x.category);
foreach (var group in groups)
{
foreach (var attribute in group)
{
yield return new GetAttributeVariant(attribute.name, attribute.category);
}
}
}
}
[VFXHelpURL("Operator-GetAttribute{0}")]
[VFXInfo(category = "Attribute", variantProvider = typeof(AttributeVariant))]
class VFXAttributeParameter : VFXOperator, IVFXAttributeUsage
{
[VFXSetting(VFXSettingAttribute.VisibleFlags.InInspector), StringProvider(typeof(AttributeProvider)), Tooltip("Specifies which attribute to use.")]
public string attribute = VFXAttributesManager.GetBuiltInNamesOrCombination(true, true, true, false).First();
[VFXSetting, Tooltip("Specifies which version of the parameter to use. It can return the current value, or the source value derived from a GPU event or a spawn attribute.")]
public VFXAttributeLocation location = VFXAttributeLocation.Current;
[VFXSetting, Regex("[^x-zX-Z]", 3), Tooltip("Sets the axes and the order in which they are derived. The input can be only the letters x, y, and z, in any combination, up to a length of 3 (i.e. xyz).")]
public string mask = "xyz";
protected override IEnumerable<string> filteredOutSettings
{
get
{
if (string.IsNullOrEmpty(attribute))
{
yield break;
}
foreach (string setting in base.filteredOutSettings)
yield return setting;
if (currentAttribute.variadic == VFXVariadic.False)
yield return "mask";
}
}
public IEnumerable<VFXAttribute> usedAttributes
{
get { yield return currentAttribute; }
}
protected override IEnumerable<VFXPropertyWithValue> outputProperties
{
get
{
if (string.IsNullOrEmpty(attribute))
{
yield break;
}
var vfxAttribute = currentAttribute;
var tooltip = !string.IsNullOrEmpty(vfxAttribute.description) ? new TooltipAttribute(vfxAttribute.description) : null;
var attr = tooltip != null ? new VFXPropertyAttributes(tooltip) : new VFXPropertyAttributes();
if (vfxAttribute.variadic == VFXVariadic.True)
{
Type slotType = null;
switch (mask.Length)
{
case 1: slotType = typeof(float); break;
case 2: slotType = typeof(Vector2); break;
case 3: slotType = typeof(Vector3); break;
case 4: slotType = typeof(Vector4); break;
default: break;
}
if (slotType != null)
yield return new VFXPropertyWithValue(new VFXProperty(slotType, vfxAttribute.name, attr));
}
else
{
yield return new VFXPropertyWithValue(new VFXProperty(VFXExpression.TypeToType(vfxAttribute.type), ObjectNames.NicifyVariableName(vfxAttribute.name), attr));
}
}
}
public override string name
{
get
{
string result = "Get".AppendLiteral(attribute).AppendLabel(location.ToString());
if (GetGraph() is {} graph && graph.attributesManager.TryFind(attribute, out var attrib))
{
if (attrib.variadic == VFXVariadic.True)
result += "." + mask;
}
return result;
}
}
public override void Sanitize(int version)
{
var graph = GetGraph();
if (graph != null)
{
if (!graph.attributesManager.Exist(attribute))
{
Debug.LogWarningFormat("Attribute parameter was removed because attribute {0} does not exist",
attribute);
RemoveModel(this, false);
return; // Dont sanitize further, model was removed
}
VFXBlockUtility.SanitizeAttribute(graph, ref attribute, ref mask, version);
}
else
{
throw new InvalidOperationException("Graph is null during Sanitize");
}
base.Sanitize(version);
}
protected override VFXExpression[] BuildExpression(VFXExpression[] inputExpression)
{
var vfxAttribute = currentAttribute;
if (vfxAttribute.variadic == VFXVariadic.True)
{
var attributes = new VFXAttribute[] { VFXAttributesManager.FindBuiltInOnly(vfxAttribute.name + "X"), VFXAttributesManager.FindBuiltInOnly(vfxAttribute.name + "Y"), VFXAttributesManager.FindBuiltInOnly(vfxAttribute.name + "Z") };
var expressions = attributes.Select(a => new VFXAttributeExpression(a, location)).ToArray();
var componentStack = new Stack<VFXExpression>();
int outputSize = mask.Length;
for (int iComponent = 0; iComponent < outputSize; iComponent++)
{
char componentChar = char.ToLower(mask[iComponent]);
int currentComponent = Math.Min(componentChar - 'x', 2);
componentStack.Push(expressions[currentComponent]);
}
VFXExpression finalExpression = null;
if (componentStack.Count == 1)
{
finalExpression = componentStack.Pop();
}
else
{
finalExpression = new VFXExpressionCombine(componentStack.Reverse().ToArray());
}
return new[] { finalExpression };
}
else
{
var expression = new VFXAttributeExpression(vfxAttribute, location);
return new VFXExpression[] { expression };
}
}
public VFXAttribute currentAttribute
{
get
{
if (GetGraph() is { } graph)
{
if (graph.attributesManager.TryFind(attribute, out var vfxAttribute))
{
return vfxAttribute;
}
}
else // Happens when the node is not yet added to the graph, but should be ok as soon as it's added (see OnAdded)
{
var attr = VFXAttributesManager.FindBuiltInOnly(attribute);
if (string.Compare(attribute, attr.name, StringComparison.OrdinalIgnoreCase) == 0)
{
return attr;
}
}
// Temporary attribute
return new VFXAttribute(attribute, VFXValueType.Float, null);
}
}
public void Rename(string oldName, string newName)
{
if (GetGraph() is {} graph && graph.attributesManager.IsCustom(newName))
{
attribute = newName;
SyncSlots(VFXSlot.Direction.kOutput, true);
}
}
internal sealed override void GenerateErrors(VFXErrorReporter report)
{
base.GenerateErrors(report);
if (!CustomAttributeUtility.IsShaderCompilableName(attribute))
{
report.RegisterError("InvalidCustomAttributeName", VFXErrorType.Error, $"Custom attribute name '{attribute}' is not valid.\n\t- The name must not contain spaces or any special character\n\t- The name must not start with a digit character", this);
}
}
protected override void OnAdded()
{
base.OnAdded();
SyncCustomAttributeIfNeeded();
}
private void SyncCustomAttributeIfNeeded()
{
var graph = GetGraph();
if (graph != null && graph.attributesManager.IsCustom(attribute))
{
Invalidate(InvalidationCause.kUIChangedTransient);
SyncSlots(VFXSlot.Direction.kOutput, true);
}
else if (graph != null && !string.IsNullOrEmpty(attribute) && !graph.attributesManager.TryFind(attribute, out _))
{
graph.TryAddCustomAttribute(attribute, VFXValueType.Float, string.Empty, false, out _);
graph.SetCustomAttributeDirty();
Invalidate(InvalidationCause.kUIChangedTransient);
}
}
}
}