Files
UnrealEngine/Engine/Plugins/Experimental/Animation/RelativeIKOp/Source/RelativeBodyAnimUtils/Private/RelativeBodyAnimModifier.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

510 lines
21 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "RelativeBodyAnimModifier.h"
#include "AnimPose.h"
#include "MeshDescription.h"
#include "RelativeBodyAnimNotifies.h"
#include "RelativeBodyUtils.h"
#include "SkeletalMeshAttributes.h"
#include "Animation/AnimSequence.h"
#include "Components/SkeletalMeshComponent.h"
#include "Engine/SkeletalMesh.h"
#include "Misc/ScopedSlowTask.h"
#include "PhysicsEngine/PhysicsAsset.h"
#include "PhysicsEngine/SkeletalBodySetup.h"
#include "Rendering/SkeletalMeshRenderData.h"
#include "Spatial/PointHashGrid3.h"
#define LOCTEXT_NAMESPACE "RelativeBodyAnimModifier"
void URelativeBodyAnimModifier::OnApply_Implementation(UAnimSequence* InAnimation)
{
// TODO: Make log category for relative body utils?
if (InAnimation == nullptr)
{
UE_LOG(LogAnimation, Error, TEXT("Cannot create RelativeBodyAnimNotify. Reason: Invalid Animation"));
return;
}
if (!SkeletalMeshAsset)
{
UE_LOG(LogAnimation, Error, TEXT("Cannot create RelativeBodyAnimNotify. Reason: Invalid SkeletalMeshComponent"));
return;
}
if (!SkeletalMeshAsset->CloneMeshDescription(LODIndex, MeshDescription))
{
UE_LOG(LogAnimation, Error, TEXT("Cannot create RelativeBodyAnimNotify. Reason: Could not clone mesh description"));
return;
}
if (!NotifyClass || NotifyClass == URelativeBodyAnimNotifyBase::StaticClass())
{
UE_LOG(LogAnimation, Error, TEXT("Cannot create RelativeBodyAnimNotify. Reason: Must specify valid relative body notify subclass to create"));
return;
}
if (!PhysicsAssetOverride)
{
UE_LOG(LogAnimation, Warning, TEXT("RelativeBodyAnimModifier: No physics asset override specified, using skeletal mesh physics asset!"))
}
//Cache
UPhysicsAsset* PhysicsAsset = GetPhysicsAsset();
FReferenceSkeleton& RefSkeleton = SkeletalMeshAsset->GetRefSkeleton();
// TODO: Move cached data out of modifier (or check if it's outdated and only rebuild in that case)
CacheBodyDataForSourceMesh(CachedBodySourceData);
const TArray<int32>& BodyIndicesParentBodyIndices = CachedBodySourceData.BodyIndicesParentBodyIndices;
const TArray<bool>& BodyIndicesToIgnore = CachedBodySourceData.BodyIndicesToIgnore;
const TArray<bool>& IsDomainBodyIndices = CachedBodySourceData.IsDomainBody;
const TArray<int32>& BodyIndicesToBoneIndices = CachedBodySourceData.BodyIndicesToSourceBoneIndices;
const TArray<int32>& BoneIndicesToBodyIndices = CachedBodySourceData.SourceBoneIndicesToBodyIndices;
const TArray<TArray<int32>>& VertexIndicesInfluencedByBodyIndices = CachedBodySourceData.SourceVertexIndicesInfluencedByBodyIndices;
const int32 NumBodies = BodyIndicesToBoneIndices.Num();
const float HashGridCellSize = std::min(1.5f*ContactThreshold, 50.f); // the length of the cell size in the point hash gri
// Process animation asset
{
const FAnimPoseEvaluationOptions AnimPoseEvalOptions{
EAnimDataEvalType::Raw,
true,
false,
false,
nullptr,
true,
false
};
const float SequenceLength = InAnimation->GetPlayLength();
const float SampleStep = 1.0f / static_cast<float>(SampleRate);
const int SampleNum = FMath::TruncToInt(SequenceLength / SampleStep);
URelativeBodyBakeAnimNotify* BakedAnimNotifyInfo = nullptr;
bool bUseDenseNotifyInfo = NotifyClass->IsChildOf(URelativeBodyBakeAnimNotify::StaticClass());
if (bUseDenseNotifyInfo)
{
FName TrackName("RelBody-<" + SkeletalMeshAsset->GetName() + ">");
UE_LOG(LogAnimation, Verbose, TEXT("TrackName %d: '%s'."), GeneratedNotifyTracks.Num()-1, *TrackName.ToString());
const bool bDoesTrackNameAlreadyExist = UAnimationBlueprintLibrary::IsValidAnimNotifyTrackName(InAnimation, TrackName);
if (!bDoesTrackNameAlreadyExist)
{
UAnimationBlueprintLibrary::AddAnimationNotifyTrack(InAnimation, TrackName, FLinearColor::MakeRandomColor());
GeneratedNotifyTracks.Add(TrackName);
}
BakedAnimNotifyInfo = Cast<URelativeBodyBakeAnimNotify>(UAnimationBlueprintLibrary::AddAnimationNotifyEvent(InAnimation, TrackName, 0., NotifyClass));
if (!BakedAnimNotifyInfo)
{
return;
}
for (int32 BodyIndex1 = 0; BodyIndex1 < NumBodies; ++BodyIndex1)
{
if (BodyIndicesToIgnore[BodyIndex1])
{
continue;
}
for (int32 BodyIndex2 = BodyIndex1 + 1; BodyIndex2 < NumBodies; ++BodyIndex2)
{
if (BodyIndicesToIgnore[BodyIndex2]) continue;
if (BodyIndicesParentBodyIndices[BodyIndex1] == BodyIndex2 || BodyIndicesParentBodyIndices[BodyIndex2] == BodyIndex1) continue;
if (IsDomainBodyIndices[BodyIndex1]&&IsDomainBodyIndices[BodyIndex2]) continue;
int32 QueryBodyIndex = VertexIndicesInfluencedByBodyIndices[BodyIndex1].Num() < VertexIndicesInfluencedByBodyIndices[BodyIndex2].Num() ? BodyIndex1 : BodyIndex2;
int32 HashBodyIndex = VertexIndicesInfluencedByBodyIndices[BodyIndex1].Num() < VertexIndicesInfluencedByBodyIndices[BodyIndex2].Num() ? BodyIndex2 : BodyIndex1;
bool IsParentDominates = false;
if (IsDomainBodyIndices[BodyIndex1])
{
QueryBodyIndex = BodyIndex2;
HashBodyIndex = BodyIndex1;
IsParentDominates = true;
}
else if (IsDomainBodyIndices[BodyIndex2])
{
QueryBodyIndex = BodyIndex1;
HashBodyIndex = BodyIndex2;
IsParentDominates = true;
}
else if (VertexIndicesInfluencedByBodyIndices[BodyIndex1].Num() < VertexIndicesInfluencedByBodyIndices[BodyIndex2].Num())
{
QueryBodyIndex = BodyIndex1;
HashBodyIndex = BodyIndex2;
}
else
{
QueryBodyIndex = BodyIndex2;
HashBodyIndex = BodyIndex1;
}
FName BoneName1 = RefSkeleton.GetBoneName(BodyIndicesToBoneIndices[HashBodyIndex]);
FName BoneName2 = RefSkeleton.GetBoneName(BodyIndicesToBoneIndices[QueryBodyIndex]);
// FName BodyPairName("<" + SkeletalMeshAsset->GetName() + ">" + BoneName1.ToString() + "-" + BoneName2.ToString());
BakedAnimNotifyInfo->BodyPairs.Add(BoneName1);
BakedAnimNotifyInfo->BodyPairs.Add(BoneName2);
BakedAnimNotifyInfo->bBodyPairsIsParentDominates.Add(IsParentDominates);
}
}
BakedAnimNotifyInfo->BodyPairsLocalReference.SetNumZeroed(BakedAnimNotifyInfo->BodyPairs.Num()*SampleNum);
BakedAnimNotifyInfo->BodyPairsSampleTime.SetNumZeroed(SampleNum);
BakedAnimNotifyInfo->NumSamples = SampleNum;
}
FScopedSlowTask BakePairVertsTask((float)SampleNum, LOCTEXT("BakeVertsTaskText", "Baking Body Pair Verts..."));
BakePairVertsTask.MakeDialog();
// Get ground levels and max speed values.
TArray<FVector3f> VLocations;
for (int SampleIndex = 0; SampleIndex < SampleNum; ++SampleIndex)
{
BakePairVertsTask.EnterProgressFrame(1.0f);
const float SampleTime = FMath::Clamp(static_cast<float>(SampleIndex) * SampleStep, 0.0f, SequenceLength);
const float FutureSampleTime = FMath::Clamp((static_cast<float>(SampleIndex) + 1.0f) * SampleStep, 0.0f, SequenceLength);
FAnimPose AnimPose;
FAnimPose FutureAnimPose;
UAnimPoseExtensions::GetAnimPoseAtTime(InAnimation, SampleTime, AnimPoseEvalOptions, AnimPose);
UAnimPoseExtensions::GetAnimPoseAtTime(InAnimation, FutureSampleTime, AnimPoseEvalOptions, FutureAnimPose);
TArray<FMatrix44f> CacheToLocals;
GetRefToAnimPoseMatrices(CacheToLocals, AnimPose);
GetSkinnedVertices(VLocations, CacheToLocals);
// build a spatial hash grid
TArray<UE::Geometry::TPointHashGrid3f<int32>*> BodiesVertHash;
BodiesVertHash.SetNum(NumBodies);
size_t count = 0;
for (int32 BodyIndex = 0; BodyIndex < NumBodies; ++BodyIndex)
{
BodiesVertHash[BodyIndex] = new UE::Geometry::TPointHashGrid3f<int32>(HashGridCellSize, INDEX_NONE);
BodiesVertHash[BodyIndex]->Reserve(VertexIndicesInfluencedByBodyIndices[BodyIndex].Num());
for (int32 VertexIndex : VertexIndicesInfluencedByBodyIndices[BodyIndex])
{
BodiesVertHash[BodyIndex]->InsertPointUnsafe(VertexIndex, static_cast<FVector3f>(VLocations[VertexIndex]));
}
}
TArray<FName> ContactedBodyPairs;
TArray<FVector3f> ContactPoints;
ContactPoints.Empty();
if (BakedAnimNotifyInfo)
{
BakedAnimNotifyInfo->BodyPairsSampleTime[SampleIndex] = SampleTime;
}
for (int32 BodyIndex1 = 0; BodyIndex1 < NumBodies; ++BodyIndex1)
{
if (BodyIndicesToIgnore[BodyIndex1]) continue;
for (int32 BodyIndex2 = BodyIndex1 + 1; BodyIndex2 < NumBodies; ++BodyIndex2)
{
if (BodyIndicesToIgnore[BodyIndex2]) continue;
if (BodyIndicesParentBodyIndices[BodyIndex1] == BodyIndex2 || BodyIndicesParentBodyIndices[BodyIndex2] == BodyIndex1) continue;
if (IsDomainBodyIndices[BodyIndex1]&&IsDomainBodyIndices[BodyIndex2]) continue;
int32 QueryBodyIndex = VertexIndicesInfluencedByBodyIndices[BodyIndex1].Num() < VertexIndicesInfluencedByBodyIndices[BodyIndex2].Num() ? BodyIndex1 : BodyIndex2;
int32 HashBodyIndex = VertexIndicesInfluencedByBodyIndices[BodyIndex1].Num() < VertexIndicesInfluencedByBodyIndices[BodyIndex2].Num() ? BodyIndex2 : BodyIndex1;
bool IsParentDominates = false;
if (IsDomainBodyIndices[BodyIndex1])
{
QueryBodyIndex = BodyIndex2;
HashBodyIndex = BodyIndex1;
IsParentDominates = true;
}
else if (IsDomainBodyIndices[BodyIndex2])
{
QueryBodyIndex = BodyIndex1;
HashBodyIndex = BodyIndex2;
IsParentDominates = true;
}
else if (VertexIndicesInfluencedByBodyIndices[BodyIndex1].Num() < VertexIndicesInfluencedByBodyIndices[BodyIndex2].Num())
{
QueryBodyIndex = BodyIndex1;
HashBodyIndex = BodyIndex2;
}
else
{
QueryBodyIndex = BodyIndex2;
HashBodyIndex = BodyIndex1;
}
float MinDist = FLT_MAX;
int32 NearestHashVertexIndex = INDEX_NONE;
int32 NearestQueryVertexIndex = INDEX_NONE;
for (int32 QueryVertexIndex : VertexIndicesInfluencedByBodyIndices[QueryBodyIndex])
{
FVector3f QueryPoint = VLocations[QueryVertexIndex];
TPair<int32, float> AnyClosePoint = { INDEX_NONE, TNumericLimits<float>::Max() };
AnyClosePoint = BodiesVertHash[HashBodyIndex]->FindNearestInRadius(
QueryPoint,
ContactThreshold,
[&VLocations, QueryPoint](int32 VID)
{
return FVector3f::DistSquared(FVector3f(VLocations[VID]), QueryPoint);
});
if (AnyClosePoint.Value < MinDist)
{
NearestQueryVertexIndex = QueryVertexIndex;
NearestHashVertexIndex = AnyClosePoint.Key;
MinDist = AnyClosePoint.Value;
}
}
if (NearestQueryVertexIndex != INDEX_NONE)
{
FName BoneName1 = RefSkeleton.GetBoneName(BodyIndicesToBoneIndices[HashBodyIndex]);
FName BoneName2 = RefSkeleton.GetBoneName(BodyIndicesToBoneIndices[QueryBodyIndex]);
FVector3f ContactPoint1 = VLocations[NearestHashVertexIndex];
FVector3f ContactPoint2 = VLocations[NearestQueryVertexIndex];
ContactedBodyPairs.Add(BoneName1);
ContactedBodyPairs.Add(BoneName2);
FVector3f Loc1 = FRelativeBodyAnimUtils::CalcVertLocationInUnitBody(ContactPoint1, BoneName1, RefSkeleton, CacheToLocals, CachedBodySourceData.SourceRetargetGlobalPose, PhysicsAsset);
FVector3f Loc2 = FRelativeBodyAnimUtils::CalcVertLocationInUnitBody(ContactPoint2, BoneName2, RefSkeleton, CacheToLocals, CachedBodySourceData.SourceRetargetGlobalPose, PhysicsAsset);
ContactPoints.Add(Loc1);
ContactPoints.Add(Loc2);
// Use your custom notify class
if (!bUseDenseNotifyInfo)
{
FName TrackName("<" + SkeletalMeshAsset->GetName() + ">" + BoneName1.ToString() + "-" + BoneName2.ToString());
UE_LOG(LogAnimation, Verbose, TEXT("TrackName %d: '%s'."), GeneratedNotifyTracks.Num()-1, *TrackName.ToString());
const bool bDoesTrackNameAlreadyExist = UAnimationBlueprintLibrary::IsValidAnimNotifyTrackName(InAnimation, TrackName);
if (!bDoesTrackNameAlreadyExist)
{
UAnimationBlueprintLibrary::AddAnimationNotifyTrack(InAnimation, TrackName, FLinearColor::MakeRandomColor());
GeneratedNotifyTracks.Add(TrackName);
}
URelativeBodyPerFrameAnimNotify* NewNotify = Cast<URelativeBodyPerFrameAnimNotify>(UAnimationBlueprintLibrary::AddAnimationNotifyEvent(InAnimation, TrackName, SampleTime, NotifyClass));
if (NewNotify)
{
NewNotify->SetInfo(SkeletalMeshAsset,BoneName1,BoneName2,Loc1,Loc2,IsParentDominates);
}
}
else if (BakedAnimNotifyInfo)
{
size_t offset = SampleIndex*BakedAnimNotifyInfo->BodyPairs.Num();
BakedAnimNotifyInfo->BodyPairsLocalReference[offset+count*2+0] = Loc1;
BakedAnimNotifyInfo->BodyPairsLocalReference[offset+count*2+1] = Loc2;
}
}
count++;
}
}
}
}
}
void URelativeBodyAnimModifier::OnRevert_Implementation(UAnimSequence* InAnimation)
{
// Delete any generate tracks.
for (const FName GeneratedNotifyTrack : GeneratedNotifyTracks)
{
UAnimationBlueprintLibrary::RemoveAnimationNotifyTrack(InAnimation, GeneratedNotifyTrack);
}
GeneratedNotifyTracks.Reset();
}
UPhysicsAsset* URelativeBodyAnimModifier::GetPhysicsAsset() const
{
if (PhysicsAssetOverride)
{
return PhysicsAssetOverride;
}
if (SkeletalMeshAsset)
{
return SkeletalMeshAsset->GetPhysicsAsset();
}
return nullptr;
}
void URelativeBodyAnimModifier::CacheBodyDataForSourceMesh(FRelativeBodySourceData& OutSourceData)
{
const int32 NumVertices = MeshDescription.Vertices().Num();
//Extract skin weights
UPhysicsAsset* PhysicsAsset = GetPhysicsAsset();
const FReferenceSkeleton& RefSkeleton = SkeletalMeshAsset->GetRefSkeleton();
FSkeletalMeshAttributes MeshAttribs(MeshDescription);
FSkinWeightsVertexAttributesRef VertexSkinWeights = MeshAttribs.GetVertexSkinWeights();
//CPU skinninged vertices
const int32 NumBones = MeshAttribs.GetNumBones();
OutSourceData.SourceVLocations.Empty();
OutSourceData.SourceVLocations.AddUninitialized(NumVertices);
OutSourceData.BodyIndicesToSourceBoneIndices.Empty();
OutSourceData.SourceBoneIndicesToBodyIndices.Init(INDEX_NONE, NumBones);
OutSourceData.SourceRetargetGlobalPose.Empty();
FRelativeBodyAnimUtils::GetRefGlobalPos(OutSourceData.SourceRetargetGlobalPose, RefSkeleton);
TArray<FMatrix44f> SourceCacheToLocals;
FRelativeBodyAnimUtils::GetRefPoseToLocalMatrices(SourceCacheToLocals, SkeletalMeshAsset, LODIndex, OutSourceData.SourceRetargetGlobalPose);
OutSourceData.SourceVLocations.Empty();
OutSourceData.SourceVLocations.AddUninitialized(NumVertices);
for (int32 VertexIndex = 0; VertexIndex < NumVertices; ++VertexIndex)
{
FVertexBoneWeights BoneWeights = VertexSkinWeights.Get(FVertexID(VertexIndex));
const int32 InfluenceCount = BoneWeights.Num();
OutSourceData.SourceVLocations[VertexIndex] = FVector3f(0, 0, 0);
int32 VertexMajorBoneIndex = INDEX_NONE;
float VertexMajorBoneWeight = 0.;
FName VertexMajorBoneName;
for (int32 InfluenceIndex = 0; InfluenceIndex < InfluenceCount; ++InfluenceIndex)
{
int32 InfluenceBoneIndex = BoneWeights[InfluenceIndex].GetBoneIndex();
float InfluenceBoneWeight = BoneWeights[InfluenceIndex].GetWeight();
FName InfluenceBoneName = RefSkeleton.GetBoneName(InfluenceBoneIndex);
UE_LOG(LogAnimation, Log, TEXT("Source %d: Bone '%s'[%f]."), VertexIndex, *InfluenceBoneName.ToString(), InfluenceBoneWeight);
if (InfluenceBoneWeight > VertexMajorBoneWeight)
{
VertexMajorBoneIndex = InfluenceBoneIndex;
VertexMajorBoneWeight = InfluenceBoneWeight;
VertexMajorBoneName = InfluenceBoneName;
}
// TODO: Is this necessary here? The ref/inv-ref should be identity transform
const FMatrix44f RefToLocal = SourceCacheToLocals[InfluenceBoneIndex];
OutSourceData.SourceVLocations[VertexIndex] += RefToLocal.TransformPosition(MeshDescription.GetVertexPosition(VertexIndex)) * InfluenceBoneWeight;
}
const int32 ParentBodyIndex = PhysicsAsset->FindBodyIndex(VertexMajorBoneName);
FName ParentBoneName;
if (ParentBodyIndex == INDEX_NONE)
ParentBoneName = FRelativeBodyAnimUtils::FindParentBodyBoneName(VertexMajorBoneName, RefSkeleton, PhysicsAsset);
else
ParentBoneName = PhysicsAsset->SkeletalBodySetups[ParentBodyIndex]->BoneName;
const int32 ParentBoneIndex = RefSkeleton.FindBoneIndex(ParentBoneName);
if (ParentBoneIndex == INDEX_NONE) continue;
if (OutSourceData.SourceBoneIndicesToBodyIndices[ParentBoneIndex] == INDEX_NONE)
{
OutSourceData.SourceBoneIndicesToBodyIndices[ParentBoneIndex] = OutSourceData.BodyIndicesToSourceBoneIndices.Num();
OutSourceData.BodyIndicesToSourceBoneIndices.Add(ParentBoneIndex);
TArray<int32>& VertexIndicesInfluenced = OutSourceData.SourceVertexIndicesInfluencedByBodyIndices.AddDefaulted_GetRef();
VertexIndicesInfluenced.Add(VertexIndex);
}
else {
OutSourceData.SourceVertexIndicesInfluencedByBodyIndices[OutSourceData.SourceBoneIndicesToBodyIndices[ParentBoneIndex]].Add(VertexIndex);
}
}
int32 NumBodies = OutSourceData.BodyIndicesToSourceBoneIndices.Num();
OutSourceData.BodyIndicesParentBodyIndices.Empty();
OutSourceData.BodyIndicesParentBodyIndices.SetNum(NumBodies);
OutSourceData.BodyIndicesToIgnore.Empty();
OutSourceData.IsDomainBody.Empty();
OutSourceData.BodyIndicesToIgnore.SetNum(NumBodies);
OutSourceData.IsDomainBody.SetNum(NumBodies);
for (int32 idx = 0; idx < NumBodies; ++idx)
{
FName BoneName = RefSkeleton.GetBoneName(OutSourceData.BodyIndicesToSourceBoneIndices[idx]);
FName ParentBoneName = FRelativeBodyAnimUtils::FindParentBodyBoneName(BoneName, RefSkeleton, PhysicsAsset);
int32 BodyIndicesParentBoneIndex = RefSkeleton.FindBoneIndex(ParentBoneName);
if (BodyIndicesParentBoneIndex != INDEX_NONE)
{
OutSourceData.BodyIndicesParentBodyIndices[idx] = OutSourceData.SourceBoneIndicesToBodyIndices[BodyIndicesParentBoneIndex];
}
else
{
OutSourceData.BodyIndicesParentBodyIndices[idx] = INDEX_NONE;
}
OutSourceData.BodyIndicesToIgnore[idx] = true;
OutSourceData.IsDomainBody[idx] = false;
UE_LOG(LogAnimation, Log, TEXT("%d: Bone '%s' [%d verts]."), idx, *BoneName.ToString(), OutSourceData.SourceVertexIndicesInfluencedByBodyIndices[idx].Num());
}
for (FName BodyName : DomainBodyNames)
{
int32 BodyIndex = PhysicsAsset->FindBodyIndex(BodyName);
if (BodyIndex != INDEX_NONE)
{
FName BoneName = PhysicsAsset->SkeletalBodySetups[BodyIndex]->BoneName;
int32 BoneIndex = RefSkeleton.FindBoneIndex(BoneName);
OutSourceData.BodyIndicesToIgnore[OutSourceData.SourceBoneIndicesToBodyIndices[BoneIndex]] = false;
OutSourceData.IsDomainBody[OutSourceData.SourceBoneIndicesToBodyIndices[BoneIndex]] = true;
}
}
for (FName BodyName : ContactBodyNames)
{
int32 BodyIndex = PhysicsAsset->FindBodyIndex(BodyName);
if (BodyIndex != INDEX_NONE)
{
FName BoneName = PhysicsAsset->SkeletalBodySetups[BodyIndex]->BoneName;
int32 BoneIndex = RefSkeleton.FindBoneIndex(BoneName);
OutSourceData.BodyIndicesToIgnore[OutSourceData.SourceBoneIndicesToBodyIndices[BoneIndex]] = false;
}
}
}
void URelativeBodyAnimModifier::GetRefToAnimPoseMatrices(TArray<FMatrix44f>& OutRefToPose, const FAnimPose& AnimPose) const
{
const FReferenceSkeleton& RefSkeleton = SkeletalMeshAsset->GetRefSkeleton();
const TArray<FMatrix44f>& RefBasesInvMatrix = SkeletalMeshAsset->GetRefBasesInvMatrix();
OutRefToPose.Init(FMatrix44f::Identity, RefBasesInvMatrix.Num());
const FSkeletalMeshLODRenderData& LOD = SkeletalMeshAsset->GetResourceForRendering()->LODRenderData[LODIndex];
const TArray<FBoneIndexType>* RequiredBoneSets[3] = { &LOD.ActiveBoneIndices, nullptr, NULL };
for (int32 RequiredBoneSetIndex = 0; RequiredBoneSets[RequiredBoneSetIndex] != NULL; RequiredBoneSetIndex++)
{
const TArray<FBoneIndexType>& RequiredBoneIndices = *RequiredBoneSets[RequiredBoneSetIndex];
for (int32 BoneIndex = 0; BoneIndex < RequiredBoneIndices.Num(); BoneIndex++)
{
const int32 ThisBoneIndex = RequiredBoneIndices[BoneIndex];
if (RefBasesInvMatrix.IsValidIndex(ThisBoneIndex))
{
FName ThisBoneName = RefSkeleton.GetBoneName(ThisBoneIndex);
const FTransform ThisBoneTransform = UAnimPoseExtensions::GetBonePose(AnimPose, ThisBoneName, EAnimPoseSpaces::World);
OutRefToPose[ThisBoneIndex] = static_cast<FMatrix44f>(ThisBoneTransform.ToMatrixWithScale());
}
}
}
for (int32 ThisBoneIndex = 0; ThisBoneIndex < OutRefToPose.Num(); ++ThisBoneIndex)
{
OutRefToPose[ThisBoneIndex] = RefBasesInvMatrix[ThisBoneIndex] * OutRefToPose[ThisBoneIndex];
}
}
void URelativeBodyAnimModifier::GetSkinnedVertices(TArray<FVector3f>& VLocations, const TArray<FMatrix44f>& RefToPoseMatrices)
{
const int32 NumVertices = MeshDescription.Vertices().Num();
VLocations.Empty();
VLocations.AddUninitialized(NumVertices);
//Extract skin weights
FReferenceSkeleton& RefSkeleton = SkeletalMeshAsset->GetRefSkeleton();
FSkeletalMeshAttributes MeshAttribs(MeshDescription);
FSkinWeightsVertexAttributesRef VertexSkinWeights = MeshAttribs.GetVertexSkinWeights();
//CPU skinned vertices
const int32 NumBones = MeshAttribs.GetNumBones();
for (int32 VertexIndex = 0; VertexIndex < NumVertices; ++VertexIndex)
{
FVertexBoneWeights BoneWeights = VertexSkinWeights.Get(FVertexID(VertexIndex));
const int32 InfluenceCount = BoneWeights.Num();
VLocations[VertexIndex] = FVector3f(0, 0, 0);
for (int32 InfluenceIndex = 0; InfluenceIndex < InfluenceCount; ++InfluenceIndex)
{
int32 InfluenceBoneIndex = BoneWeights[InfluenceIndex].GetBoneIndex();
float InfluenceBoneWeight = BoneWeights[InfluenceIndex].GetWeight();
const FMatrix44f& RefToPose = RefToPoseMatrices[InfluenceBoneIndex];
VLocations[VertexIndex] += RefToPose.TransformPosition(MeshDescription.GetVertexPosition(VertexIndex)) * InfluenceBoneWeight;
}
}
}
#undef LOCTEXT_NAMESPACE