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.
909 lines
46 KiB
909 lines
46 KiB
using System;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.VFX;
|
|
using UnityEngine.Rendering;
|
|
|
|
namespace UnityEditor.VFX.Operator
|
|
{
|
|
class SampleMeshProvider : VariantProvider
|
|
{
|
|
protected virtual string nameTemplate { get; } = "Sample {0}";
|
|
protected virtual Type operatorType { get; } = typeof(SampleMesh);
|
|
|
|
public override IEnumerable<Variant> GetVariants()
|
|
{
|
|
yield return new Variant(
|
|
string.Format(nameTemplate, "Mesh"),
|
|
"Sampling",
|
|
operatorType,
|
|
new[] { new KeyValuePair<string, object>("source", SampleMesh.SourceType.Mesh) });
|
|
|
|
yield return new Variant(
|
|
string.Format(nameTemplate, "Skinned Mesh"),
|
|
"Sampling",
|
|
operatorType,
|
|
new[] { new KeyValuePair<string, object>("source", SampleMesh.SourceType.SkinnedMeshRenderer) });
|
|
}
|
|
}
|
|
|
|
[VFXHelpURL("Operator-SampleMesh")]
|
|
[VFXInfo(variantProvider = typeof(SampleMeshProvider))]
|
|
class SampleMesh : VFXOperator
|
|
{
|
|
public override string name
|
|
{
|
|
get
|
|
{
|
|
if (source == SourceType.Mesh)
|
|
return "Sample Mesh";
|
|
else
|
|
return "Sample Skinned Mesh";
|
|
}
|
|
}
|
|
|
|
public enum PlacementMode
|
|
{
|
|
Vertex,
|
|
Edge,
|
|
Surface
|
|
};
|
|
|
|
public class InputPropertiesMesh
|
|
{
|
|
[Tooltip("Specifies the Mesh to sample from.")]
|
|
public Mesh mesh = VFXResources.defaultResources.mesh;
|
|
}
|
|
|
|
public class InputPropertiesSkinnedMeshRenderer
|
|
{
|
|
[Tooltip("Specifies the Skinned Mesh Renderer component to sample from. The Skinned Mesh Renderer has to be an exposed entry.")]
|
|
public SkinnedMeshRenderer skinnedMesh = null;
|
|
}
|
|
|
|
public class InputPropertiesPlacementVertex
|
|
{
|
|
[Tooltip("Sets the vertex index to read from.")]
|
|
public uint vertex = 0u;
|
|
}
|
|
|
|
public enum SurfaceCoordinates
|
|
{
|
|
Barycentric,
|
|
Uniform,
|
|
}
|
|
|
|
public class InputPropertiesEdge
|
|
{
|
|
[Tooltip("Sets the start index of the edge. The Block uses this index and the next index to render the line.")]
|
|
public uint index = 0u;
|
|
|
|
[Range(0, 1), Tooltip("Controls the percentage along the edge, from the start position to the end position, that the sample position is taken.")]
|
|
public float edge;
|
|
}
|
|
|
|
public class InputPropertiesPlacementSurfaceBarycentricCoordinates
|
|
{
|
|
[Tooltip("The triangle index to read from.")]
|
|
public uint triangle = 0u;
|
|
|
|
[Tooltip("Barycentric coordinate (z is computed from x & y).")]
|
|
public Vector2 barycentric;
|
|
}
|
|
|
|
public class InputPropertiesPlacementSurfaceLowDistorsionMapping
|
|
{
|
|
[Tooltip("The triangle index to read from.")]
|
|
public uint triangle = 0u;
|
|
|
|
[Tooltip("The uniform coordinate to sample the triangle at. The Block takes this value and maps it from a square coordinate to triangle space.")]
|
|
public Vector2 square;
|
|
}
|
|
|
|
public class TransformProperties
|
|
{
|
|
[Tooltip("Sets the transform of the sampled Mesh. If used with a Skinned Mesh, it is applied after the transform of the root bone.")]
|
|
public Transform transform = Transform.defaultValue;
|
|
}
|
|
|
|
[Flags]
|
|
public enum VertexAttributeFlag
|
|
{
|
|
None = 0,
|
|
Position = 1 << 0,
|
|
Normal = 1 << 1,
|
|
Tangent = 1 << 2,
|
|
Bitangent = 1 << 3,
|
|
BitangentSign = 1 << 4,
|
|
Color = 1 << 5,
|
|
TexCoord0 = 1 << 6,
|
|
TexCoord1 = 1 << 7,
|
|
TexCoord2 = 1 << 8,
|
|
TexCoord3 = 1 << 9,
|
|
TexCoord4 = 1 << 10,
|
|
TexCoord5 = 1 << 11,
|
|
TexCoord6 = 1 << 12,
|
|
TexCoord7 = 1 << 13,
|
|
BlendWeight = 1 << 14,
|
|
BlendIndices = 1 << 15,
|
|
|
|
Transform = 1 << 16,
|
|
|
|
PreviousPosition = 1 << 17,
|
|
PreviousNormal = 1 << 18,
|
|
PreviousTangent = 1 << 19,
|
|
PreviousBitangent = 1 << 20,
|
|
Velocity = 1 << 21,
|
|
|
|
PreviousTransform = 1 << 22
|
|
}
|
|
|
|
public enum SourceType
|
|
{
|
|
Mesh,
|
|
SkinnedMeshRenderer
|
|
}
|
|
|
|
[VFXSetting(VFXSettingAttribute.VisibleFlags.InInspector), SerializeField, Tooltip("Outputs the result of the Mesh sampling operation.")]
|
|
private VertexAttributeFlag output = VertexAttributeFlag.Position | VertexAttributeFlag.Color | VertexAttributeFlag.TexCoord0;
|
|
|
|
[VFXSetting, SerializeField, Tooltip("Specifies how Unity handles the sample when the custom vertex index is out the out of bounds of the vertex array.")]
|
|
private VFXOperatorUtility.SequentialAddressingMode mode = VFXOperatorUtility.SequentialAddressingMode.Clamp;
|
|
|
|
[VFXSetting, SerializeField, Tooltip("Specifies which primitive part of the mesh to sample from.")]
|
|
private PlacementMode placementMode = PlacementMode.Vertex;
|
|
|
|
[VFXSetting, SerializeField, Tooltip("Specifies how to sample the surface of a triangle.")]
|
|
private SurfaceCoordinates surfaceCoordinates = SurfaceCoordinates.Uniform;
|
|
|
|
[VFXSetting(VFXSettingAttribute.VisibleFlags.InInspector), SerializeField, Tooltip("Specifies the kind of geometry to sample from.")]
|
|
private SourceType source = SourceType.Mesh;
|
|
|
|
public enum SkinnedRootTransform
|
|
{
|
|
None,
|
|
ApplyLocalRootTransform,
|
|
ApplyWorldRootTransform
|
|
}
|
|
|
|
[VFXSetting, SerializeField, Tooltip("Specifies the transform to apply to the root bone retrieved from the Skinned Mesh Renderer.")]
|
|
public SkinnedRootTransform skinnedTransform = SkinnedRootTransform.ApplyLocalRootTransform;
|
|
|
|
public override void Sanitize(int version)
|
|
{
|
|
if (version < 10)
|
|
{
|
|
SanitizeHelper.MigrateSampleMeshFrom9To10(this);
|
|
}
|
|
else
|
|
{
|
|
base.Sanitize(version);
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<VertexAttributeFlag> GetAttributesFromFlags(VertexAttributeFlag flags)
|
|
{
|
|
var vertexAttributes = Enum.GetValues(typeof(VertexAttributeFlag)).Cast<VertexAttributeFlag>();
|
|
foreach (var vertexAttribute in vertexAttributes)
|
|
if (vertexAttribute != VertexAttributeFlag.None && flags.HasFlag(vertexAttribute))
|
|
yield return vertexAttribute;
|
|
}
|
|
|
|
private IEnumerable<VertexAttributeFlag> GetOutputVertexAttributes()
|
|
{
|
|
return GetAttributesFromFlags(output);
|
|
}
|
|
|
|
public static readonly string kMixingSMRWorldAndLocalPostTransformMsg = @"Mixing World Root Bone transform with an input transform in Local space can yield unexpected results.
|
|
To avoid this, change the input Transform space from Local to World or None.";
|
|
|
|
internal override void GenerateErrors(VFXErrorReporter report)
|
|
{
|
|
base.GenerateErrors(report);
|
|
|
|
var transformSlot = inputSlots.Last();
|
|
if (actualSkinnedTransform == SkinnedRootTransform.ApplyWorldRootTransform && transformSlot.space == VFXSpace.Local)
|
|
{
|
|
report.RegisterError("MixingSMRWorldAndLocalPostTransformOperator", VFXErrorType.Warning, kMixingSMRWorldAndLocalPostTransformMsg, this);
|
|
}
|
|
|
|
var previousFlag = VertexAttributeFlag.PreviousNormal
|
|
| VertexAttributeFlag.PreviousTangent
|
|
| VertexAttributeFlag.PreviousBitangent
|
|
| VertexAttributeFlag.PreviousPosition
|
|
| VertexAttributeFlag.Velocity
|
|
| VertexAttributeFlag.PreviousTransform;
|
|
|
|
if (source == SourceType.Mesh && (output & previousFlag) != 0)
|
|
{
|
|
report.RegisterError("PreviousOutputUsageOnMesh", VFXErrorType.Warning, "Sampling previous data is only available with SkinnedMeshRenderer sources.\nWhen using a Mesh source, previous outputs return the same values as current ones.", this);
|
|
}
|
|
}
|
|
|
|
protected internal override void Invalidate(VFXModel model, InvalidationCause cause)
|
|
{
|
|
base.Invalidate(model, cause);
|
|
|
|
//Called from VFXSlot.InvalidateExpressionTree, can be triggered from a space change, need to refresh block warning
|
|
if (cause == InvalidationCause.kExpressionInvalidated)
|
|
{
|
|
model.RefreshErrors();
|
|
}
|
|
}
|
|
|
|
private VFXSpace GetWantedOutputSpace()
|
|
{
|
|
//If we are applying the skinned mesh in world, using a local transform afterwards looks unexpected, forcing conversion of inputs to world.
|
|
if (actualSkinnedTransform == SkinnedRootTransform.ApplyWorldRootTransform)
|
|
return VFXSpace.World;
|
|
|
|
//Otherwise, the input slot transform control the output space.
|
|
var transformSlot = inputSlots.Last();
|
|
if (!transformSlot.spaceable)
|
|
throw new InvalidOperationException("Unexpected mesh slot: " + transformSlot);
|
|
return transformSlot.space;
|
|
}
|
|
|
|
public override VFXSpace GetOutputSpaceFromSlot(VFXSlot outputSlot)
|
|
{
|
|
if (outputSlot.spaceable)
|
|
{
|
|
return GetWantedOutputSpace();
|
|
}
|
|
return VFXSpace.None;
|
|
}
|
|
|
|
private static string GetTooltip(VertexAttributeFlag attribute)
|
|
{
|
|
switch (attribute)
|
|
{
|
|
case VertexAttributeFlag.Position: return "Returns the position of the sampled vertex, edge, or surface coordinate.";
|
|
case VertexAttributeFlag.Normal: return "Returns the normalized Normal vector of the sampled vertex, edge, or surface coordinate.";
|
|
case VertexAttributeFlag.Tangent: return "Returns the non-normalized Tangent vector of the sampled vertex, edge, or surface coordinate.";
|
|
case VertexAttributeFlag.Bitangent: return "Returns the Bitangent vector, which is a cross product of the Normal and Tangent vector multiplied by the Tangent sign.";
|
|
case VertexAttributeFlag.Color: return "Returns the Vertex color. Sampling both Color and Color32 structures are supported.";
|
|
case VertexAttributeFlag.TexCoord0:
|
|
case VertexAttributeFlag.TexCoord1:
|
|
case VertexAttributeFlag.TexCoord2:
|
|
case VertexAttributeFlag.TexCoord3:
|
|
case VertexAttributeFlag.TexCoord4:
|
|
case VertexAttributeFlag.TexCoord5:
|
|
case VertexAttributeFlag.TexCoord6:
|
|
case VertexAttributeFlag.TexCoord7: return "Returns the vertex data stored in this TexCoord. TexCoord0 typically stores the UV data of the sampled mesh.";
|
|
case VertexAttributeFlag.BlendWeight: return "Returns the bone blend weights for the mesh. This maps to the BLENDWEIGHTS semantic in the HLSL shading language.";
|
|
case VertexAttributeFlag.BlendIndices: return "Returns the bone blend indices. This maps to the BLENDINDICES semantic in the HLSL shading language.";
|
|
case VertexAttributeFlag.Transform: return "Returns the computed Transform after normalization and correction from the Normal, Tangent, Bitangent, and Position inputs.";
|
|
case VertexAttributeFlag.PreviousPosition: return "Returns Previous Position considering previous root bone transform.";
|
|
case VertexAttributeFlag.PreviousNormal: return "Returns Previous Normal considering previous root bone transform.";
|
|
case VertexAttributeFlag.PreviousBitangent: return "Returns Previous Bitangent computed with cross(previousNormal, previousTangent.xyz) * previousTangent.w";
|
|
case VertexAttributeFlag.Velocity: return "Returns Velocity vector, returns the difference between current and previous position divided by game time.";
|
|
case VertexAttributeFlag.PreviousTransform: return "Returns the computed Transform after normalization and correction from the Previous Normal, Tangent, Bitangent, and Previous Position inputs.";
|
|
}
|
|
return string.Empty;
|
|
}
|
|
|
|
private static Type GetOutputType(VertexAttributeFlag attribute)
|
|
{
|
|
switch (attribute)
|
|
{
|
|
case VertexAttributeFlag.PreviousPosition:
|
|
case VertexAttributeFlag.Position: return typeof(Position);
|
|
case VertexAttributeFlag.PreviousNormal:
|
|
case VertexAttributeFlag.Normal: return typeof(DirectionType);
|
|
case VertexAttributeFlag.PreviousTangent:
|
|
case VertexAttributeFlag.PreviousBitangent:
|
|
case VertexAttributeFlag.Tangent:
|
|
case VertexAttributeFlag.Bitangent:
|
|
case VertexAttributeFlag.Velocity: return typeof(Vector);
|
|
case VertexAttributeFlag.Color: return typeof(Vector4);
|
|
case VertexAttributeFlag.TexCoord0:
|
|
case VertexAttributeFlag.TexCoord1:
|
|
case VertexAttributeFlag.TexCoord2:
|
|
case VertexAttributeFlag.TexCoord3:
|
|
case VertexAttributeFlag.TexCoord4:
|
|
case VertexAttributeFlag.TexCoord5:
|
|
case VertexAttributeFlag.TexCoord6:
|
|
case VertexAttributeFlag.TexCoord7: return typeof(Vector4);
|
|
case VertexAttributeFlag.BlendWeight: return typeof(Vector4);
|
|
case VertexAttributeFlag.BlendIndices: return typeof(Vector4);
|
|
case VertexAttributeFlag.BitangentSign: return typeof(float);
|
|
case VertexAttributeFlag.Transform:
|
|
case VertexAttributeFlag.PreviousTransform: return typeof(Transform);
|
|
default: throw new InvalidOperationException("Unexpected attribute : " + attribute);
|
|
}
|
|
}
|
|
|
|
private static VFXValueType GetSampledType(VertexAttribute attribute)
|
|
{
|
|
switch (attribute)
|
|
{
|
|
case VertexAttribute.Position: return VFXValueType.Float3;
|
|
case VertexAttribute.Normal: return VFXValueType.Float3;
|
|
case VertexAttribute.Tangent: return VFXValueType.Float4;
|
|
case VertexAttribute.Color: return VFXValueType.Float4;
|
|
case VertexAttribute.TexCoord0:
|
|
case VertexAttribute.TexCoord1:
|
|
case VertexAttribute.TexCoord2:
|
|
case VertexAttribute.TexCoord3:
|
|
case VertexAttribute.TexCoord4:
|
|
case VertexAttribute.TexCoord5:
|
|
case VertexAttribute.TexCoord6:
|
|
case VertexAttribute.TexCoord7: return VFXValueType.Float4;
|
|
case VertexAttribute.BlendWeight:
|
|
case VertexAttribute.BlendIndices: return VFXValueType.Float4;
|
|
default: throw new InvalidOperationException("Unexpected attribute: " + attribute);
|
|
}
|
|
}
|
|
|
|
private static VertexAttribute GetActualVertexAttribute(VertexAttributeFlag attribute)
|
|
{
|
|
switch (attribute)
|
|
{
|
|
case VertexAttributeFlag.Position: return VertexAttribute.Position;
|
|
case VertexAttributeFlag.Normal: return VertexAttribute.Normal;
|
|
case VertexAttributeFlag.BitangentSign:
|
|
case VertexAttributeFlag.Tangent: return VertexAttribute.Tangent;
|
|
case VertexAttributeFlag.Color: return VertexAttribute.Color;
|
|
case VertexAttributeFlag.TexCoord0: return VertexAttribute.TexCoord0;
|
|
case VertexAttributeFlag.TexCoord1: return VertexAttribute.TexCoord1;
|
|
case VertexAttributeFlag.TexCoord2: return VertexAttribute.TexCoord2;
|
|
case VertexAttributeFlag.TexCoord3: return VertexAttribute.TexCoord3;
|
|
case VertexAttributeFlag.TexCoord4: return VertexAttribute.TexCoord4;
|
|
case VertexAttributeFlag.TexCoord5: return VertexAttribute.TexCoord5;
|
|
case VertexAttributeFlag.TexCoord6: return VertexAttribute.TexCoord6;
|
|
case VertexAttributeFlag.TexCoord7: return VertexAttribute.TexCoord7;
|
|
case VertexAttributeFlag.BlendWeight: return VertexAttribute.BlendWeight;
|
|
case VertexAttributeFlag.BlendIndices: return VertexAttribute.BlendIndices;
|
|
default: throw new InvalidOperationException("Unexpected attribute : " + attribute);
|
|
}
|
|
}
|
|
|
|
protected override IEnumerable<string> filteredOutSettings
|
|
{
|
|
get
|
|
{
|
|
foreach (var settings in base.filteredOutSettings)
|
|
yield return settings;
|
|
|
|
if (placementMode != PlacementMode.Surface)
|
|
yield return nameof(surfaceCoordinates);
|
|
|
|
if (source != SourceType.SkinnedMeshRenderer)
|
|
yield return nameof(skinnedTransform);
|
|
}
|
|
}
|
|
|
|
protected sealed override IEnumerable<VFXPropertyWithValue> inputProperties
|
|
{
|
|
get
|
|
{
|
|
var props = base.inputProperties;
|
|
if (source == SourceType.Mesh)
|
|
props = props.Concat(PropertiesFromType(nameof(InputPropertiesMesh)));
|
|
else if (source == SourceType.SkinnedMeshRenderer)
|
|
props = props.Concat(PropertiesFromType(nameof(InputPropertiesSkinnedMeshRenderer)));
|
|
else
|
|
throw new InvalidOperationException("Unexpected source type : " + source);
|
|
|
|
if (placementMode == PlacementMode.Vertex)
|
|
props = props.Concat(PropertiesFromType(nameof(InputPropertiesPlacementVertex)));
|
|
else if (placementMode == PlacementMode.Surface)
|
|
{
|
|
if (surfaceCoordinates == SurfaceCoordinates.Barycentric)
|
|
props = props.Concat(PropertiesFromType(nameof(InputPropertiesPlacementSurfaceBarycentricCoordinates)));
|
|
else if (surfaceCoordinates == SurfaceCoordinates.Uniform)
|
|
props = props.Concat(PropertiesFromType(nameof(InputPropertiesPlacementSurfaceLowDistorsionMapping)));
|
|
else
|
|
throw new InvalidOperationException("Unexpected surfaceCoordinates : " + surfaceCoordinates);
|
|
}
|
|
else if (placementMode == PlacementMode.Edge)
|
|
props = props.Concat(PropertiesFromType(nameof(InputPropertiesEdge)));
|
|
else
|
|
throw new InvalidOperationException("Unexpected placementMode : " + placementMode);
|
|
|
|
props = props.Concat(PropertiesFromType(nameof(TransformProperties)));
|
|
return props;
|
|
}
|
|
}
|
|
|
|
private SkinnedRootTransform actualSkinnedTransform
|
|
{
|
|
get
|
|
{
|
|
if (source == SourceType.SkinnedMeshRenderer)
|
|
return skinnedTransform;
|
|
return SkinnedRootTransform.None;
|
|
}
|
|
}
|
|
|
|
protected override IEnumerable<VFXPropertyWithValue> outputProperties
|
|
{
|
|
get
|
|
{
|
|
foreach (var vertexAttribute in GetOutputVertexAttributes())
|
|
{
|
|
var outputType = GetOutputType(vertexAttribute);
|
|
var tooltip = GetTooltip(vertexAttribute);
|
|
yield return new VFXPropertyWithValue(
|
|
new VFXProperty(outputType,
|
|
ObjectNames.NicifyVariableName(vertexAttribute.ToString()),
|
|
new TooltipAttribute(tooltip)));
|
|
}
|
|
}
|
|
}
|
|
|
|
private static VFXExpression SampleVertexAttribute(VFXExpression source, VFXExpression vertexIndex, VertexAttribute vertexAttribute, VFXSkinnedMeshFrame frame = VFXSkinnedMeshFrame.Current)
|
|
{
|
|
bool skinnedMesh = source.valueType == VFXValueType.SkinnedMeshRenderer;
|
|
var mesh = !skinnedMesh ? source : new VFXExpressionMeshFromSkinnedMeshRenderer(source);
|
|
|
|
var channelIndex = VFXValue.Constant<uint>((uint)vertexAttribute);
|
|
var meshVertexStride = new VFXExpressionMeshVertexStride(mesh, channelIndex);
|
|
var meshChannelOffset = new VFXExpressionMeshChannelOffset(mesh, channelIndex);
|
|
|
|
var outputType = GetSampledType(vertexAttribute);
|
|
VFXExpression sampled = null;
|
|
|
|
var meshChannelFormatAndDimension = new VFXExpressionMeshChannelInfos(mesh, channelIndex);
|
|
var vertexOffset = vertexIndex * meshVertexStride + meshChannelOffset;
|
|
|
|
if (!skinnedMesh)
|
|
{
|
|
if (vertexAttribute == VertexAttribute.Color)
|
|
sampled = new VFXExpressionSampleMeshColor(source, vertexOffset, meshChannelFormatAndDimension);
|
|
else if (outputType == VFXValueType.Float)
|
|
sampled = new VFXExpressionSampleMeshFloat(source, vertexOffset, meshChannelFormatAndDimension);
|
|
else if (outputType == VFXValueType.Float2)
|
|
sampled = new VFXExpressionSampleMeshFloat2(source, vertexOffset, meshChannelFormatAndDimension);
|
|
else if (outputType == VFXValueType.Float3)
|
|
sampled = new VFXExpressionSampleMeshFloat3(source, vertexOffset, meshChannelFormatAndDimension);
|
|
else if (outputType == VFXValueType.Float4)
|
|
sampled = new VFXExpressionSampleMeshFloat4(source, vertexOffset, meshChannelFormatAndDimension);
|
|
}
|
|
else
|
|
{
|
|
if (frame == VFXSkinnedMeshFrame.Previous && outputType != VFXValueType.Float3 && outputType != VFXValueType.Float4)
|
|
throw new InvalidOperationException("Unexpected type to sample previous frame : " + outputType);
|
|
|
|
if (vertexAttribute == VertexAttribute.Color)
|
|
sampled = new VFXExpressionSampleSkinnedMeshRendererColor(source, vertexOffset, meshChannelFormatAndDimension);
|
|
else if (outputType == VFXValueType.Float)
|
|
sampled = new VFXExpressionSampleSkinnedMeshRendererFloat(source, vertexOffset, meshChannelFormatAndDimension);
|
|
else if (outputType == VFXValueType.Float2)
|
|
sampled = new VFXExpressionSampleSkinnedMeshRendererFloat2(source, vertexOffset, meshChannelFormatAndDimension);
|
|
else if (outputType == VFXValueType.Float3)
|
|
sampled = new VFXExpressionSampleSkinnedMeshRendererFloat3(source, vertexOffset, meshChannelFormatAndDimension, frame);
|
|
else if (outputType == VFXValueType.Float4)
|
|
sampled = new VFXExpressionSampleSkinnedMeshRendererFloat4(source, vertexOffset, meshChannelFormatAndDimension, frame);
|
|
}
|
|
|
|
if (sampled == null)
|
|
throw new InvalidOperationException("Unexpected Mesh Sampling type.");
|
|
|
|
return sampled;
|
|
}
|
|
|
|
private static SpaceableType GetSpaceableFromVertexAttribute(VertexAttributeFlag currentAttribute)
|
|
{
|
|
if ((currentAttribute & (currentAttribute - 1)) != 0)
|
|
throw new InvalidOperationException("Unexpected not single bit current attribute: " + currentAttribute);
|
|
|
|
switch (currentAttribute)
|
|
{
|
|
case VertexAttributeFlag.Position:
|
|
case VertexAttributeFlag.PreviousPosition:
|
|
return SpaceableType.Position;
|
|
case VertexAttributeFlag.Normal:
|
|
case VertexAttributeFlag.Tangent:
|
|
case VertexAttributeFlag.Bitangent:
|
|
case VertexAttributeFlag.PreviousNormal:
|
|
case VertexAttributeFlag.PreviousTangent:
|
|
case VertexAttributeFlag.PreviousBitangent:
|
|
return SpaceableType.Vector;
|
|
case VertexAttributeFlag.PreviousTransform:
|
|
case VertexAttributeFlag.Transform:
|
|
return SpaceableType.Matrix;
|
|
}
|
|
|
|
return SpaceableType.None;
|
|
}
|
|
|
|
private static bool ShouldUsePreviousMatrix(VertexAttributeFlag flag)
|
|
{
|
|
switch (flag)
|
|
{
|
|
case VertexAttributeFlag.PreviousNormal:
|
|
case VertexAttributeFlag.PreviousTangent:
|
|
case VertexAttributeFlag.PreviousBitangent:
|
|
case VertexAttributeFlag.PreviousPosition:
|
|
case VertexAttributeFlag.PreviousTransform:
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public struct VFXMeshTransform
|
|
{
|
|
public VFXExpression current;
|
|
public VFXExpression previous;
|
|
}
|
|
|
|
private static VFXExpression ComputeVertexAttribute(IEnumerable<VFXExpression> sampledVertexAttribute, VertexAttributeFlag currentAttribute, VFXMeshTransform postTransform)
|
|
{
|
|
if (postTransform.current == null || postTransform.previous == null)
|
|
throw new InvalidOperationException("Unexpected null transform");
|
|
|
|
if ((currentAttribute & (currentAttribute - 1)) != 0)
|
|
throw new InvalidOperationException("Unexpected not single bit current attribute: " + currentAttribute);
|
|
|
|
//Compute expected attribute
|
|
VFXExpression sampled;
|
|
if (currentAttribute == VertexAttributeFlag.Tangent || currentAttribute == VertexAttributeFlag.PreviousTangent)
|
|
{
|
|
sampled = sampledVertexAttribute.First().xyz;
|
|
}
|
|
else if (currentAttribute == VertexAttributeFlag.BitangentSign)
|
|
{
|
|
sampled = sampledVertexAttribute.First().w;
|
|
}
|
|
else if (currentAttribute == VertexAttributeFlag.Bitangent || currentAttribute == VertexAttributeFlag.PreviousBitangent)
|
|
{
|
|
var sampledNormal = sampledVertexAttribute.First();
|
|
var sampledTangent = sampledVertexAttribute.Last();
|
|
if (sampledNormal == sampledTangent)
|
|
throw new InvalidOperationException("Unexpected tangent/normal equality");
|
|
sampled = VFXOperatorUtility.Cross(sampledNormal, sampledTangent.xyz) * sampledTangent.www;
|
|
}
|
|
else if (currentAttribute == VertexAttributeFlag.Normal || currentAttribute == VertexAttributeFlag.PreviousNormal)
|
|
{
|
|
//N.B.: Only normal is actually normalized
|
|
var sampledNormal = sampledVertexAttribute.First();
|
|
sampled = VFXOperatorUtility.Normalize(sampledNormal);
|
|
}
|
|
else if (currentAttribute == VertexAttributeFlag.Transform || currentAttribute == VertexAttributeFlag.PreviousTransform)
|
|
{
|
|
var position = sampledVertexAttribute.ElementAt(0);
|
|
var normal = sampledVertexAttribute.ElementAt(1);
|
|
var tangent = sampledVertexAttribute.ElementAt(2);
|
|
|
|
//insure normal/tangent aren't zero
|
|
var sqrLengthNormal = VFXOperatorUtility.Dot(normal, normal);
|
|
var sqrLengthTangent = VFXOperatorUtility.Dot(tangent, tangent);
|
|
|
|
var srqEpsilon = VFXOperatorUtility.EpsilonSqrExpression[VFXValueType.Float];
|
|
var nullNormal = new VFXExpressionCondition(VFXValueType.Float, VFXCondition.Less, sqrLengthNormal, srqEpsilon);
|
|
var nullTangent = new VFXExpressionCondition(VFXValueType.Float, VFXCondition.Less, sqrLengthTangent, srqEpsilon);
|
|
|
|
normal = new VFXExpressionBranch(nullNormal, VFXValue.Constant(Vector3.up), normal);
|
|
tangent = new VFXExpressionBranch(nullTangent, VFXValue.Constant(Vector3.forward), tangent.xyz);
|
|
|
|
//compute initial basis
|
|
normal = VFXOperatorUtility.Normalize(normal);
|
|
var bitangent = VFXOperatorUtility.Cross(normal, tangent);
|
|
bitangent = VFXOperatorUtility.Normalize(bitangent);
|
|
|
|
//insure tangent orthonormal with normal (cross of normalized input, not need to renormalize)
|
|
tangent = VFXOperatorUtility.Cross(bitangent, normal);
|
|
sampled = new VFXExpressionAxisToMatrix(bitangent, normal, tangent, position);
|
|
}
|
|
else if (currentAttribute == VertexAttributeFlag.Velocity)
|
|
{
|
|
var currentPosition = sampledVertexAttribute.ElementAt(0);
|
|
var previousPosition = sampledVertexAttribute.ElementAt(1);
|
|
|
|
currentPosition = TransformExpression(currentPosition, SpaceableType.Position, postTransform.current);
|
|
previousPosition = TransformExpression(previousPosition, SpaceableType.Position, postTransform.previous);
|
|
|
|
//Warning: This is not VFX deltaTime here, the skin mesh renderer isn't using the vfx time step.
|
|
var deltaTime = VFXBuiltInExpression.GameDeltaTime;
|
|
deltaTime = new VFXExpressionMax(deltaTime, VFXOperatorUtility.EpsilonExpression[VFXValueType.Float]);
|
|
deltaTime = VFXOperatorUtility.CastFloat(deltaTime, VFXValueType.Float3);
|
|
sampled = (currentPosition - previousPosition) / deltaTime;
|
|
|
|
//Cancel following transform which has already been done
|
|
postTransform.current = postTransform.previous = null;
|
|
}
|
|
else
|
|
{
|
|
//Default: 1:1 between flag & actual vertex attribute
|
|
sampled = sampledVertexAttribute.First();
|
|
}
|
|
|
|
if (postTransform.current != null && postTransform.previous != null)
|
|
{
|
|
var previous = ShouldUsePreviousMatrix(currentAttribute);
|
|
var spaceableType = GetSpaceableFromVertexAttribute(currentAttribute);
|
|
sampled = TransformExpression(sampled, spaceableType, previous ? postTransform.previous : postTransform.current);
|
|
}
|
|
|
|
return sampled;
|
|
}
|
|
|
|
private static IEnumerable<VFXExpression> SampleNeededVertexAttribute(VFXExpression source, VFXExpression vertexIndex, VertexAttributeFlag currentAttribute)
|
|
{
|
|
if ((currentAttribute & (currentAttribute - 1)) != 0)
|
|
throw new InvalidOperationException("Unexpected not single bit current attribute: " + currentAttribute);
|
|
|
|
if (currentAttribute == VertexAttributeFlag.Tangent || currentAttribute == VertexAttributeFlag.BitangentSign)
|
|
{
|
|
yield return SampleVertexAttribute(source, vertexIndex, VertexAttribute.Tangent);
|
|
}
|
|
else if (currentAttribute == VertexAttributeFlag.Bitangent)
|
|
{
|
|
yield return SampleVertexAttribute(source, vertexIndex, VertexAttribute.Normal);
|
|
yield return SampleVertexAttribute(source, vertexIndex, VertexAttribute.Tangent);
|
|
}
|
|
else if (currentAttribute == VertexAttributeFlag.PreviousBitangent)
|
|
{
|
|
yield return SampleVertexAttribute(source, vertexIndex, VertexAttribute.Normal, VFXSkinnedMeshFrame.Previous);
|
|
yield return SampleVertexAttribute(source, vertexIndex, VertexAttribute.Tangent, VFXSkinnedMeshFrame.Previous);
|
|
}
|
|
else if (currentAttribute == VertexAttributeFlag.Transform)
|
|
{
|
|
yield return SampleVertexAttribute(source, vertexIndex, VertexAttribute.Position);
|
|
yield return SampleVertexAttribute(source, vertexIndex, VertexAttribute.Normal);
|
|
yield return SampleVertexAttribute(source, vertexIndex, VertexAttribute.Tangent);
|
|
}
|
|
else if (currentAttribute == VertexAttributeFlag.PreviousTransform)
|
|
{
|
|
yield return SampleVertexAttribute(source, vertexIndex, VertexAttribute.Position, VFXSkinnedMeshFrame.Previous);
|
|
yield return SampleVertexAttribute(source, vertexIndex, VertexAttribute.Normal, VFXSkinnedMeshFrame.Previous);
|
|
yield return SampleVertexAttribute(source, vertexIndex, VertexAttribute.Tangent, VFXSkinnedMeshFrame.Previous);
|
|
}
|
|
else if (currentAttribute == VertexAttributeFlag.PreviousNormal)
|
|
{
|
|
yield return SampleVertexAttribute(source, vertexIndex, VertexAttribute.Normal, VFXSkinnedMeshFrame.Previous);
|
|
}
|
|
else if (currentAttribute == VertexAttributeFlag.PreviousTangent)
|
|
{
|
|
yield return SampleVertexAttribute(source, vertexIndex, VertexAttribute.Tangent, VFXSkinnedMeshFrame.Previous);
|
|
}
|
|
else if (currentAttribute == VertexAttributeFlag.PreviousPosition)
|
|
{
|
|
yield return SampleVertexAttribute(source, vertexIndex, VertexAttribute.Position, VFXSkinnedMeshFrame.Previous);
|
|
}
|
|
else if (currentAttribute == VertexAttributeFlag.Velocity)
|
|
{
|
|
yield return SampleVertexAttribute(source, vertexIndex, VertexAttribute.Position, VFXSkinnedMeshFrame.Current);
|
|
yield return SampleVertexAttribute(source, vertexIndex, VertexAttribute.Position, VFXSkinnedMeshFrame.Previous);
|
|
}
|
|
else
|
|
{
|
|
//Default: 1:1 between flag & actual vertex attribute
|
|
var vertexAttribute = GetActualVertexAttribute(currentAttribute);
|
|
yield return SampleVertexAttribute(source, vertexIndex, vertexAttribute);
|
|
}
|
|
}
|
|
|
|
public static IEnumerable<VFXExpression> SampleVertexAttribute(VFXExpression source, VFXExpression vertexIndex, IEnumerable<VertexAttributeFlag> vertexAttributes, VFXMeshTransform postTransform)
|
|
{
|
|
foreach (var currentAttribute in vertexAttributes)
|
|
{
|
|
var neededAttribute = SampleNeededVertexAttribute(source, vertexIndex, currentAttribute);
|
|
var computedAttribute = ComputeVertexAttribute(neededAttribute, currentAttribute, postTransform);
|
|
yield return computedAttribute;
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<VFXExpression> SampleVertexAttribute(VFXExpression source, VFXExpression vertexIndex, VFXOperatorUtility.SequentialAddressingMode mode, IEnumerable<VertexAttributeFlag> vertexAttributes, VFXMeshTransform postTransform)
|
|
{
|
|
bool skinnedMesh = source.valueType == VFXValueType.SkinnedMeshRenderer;
|
|
var mesh = !skinnedMesh ? source : new VFXExpressionMeshFromSkinnedMeshRenderer(source);
|
|
var meshVertexCount = new VFXExpressionMeshVertexCount(mesh);
|
|
vertexIndex = VFXOperatorUtility.ApplyAddressingMode(vertexIndex, meshVertexCount, mode);
|
|
return SampleVertexAttribute(source, vertexIndex, vertexAttributes, postTransform);
|
|
}
|
|
|
|
public static IEnumerable<VFXExpression> SampleEdgeAttribute(VFXExpression source, VFXExpression index, VFXExpression lerp, IEnumerable<VertexAttributeFlag> vertexAttributes, VFXMeshTransform postTransform)
|
|
{
|
|
bool skinnedMesh = source.valueType == VFXValueType.SkinnedMeshRenderer;
|
|
var mesh = !skinnedMesh ? source : new VFXExpressionMeshFromSkinnedMeshRenderer(source);
|
|
|
|
var meshIndexFormat = new VFXExpressionMeshIndexFormat(mesh);
|
|
|
|
var oneUint = VFXOperatorUtility.OneExpression[VFXValueType.Uint32];
|
|
var threeUint = VFXOperatorUtility.ThreeExpression[VFXValueType.Uint32];
|
|
|
|
var nextIndex = index + oneUint;
|
|
|
|
//Loop triangle
|
|
var loop = VFXOperatorUtility.Modulo(nextIndex, threeUint);
|
|
var predicat = new VFXExpressionCondition(VFXValueType.Uint32, VFXCondition.NotEqual, loop, VFXOperatorUtility.ZeroExpression[VFXValueType.Uint32]);
|
|
nextIndex = new VFXExpressionBranch(predicat, nextIndex, nextIndex - threeUint);
|
|
|
|
var sampledIndex_A = new VFXExpressionSampleIndex(mesh, index, meshIndexFormat);
|
|
var sampledIndex_B = new VFXExpressionSampleIndex(mesh, nextIndex, meshIndexFormat);
|
|
|
|
foreach (var attribute in vertexAttributes)
|
|
{
|
|
var neededAttribute_A = SampleNeededVertexAttribute(source, sampledIndex_A, attribute);
|
|
var neededAttribute_B = SampleNeededVertexAttribute(source, sampledIndex_B, attribute);
|
|
|
|
var interpolatedAttribute = Enumerable.Zip(neededAttribute_A, neededAttribute_B, (a, b) =>
|
|
{
|
|
var outputValueType = a.valueType;
|
|
var s = VFXOperatorUtility.CastFloat(lerp, outputValueType);
|
|
var r = VFXOperatorUtility.Lerp(a, b, s);
|
|
return r;
|
|
});
|
|
|
|
yield return ComputeVertexAttribute(interpolatedAttribute, attribute, postTransform);
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<VFXExpression> SampleEdgeAttribute(VFXExpression source, VFXExpression index, VFXExpression x, VFXOperatorUtility.SequentialAddressingMode mode, IEnumerable<VertexAttributeFlag> vertexAttributes, VFXMeshTransform postTransform)
|
|
{
|
|
bool skinnedMesh = source.valueType == VFXValueType.SkinnedMeshRenderer;
|
|
var mesh = !skinnedMesh ? source : new VFXExpressionMeshFromSkinnedMeshRenderer(source);
|
|
var meshIndexCount = new VFXExpressionMeshIndexCount(mesh);
|
|
|
|
index = VFXOperatorUtility.ApplyAddressingMode(index, meshIndexCount, mode);
|
|
return SampleEdgeAttribute(source, index, x, vertexAttributes, postTransform);
|
|
}
|
|
|
|
static IEnumerable<T> Zip3<T>(IEnumerable<T> first, IEnumerable<T> second, IEnumerable<T> third, Func<T, T, T, T> func)
|
|
{
|
|
using (var itFirst = first.GetEnumerator())
|
|
using (var itSecond = second.GetEnumerator())
|
|
using (var itThird = third.GetEnumerator())
|
|
{
|
|
while (itFirst.MoveNext() && itSecond.MoveNext() && itThird.MoveNext())
|
|
yield return func(itFirst.Current, itSecond.Current, itThird.Current);
|
|
}
|
|
}
|
|
|
|
public static IEnumerable<VFXExpression> SampleTriangleAttribute(VFXExpression source, VFXExpression triangleIndex, VFXExpression coord, SurfaceCoordinates coordMode, IEnumerable<VertexAttributeFlag> vertexAttributes, VFXMeshTransform postTranform)
|
|
{
|
|
bool skinnedMesh = source.valueType == VFXValueType.SkinnedMeshRenderer;
|
|
var mesh = !skinnedMesh ? source : new VFXExpressionMeshFromSkinnedMeshRenderer(source);
|
|
|
|
var meshIndexFormat = new VFXExpressionMeshIndexFormat(mesh);
|
|
|
|
var threeUint = VFXOperatorUtility.ThreeExpression[VFXValueType.Uint32];
|
|
var baseIndex = triangleIndex * threeUint;
|
|
|
|
var sampledIndex_A = new VFXExpressionSampleIndex(mesh, baseIndex, meshIndexFormat);
|
|
var sampledIndex_B = new VFXExpressionSampleIndex(mesh, baseIndex + VFXValue.Constant<uint>(1u), meshIndexFormat);
|
|
var sampledIndex_C = new VFXExpressionSampleIndex(mesh, baseIndex + VFXValue.Constant<uint>(2u), meshIndexFormat);
|
|
|
|
var allInputValues = new List<VFXExpression>();
|
|
VFXExpression barycentricCoordinates = null;
|
|
var one = VFXOperatorUtility.OneExpression[VFXValueType.Float];
|
|
if (coordMode == SurfaceCoordinates.Barycentric)
|
|
{
|
|
var barycentricCoordinateInput = coord;
|
|
barycentricCoordinates = new VFXExpressionCombine(barycentricCoordinateInput.x, barycentricCoordinateInput.y, one - barycentricCoordinateInput.x - barycentricCoordinateInput.y);
|
|
}
|
|
else if (coordMode == SurfaceCoordinates.Uniform)
|
|
{
|
|
//https://hal.archives-ouvertes.fr/hal-02073696v2/document
|
|
var input = coord;
|
|
|
|
var half2 = VFXOperatorUtility.HalfExpression[VFXValueType.Float2];
|
|
var zero = VFXOperatorUtility.ZeroExpression[VFXValueType.Float];
|
|
var t = input * half2;
|
|
var offset = t.y - t.x;
|
|
var pred = new VFXExpressionCondition(VFXValueType.Float, VFXCondition.Greater, offset, zero);
|
|
var t2 = new VFXExpressionBranch(pred, t.y + offset, t.y);
|
|
var t1 = new VFXExpressionBranch(pred, t.x, t.x - offset);
|
|
var t3 = one - t2 - t1;
|
|
barycentricCoordinates = new VFXExpressionCombine(t1, t2, t3);
|
|
|
|
/* Possible variant See http://inis.jinr.ru/sl/vol1/CMC/Graphics_Gems_1,ed_A.Glassner.pdf (p24) uniform distribution from two numbers in triangle generating barycentric coordinate
|
|
var input = VFXOperatorUtility.Saturate(inputExpression[2]);
|
|
var s = input.x;
|
|
var t = VFXOperatorUtility.Sqrt(input.y);
|
|
var a = one - t;
|
|
var b = (one - s) * t;
|
|
var c = s * t;
|
|
barycentricCoordinates = new VFXExpressionCombine(a, b, c);
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
throw new InvalidOperationException("No supported surfaceCoordinates : " + coord);
|
|
}
|
|
|
|
foreach (var attribute in vertexAttributes)
|
|
{
|
|
var neededAttribute_A = SampleNeededVertexAttribute(source, sampledIndex_A, attribute);
|
|
var neededAttribute_B = SampleNeededVertexAttribute(source, sampledIndex_B, attribute);
|
|
var neededAttribute_C = SampleNeededVertexAttribute(source, sampledIndex_C, attribute);
|
|
|
|
var interpolatedAttribute = Zip3(neededAttribute_A, neededAttribute_B, neededAttribute_C, (a, b, c) =>
|
|
{
|
|
var outputValueType = a.valueType;
|
|
|
|
var barycentricCoordinateX = VFXOperatorUtility.CastFloat(barycentricCoordinates.x, outputValueType);
|
|
var barycentricCoordinateY = VFXOperatorUtility.CastFloat(barycentricCoordinates.y, outputValueType);
|
|
var barycentricCoordinateZ = VFXOperatorUtility.CastFloat(barycentricCoordinates.z, outputValueType);
|
|
|
|
var r = a * barycentricCoordinateX + b * barycentricCoordinateY + c * barycentricCoordinateZ;
|
|
return r;
|
|
});
|
|
|
|
yield return ComputeVertexAttribute(interpolatedAttribute, attribute, postTranform);
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<VFXExpression> SampleTriangleAttribute(VFXExpression source, VFXExpression triangleIndex, VFXExpression coord, VFXOperatorUtility.SequentialAddressingMode mode, SurfaceCoordinates coordMode, IEnumerable<VertexAttributeFlag> vertexAttributes, VFXMeshTransform postTranform)
|
|
{
|
|
bool skinnedMesh = source.valueType == VFXValueType.SkinnedMeshRenderer;
|
|
var mesh = !skinnedMesh ? source : new VFXExpressionMeshFromSkinnedMeshRenderer(source);
|
|
var UintThree = VFXOperatorUtility.ThreeExpression[VFXValueType.Uint32];
|
|
|
|
var meshIndexCount = new VFXExpressionMeshIndexCount(mesh);
|
|
var triangleCount = meshIndexCount / UintThree;
|
|
triangleIndex = VFXOperatorUtility.ApplyAddressingMode(triangleIndex, triangleCount, mode);
|
|
|
|
return SampleTriangleAttribute(source, triangleIndex, coord, coordMode, vertexAttributes, postTranform);
|
|
}
|
|
|
|
public static VFXMeshTransform ComputeTransformMatrix(VFXExpression source, SkinnedRootTransform smrTransform, VFXExpression postTransform)
|
|
{
|
|
var transfom = new VFXMeshTransform()
|
|
{
|
|
current = postTransform,
|
|
previous = postTransform
|
|
};
|
|
|
|
if (smrTransform != SkinnedRootTransform.None)
|
|
{
|
|
VFXExpression transformRootBoneCurrent;
|
|
VFXExpression transformRootBonePrevious;
|
|
if (source.valueType != VFXValueType.SkinnedMeshRenderer)
|
|
throw new InvalidOperationException();
|
|
|
|
if (smrTransform == SkinnedRootTransform.ApplyLocalRootTransform)
|
|
{
|
|
transformRootBoneCurrent = new VFXExpressionRootBoneTransformFromSkinnedMeshRenderer(source, VFXSkinnedTransform.LocalRootBoneTransform, VFXSkinnedMeshFrame.Current);
|
|
transformRootBonePrevious = new VFXExpressionRootBoneTransformFromSkinnedMeshRenderer(source, VFXSkinnedTransform.LocalRootBoneTransform, VFXSkinnedMeshFrame.Previous);
|
|
}
|
|
else if (smrTransform == SkinnedRootTransform.ApplyWorldRootTransform)
|
|
{
|
|
transformRootBoneCurrent = new VFXExpressionRootBoneTransformFromSkinnedMeshRenderer(source, VFXSkinnedTransform.WorldRootBoneTransform, VFXSkinnedMeshFrame.Current);
|
|
transformRootBonePrevious = new VFXExpressionRootBoneTransformFromSkinnedMeshRenderer(source, VFXSkinnedTransform.WorldRootBoneTransform, VFXSkinnedMeshFrame.Previous);
|
|
}
|
|
else
|
|
{
|
|
throw new InvalidOperationException("Unexpected SMR Transform" + smrTransform);
|
|
}
|
|
|
|
transfom.current = new VFXExpressionTransformMatrix(postTransform, transformRootBoneCurrent);
|
|
transfom.previous = new VFXExpressionTransformMatrix(postTransform, transformRootBonePrevious);
|
|
}
|
|
|
|
return transfom;
|
|
}
|
|
|
|
protected sealed override VFXExpression[] BuildExpression(VFXExpression[] inputExpression)
|
|
{
|
|
var source = inputExpression[0];
|
|
var matrix = ComputeTransformMatrix(source, actualSkinnedTransform, inputExpression.Last());
|
|
|
|
VFXExpression[] outputExpressions = null;
|
|
if (placementMode == PlacementMode.Vertex)
|
|
{
|
|
var sampled = SampleVertexAttribute(inputExpression[0], inputExpression[1], mode, GetOutputVertexAttributes(), matrix);
|
|
outputExpressions = sampled.ToArray();
|
|
}
|
|
else if (placementMode == PlacementMode.Edge)
|
|
{
|
|
var sampled = SampleEdgeAttribute(inputExpression[0], inputExpression[1], inputExpression[2], mode, GetOutputVertexAttributes(), matrix);
|
|
outputExpressions = sampled.ToArray();
|
|
}
|
|
else if (placementMode == PlacementMode.Surface)
|
|
{
|
|
var sampled = SampleTriangleAttribute(inputExpression[0], inputExpression[1], inputExpression[2], mode, surfaceCoordinates, GetOutputVertexAttributes(), matrix);
|
|
outputExpressions = sampled.ToArray();
|
|
}
|
|
else
|
|
{
|
|
throw new InvalidOperationException("Not supported placement mode " + placementMode);
|
|
}
|
|
return outputExpressions;
|
|
}
|
|
}
|
|
}
|