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.
527 lines
20 KiB
527 lines
20 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.ComponentModel;
|
|
using UnityEngine;
|
|
|
|
namespace UnityEditor.VFX.Block
|
|
{
|
|
class OrientationModeProvider : VariantProvider
|
|
{
|
|
public override IEnumerable<Variant> GetVariants()
|
|
{
|
|
foreach (var mode in Enum.GetValues(typeof(Orient.Mode)))
|
|
{
|
|
yield return new Variant(
|
|
$"Orient {ObjectNames.NicifyVariableName(mode.ToString())}",
|
|
"Attribute/orientation",
|
|
typeof(Orient),
|
|
new[] {new KeyValuePair<string, object>("mode", mode)});
|
|
}
|
|
}
|
|
}
|
|
|
|
[VFXHelpURL("Block-Orient")]
|
|
[VFXInfo(variantProvider = typeof(OrientationModeProvider))]
|
|
class Orient : VFXBlock
|
|
{
|
|
public enum Mode
|
|
{
|
|
FaceCameraPlane,
|
|
FaceCameraPosition,
|
|
LookAtPosition,
|
|
LookAtLine,
|
|
Advanced,
|
|
FixedAxis, // non strips only
|
|
AlongVelocity, // non strips only
|
|
CustomZ, // strips only
|
|
CustomY, // strips only
|
|
}
|
|
|
|
public enum AxesPair
|
|
{
|
|
XY = 0,
|
|
YZ = 1,
|
|
ZX = 2,
|
|
YX = 3,
|
|
ZY = 4,
|
|
XZ = 5,
|
|
}
|
|
|
|
[VFXSetting, Tooltip("Specifies the orientation mode of the particle. It can face towards the camera or a specific position, orient itself along the velocity or a fixed axis, or use more advanced facing behavior.")]
|
|
public Mode mode;
|
|
|
|
[VFXSetting, Tooltip("Specifies which two axes to use for the particle orientation.")]
|
|
public AxesPair axes = AxesPair.ZY;
|
|
|
|
[VFXSetting, Tooltip("Specifies if the particles faces the ray in Ray-Traced effects.")]
|
|
public bool faceRay = true;
|
|
protected override IEnumerable<string> filteredOutSettings
|
|
{
|
|
get
|
|
{
|
|
if (mode != Mode.Advanced)
|
|
yield return "axes";
|
|
if ((mode != Mode.FaceCameraPlane && mode != Mode.FaceCameraPosition) || m_Parent is VFXAbstractParticleOutput output && !output.isRayTraced)
|
|
yield return "faceRay";
|
|
}
|
|
}
|
|
|
|
public override IEnumerable<int> GetFilteredOutEnumerators(string name)
|
|
{
|
|
if (name == "mode" && canTestStrips)
|
|
{
|
|
if (hasStrips)
|
|
{
|
|
yield return (int)Mode.FaceCameraPlane;
|
|
yield return (int)Mode.FixedAxis;
|
|
yield return (int)Mode.AlongVelocity;
|
|
}
|
|
else
|
|
{
|
|
yield return (int)Mode.CustomZ;
|
|
yield return (int)Mode.CustomY;
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool canTestStrips => flattenedParent as VFXAbstractParticleOutput; // Cannot check strip in subblock context or not child of a context
|
|
private bool hasStrips => ((VFXAbstractParticleOutput)flattenedParent).HasStrips(); // direct cast as canTestStrips is supposed to have been called priorly
|
|
|
|
public override string name { get { return "Orient: " + ObjectNames.NicifyVariableName(mode.ToString()); } }
|
|
|
|
public override VFXContextType compatibleContexts { get { return VFXContextType.Output; } }
|
|
public override VFXDataType compatibleData { get { return VFXDataType.Particle; } }
|
|
|
|
public override IEnumerable<VFXAttributeInfo> attributes
|
|
{
|
|
get
|
|
{
|
|
yield return new VFXAttributeInfo(VFXAttribute.AxisX, VFXAttributeMode.Write);
|
|
yield return new VFXAttributeInfo(VFXAttribute.AxisY, VFXAttributeMode.Write);
|
|
yield return new VFXAttributeInfo(VFXAttribute.AxisZ, VFXAttributeMode.Write);
|
|
if (mode != Mode.Advanced && mode != Mode.CustomY && mode != Mode.CustomZ && (mode != Mode.FaceCameraPlane || faceRay))
|
|
yield return new VFXAttributeInfo(VFXAttribute.Position, VFXAttributeMode.Read);
|
|
if (mode == Mode.AlongVelocity)
|
|
yield return new VFXAttributeInfo(VFXAttribute.Velocity, VFXAttributeMode.Read);
|
|
}
|
|
}
|
|
|
|
protected override IEnumerable<VFXPropertyWithValue> inputProperties
|
|
{
|
|
get
|
|
{
|
|
switch (mode)
|
|
{
|
|
case Mode.LookAtPosition:
|
|
yield return new VFXPropertyWithValue(new VFXProperty(typeof(Position), "Position"));
|
|
break;
|
|
|
|
case Mode.LookAtLine:
|
|
yield return new VFXPropertyWithValue(new VFXProperty(typeof(Line), "Line"), Line.defaultValue);
|
|
break;
|
|
|
|
case Mode.Advanced:
|
|
{
|
|
string axis1, axis2;
|
|
Vector3 vector1, vector2;
|
|
AxesPairToUI(axes, out axis1, out axis2);
|
|
AxesPairToVector(axes, out vector1, out vector2);
|
|
yield return new VFXPropertyWithValue(new VFXProperty(typeof(DirectionType), axis1), new DirectionType() { direction = vector1 });
|
|
yield return new VFXPropertyWithValue(new VFXProperty(typeof(DirectionType), axis2), new DirectionType() { direction = vector2 });
|
|
break;
|
|
}
|
|
|
|
case Mode.FixedAxis:
|
|
yield return new VFXPropertyWithValue(new VFXProperty(typeof(DirectionType), "Up"), new DirectionType() { direction = Vector3.up });
|
|
break;
|
|
|
|
case Mode.CustomZ:
|
|
yield return new VFXPropertyWithValue(new VFXProperty(typeof(DirectionType), "Front"), new DirectionType() { direction = -Vector3.forward });
|
|
break;
|
|
|
|
case Mode.CustomY:
|
|
yield return new VFXPropertyWithValue(new VFXProperty(typeof(DirectionType), "Up"), new DirectionType() { direction = Vector3.up });
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public override IEnumerable<VFXNamedExpression> parameters
|
|
{
|
|
get
|
|
{
|
|
foreach (var exp in base.parameters)
|
|
yield return exp;
|
|
|
|
if (canTestStrips && hasStrips && mode != Mode.Advanced)
|
|
yield return new VFXNamedExpression(new VFXExpressionStripTangent(), "stripTangent");
|
|
}
|
|
}
|
|
|
|
public override IEnumerable<string> defines
|
|
{
|
|
get
|
|
{
|
|
if (faceRay && (mode == Mode.FaceCameraPlane || mode == Mode.FaceCameraPosition))
|
|
yield return "VFX_FACE_RAY";
|
|
}
|
|
}
|
|
|
|
public override string source
|
|
{
|
|
get
|
|
{
|
|
switch (mode)
|
|
{
|
|
case Mode.FaceCameraPlane:
|
|
if (canTestStrips && hasStrips)
|
|
throw new NotImplementedException("This orient mode (FaceCameraPlane) is not available for strips");
|
|
string sourceCodeCameraPlane = @"
|
|
float3x3 viewRot = GetVFXToViewRotMatrix();";
|
|
if (faceRay)
|
|
sourceCodeCameraPlane += @"
|
|
float3 worldUp = VFXGetObjectToWorldMatrix()._m10_m11_m12;
|
|
GetCameraPlaneOrRayFacingAxes(viewRot, position, worldUp, axisX, axisY, axisZ);";
|
|
else
|
|
sourceCodeCameraPlane += @"
|
|
GetCameraPlaneFacingAxes(viewRot, axisX, axisY, axisZ);";
|
|
|
|
return sourceCodeCameraPlane + @"
|
|
#if VFX_LOCAL_SPACE // Need to remove potential scale in local transform
|
|
axisX = normalize(axisX);
|
|
axisY = normalize(axisY);
|
|
axisZ = normalize(axisZ);
|
|
#endif
|
|
";
|
|
|
|
case Mode.FaceCameraPosition:
|
|
if (canTestStrips && hasStrips)
|
|
{
|
|
return @"
|
|
axisX = stripTangent;
|
|
if (IsPerspectiveProjection())
|
|
{
|
|
axisZ = position - GetViewVFXPosition();
|
|
}
|
|
else // Face plane for ortho
|
|
{
|
|
axisZ = -GetVFXToViewRotMatrix()[2].xyz;
|
|
#if VFX_LOCAL_SPACE // Need to remove potential scale in local transform
|
|
axisZ = normalize(axisZ);
|
|
#endif
|
|
}
|
|
axisY = VFXSafeNormalizedCross(axisZ, axisX, float3(0,1,0));
|
|
axisZ = cross(axisX, axisY);
|
|
";
|
|
}
|
|
else
|
|
{
|
|
string getPerspectiveAxesCode = faceRay
|
|
? "GetCameraPositionOrRayFacingAxes(viewRot, position, worldUp, axisX, axisY, axisZ);"
|
|
: "GetCameraPositionFacingAxes(viewRot, position, axisX, axisY, axisZ);";
|
|
string getOrthographicAxesCode = faceRay
|
|
? "GetCameraPlaneOrRayFacingAxes(viewRot, position, worldUp, axisX, axisY, axisZ);"
|
|
: "GetCameraPlaneFacingAxes(viewRot, axisX, axisY, axisZ);";
|
|
|
|
return $@"
|
|
float3x3 viewRot = GetVFXToViewRotMatrix();
|
|
float3 worldUp = VFXGetObjectToWorldMatrix()._m10_m11_m12;
|
|
|
|
if (IsPerspectiveProjection())
|
|
{getPerspectiveAxesCode}
|
|
else // Face plane for ortho
|
|
{getOrthographicAxesCode}
|
|
#if VFX_LOCAL_SPACE // Need to remove potential scale in local transform
|
|
axisX = normalize(axisX);
|
|
axisY = normalize(axisY);
|
|
axisZ = normalize(axisZ);
|
|
#endif
|
|
";
|
|
}
|
|
|
|
case Mode.LookAtPosition:
|
|
if (canTestStrips && hasStrips)
|
|
return @"
|
|
axisX = stripTangent;
|
|
axisZ = -normalize(position - Position);
|
|
axisY = VFXSafeNormalizedCross(axisZ, axisX, float3(0,1,0));
|
|
axisZ = cross(axisX, axisY);
|
|
";
|
|
else
|
|
return @"
|
|
axisZ = normalize(position - Position);
|
|
axisX = VFXSafeNormalizedCross(GetVFXToViewRotMatrix()[1].xyz,axisZ, float3(1,0,0));
|
|
axisY = cross(axisZ,axisX);
|
|
";
|
|
|
|
case Mode.LookAtLine:
|
|
if (canTestStrips && hasStrips)
|
|
return @"
|
|
float3 lineDir = normalize(Line_end - Line_start);
|
|
float3 target = dot(position - Line_start,lineDir) * lineDir + Line_start;
|
|
axisX = stripTangent;
|
|
axisZ = normalize(position - target);
|
|
axisY = VFXSafeNormalizedCross(axisZ, axisX, float3(0,1,0));
|
|
axisZ = cross(axisX, axisY);
|
|
";
|
|
else
|
|
return @"
|
|
float3 lineDir = normalize(Line_end - Line_start);
|
|
float3 target = dot(position - Line_start,lineDir) * lineDir + Line_start;
|
|
axisZ = normalize(position - target);
|
|
axisX = VFXSafeNormalizedCross(GetVFXToViewRotMatrix()[1].xyz,axisZ, float3(1,0,0));
|
|
axisY = cross(axisZ,axisX);
|
|
";
|
|
|
|
case Mode.Advanced:
|
|
{
|
|
string rotAxis1, rotAxis2, rotAxis3, uiAxis1, uiAxis2;
|
|
AxesPairToHLSL(axes, out rotAxis1, out rotAxis2, out rotAxis3);
|
|
AxesPairToUI(axes, out uiAxis1, out uiAxis2);
|
|
string code = string.Format(@"
|
|
{0} = normalize({3});
|
|
{2} = normalize({4});
|
|
{1} = {5};
|
|
", rotAxis1, rotAxis2, rotAxis3,
|
|
uiAxis1, LeftHandedBasis(axes, uiAxis1, uiAxis2), LeftHandedBasis(GetSecondAxesPair(axes), rotAxis1, rotAxis3));
|
|
return code;
|
|
}
|
|
|
|
case Mode.FixedAxis:
|
|
if (canTestStrips && hasStrips)
|
|
throw new NotImplementedException("This orient (FixedAxis) mode is not available for strips");
|
|
|
|
return @"
|
|
axisY = Up;
|
|
axisZ = position - GetViewVFXPosition();
|
|
axisX = VFXSafeNormalizedCross(axisY, axisZ, float3(1,0,0));
|
|
axisZ = cross(axisX,axisY);
|
|
";
|
|
|
|
case Mode.AlongVelocity:
|
|
if (canTestStrips && hasStrips)
|
|
throw new NotImplementedException("This orient mode (AlongVelocity) is not available for strips");
|
|
|
|
return @"
|
|
axisY = normalize(velocity);
|
|
axisZ = position - GetViewVFXPosition();
|
|
axisX = VFXSafeNormalizedCross(axisY, axisZ, float3(1,0,0));
|
|
axisZ = cross(axisX,axisY);
|
|
";
|
|
|
|
case Mode.CustomZ:
|
|
if (canTestStrips && !hasStrips)
|
|
throw new NotImplementedException("This orient mode (CustomZ) is only available for strips");
|
|
|
|
return
|
|
@"axisX = stripTangent;
|
|
axisZ = -Front;
|
|
axisY = VFXSafeNormalizedCross(axisZ, axisX, float3(0,1,0));
|
|
axisZ = cross(axisX, axisY);
|
|
";
|
|
|
|
case Mode.CustomY:
|
|
if (canTestStrips && !hasStrips)
|
|
throw new NotImplementedException("This orient mode (CustomY) is only available for strips");
|
|
|
|
return
|
|
@"axisX = stripTangent;
|
|
axisY = Up;
|
|
axisZ = VFXSafeNormalizedCross(axisX, axisY, float3(0,0,1));
|
|
axisY = cross(axisZ, axisX);
|
|
";
|
|
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void Sanitize(int version)
|
|
{
|
|
if (mode == Mode.LookAtPosition)
|
|
{
|
|
/* Slot of type position has changed from undefined VFXSlot to VFXSlotPosition*/
|
|
if (GetNbInputSlots() > 0 && !(GetInputSlot(0) is VFXSlotPosition))
|
|
{
|
|
VFXSlot oldSlot = GetInputSlot(0);
|
|
var oldValue = oldSlot.value;
|
|
VFXSlot newSlot = VFXSlot.Create(new VFXProperty(typeof(Position), "Position"), VFXSlot.Direction.kInput, oldValue);
|
|
ReplaceSlot(oldSlot, newSlot);
|
|
}
|
|
}
|
|
base.Sanitize(version);
|
|
}
|
|
|
|
internal sealed override void GenerateErrors(VFXErrorReporter report)
|
|
{
|
|
base.GenerateErrors(report);
|
|
|
|
if (!canTestStrips)
|
|
return;
|
|
|
|
bool hasInvalidMode = false;
|
|
if (hasStrips)
|
|
hasInvalidMode =
|
|
mode == Mode.FaceCameraPlane ||
|
|
mode == Mode.FixedAxis ||
|
|
mode == Mode.AlongVelocity;
|
|
else
|
|
hasInvalidMode =
|
|
mode == Mode.CustomZ ||
|
|
mode == Mode.CustomY;
|
|
|
|
if (hasInvalidMode)
|
|
{
|
|
string outputTypeStr = hasStrips ? "strip" : "non strip";
|
|
report.RegisterError("InvalidOrientMode", VFXErrorType.Error, string.Format("Orient mode {0} is invalid with {1} output", mode, outputTypeStr), this);
|
|
}
|
|
|
|
if (mode is Mode.Advanced or Mode.FixedAxis)
|
|
{
|
|
var context = new VFXExpression.Context(VFXExpressionContextOption.CPUEvaluation | VFXExpressionContextOption.ConstantFolding);
|
|
var expressions = new VFXExpression[GetNbInputSlots()];
|
|
for (var i = 0; i < GetNbInputSlots(); i++)
|
|
{
|
|
var expression = GetInputSlot(i).GetExpression();
|
|
expressions[i] = expression;
|
|
context.RegisterExpression(expression);
|
|
}
|
|
|
|
context.Compile();
|
|
|
|
for (var i = 0; i < GetNbInputSlots(); i++)
|
|
{
|
|
var expression = expressions[i];
|
|
if (context.GetReduced(expression) is { } direction &&
|
|
direction.Is(VFXExpression.Flags.Constant) &&
|
|
direction.valueType == UnityEngine.VFX.VFXValueType.Float3 &&
|
|
direction.Get<Vector3>() is { sqrMagnitude: var sqrMag } &&
|
|
(float.IsNaN(sqrMag) || sqrMag <= Mathf.Epsilon))
|
|
{
|
|
report.RegisterError("InvalidAxis", VFXErrorType.Error, $"{GetInputSlot(i).property.name} vector must not be zero length", this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void AxesPairToHLSL(AxesPair axes, out string axis1, out string axis2, out string axis3)
|
|
{
|
|
const string X = "axisX";
|
|
const string Y = "axisY";
|
|
const string Z = "axisZ";
|
|
switch (axes)
|
|
{
|
|
case AxesPair.XY:
|
|
axis1 = X;
|
|
axis2 = Y;
|
|
axis3 = Z;
|
|
break;
|
|
case AxesPair.XZ:
|
|
axis1 = X;
|
|
axis2 = Z;
|
|
axis3 = Y;
|
|
break;
|
|
case AxesPair.YX:
|
|
axis1 = Y;
|
|
axis2 = X;
|
|
axis3 = Z;
|
|
break;
|
|
case AxesPair.YZ:
|
|
axis1 = Y;
|
|
axis2 = Z;
|
|
axis3 = X;
|
|
break;
|
|
case AxesPair.ZX:
|
|
axis1 = Z;
|
|
axis2 = X;
|
|
axis3 = Y;
|
|
break;
|
|
case AxesPair.ZY:
|
|
axis1 = Z;
|
|
axis2 = Y;
|
|
axis3 = X;
|
|
break;
|
|
default:
|
|
throw new InvalidEnumArgumentException("Unsupported axes pair");
|
|
}
|
|
}
|
|
|
|
private void AxesPairToUI(AxesPair pair, out string uiAxis1, out string uiAxis2)
|
|
{
|
|
string axis1, axis2, axis3;
|
|
AxesPairToHLSL(pair, out axis1, out axis2, out axis3);
|
|
uiAxis1 = "Axis" + axis1[axis1.Length - 1];
|
|
uiAxis2 = "Axis" + axis2[axis2.Length - 1];
|
|
}
|
|
|
|
private void AxesPairToVector(AxesPair pair, out Vector3 axis1, out Vector3 axis2)
|
|
{
|
|
Vector3 X = Vector3.right, Y = Vector3.up, Z = Vector3.forward;
|
|
switch (pair)
|
|
{
|
|
case AxesPair.XY:
|
|
axis1 = X;
|
|
axis2 = Y;
|
|
break;
|
|
case AxesPair.XZ:
|
|
axis1 = X;
|
|
axis2 = Z;
|
|
break;
|
|
case AxesPair.YX:
|
|
axis1 = Y;
|
|
axis2 = X;
|
|
break;
|
|
case AxesPair.YZ:
|
|
axis1 = Y;
|
|
axis2 = Z;
|
|
break;
|
|
case AxesPair.ZX:
|
|
axis1 = Z;
|
|
axis2 = X;
|
|
break;
|
|
case AxesPair.ZY:
|
|
axis1 = Z;
|
|
axis2 = Y;
|
|
break;
|
|
default:
|
|
throw new InvalidEnumArgumentException("Unsupported axes pair");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Given two axes in (X, Y, Z), compute the third one so that the resulting basis is left handed
|
|
/// </summary>
|
|
/// <param name="axis1">hlsl value of first axis in pair</param>
|
|
/// <param name="axis2">hlsl value of second axis</param>
|
|
private string LeftHandedBasis(AxesPair axes, string axis1, string axis2)
|
|
{
|
|
if (axes <= AxesPair.ZX)
|
|
return "cross(" + axis1 + ", " + axis2 + ")";
|
|
else
|
|
return "cross(" + axis2 + ", " + axis1 + ")";
|
|
}
|
|
|
|
private AxesPair GetSecondAxesPair(AxesPair axes)
|
|
{
|
|
switch (axes)
|
|
{
|
|
case AxesPair.XY:
|
|
return AxesPair.XZ;
|
|
case AxesPair.YZ:
|
|
return AxesPair.YX;
|
|
case AxesPair.ZX:
|
|
return AxesPair.ZY;
|
|
case AxesPair.YX:
|
|
return AxesPair.YZ;
|
|
case AxesPair.ZY:
|
|
return AxesPair.ZX;
|
|
case AxesPair.XZ:
|
|
return AxesPair.XY;
|
|
default:
|
|
throw new InvalidEnumArgumentException("Unsupported axes pair");
|
|
}
|
|
}
|
|
}
|
|
}
|