// 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(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(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 CachedChannelData; if (const UPoseSearchFeatureChannel* CachedChannel = SearchContext.GetCachedChannelData(UniqueIdentifier, this, CachedChannelData)) { #if DO_CHECK const UPoseSearchFeatureChannel_Curve* CachedCurveChannel = Cast(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 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