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

141 lines
5.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "PoseSearch/PoseSearchFeatureChannel_Curve.h"
#include "Engine/BlueprintGeneratedClass.h"
#include "PoseSearch/PoseSearchAssetIndexer.h"
#include "PoseSearch/PoseSearchAssetSampler.h"
#include "PoseSearch/PoseSearchContext.h"
#include "PoseSearch/PoseSearchHistory.h"
#include "PoseSearch/PoseSearchSchema.h"
#if WITH_EDITOR
#include "PropertyHandle.h"
#endif // WITH_EDITOR
UPoseSearchFeatureChannel_Curve::UPoseSearchFeatureChannel_Curve()
{
bUseBlueprintQueryOverride = Cast<UBlueprintGeneratedClass>(GetClass()) != nullptr;
}
bool UPoseSearchFeatureChannel_Curve::Finalize(UPoseSearchSchema* Schema)
{
ChannelDataOffset = Schema->SchemaCardinality;
ChannelCardinality = 1;
Schema->SchemaCardinality += ChannelCardinality;
CurveIdx = Schema->AddCurveReference(CurveName, SampleRole);
return true;
}
void UPoseSearchFeatureChannel_Curve::BuildQuery(UE::PoseSearch::FSearchContext& SearchContext) const
{
using namespace UE::PoseSearch;
float CurveValue = 0.0f;
if (bUseBlueprintQueryOverride)
{
if (const UAnimInstance* AnimInstance = Cast<UAnimInstance>(SearchContext.GetContext(SampleRole)->GetFirstObjectParam()))
{
CurveValue = BP_GetCurveValue(AnimInstance);
FFeatureVectorHelper::EncodeFloat(SearchContext.EditFeatureVector(), ChannelDataOffset, CurveValue);
}
else
{
// @todo: support non UAnimInstance anim contexts for AnimNext
UE_LOG(LogPoseSearch, Warning, TEXT("UPoseSearchFeatureChannel_Curve::BuildQuery - unsupported null UAnimInstance: WIP support for AnimNext!"));
}
return;
}
// trying to get the BuildQuery data from another schema UPoseSearchFeatureChannel_Curve already cached in the SearchContext
if (SearchContext.IsUseCachedChannelData())
{
// composing a unique identifier to specify this channel with all the required properties to be able to share the query data with other channels of the same type
uint32 UniqueIdentifier = GetClass()->GetUniqueID();
UniqueIdentifier = HashCombineFast(UniqueIdentifier, GetTypeHash(CurveName));
UniqueIdentifier = HashCombineFast(UniqueIdentifier, GetTypeHash(SampleRole));
UniqueIdentifier = HashCombineFast(UniqueIdentifier, GetTypeHash(CurveIdx));
UniqueIdentifier = HashCombineFast(UniqueIdentifier, GetTypeHash(SampleTimeOffset));
UniqueIdentifier = HashCombineFast(UniqueIdentifier, GetTypeHash(InputQueryPose));
TConstArrayView<float> CachedChannelData;
if (const UPoseSearchFeatureChannel* CachedChannel = SearchContext.GetCachedChannelData(UniqueIdentifier, this, CachedChannelData))
{
#if DO_CHECK
const UPoseSearchFeatureChannel_Curve* CachedCurveChannel = Cast<UPoseSearchFeatureChannel_Curve>(CachedChannel);
check(CachedCurveChannel);
check(CachedCurveChannel->GetChannelCardinality() == ChannelCardinality);
check(CachedChannelData.Num() == ChannelCardinality);
// making sure there were no hash collisions
check(CachedCurveChannel->CurveName == CurveName);
check(CachedCurveChannel->SampleRole == SampleRole);
check(CachedCurveChannel->CurveIdx == CurveIdx);
check(CachedCurveChannel->SampleTimeOffset == SampleTimeOffset);
check(CachedCurveChannel->InputQueryPose == InputQueryPose);
#endif //DO_CHECK
// copying the CachedChannelData into this channel portion of the FeatureVectorBuilder
FFeatureVectorHelper::Copy(SearchContext.EditFeatureVector().Slice(ChannelDataOffset, ChannelCardinality), 0, ChannelCardinality, CachedChannelData);
return;
}
}
const bool bCanUseCurrentResult = SearchContext.CanUseCurrentResult();
const bool bSkip = InputQueryPose != EInputQueryPose::UseCharacterPose && bCanUseCurrentResult;
if (bSkip || (!SearchContext.ArePoseHistoriesValid()))
{
if (bCanUseCurrentResult)
{
FFeatureVectorHelper::Copy(SearchContext.EditFeatureVector(), ChannelDataOffset, ChannelCardinality, SearchContext.GetCurrentResultPoseVector());
return;
}
// we leave the SearchContext.EditFeatureVector() set to zero since the SearchContext.PoseHistory is invalid and it'll fail if we continue
UE_LOG(LogPoseSearch, Error, TEXT("UPoseSearchFeatureChannel_Curve::BuildQuery - Failed because Pose History Node is missing."));
return;
}
CurveValue = SearchContext.GetSampleCurveValue(SampleTimeOffset, CurveName, SampleRole);
FFeatureVectorHelper::EncodeFloat(SearchContext.EditFeatureVector(), ChannelDataOffset, CurveValue);
}
#if WITH_EDITOR
void UPoseSearchFeatureChannel_Curve::FillWeights(TArrayView<float> Weights) const
{
for (int32 i = 0; i < ChannelCardinality; ++i)
{
Weights[ChannelDataOffset + i] = Weight;
}
}
bool UPoseSearchFeatureChannel_Curve::IndexAsset(UE::PoseSearch::FAssetIndexer& Indexer) const
{
using namespace UE::PoseSearch;
for (int32 SampleIdx = Indexer.GetBeginSampleIdx(); SampleIdx != Indexer.GetEndSampleIdx(); ++SampleIdx)
{
float CurveValue;
Indexer.GetSampleCurveValue(CurveValue, SampleTimeOffset, SampleIdx, CurveName, SampleRole);
FFeatureVectorHelper::EncodeFloat(Indexer.GetPoseVector(SampleIdx), ChannelDataOffset, CurveValue);
}
return true;
}
UE::PoseSearch::TLabelBuilder& UPoseSearchFeatureChannel_Curve::GetLabel(UE::PoseSearch::TLabelBuilder& LabelBuilder, UE::PoseSearch::ELabelFormat LabelFormat) const
{
using namespace UE::PoseSearch;
GetOuterLabel(LabelBuilder, LabelFormat);
AppendLabelSeparator(LabelBuilder, LabelFormat);
LabelBuilder.Append(TEXT("Curve_"));
LabelBuilder.Append(CurveName.ToString());
AppendLabelSeparator(LabelBuilder, LabelFormat, true);
LabelBuilder.Appendf(TEXT("%.2f"), SampleTimeOffset);
return LabelBuilder;
}
#endif