// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "../Common.ush" #include "../ViewData.ush" #include "../SplineMeshCommon.ush" #include "NaniteSceneCommon.ush" #include "NaniteAttributeDecode.ush" #include "NaniteSceneCommon.ush" #include "NaniteVertexFetch.ush" #include "../Matrices.ush" #define NANITE_VOXEL_FAST_INVERSE 0 // TODO: Assumes orthonormal. Not generally safe float3x3 VoxelInverse(float3x3 M) { #if NANITE_VOXEL_FAST_INVERSE return transpose(M); #else return Inverse(M); #endif } // Represents vertex data for a Nanite mesh in local space, post-deformation (when applicable) // This includes fixed function deformation from splines and skinning, but no material specific deformation like WPO or displacement. struct FNanitePostDeformVertex { // Index of the vertex in the cluster uint VertIndex; // Post-deformed position of the vertex float3 Position; float3 PrevPosition; // 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; // Vertex color float4 Color; // Texture coordinates float2 TexCoords[NANITE_MAX_UVS]; }; struct FReconstructedVoxelData { float3 LocalPosition; float3 PostDeformPosition; float3 PrevPostDeformPosition; uint VertIndex; float4x3 SkinningTransform; bool bActiveSkinning; }; FNanitePostDeformVertex DeformLocalNaniteVoxelVertex(FReconstructedVoxelData VoxelData, FNaniteLocalVertex Input) { FNanitePostDeformVertex Output; Output.VertIndex = Input.VertIndex; Output.TangentBasis = MakeTangentBasis(Input.RawAttributeData); Output.SplineDist = 0.0f; Output.PointLocal = VoxelData.LocalPosition; Output.Position = VoxelData.PostDeformPosition; Output.PrevPosition = VoxelData.PrevPostDeformPosition; Output.PreSkinnedNormal = Output.TangentBasis.TangentZ; Output.Color = Input.RawAttributeData.Color; Output.TexCoords = Input.RawAttributeData.TexCoords; #if USE_SKINNING BRANCH if(VoxelData.bActiveSkinning) { Output.TangentBasis.TangentZ = mul(Output.TangentBasis.TangentZ, (float3x3)VoxelData.SkinningTransform); Output.TangentBasis.TangentXAndSign.xyz = mul(Output.TangentBasis.TangentXAndSign.xyz, (float3x3)VoxelData.SkinningTransform); } #endif return Output; } 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.PointLocal = Input.Position; Output.Position = Input.Position; Output.PrevPosition = Input.PrevPosition; Output.PreSkinnedNormal = Output.TangentBasis.TangentZ; Output.Color = Input.RawAttributeData.Color; Output.TexCoords = Input.RawAttributeData.TexCoords; #if USE_SKINNING bool bActiveSkinning = Cluster.bSkinning && InstanceViewData.bIsDeforming && Input.bEnableSkinning; BRANCH if ((PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_SKINNED_MESH) != 0 && bActiveSkinning) { FSkinningHeader SkinningHeader = LoadSkinningHeader(InstanceData.PrimitiveId); FBoneInfluenceHeader BoneInfluenceHeader = GetBoneInfluenceHeader(Cluster); float3 SkinnedPosition = float3(0.0f, 0.0f, 0.0f); float3 PrevSkinnedPosition = 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 uint BoneTransformIndex = SkinningHeader.TransformBufferOffset + InstanceData.SkinningData * SkinningHeader.MaxTransformCount + BoneIndex; const float4x3 BoneTransform = LoadSkinningBoneTransform(BoneTransformIndex); 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; // Skin the prev position, too (NOTE: this should DCE unless base pass velocity is enabled) const uint PrevBoneTransformIndex = BoneTransformIndex + SkinningHeader.MaxTransformCount; const float4x3 PrevBoneTransform = LoadSkinningBoneTransform(PrevBoneTransformIndex); PrevSkinnedPosition += mul(float4(Input.PrevPosition, 1.0f), PrevBoneTransform) * BoneWeight; } Output.TangentBasis.TangentZ = SkinnedNormal; Output.TangentBasis.TangentXAndSign.xyz = SkinnedTangent; #if 0 Output.TangentBasis.RecalculateTangentX(); #endif Output.Position = SkinnedPosition; Output.PrevPosition = PrevSkinnedPosition; } #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 ); // We don't currently support proper velocity with moving splines. Would require previous frame's spline mesh params here. Output.PrevPosition = Output.Position; } #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(PrimitiveData, InstanceData, InstanceViewData, Cluster, VisibleCluster, VertIndex, CompileTimeMaxTexCoords)); } template void FetchAndDeformLocalNaniteVerts( FPrimitiveSceneData PrimitiveData, FInstanceSceneData InstanceData, FInstanceViewData InstanceViewData, FCluster Cluster, FVisibleCluster VisibleCluster, uint3 VertIndexes, uint CompileTimeMaxTexCoords, inout FNanitePostDeformVertex OutVerts[N]) { FNaniteLocalVertex InVerts[N]; FetchLocalNaniteVerts(PrimitiveData, InstanceData, InstanceViewData, Cluster, VisibleCluster, VertIndexes, CompileTimeMaxTexCoords, InVerts); UNROLL for(uint i = 0; i < N; ++i) { OutVerts[i] = DeformLocalNaniteVertex(PrimitiveData, InstanceData, InstanceViewData, Cluster, InVerts[i]); } } void SampleVoxelPerBrickSkinningTransforms(FInstanceSceneData InstanceData, FSkinningHeader SkinningHeader, FBrick Brick, inout float4x3 OutSkinningTransform, inout float4x3 OutPrevSkinningTransform) { // TODO: Implement support for multiple influences per brick? OutSkinningTransform = LoadSkinningBoneTransform(SkinningHeader.TransformBufferOffset + (InstanceData.SkinningData * SkinningHeader.MaxTransformCount) + Brick.BoneIndex); OutPrevSkinningTransform = LoadSkinningBoneTransform(SkinningHeader.TransformBufferOffset + (InstanceData.SkinningData * SkinningHeader.MaxTransformCount) + SkinningHeader.MaxTransformCount + Brick.BoneIndex); } float4x3 SampleVoxelPerBrickSkinningTransform(FInstanceSceneData InstanceData, FSkinningHeader SkinningHeader, FBrick Brick) { float4x3 SkinningTransform, PrevSkinningTransform; SampleVoxelPerBrickSkinningTransforms(InstanceData, SkinningHeader, Brick, SkinningTransform, PrevSkinningTransform); return SkinningTransform; } void SampleVoxelPerClusterSkinningTransforms(FInstanceSceneData InstanceData, FCluster Cluster, FSkinningHeader SkinningHeader, inout float4x3 OutSkinningTransform, inout float4x3 OutPrevSkinningTransform) { #if NANITE_MAX_VOXEL_ANIMATION_BONE_INFLUENCES > 1 OutSkinningTransform = (float4x3)0; OutPrevSkinningTransform = (float4x3)0; LOOP for (uint InfluenceIndex = 0; InfluenceIndex < Cluster.NumClusterBoneInfluences; ++InfluenceIndex) { FBoneInfluence Influence = DecodeVoxelBoneInfluence(Cluster, InfluenceIndex); const uint BoneTransformIndex = SkinningHeader.TransformBufferOffset + (InstanceData.SkinningData * SkinningHeader.MaxTransformCount) + Influence.BoneIndex; const float4x3 BoneTransform = LoadSkinningBoneTransform(BoneTransformIndex); OutSkinningTransform += BoneTransform * Influence.Weight; const uint PrevBoneTransformIndex = BoneTransformIndex + SkinningHeader.MaxTransformCount; const float4x3 PrevBoneTransform = LoadSkinningBoneTransform(PrevBoneTransformIndex); OutPrevSkinningTransform += PrevBoneTransform * Influence.Weight; } #else FBoneInfluence Influence = DecodeVoxelBoneInfluence(Cluster, 0); const uint BoneTransformIndex = SkinningHeader.TransformBufferOffset + (InstanceData.SkinningData * SkinningHeader.MaxTransformCount) + Influence.BoneIndex; OutSkinningTransform = LoadSkinningBoneTransform(BoneTransformIndex); const uint PrevBoneTransformIndex = BoneTransformIndex + SkinningHeader.MaxTransformCount; OutPrevSkinningTransform = LoadSkinningBoneTransform(PrevBoneTransformIndex); #endif } float4x3 SampleVoxelPerClusterSkinningTransform(FInstanceSceneData InstanceData, FCluster Cluster, FSkinningHeader SkinningHeader) { float4x3 SkinningTransform, PrevSkinningTransform; SampleVoxelPerClusterSkinningTransforms(InstanceData, Cluster, SkinningHeader, SkinningTransform, PrevSkinningTransform); return SkinningTransform; } void SkinClusterBounds(FCluster Cluster, FInstanceSceneData InstanceData, FSkinningHeader 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 = LoadSkinningBoneTransform(SkinningHeader.TransformBufferOffset + (InstanceData.SkinningData * SkinningHeader.MaxTransformCount) + 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; } float4x4 Orthogonalize(float4x4 Input) { float4 X = Input[0]; float4 Y = Input[1]; float4 Z = Input[2]; // Modified Gram-Schmidt orthogonalization Y.xyz -= dot(Y.xyz, X.xyz) / dot(X.xyz, X.xyz) * X.xyz; Z.xyz -= dot(Z.xyz, X.xyz) / dot(X.xyz, X.xyz) * X.xyz; Z.xyz -= dot(Z.xyz, Y.xyz) / dot(Y.xyz, Y.xyz) * Y.xyz; return float4x4(X, Y, Z, Input[3]); } float4x4 SkinNaniteHierarchyAssemblyTransform(FPrimitiveSceneData PrimitiveData, FInstanceSceneData InstanceData, uint AssemblyTransformIndex, float4x4 AssemblyTransform, bool bPrevTransform) { #if NANITE_ASSEMBLY_DATA const uint AssemblyTransformOffset = PrimitiveData.NaniteAssemblyTransformOffset; const uint NumTransforms = PrimitiveData.NaniteAssemblyTransformCount; const FNaniteAssemblyBoneInfluenceHeader Header = DecodeHierarchyAssemblyTransformBoneInfluenceHeader(AssemblyTransformOffset, NumTransforms, AssemblyTransformIndex); if (Header.NumInfluences > 0) { const FSkinningHeader SkinningHeader = LoadSkinningHeader(InstanceData.PrimitiveId); const uint SkinningBufferOffset = SkinningHeader.TransformBufferOffset + (InstanceData.SkinningData + (bPrevTransform ? 1u : 0u)) * SkinningHeader.MaxTransformCount; float4x3 SkinningTransform; if (Header.NumInfluences == 1) { // Fast path: single bone influence SkinningTransform = LoadSkinningBoneTransform(SkinningBufferOffset + Header.SingleBoneIndex); } else { // Multiple influences SkinningTransform = (float4x3)0; for (uint InfluenceIndex = 0; InfluenceIndex < Header.NumInfluences; ++InfluenceIndex) { const FBoneInfluence Influence = DecodeHierarchyAssemblyTransformBoneInfluence(Header.InfluenceBufferOffset, InfluenceIndex); SkinningTransform += LoadSkinningBoneTransform(SkinningBufferOffset + Influence.BoneIndex) * Influence.Weight; } } // multiply and remove shear AssemblyTransform = Orthogonalize(mul(AssemblyTransform, float4x4( float4(SkinningTransform[0], 0.f), float4(SkinningTransform[1], 0.f), float4(SkinningTransform[2], 0.f), float4(SkinningTransform[3], 1.f) ))); } #endif return AssemblyTransform; } ENCODED_VELOCITY_TYPE CalculateNaniteVelocity(FNaniteView NaniteView, FInstanceSceneData InstanceData, FCluster Cluster, FVisibleCluster VisibleCluster, float4 SvPosition, uint TriIndex, uint PrimitiveFlags, bool bWPOEnabled) { #if VELOCITY_EXPORT const bool bOutputVelocity = !bWPOEnabled && (PrimitiveFlags & PRIMITIVE_SCENE_DATA_FLAG_OUTPUT_VELOCITY) != 0; if (!bOutputVelocity) { return (ENCODED_VELOCITY_TYPE)0; } FInstanceDynamicData InstanceDynamicData = CalculateInstanceDynamicData(NaniteView, InstanceData); // 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? const bool bActiveSkinning = Cluster.bSkinning && GetInstanceViewData(InstanceData.InstanceId, NaniteView.SceneRendererPrimaryViewId).bIsDeforming; const FDFVector3 WorldPos = SvPositionToWorld(SvPosition); const float3 PostDeformPos = DFMultiplyDemote(WorldPos, InstanceData.WorldToLocal); float3 PrevPostDeformPos = PostDeformPos; #if USE_SKINNING BRANCH if ((PrimitiveFlags & PRIMITIVE_SCENE_DATA_FLAG_SKINNED_MESH) != 0 && bActiveSkinning) { const bool bIsAssemblyPart = IsAssemblyPartCluster(VisibleCluster); BRANCH if (bIsAssemblyPart) { const float4x4 InvAssemblyTransform = InverseNaniteAssemblyTransform(LoadNaniteAssemblyTransform(VisibleCluster.AssemblyTransformIndex)); const float4x4 PrevAssemblyTransform = LoadNaniteAssemblyTransform(VisibleCluster.AssemblyTransformIndex + 1); const float3 LocalPos = mul(float4(PostDeformPos, 1.0f), InvAssemblyTransform).xyz; PrevPostDeformPos = mul(float4(LocalPos, 1.0f), PrevAssemblyTransform).xyz; } else { const FSkinningHeader SkinningHeader = LoadSkinningHeader(InstanceData.PrimitiveId); const FBoneInfluenceHeader BoneInfluenceHeader = GetBoneInfluenceHeader(Cluster); BRANCH if (Cluster.bVoxel) { const FBrick Brick = DecodeBrick(Cluster, TriIndex); float4x3 LocalToSkinned, PrevLocalToSkinned; SampleVoxelPerBrickSkinningTransforms(InstanceData, SkinningHeader, Brick, LocalToSkinned, PrevLocalToSkinned); const float3x3 SkinnedToLocal3x3 = VoxelInverse(float3x3(LocalToSkinned[0], LocalToSkinned[1], LocalToSkinned[2])); const float3 LocalPos = mul(PostDeformPos - LocalToSkinned[3], SkinnedToLocal3x3); PrevPostDeformPos = mul(float4(LocalPos, 1.0f), PrevLocalToSkinned).xyz; } else { const uint3 TriIndices = DecodeTriangleIndices(Cluster, TriIndex); float3 PointLocal[3]; PointLocal[0] = DecodePosition(TriIndices[0], Cluster); PointLocal[1] = DecodePosition(TriIndices[1], Cluster); PointLocal[2] = DecodePosition(TriIndices[2], Cluster); 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); 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] = LoadSkinningBoneTransform(SkinningHeader.TransformBufferOffset + (InstanceData.SkinningData * SkinningHeader.MaxTransformCount) + BoneIndex0); CurrentBoneTransform[1] = LoadSkinningBoneTransform(SkinningHeader.TransformBufferOffset + (InstanceData.SkinningData * SkinningHeader.MaxTransformCount) + BoneIndex1); CurrentBoneTransform[2] = LoadSkinningBoneTransform(SkinningHeader.TransformBufferOffset + (InstanceData.SkinningData * SkinningHeader.MaxTransformCount) + 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; } float3 PointWorld0 = mul(float4(CurrentPosition[0], 1), InstanceDynamicData.LocalToTranslatedWorld).xyz; float3 PointWorld1 = mul(float4(CurrentPosition[1], 1), InstanceDynamicData.LocalToTranslatedWorld).xyz; float3 PointWorld2 = mul(float4(CurrentPosition[2], 1), InstanceDynamicData.LocalToTranslatedWorld).xyz; if ((PrimitiveFlags & PRIMITIVE_SCENE_DATA_FLAG_IS_FIRST_PERSON) != 0) { PointWorld0 = mul(PointWorld0, NaniteView.FirstPersonTransform); PointWorld1 = mul(PointWorld1, NaniteView.FirstPersonTransform); PointWorld2 = mul(PointWorld2, NaniteView.FirstPersonTransform); } 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); PrevPostDeformPos = 0.0f; // Intentionally split prev transforms into separate loop to increase occupancy. // This does mean we have to fetch the bone influences again, but it seems to be well worth it in practice. // TODO: Reevaluate on more triangle-heavy content. 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 PreviousBoneTransform[3]; PreviousBoneTransform[0] = LoadSkinningBoneTransform(SkinningHeader.TransformBufferOffset + SkinningHeader.MaxTransformCount + (InstanceData.SkinningData * SkinningHeader.MaxTransformCount) + BoneIndex0); PreviousBoneTransform[1] = LoadSkinningBoneTransform(SkinningHeader.TransformBufferOffset + SkinningHeader.MaxTransformCount + (InstanceData.SkinningData * SkinningHeader.MaxTransformCount) + BoneIndex1); PreviousBoneTransform[2] = LoadSkinningBoneTransform(SkinningHeader.TransformBufferOffset + SkinningHeader.MaxTransformCount + (InstanceData.SkinningData * SkinningHeader.MaxTransformCount) + BoneIndex2); PrevPostDeformPos += mul(float4(PointLocal[0], 1.0f), PreviousBoneTransform[0]) * (BoneWeight0 * Barycentrics.Value.x); PrevPostDeformPos += mul(float4(PointLocal[1], 1.0f), PreviousBoneTransform[1]) * (BoneWeight1 * Barycentrics.Value.y); PrevPostDeformPos += mul(float4(PointLocal[2], 1.0f), PreviousBoneTransform[2]) * (BoneWeight2 * Barycentrics.Value.z); } } } } #endif float3 WorldPosPrev = mul(float4(PrevPostDeformPos, 1), InstanceDynamicData.PrevLocalToTranslatedWorld).xyz; if ((PrimitiveFlags & PRIMITIVE_SCENE_DATA_FLAG_IS_FIRST_PERSON) != 0) { WorldPosPrev = mul(WorldPosPrev, NaniteView.PrevFirstPersonTransform); } const float4 ScreenPosPrev = mul(float4(WorldPosPrev, 1), NaniteView.PrevTranslatedWorldToClip); const float4 ScreenPos = SvPositionToScreenPosition(SvPosition); 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 } // TODO: Find a better place for this? FReconstructedVoxelData ReconstructVoxel(FNaniteView NaniteView, FPrimitiveSceneData PrimitiveData, FInstanceSceneData InstanceData, FInstanceViewData InstanceViewData, FVisibleCluster VisibleCluster, FCluster Cluster, float4 SvPosition, uint BrickIndex) { // TODO Share with USES_DISPLACEMENT float4x4 TranslatedWorldToLocal = DFFastToTranslatedWorld( InstanceData.WorldToLocal, NaniteView.PreViewTranslation ); float4 PixelTranslatedWorld = mul( float4( SvPosition.xyz, 1.0f ), NaniteView.SVPositionToTranslatedWorld ); float4 PixelLocal = mul( PixelTranslatedWorld, TranslatedWorldToLocal ); float3 LocalPos = PixelLocal.xyz / PixelLocal.w; FBrick Brick = DecodeBrick(Cluster, BrickIndex); const bool bActiveSkinning = Cluster.bSkinning && InstanceViewData.bIsDeforming && ((PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_SKINNED_MESH)) != 0; const bool bIsAssemblyPart = IsAssemblyPartCluster(VisibleCluster); FReconstructedVoxelData VoxelData; VoxelData.PostDeformPosition = LocalPos; VoxelData.PrevPostDeformPosition = LocalPos; #if USE_SKINNING VoxelData.bActiveSkinning = bActiveSkinning && !bIsAssemblyPart; // TODO: Nanite-Assemblies: Assembly part skinning float4x3 PrevSkinningTransform; BRANCH if (VoxelData.bActiveSkinning) { const FSkinningHeader SkinningHeader = LoadSkinningHeader(InstanceData.PrimitiveId); SampleVoxelPerBrickSkinningTransforms(InstanceData, SkinningHeader, Brick, VoxelData.SkinningTransform, PrevSkinningTransform); const float3x3 InvSkinningTransform3x3 = VoxelInverse(float3x3(VoxelData.SkinningTransform[0], VoxelData.SkinningTransform[1], VoxelData.SkinningTransform[2])); LocalPos = mul(LocalPos - VoxelData.SkinningTransform[3], InvSkinningTransform3x3); VoxelData.PrevPostDeformPosition = mul(float4(LocalPos, 1), PrevSkinningTransform); } BRANCH if (bIsAssemblyPart && bActiveSkinning) { const float4x4 AssemblyTransform = LoadNaniteAssemblyTransform(VisibleCluster.AssemblyTransformIndex); const float4x4 PrevAssemblyTransform = LoadNaniteAssemblyTransform(VisibleCluster.AssemblyTransformIndex + 1); const float3 PartLocalPos = mul(LocalPos - AssemblyTransform[3].xyz, (float3x3)InverseNaniteAssemblyTransform(AssemblyTransform)); // TODO: Nanite-Assemblies: Assembly part skinning //PrevAssemblyTransform = mul(PrevSkinningTransform, PrevAssemblyTransform); VoxelData.PrevPostDeformPosition = mul(float4(PartLocalPos, 1), PrevAssemblyTransform).xyz; } #else VoxelData.bActiveSkinning = false; #endif const float3 LocalVoxelPos = LocalPos.xyz / ( Cluster.LODError ); const int3 FlooredVoxelPos = floor( LocalVoxelPos ); const uint3 Voxel = uint3( FlooredVoxelPos - Brick.StartPos ); const uint VoxelIndex = Voxel.x + Voxel.y * 4 + Voxel.z * 16; const uint Mask = (1u << ( VoxelIndex & 31 )) - 1u; const uint2 BrickBits = reversebits( Brick.ReverseBrickBits.yx ); //TODO: Fix up logic to work on reversed bits directly uint VertIndex = Brick.VertOffset; VertIndex += countbits( BrickBits.x & ( VoxelIndex < 32 ? Mask : ~0u ) ); VertIndex += countbits( BrickBits.y & ( VoxelIndex < 32 ? 0 : Mask ) ); VoxelData.VertIndex = VertIndex; #if 1 // Just return ray intersection (faster) VoxelData.LocalPosition = LocalPos; #else // Return center of voxel (stable, but slower) VoxelData.LocalPosition = (FlooredVoxelPos + 0.5f) * Cluster.LODError; if (VoxelData.bActiveSkinning) { VoxelData.PostDeformPosition = mul(float4(VoxelData.LocalPosition, 1.0f), VoxelData.SkinningTransform); VoxelData.PrevPostDeformPosition = mul(float4(VoxelData.LocalPosition, 1.0f), PrevSkinningTransform); } else { VoxelData.PostDeformPosition = VoxelData.PrevPostDeformPosition = VoxelData.LocalPosition; } #endif return VoxelData; }