EM_Task/UnrealEd/Private/SkinnedBoneTriangleCache.cpp
Boshuang Zhao 5144a49c9b add
2026-02-13 16:18:33 +08:00

167 lines
6.3 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SkinnedBoneTriangleCache.h"
#include "Editor.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<FVector>& 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 uint8 InfluenceWeight = SoftVert.InfluenceWeights[InfluenceIndex];
if (InfluenceHeuristic == EVW_DominantWeight)
{
if (InfluenceWeight < MaxWeight)
{
continue;
}
}
else
{
if (InfluenceWeight < 1)
{
continue;
}
}
const uint8 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;
}
}
}
}
FVector FSkinnedBoneTriangleCache::VertexPosition(const FSkinnedVertexIndex VertIndex, const FMatrix& ComponentToBoneMatrix) const
{
const FVector Position = VertexBuffer.VertexPosition(VertIndex);
return ComponentToBoneMatrix.TransformPosition(Position);
}
FMatrix FSkinnedBoneTriangleCache::BoneTransformMatrix(FBoneIndex BoneIndex) const
{
return SkeletalMesh.GetRefBasesInvMatrix()[BoneIndex];
}