Files
UnrealEngineUWP/Engine/Source/Developer/PhysicsUtilities/Private/SkinnedBoneTriangleCache.cpp
halfdan ingvarsson e2210cb024 Support for 16-bit skin weights on the skelmesh.
The main change is that FSoftSkinVertex, used by FSkeletalMeshLODModel, in now stores weights as 16-bit after conversion from the import data. This increases the size of each FSoftSkinVertex from 144 bytes to 160 bytes (about 10% increase). By default render meshes still use 8-bit skin weights, with weights downshifted from the 16-bit modeling data, so no change in GPU memory consumption there. However, the vertex buffer will automatically return a 16-bit skin weights when requested from the GPU side (e.g. for CPU skinning and viewing tangents).

This change in the model data and vertex buffer CPU-side query, resulted in many changes throughout the codebase and will have an effect on licensees who are actively reading from and writing to these two storage locations.

The GPU skin cache shader has had one more permutation added when not using unlimited skin weights. The vertex factory is not affected.

#jira UE-164386
#rb alexis.matte, josie.yang
#preflight 632c0c5ab4515b7e22b4804d

[CL 22215219 by halfdan ingvarsson in ue5-main branch]
2022-09-27 19:48:05 -04:00

168 lines
5.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SkinnedBoneTriangleCache.h"
#include "Components/SkeletalMeshComponent.h"
#include "Rendering/SkeletalMeshModel.h"
#include "Rendering/SkeletalMeshRenderData.h"
FSkinnedBoneTriangleCache::FSkinnedBoneTriangleCache(USkeletalMesh& InSkeletalMesh, const FPhysAssetCreateParams& Params)
: SkeletalMesh(InSkeletalMesh),
StaticLODModel(*SkeletalMesh.GetImportedModel()),
RenderData(*SkeletalMesh.GetResourceForRendering()),
VertexBuffer(RenderData.LODRenderData[0].StaticVertexBuffers.PositionVertexBuffer),
InfluenceHeuristic(Params.VertWeight),
BoneIndexToInfluencedVertices(),
BoneIndexToTriangles(),
LODModelIndexBufferInOrder()
{
}
void FSkinnedBoneTriangleCache::BuildCache()
{
BuildInfluencedIndexSetForEachBone();
BuildOwnedTrianglesSetForEachBone();
}
void FSkinnedBoneTriangleCache::GetVerticesAndIndicesForBone(const int32 BoneIndex, TArray<FVector3f>& OutVertexPositions, TArray<uint32>& OutIndices) const
{
OutVertexPositions.Empty();
OutIndices.Empty();
const FTriangleArray* TriangleArrayPointer = BoneIndexToTriangles.Find(BoneIndex);
if ( !TriangleArrayPointer )
{
return;
}
const FTriangleArray& TrianglesForBone = *TriangleArrayPointer;
const FMatrix ComponentToBoneMatrix = BoneTransformMatrix(BoneIndex);
TMap<FSkinnedVertexIndex, uint32> SkinnedVertIndexToOutputIndex;
for ( int32 CurrentIndex = 0; CurrentIndex < TrianglesForBone.Num(); ++CurrentIndex )
{
const FTriangleIndex TriangleIndex = TrianglesForBone[CurrentIndex];
check(BufferIndexForTri(TriangleIndex, 2) < static_cast<uint32>(LODModelIndexBufferInOrder.Num()));
for ( int32 TriangleVert = 0; TriangleVert < 3; ++TriangleVert )
{
const uint32 BufferIndex = BufferIndexForTri(TriangleIndex, TriangleVert);
const FSkinnedVertexIndex VertIndex = LODModelIndexBufferInOrder[BufferIndex];
// If we haven't seen this vertex before, we need to add it to our output positions.
if ( !SkinnedVertIndexToOutputIndex.Contains(VertIndex) )
{
OutVertexPositions.Add(VertexPosition(VertIndex, ComponentToBoneMatrix));
SkinnedVertIndexToOutputIndex.Add(VertIndex, static_cast<uint32>(OutVertexPositions.Num() - 1));
}
// Map the skinned vert index to the index in our output array.
OutIndices.Add(SkinnedVertIndexToOutputIndex[VertIndex]);
}
}
}
void FSkinnedBoneTriangleCache::BuildInfluencedIndexSetForEachBone()
{
const uint32 TotalVertices = VertexBuffer.GetNumVertices();
for ( FSkinnedVertexIndex VertIndex = 0; VertIndex < TotalVertices; ++VertIndex )
{
AddIndexToInfluencerBoneSets(VertIndex);
}
}
void FSkinnedBoneTriangleCache::AddIndexToInfluencerBoneSets(const FSkinnedVertexIndex VertIndex)
{
int32 SectionIndex;
int32 SoftVertIndex;
RenderData.LODRenderData[0].GetSectionFromVertexIndex(VertIndex, SectionIndex, SoftVertIndex);
const FSkelMeshSection& Section = StaticLODModel.LODModels[0].Sections[SectionIndex];
const FSoftSkinVertex& SoftVert = Section.SoftVertices[SoftVertIndex];
const uint8 MaxWeight = InfluenceHeuristic == EVW_DominantWeight ? SoftVert.GetMaximumWeight() : 0;
for (int32 InfluenceIndex = 0; InfluenceIndex < MAX_TOTAL_INFLUENCES; ++InfluenceIndex)
{
const uint16 InfluenceWeight = SoftVert.InfluenceWeights[InfluenceIndex];
if ( InfluenceHeuristic == EVW_DominantWeight )
{
if ( InfluenceWeight < MaxWeight )
{
continue;
}
}
else
{
if ( InfluenceWeight < 1 )
{
continue;
}
}
const FBoneIndexType BoneMapIndex = SoftVert.InfluenceBones[InfluenceIndex];
const int32 ActualBoneIndex = Section.BoneMap[BoneMapIndex];
FInfluencedVerticesSet& InfluencedVertexIndicesForBone = BoneIndexToInfluencedVertices.FindOrAdd(ActualBoneIndex);
InfluencedVertexIndicesForBone.Add(VertIndex);
}
}
void FSkinnedBoneTriangleCache::BuildOwnedTrianglesSetForEachBone()
{
LODModelIndexBufferInOrder.Empty();
RenderData.LODRenderData[0].MultiSizeIndexContainer.GetIndexBuffer(LODModelIndexBufferInOrder);
// We assume that each triplet of indices in the index buffer forms a triangle.
check(LODModelIndexBufferInOrder.Num() % 3 == 0);
const uint32 TotalTriangles = LODModelIndexBufferInOrder.Num() / 3;
for ( FTriangleIndex TriangleIndex = 0; TriangleIndex < TotalTriangles; ++TriangleIndex )
{
AddTriangleIndexToOwnerBoneSets(TriangleIndex);
}
}
void FSkinnedBoneTriangleCache::AddTriangleIndexToOwnerBoneSets(const FTriangleIndex TriangleIndex)
{
check(BufferIndexForTri(TriangleIndex, 2) < static_cast<uint32>(LODModelIndexBufferInOrder.Num()));
const FSkinnedVertexIndex TriangleVertices[3] =
{
LODModelIndexBufferInOrder[BufferIndexForTri(TriangleIndex, 0)],
LODModelIndexBufferInOrder[BufferIndexForTri(TriangleIndex, 1)],
LODModelIndexBufferInOrder[BufferIndexForTri(TriangleIndex, 2)]
};
for ( FBoneIndexToInfluencedVertices::TConstIterator ConstIt = BoneIndexToInfluencedVertices.CreateConstIterator(); ConstIt; ++ConstIt )
{
const FBoneIndex CurrentBoneIndex = ConstIt.Key();
const FInfluencedVerticesSet& InfluencedVerticesForBone = ConstIt.Value();
for (int32 TriangleVert = 0; TriangleVert < 3; ++TriangleVert)
{
if (InfluencedVerticesForBone.Contains(TriangleVertices[TriangleVert]))
{
FTriangleArray& TriangleArrayForBone = BoneIndexToTriangles.FindOrAdd(CurrentBoneIndex);
TriangleArrayForBone.Add(TriangleIndex);
break;
}
}
}
}
FVector3f FSkinnedBoneTriangleCache::VertexPosition(const FSkinnedVertexIndex VertIndex, const FMatrix& ComponentToBoneMatrix) const
{
const FVector3f& Position = VertexBuffer.VertexPosition(VertIndex);
return FVector4f(ComponentToBoneMatrix.TransformPosition((FVector)Position));
}
FMatrix FSkinnedBoneTriangleCache::BoneTransformMatrix(FBoneIndex BoneIndex) const
{
return FMatrix(SkeletalMesh.GetRefBasesInvMatrix()[BoneIndex]);
}