Files
UnrealEngine/Engine/Plugins/Animation/LiveLink/Source/LiveLinkMovieScene/Public/MovieScene/MovieSceneLiveLinkBufferData.h
2025-05-18 13:04:45 +08:00

139 lines
5.1 KiB
C

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Channels/MovieSceneFloatChannel.h"
/** Data structure to handle buffering transform keys. Inspired from 3d Transform Track Recorder */
struct FLiveLinkTransformKeys
{
TArray<FFrameNumber> Times;
TArray<FMovieSceneFloatValue> LocationX, LocationY, LocationZ;
TArray<FMovieSceneFloatValue> RotationX, RotationY, RotationZ;
TArray<FMovieSceneFloatValue> ScaleX, ScaleY, ScaleZ;
//returns rotation as -180 to 180.
FVector GetNormalizedRotation(float X, float Y, float Z)
{
const FQuat Quat(FRotator(Y, Z, X));
const FVector Rot = Quat.Euler();
return Rot;
}
void FixEulerFlips(int32 StartIndex, TArray<FMovieSceneFloatChannel>& FloatChannels)
{
TArrayView<const FMovieSceneFloatValue> XRotChannel = FloatChannels[StartIndex].GetValues();
TArrayView<const FMovieSceneFloatValue> YRotChannel = FloatChannels[StartIndex].GetValues();
TArrayView<const FMovieSceneFloatValue> ZRotChannel = FloatChannels[StartIndex].GetValues();
if (XRotChannel.Num() > 0)
{
int32 LastOne = XRotChannel.Num() - 1;
FVector Rotation = GetNormalizedRotation(RotationX[0].Value, RotationY[0].Value, RotationZ[0].Value);
RotationX[0].Value = Rotation.X;
RotationY[0].Value = Rotation.Y;
RotationZ[0].Value = Rotation.Z;
//Due winding from last one saved with the new one normalized.
FMath::WindRelativeAnglesDegrees(XRotChannel[LastOne].Value, RotationX[0].Value);
FMath::WindRelativeAnglesDegrees(YRotChannel[LastOne].Value, RotationY[0].Value);
FMath::WindRelativeAnglesDegrees(ZRotChannel[LastOne].Value, RotationZ[0].Value);
}
else
{
FVector Rotation = GetNormalizedRotation(RotationX[0].Value, RotationY[0].Value, RotationZ[0].Value);
RotationX[0].Value = Rotation.X;
RotationY[0].Value = Rotation.Y;
RotationZ[0].Value = Rotation.Z;
}
int32 TotalCount = Times.Num();
for (int32 RotIndex = 0; RotIndex < TotalCount - 1; RotIndex++)
{
FVector Rotation = GetNormalizedRotation(RotationX[RotIndex + 1].Value, RotationY[RotIndex + 1].Value, RotationZ[RotIndex + 1].Value);
RotationX[RotIndex + 1].Value = Rotation.X;
RotationY[RotIndex + 1].Value = Rotation.Y;
RotationZ[RotIndex + 1].Value = Rotation.Z;
FMath::WindRelativeAnglesDegrees(RotationX[RotIndex].Value, RotationX[RotIndex + 1].Value);
FMath::WindRelativeAnglesDegrees(RotationY[RotIndex].Value, RotationY[RotIndex + 1].Value);
FMath::WindRelativeAnglesDegrees(RotationZ[RotIndex].Value, RotationZ[RotIndex + 1].Value);
}
}
void Add(const FTransform& InTransform, FFrameNumber InKeyTime)
{
Times.Add(InKeyTime);
Add(InTransform);
}
void Add(const FTransform& InTransform)
{
FMovieSceneFloatValue NewValue(InTransform.GetTranslation().X);
NewValue.InterpMode = RCIM_Cubic;
LocationX.Add(NewValue);
NewValue = FMovieSceneFloatValue(InTransform.GetTranslation().Y);
NewValue.InterpMode = RCIM_Cubic;
LocationY.Add(NewValue);
NewValue = FMovieSceneFloatValue(InTransform.GetTranslation().Z);
NewValue.InterpMode = RCIM_Cubic;
LocationZ.Add(NewValue);
FRotator WoundRotation = InTransform.Rotator();
NewValue = FMovieSceneFloatValue(WoundRotation.Roll);
NewValue.InterpMode = RCIM_Cubic;
RotationX.Add(NewValue);
NewValue = FMovieSceneFloatValue(WoundRotation.Pitch);
NewValue.InterpMode = RCIM_Cubic;
RotationY.Add(NewValue);
NewValue = FMovieSceneFloatValue(WoundRotation.Yaw);
NewValue.InterpMode = RCIM_Cubic;
RotationZ.Add(NewValue);
NewValue = FMovieSceneFloatValue(InTransform.GetScale3D().X);
NewValue.InterpMode = RCIM_Cubic;
ScaleX.Add(NewValue);
NewValue = FMovieSceneFloatValue(InTransform.GetScale3D().Y);
NewValue.InterpMode = RCIM_Cubic;
ScaleY.Add(NewValue);
NewValue = FMovieSceneFloatValue(InTransform.GetScale3D().Z);
NewValue.InterpMode = RCIM_Cubic;
ScaleZ.Add(NewValue);
}
//This function is the one that's called when recording live link incrementally. We move the values over from our saved
//Location, Rotation and Scale buffers into the specified float channels and then reset our buffers, re-using it's memory
//for the next iteration. We also fix any euler flips during this process, avoiding iterating over the data once again during Finalize.
void AppendToFloatChannelsAndReset(int32 StartIndex, TArray<FMovieSceneFloatChannel>& FloatChannels)
{
if (Times.Num() > 0)
{
FloatChannels[StartIndex++].AddKeys(Times, LocationX);
LocationX.Reset();
FloatChannels[StartIndex++].AddKeys(Times, LocationY);
LocationY.Reset();
FloatChannels[StartIndex++].AddKeys(Times, LocationZ);
LocationZ.Reset();
FixEulerFlips(StartIndex, FloatChannels);
FloatChannels[StartIndex++].AddKeys(Times, RotationX);
RotationX.Reset();
FloatChannels[StartIndex++].AddKeys(Times, RotationY);
RotationY.Reset();
FloatChannels[StartIndex++].AddKeys(Times, RotationZ);
RotationZ.Reset();
FloatChannels[StartIndex++].AddKeys(Times, ScaleX);
ScaleX.Reset();
FloatChannels[StartIndex++].AddKeys(Times, ScaleY);
ScaleY.Reset();
FloatChannels[StartIndex++].AddKeys(Times, ScaleZ);
ScaleZ.Reset();
Times.Reset();
}
}
};