969 lines
32 KiB
HLSL
969 lines
32 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
GpuSkinVertexFactory.hlsl: GPU skinned vertex factory shader code
|
|
=============================================================================*/
|
|
|
|
|
|
#include "VertexFactoryCommon.ush"
|
|
#include "GpuSkinCommon.ush"
|
|
|
|
// Whether the material supports morph targets
|
|
#ifndef GPUSKIN_MORPH_BLEND
|
|
#define GPUSKIN_MORPH_BLEND 0
|
|
#endif
|
|
|
|
#ifndef GPUSKIN_APEX_CLOTH
|
|
#define GPUSKIN_APEX_CLOTH 0
|
|
#endif
|
|
#ifndef GPUSKIN_APEX_CLOTH_PREVIOUS
|
|
#define GPUSKIN_APEX_CLOTH_PREVIOUS 0
|
|
#endif
|
|
|
|
#ifndef GPUSKIN_LIMIT_2BONE_INFLUENCES
|
|
#define GPUSKIN_LIMIT_2BONE_INFLUENCES 0 // default is 4 bones
|
|
#endif
|
|
|
|
#ifndef GPUSKIN_UNLIMITED_BONE_INFLUENCE
|
|
#define GPUSKIN_UNLIMITED_BONE_INFLUENCE 0
|
|
#endif
|
|
|
|
#define MAX_INFLUENCES_PER_STREAM 4
|
|
|
|
#define FIXED_VERTEX_INDEX 0xFFFF
|
|
|
|
#if GPUSKIN_APEX_CLOTH
|
|
#define GPUSkinVF GPUSkinAPEXClothVF.Common
|
|
#else
|
|
#define GPUSkinVF GPUSkinVFBase.Common
|
|
#endif
|
|
|
|
bool IsMorphTarget()
|
|
{
|
|
#if GPUSKIN_MORPH_BLEND
|
|
return GPUSkinVF.bIsMorphTarget;
|
|
#else
|
|
// When Morph targets are not supported on the material, always return false so the associated shader code can be compiled out.
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
struct FVertexFactoryInput
|
|
{
|
|
float4 Position : ATTRIBUTE0;
|
|
// 0..1
|
|
HALF3_TYPE TangentX : ATTRIBUTE1;
|
|
// 0..1
|
|
// TangentZ.w contains sign of tangent basis determinant
|
|
HALF4_TYPE TangentZ : ATTRIBUTE2;
|
|
|
|
#if GPUSKIN_UNLIMITED_BONE_INFLUENCE
|
|
uint BlendOffsetCount : ATTRIBUTE3;
|
|
#else
|
|
uint4 BlendIndices : ATTRIBUTE3;
|
|
uint4 BlendIndicesExtra : ATTRIBUTE14;
|
|
float4 BlendWeights : ATTRIBUTE4;
|
|
float4 BlendWeightsExtra : ATTRIBUTE15;
|
|
#endif // GPUSKIN_UNLIMITED_BONE_INFLUENCE
|
|
|
|
#if NUM_MATERIAL_TEXCOORDS_VERTEX
|
|
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 1
|
|
float4 TexCoords0 : ATTRIBUTE5;
|
|
#elif NUM_MATERIAL_TEXCOORDS_VERTEX == 1
|
|
float2 TexCoords0 : ATTRIBUTE5;
|
|
#endif
|
|
|
|
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 3
|
|
float4 TexCoords1 : ATTRIBUTE6;
|
|
#elif NUM_MATERIAL_TEXCOORDS_VERTEX == 3
|
|
float2 TexCoords1 : ATTRIBUTE6;
|
|
#endif
|
|
|
|
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 4
|
|
#error Too many texture coordinate sets defined on GPUSkin vertex input. Max: 4.
|
|
#endif
|
|
#endif // NUM_MATERIAL_TEXCOORDS_VERTEX
|
|
|
|
#if GPUSKIN_MORPH_BLEND
|
|
// NOTE: TEXCOORD6,TEXCOORD7 used instead of POSITION1,NORMAL1 since those semantics are not supported by Cg
|
|
/** added to the Position */
|
|
float3 DeltaPosition : ATTRIBUTE9; //POSITION1;
|
|
/** added to the TangentZ and then used to derive new TangentX,TangentY, .w contains the weight of the tangent blend */
|
|
float3 DeltaTangentZ : ATTRIBUTE10; //NORMAL1;
|
|
#endif
|
|
|
|
#if GPUSKIN_MORPH_BLEND || GPUSKIN_APEX_CLOTH
|
|
uint VertexID : SV_VertexID;
|
|
#endif
|
|
|
|
/** Per vertex color */
|
|
float4 Color : ATTRIBUTE13;
|
|
|
|
// Dynamic instancing related attributes with InstanceIdOffset : ATTRIBUTE16
|
|
VF_GPUSCENE_DECLARE_INPUT_BLOCK(16)
|
|
VF_INSTANCED_STEREO_DECLARE_INPUT_BLOCK()
|
|
VF_MOBILE_MULTI_VIEW_DECLARE_INPUT_BLOCK()
|
|
};
|
|
|
|
#if GPUSKIN_APEX_CLOTH
|
|
// This must match NUM_INFLUENCES_PER_VERTEX in ClothingMeshUtils.cpp and GpuSkinCacheComputeShader.usf
|
|
// It represents the maximum number of influences so perhaps should be renamed. Right now we are keeping the name matching for easy search.
|
|
// TODO: Make this easier to change in without messing things up
|
|
#define NUM_INFLUENCES_PER_VERTEX 5
|
|
|
|
struct FClothVertex
|
|
{
|
|
// APEX cloth mesh-mesh mapping data
|
|
// Barycentric Coordinate Data
|
|
float4 BaryCoordPos;
|
|
float4 BaryCoordNormal;
|
|
float4 BaryCoordTangent;
|
|
uint4 SimulIndices;
|
|
|
|
float Weight;
|
|
|
|
/*
|
|
// Contains the 3 indices for verts in the source mesh forming a triangle, the last element
|
|
// is a flag to decide how the skinning works, 0xffff uses no simulation, and just normal
|
|
// skinning, anything else uses the source mesh and the above skin data to get the final position
|
|
uint16 SourceMeshVertIndices[4];
|
|
|
|
// Dummy for alignment (16 bytes)
|
|
uint32 Padding[2];
|
|
*/
|
|
};
|
|
|
|
|
|
#define NUM_FLOAT4S_PER_VERTEX_INFLUENCE 4; // stride of GPUSkinApexCloth
|
|
|
|
void SetupClothVertex(uint VertexIndex, out FClothVertex ClothVertexInfluences[NUM_INFLUENCES_PER_VERTEX])
|
|
{
|
|
const uint MinVertexIndex = GPUSkinAPEXClothVF.GPUSkinApexClothStartIndexOffset.x;
|
|
const uint ClothIndexOffset = GPUSkinAPEXClothVF.GPUSkinApexClothStartIndexOffset.y;
|
|
VertexIndex = GPUSkinAPEXClothVF.ClothNumInfluencesPerVertex * (VertexIndex - MinVertexIndex) + ClothIndexOffset;
|
|
|
|
UNROLL_N(NUM_INFLUENCES_PER_VERTEX)
|
|
for (int i = 0; i < GPUSkinAPEXClothVF.ClothNumInfluencesPerVertex; ++i )
|
|
{
|
|
const uint Offset = (VertexIndex + i) * NUM_FLOAT4S_PER_VERTEX_INFLUENCE;
|
|
|
|
ClothVertexInfluences[i].BaryCoordPos = GPUSkinAPEXClothVF.GPUSkinApexCloth[Offset];
|
|
ClothVertexInfluences[i].BaryCoordNormal = GPUSkinAPEXClothVF.GPUSkinApexCloth[Offset + 1];
|
|
ClothVertexInfluences[i].BaryCoordTangent = GPUSkinAPEXClothVF.GPUSkinApexCloth[Offset + 2];
|
|
|
|
uint4 PackedIndices = asuint(GPUSkinAPEXClothVF.GPUSkinApexCloth[Offset + 3]);
|
|
ClothVertexInfluences[i].SimulIndices.yw = (PackedIndices.xy >> 16) & 0xffff;
|
|
ClothVertexInfluences[i].SimulIndices.xz = PackedIndices.xy & 0xffff;
|
|
|
|
ClothVertexInfluences[i].Weight = asfloat(PackedIndices[2]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
struct FVertexFactoryInterpolantsVSToPS
|
|
{
|
|
TANGENTTOWORLD_INTERPOLATOR_BLOCK
|
|
|
|
#if INTERPOLATE_VERTEX_COLOR
|
|
float4 Color : COLOR0;
|
|
#endif
|
|
|
|
#if NUM_TEX_COORD_INTERPOLATORS
|
|
// Pack interpolators to reduce the number of semantics used
|
|
float4 TexCoords[(NUM_TEX_COORD_INTERPOLATORS+1)/2] : TEXCOORD0;
|
|
#endif
|
|
|
|
#if VF_USE_PRIMITIVE_SCENE_DATA
|
|
nointerpolation uint PrimitiveId : PRIMITIVE_ID;
|
|
#endif
|
|
};
|
|
|
|
struct FVertexFactoryRayTracingInterpolants
|
|
{
|
|
FVertexFactoryInterpolantsVSToPS InterpolantsVSToPS;
|
|
};
|
|
|
|
#if NUM_TEX_COORD_INTERPOLATORS
|
|
float2 GetUV(FVertexFactoryInterpolantsVSToPS Interpolants, int UVIndex)
|
|
{
|
|
float4 UVVector = Interpolants.TexCoords[UVIndex / 2];
|
|
return UVIndex % 2 ? UVVector.zw : UVVector.xy;
|
|
}
|
|
|
|
void SetUV(inout FVertexFactoryInterpolantsVSToPS Interpolants, int UVIndex, float2 InValue)
|
|
{
|
|
FLATTEN
|
|
if (UVIndex % 2)
|
|
{
|
|
Interpolants.TexCoords[UVIndex / 2].zw = InValue;
|
|
}
|
|
else
|
|
{
|
|
Interpolants.TexCoords[UVIndex / 2].xy = InValue;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/** Converts from vertex factory specific interpolants to a FMaterialPixelParameters, which is used by material inputs. */
|
|
FMaterialPixelParameters GetMaterialPixelParameters(FVertexFactoryInterpolantsVSToPS Interpolants, float4 SvPosition)
|
|
{
|
|
// GetMaterialPixelParameters is responsible for fully initializing the result
|
|
FMaterialPixelParameters Result = MakeInitializedMaterialPixelParameters();
|
|
|
|
#if NUM_TEX_COORD_INTERPOLATORS
|
|
UNROLL
|
|
for(int CoordinateIndex = 0;CoordinateIndex < NUM_TEX_COORD_INTERPOLATORS;CoordinateIndex++)
|
|
{
|
|
Result.TexCoords[CoordinateIndex] = GetUV(Interpolants, CoordinateIndex);
|
|
}
|
|
#endif
|
|
|
|
half3 TangentToWorld0 = Interpolants.TangentToWorld0.xyz;
|
|
half4 TangentToWorld2 = Interpolants.TangentToWorld2;
|
|
Result.TangentToWorld = AssembleTangentToWorld( TangentToWorld0, TangentToWorld2 );
|
|
#if USE_WORLDVERTEXNORMAL_CENTER_INTERPOLATION
|
|
Result.WorldVertexNormal_Center = Interpolants.TangentToWorld2_Center.xyz;
|
|
#endif
|
|
Result.UnMirrored = TangentToWorld2.w;
|
|
#if INTERPOLATE_VERTEX_COLOR
|
|
Result.VertexColor = Interpolants.Color;
|
|
#else
|
|
Result.VertexColor = 0;
|
|
#endif
|
|
Result.TwoSidedSign = 1;
|
|
|
|
#if VF_USE_PRIMITIVE_SCENE_DATA
|
|
Result.PrimitiveId = Interpolants.PrimitiveId;
|
|
#endif
|
|
|
|
return Result;
|
|
}
|
|
|
|
#define FBoneMatrix float3x4
|
|
|
|
// Cache data to avoid multiple calculation
|
|
struct FVertexFactoryIntermediates
|
|
{
|
|
float3 InvNonUniformScale;
|
|
float DeterminantSign;
|
|
|
|
FDFMatrix LocalToWorld;
|
|
FDFInverseMatrix WorldToLocal;
|
|
FDFMatrix PrevLocalToWorld;
|
|
|
|
// Blend Matrix (used for position/tangents)
|
|
FBoneMatrix BlendMatrix;
|
|
|
|
// Unpacked position
|
|
float3 UnpackedPosition;
|
|
|
|
// Tangent Basis
|
|
float3x3 TangentToLocal;
|
|
|
|
// Vertex Color
|
|
float4 Color;
|
|
|
|
#if GPUSKIN_APEX_CLOTH
|
|
// in world space (non translated)
|
|
float4 SimulatedPosition;
|
|
// in world space (non translated)
|
|
float4 PreviousSimulatedPosition;
|
|
|
|
FClothVertex ClothVertexInfluences[NUM_INFLUENCES_PER_VERTEX];
|
|
#endif
|
|
/** Cached primitive and instance data */
|
|
FSceneDataIntermediates SceneData;
|
|
};
|
|
|
|
FPrimitiveSceneData GetPrimitiveData(FVertexFactoryIntermediates Intermediates)
|
|
{
|
|
return Intermediates.SceneData.Primitive;
|
|
}
|
|
|
|
FInstanceSceneData GetInstanceData(FVertexFactoryIntermediates Intermediates)
|
|
{
|
|
return Intermediates.SceneData.InstanceData;
|
|
}
|
|
|
|
float3 VertexFactoryGetPreviousInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates);
|
|
float3 VertexFactoryGetInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates);
|
|
|
|
/** Converts from vertex factory specific input to a FMaterialVertexParameters, which is used by vertex shader material inputs. */
|
|
FMaterialVertexParameters GetMaterialVertexParameters(
|
|
FVertexFactoryInput Input,
|
|
FVertexFactoryIntermediates Intermediates,
|
|
float3 WorldPosition,
|
|
float3x3 TangentToLocal,
|
|
bool bIsPreviousFrame = false)
|
|
{
|
|
FMaterialVertexParameters Result = MakeInitializedMaterialVertexParameters();
|
|
Result.SceneData = Intermediates.SceneData;
|
|
|
|
Result.WorldPosition = WorldPosition;
|
|
if (bIsPreviousFrame)
|
|
{
|
|
Result.PositionInstanceSpace = VertexFactoryGetPreviousInstanceSpacePosition(Input, Intermediates);
|
|
}
|
|
else
|
|
{
|
|
Result.PositionInstanceSpace = VertexFactoryGetInstanceSpacePosition(Input, Intermediates);
|
|
}
|
|
Result.PositionPrimitiveSpace = Result.PositionInstanceSpace; // No support for instancing, so instance == primitive
|
|
|
|
Result.VertexColor = Intermediates.Color;
|
|
Result.TangentToWorld = mul(TangentToLocal, DFToFloat3x3(Intermediates.LocalToWorld));
|
|
Result.PreSkinnedPosition = Intermediates.UnpackedPosition.xyz;
|
|
Result.PreSkinnedNormal = Input.TangentZ.xyz;
|
|
|
|
// Assumes no instacing
|
|
Result.PrevFrameLocalToWorld = Intermediates.PrevLocalToWorld;
|
|
|
|
#if NUM_MATERIAL_TEXCOORDS_VERTEX
|
|
|
|
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 0
|
|
Result.TexCoords[0] = Input.TexCoords0.xy;
|
|
#endif
|
|
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 1
|
|
Result.TexCoords[1] = Input.TexCoords0.zw;
|
|
#endif
|
|
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 2
|
|
Result.TexCoords[2] = Input.TexCoords1.xy;
|
|
#endif
|
|
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 3
|
|
Result.TexCoords[3] = Input.TexCoords1.zw;
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#if ENABLE_NEW_HLSL_GENERATOR
|
|
EvaluateVertexMaterialAttributes(Result);
|
|
#endif
|
|
|
|
Result.LWCData = MakeMaterialLWCData(Result);
|
|
|
|
return Result;
|
|
}
|
|
|
|
#if GPUSKIN_APEX_CLOTH
|
|
|
|
// if false, fixed vertex
|
|
bool IsSimulatedVertex(FClothVertex Input)
|
|
{
|
|
return (Input.SimulIndices.w < FIXED_VERTEX_INDEX);
|
|
}
|
|
|
|
float GetClothSimulWeight(uint StaticAlpha)
|
|
{
|
|
return 1.0f - (StaticAlpha / 65535.0f);
|
|
}
|
|
|
|
float3 GetClothSimulPosition(int Index, bool bPrevious)
|
|
{
|
|
float3 Position;
|
|
#if GPUSKIN_APEX_CLOTH_PREVIOUS
|
|
if(bPrevious)
|
|
{
|
|
return float3(GPUSkinAPEXClothVF.PreviousClothSimulVertsPositionsNormals[Index * 3], GPUSkinAPEXClothVF.PreviousClothSimulVertsPositionsNormals[Index * 3 + 1].x);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
return float3(GPUSkinAPEXClothVF.ClothSimulVertsPositionsNormals[Index * 3], GPUSkinAPEXClothVF.ClothSimulVertsPositionsNormals[Index * 3 + 1].x);
|
|
}
|
|
}
|
|
|
|
float3 GetClothSimulNormal(int Index, bool bPrevious)
|
|
{
|
|
float3 Normal;
|
|
#if GPUSKIN_APEX_CLOTH_PREVIOUS
|
|
if(bPrevious)
|
|
{
|
|
return float3(GPUSkinAPEXClothVF.PreviousClothSimulVertsPositionsNormals[Index * 3 + 1].y, GPUSkinAPEXClothVF.PreviousClothSimulVertsPositionsNormals[Index * 3 + 2]);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
return float3(GPUSkinAPEXClothVF.ClothSimulVertsPositionsNormals[Index * 3 + 1].y, GPUSkinAPEXClothVF.ClothSimulVertsPositionsNormals[Index * 3 + 2]);
|
|
}
|
|
}
|
|
|
|
float4 ClothingPosition(FClothVertex ClothVertexInfluences[NUM_INFLUENCES_PER_VERTEX], bool bPrevious)
|
|
{
|
|
int NumInfluences = 0;
|
|
float SumWeights = 0.0f;
|
|
float SimulWeight = 0.0f;
|
|
float3 AveragedSimulatedPosition = float3(0, 0, 0);
|
|
|
|
const float3 WorldScaleAbs = abs(GPUSkinAPEXClothVF.WorldScale); // World scale can't be used mirrored to calculate the clothing positions and tangents since the cloth normals are then reversed
|
|
|
|
UNROLL_N(NUM_INFLUENCES_PER_VERTEX)
|
|
for (int i = 0; i < GPUSkinAPEXClothVF.ClothNumInfluencesPerVertex; ++i )
|
|
{
|
|
const FClothVertex Influence = ClothVertexInfluences[i];
|
|
|
|
if( IsSimulatedVertex(Influence) )
|
|
{
|
|
++NumInfluences;
|
|
|
|
float3 A = GetClothSimulPosition(Influence.SimulIndices.x, bPrevious);
|
|
float3 B = GetClothSimulPosition(Influence.SimulIndices.y, bPrevious);
|
|
float3 C = GetClothSimulPosition(Influence.SimulIndices.z, bPrevious);
|
|
|
|
float3 NA = GetClothSimulNormal(Influence.SimulIndices.x, bPrevious);
|
|
float3 NB = GetClothSimulNormal(Influence.SimulIndices.y, bPrevious);
|
|
float3 NC = GetClothSimulNormal(Influence.SimulIndices.z, bPrevious);
|
|
|
|
SimulWeight += GetClothSimulWeight(Influence.SimulIndices.w);
|
|
|
|
float Weight = 1.0f;
|
|
if ( GPUSkinAPEXClothVF.ClothNumInfluencesPerVertex > 1 )
|
|
{
|
|
Weight = Influence.Weight;
|
|
SumWeights += Weight;
|
|
}
|
|
else
|
|
{
|
|
// Single influence, weight is 1.0
|
|
Weight = 1.0f;
|
|
SumWeights = 1.0f;
|
|
}
|
|
|
|
float3 TriangleBary = float3(Influence.BaryCoordPos.x,
|
|
Influence.BaryCoordPos.y,
|
|
1.0f - Influence.BaryCoordPos.x - Influence.BaryCoordPos.y);
|
|
|
|
float3 SimPosition = TriangleBary.x * (A + NA * Influence.BaryCoordPos.w * WorldScaleAbs.x)
|
|
+ TriangleBary.y * (B + NB * Influence.BaryCoordPos.w * WorldScaleAbs.y)
|
|
+ TriangleBary.z * (C + NC * Influence.BaryCoordPos.w * WorldScaleAbs.z);
|
|
|
|
AveragedSimulatedPosition += Weight * SimPosition;
|
|
}
|
|
}
|
|
|
|
if ( NumInfluences > 0 && SumWeights > 1e-4f )
|
|
{
|
|
float InvWeight = 1.0f / SumWeights;
|
|
AveragedSimulatedPosition *= InvWeight;
|
|
}
|
|
else
|
|
{
|
|
AveragedSimulatedPosition = float3(0, 0, 0);
|
|
SimulWeight = 0.0f; // Fall back to skinned vertex position by setting this lerp param to zero
|
|
}
|
|
|
|
if (GPUSkinAPEXClothVF.ClothNumInfluencesPerVertex > 1)
|
|
{
|
|
SimulWeight /= GPUSkinAPEXClothVF.ClothNumInfluencesPerVertex;
|
|
}
|
|
|
|
return float4(AveragedSimulatedPosition, SimulWeight);
|
|
}
|
|
|
|
#endif //#if GPUSKIN_APEX_CLOTH
|
|
|
|
/**
|
|
* Unpack position - uncompress xyz position to world position
|
|
*/
|
|
float3 UnpackedPosition( FVertexFactoryInput Input )
|
|
{
|
|
return float3(Input.Position.xyz);
|
|
}
|
|
|
|
#if GPUSKIN_MORPH_BLEND
|
|
|
|
/**
|
|
* Adds the delta position from the combined morph targets to the vertex position
|
|
*/
|
|
float3 MorphPosition( FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates )
|
|
{
|
|
return Intermediates.UnpackedPosition + Input.DeltaPosition;
|
|
}
|
|
|
|
float3 MorphPreviousPosition( FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates )
|
|
{
|
|
#if GPUSKIN_MORPH_USE_PREVIOUS
|
|
if (ResolvedView.FrameCounter == GPUSkinVF.MorphUpdatedFrameNumber && ResolvedView.WorldIsPaused == 0)
|
|
{
|
|
// 3 + 3 floats because of the format defined in FMorphGPUSkinVertex
|
|
uint Offset = Input.VertexID * (3 + 3);
|
|
|
|
float3 PreviousDeltaPosition = float3(
|
|
GPUSkinVF.PreviousMorphBuffer[Offset + 0],
|
|
GPUSkinVF.PreviousMorphBuffer[Offset + 1],
|
|
GPUSkinVF.PreviousMorphBuffer[Offset + 2]);
|
|
|
|
return Intermediates.UnpackedPosition + PreviousDeltaPosition;
|
|
}
|
|
#endif
|
|
return Intermediates.UnpackedPosition + Input.DeltaPosition;
|
|
}
|
|
#endif // GPUSKIN_MORPH_BLEND
|
|
|
|
FBoneMatrix GetBoneMatrix(int Index)
|
|
{
|
|
float4 A = GPUSkinVF.BoneMatrices[Index * 3];
|
|
float4 B = GPUSkinVF.BoneMatrices[Index * 3 + 1];
|
|
float4 C = GPUSkinVF.BoneMatrices[Index * 3 + 2];
|
|
return FBoneMatrix(A,B,C);
|
|
}
|
|
|
|
FBoneMatrix CalcBoneMatrix( FVertexFactoryInput Input )
|
|
{
|
|
#if GPUSKIN_UNLIMITED_BONE_INFLUENCE
|
|
return ComputeBoneMatrixWithUnlimitedInfluences(GPUSkinVF.BoneMatrices, GPUSkinVF.InputWeightStream, GPUSkinVF.InputWeightIndexSize, Input.BlendOffsetCount);
|
|
#else
|
|
const bool bHasExtraInfluences = GPUSkinVF.NumBoneInfluencesParam > MAX_INFLUENCES_PER_STREAM;
|
|
FGPUSkinIndexAndWeight In = (FGPUSkinIndexAndWeight)0;
|
|
In.BlendIndices = Input.BlendIndices;
|
|
In.BlendWeights = Input.BlendWeights;
|
|
if (bHasExtraInfluences)
|
|
{
|
|
In.BlendIndices2 = Input.BlendIndicesExtra;
|
|
In.BlendWeights2 = Input.BlendWeightsExtra;
|
|
}
|
|
return ComputeBoneMatrixWithLimitedInfluences(GPUSkinVF.BoneMatrices, In, bHasExtraInfluences);
|
|
#endif
|
|
}
|
|
|
|
FBoneMatrix CalcPreviousBoneMatrix(FVertexFactoryInput Input)
|
|
{
|
|
#if GPUSKIN_UNLIMITED_BONE_INFLUENCE
|
|
return ComputeBoneMatrixWithUnlimitedInfluences(GPUSkinVF.PreviousBoneMatrices, GPUSkinVF.InputWeightStream, GPUSkinVF.InputWeightIndexSize, Input.BlendOffsetCount);
|
|
#else
|
|
const bool bHasExtraInfluences = GPUSkinVF.NumBoneInfluencesParam > MAX_INFLUENCES_PER_STREAM;
|
|
FGPUSkinIndexAndWeight In = (FGPUSkinIndexAndWeight)0;
|
|
In.BlendIndices = Input.BlendIndices;
|
|
In.BlendWeights = Input.BlendWeights;
|
|
if (bHasExtraInfluences)
|
|
{
|
|
In.BlendIndices2 = Input.BlendIndicesExtra;
|
|
In.BlendWeights2 = Input.BlendWeightsExtra;
|
|
}
|
|
return ComputeBoneMatrixWithLimitedInfluences(GPUSkinVF.PreviousBoneMatrices, In, bHasExtraInfluences);
|
|
#endif
|
|
}
|
|
|
|
/** transform position by weighted sum of skinning matrices */
|
|
float3 SkinPosition( FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates )
|
|
{
|
|
float3 Position;
|
|
if (IsMorphTarget())
|
|
{
|
|
#if GPUSKIN_MORPH_BLEND
|
|
Position = MorphPosition(Input, Intermediates);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
Position = Intermediates.UnpackedPosition;
|
|
}
|
|
|
|
// Note the use of mul(Matrix,Vector), bone matrices are stored transposed
|
|
// for tighter packing.
|
|
Position = mul(Intermediates.BlendMatrix, float4(Position, 1));
|
|
|
|
return Position;
|
|
}
|
|
|
|
/** transform position by weighted sum of skinning matrices */
|
|
float3 SkinPreviousPosition( FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates )
|
|
{
|
|
float3 Position;
|
|
if (IsMorphTarget())
|
|
{
|
|
#if GPUSKIN_MORPH_BLEND
|
|
Position = MorphPreviousPosition(Input, Intermediates);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
Position = Intermediates.UnpackedPosition;
|
|
}
|
|
|
|
FBoneMatrix BlendMatrix = Intermediates.BlendMatrix;
|
|
|
|
// Previous Blend Matrix (used for position in velocity rendering)
|
|
if (ResolvedView.FrameCounter == GPUSkinVF.BoneUpdatedFrameNumber && ResolvedView.WorldIsPaused == 0)
|
|
{
|
|
BlendMatrix = CalcPreviousBoneMatrix( Input );
|
|
}
|
|
|
|
// Note the use of mul(Matrix,Vector), bone matrices are stored transposed
|
|
// for tighter packing.
|
|
Position = mul(BlendMatrix, float4(Position, 1));
|
|
|
|
return Position;
|
|
}
|
|
/** transform the tangent basis vectors */
|
|
float3x3 SkinTangents( FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates )
|
|
{
|
|
float3x3 TangentToLocal;
|
|
|
|
// tangent
|
|
// -1..1
|
|
half3 LocalTangentX = Input.TangentX;
|
|
// -1..1 .xyz:normal, .w:contains sign of tangent basis determinant (left or right handed)
|
|
half4 LocalTangentZ = Input.TangentZ;
|
|
|
|
#if GPUSKIN_APEX_CLOTH
|
|
float TempClothBlendWeight = 0.0f;
|
|
float3x3 ClothTangent = float3x3(1,0,0, 0,1,0, 0,0,1);
|
|
if (GPUSkinAPEXClothVF.bEnabled && IsSimulatedVertex(Intermediates.ClothVertexInfluences[0]))
|
|
{
|
|
TempClothBlendWeight = GPUSkinAPEXClothVF.ClothBlendWeight;
|
|
bool bPrevious = false;
|
|
|
|
float3 NormalPosition = float3(0,0,0);
|
|
float3 TangentPosition = float3(0,0,0);
|
|
int NumInfluences = 0;
|
|
float SumWeights = 0.0;
|
|
float SimulWeight = 0.0;
|
|
|
|
const float3 WorldScaleAbs = abs(GPUSkinAPEXClothVF.WorldScale); // World scale can't be used mirrored to calculate the clothing positions and tangents since the cloth normals are then reversed
|
|
|
|
for (int i = 0; i < GPUSkinAPEXClothVF.ClothNumInfluencesPerVertex; ++i )
|
|
{
|
|
const FClothVertex Influence = Intermediates.ClothVertexInfluences[i];
|
|
|
|
if( IsSimulatedVertex(Influence) )
|
|
{
|
|
++NumInfluences;
|
|
TempClothBlendWeight = GPUSkinAPEXClothVF.ClothBlendWeight;
|
|
|
|
float3 A = GetClothSimulPosition(Influence.SimulIndices.x, bPrevious);
|
|
float3 B = GetClothSimulPosition(Influence.SimulIndices.y, bPrevious);
|
|
float3 C = GetClothSimulPosition(Influence.SimulIndices.z, bPrevious);
|
|
|
|
float3 NA = GetClothSimulNormal(Influence.SimulIndices.x, bPrevious);
|
|
float3 NB = GetClothSimulNormal(Influence.SimulIndices.y, bPrevious);
|
|
float3 NC = GetClothSimulNormal(Influence.SimulIndices.z, bPrevious);
|
|
|
|
SimulWeight += GetClothSimulWeight(Influence.SimulIndices.w);
|
|
|
|
float Weight = 1.0f;
|
|
if ( GPUSkinAPEXClothVF.ClothNumInfluencesPerVertex > 1 )
|
|
{
|
|
// Weight is packed in the last coordinate
|
|
Weight = Influence.Weight;
|
|
SumWeights += Weight;
|
|
}
|
|
else
|
|
{
|
|
// Single influence, weight is 1.0
|
|
Weight = 1.0f;
|
|
SumWeights = 1.0f;
|
|
}
|
|
|
|
NormalPosition += Weight * (Influence.BaryCoordNormal.x * (A + NA * Influence.BaryCoordNormal.w * WorldScaleAbs.x)
|
|
+ Influence.BaryCoordNormal.y * (B + NB * Influence.BaryCoordNormal.w * WorldScaleAbs.y)
|
|
+ Influence.BaryCoordNormal.z * (C + NC * Influence.BaryCoordNormal.w * WorldScaleAbs.z));
|
|
|
|
TangentPosition += Weight * (Influence.BaryCoordTangent.x * (A + NA * Influence.BaryCoordTangent.w * WorldScaleAbs.x)
|
|
+ Influence.BaryCoordTangent.y * (B + NB * Influence.BaryCoordTangent.w * WorldScaleAbs.y)
|
|
+ Influence.BaryCoordTangent.z * (C + NC * Influence.BaryCoordTangent.w * WorldScaleAbs.z));
|
|
}
|
|
}
|
|
|
|
if (GPUSkinAPEXClothVF.ClothNumInfluencesPerVertex > 1)
|
|
{
|
|
SimulWeight /= GPUSkinAPEXClothVF.ClothNumInfluencesPerVertex;
|
|
}
|
|
|
|
if ( NumInfluences > 0 && SumWeights > 1e-4f )
|
|
{
|
|
float InvWeight = 1.0f / SumWeights;
|
|
TangentPosition *= InvWeight;
|
|
NormalPosition *= InvWeight;
|
|
|
|
TangentToLocal[0] = normalize(TangentPosition - Intermediates.SimulatedPosition.xyz);
|
|
TangentToLocal[2] = normalize(NormalPosition - Intermediates.SimulatedPosition.xyz);
|
|
|
|
// Simulated cloth data are in cloth space so need to change into local space
|
|
TangentToLocal[0] = mul(float4(TangentToLocal[0], 0.0f), GPUSkinAPEXClothVF.ClothToLocal).xyz;
|
|
TangentToLocal[2] = mul(float4(TangentToLocal[2], 0.0f), GPUSkinAPEXClothVF.ClothToLocal).xyz;
|
|
|
|
// derive the new binormal by getting the cross product of the normal and tangent
|
|
// and flip vector based on sign of tangent basis determinant
|
|
TangentToLocal[1] = cross(TangentToLocal[2], TangentToLocal[0]) * LocalTangentZ.w;
|
|
|
|
ClothTangent = TangentToLocal;
|
|
}
|
|
else
|
|
{
|
|
SimulWeight = 0.0f; // Fall back to skinned data by setting this lerp param to zero
|
|
}
|
|
|
|
TempClothBlendWeight *= SimulWeight;
|
|
}
|
|
#else
|
|
if (IsMorphTarget())
|
|
{
|
|
#if GPUSKIN_MORPH_BLEND
|
|
// calc new normal by offseting it with the delta
|
|
LocalTangentZ.xyz = normalize(LocalTangentZ.xyz + Input.DeltaTangentZ);
|
|
// derive the new tangent by orthonormalizing the new normal against
|
|
// the base tangent vector (assuming these are normalized)
|
|
LocalTangentX = normalize(LocalTangentX - dot(LocalTangentX, LocalTangentZ.xyz) * LocalTangentZ.xyz);
|
|
#endif
|
|
}
|
|
#endif // GPUSKIN_APEX_CLOTH
|
|
|
|
|
|
// Note the use of mul(Matrix,Vector), bone matrices are stored transposed
|
|
// for tighter packing.
|
|
TangentToLocal[0] = normalize(mul( Intermediates.BlendMatrix, float4(LocalTangentX, 0) ));
|
|
TangentToLocal[2] = normalize(mul( Intermediates.BlendMatrix, float4(LocalTangentZ.xyz, 0) ));
|
|
|
|
// derive the new binormal by getting the cross product of the normal and tangent
|
|
// and flip vector based on sign of tangent basis determinant
|
|
TangentToLocal[1] = normalize(cross(TangentToLocal[2], TangentToLocal[0]) * LocalTangentZ.w);
|
|
|
|
#if GPUSKIN_APEX_CLOTH
|
|
if (GPUSkinAPEXClothVF.bEnabled)
|
|
{
|
|
return ClothTangent * TempClothBlendWeight + TangentToLocal * (1.0f - TempClothBlendWeight);
|
|
}
|
|
#endif // GPUSKIN_APEX_CLOTH
|
|
return TangentToLocal;
|
|
}
|
|
|
|
FVertexFactoryIntermediates GetVertexFactoryIntermediates(FVertexFactoryInput Input)
|
|
{
|
|
FVertexFactoryIntermediates Intermediates;
|
|
Intermediates.SceneData = VF_GPUSCENE_GET_INTERMEDIATES(Input);
|
|
Intermediates.InvNonUniformScale = GetInstanceData(Intermediates).InvNonUniformScale;
|
|
Intermediates.DeterminantSign = GetInstanceData(Intermediates).DeterminantSign;
|
|
Intermediates.LocalToWorld = GetInstanceData(Intermediates).LocalToWorld;
|
|
Intermediates.WorldToLocal = GetInstanceData(Intermediates).WorldToLocal;
|
|
Intermediates.PrevLocalToWorld = GetInstanceData(Intermediates).PrevLocalToWorld;
|
|
Intermediates.UnpackedPosition = UnpackedPosition(Input);
|
|
|
|
#if GPUSKIN_APEX_CLOTH
|
|
if (GPUSkinAPEXClothVF.bEnabled)
|
|
{
|
|
SetupClothVertex(Input.VertexID, Intermediates.ClothVertexInfluences);
|
|
if( IsSimulatedVertex(Intermediates.ClothVertexInfluences[0]) )
|
|
{
|
|
Intermediates.SimulatedPosition = ClothingPosition(Intermediates.ClothVertexInfluences, false);
|
|
#if GPUSKIN_APEX_CLOTH_PREVIOUS
|
|
Intermediates.PreviousSimulatedPosition = ClothingPosition(Intermediates.ClothVertexInfluences, true);
|
|
#else
|
|
Intermediates.PreviousSimulatedPosition = Intermediates.SimulatedPosition;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
Intermediates.PreviousSimulatedPosition = Intermediates.SimulatedPosition = float4(Intermediates.UnpackedPosition, 0.0f);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
Intermediates.BlendMatrix = CalcBoneMatrix( Input );
|
|
|
|
// Fill TangentToLocal
|
|
Intermediates.TangentToLocal = SkinTangents(Input, Intermediates);
|
|
|
|
// Swizzle vertex color.
|
|
Intermediates.Color = Input.Color FCOLOR_COMPONENT_SWIZZLE;
|
|
|
|
return Intermediates;
|
|
}
|
|
|
|
/**
|
|
* Get the 3x3 tangent basis vectors for this vertex factory
|
|
*
|
|
* @param Input - vertex input stream structure
|
|
* @return 3x3 matrix
|
|
*/
|
|
float3x3 VertexFactoryGetTangentToLocal( FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
|
|
{
|
|
return Intermediates.TangentToLocal;
|
|
}
|
|
|
|
float3 VertexFactoryGetInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
|
|
{
|
|
float3 SkinnedPosition = SkinPosition(Input, Intermediates);
|
|
#if GPUSKIN_APEX_CLOTH
|
|
if (GPUSkinAPEXClothVF.bEnabled && IsSimulatedVertex(Intermediates.ClothVertexInfluences[0]))
|
|
{
|
|
// Transform simulated position from cloth space into local space and blend with skinned position
|
|
float4 TransformedSimulatedPos = mul(float4(Intermediates.SimulatedPosition.xyz, 1), GPUSkinAPEXClothVF.ClothToLocal);
|
|
SkinnedPosition = lerp(SkinnedPosition, TransformedSimulatedPos.xyz, GPUSkinAPEXClothVF.ClothBlendWeight * Intermediates.SimulatedPosition.w);
|
|
}
|
|
#endif
|
|
return SkinnedPosition;
|
|
}
|
|
|
|
float4 VertexFactoryGetWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
|
|
{
|
|
return TransformLocalToTranslatedWorld(VertexFactoryGetInstanceSpacePosition(Input, Intermediates), Intermediates.LocalToWorld);
|
|
}
|
|
|
|
float4 VertexFactoryGetRasterizedWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float4 InWorldPosition)
|
|
{
|
|
return InWorldPosition;
|
|
}
|
|
|
|
float3 VertexFactoryGetPositionForVertexLighting(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float3 TranslatedWorldPosition)
|
|
{
|
|
return TranslatedWorldPosition;
|
|
}
|
|
|
|
void CalcTangentToWorld(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, out float3 TangentToWorld0, out float4 TangentToWorld2)
|
|
{
|
|
float3x3 LocalToWorld = DFToFloat3x3(Intermediates.LocalToWorld);
|
|
|
|
// Remove scaling.
|
|
half3 InvScale = Intermediates.InvNonUniformScale;
|
|
LocalToWorld[0] *= InvScale.x;
|
|
LocalToWorld[1] *= InvScale.y;
|
|
LocalToWorld[2] *= InvScale.z;
|
|
|
|
float3x3 TangentToWorld = mul(Intermediates.TangentToLocal, LocalToWorld);
|
|
|
|
TangentToWorld0 = TangentToWorld[0];
|
|
TangentToWorld2 = float4(TangentToWorld[2], Input.TangentZ.w * Intermediates.DeterminantSign);
|
|
}
|
|
|
|
float3 VertexFactoryGetWorldNormal(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
|
|
{
|
|
float3 TangentToWorld0;
|
|
float4 TangentToWorld2;
|
|
CalcTangentToWorld(Input, Intermediates, TangentToWorld0, TangentToWorld2);
|
|
|
|
return TangentToWorld2.xyz;
|
|
}
|
|
|
|
FVertexFactoryInterpolantsVSToPS VertexFactoryGetInterpolantsVSToPS(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, FMaterialVertexParameters VertexParameters)
|
|
{
|
|
FVertexFactoryInterpolantsVSToPS Interpolants;
|
|
Interpolants = (FVertexFactoryInterpolantsVSToPS)0; // compiling the SetUV loop will fail if not completely initialized
|
|
|
|
#if NUM_TEX_COORD_INTERPOLATORS
|
|
float2 CustomizedUVs[NUM_TEX_COORD_INTERPOLATORS]; // = (float2[NUM_TEX_COORD_INTERPOLATORS])0; <- our FHlslParser used for RemoveUnusedOutputs doesn't seem to like this, so zero-initialize manually below.
|
|
// Why zero-initialize? Because material translator stores interpolants in an array of float2, packing them, but the material may have an odd number of interpolants (e.g. float3).
|
|
// In such a case GetCustomInterpolators() will omit writing the last component, leaving it uninitialized, hence the initialization below.
|
|
CustomizedUVs[NUM_TEX_COORD_INTERPOLATORS - 1] = float2(0, 0);
|
|
|
|
GetMaterialCustomizedUVs(VertexParameters, CustomizedUVs);
|
|
GetCustomInterpolators(VertexParameters, CustomizedUVs);
|
|
|
|
UNROLL
|
|
for (int CoordinateIndex = 0; CoordinateIndex < NUM_TEX_COORD_INTERPOLATORS; CoordinateIndex++)
|
|
{
|
|
SetUV(Interpolants, CoordinateIndex, CustomizedUVs[CoordinateIndex]);
|
|
}
|
|
#endif
|
|
|
|
Interpolants.TangentToWorld0.w = 0;
|
|
CalcTangentToWorld(Input, Intermediates, Interpolants.TangentToWorld0.xyz, Interpolants.TangentToWorld2);
|
|
#if USE_WORLDVERTEXNORMAL_CENTER_INTERPOLATION
|
|
Interpolants.TangentToWorld2_Center = Interpolants.TangentToWorld2;
|
|
#endif
|
|
|
|
#if INTERPOLATE_VERTEX_COLOR
|
|
Interpolants.Color = Intermediates.Color;
|
|
#endif
|
|
|
|
#if VF_USE_PRIMITIVE_SCENE_DATA
|
|
Interpolants.PrimitiveId = Intermediates.SceneData.PrimitiveId;
|
|
#endif
|
|
|
|
return Interpolants;
|
|
}
|
|
|
|
// local position relative to instance
|
|
float3 VertexFactoryGetPreviousInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
|
|
{
|
|
float3 PrevSkinnedPosition = SkinPreviousPosition(Input, Intermediates);
|
|
#if GPUSKIN_APEX_CLOTH
|
|
#if GPUSKIN_APEX_CLOTH_PREVIOUS
|
|
if (GPUSkinAPEXClothVF.bEnabled && IsSimulatedVertex(Intermediates.ClothVertexInfluences[0]))
|
|
{
|
|
// Transform simulated position from cloth space into local space and blend with skinned position
|
|
float4 PrevTransformedSimulatedPos = mul(float4(Intermediates.PreviousSimulatedPosition.xyz, 1), GPUSkinAPEXClothVF.PreviousClothToLocal);
|
|
PrevSkinnedPosition = lerp(PrevSkinnedPosition, PrevTransformedSimulatedPos.xyz, GPUSkinAPEXClothVF.ClothBlendWeight * Intermediates.PreviousSimulatedPosition.w);
|
|
}
|
|
#endif // GPUSKIN_APEX_CLOTH_PREVIOUS
|
|
#endif // GPUSKIN_APEX_CLOTH
|
|
return PrevSkinnedPosition;
|
|
}
|
|
|
|
// @return previous translated world position
|
|
float4 VertexFactoryGetPreviousWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
|
|
{
|
|
float3 PrevSkinnedPosition = VertexFactoryGetPreviousInstanceSpacePosition(Input, Intermediates);
|
|
return TransformPreviousLocalPositionToTranslatedWorld(PrevSkinnedPosition, Intermediates.PrevLocalToWorld);
|
|
}
|
|
|
|
|
|
#if NEEDS_VERTEX_FACTORY_INTERPOLATION
|
|
float2 VertexFactoryGetRayTracingTextureCoordinate( FVertexFactoryRayTracingInterpolants Interpolants )
|
|
{
|
|
#if NUM_MATERIAL_TEXCOORDS
|
|
return Interpolants.InterpolantsVSToPS.TexCoords[0].xy;
|
|
#else // #if NUM_MATERIAL_TEXCOORDS
|
|
return float2(0,0);
|
|
#endif // #if NUM_MATERIAL_TEXCOORDS
|
|
}
|
|
|
|
FVertexFactoryInterpolantsVSToPS VertexFactoryAssignInterpolants(FVertexFactoryRayTracingInterpolants Input)
|
|
{
|
|
return Input.InterpolantsVSToPS;
|
|
}
|
|
|
|
FVertexFactoryRayTracingInterpolants VertexFactoryGetRayTracingInterpolants(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, FMaterialVertexParameters VertexParameters)
|
|
{
|
|
FVertexFactoryRayTracingInterpolants Interpolants;
|
|
|
|
Interpolants.InterpolantsVSToPS = VertexFactoryGetInterpolantsVSToPS(Input, Intermediates, VertexParameters);
|
|
|
|
return Interpolants;
|
|
}
|
|
|
|
FVertexFactoryRayTracingInterpolants VertexFactoryInterpolate(FVertexFactoryRayTracingInterpolants a, float aInterp, FVertexFactoryRayTracingInterpolants b, float bInterp)
|
|
{
|
|
// Default initialize. Otherwise, some graphics pipelines that
|
|
// couple tessellation with geometry shaders won't write to all TEXCOORD semantics,
|
|
// but read from them when <FVertexFactoryRayTracingInterpolants> is being copied as a whole.
|
|
FVertexFactoryRayTracingInterpolants O = (FVertexFactoryRayTracingInterpolants)0;
|
|
|
|
INTERPOLATE_MEMBER(InterpolantsVSToPS.TangentToWorld0.xyz);
|
|
INTERPOLATE_MEMBER(InterpolantsVSToPS.TangentToWorld2);
|
|
#if INTERPOLATE_VERTEX_COLOR
|
|
INTERPOLATE_MEMBER(InterpolantsVSToPS.Color);
|
|
#endif
|
|
|
|
#if NUM_TEX_COORD_INTERPOLATORS
|
|
UNROLL
|
|
// TexCoords in VSToPS are a float4 array with 2 coords packed together
|
|
for(int tc = 0; tc < (NUM_TEX_COORD_INTERPOLATORS+1)/2; ++tc)
|
|
{
|
|
INTERPOLATE_MEMBER(InterpolantsVSToPS.TexCoords[tc]);
|
|
}
|
|
#endif
|
|
|
|
#if VF_USE_PRIMITIVE_SCENE_DATA
|
|
O.InterpolantsVSToPS.PrimitiveId = a.InterpolantsVSToPS.PrimitiveId;
|
|
#endif
|
|
|
|
return O;
|
|
}
|
|
#endif // #if NEEDS_VERTEX_FACTORY_INTERPOLATION
|
|
|
|
float4 VertexFactoryGetTranslatedPrimitiveVolumeBounds(FVertexFactoryInterpolantsVSToPS Interpolants)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
uint VertexFactoryGetPrimitiveId(FVertexFactoryInterpolantsVSToPS Interpolants)
|
|
{
|
|
#if VF_USE_PRIMITIVE_SCENE_DATA
|
|
return Interpolants.PrimitiveId;
|
|
#else // VF_USE_PRIMITIVE_SCENE_DATA
|
|
return 0;
|
|
#endif // VF_USE_PRIMITIVE_SCENE_DATA
|
|
}
|
|
|
|
#include "VertexFactoryDefaultInterface.ush"
|