275 lines
11 KiB
C#
275 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
|
|
namespace UnityEditor.VFX
|
|
{
|
|
class VFXOutputUpdate : VFXContext
|
|
{
|
|
[Flags]
|
|
public enum Features
|
|
{
|
|
None = 0,
|
|
MotionVector = 1 << 0,
|
|
IndirectDraw = 1 << 1,
|
|
Culling = 1 << 2 | IndirectDraw,
|
|
MultiMesh = 1 << 3 | Culling,
|
|
LOD = 1 << 4 | Culling,
|
|
Sort = 1 << 5 | IndirectDraw,
|
|
FrustumCulling = 1 << 6 | IndirectDraw,
|
|
}
|
|
|
|
public VFXOutputUpdate() : base(VFXContextType.Filter, VFXDataType.Particle, VFXDataType.Particle) {}
|
|
public override string name => "OutputUpdate";
|
|
|
|
private VFXAbstractParticleOutput m_Output;
|
|
public VFXAbstractParticleOutput output => m_Output;
|
|
public void SetOutput(VFXAbstractParticleOutput output)
|
|
{
|
|
if (m_Output != null)
|
|
throw new InvalidOperationException("Unexpected SetOutput called twice, supposed to be call only once after construction");
|
|
|
|
features = output.outputUpdateFeatures;
|
|
|
|
if (features == VFXOutputUpdate.Features.None)
|
|
throw new ArgumentException("This output does not need an output update pass");
|
|
|
|
m_Output = output;
|
|
}
|
|
|
|
private Features features = Features.None;
|
|
|
|
public static bool HasFeature(Features flags, Features feature)
|
|
{
|
|
return (flags & feature) == feature;
|
|
}
|
|
|
|
public static bool IsPerCamera(Features flags)
|
|
{
|
|
return HasFeature(flags, Features.MotionVector)
|
|
|| HasFeature(flags, Features.LOD)
|
|
|| HasFeature(flags, Features.Sort)
|
|
|| HasFeature(flags, Features.FrustumCulling);
|
|
}
|
|
|
|
public bool HasFeature(Features feature)
|
|
{
|
|
return HasFeature(this.features, feature);
|
|
}
|
|
|
|
public bool isPerCamera => IsPerCamera(features);
|
|
|
|
// Set by compiler
|
|
public int bufferIndex = -1;
|
|
public int sortedBufferIndex = -1;
|
|
public uint bufferCount
|
|
{
|
|
get
|
|
{
|
|
if (HasFeature(Features.MultiMesh))
|
|
return ((IVFXMultiMeshOutput)m_Output).meshCount;
|
|
if (HasFeature(Features.IndirectDraw))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public override string codeGeneratorTemplate
|
|
{
|
|
get
|
|
{
|
|
return VisualEffectGraphPackageInfo.assetPackagePath + "/Shaders/VFXOutputUpdate";
|
|
}
|
|
}
|
|
public override bool codeGeneratorCompute { get { return true; } }
|
|
public override bool doesIncludeCommonCompute { get { return false; } }
|
|
|
|
public override VFXTaskType taskType { get { return isPerCamera ? VFXTaskType.PerCameraUpdate : VFXTaskType.Update; } }
|
|
|
|
public override VFXExpressionMapper GetExpressionMapper(VFXDeviceTarget target)
|
|
{
|
|
if (!m_Output)
|
|
throw new NullReferenceException("Unexpected call of GetExpressionMapper with a null output");
|
|
if (features == Features.None)
|
|
throw new InvalidOperationException("This additional update context has no feature set");
|
|
|
|
|
|
if (target == VFXDeviceTarget.GPU)
|
|
{
|
|
var expressionMapper = m_Output.GetExpressionMapper(target);
|
|
|
|
var exp = GetExpressionsFromSlots(m_Output);
|
|
|
|
if (HasFeature(Features.LOD))
|
|
{
|
|
var lodExp = exp.FirstOrDefault(e => e.name == VFXMultiMeshHelper.lodName);
|
|
var ratioExp = lodExp.exp * VFXValue.Constant(new Vector4(0.01f, 0.01f, 0.01f, 0.01f));
|
|
expressionMapper.AddExpression(ratioExp, VFXMultiMeshHelper.lodName, -1);
|
|
}
|
|
|
|
if (HasFeature(Features.LOD) || HasFeature(Features.FrustumCulling))
|
|
{
|
|
var radiusScaleExp = exp.FirstOrDefault(e => e.name == "radiusScale").exp;
|
|
if (radiusScaleExp == null) // Not found, assume it's 1
|
|
radiusScaleExp = VFXValue.Constant(1.0f);
|
|
expressionMapper.AddExpression(radiusScaleExp, "radiusScale", -1);
|
|
}
|
|
|
|
if (HasFeature(Features.MotionVector))
|
|
{
|
|
var currentFrameIndex = expressionMapper.FromNameAndId("currentFrameIndex", -1);
|
|
if (currentFrameIndex == null)
|
|
Debug.LogError("CurrentFrameIndex isn't reachable in encapsulatedOutput for motionVector");
|
|
}
|
|
|
|
//Since it's a compute shader without renderer associated, these entries aren't automatically sent
|
|
expressionMapper.AddExpression(VFXBuiltInExpression.LocalToWorld, "unity_ObjectToWorld", -1);
|
|
expressionMapper.AddExpression(VFXBuiltInExpression.WorldToLocal, "unity_WorldToObject", -1);
|
|
|
|
return expressionMapper;
|
|
}
|
|
else
|
|
return new VFXExpressionMapper();
|
|
}
|
|
|
|
protected override IEnumerable<VFXBlock> implicitPostBlock
|
|
{
|
|
get
|
|
{
|
|
foreach (var inBase in base.implicitPostBlock)
|
|
yield return inBase;
|
|
|
|
if (m_Output != null)
|
|
{
|
|
foreach (var block in m_Output.activeChildrenWithImplicit)
|
|
{
|
|
yield return block;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public override IEnumerable<VFXAttributeInfo> attributes
|
|
{
|
|
get
|
|
{
|
|
yield return new VFXAttributeInfo(VFXAttribute.Position, VFXAttributeMode.Read);
|
|
yield return new VFXAttributeInfo(VFXAttribute.Alive, VFXAttributeMode.Read);
|
|
yield return new VFXAttributeInfo(VFXAttribute.AxisX, VFXAttributeMode.Read);
|
|
yield return new VFXAttributeInfo(VFXAttribute.AxisY, VFXAttributeMode.Read);
|
|
yield return new VFXAttributeInfo(VFXAttribute.AxisZ, VFXAttributeMode.Read);
|
|
yield return new VFXAttributeInfo(VFXAttribute.AngleX, VFXAttributeMode.Read);
|
|
yield return new VFXAttributeInfo(VFXAttribute.AngleY, VFXAttributeMode.Read);
|
|
yield return new VFXAttributeInfo(VFXAttribute.AngleZ, VFXAttributeMode.Read);
|
|
yield return new VFXAttributeInfo(VFXAttribute.PivotX, VFXAttributeMode.Read);
|
|
yield return new VFXAttributeInfo(VFXAttribute.PivotY, VFXAttributeMode.Read);
|
|
yield return new VFXAttributeInfo(VFXAttribute.PivotZ, VFXAttributeMode.Read);
|
|
yield return new VFXAttributeInfo(VFXAttribute.Size, VFXAttributeMode.Read);
|
|
yield return new VFXAttributeInfo(VFXAttribute.ScaleX, VFXAttributeMode.Read);
|
|
yield return new VFXAttributeInfo(VFXAttribute.ScaleY, VFXAttributeMode.Read);
|
|
yield return new VFXAttributeInfo(VFXAttribute.ScaleZ, VFXAttributeMode.Read);
|
|
|
|
if (HasFeature(Features.MultiMesh))
|
|
yield return new VFXAttributeInfo(VFXAttribute.MeshIndex, VFXAttributeMode.Read);
|
|
|
|
if (HasFeature(Features.MotionVector) && output is VFXLineOutput)
|
|
yield return new VFXAttributeInfo(VFXAttribute.TargetPosition, VFXAttributeMode.Read);
|
|
}
|
|
}
|
|
|
|
public override IEnumerable<string> additionalDefines
|
|
{
|
|
get
|
|
{
|
|
foreach (var d in base.additionalDefines)
|
|
yield return d;
|
|
|
|
yield return "INDIRECT_BUFFER_COUNT " + bufferCount;
|
|
|
|
if (HasFeature(Features.MotionVector))
|
|
{
|
|
yield return "VFX_FEATURE_MOTION_VECTORS";
|
|
if (output.SupportsMotionVectorPerVertex(out uint vertsCount))
|
|
yield return "VFX_FEATURE_MOTION_VECTORS_VERTS " + vertsCount;
|
|
}
|
|
if (HasFeature(Features.LOD))
|
|
yield return "VFX_FEATURE_LOD";
|
|
if (HasFeature(Features.Sort))
|
|
yield return "VFX_FEATURE_SORT";
|
|
if (HasFeature(Features.FrustumCulling))
|
|
yield return "VFX_FEATURE_FRUSTUM_CULL";
|
|
}
|
|
}
|
|
|
|
public override IEnumerable<KeyValuePair<string, VFXShaderWriter>> additionalReplacements
|
|
{
|
|
get
|
|
{
|
|
if (HasFeature(Features.MotionVector))
|
|
{
|
|
string motionVectorVerts = null;
|
|
|
|
if (output is VFXLineOutput)
|
|
{
|
|
bool useTargetOffset = (bool)output.GetSettingValue("useTargetOffset");
|
|
string targetPosition = useTargetOffset ? "mul(elementToVFX, float4(targetOffset, 1)).xyz" : "attributes.targetPosition";
|
|
switch (output.taskType)
|
|
{
|
|
case VFXTaskType.ParticleQuadOutput:
|
|
motionVectorVerts = @"float3 verts[] =
|
|
{
|
|
attributes.position,
|
|
attributes.position,
|
|
" + targetPosition + @",
|
|
" + targetPosition + @"
|
|
};";
|
|
break;
|
|
case VFXTaskType.ParticleLineOutput:
|
|
motionVectorVerts = @"float3 verts[] =
|
|
{
|
|
attributes.position,
|
|
" + targetPosition + @"
|
|
};";
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (output.taskType)
|
|
{
|
|
case VFXTaskType.ParticleQuadOutput:
|
|
motionVectorVerts = @"float3 verts[] =
|
|
{
|
|
mul(elementToVFX, float4(-0.5f, -0.5f, 0.0f, 1.0f)).xyz,
|
|
mul(elementToVFX, float4( 0.5f, -0.5f, 0.0f, 1.0f)).xyz,
|
|
mul(elementToVFX, float4(-0.5f, 0.5f, 0.0f, 1.0f)).xyz,
|
|
mul(elementToVFX, float4( 0.5f, 0.5f, 0.0f, 1.0f)).xyz
|
|
};";
|
|
break;
|
|
case VFXTaskType.ParticleTriangleOutput:
|
|
motionVectorVerts = @"float3 verts[] =
|
|
{
|
|
mul(elementToVFX, float4(-0.5f, -0.288675129413604736328125f, 0.0f, 1.0f)).xyz,
|
|
mul(elementToVFX, float4( 0.0f, 0.57735025882720947265625f, 0.0f, 1.0f)).xyz,
|
|
mul(elementToVFX, float4( 0.5f, -0.288675129413604736328125f, 0.0f, 1.0f)).xyz
|
|
};";
|
|
break;
|
|
case VFXTaskType.ParticlePointOutput:
|
|
motionVectorVerts = @"float3 verts[] = { elementToVFX._m03_m13_m23 };";
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (motionVectorVerts != null)
|
|
{
|
|
var motionVectorVertsWriter = new VFXShaderWriter();
|
|
motionVectorVertsWriter.Write(motionVectorVerts);
|
|
yield return new KeyValuePair<string, VFXShaderWriter>("${VFXMotionVectorVerts}", motionVectorVertsWriter);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|