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.
 
 
 
 

378 lines
18 KiB

using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.VFX.Block
{
class PositionSequentialVariantProvider : VariantProvider
{
public override IEnumerable<Variant> GetVariants()
{
return new[] { PositionSequential.SequentialShape.Circle, PositionSequential.SequentialShape.Line, PositionSequential.SequentialShape.ThreeDimensional }
.Select(x => new Variant(
"Set".Label(false).AppendLiteral("Position Sequential", false).AppendLabel(x.ToString()),
"Position Shape/Sequential",
typeof(PositionSequential),
new[]
{
new KeyValuePair<string, object>("compositionPosition", AttributeCompositionMode.Overwrite),
new KeyValuePair<string, object>("shape", x)
}));
}
}
[VFXHelpURL("Block-SetPosition(Sequential)")]
[VFXInfo(variantProvider = typeof(PositionSequentialVariantProvider))]
class PositionSequential : VFXBlock
{
public enum SequentialShape
{
Line,
Circle,
ThreeDimensional,
}
public enum IndexMode
{
ParticleID,
Custom
}
[VFXSetting(VFXSettingAttribute.VisibleFlags.InInspector), Tooltip("Specifies what operation to perform on Position. The input value can overwrite, add to, multiply with, or blend with the existing attribute value.")]
public AttributeCompositionMode compositionPosition = AttributeCompositionMode.Add;
[VFXSetting(VFXSettingAttribute.VisibleFlags.InInspector), Tooltip("Specifies what operation to perform on Direction. The input value can overwrite, add to, multiply with, or blend with the existing attribute value.")]
public AttributeCompositionMode compositionDirection = AttributeCompositionMode.Overwrite;
[VFXSetting(VFXSettingAttribute.VisibleFlags.InInspector), Tooltip("Specifies what operation to perform on TargetPosition. The input value can overwrite, add to, multiply with, or blend with the existing attribute value.")]
public AttributeCompositionMode compositionTargetPosition = AttributeCompositionMode.Add;
[SerializeField, VFXSetting(VFXSettingAttribute.VisibleFlags.InInspector)]
[Tooltip("Specifies the type of shape to use for the position sequence.")]
protected SequentialShape shape = SequentialShape.Line;
[SerializeField, VFXSetting]
[Tooltip("Specifies whether to use the Particle ID or a custom index when fetching the progression in the sequence.")]
protected IndexMode index = IndexMode.ParticleID;
[SerializeField, VFXSetting]
[Tooltip("When enabled, the block will write to the particle position attribute.")]
private bool writePosition = true;
[SerializeField, VFXSetting]
[Tooltip("When enabled, the block will write to the particle target position attribute.")]
private bool writeTargetPosition = false;
[SerializeField, VFXSetting]
[Tooltip("Specifies how the sequence should behave at the end. It can either wrap back to the beginning, clamp, or continue in a mirrored direction.")]
private VFXOperatorUtility.SequentialAddressingMode mode = VFXOperatorUtility.SequentialAddressingMode.Clamp;
public override string name => VFXBlockUtility.GetNameString(compositionPosition).Label(false).AppendLiteral("Position Sequential", false).AppendLabel(shape.ToString());
public override VFXContextType compatibleContexts { get { return VFXContextType.InitAndUpdateAndOutput; } }
public override VFXDataType compatibleData { get { return VFXDataType.Particle; } }
public class InputProperties
{
}
public class InputPropertiesCustomIndex
{
[Tooltip("Sets the index used to sample the sequential distribution.")]
public uint Index = 0;
}
public class InputPropertiesWritePosition
{
[Tooltip("Sets an offset to the initial index used to compute the position.")]
public int OffsetIndex = 0;
}
public class InputPropertiesWriteTargetPosition
{
[Tooltip("Sets an offset to the initial index used to compute the target position.")]
public int OffsetTargetIndex = 1;
}
public class InputPropertiesBlendPosition
{
[Range(0.0f, 1.0f), Tooltip("Sets the blending value for position attribute.")]
public float blendPosition = 1.0f;
}
public class InputPropertiesBlendTargetPosition
{
[Range(0.0f, 1.0f), Tooltip("Sets the blending value for targetPosition attribute.")]
public float blendTargetPosition = 1.0f;
}
public class InputPropertiesBlendDirection
{
[Range(0.0f, 1.0f), Tooltip("Set the blending value for direction attribute.")]
public float blendDirection = 1.0f;
}
public class InputPropertiesLine
{
[Tooltip("Sets the count used to loop over the entire sequence.")]
public uint Count = 64;
[Tooltip("Sets the start position of the sequential line.")]
public Position Start = Position.defaultValue;
[Tooltip("Sets the end position of the sequential line.")]
public Position End = new Position() { position = new Vector3(1, 0, 0) };
}
public class InputPropertiesCircle
{
[Tooltip("Sets the count used to loop over the entire sequence.")]
public uint Count = 64;
[Tooltip("Sets the center of the sequential circle.")]
public Position Center = Position.defaultValue;
[Tooltip("Sets the Forward axis of the sequential circle.")]
public DirectionType Normal = new DirectionType() { direction = Vector3.forward };
[Tooltip("Sets the Up axis of the sequential circle.")]
public DirectionType Up = new DirectionType() { direction = Vector3.up };
[Tooltip("Sets the radius of the sequential circle.")]
public float Radius = 1.0f;
}
public class InputPropertiesThreeDimensional
{
[Tooltip("Sets the count on the X axis used to loop over the entire sequence.")]
public uint CountX = 8;
[Tooltip("Sets the count on the Y axis used to loop over the entire sequence.")]
public uint CountY = 8;
[Tooltip("Sets the count on the Z axis used to loop over the entire sequence.")]
public uint CountZ = 8;
[Tooltip("Sets the origin position of the sequence.")]
public Position Origin = Position.defaultValue;
[Tooltip("Sets the X axis of the sequence.")]
public Vector AxisX = Vector3.right;
[Tooltip("Sets the Y axis of the sequence.")]
public Vector AxisY = Vector3.up;
[Tooltip("Sets the Z axis of the sequence.")]
public Vector AxisZ = Vector3.forward;
}
protected override IEnumerable<VFXPropertyWithValue> inputProperties
{
get
{
var commonProperties = PropertiesFromType("InputProperties");
if (index == IndexMode.Custom)
commonProperties = commonProperties.Concat(PropertiesFromType("InputPropertiesCustomIndex"));
if (writePosition)
{
commonProperties = commonProperties.Concat(PropertiesFromType("InputPropertiesWritePosition"));
if (compositionPosition == AttributeCompositionMode.Blend)
commonProperties = commonProperties.Concat(PropertiesFromType("InputPropertiesBlendPosition"));
if (compositionDirection == AttributeCompositionMode.Blend)
commonProperties = commonProperties.Concat(PropertiesFromType("InputPropertiesBlendDirection"));
}
if (writeTargetPosition)
{
commonProperties = commonProperties.Concat(PropertiesFromType("InputPropertiesWriteTargetPosition"));
if (compositionTargetPosition == AttributeCompositionMode.Blend)
commonProperties = commonProperties.Concat(PropertiesFromType("InputPropertiesBlendTargetPosition"));
}
switch (shape)
{
case SequentialShape.Line: commonProperties = commonProperties.Concat(PropertiesFromType("InputPropertiesLine")); break;
case SequentialShape.Circle: commonProperties = commonProperties.Concat(PropertiesFromType("InputPropertiesCircle")); break;
case SequentialShape.ThreeDimensional: commonProperties = commonProperties.Concat(PropertiesFromType("InputPropertiesThreeDimensional")); break;
}
return commonProperties;
}
}
protected override IEnumerable<string> filteredOutSettings
{
get
{
if (!writePosition)
{
yield return "compositionPosition";
yield return "compositionDirection";
}
if (!writeTargetPosition)
yield return "compositionTargetPosition";
}
}
public override IEnumerable<VFXAttributeInfo> attributes
{
get
{
if (index == IndexMode.ParticleID)
yield return new VFXAttributeInfo(VFXAttribute.ParticleId, VFXAttributeMode.Read);
if (writePosition)
{
yield return new VFXAttributeInfo(VFXAttribute.Position, compositionPosition == AttributeCompositionMode.Overwrite ? VFXAttributeMode.Write : VFXAttributeMode.ReadWrite);
yield return new VFXAttributeInfo(VFXAttribute.Direction, compositionDirection == AttributeCompositionMode.Overwrite ? VFXAttributeMode.Write : VFXAttributeMode.ReadWrite);
}
if (writeTargetPosition)
yield return new VFXAttributeInfo(VFXAttribute.TargetPosition, compositionTargetPosition == AttributeCompositionMode.Overwrite ? VFXAttributeMode.Write : VFXAttributeMode.ReadWrite);
}
}
private void GetPositionAndDirectionFromIndex(VFXExpression indexExpr, IEnumerable<VFXNamedExpression> expressions, out VFXExpression positionExpr, out VFXExpression directionExpr)
{
if (shape == SequentialShape.Line)
{
var start = expressions.First(o => o.name == "Start").exp;
var end = expressions.First(o => o.name == "End").exp;
var count = expressions.First(o => o.name == "Count").exp;
positionExpr = VFXOperatorUtility.SequentialLine(start, end, indexExpr, count, mode);
directionExpr = VFXOperatorUtility.SafeNormalize(end - start);
}
else if (shape == SequentialShape.Circle)
{
var center = expressions.First(o => o.name == "Center").exp;
var normal = expressions.First(o => o.name == "Normal").exp;
var up = expressions.First(o => o.name == "Up").exp;
var radius = expressions.First(o => o.name == "Radius").exp;
var count = expressions.First(o => o.name == "Count").exp;
positionExpr = VFXOperatorUtility.SequentialCircle(center, radius, normal, up, indexExpr, count, mode);
directionExpr = VFXOperatorUtility.SafeNormalize(positionExpr - center);
}
else if (shape == SequentialShape.ThreeDimensional)
{
var origin = expressions.First(o => o.name == "Origin").exp;
var axisX = expressions.First(o => o.name == "AxisX").exp;
var axisY = expressions.First(o => o.name == "AxisY").exp;
var axisZ = expressions.First(o => o.name == "AxisZ").exp;
var countX = expressions.First(o => o.name == "CountX").exp;
var countY = expressions.First(o => o.name == "CountY").exp;
var countZ = expressions.First(o => o.name == "CountZ").exp;
positionExpr = VFXOperatorUtility.Sequential3D(origin, axisX, axisY, axisZ, indexExpr, countX, countY, countZ, mode);
directionExpr = VFXOperatorUtility.SafeNormalize(positionExpr - origin);
}
else throw new NotImplementedException();
}
private static readonly string s_computedPosition = "computedPosition";
private static readonly string s_computedDirection = "computedDirection";
private static readonly string s_computedTargetPosition = "computedTargetPosition";
public override IEnumerable<VFXNamedExpression> parameters
{
get
{
var expressions = GetExpressionsFromSlots(this);
var indexExpr = (index == IndexMode.ParticleID) ? new VFXAttributeExpression(VFXAttribute.ParticleId) : expressions.First(o => o.name == "Index").exp;
if (writePosition)
{
var indexOffsetExpr = indexExpr + new VFXExpressionCastIntToUint(expressions.First(o => o.name == "OffsetIndex").exp);
GetPositionAndDirectionFromIndex(indexOffsetExpr, expressions, out var positionExpr, out var directionExpr);
yield return new VFXNamedExpression(positionExpr, s_computedPosition);
yield return new VFXNamedExpression(directionExpr, s_computedDirection);
if (compositionPosition == AttributeCompositionMode.Blend)
yield return expressions.FirstOrDefault(o => o.name == "blendPosition");
if (compositionDirection == AttributeCompositionMode.Blend)
yield return expressions.FirstOrDefault(o => o.name == "blendDirection");
}
if (writeTargetPosition)
{
var indexOffsetExpr = indexExpr + new VFXExpressionCastIntToUint(expressions.First(o => o.name == "OffsetTargetIndex").exp);
GetPositionAndDirectionFromIndex(indexOffsetExpr, expressions, out var targetPositionExpr, out var targetDirectionExpr);
yield return new VFXNamedExpression(targetPositionExpr, s_computedTargetPosition);
if (compositionTargetPosition == AttributeCompositionMode.Blend)
yield return expressions.FirstOrDefault(o => o.name == "blendTargetPosition");
}
}
}
public override string source
{
get
{
var source = string.Empty;
if (writePosition)
{
source += VFXBlockUtility.GetComposeString(compositionPosition, "position", s_computedPosition, "blendPosition");
source += "\n";
source += VFXBlockUtility.GetComposeString(compositionDirection, "direction", s_computedDirection, "blendDirection");
}
if (writeTargetPosition)
{
source += "\n";
source += VFXBlockUtility.GetComposeString(compositionTargetPosition, "targetPosition", s_computedTargetPosition, "blendTargetPosition");
}
return source;
}
}
public static void GenerateSequentialCircleErrors(IVFXErrorReporter report, string countName, string normalName, string upName, VFXModel model)
{
var slotContainer = model as IVFXSlotContainer;
if (slotContainer == null)
return;
var context = new VFXExpression.Context(VFXExpressionContextOption.CPUEvaluation | VFXExpressionContextOption.ConstantFolding);
var countExpression = slotContainer.inputSlots.Single(x => x.name == countName).GetExpression();
var normalExpression = slotContainer.inputSlots.Single(x => x.name == normalName).GetExpression();
var upExpression = slotContainer.inputSlots.Single(x => x.name == upName).GetExpression();
context.RegisterExpression(countExpression);
context.RegisterExpression(normalExpression);
context.RegisterExpression(upExpression);
context.Compile();
if (context.GetReduced(countExpression) is var countExpressionReduced &&
countExpressionReduced.Is(VFXExpression.Flags.Constant) &&
countExpressionReduced.Get<uint>() == 0)
{
report.RegisterError("CircleCountIsZero", VFXErrorType.Warning, "A circle with Count = 0 is not valid", model);
}
if (context.GetReduced(normalExpression) is var normalExpressionReduced &&
context.GetReduced(upExpression) is var upExpressionReduced &&
normalExpressionReduced.Is(VFXExpression.Flags.Constant) &&
upExpressionReduced.Is(VFXExpression.Flags.Constant))
{
var normal = normalExpressionReduced.Get<Vector3>();
var up = upExpressionReduced.Get<Vector3>();
if (float.IsNaN(normal.x) || float.IsNaN(normal.y) || float.IsNaN(normal.z))
{
report.RegisterError("CircleNormalIsInvalid", VFXErrorType.Warning, "Normal vector is invalid.", model);
}
if (float.IsNaN(up.x) || float.IsNaN(up.y) || float.IsNaN(up.z))
{
report.RegisterError("CircleUpIsInvalid", VFXErrorType.Warning, "Up vector is invalid.", model);
}
if (Math.Abs(Vector3.Cross(normal, up).sqrMagnitude) < 10e-5f)
{
report.RegisterError("CircleNormalAndUpArCollinear", VFXErrorType.Warning, "Normal and Up vectors are collinear, circle orientation cannot be computed.", model);
}
}
}
internal sealed override void GenerateErrors(VFXErrorReporter report)
{
if (shape == SequentialShape.Circle)
{
GenerateSequentialCircleErrors(report, nameof(InputPropertiesCircle.Count), nameof(InputPropertiesCircle.Normal), nameof(InputPropertiesCircle.Up), this);
}
}
}
}