Files
UnrealEngine/Engine/Source/Runtime/MovieScene/Public/Evaluation/MovieSceneTimeWarping.h
2025-05-18 13:04:45 +08:00

312 lines
11 KiB
C

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Containers/UnrealString.h"
#include "CoreMinimal.h"
#include "HAL/Platform.h"
#include "Math/Range.h"
#include "Math/RangeBound.h"
#include "Misc/AssertionMacros.h"
#include "Misc/FrameNumber.h"
#include "Misc/FrameTime.h"
#include "MovieSceneTimeTransform.h"
#include "UObject/ObjectMacros.h"
#include "MovieSceneTimeWarping.generated.h"
/**
* Transform time by warping it around from end to start. This is mostly useful for
* looping things.
*/
USTRUCT()
struct FMovieSceneTimeWarping
{
UE_DEPRECATED(5.5, "Please update your code to the new FMovieSceneSequenceTransform API. The closest analogue is FMovieSceneTimeWarpLoop.")
MOVIESCENE_API static const uint32 InvalidWarpCount;
GENERATED_BODY()
FMovieSceneTimeWarping()
: Start(0)
, End(0)
{}
explicit FMovieSceneTimeWarping(FFrameNumber InStart, FFrameNumber InEnd)
: Start(InStart)
, End(InEnd)
{
check(Start <= End);
}
/**
* Returns the length of the warping.
*/
UE_DEPRECATED(5.5, "Please update your code to the new FMovieSceneSequenceTransform API. The closest analogue is FMovieSceneTimeWarpLoop.")
FFrameNumber Length() const { return End - Start; }
/**
* Returns whether this warping transform is doing anything.
*/
UE_DEPRECATED(5.5, "Please update your code to the new FMovieSceneSequenceTransform API. The closest analogue is FMovieSceneTimeWarpLoop.")
bool IsValid() const { return End > Start; }
/**
* Returns a range that encompasses the whole warping time span.
*/
UE_DEPRECATED(5.5, "Please update your code to the new FMovieSceneSequenceTransform API. The closest analogue is FMovieSceneTimeWarpLoop.")
TRange<FFrameTime> GetRange() const
{
const TRangeBound<FFrameTime> OutLower(TRangeBound<FFrameTime>::Inclusive(Start));
const TRangeBound<FFrameTime> OutUpper(TRangeBound<FFrameTime>::Exclusive(End));
return TRange<FFrameTime>(OutLower, OutUpper);
}
/**
* Returns a transformation that takes us from a local time into a given loop back to the root time.
*/
UE_DEPRECATED(5.5, "Please update your code to the new FMovieSceneSequenceTransform API. The closest analogue is FMovieSceneTimeWarpLoop.")
FMovieSceneTimeTransform InverseFromWarp(uint32 WarpCount) const;
/**
* Transforms the given frame and returns the warped frame time along with the warp index we ended up in.
*/
UE_DEPRECATED(5.5, "Please update your code to the new FMovieSceneSequenceTransform API. The closest analogue is FMovieSceneTimeWarpLoop.")
void TransformFrame(FFrameNumber InFrame, FFrameNumber& OutFrame, uint32& OutWarpIndex) const;
/**
* Transforms the given time and returns the warped time along with the warp index we ended up in.
*/
UE_DEPRECATED(5.5, "Please update your code to the new FMovieSceneSequenceTransform API. The closest analogue is FMovieSceneTimeWarpLoop.")
void TransformTime(FFrameTime InTime, FFrameTime& OutTime, uint32& OutWarpIndex) const;
/**
* Transforms the given time by warping it by a specific warp count, regardless of how many warps are
* needed in theory to stay in the warping range.
*/
UE_DEPRECATED(5.5, "Please update your code to the new FMovieSceneSequenceTransform API. The closest analogue is FMovieSceneTimeWarpLoop.")
void TransformTimeSpecific(FFrameTime InTime, uint32 WarpCount, FFrameTime& OutTime) const;
/**
* Transforms the given range in a "naive" way, i.e. its lower and upper bounds are transformed
* independently by this time warping. This means that the output range could be "inside-out"... that is,
* the output lower bound could be greater than the output upper bound, like, for instance, in the case
* of the input range starting near the end of a loop, and ending near the beginning of another loop.
*/
UE_DEPRECATED(5.5, "Please update your code to the new FMovieSceneSequenceTransform API. The closest analogue is FMovieSceneTimeWarpLoop.")
TRange<FFrameTime> TransformRangePure(const TRange<FFrameTime>& Range) const;
/**
* Transforms the given range by time-warping its lower bound, and computing an upper bound so that the
* size of the input range is preserved. So if the input range starts in the middle of a loop and lasts
* 3 loops, the output range will start in the middle of the first loop, and last 3 times as long as
* the Length of this time warp.
*/
UE_DEPRECATED(5.5, "Please update your code to the new FMovieSceneSequenceTransform API. The closest analogue is FMovieSceneTimeWarpLoop.")
TRange<FFrameTime> TransformRangeUnwarped(const TRange<FFrameTime>& Range) const;
/**
* Transforms the given range by time-warping it and figuring out if it "covered" a full loop or not.
* If it covered a full loop, return a full-loop range, i.e. a range that goes from StartOffset to
* (Length - EndOffset).
* If it didn't cover a full loop, return the transformed range, which should be a subset of the full
* loop range mentioned above.
*/
UE_DEPRECATED(5.5, "Please update your code to the new FMovieSceneSequenceTransform API. The closest analogue is FMovieSceneTimeWarpLoop.")
TRange<FFrameTime> TransformRangeConstrained(const TRange<FFrameTime>& Range) const;
UE_DEPRECATED(5.5, "Please update your code to the new FMovieSceneSequenceTransform API. The closest analogue is FMovieSceneTimeWarpLoop.")
friend bool operator==(const FMovieSceneTimeWarping& A, const FMovieSceneTimeWarping& B)
{
return A.Start == B.Start && A.End == B.End;
}
UE_DEPRECATED(5.5, "Please update your code to the new FMovieSceneSequenceTransform API. The closest analogue is FMovieSceneTimeWarpLoop.")
friend bool operator!=(const FMovieSceneTimeWarping& A, const FMovieSceneTimeWarping& B)
{
return A.Start != B.Start || A.End != B.End;
}
UPROPERTY()
FFrameNumber Start;
UPROPERTY()
FFrameNumber End;
};
PRAGMA_DISABLE_DEPRECATION_WARNINGS
inline FFrameNumber operator*(FFrameNumber InFrame, const FMovieSceneTimeWarping& RHS)
{
checkSlow(RHS.IsValid());
const FFrameNumber WarpLength = RHS.Length();
return (InFrame - RHS.Start) % WarpLength + RHS.Start;
}
inline FFrameTime operator*(FFrameTime InTime, const FMovieSceneTimeWarping& RHS)
{
checkSlow(RHS.IsValid());
const FFrameNumber WarpLength = RHS.Length();
return (InTime - RHS.Start) % WarpLength + RHS.Start;
}
inline FFrameNumber& operator*=(FFrameNumber& InFrame, const FMovieSceneTimeWarping& RHS)
{
InFrame = InFrame * RHS;
return InFrame;
}
inline FFrameTime& operator*=(FFrameTime& InTime, const FMovieSceneTimeWarping& RHS)
{
InTime = InTime * RHS;
return InTime;
}
inline FMovieSceneTimeTransform FMovieSceneTimeWarping::InverseFromWarp(uint32 WarpCount) const
{
check(IsValid() || WarpCount == InvalidWarpCount);
if (WarpCount == InvalidWarpCount || WarpCount == 0)
{
return FMovieSceneTimeTransform();
}
else
{
const FFrameNumber WarpLength = Length();
const FFrameNumber WarpsOffset = WarpLength * (float)WarpCount;
return FMovieSceneTimeTransform(WarpsOffset);
}
}
inline void FMovieSceneTimeWarping::TransformFrame(FFrameNumber InFrame, FFrameNumber& OutFrame, uint32& OutWarpIndex) const
{
checkSlow(IsValid());
const FFrameNumber WarpLength = Length();
OutWarpIndex = 0;
FFrameNumber TempFrame = InFrame - Start;
while (TempFrame >= WarpLength)
{
TempFrame -= WarpLength;
++OutWarpIndex;
}
OutFrame = TempFrame + Start;
}
inline void FMovieSceneTimeWarping::TransformTime(FFrameTime InTime, FFrameTime& OutTime, uint32& OutWarpIndex) const
{
checkSlow(IsValid());
const FFrameTime WarpLength = Length();
OutWarpIndex = 0;
FFrameTime TempTime = InTime - Start;
while (TempTime >= WarpLength)
{
TempTime = TempTime - WarpLength;
++OutWarpIndex;
}
OutTime = TempTime + Start;
}
inline void FMovieSceneTimeWarping::TransformTimeSpecific(FFrameTime InTime, uint32 WarpCount, FFrameTime& OutTime) const
{
checkSlow(IsValid());
const FFrameTime WarpLength = Length();
FFrameTime TempTime = InTime - Start;
for (uint32 WarpIndex = 0; WarpIndex < WarpCount; ++WarpIndex)
{
TempTime = TempTime - WarpLength;
}
OutTime = TempTime + Start;
}
inline TRange<FFrameTime> FMovieSceneTimeWarping::TransformRangePure(const TRange<FFrameTime>& Range) const
{
checkSlow(IsValid());
const TRangeBound<FFrameTime> SourceLower = Range.GetLowerBound();
const TRangeBound<FFrameTime> TransformedLower =
SourceLower.IsOpen() ?
TRangeBound<FFrameTime>() :
SourceLower.IsInclusive() ?
TRangeBound<FFrameTime>::Inclusive(SourceLower.GetValue() * (*this)) :
TRangeBound<FFrameTime>::Exclusive(SourceLower.GetValue() * (*this));
const TRangeBound<FFrameTime> SourceUpper = Range.GetUpperBound();
const TRangeBound<FFrameTime> TransformedUpper =
SourceUpper.IsOpen() ?
TRangeBound<FFrameTime>() :
SourceUpper.IsInclusive() ?
TRangeBound<FFrameTime>::Inclusive(SourceUpper.GetValue() * (*this)) :
TRangeBound<FFrameTime>::Exclusive(SourceUpper.GetValue() * (*this));
return TRange<FFrameTime>(TransformedLower, TransformedUpper);
}
inline TRange<FFrameTime> FMovieSceneTimeWarping::TransformRangeUnwarped(const TRange<FFrameTime>& Range) const
{
checkSlow(IsValid());
if (!Range.GetLowerBound().IsOpen() && !Range.GetUpperBound().IsOpen())
{
const TRangeBound<FFrameTime> SourceLower = Range.GetLowerBound();
const TRangeBound<FFrameTime> TransformedLower =
SourceLower.IsInclusive() ?
TRangeBound<FFrameTime>::Inclusive(SourceLower.GetValue() * (*this)) :
TRangeBound<FFrameTime>::Exclusive(SourceLower.GetValue() * (*this));
const FFrameTime RangeSize = Range.Size<FFrameTime>();
const FFrameTime TransformedUpperValue = TransformedLower.GetValue() + RangeSize;
const TRangeBound<FFrameTime> TransformedUpper =
Range.GetUpperBound().IsInclusive() ?
TRangeBound<FFrameTime>::Inclusive(TransformedUpperValue) :
TRangeBound<FFrameTime>::Exclusive(TransformedUpperValue);
return TRange<FFrameTime>(TransformedLower, TransformedUpper);
}
else
{
return TransformRangePure(Range);
}
}
inline TRange<FFrameTime> FMovieSceneTimeWarping::TransformRangeConstrained(const TRange<FFrameTime>& Range) const
{
checkSlow(IsValid());
if (!Range.GetLowerBound().IsOpen() && !Range.GetUpperBound().IsOpen())
{
const FFrameNumber WarpLength = Length();
const FFrameTime RangeSize = Range.Size<FFrameTime>();
if (RangeSize >= WarpLength)
{
// If the range is longer than a loop, we could, in theory, end up with a disjointed range, like, for
// example, a range that starts in the first loop and ends in the second loop. Regardless of whether
// we actually finished a full loop or not, we need here to take the "hull" of that, which is always
// the looping range inside our local space.
return GetRange();
}
const TRange<FFrameTime> UnwarpedTransformedRange = TransformRangeUnwarped(Range);
if (UnwarpedTransformedRange.GetUpperBoundValue() > End || UnwarpedTransformedRange.GetLowerBoundValue() < Start)
{
// The input range is going over a warp boundary, so we return the whole warping range.
return GetRange();
}
// The input range fits inside a single loop, so we can return that.
return UnwarpedTransformedRange;
}
else
{
// One of the bounds is open, so we are looping from or to infinitiy. That encompasses our whole
// looping range.
return GetRange();
}
}
/** Convert a FMovieSceneTimeWarping into a string */
inline FString LexToString(const FMovieSceneTimeWarping& InWarping)
{
return *FString::Printf(TEXT("[ %+i ⟳ %+i ]"), InWarping.Start.Value, InWarping.End.Value);
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS