Files
UnrealEngine/Engine/Plugins/Animation/PoseSearch/Source/Editor/Private/PoseSearchDebuggerViewModel.cpp
2025-05-18 13:04:45 +08:00

250 lines
8.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "PoseSearchDebuggerViewModel.h"
#include "Animation/AnimComposite.h"
#include "Animation/AnimSequence.h"
#include "Animation/MirrorDataTable.h"
#include "Engine/SkeletalMesh.h"
#include "IAnimationProvider.h"
#include "IGameplayProvider.h"
#include "StructUtils/InstancedStruct.h"
#include "IRewindDebugger.h"
#include "PoseSearchDebuggerSettings.h"
#include "PoseSearch/PoseSearchDatabase.h"
#include "PoseSearch/PoseSearchDerivedData.h"
#include "PoseSearch/PoseSearchSchema.h"
#include "Trace/PoseSearchTraceProvider.h"
namespace UE::PoseSearch
{
FDebuggerViewModel::FDebuggerViewModel(uint64 InAnimInstanceId)
: AnimInstanceId(InAnimInstanceId)
{
}
FDebuggerViewModel::~FDebuggerViewModel()
{
for (TPair<uint64, TWeakObjectPtr<AActor>>& DebugDrawActorPair : DebugDrawActors)
{
if (DebugDrawActorPair.Value.IsValid())
{
DebugDrawActorPair.Value->Destroy();
}
}
}
const FTraceMotionMatchingStateMessage* FDebuggerViewModel::GetMotionMatchingState() const
{
if (MotionMatchingStates.IsValidIndex(ActiveMotionMatchingStateIdx))
{
return &MotionMatchingStates[ActiveMotionMatchingStateIdx];
}
return nullptr;
}
const UPoseSearchDatabase* FDebuggerViewModel::GetCurrentDatabase() const
{
if (const FTraceMotionMatchingStateMessage* State = GetMotionMatchingState())
{
return State->GetCurrentDatabase();
}
return nullptr;
}
int32 FDebuggerViewModel::GetNodesNum() const
{
return MotionMatchingStates.Num();
}
void FDebuggerViewModel::OnUpdate()
{
MotionMatchingStates.Reset();
// Get provider and validate
const TraceServices::IAnalysisSession* Session = RewindDebugger.Get()->GetAnalysisSession();
TraceServices::FAnalysisSessionReadScope SessionReadScope(*Session);
const FTraceProvider* PoseSearchProvider = Session->ReadProvider<FTraceProvider>(FTraceProvider::ProviderName);
const IAnimationProvider* AnimationProvider = Session->ReadProvider<IAnimationProvider>("AnimationProvider");
const IGameplayProvider* GameplayProvider = Session->ReadProvider<IGameplayProvider>("GameplayProvider");
if (!(PoseSearchProvider && AnimationProvider && GameplayProvider))
{
return;
}
const double TraceTime = RewindDebugger.Get()->CurrentTraceTime();
TraceServices::FFrame Frame;
ReadFrameProvider(*Session).GetFrameFromTime(TraceFrameType_Game, TraceTime, Frame);
PoseSearchProvider->EnumerateMotionMatchingStateTimelines(AnimInstanceId, [&Frame, this](const FTraceProvider::FMotionMatchingStateTimeline& InTimeline)
{
const FTraceMotionMatchingStateMessage* Message = nullptr;
InTimeline.EnumerateEvents(Frame.StartTime, Frame.EndTime, [&Message](double InStartTime, double InEndTime, const FTraceMotionMatchingStateMessage& InMessage)
{
Message = &InMessage;
return TraceServices::EEventEnumerate::Stop;
});
if (Message)
{
check(Message->Roles.Num() == Message->SkeletalMeshComponentIds.Num());
MotionMatchingStates.Add(*Message);
}
});
// adding all the missing DebugDrawActors, and tracking them via UsedDebugDrawActorIds
TSet<uint64> UsedDebugDrawActorIds;
for (const FTraceMotionMatchingStateMessage& MotionMatchingState : MotionMatchingStates)
{
for (uint64 SkeletalMeshComponentId : MotionMatchingState.SkeletalMeshComponentIds)
{
UsedDebugDrawActorIds.Add(SkeletalMeshComponentId);
bool bNeedToAdd = true;
if (TWeakObjectPtr<AActor>* DebugDrawActor = DebugDrawActors.Find(SkeletalMeshComponentId))
{
if (DebugDrawActor->IsValid())
{
bNeedToAdd = false;
}
}
if (bNeedToAdd)
{
UWorld* World = RewindDebugger.Get()->GetWorldToVisualize();
FActorSpawnParameters ActorSpawnParameters;
ActorSpawnParameters.bHideFromSceneOutliner = false;
ActorSpawnParameters.ObjectFlags |= RF_Transient;
AActor* DebugDrawActor = World->SpawnActor<AActor>(ActorSpawnParameters);
DebugDrawActor->SetActorLabel(TEXT("PoseSearch"));
UPoseSearchMeshComponent* DebugDrawMeshComponent = NewObject<UPoseSearchMeshComponent>(DebugDrawActor);
DebugDrawActor->AddInstanceComponent(DebugDrawMeshComponent);
DebugDrawMeshComponent->RegisterComponentWithWorld(World);
DebugDrawActors.Add(SkeletalMeshComponentId) = DebugDrawActor;
}
}
}
// cleaning up the no longer used actors (with a SkeletalMeshComponentId not in UsedDebugDrawActorIds)
for (auto DebugDrawActorsIt = DebugDrawActors.CreateIterator(); DebugDrawActorsIt; ++DebugDrawActorsIt)
{
if (!UsedDebugDrawActorIds.Find(DebugDrawActorsIt.Key()))
{
if (DebugDrawActorsIt.Value().IsValid())
{
DebugDrawActorsIt.Value()->Destroy();
}
DebugDrawActorsIt.RemoveCurrent();
}
}
/** No active motion matching state as no messages were read */
if (MotionMatchingStates.IsEmpty())
{
return;
}
for (TPair<uint64, TWeakObjectPtr<AActor>>& DebugDrawActorPair : DebugDrawActors)
{
const uint64 SkeletalMeshComponentId = DebugDrawActorPair.Key;
if (AActor* DebugDrawActor = DebugDrawActorPair.Value.Get())
{
for (UActorComponent* ActorComponent : DebugDrawActor->GetInstanceComponents())
{
if (UPoseSearchMeshComponent* PoseSearchMeshComponent = Cast<UPoseSearchMeshComponent>(ActorComponent))
{
AnimationProvider->ReadSkeletalMeshPoseTimeline(SkeletalMeshComponentId, [&Frame, AnimationProvider, GameplayProvider, PoseSearchMeshComponent](const IAnimationProvider::SkeletalMeshPoseTimeline& TimelineData, bool bHasCurves)
{
TimelineData.EnumerateEvents(Frame.StartTime, Frame.EndTime, [AnimationProvider, GameplayProvider, PoseSearchMeshComponent](double InStartTime, double InEndTime, uint32 InDepth, const FSkeletalMeshPoseMessage& PoseMessage) -> TraceServices::EEventEnumerate
{
const FSkeletalMeshInfo* SkeletalMeshInfo = AnimationProvider->FindSkeletalMeshInfo(PoseMessage.MeshId);
const FObjectInfo* SkeletalMeshObjectInfo = GameplayProvider->FindObjectInfo(PoseMessage.MeshId);
if (!SkeletalMeshInfo || !SkeletalMeshObjectInfo)
{
return TraceServices::EEventEnumerate::Stop;
}
USkeletalMesh* SkeletalMesh = TSoftObjectPtr<USkeletalMesh>(FSoftObjectPath(SkeletalMeshObjectInfo->PathName)).LoadSynchronous();
if (SkeletalMesh)
{
PoseSearchMeshComponent->SetSkinnedAssetAndUpdate(SkeletalMesh, true);
}
FTransform ComponentWorldTransform;
// Active skeleton is simply the traced bone transforms
TArray<FTransform>& ComponentSpaceTransforms = PoseSearchMeshComponent->GetEditableComponentSpaceTransforms();
AnimationProvider->GetSkeletalMeshComponentSpacePose(PoseMessage, *SkeletalMeshInfo, ComponentWorldTransform, ComponentSpaceTransforms);
check(ComponentWorldTransform.Equals(PoseMessage.ComponentToWorld));
PoseSearchMeshComponent->Initialize(ComponentWorldTransform);
return TraceServices::EEventEnumerate::Stop;
});
});
break;
}
}
}
}
}
void FDebuggerViewModel::OnUpdateSearchSelection(int32 InSearchId)
{
if (InSearchId != InvalidSearchId)
{
// Find node in all motion matching states this frame
ActiveMotionMatchingStateIdx = INDEX_NONE;
const int32 NodesNum = MotionMatchingStates.Num();
for (int32 i = 0; i < NodesNum; ++i)
{
if (MotionMatchingStates[i].GetSearchId() == InSearchId)
{
ActiveMotionMatchingStateIdx = i;
break;
}
}
}
}
void FDebuggerViewModel::SetVerbose(bool bVerbose)
{
UPoseSearchDebuggerConfig::Get().bIsVerbose = bVerbose;
}
bool FDebuggerViewModel::IsVerbose() const
{
return UPoseSearchDebuggerConfig::Get().bIsVerbose;
}
void FDebuggerViewModel::SetDrawQuery(bool bInDrawQuery)
{
UPoseSearchDebuggerConfig::Get().bDrawQuery = bInDrawQuery;
}
bool FDebuggerViewModel::GetDrawQuery() const
{
return UPoseSearchDebuggerConfig::Get().bDrawQuery;
}
void FDebuggerViewModel::SetDrawTrajectory(bool bInDrawTrajectory)
{
UPoseSearchDebuggerConfig::Get().bDrawTrajectory = bInDrawTrajectory;
}
bool FDebuggerViewModel::GetDrawTrajectory() const
{
return UPoseSearchDebuggerConfig::Get().bDrawTrajectory;
}
void FDebuggerViewModel::SetDrawHistory(bool bInDrawHistory)
{
UPoseSearchDebuggerConfig::Get().bDrawHistory = bInDrawHistory;
}
bool FDebuggerViewModel::GetDrawHistory() const
{
return UPoseSearchDebuggerConfig::Get().bDrawHistory;
}
} // namespace UE::PoseSearch