536 lines
21 KiB
HLSL
536 lines
21 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "../Common.ush"
|
|
#include "../ViewData.ush"
|
|
#include "../SplineMeshCommon.ush"
|
|
#include "NaniteSceneCommon.ush"
|
|
#include "NaniteAttributeDecode.ush"
|
|
#include "NaniteSceneCommon.ush"
|
|
#include "NaniteVertexFetch.ush"
|
|
#include "../Matrices.ush"
|
|
|
|
// Represents vertex data for a Nanite mesh in local space, post-deformation (when applicable)
|
|
struct FNanitePostDeformVertex
|
|
{
|
|
// Index of the vertex in the cluster
|
|
uint VertIndex;
|
|
|
|
// Post-deformed position of the vertex
|
|
float3 Position;
|
|
|
|
// Decoded vertex position (BEFORE deformation)
|
|
float3 PointLocal;
|
|
|
|
// Vertex normal (BEFORE deformation)
|
|
float3 PreSkinnedNormal;
|
|
|
|
// Post-deformed tangent basis of the vertex
|
|
FNaniteTangentBasis TangentBasis;
|
|
|
|
// Normalized distance along the spline (spline meshes only)
|
|
half SplineDist;
|
|
|
|
// Decoded vertex attribute data
|
|
FNaniteRawAttributeData RawAttributeData;
|
|
};
|
|
|
|
FNanitePostDeformVertex DeformLocalNaniteVertex(FPrimitiveSceneData PrimitiveData, FInstanceSceneData InstanceData, FInstanceViewData InstanceViewData, FCluster Cluster, FNaniteLocalVertex Input)
|
|
{
|
|
FNanitePostDeformVertex Output;
|
|
Output.VertIndex = Input.VertIndex;
|
|
Output.TangentBasis = MakeTangentBasis(Input.RawAttributeData);
|
|
Output.SplineDist = 0.0f;
|
|
Output.RawAttributeData = Input.RawAttributeData;
|
|
Output.PointLocal = Input.Position;
|
|
Output.Position = Input.Position;
|
|
Output.PreSkinnedNormal = Output.TangentBasis.TangentZ;
|
|
|
|
#if USE_SKINNING
|
|
bool bActiveSkinning = Cluster.bSkinning && InstanceViewData.bIsDeforming;
|
|
BRANCH
|
|
if ((PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_SKINNED_MESH) != 0 && bActiveSkinning)
|
|
{
|
|
FNaniteSkinningHeader SkinningHeader = LoadNaniteSkinningHeader(InstanceData.PrimitiveId);
|
|
FBoneInfluenceHeader BoneInfluenceHeader = GetBoneInfluenceHeader(Cluster);
|
|
|
|
float3 SkinnedPosition = float3(0.0f, 0.0f, 0.0f);
|
|
float3 SkinnedNormal = float3(0.0f, 0.0f, 0.0f);
|
|
float3 SkinnedTangent = float3(0.0f, 0.0f, 0.0f);
|
|
|
|
LOOP
|
|
for (uint InfluenceIndex = 0; InfluenceIndex < BoneInfluenceHeader.NumVertexBoneInfluences; ++InfluenceIndex)
|
|
{
|
|
uint BoneIndex = 0;
|
|
float BoneWeight = 0.0f;
|
|
DecodeVertexBoneInfluence(BoneInfluenceHeader, Input.VertIndex, InfluenceIndex, BoneIndex, BoneWeight);
|
|
|
|
const float4x3 BoneTransform = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneIndex);
|
|
SkinnedPosition += mul(float4(Input.Position, 1.0f), BoneTransform) * BoneWeight;
|
|
SkinnedNormal += mul(Output.TangentBasis.TangentZ, (float3x3)BoneTransform) * BoneWeight;
|
|
SkinnedTangent += mul(Output.TangentBasis.TangentXAndSign.xyz, (float3x3)BoneTransform) * BoneWeight;
|
|
}
|
|
|
|
Output.TangentBasis.TangentZ = SkinnedNormal;
|
|
Output.TangentBasis.TangentXAndSign.xyz = SkinnedTangent;
|
|
|
|
#if 0
|
|
Output.TangentBasis.RecalculateTangentX();
|
|
#endif
|
|
|
|
Output.Position = SkinnedPosition;
|
|
}
|
|
#endif
|
|
|
|
#if USE_SPLINEDEFORM
|
|
BRANCH
|
|
if ((PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_SPLINE_MESH) != 0 &&
|
|
(InstanceData.Flags & INSTANCE_SCENE_DATA_FLAG_HAS_PAYLOAD_EXTENSION) != 0)
|
|
{
|
|
// Deform the local position and tangent basis along the spline
|
|
// NOTE: Storing off the spline distance for use later when calculating tangent frame.
|
|
FSplineMeshShaderParams SplineMeshParams = SplineMeshLoadParamsFromInstancePayload(InstanceData);
|
|
Output.SplineDist = SplineMeshDeformLocalPosNormalTangent(
|
|
SplineMeshParams,
|
|
Output.Position,
|
|
Output.TangentBasis.TangentZ,
|
|
Output.TangentBasis.TangentXAndSign.xyz
|
|
);
|
|
}
|
|
#endif
|
|
|
|
return Output;
|
|
}
|
|
|
|
FNanitePostDeformVertex FetchAndDeformLocalNaniteVertex(FPrimitiveSceneData PrimitiveData, FInstanceSceneData InstanceData, FInstanceViewData InstanceViewData, FCluster Cluster, FVisibleCluster VisibleCluster, uint VertIndex, uint CompileTimeMaxTexCoords)
|
|
{
|
|
return DeformLocalNaniteVertex(
|
|
PrimitiveData,
|
|
InstanceData,
|
|
InstanceViewData,
|
|
Cluster,
|
|
FetchLocalNaniteVertex(InstanceData, Cluster, VisibleCluster, VertIndex, CompileTimeMaxTexCoords));
|
|
}
|
|
|
|
void FetchAndDeformLocalNaniteTriangle(
|
|
FPrimitiveSceneData PrimitiveData,
|
|
FInstanceSceneData InstanceData,
|
|
FInstanceViewData InstanceViewData,
|
|
FCluster Cluster,
|
|
FVisibleCluster VisibleCluster,
|
|
uint3 VertIndexes,
|
|
uint CompileTimeMaxTexCoords,
|
|
inout FNanitePostDeformVertex OutVerts[3])
|
|
{
|
|
FNaniteLocalVertex InVerts[3];
|
|
FetchLocalNaniteTriangle(InstanceData, Cluster, VisibleCluster, VertIndexes, CompileTimeMaxTexCoords, InVerts);
|
|
|
|
UNROLL_N(3)
|
|
for(uint i = 0; i < 3; ++i)
|
|
{
|
|
OutVerts[i] = DeformLocalNaniteVertex(PrimitiveData, InstanceData, InstanceViewData, Cluster, InVerts[i]);
|
|
}
|
|
}
|
|
|
|
float4x3 SampleSkinningTransform(FInstanceSceneData InstanceData, FNaniteSkinningHeader SkinningHeader, FBoneInfluenceHeader BoneInfluenceHeader, uint VertIndex)
|
|
{
|
|
float4x3 SkinningTransform4x3 = 0.0f;
|
|
LOOP
|
|
for (uint InfluenceIndex = 0; InfluenceIndex < BoneInfluenceHeader.NumVertexBoneInfluences; ++InfluenceIndex)
|
|
{
|
|
uint BoneIndex = 0;
|
|
float BoneWeight = 0.0f;
|
|
DecodeVertexBoneInfluence(BoneInfluenceHeader, VertIndex, InfluenceIndex, BoneIndex, BoneWeight);
|
|
|
|
const float4x3 BoneTransform = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneIndex);
|
|
SkinningTransform4x3 += BoneTransform * BoneWeight;
|
|
}
|
|
return SkinningTransform4x3;
|
|
}
|
|
|
|
float4x3 SampleVoxelSkinningTransform(FInstanceSceneData InstanceData, FCluster Cluster, FNaniteSkinningHeader SkinningHeader)
|
|
{
|
|
float4x3 SkinningTransform4x3 = 0.0f;
|
|
LOOP
|
|
for (uint InfluenceIndex = 0; InfluenceIndex < Cluster.NumClusterBoneInfluences; ++InfluenceIndex)
|
|
{
|
|
const uint PackedBoneInfluence = ClusterPageData.Load(Cluster.ClusterBoneInfluenceAddress + InfluenceIndex * Cluster.ClusterBoneInfluenceStride);
|
|
|
|
FVoxelBoneInfluence Influence;
|
|
Influence.BoneIndex = PackedBoneInfluence >> 8;
|
|
Influence.Weight = (PackedBoneInfluence & 0xFFu) * (1.0f / 255.0f);
|
|
|
|
const float4x3 BoneTransform = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + Influence.BoneIndex);
|
|
SkinningTransform4x3 += BoneTransform * Influence.Weight;
|
|
}
|
|
return SkinningTransform4x3;
|
|
}
|
|
|
|
#if NANITE_USE_PRECISE_SKINNING_BOUNDS
|
|
// TODO: Get rid of these silly helpers
|
|
// Axis 0...5 == -X, +X, -Y, +Y, -Z, +Z
|
|
void SetMinMaxValueByAxis(float Value, uint Axis, inout float3 MinPoint, inout float3 MaxPoint)
|
|
{
|
|
switch (Axis)
|
|
{
|
|
case 0: MinPoint.x = Value; break;
|
|
case 1: MaxPoint.x = Value; break;
|
|
case 2: MinPoint.y = Value; break;
|
|
case 3: MaxPoint.y = Value; break;
|
|
case 4: MinPoint.z = Value; break;
|
|
case 5: MaxPoint.z = Value; break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
float GetMinMaxValueByAxis(uint Axis, float3 MinPoint, float3 MaxPoint)
|
|
{
|
|
switch (Axis)
|
|
{
|
|
case 0: return MinPoint.x;
|
|
case 1: return MaxPoint.x;
|
|
case 2: return MinPoint.y;
|
|
case 3: return MaxPoint.y;
|
|
case 4: return MinPoint.z;
|
|
case 5: return MaxPoint.z;
|
|
default: return 0.f;
|
|
}
|
|
}
|
|
|
|
void SkinClusterBounds(FCluster Cluster, FInstanceSceneData InstanceData, FNaniteSkinningHeader SkinningHeader, inout float3 OutBoxCenter, inout float3 OutBoxExtent)
|
|
{
|
|
float3 MinBounds = float(999999999.0f).xxx;
|
|
float3 MaxBounds = float(-999999999.0f).xxx;
|
|
|
|
const uint IBI = 0xFFFFFFFFu;
|
|
uint ClusterBoneIndexes[NANITE_MAX_CLUSTER_BONE_INFLUENCES];
|
|
float ClusterBoneWeightMin[NANITE_MAX_CLUSTER_BONE_INFLUENCES];
|
|
float ClusterBoneWeightMax[NANITE_MAX_CLUSTER_BONE_INFLUENCES];
|
|
float3 ClusterBoneBoundsMin[NANITE_MAX_CLUSTER_BONE_INFLUENCES];
|
|
float3 ClusterBoneBoundsMax[NANITE_MAX_CLUSTER_BONE_INFLUENCES];
|
|
|
|
for (uint i = 0; i < NANITE_MAX_CLUSTER_BONE_INFLUENCES; i++)
|
|
{
|
|
ClusterBoneIndexes[i] = IBI;
|
|
ClusterBoneWeightMin[i] = 0.0f;
|
|
ClusterBoneWeightMax[i] = 0.0f;
|
|
ClusterBoneBoundsMin[i] = 0.0f;
|
|
ClusterBoneBoundsMax[i] = 0.0f;
|
|
}
|
|
|
|
// Load cooked data
|
|
for (uint i = 0; i < Cluster.NumClusterBoneInfluences; ++i)
|
|
{
|
|
FClusterBoneInfluence BoneInfluence = DecodeClusterBoneInfluence(Cluster, i);
|
|
ClusterBoneIndexes[i] = BoneInfluence.BoneIndex;
|
|
ClusterBoneWeightMin[i] = BoneInfluence.MinWeight;
|
|
ClusterBoneWeightMax[i] = BoneInfluence.MaxWeight;
|
|
ClusterBoneBoundsMin[i] = BoneInfluence.BoundMin;
|
|
ClusterBoneBoundsMax[i] = BoneInfluence.BoundMax;
|
|
}
|
|
|
|
// Deform each of the cluster bone bboxes by their associated bone transform.
|
|
uint ClusterBoneCount = 0;
|
|
for (uint ClusterBoneIndex = 0; ClusterBoneIndex < NANITE_MAX_CLUSTER_BONE_INFLUENCES; ++ClusterBoneIndex)
|
|
{
|
|
if (ClusterBoneIndexes[ClusterBoneIndex] == IBI)
|
|
{
|
|
break;
|
|
}
|
|
ClusterBoneCount++;
|
|
|
|
float3 BoneBoundsMin = ClusterBoneBoundsMin[ClusterBoneIndex];
|
|
float3 BoneBoundsMax = ClusterBoneBoundsMax[ClusterBoneIndex];
|
|
|
|
float3 BoneBoundsCorners[8] = {
|
|
float3(BoneBoundsMin.x, BoneBoundsMin.y, BoneBoundsMin.z),
|
|
float3(BoneBoundsMax.x, BoneBoundsMin.y, BoneBoundsMin.z),
|
|
float3(BoneBoundsMin.x, BoneBoundsMax.y, BoneBoundsMin.z),
|
|
float3(BoneBoundsMax.x, BoneBoundsMax.y, BoneBoundsMin.z),
|
|
float3(BoneBoundsMin.x, BoneBoundsMin.y, BoneBoundsMax.z),
|
|
float3(BoneBoundsMax.x, BoneBoundsMin.y, BoneBoundsMax.z),
|
|
float3(BoneBoundsMin.x, BoneBoundsMax.y, BoneBoundsMax.z),
|
|
float3(BoneBoundsMax.x, BoneBoundsMax.y, BoneBoundsMax.z)
|
|
};
|
|
|
|
uint BoneIndex = ClusterBoneIndexes[ClusterBoneIndex];
|
|
if (BoneIndex == IBI)
|
|
{
|
|
break;
|
|
}
|
|
float4x3 BoneTransform = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneIndex);
|
|
ClusterBoneBoundsMin[ClusterBoneIndex] = float(999999999.0f).xxx;
|
|
ClusterBoneBoundsMax[ClusterBoneIndex] = float(-999999999.0f).xxx;
|
|
|
|
for (uint CornerIndex = 0; CornerIndex < 8; ++CornerIndex)
|
|
{
|
|
float3 SkinnedClusterBoxCorner = mul(float4(BoneBoundsCorners[CornerIndex], 1.0f), BoneTransform);
|
|
|
|
ClusterBoneBoundsMin[ClusterBoneIndex] = min(ClusterBoneBoundsMin[ClusterBoneIndex], SkinnedClusterBoxCorner);
|
|
ClusterBoneBoundsMax[ClusterBoneIndex] = max(ClusterBoneBoundsMax[ClusterBoneIndex], SkinnedClusterBoxCorner);
|
|
}
|
|
}
|
|
|
|
for (uint Axis = 0; Axis < 6; ++Axis)
|
|
{
|
|
// Find the extrema and second extrema bone values for this axis based on their deformed bbox.
|
|
uint ExtremaIndex = IBI;
|
|
uint ExtremaIndex2 = IBI;
|
|
float ExtremaValue = (Axis % 2 == 0) ? 999999999.0f : -999999999.0f;
|
|
float ExtremaValue2 = (Axis % 2 == 0) ? 999999999.0f : -999999999.0f;
|
|
|
|
for (uint ClusterBoneIndex = 0; ClusterBoneIndex < NANITE_MAX_CLUSTER_BONE_INFLUENCES; ++ClusterBoneIndex)
|
|
{
|
|
if (ClusterBoneIndexes[ClusterBoneIndex] == IBI)
|
|
{
|
|
break;
|
|
}
|
|
|
|
float BBoxValue = GetMinMaxValueByAxis(Axis, ClusterBoneBoundsMin[ClusterBoneIndex], ClusterBoneBoundsMax[ClusterBoneIndex]);
|
|
|
|
if (Axis % 2 == 0) // Negative
|
|
{
|
|
if (BBoxValue < ExtremaValue)
|
|
{
|
|
if (ExtremaValue < ExtremaValue2)
|
|
{
|
|
ExtremaIndex2 = ExtremaIndex;
|
|
ExtremaValue2 = ExtremaValue;
|
|
}
|
|
|
|
ExtremaIndex = ClusterBoneIndex;
|
|
ExtremaValue = BBoxValue;
|
|
}
|
|
else if (BBoxValue < ExtremaValue2)
|
|
{
|
|
ExtremaIndex2 = ClusterBoneIndex;
|
|
ExtremaValue2 = BBoxValue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (BBoxValue > ExtremaValue)
|
|
{
|
|
if (ExtremaValue > ExtremaValue2)
|
|
{
|
|
ExtremaIndex2 = ExtremaIndex;
|
|
ExtremaValue2 = ExtremaValue;
|
|
}
|
|
|
|
ExtremaIndex = ClusterBoneIndex;
|
|
ExtremaValue = BBoxValue;
|
|
}
|
|
else if (BBoxValue > ExtremaValue2)
|
|
{
|
|
ExtremaIndex2 = ClusterBoneIndex;
|
|
ExtremaValue2 = BBoxValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
float SkinnedValue = ClusterBoneWeightMax[ExtremaIndex] * ExtremaValue;
|
|
|
|
if (ExtremaIndex2 != IBI)
|
|
{
|
|
float Weight2 = 1.0f - ClusterBoneWeightMax[ExtremaIndex];
|
|
for (uint ClusterBoneIndex = 0; ClusterBoneIndex < NANITE_MAX_CLUSTER_BONE_INFLUENCES; ++ClusterBoneIndex)
|
|
{
|
|
if (ClusterBoneIndexes[ClusterBoneIndex] == IBI)
|
|
{
|
|
break;
|
|
}
|
|
if (ClusterBoneIndex != ExtremaIndex && ClusterBoneIndex != ExtremaIndex2)
|
|
{
|
|
float MinWeight = ClusterBoneWeightMin[ClusterBoneIndex];
|
|
|
|
Weight2 -= MinWeight;
|
|
|
|
SkinnedValue += MinWeight * GetMinMaxValueByAxis(Axis, ClusterBoneBoundsMin[ClusterBoneIndex], ClusterBoneBoundsMax[ClusterBoneIndex]);
|
|
}
|
|
}
|
|
|
|
SkinnedValue += Weight2 * ExtremaValue2;
|
|
}
|
|
|
|
SetMinMaxValueByAxis(SkinnedValue, Axis, MinBounds, MaxBounds);
|
|
}
|
|
|
|
OutBoxCenter = (MaxBounds + MinBounds) * 0.5f;
|
|
OutBoxExtent = (MaxBounds - MinBounds) * 0.5f;
|
|
}
|
|
#else
|
|
void SkinClusterBounds(FCluster Cluster, FInstanceSceneData InstanceData, FNaniteSkinningHeader SkinningHeader, inout float3 BoxCenter, inout float3 BoxExtent)
|
|
{
|
|
float3 BoundsMin = 1e20f;
|
|
float3 BoundsMax = -1e20f;
|
|
|
|
for (uint i = 0; i < Cluster.NumClusterBoneInfluences; ++i)
|
|
{
|
|
const FClusterBoneInfluence BoneInfluence = DecodeClusterBoneInfluence(Cluster, i);
|
|
|
|
const float4x3 BoneTransform = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneInfluence.BoneIndex);
|
|
|
|
const float3 Center = mul(float4(BoxCenter, 1.0f), BoneTransform);
|
|
const float3 Delta = mul(BoxExtent, abs((float3x3)BoneTransform));
|
|
|
|
BoundsMin = min(BoundsMin, Center - Delta);
|
|
BoundsMax = max(BoundsMax, Center + Delta);
|
|
}
|
|
|
|
BoxCenter = (BoundsMax + BoundsMin) * 0.5f;
|
|
BoxExtent = (BoundsMax - BoundsMin) * 0.5f;
|
|
}
|
|
#endif
|
|
|
|
ENCODED_VELOCITY_TYPE CalculateNaniteVelocity(FNaniteView NaniteView, FInstanceSceneData InstanceData, FCluster Cluster, FVisibleCluster VisibleCluster, float4 SvPosition, uint TriIndex, uint PrimitiveFlags, bool bWPOEnabled)
|
|
{
|
|
#if VELOCITY_EXPORT
|
|
FInstanceDynamicData InstanceDynamicData = CalculateInstanceDynamicData(NaniteView, InstanceData);
|
|
|
|
const float4 ScreenPos = SvPositionToScreenPosition(SvPosition);
|
|
|
|
float4 ScreenPosPrev;
|
|
|
|
const bool bOutputVelocity = !bWPOEnabled && (PrimitiveFlags & PRIMITIVE_SCENE_DATA_FLAG_OUTPUT_VELOCITY) != 0;
|
|
if (!bOutputVelocity)
|
|
{
|
|
return (ENCODED_VELOCITY_TYPE)0;
|
|
}
|
|
|
|
#if USE_SKINNING
|
|
// TODO: Skipping this means the AA smears the foliage such that it looks like there's movement, is this a good idea or a bug?
|
|
bool bActiveSkinning = Cluster.bSkinning && GetInstanceViewData(InstanceData.InstanceId, NaniteView.SceneRendererPrimaryViewId).bIsDeforming;
|
|
|
|
BRANCH
|
|
if ((PrimitiveFlags & PRIMITIVE_SCENE_DATA_FLAG_SKINNED_MESH) != 0 && bActiveSkinning)
|
|
{
|
|
FNaniteSkinningHeader SkinningHeader = LoadNaniteSkinningHeader(InstanceData.PrimitiveId);
|
|
FBoneInfluenceHeader BoneInfluenceHeader = GetBoneInfluenceHeader(Cluster);
|
|
|
|
BRANCH
|
|
if (Cluster.bVoxel)
|
|
{
|
|
const FDFVector3 WorldPos = SvPositionToWorld(SvPosition);
|
|
const float3 SkinnedLocalPos = DFMultiplyDemote(WorldPos, InstanceData.WorldToLocal);
|
|
|
|
float4x3 LocalToSkinned = 0;
|
|
float4x3 PrevLocalToSkinned = 0;
|
|
|
|
uint VertIndex = 0;
|
|
#if NANITE_PER_VOXEL_BRICK_SKINNING
|
|
VertIndex = DecodeBrick(Cluster, TriIndex).VertOffset;
|
|
|
|
LOOP
|
|
for (uint InfluenceIndex = 0; InfluenceIndex < Cluster.NumVertexBoneInfluences; ++InfluenceIndex)
|
|
{
|
|
uint BoneIndex = 0;
|
|
float BoneWeight = 0.0f;
|
|
DecodeVertexBoneInfluence(BoneInfluenceHeader, VertIndex, InfluenceIndex, BoneIndex, BoneWeight);
|
|
const float4x3 BoneTransform = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneIndex);
|
|
const float4x3 PrevBoneTransform = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + SkinningHeader.MaxTransformCount + InstanceData.SkinningData + BoneIndex);
|
|
|
|
LocalToSkinned += BoneTransform * BoneWeight;
|
|
PrevLocalToSkinned += PrevBoneTransform * BoneWeight;
|
|
}
|
|
#else
|
|
LOOP
|
|
for (uint InfluenceIndex = 0; InfluenceIndex < Cluster.NumClusterBoneInfluences; ++InfluenceIndex)
|
|
{
|
|
FVoxelBoneInfluence BoneInfluence = DecodeVoxelBoneInfluence(Cluster, InfluenceIndex);
|
|
|
|
const float4x3 BoneTransform = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneInfluence.BoneIndex);
|
|
const float4x3 PrevBoneTransform = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + SkinningHeader.MaxTransformCount + InstanceData.SkinningData + BoneInfluence.BoneIndex);
|
|
LocalToSkinned += BoneTransform * BoneInfluence.Weight;
|
|
PrevLocalToSkinned += PrevBoneTransform * BoneInfluence.Weight;
|
|
}
|
|
#endif
|
|
|
|
const float3x3 SkinnedToLocal3x3 = Inverse(float3x3(LocalToSkinned[0], LocalToSkinned[1], LocalToSkinned[2]));
|
|
|
|
const float3 LocalPos = mul(SkinnedLocalPos - LocalToSkinned[3], SkinnedToLocal3x3);
|
|
const float3 PrevSkinnedLocalPos = mul(float4(LocalPos, 1.0f), PrevLocalToSkinned).xyz;
|
|
const float3 PrevWorldPos = mul(float4(PrevSkinnedLocalPos, 1), InstanceDynamicData.PrevLocalToTranslatedWorld).xyz;
|
|
ScreenPosPrev = mul(float4(PrevWorldPos, 1), NaniteView.PrevTranslatedWorldToClip);
|
|
}
|
|
else
|
|
{
|
|
const uint3 TriIndices = DecodeTriangleIndices(Cluster, TriIndex);
|
|
|
|
float3 PointLocal[3];
|
|
FetchLocalNaniteTrianglePositions(InstanceData, Cluster, VisibleCluster, TriIndices, PointLocal);
|
|
|
|
float3 CurrentPosition[3];
|
|
CurrentPosition[0] = float3(0.0f, 0.0f, 0.0f);
|
|
CurrentPosition[1] = float3(0.0f, 0.0f, 0.0f);
|
|
CurrentPosition[2] = float3(0.0f, 0.0f, 0.0f);
|
|
|
|
float3 PreviousPosition[3];
|
|
PreviousPosition[0] = float3(0.0f, 0.0f, 0.0f);
|
|
PreviousPosition[1] = float3(0.0f, 0.0f, 0.0f);
|
|
PreviousPosition[2] = float3(0.0f, 0.0f, 0.0f);
|
|
|
|
LOOP
|
|
for (uint InfluenceIndex = 0; InfluenceIndex < BoneInfluenceHeader.NumVertexBoneInfluences; ++InfluenceIndex)
|
|
{
|
|
uint BoneIndex0 = 0;
|
|
float BoneWeight0 = 0.0f;
|
|
DecodeVertexBoneInfluence(BoneInfluenceHeader, TriIndices.x, InfluenceIndex, BoneIndex0, BoneWeight0);
|
|
|
|
uint BoneIndex1 = 0;
|
|
float BoneWeight1 = 0.0f;
|
|
DecodeVertexBoneInfluence(BoneInfluenceHeader, TriIndices.y, InfluenceIndex, BoneIndex1, BoneWeight1);
|
|
|
|
uint BoneIndex2 = 0;
|
|
float BoneWeight2 = 0.0f;
|
|
DecodeVertexBoneInfluence(BoneInfluenceHeader, TriIndices.z, InfluenceIndex, BoneIndex2, BoneWeight2);
|
|
|
|
float4x3 CurrentBoneTransform[3];
|
|
CurrentBoneTransform[0] = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneIndex0);
|
|
CurrentBoneTransform[1] = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneIndex1);
|
|
CurrentBoneTransform[2] = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneIndex2);
|
|
|
|
float4x3 PreviousBoneTransform[3];
|
|
PreviousBoneTransform[0] = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + SkinningHeader.MaxTransformCount + InstanceData.SkinningData + BoneIndex0);
|
|
PreviousBoneTransform[1] = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + SkinningHeader.MaxTransformCount + InstanceData.SkinningData + BoneIndex1);
|
|
PreviousBoneTransform[2] = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + SkinningHeader.MaxTransformCount + InstanceData.SkinningData + BoneIndex2);
|
|
|
|
CurrentPosition[0] += mul(float4(PointLocal[0], 1.0f), CurrentBoneTransform[0]) * BoneWeight0;
|
|
CurrentPosition[1] += mul(float4(PointLocal[1], 1.0f), CurrentBoneTransform[1]) * BoneWeight1;
|
|
CurrentPosition[2] += mul(float4(PointLocal[2], 1.0f), CurrentBoneTransform[2]) * BoneWeight2;
|
|
|
|
PreviousPosition[0] += mul(float4(PointLocal[0], 1.0f), PreviousBoneTransform[0]) * BoneWeight0;
|
|
PreviousPosition[1] += mul(float4(PointLocal[1], 1.0f), PreviousBoneTransform[1]) * BoneWeight1;
|
|
PreviousPosition[2] += mul(float4(PointLocal[2], 1.0f), PreviousBoneTransform[2]) * BoneWeight2;
|
|
}
|
|
|
|
const float3 PointWorld0 = mul(float4(CurrentPosition[0], 1), InstanceDynamicData.LocalToTranslatedWorld).xyz;
|
|
const float3 PointWorld1 = mul(float4(CurrentPosition[1], 1), InstanceDynamicData.LocalToTranslatedWorld).xyz;
|
|
const float3 PointWorld2 = mul(float4(CurrentPosition[2], 1), InstanceDynamicData.LocalToTranslatedWorld).xyz;
|
|
|
|
const float4 PointClip0 = mul(float4(PointWorld0, 1), NaniteView.TranslatedWorldToClip);
|
|
const float4 PointClip1 = mul(float4(PointWorld1, 1), NaniteView.TranslatedWorldToClip);
|
|
const float4 PointClip2 = mul(float4(PointWorld2, 1), NaniteView.TranslatedWorldToClip);
|
|
|
|
// Calculate perspective correct barycentric coordinates with screen derivatives
|
|
const float2 PixelClip = (SvPosition.xy - NaniteView.ViewRect.xy) * NaniteView.ViewSizeAndInvSize.zw * float2(2, -2) + float2(-1, 1);
|
|
const FBarycentrics Barycentrics = CalculateTriangleBarycentrics(PixelClip, PointClip0, PointClip1, PointClip2, NaniteView.ViewSizeAndInvSize.zw);
|
|
|
|
const float3 PrevPointLocal = Lerp(PreviousPosition[0], PreviousPosition[1], PreviousPosition[2], Barycentrics).Value;
|
|
float3 PrevPointWorld = mul(float4(PrevPointLocal.xyz, 1), InstanceDynamicData.PrevLocalToTranslatedWorld).xyz;
|
|
ScreenPosPrev = mul(float4(PrevPointWorld, 1), NaniteView.PrevTranslatedWorldToClip);
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
const FDFVector3 WorldPos = SvPositionToWorld(SvPosition);
|
|
const float3 LocalPos = DFMultiplyDemote(WorldPos, InstanceData.WorldToLocal);
|
|
const float3 WorldPosPrev = mul(float4(LocalPos, 1), InstanceDynamicData.PrevLocalToTranslatedWorld).xyz;
|
|
ScreenPosPrev = mul(float4(WorldPosPrev, 1), NaniteView.PrevTranslatedWorldToClip);
|
|
}
|
|
|
|
const float3 Velocity = Calculate3DVelocity(ScreenPos, ScreenPosPrev);
|
|
return EncodeVelocityToTexture(Velocity, (PrimitiveFlags & PRIMITIVE_SCENE_DATA_FLAG_HAS_PIXEL_ANIMATION) != 0);
|
|
#else
|
|
return (ENCODED_VELOCITY_TYPE)0;
|
|
#endif
|
|
} |