Files
UnrealEngine/Engine/Plugins/Interchange/Runtime/Source/Parsers/CommonParser/Private/InterchangeCommonAnimationPayload.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

431 lines
13 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "InterchangeCommonAnimationPayload.h"
#include "CoreMinimal.h"
#if WITH_ENGINE
#include "Animation/AnimTypes.h"
#endif
#include "InterchangeHelper.h"
#include "Dom/JsonValue.h"
#include "Dom/JsonObject.h"
#include "Serialization/JsonReader.h"
#include "Serialization/JsonWriter.h"
#include "Serialization/JsonSerializer.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(InterchangeCommonAnimationPayload)
namespace UE::Interchange
{
FString Private::HashString(const FString& String)
{
TArray<uint8> TempBytes;
TempBytes.Reserve(64);
//The archive is flagged as persistent so that machines of different endianness produce identical binary results.
FMemoryWriter Ar(TempBytes, /*bIsPersistent=*/ true);
//Hack because serialization do not support const
FString& NonConst = *const_cast<FString*>(&String);
Ar << NonConst;
FSHA1 Sha;
Sha.Update(TempBytes.GetData(), TempBytes.Num() * TempBytes.GetTypeSize());
Sha.Final();
// Retrieve the hash and use it to construct a pseudo-GUID.
uint32 Hash[5];
Sha.GetHash((uint8*)Hash);
FGuid Guid = FGuid(Hash[0] ^ Hash[4], Hash[1], Hash[2], Hash[3]);
return Guid.ToString(EGuidFormats::Base36Encoded);
}
void FAnimationPayloadData::SerializeBaked(FArchive& Ar)
{
Ar << BakeFrequency;
Ar << RangeStartTime;
Ar << RangeEndTime;
Ar << Transforms;
}
void FAnimationPayloadData::CalculateDataFor(const EInterchangeAnimationPayLoadType& ToType, const FTransform& DefaultTransform)
{
#if WITH_ENGINE
if (PayloadKey.Type == EInterchangeAnimationPayLoadType::CURVE
&& ToType == EInterchangeAnimationPayLoadType::STEPCURVE)
{
StepCurves.Reserve(Curves.Num());
for (const FRichCurve& RichCurve : Curves)
{
FInterchangeStepCurve& StepCurve = StepCurves.AddDefaulted_GetRef();
const int32 KeyCount = RichCurve.GetNumKeys();
StepCurve.KeyTimes.AddZeroed(KeyCount);
TArray<float> KeyValues;
KeyValues.AddZeroed(KeyCount);
int32 KeyIndex = 0;
for (FKeyHandle KeyHandle = RichCurve.GetFirstKeyHandle(); KeyHandle != FKeyHandle::Invalid(); KeyHandle = RichCurve.GetNextKey(KeyHandle), ++KeyIndex)
{
StepCurve.KeyTimes[KeyIndex] = RichCurve.GetKeyTime(KeyHandle);
KeyValues[KeyIndex] = RichCurve.GetKeyValue(KeyHandle);
}
StepCurve.FloatKeyValues = MoveTemp(KeyValues);
}
AdditionalSupportedType = ToType;
}
else if (PayloadKey.Type == EInterchangeAnimationPayLoadType::CURVE
&& ToType == EInterchangeAnimationPayLoadType::BAKED)
{
if (Curves.Num() != 9)
{
return;
}
//calculate RangeEnd -> 'BakeKeyCount'
RangeEndTime = -FLT_MAX;
for (const FRichCurve& Curve : Curves)
{
const TArray<FRichCurveKey>& CurveKeys = Curve.GetConstRefOfKeys();
for (const FRichCurveKey& CurveKey : CurveKeys)
{
if (RangeEndTime < CurveKey.Time)
{
RangeEndTime = CurveKey.Time;
}
}
}
if (RangeEndTime < 0)
{
RangeEndTime = 0;
}
const double BakeInterval = 1.0 / (BakeFrequency != 0.0 ? BakeFrequency : DEFAULT_SAMPLERATE);
const double SequenceLength = FMath::Max<double>(FMath::Max<double>(RangeEndTime - RangeStartTime, BakeInterval), MINIMUM_ANIMATION_LENGTH);
int32 BakeKeyCount = FMath::RoundToInt32(SequenceLength * BakeFrequency) + 1;
auto EvaluateCurve = [this](const int32& CurveIndex, double CurrentTime, float DefaultValue)
{
if (Curves[CurveIndex].IsEmpty())
{
return DefaultValue;
}
else
{
return Curves[CurveIndex].Eval(CurrentTime);
}
};
FVector DefaultTranslation = DefaultTransform.GetTranslation();
FVector DefaultEuler = DefaultTransform.GetRotation().Euler();
FVector DefaultScale = DefaultTransform.GetScale3D();
double CurrentTime = 0;
for (int32 BakeIndex = 0; BakeIndex < BakeKeyCount; BakeIndex++, CurrentTime += BakeInterval)
{
FVector Translation;
Translation.X = EvaluateCurve(0, CurrentTime, DefaultTranslation.X);
Translation.Y = EvaluateCurve(1, CurrentTime, DefaultTranslation.Y);
Translation.Z = EvaluateCurve(2, CurrentTime, DefaultTranslation.Z);
FVector RotationEuler;
RotationEuler.X = EvaluateCurve(3, CurrentTime, DefaultEuler.X);
RotationEuler.Y = EvaluateCurve(4, CurrentTime, DefaultEuler.Y);
RotationEuler.Z = EvaluateCurve(5, CurrentTime, DefaultEuler.Z);
FVector Scale3d;
Scale3d.X = EvaluateCurve(6, CurrentTime, DefaultScale.X);
Scale3d.Y = EvaluateCurve(7, CurrentTime, DefaultScale.Y);
Scale3d.Z = EvaluateCurve(8, CurrentTime, DefaultScale.Z);
FTransform AnimKeyTransform(FRotator::MakeFromEuler(RotationEuler), Translation, Scale3d);
Transforms.Add(AnimKeyTransform);
}
AdditionalSupportedType = ToType;
}
#endif
}
FString FAnimationPayloadQuery::GetHashString() const
{
if (!HashStringCache.IsSet())
{
FString ResultPayloadUniqueId = PayloadKey.UniqueId + FString::FromInt(TimeDescription.GetHash());
HashStringCache = Private::HashString(ResultPayloadUniqueId);
}
return HashStringCache.GetValue();
}
FString FAnimationPayloadQuery::ToJson() const
{
TSharedPtr<FJsonObject> QueryObject = MakeShared<FJsonObject>();
QueryObject->SetStringField(TEXT("SceneNodeUniqueID"), SceneNodeUniqueID);
QueryObject->SetStringField(TEXT("PayloadKey.UniqueID"), PayloadKey.UniqueId);
QueryObject->SetNumberField(TEXT("PayloadKey.Type"), (uint8)PayloadKey.Type);
QueryObject->SetNumberField(TEXT("TimeDescription.BakeFrequency"), TimeDescription.BakeFrequency);
QueryObject->SetNumberField(TEXT("TimeDescription.RangeStartSecond"), TimeDescription.RangeStartSecond);
QueryObject->SetNumberField(TEXT("TimeDescription.RangeStopSecond"), TimeDescription.RangeStopSecond);
FString JsonString;
TSharedRef<TJsonWriter<TCHAR, TPrettyJsonPrintPolicy<TCHAR>>> JsonWriter = TJsonWriterFactory<TCHAR, TPrettyJsonPrintPolicy<TCHAR>>::Create(&JsonString);
if (!FJsonSerializer::Serialize(QueryObject.ToSharedRef(), JsonWriter))
{
// Error creating the json string
return FString();
}
return JsonString;
}
void FAnimationPayloadQuery::FromJson(const FString& JsonString)
{
TSharedRef<TJsonReader<TCHAR>> Reader = FJsonStringReader::Create(JsonString);
TSharedPtr<FJsonObject> QueryObject;
if (!FJsonSerializer::Deserialize(Reader, QueryObject) || !QueryObject.IsValid())
{
// Cannot read the json file
return;
}
QueryObject->TryGetStringField(TEXT("SceneNodeUniqueID"), SceneNodeUniqueID);
QueryObject->TryGetStringField(TEXT("PayloadKey.UniqueId"), PayloadKey.UniqueId);
uint8 Type;
if (QueryObject->TryGetNumberField(TEXT("PayloadKey.Type"), Type))
{
PayloadKey.Type = EInterchangeAnimationPayLoadType(Type);
}
QueryObject->TryGetNumberField(TEXT("TimeDescription.BakeFrequency"), TimeDescription.BakeFrequency);
QueryObject->TryGetNumberField(TEXT("TimeDescription.RangeStartSecond"), TimeDescription.RangeStartSecond);
QueryObject->TryGetNumberField(TEXT("TimeDescription.RangeStopSecond"), TimeDescription.RangeStopSecond);
}
FString FAnimationPayloadQuery::ToJson(const TArray<FAnimationPayloadQuery>& Queries)
{
TArray<TSharedPtr<FJsonValue>> QueriesJsonArray;
for (const FAnimationPayloadQuery& Query : Queries)
{
QueriesJsonArray.Add(MakeShared<FJsonValueString>(Query.ToJson()));
}
FString JsonString;
TSharedRef<TJsonWriter<TCHAR, TPrettyJsonPrintPolicy<TCHAR>>> JsonWriter = TJsonWriterFactory<TCHAR, TPrettyJsonPrintPolicy<TCHAR>>::Create(&JsonString);
if (!FJsonSerializer::Serialize(QueriesJsonArray, JsonWriter))
{
// Error creating the json string
return FString();
}
return JsonString;
}
void FAnimationPayloadQuery::FromJson(const FString& JsonString, TArray<FAnimationPayloadQuery>& Queries)
{
TSharedRef<TJsonReader<TCHAR>> Reader = FJsonStringReader::Create(JsonString);
TArray<TSharedPtr<FJsonValue>> JsonValueArray;
if (!FJsonSerializer::Deserialize(Reader, JsonValueArray))
{
// Cannot read the json file
return;
}
Queries.Reserve(JsonValueArray.Num());
for (size_t QueryIndex = 0; QueryIndex < JsonValueArray.Num(); QueryIndex++)
{
FString QueryJsonString = JsonValueArray[QueryIndex]->AsString();
FAnimationPayloadQuery Query;
Query.FromJson(QueryJsonString);
Queries.Add(Query);
}
}
}
#if WITH_ENGINE
void FInterchangeCurveKey::ToRichCurveKey(FRichCurveKey& RichCurveKey) const
{
RichCurveKey.Time = Time;
RichCurveKey.Value = Value;
switch (InterpMode)
{
case EInterchangeCurveInterpMode::Constant:
RichCurveKey.InterpMode = ERichCurveInterpMode::RCIM_Constant;
break;
case EInterchangeCurveInterpMode::Cubic:
RichCurveKey.InterpMode = ERichCurveInterpMode::RCIM_Cubic;
break;
case EInterchangeCurveInterpMode::Linear:
RichCurveKey.InterpMode = ERichCurveInterpMode::RCIM_Linear;
break;
default:
RichCurveKey.InterpMode = ERichCurveInterpMode::RCIM_None;
break;
}
switch (TangentMode)
{
case EInterchangeCurveTangentMode::Auto:
RichCurveKey.TangentMode = ERichCurveTangentMode::RCTM_Auto;
break;
case EInterchangeCurveTangentMode::Break:
RichCurveKey.TangentMode = ERichCurveTangentMode::RCTM_Break;
break;
case EInterchangeCurveTangentMode::User:
RichCurveKey.TangentMode = ERichCurveTangentMode::RCTM_User;
break;
default:
RichCurveKey.TangentMode = ERichCurveTangentMode::RCTM_None;
break;
}
switch (TangentWeightMode)
{
case EInterchangeCurveTangentWeightMode::WeightedArrive:
RichCurveKey.TangentWeightMode = ERichCurveTangentWeightMode::RCTWM_WeightedArrive;
break;
case EInterchangeCurveTangentWeightMode::WeightedBoth:
RichCurveKey.TangentWeightMode = ERichCurveTangentWeightMode::RCTWM_WeightedBoth;
break;
case EInterchangeCurveTangentWeightMode::WeightedLeave:
RichCurveKey.TangentWeightMode = ERichCurveTangentWeightMode::RCTWM_WeightedLeave;
break;
default:
RichCurveKey.TangentWeightMode = ERichCurveTangentWeightMode::RCTWM_WeightedNone;
break;
}
RichCurveKey.ArriveTangent = ArriveTangent;
RichCurveKey.ArriveTangentWeight = ArriveTangentWeight;
RichCurveKey.LeaveTangent = LeaveTangent;
RichCurveKey.LeaveTangentWeight = LeaveTangentWeight;
}
#endif //WITH_ENGINE
void FInterchangeCurveKey::Serialize(FArchive& Ar)
{
Ar << InterpMode;
Ar << TangentMode;
Ar << TangentWeightMode;
Ar << Time;
Ar << Value;
Ar << ArriveTangent;
Ar << ArriveTangentWeight;
Ar << LeaveTangent;
Ar << LeaveTangentWeight;
}
#if WITH_ENGINE
void FInterchangeCurve::ToRichCurve(FRichCurve& OutRichCurve) const
{
OutRichCurve.Keys.Reserve(Keys.Num());
for (const FInterchangeCurveKey& CurveKey : Keys)
{
FKeyHandle RichCurveKeyHandle = OutRichCurve.AddKey(CurveKey.Time, CurveKey.Value);
CurveKey.ToRichCurveKey(OutRichCurve.GetKey(RichCurveKeyHandle));
}
OutRichCurve.AutoSetTangents();
}
#endif //WITH_ENGINE
void FInterchangeCurve::Serialize(FArchive& Ar)
{
Ar << Keys;
}
void FInterchangeStepCurve::RemoveRedundantKeys(float Threshold)
{
const int32 KeyCount = KeyTimes.Num();
if (KeyCount < 2)
{
//Nothing to optimize
return;
}
if (FloatKeyValues.IsSet())
{
InternalRemoveRedundantKey<float>(FloatKeyValues.GetValue(), [Threshold](const float& ValueA, const float& ValueB)
{
return FMath::IsNearlyEqual(ValueA, ValueB, Threshold);
});
}
else if (IntegerKeyValues.IsSet())
{
InternalRemoveRedundantKey<int32>(IntegerKeyValues.GetValue(), [](const int32& ValueA, const int32& ValueB)
{
return ValueA == ValueB;
});
}
else if (StringKeyValues.IsSet())
{
InternalRemoveRedundantKey<FString>(StringKeyValues.GetValue(), [](const FString& ValueA, const FString& ValueB)
{
return ValueA == ValueB;
});
}
else if(BooleanKeyValues.IsSet())
{
InternalRemoveRedundantKey<bool>(BooleanKeyValues.GetValue(), [](bool ValueA, bool ValueB)
{
return ValueA == ValueB;
});
}
else if(ByteKeyValues.IsSet())
{
InternalRemoveRedundantKey<uint8>(ByteKeyValues.GetValue(), [](uint8 ValueA, uint8 ValueB)
{
return ValueA == ValueB;
});
}
}
void FInterchangeStepCurve::Serialize(FArchive& Ar)
{
Ar << KeyTimes;
Ar << FloatKeyValues;
Ar << ByteKeyValues;
Ar << BooleanKeyValues;
Ar << IntegerKeyValues;
Ar << StringKeyValues;
}
template<typename ValueType>
void FInterchangeStepCurve::InternalRemoveRedundantKey(TArray<ValueType>& Values, TFunctionRef<bool(const ValueType& ValueA, const ValueType& ValueB)> CompareFunction)
{
const int32 KeyCount = Values.Num();
if (KeyCount == 0)
{
return;
}
TArray<float> NewKeyTimes;
NewKeyTimes.Reserve(KeyCount);
TArray<ValueType> NewValues;
NewValues.Reserve(KeyCount);
ValueType LastValue = Values[0];
//Add the first key
NewKeyTimes.Add(KeyTimes[0]);
NewValues.Add(Values[0]);
for (int32 KeyIndex = 1; KeyIndex < KeyCount; ++KeyIndex)
{
//Only add the not equal keys
if (!CompareFunction(LastValue, Values[KeyIndex]))
{
LastValue = Values[KeyIndex];
NewKeyTimes.Add(KeyTimes[KeyIndex]);
NewValues.Add(Values[KeyIndex]);
}
}
NewKeyTimes.Shrink();
NewValues.Shrink();
KeyTimes = MoveTemp(NewKeyTimes);
Values = MoveTemp(NewValues);
}
template void FInterchangeStepCurve::InternalRemoveRedundantKey<float>(TArray<float>& Values, TFunctionRef<bool(const float& ValueA, const float& ValueB)> CompareFunction);
template void FInterchangeStepCurve::InternalRemoveRedundantKey<int32>(TArray<int32>& Values, TFunctionRef<bool(const int32& ValueA, const int32& ValueB)> CompareFunction);
template void FInterchangeStepCurve::InternalRemoveRedundantKey<FString>(TArray<FString>& Values, TFunctionRef<bool(const FString& ValueA, const FString& ValueB)> CompareFunction);