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.
519 lines
23 KiB
519 lines
23 KiB
using System;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
|
|
using UnityEngine;
|
|
using UnityEngine.VFX;
|
|
|
|
namespace UnityEditor.VFX.Block
|
|
{
|
|
class AttributeFromMapVariantProvider : VariantProvider
|
|
{
|
|
private readonly string m_Attribute;
|
|
|
|
public AttributeFromMapVariantProvider(string attribute)
|
|
{
|
|
m_Attribute = attribute;
|
|
}
|
|
|
|
public override IEnumerable<Variant> GetVariants()
|
|
{
|
|
var compositions = new[] { AttributeCompositionMode.Add, AttributeCompositionMode.Overwrite, AttributeCompositionMode.Multiply, AttributeCompositionMode.Blend };
|
|
var sampleModes = new [] { AttributeFromMap.AttributeMapSampleMode.Index, AttributeFromMap.AttributeMapSampleMode.Random, AttributeFromMap.AttributeMapSampleMode.Sample3DLOD, AttributeFromMap.AttributeMapSampleMode.Sequential};
|
|
|
|
foreach (var composition in compositions)
|
|
{
|
|
foreach (var sampleMode in sampleModes)
|
|
{
|
|
// This is the main variant settings
|
|
if (composition == AttributeCompositionMode.Overwrite && sampleMode == AttributeFromMap.AttributeMapSampleMode.Sample2DLOD)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var compositionString = VFXBlockUtility.GetNameString(composition);
|
|
yield return new Variant(
|
|
compositionString.Label(false).AppendLiteral(m_Attribute + " from Map").AppendLabel(VFXBlockUtility.GetNameString(sampleMode), false),
|
|
VFXLibraryStringHelper.Separator(compositionString, 0),
|
|
typeof(AttributeFromMap),
|
|
new[]
|
|
{
|
|
new KeyValuePair<string, object>("attribute", m_Attribute),
|
|
new KeyValuePair<string, object>("Composition", composition),
|
|
new KeyValuePair<string, object>("SampleMode", sampleMode)
|
|
},
|
|
null,
|
|
VFXBlockUtility.GetCompositionSynonym(composition));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class AttributeFromMapProvider : VariantProvider
|
|
{
|
|
public override IEnumerable<Variant> GetVariants()
|
|
{
|
|
var setSynonyms = VFXBlockUtility.GetCompositionSynonym(AttributeCompositionMode.Overwrite);
|
|
var groups = VFXAttributesManager
|
|
.GetBuiltInAttributesOrCombination(true, false, false, false)
|
|
.GroupBy(x => x.category);
|
|
|
|
foreach (var group in groups)
|
|
{
|
|
foreach (var attribute in group)
|
|
{
|
|
yield return new Variant(
|
|
"Set".Label(false).AppendLiteral(attribute.name + " from Map").AppendLabel("2D", false),
|
|
$"Attribute from Map/{attribute.category}",
|
|
typeof(AttributeFromMap),
|
|
new[]
|
|
{
|
|
new KeyValuePair<string, object>("attribute", attribute.name),
|
|
new KeyValuePair<string, object>("Composition", AttributeCompositionMode.Overwrite),
|
|
new KeyValuePair<string, object>("SampleMode", AttributeFromMap.AttributeMapSampleMode.Sample2DLOD)
|
|
},
|
|
() => new AttributeFromMapVariantProvider(attribute.name),
|
|
setSynonyms);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[VFXHelpURL("Block-SetAttributeFromMap")]
|
|
[VFXInfo(variantProvider = typeof(AttributeFromMapProvider))]
|
|
class AttributeFromMap : VFXBlock
|
|
{
|
|
// TODO: Let's factorize this this into a utility class
|
|
public enum AttributeMapSampleMode
|
|
{
|
|
IndexRelative,
|
|
Index,
|
|
Sequential,
|
|
Sample2DLOD,
|
|
Sample3DLOD,
|
|
Random,
|
|
RandomConstantPerParticle,
|
|
}
|
|
|
|
[VFXSetting(VFXSettingAttribute.VisibleFlags.InInspector), StringProvider(typeof(ReadWritableAttributeProvider)), Tooltip("Target Attribute")]
|
|
public string attribute = VFXAttributesManager.GetBuiltInNamesOrCombination(true, false, false, false).First();
|
|
|
|
[VFXSetting(VFXSettingAttribute.VisibleFlags.InInspector), Tooltip("Specifies what operation to perform on the chosen attribute. The value derived from this block can overwrite, add to, multiply with, or blend with the existing attribute value.")]
|
|
public AttributeCompositionMode Composition = AttributeCompositionMode.Overwrite;
|
|
|
|
[VFXSetting, Tooltip("Specifies the mode by which to sample the attribute map. This can be done via an index, sequentially, by sampling a 2D/3D texture, or randomly.")]
|
|
public AttributeMapSampleMode SampleMode = AttributeMapSampleMode.RandomConstantPerParticle;
|
|
|
|
[VFXSetting, SerializeField, Tooltip("Specifies how Unity handles the sample when the index is out of the point cache bounds.")]
|
|
private VFXOperatorUtility.SequentialAddressingMode mode = VFXOperatorUtility.SequentialAddressingMode.Wrap;
|
|
|
|
[VFXSetting, Tooltip("Specifies which channels to use in this block. This is useful for only affecting the relevant data if not all channels are used.")]
|
|
public VariadicChannelOptions channels = VariadicChannelOptions.XYZ;
|
|
private static readonly char[] channelNames = new char[] { 'x', 'y', 'z' };
|
|
|
|
[VFXSetting, SerializeField, Tooltip("When enabled, you can specify the number of points contained in the attribute map. This is useful when the number of points is smaller than the texture size.")]
|
|
private bool usePointCount = false;
|
|
|
|
public override string name
|
|
{
|
|
get
|
|
{
|
|
var variadicName = (currentAttribute.variadic == VFXVariadic.True) ? "." + channels : "";
|
|
return VFXBlockUtility.GetNameString(Composition).Label(false).AppendLiteral(attribute + variadicName + " from Map").AppendLabel(VFXBlockUtility.GetNameString(SampleMode), false);
|
|
}
|
|
}
|
|
|
|
public override VFXContextType compatibleContexts { get; } = VFXContextType.InitAndUpdateAndOutput;
|
|
public override VFXDataType compatibleData { get; } = VFXDataType.Particle;
|
|
|
|
public override IEnumerable<VFXAttributeInfo> attributes
|
|
{
|
|
get
|
|
{
|
|
var attrib = currentAttribute;
|
|
VFXAttributeMode attributeMode = (Composition == AttributeCompositionMode.Overwrite) ? VFXAttributeMode.Write : VFXAttributeMode.ReadWrite;
|
|
if (attrib.variadic == VFXVariadic.True)
|
|
{
|
|
string channelsString = channels.ToString();
|
|
for (int i = 0; i < channelsString.Length; i++)
|
|
yield return new VFXAttributeInfo(VFXAttributesManager.FindBuiltInOnly(attrib.name + channelsString[i]), attributeMode);
|
|
}
|
|
else
|
|
{
|
|
yield return new VFXAttributeInfo(attrib, attributeMode);
|
|
}
|
|
|
|
if (SampleMode == AttributeMapSampleMode.Sequential)
|
|
yield return new VFXAttributeInfo(VFXAttribute.ParticleId, VFXAttributeMode.Read);
|
|
if (SampleMode == AttributeMapSampleMode.Random)
|
|
yield return new VFXAttributeInfo(VFXAttribute.Seed, VFXAttributeMode.ReadWrite);
|
|
if (SampleMode == AttributeMapSampleMode.RandomConstantPerParticle)
|
|
yield return new VFXAttributeInfo(VFXAttribute.ParticleId, VFXAttributeMode.Read);
|
|
}
|
|
}
|
|
|
|
public override void Sanitize(int version)
|
|
{
|
|
if (GetGraph() is {} graph)
|
|
{
|
|
if (VFXBlockUtility.SanitizeAttribute(graph, ref attribute, ref channels, version))
|
|
{
|
|
Invalidate(InvalidationCause.kSettingChanged);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError($"Trying to find attribute '{attribute}' when graph is not available");
|
|
}
|
|
|
|
base.Sanitize(version);
|
|
}
|
|
|
|
protected override IEnumerable<string> filteredOutSettings
|
|
{
|
|
get
|
|
{
|
|
foreach (string setting in base.filteredOutSettings)
|
|
yield return setting;
|
|
if (currentAttribute.variadic == VFXVariadic.False)
|
|
yield return "channels";
|
|
if (SampleMode is AttributeMapSampleMode.Sample2DLOD or AttributeMapSampleMode.Sample3DLOD)
|
|
yield return "usePointCount";
|
|
}
|
|
}
|
|
|
|
protected override IEnumerable<VFXPropertyWithValue> inputProperties
|
|
{
|
|
get
|
|
{
|
|
// Texture Property (2D/3D)
|
|
string textureInputPropertiesType = "InputProperties2DTexture";
|
|
|
|
if (SampleMode == AttributeMapSampleMode.Sample3DLOD)
|
|
textureInputPropertiesType = "InputProperties3DTexture";
|
|
|
|
var properties = PropertiesFromType(textureInputPropertiesType);
|
|
if(usePointCount && SampleMode != AttributeMapSampleMode.Sample2DLOD && SampleMode != AttributeMapSampleMode.Sample3DLOD)
|
|
properties = properties.Concat(PropertiesFromType("InputPropertiesPointCount"));
|
|
|
|
// Sample Mode
|
|
switch (SampleMode)
|
|
{
|
|
case AttributeMapSampleMode.IndexRelative:
|
|
properties = properties.Concat(PropertiesFromType("InputPropertiesRelative"));
|
|
break;
|
|
case AttributeMapSampleMode.Index:
|
|
properties = properties.Concat(PropertiesFromType("InputPropertiesIndex"));
|
|
break;
|
|
case AttributeMapSampleMode.Sample2DLOD:
|
|
properties = properties.Concat(PropertiesFromType("InputPropertiesSample2DLOD"));
|
|
break;
|
|
case AttributeMapSampleMode.Sample3DLOD:
|
|
properties = properties.Concat(PropertiesFromType("InputPropertiesSample3DLOD"));
|
|
break;
|
|
case AttributeMapSampleMode.RandomConstantPerParticle:
|
|
properties = properties.Concat(PropertiesFromType("InputPropertiesRandomConstant"));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Need Composition Input Properties?
|
|
if (Composition == AttributeCompositionMode.Blend)
|
|
{
|
|
properties = properties.Concat(PropertiesFromType("InputPropertiesBlend"));
|
|
}
|
|
|
|
// Scale and Bias for the values, depending on the property type
|
|
var attrib = currentAttribute;
|
|
if (VFXExpression.IsUniform(attrib.type))
|
|
{
|
|
int count = VFXExpression.TypeToSize(attrib.type);
|
|
if (attrib.variadic == VFXVariadic.True)
|
|
count = channels.ToString().Length;
|
|
|
|
string scaleInputPropertiesType;
|
|
switch (count)
|
|
{
|
|
default:
|
|
case 1:
|
|
scaleInputPropertiesType = "InputPropertiesScaleFloat";
|
|
break;
|
|
case 2:
|
|
scaleInputPropertiesType = "InputPropertiesScaleFloat2";
|
|
break;
|
|
case 3:
|
|
scaleInputPropertiesType = "InputPropertiesScaleFloat3";
|
|
break;
|
|
case 4:
|
|
scaleInputPropertiesType = "InputPropertiesScaleFloat4";
|
|
break;
|
|
}
|
|
|
|
properties = properties.Concat(PropertiesFromType(scaleInputPropertiesType));
|
|
}
|
|
|
|
return properties;
|
|
}
|
|
}
|
|
|
|
public override IEnumerable<VFXNamedExpression> parameters
|
|
{
|
|
get
|
|
{
|
|
foreach (var param in base.parameters)
|
|
yield return param;
|
|
if (SampleMode == AttributeMapSampleMode.Sample2DLOD || SampleMode == AttributeMapSampleMode.Sample3DLOD)
|
|
{
|
|
//Do nothing
|
|
}
|
|
else
|
|
{
|
|
var particleIdExpr = new VFXAttributeExpression(VFXAttribute.ParticleId);
|
|
var attribMapExpr = GetExpressionsFromSlots(this).First(o => o.name == "attributeMap").exp;
|
|
var height = new VFXExpressionTextureHeight(attribMapExpr);
|
|
var width = new VFXExpressionTextureWidth(attribMapExpr);
|
|
var texSizeExpr = height * width;
|
|
var countExpr = texSizeExpr;
|
|
if (usePointCount)
|
|
{
|
|
var pointCountExpr = GetExpressionsFromSlots(this).First(o => o.name == "pointCount").exp;
|
|
countExpr = new VFXExpressionMin(pointCountExpr, texSizeExpr);
|
|
}
|
|
|
|
|
|
VFXExpression samplePos = VFXValue.Constant(0);
|
|
|
|
switch (SampleMode)
|
|
{
|
|
case AttributeMapSampleMode.IndexRelative:
|
|
var relativePosExpr = GetExpressionsFromSlots(this).First(o => o.name == "relativePos").exp;
|
|
samplePos = VFXOperatorUtility.ApplyAddressingMode(
|
|
new VFXExpressionCastFloatToUint(relativePosExpr * new VFXExpressionCastUintToFloat(countExpr)),
|
|
countExpr,
|
|
mode);
|
|
break;
|
|
case AttributeMapSampleMode.Index:
|
|
var indexExpr = GetExpressionsFromSlots(this).First(o => o.name == "index").exp;
|
|
samplePos = VFXOperatorUtility.ApplyAddressingMode(indexExpr, countExpr, mode);
|
|
break;
|
|
case AttributeMapSampleMode.Sequential:
|
|
samplePos = VFXOperatorUtility.ApplyAddressingMode(particleIdExpr, countExpr, mode);
|
|
break;
|
|
case AttributeMapSampleMode.Random:
|
|
var randExpr = VFXOperatorUtility.BuildRandom(VFXSeedMode.PerParticle, false, new RandId(this));
|
|
samplePos = new VFXExpressionCastFloatToUint(randExpr * new VFXExpressionCastUintToFloat(countExpr));
|
|
break;
|
|
case AttributeMapSampleMode.RandomConstantPerParticle:
|
|
var seedExpr = GetExpressionsFromSlots(this).First(o => o.name == "Seed").exp;
|
|
var randFixedExpr = VFXOperatorUtility.BuildRandom(VFXSeedMode.PerParticle, true, new RandId(this), seedExpr);
|
|
samplePos = new VFXExpressionCastFloatToUint(randFixedExpr * new VFXExpressionCastUintToFloat(countExpr));
|
|
break;
|
|
}
|
|
var y = samplePos / width;
|
|
var x = samplePos - (y * width);
|
|
var outputType = VFXExpression.TypeToType(currentAttribute.type);
|
|
var type = typeof(VFXExpressionSampleAttributeMap<>).MakeGenericType(outputType);
|
|
var outputExpr = Activator.CreateInstance(type, new object[] { attribMapExpr, x, y });
|
|
|
|
yield return new VFXNamedExpression((VFXExpression)outputExpr, "value");
|
|
}
|
|
}
|
|
}
|
|
|
|
public override string source
|
|
{
|
|
get
|
|
{
|
|
string biasScale = "value = (value + valueBias) * valueScale;";
|
|
string output = "";
|
|
var attrib = currentAttribute;
|
|
string attributeName = attrib.name;
|
|
int loopCount = 1;
|
|
|
|
VFXValueType valueType = attrib.type;
|
|
if (attrib.variadic == VFXVariadic.True)
|
|
{
|
|
loopCount = channels.ToString().Length;
|
|
switch (loopCount)
|
|
{
|
|
case 1:
|
|
valueType = VFXValueType.Float;
|
|
break;
|
|
case 2:
|
|
valueType = VFXValueType.Float2;
|
|
break;
|
|
case 3:
|
|
valueType = VFXValueType.Float3;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (SampleMode == AttributeMapSampleMode.Sample2DLOD || SampleMode == AttributeMapSampleMode.Sample3DLOD)
|
|
{
|
|
output += string.Format(@"
|
|
{0} value = ({0})attributeMap.t.SampleLevel(attributeMap.s, SamplePosition, LOD);
|
|
{1}
|
|
", GetCompatTypeString(valueType), biasScale);
|
|
}
|
|
else // All other SampleModes
|
|
{
|
|
output += biasScale;
|
|
}
|
|
|
|
for (int i = 0; i < loopCount; i++)
|
|
{
|
|
string paramPostfix = (attrib.variadic == VFXVariadic.True) ? "." + channelNames[i] : "";
|
|
string attributePostfix = (attrib.variadic == VFXVariadic.True) ? char.ToUpper(channels.ToString()[i]).ToString() : "";
|
|
|
|
if (Composition != AttributeCompositionMode.Blend)
|
|
output += VFXBlockUtility.GetComposeString(Composition, attributeName + attributePostfix, "value" + paramPostfix);
|
|
else
|
|
output += VFXBlockUtility.GetComposeString(Composition, attributeName + attributePostfix, "value" + paramPostfix, "blend");
|
|
|
|
if (i < loopCount - 1)
|
|
output += "\n";
|
|
}
|
|
return output;
|
|
}
|
|
}
|
|
|
|
public class InputProperties2DTexture
|
|
{
|
|
[Tooltip("AttributeMap texture to read attributes from")]
|
|
public Texture2D attributeMap = VFXResources.defaultResources.noiseTexture;
|
|
}
|
|
public class InputProperties3DTexture
|
|
{
|
|
[Tooltip("3D AttributeMap texture to read attributes from")]
|
|
public Texture3D attributeMap = VFXResources.defaultResources.vectorField;
|
|
}
|
|
|
|
public class InputPropertiesRelative
|
|
{
|
|
[Tooltip("Position in range [0..1] to sample")]
|
|
public float relativePos = 0.0f;
|
|
}
|
|
|
|
public class InputPropertiesIndex
|
|
{
|
|
[Tooltip("Absolute index to sample")]
|
|
public uint index = 0;
|
|
}
|
|
|
|
public class InputPropertiesSample2DLOD
|
|
{
|
|
[Tooltip("Absolute index to sample")]
|
|
public Vector2 SamplePosition = Vector2.zero;
|
|
public float LOD = 0.0f;
|
|
}
|
|
public class InputPropertiesSample3DLOD
|
|
{
|
|
[Tooltip("Absolute index to sample")]
|
|
public Vector3 SamplePosition = Vector2.zero;
|
|
public float LOD = 0.0f;
|
|
}
|
|
public class InputPropertiesRandomConstant
|
|
{
|
|
[Tooltip("Seed to compute the constant random")]
|
|
public uint Seed = 0;
|
|
}
|
|
|
|
public class InputPropertiesBlend
|
|
{
|
|
[Tooltip("Blend fraction with previous value")]
|
|
public float blend = 0.5f;
|
|
}
|
|
|
|
public class InputPropertiesScaleFloat
|
|
{
|
|
[Tooltip("Bias Applied to the read float value")]
|
|
public float valueBias = 0.0f;
|
|
[Tooltip("Scale Applied to the read float value")]
|
|
public float valueScale = 1.0f;
|
|
}
|
|
public class InputPropertiesScaleFloat2
|
|
{
|
|
[Tooltip("Bias Applied to the read Vector2 value")]
|
|
public Vector2 valueBias = new Vector2(0.0f, 0.0f);
|
|
[Tooltip("Scale Applied to the read Vector2 value")]
|
|
public Vector2 valueScale = new Vector2(1.0f, 1.0f);
|
|
}
|
|
public class InputPropertiesScaleFloat3
|
|
{
|
|
[Tooltip("Bias Applied to the read Vector3 value")]
|
|
public Vector3 valueBias = new Vector3(0.0f, 0.0f, 0.0f);
|
|
[Tooltip("Scale Applied to the read Vector3 value")]
|
|
public Vector3 valueScale = new Vector3(1.0f, 1.0f, 1.0f);
|
|
}
|
|
|
|
public class InputPropertiesScaleFloat4
|
|
{
|
|
[Tooltip("Bias Applied to the read Vector4 value")]
|
|
public Vector4 valueBias = new Vector4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
[Tooltip("Scale Applied to the read Vector4 value")]
|
|
public Vector4 valueScale = new Vector4(1.0f, 1.0f, 1.0f, 1.0f);
|
|
}
|
|
|
|
public class InputPropertiesPointCount
|
|
{
|
|
[Tooltip("Sets the number of elements in the attribute map.")]
|
|
public uint pointCount = 0u;
|
|
}
|
|
|
|
protected override void OnAdded()
|
|
{
|
|
base.OnAdded();
|
|
// When using custom attribute we need to access to the graph to find the custom attribute
|
|
// and the graph is only available after the node being added to it.
|
|
if (GetGraph().attributesManager.IsCustom(attribute))
|
|
{
|
|
Invalidate(InvalidationCause.kSettingChanged);
|
|
}
|
|
}
|
|
|
|
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 override void Rename(string oldName, string newName)
|
|
{
|
|
if (GetGraph() is {} graph && graph.attributesManager.IsCustom(newName))
|
|
{
|
|
attribute = newName;
|
|
SyncSlots(VFXSlot.Direction.kInput, true);
|
|
}
|
|
}
|
|
|
|
private static string GetCompatTypeString(VFXValueType valueType)
|
|
{
|
|
if (!VFXExpression.IsUniform(valueType))
|
|
throw new InvalidOperationException("Trying to fetch an attribute of type: " + valueType);
|
|
|
|
return VFXExpression.TypeToCode(valueType);
|
|
}
|
|
}
|
|
}
|