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

285 lines
7.6 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "PoseSearch/PoseSearchFeatureChannel.h"
#include "PoseSearch/PoseSearchDerivedDataKey.h"
#include "PoseSearch/PoseSearchSchema.h"
#include "Animation/Skeleton.h"
namespace UE::PoseSearch
{
//////////////////////////////////////////////////////////////////////////
// FFeatureVectorHelper
int32 FFeatureVectorHelper::GetVectorCardinality(EComponentStrippingVector ComponentStrippingVector)
{
switch (ComponentStrippingVector)
{
case EComponentStrippingVector::None:
return 3;
case EComponentStrippingVector::StripXY:
return 1;
case EComponentStrippingVector::StripZ:
return 2;
default:
checkNoEntry();
return 0;
}
}
void FFeatureVectorHelper::EncodeVector(TArrayView<float> Values, int32 DataOffset, const FVector& Vector, EComponentStrippingVector ComponentStrippingVector, bool bNormalize)
{
if (bNormalize)
{
FVector NormalizedVector = Vector;
switch (ComponentStrippingVector)
{
case EComponentStrippingVector::None:
NormalizedVector.Normalize();
Values[DataOffset + 0] = NormalizedVector.X;
Values[DataOffset + 1] = NormalizedVector.Y;
Values[DataOffset + 2] = NormalizedVector.Z;
break;
case EComponentStrippingVector::StripXY:
NormalizedVector.X = 0.f;
NormalizedVector.Y = 0.f;
NormalizedVector.Normalize();
Values[DataOffset + 0] = NormalizedVector.Z;
break;
case EComponentStrippingVector::StripZ:
NormalizedVector.Z = 0.f;
NormalizedVector.Normalize();
Values[DataOffset + 0] = NormalizedVector.X;
Values[DataOffset + 1] = NormalizedVector.Y;
break;
default:
checkNoEntry();
break;
}
}
else
{
switch (ComponentStrippingVector)
{
case EComponentStrippingVector::None:
Values[DataOffset + 0] = Vector.X;
Values[DataOffset + 1] = Vector.Y;
Values[DataOffset + 2] = Vector.Z;
break;
case EComponentStrippingVector::StripXY:
Values[DataOffset + 0] = Vector.Z;
break;
case EComponentStrippingVector::StripZ:
Values[DataOffset + 0] = Vector.X;
Values[DataOffset + 1] = Vector.Y;
break;
default:
checkNoEntry();
break;
}
}
}
FVector FFeatureVectorHelper::DecodeVector(TConstArrayView<float> Values, int32 DataOffset, EComponentStrippingVector ComponentStrippingVector)
{
switch (ComponentStrippingVector)
{
case EComponentStrippingVector::None:
return FVector(Values[DataOffset + 0], Values[DataOffset + 1], Values[DataOffset + 2]);
case EComponentStrippingVector::StripXY:
return FVector(0, 0, Values[DataOffset + 0]);
case EComponentStrippingVector::StripZ:
return FVector(Values[DataOffset + 0], Values[DataOffset + 1], 0);
default:
checkNoEntry();
return FVector::Zero();
}
}
void FFeatureVectorHelper::EncodeVector2D(TArrayView<float> Values, int32 DataOffset, const FVector2D& Vector2D)
{
Values[DataOffset + 0] = Vector2D.X;
Values[DataOffset + 1] = Vector2D.Y;
}
FVector2D FFeatureVectorHelper::DecodeVector2D(TConstArrayView<float> Values, int32 DataOffset)
{
return FVector2D(Values[DataOffset + 0], Values[DataOffset + 1]);
}
void FFeatureVectorHelper::EncodeFloat(TArrayView<float> Values, int32 DataOffset, const float Value)
{
Values[DataOffset + 0] = Value;
}
float FFeatureVectorHelper::DecodeFloat(TConstArrayView<float> Values, int32 DataOffset)
{
return Values[DataOffset];
}
void FFeatureVectorHelper::Copy(TArrayView<float> Values, int32 DataOffset, int32 DataCardinality, TConstArrayView<float> OriginValues)
{
check(Values.Num() == OriginValues.Num() && DataOffset + DataCardinality <= Values.Num());
float* RESTRICT ValuesData = Values.GetData() + DataOffset;
const float* RESTRICT OriginValuesData = OriginValues.GetData() + DataOffset;
check(ValuesData != OriginValuesData);
for (int32 i = 0; i < DataCardinality; ++i, ++ValuesData, ++OriginValuesData)
{
*ValuesData = *OriginValuesData;
}
}
} // namespace UE::PoseSearch
//////////////////////////////////////////////////////////////////////////
// UPoseSearchFeatureChannel
void UPoseSearchFeatureChannel::GetPermutationTimeOffsets(EPermutationTimeType PermutationTimeType, float DesiredPermutationTimeOffset, float& OutPermutationSampleTimeOffset, float& OutPermutationOriginTimeOffset)
{
switch (PermutationTimeType)
{
case EPermutationTimeType::UseSampleTime:
OutPermutationSampleTimeOffset = 0.f;
OutPermutationOriginTimeOffset = 0.f;
break;
case EPermutationTimeType::UsePermutationTime:
OutPermutationSampleTimeOffset = DesiredPermutationTimeOffset;
OutPermutationOriginTimeOffset = DesiredPermutationTimeOffset;
break;
case EPermutationTimeType::UseSampleToPermutationTime:
OutPermutationSampleTimeOffset = DesiredPermutationTimeOffset;
OutPermutationOriginTimeOffset = 0.f;
break;
default:
checkNoEntry();
OutPermutationSampleTimeOffset = 0.f;
OutPermutationOriginTimeOffset = 0.f;
break;
}
}
#if WITH_EDITOR
void UPoseSearchFeatureChannel::GetOuterLabel(UE::PoseSearch::TLabelBuilder& LabelBuilder, UE::PoseSearch::ELabelFormat LabelFormat) const
{
if (LabelFormat != UE::PoseSearch::ELabelFormat::Compact_Horizontal)
{
if (const UPoseSearchFeatureChannel* OuterChannel = Cast<UPoseSearchFeatureChannel>(GetOuter()))
{
if (LabelBuilder.Len() > 0)
{
LabelBuilder.Append(TEXT("_"));
}
OuterChannel->GetLabel(LabelBuilder, UE::PoseSearch::ELabelFormat::Full_Horizontal);
}
}
}
void UPoseSearchFeatureChannel::AppendLabelSeparator(UE::PoseSearch::TLabelBuilder& LabelBuilder, UE::PoseSearch::ELabelFormat LabelFormat, bool bTryUsingSpace)
{
if (LabelBuilder.Len() == 0)
{
// do nothing
}
else if (LabelFormat == UE::PoseSearch::ELabelFormat::Full_Vertical)
{
LabelBuilder.Append(TEXT("\n"));
}
else if (bTryUsingSpace)
{
LabelBuilder.Append(TEXT(" "));
}
else
{
LabelBuilder.Append(TEXT("_"));
}
}
UE::PoseSearch::TLabelBuilder& UPoseSearchFeatureChannel::GetLabel(UE::PoseSearch::TLabelBuilder& LabelBuilder, UE::PoseSearch::ELabelFormat LabelFormat) const
{
GetOuterLabel(LabelBuilder, LabelFormat);
AppendLabelSeparator(LabelBuilder, LabelFormat);
LabelBuilder.Append(GetName());
return LabelBuilder;
}
bool UPoseSearchFeatureChannel::CanBeNormalizedWith(const UPoseSearchFeatureChannel* Other) const
{
using namespace UE::PoseSearch;
if (this == Other)
{
return true;
}
if (ChannelCardinality != Other->ChannelCardinality)
{
return false;
}
if (GetClass() != Other->GetClass())
{
return false;
}
if (!GetSchema()->AreSkeletonsCompatible(Other->GetSchema()))
{
return false;
}
if (!GetNormalizationGroup().IsNone() && GetNormalizationGroup() == Other->GetNormalizationGroup())
{
return true;
}
TLabelBuilder ThisLabelBuilder, OtherLabelBuilder;
if (GetLabel(ThisLabelBuilder).ToString() != Other->GetLabel(OtherLabelBuilder).ToString())
{
return false;
}
return true;
}
const UPoseSearchSchema* UPoseSearchFeatureChannel::GetSchema() const
{
UObject* Outer = GetOuter();
while (Outer != nullptr)
{
if (const UPoseSearchSchema* Schema = Cast<UPoseSearchSchema>(Outer))
{
return Schema;
}
Outer = Outer->GetOuter();
}
return nullptr;
}
const UE::PoseSearch::FRole UPoseSearchFeatureChannel::GetDefaultRole() const
{
if (const UPoseSearchSchema* Schema = GetSchema())
{
Schema->GetDefaultRole();
}
return UE::PoseSearch::DefaultRole;
}
#endif // WITH_EDITOR
USkeleton* UPoseSearchFeatureChannel::GetSkeleton(bool& bInvalidSkeletonIsError, const IPropertyHandle* PropertyHandle)
{
bInvalidSkeletonIsError = false;
#if WITH_EDITOR
// blueprint generated classes don't have a schema, until they're instanced by the schema
if (const UPoseSearchSchema* Schema = GetSchema())
{
return Schema->GetSkeleton(GetDefaultRole());
}
#else
checkNoEntry();
#endif
return nullptr;
}