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

784 lines
24 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Channels/MovieSceneChannel.h"
#include "Containers/Array.h"
#include "Containers/ArrayView.h"
#include "CoreTypes.h"
#include "Curves/KeyHandle.h"
#include "Math/Range.h"
#include "Math/RangeBound.h"
#include "Misc/AssertionMacros.h"
#include "Misc/FrameNumber.h"
#include "Misc/FrameTime.h"
#include "Templates/UnrealTemplate.h"
#include "Templates/UnrealTypeTraits.h"
#include "UObject/Class.h"
#include "UObject/ObjectMacros.h"
#include "IMovieSceneRetimingInterface.h"
#include "MovieSceneChannelData.generated.h"
struct FFrameRate;
struct FKeyDataOptimizationParams;
namespace UE::MovieScene
{
template<typename ValueType>
void OnRemapChannelKeyTime(const FMovieSceneChannel* Channel, const IRetimingInterface& Retimer, FFrameNumber PreviousTime, FFrameNumber CurrentTime, ValueType& InOutValue)
{}
}
/** A map of key handles that is copyable, but does not copy data on copy */
USTRUCT()
struct FMovieSceneKeyHandleMap : public FKeyHandleLookupTable
{
GENERATED_BODY()
public:
FMovieSceneKeyHandleMap() = default;
FMovieSceneKeyHandleMap(const FMovieSceneKeyHandleMap& RHS){}
FMovieSceneKeyHandleMap& operator=(const FMovieSceneKeyHandleMap& RHS)
{
Reset();
return *this;
}
};
template<>
struct TStructOpsTypeTraits<FMovieSceneKeyHandleMap>
: public TStructOpsTypeTraitsBase2<FMovieSceneKeyHandleMap>
{
enum
{
WithSerializer = true,
};
static constexpr EPropertyObjectReferenceType WithSerializerObjectReferences = EPropertyObjectReferenceType::None;
};
namespace UE
{
namespace MovieScene
{
/**
* Evaluate the specified time array by finding the two indices that are adjacent to the supplied time
*
* @param InTimes A sorted array of frame numbers
* @param InTime The time to find within the array
* @param OutIndex1 The first time in the array that's >= InTime, or INDEX_NONE if there are none
* @param OutIndex2 OutIndex1 + 1 if it is a valid index in the array, INDEX_NONE otherwise
*/
MOVIESCENE_API void EvaluateTime(TArrayView<const FFrameNumber> InTimes, FFrameTime InTime, int32& OutIndex1, int32& OutIndex2);
/**
* Evaluate the specified time array by finding the two indices and an interpolation value that are adjacent to the supplied time
*
* @param InTimes A sorted array of frame numbers
* @param InTime The time to find within the array
* @param OutIndex1 The first time in the array that's >= InTime, or INDEX_NONE if there are none
* @param OutIndex2 OutIndex1 + 1 if it is a valid index in the array, INDEX_NONE otherwise
* @param OutInterp A value from 0.0 -> 1.0 specifying how a linear interpolation value from index 1 to index 2
*/
MOVIESCENE_API void EvaluateTime(TArrayView<const FFrameNumber> InTimes, FFrameTime InTime, int32& OutIndex1, int32& OutIndex2, double& OutInterp);
/**
* Find the range of times that fall around PredicateTime +/- InTolerance up to a maximum
*
* @param InTimes A sorted array of frame numbers
* @param PredicateTime The time around which to search
* @param InTolerance The tolerance range to search around PredicateTime with
* @param MaxNum A maximum number of times to find, starting with those closest to the predicate time
* @param OutMin The earliest index that met the conditions of the search
* @param OutMax The latest index that met the conditions of the search
*/
MOVIESCENE_API void FindRange(TArrayView<const FFrameNumber> InTimes, FFrameNumber PredicateTime, FFrameNumber InTolerance, int32 MaxNum, int32& OutMin, int32& OutMax);
} // namespace MovieScene
} // namespace UE
/**
* Base class channel data utility that provides a consistent interface to a sorted array of times and handles.
* Complete access should be through TMovieSceneChannelData that allows mutation of the data
*/
struct FMovieSceneChannelData
{
/**
* Read-only access to this channel's key times.
*/
FORCEINLINE TArrayView<const FFrameNumber> GetTimes() const
{
return *Times;
}
/**
* Mutable access to this channel's key times.
* @note: *Warning*: any usage *must* keep times sorted. Any reordering of times will not be reflected in the values array.
*/
FORCEINLINE TArrayView<FFrameNumber> GetTimes()
{
return *Times;
}
/**
* Retrieve a key handle for the specified key time index
*
* @param Index The index to retrieve
* @return A key handle that identifies the key at the specified index, regardless of re-ordering
*/
MOVIESCENE_API FKeyHandle GetHandle(int32 Index);
/**
* Attempt to retrieve the index of key from its handle
*
* @param Handle The handle to retrieve
* @return The index of the key, or INDEX_NONE
*/
MOVIESCENE_API int32 GetIndex(FKeyHandle Handle);
/**
* Attempt to find a key at a given time and tolerance
*
* @param InTime The time at which to search
* @param InTolerance A tolerance of frame numbers to allow either side of the specified time
* @return The index of the key closest to InTime and within InTolerance, or INDEX_NONE
*/
MOVIESCENE_API int32 FindKey(FFrameNumber InTime, FFrameNumber InTolerance = 0);
/**
* Find the range of keys that fall around InTime +/- InTolerance up to a maximum
*
* @param InTime The time around which to search
* @param MaxNum A maximum number of times to find, starting with those closest to the predicate time
* @param OutMin The earliest index that met the conditions of the search
* @param OutMax The latest index that met the conditions of the search
* @param InTolerance The tolerance range to search around PredicateTime with
*/
MOVIESCENE_API void FindKeys(FFrameNumber InTime, int32 MaxNum, int32& OutMinIndex, int32& OutMaxIndex, int32 InTolerance);
/**
* Compute the total time range of the channel data.
*
* @return The range of this channel data
*/
MOVIESCENE_API TRange<FFrameNumber> GetTotalRange() const;
/**
* Convert the frame resolution of a movie scene channel by moving the key times to the equivalent frame time
*
* @param SourceRate The frame rate the channel is currently in
* @param DestinationRate The new frame rate to convert the channel to
*/
UE_DEPRECATED(5.6, "Please use RemapTimes")
MOVIESCENE_API void ChangeFrameResolution(FFrameRate SourceRate, FFrameRate DestinationRate);
/**
* Get all the keys in the given range. Resulting arrays must be the same size where indices correspond to both arrays.
*
* @param WithinRange The bounds to get keys for
* @param OutKeyTimes Array to receive all key times within the given range
* @param OutKeyHandles Array to receive all key handles within the given range
*/
MOVIESCENE_API void GetKeys(const TRange<FFrameNumber>& WithinRange, TArray<FFrameNumber>* OutKeyTimes, TArray<FKeyHandle>* OutKeyHandles);
/**
* Get key times for a number of keys in the channel data
*
* @param InHandles Array of key handles that should have their times set
* @param OutKeyTimes Array of times that should be set for each key handle. Must be exactly the size of InHandles
*/
MOVIESCENE_API void GetKeyTimes(TArrayView<const FKeyHandle> InHandles, TArrayView<FFrameNumber> OutKeyTimes);
/**
* Offset the channel data by a given delta time
*
* @param DeltaTime The time to offset by
*/
MOVIESCENE_API void Offset(FFrameNumber DeltaTime);
protected:
/**
* Constructor that takes a non-owning pointer to an array of times and a key handle map
*
* @param InTimes A pointer to an array that should be operated on by this class. Externally owned.
* @param InKeyHandles A key handle map used for persistent, order independent identification of keys
* @param InChannel A pointer to the owning channel.
*/
MOVIESCENE_API FMovieSceneChannelData(FMovieSceneChannel* InChannel, TArray<FFrameNumber>* InTimes, FKeyHandleLookupTable* InKeyHandles);
UE_DEPRECATED(5.5, "Constructor that takes an optional FMovieSceneChannel is now deprecated. FMovieSceneChannel is now required.")
MOVIESCENE_API FMovieSceneChannelData(TArray<FFrameNumber>* InTimes, FKeyHandleLookupTable* InKeyHandles, FMovieSceneChannel* InChannel = nullptr);
/**
* Move the key at index KeyIndex to a new time
*
* @return The index of the key in its new position
*/
MOVIESCENE_API int32 MoveKeyInternal(int32 KeyIndex, FFrameNumber InNewTime);
/**
* Add a new key at the specified time
*
* @return The index of the key in its new position
*/
MOVIESCENE_API int32 AddKeyInternal(FFrameNumber InTime);
protected:
/** Pointer to an external array of sorted times. Must be kept in sync with a corresponding value array. */
TArray<FFrameNumber>* Times;
/** Pointer to an external key handle map */
FKeyHandleLookupTable* KeyHandles;
/** Optional Pointer to the owning FMovieSceneChannel, should be set if the add,move, and delete callbacks are needed */
FMovieSceneChannel* OwningChannel;
};
/**
* Templated channel data utility class that provides a consistent interface for interacting with a channel's keys and values.
* Assumes that the supplied time and value arrays are already sorted ascendingly by time and are the same size.
* This class will maintain those invariants throughout its lifetime.
*/
template<typename ValueType>
struct TMovieSceneChannelData : FMovieSceneChannelData
{
typedef typename TCallTraits<ValueType>::ParamType ParamType;
/**
* Constructor that takes a non-owning pointer to an array of times and values, and a key handle map
*
* @param InTimes A pointer to an array of times that should be operated on by this class. Externally owned.
* @param InValues A pointer to an array of values that should be operated on by this class. Externally owned.
* @param InKeyHandles A key handle map used for persistent, order independent identification of keys
* @param InChannel A option point to the owning channel, should be set if the move,add, delete delegates are utilizaed
*/
UE_DEPRECATED(5.5, "Constructor that takes an optional FMovieSceneChannel is now deprecated. FMovieSceneChannel is now required.")
TMovieSceneChannelData(TArray<FFrameNumber>* InTimes, TArray<ValueType>* InValues, FKeyHandleLookupTable* InKeyHandles, FMovieSceneChannel* InChannel = nullptr)
: FMovieSceneChannelData(InChannel, InTimes, InKeyHandles), Values(InValues)
{
check(Times && Values);
}
/**
* Constructor that takes a non-owning pointer to an array of times and values, and a key handle map
*
* @param InTimes A pointer to an array of times that should be operated on by this class. Externally owned.
* @param InValues A pointer to an array of values that should be operated on by this class. Externally owned.
* @param InChannel A pointer to the owning channel.
* @param InKeyHandles A key handle map used for persistent, order independent identification of keys
*/
TMovieSceneChannelData(TArray<FFrameNumber>* InTimes, TArray<ValueType>* InValues, FMovieSceneChannel* InChannel, FKeyHandleLookupTable* InKeyHandles)
: FMovieSceneChannelData(InChannel, InTimes, InKeyHandles), Values(InValues)
{
check(Times && Values);
}
/**
* Conversion to a constant version of this class
*/
operator TMovieSceneChannelData<const ValueType>()
{
return TMovieSceneChannelData<const ValueType>(Times, Values);
}
/**
* Read-only access to this channel's values
*/
FORCEINLINE TArrayView<const ValueType> GetValues() const
{
return *Values;
}
/**
* Mutable access to this channel's values
*/
FORCEINLINE TArrayView<ValueType> GetValues()
{
return *Values;
}
/**
* Add a new key at a given time
*
* @param InTime The time at which to add the new key
* @param InValue The value of the new key
* @return The index of the newly added key
*/
int32 AddKey(FFrameNumber InTime, ParamType InValue)
{
int32 KeyIndex = AddKeyInternal(InTime);
Values->Insert(InValue, KeyIndex);
if (OwningChannel && OwningChannel->OnKeyAddedEvent().IsBound())
{
TArray<FKeyAddOrDeleteEventItem> Items;
Items.Add(FKeyAddOrDeleteEventItem(KeyIndex, InTime));
OwningChannel->OnKeyAddedEvent().Broadcast(OwningChannel, Items);
}
return KeyIndex;
}
/**
* Move the key at index KeyIndex to a new time
*
* @param KeyIndex The index of the key to move
* @param NewTime The time to move the key to
* @return The index of the key in its new position
*/
int32 MoveKey(int32 KeyIndex, FFrameNumber NewTime, bool bRemoveDuplicateKeys = false)
{
if (bRemoveDuplicateKeys)
{
FKeyHandle KeyHandle = GetHandle(KeyIndex);
TArray<FKeyHandle> KeysToRemove;
TArray<FFrameNumber> DummyTimes;
GetKeys(TRange<FFrameNumber>(NewTime, NewTime), &DummyTimes, &KeysToRemove);
KeysToRemove.Remove(KeyHandle);
if (KeysToRemove.Num() > 0)
{
DeleteKeys(KeysToRemove);
}
KeyIndex = GetIndex(KeyHandle);
}
if (KeyIndex == INDEX_NONE)
{
return KeyIndex;
}
int32 NewIndex = MoveKeyInternal(KeyIndex, NewTime);
if (NewIndex != KeyIndex)
{
// We have to remove the key and re-add it in the right place
// This could probably be done better by just shuffling up/down the items that need to move, without ever changing the size of the array
ValueType OldValue = (*Values)[KeyIndex];
Values->RemoveAt(KeyIndex, EAllowShrinking::No);
Values->Insert(OldValue, NewIndex);
}
return NewIndex;
}
/**
* Move the key at index KeyIndex to a new time
*
* @param KeyIndex The index of the key to move
* @param NewTime The time to move the key to
* @return The index of the key in its new position
*/
int32 SetKeyTime(int32 KeyIndex, FFrameNumber InNewTime)
{
return MoveKey(KeyIndex, InNewTime);
}
/**
* Remap the times of all the keys in this channel using an abstract retimer
*
* @param Retimer Custom retimer object that can convert from an old time to a new time
*/
void RemapTimes(const UE::MovieScene::IRetimingInterface& Retimer)
{
using namespace UE::MovieScene;
const int32 Num = Times->Num();
if (Num == 0)
{
return;
}
bool bIsSorted = true;
{
int32 Index = 0;
// Remap the first time
{
FFrameNumber& NewTime = (*Times)[Index];
FFrameNumber OldTime = NewTime;
NewTime = Retimer.RemapTime(OldTime);
OnRemapChannelKeyTime(OwningChannel, Retimer, OldTime, NewTime, (*Values)[Index]);
}
++Index;
// Remap others keeping track of whether we need resorting
for ( ; Index < Num; ++Index)
{
FFrameNumber& NewTime = (*Times)[Index];
FFrameNumber OldTime = NewTime;
FFrameNumber Previous = (*Times)[Index-1];
NewTime = Retimer.RemapTime(OldTime);
bIsSorted &= NewTime >= Previous;
OnRemapChannelKeyTime(OwningChannel, Retimer, OldTime, NewTime, (*Values)[Index]);
}
}
if (!bIsSorted)
{
// Have to re-sort all the keys...
TArray<int32, TInlineAllocator<128>> Indices;
for (int32 Index = 0; Index < Num; ++Index)
{
Indices.Add(Index);
}
Algo::Sort(Indices, [this](int32 A, int32 B){
return (*Times)[A] < (*Times)[B];
});
for (int32 Index = 0; Index < Num; ++Index)
{
KeyHandles->MoveHandle(Index, Indices[Index]);
}
// Move all the keys by repeatedly swapping until we get the correct index
for (int32 Index = 0; Index < Num; ++Index)
{
while (Indices[Index] != Index)
{
const int32 DestIndex = Indices[Index];
Swap((*Times)[Index], (*Times)[DestIndex]);
Swap((*Values)[Index], (*Values)[DestIndex]);
Swap(Indices[Index], Indices[DestIndex]);
}
}
}
}
/**
* Remove the key at a given index
*
* @param KeyIndex The index of the key to remove
*/
void RemoveKey(int32 KeyIndex)
{
check(Times->IsValidIndex(KeyIndex));
if (OwningChannel && OwningChannel->OnKeyDeletedEvent().IsBound())
{
const FFrameNumber Time = (*Times)[KeyIndex];
TArray<FKeyAddOrDeleteEventItem> Items;
Items.Add(FKeyAddOrDeleteEventItem(KeyIndex, Time));
OwningChannel->OnKeyDeletedEvent().Broadcast(OwningChannel, Items);
}
Times->RemoveAt(KeyIndex, EAllowShrinking::No);
Values->RemoveAt(KeyIndex, EAllowShrinking::No);
if (KeyHandles)
{
KeyHandles->DeallocateHandle(KeyIndex);
}
}
/**
* Set the value of the key at InTime to InValue, adding a new key if necessary
*
* @param InTime The time at which to add the new key
* @param InValue The value of the new key
* @return The handle of the key
*/
FKeyHandle UpdateOrAddKey(FFrameNumber InTime, ParamType InValue)
{
int32 ExistingKey = FindKey(InTime);
if (ExistingKey != INDEX_NONE)
{
(*Values)[ExistingKey] = InValue;
}
else
{
ExistingKey = AddKey(InTime, InValue);
}
return GetHandle(ExistingKey);
}
/**
* Updates keys with these times in the channel. Will add or insert any missing keys, and replace values of existing keys.
* This assumes that the InTimes array is sorted, as it performs a tape merge to efficiently update the keys.
*
* @param InTimes Times to update
* @param InValues Values to update
*/
void UpdateOrAddKeys(const TArrayView<const FFrameNumber> InTimes, const TArrayView<ValueType> InValues)
{
check(InTimes.Num() == InValues.Num());
const int32 UpdateNum = InTimes.Num();
int32 CurrIdx = 0;
int32 UpdateIdx = 0;
while (true)
{
if (UpdateIdx == UpdateNum)
{
// No more keys to insert
break;
}
if (CurrIdx == Times->Num())
{
// Append the rest of the keys to the end
Times->Append(InTimes.RightChop(UpdateIdx));
Values->Append(InValues.RightChop(UpdateIdx));
break;
}
if ((*Times)[CurrIdx] == InTimes[UpdateIdx])
{
// Replace value at the associated time
(*Values)[CurrIdx] = InValues[UpdateIdx];
CurrIdx++;
UpdateIdx++;
continue;
}
if ((*Times)[CurrIdx] > InTimes[UpdateIdx])
{
// Insert new time value pair
Times->Insert(InTimes[UpdateIdx], CurrIdx);
Values->Insert(InValues[UpdateIdx], CurrIdx);
CurrIdx++;
UpdateIdx++;
continue;
}
if ((*Times)[CurrIdx] < InTimes[UpdateIdx])
{
// Not found insertion slot yet
CurrIdx++;
continue;
}
}
KeyHandles->Reset();
for (int32 Index = 0; Index < Times->Num(); ++Index)
{
KeyHandles->AllocateHandle(Index);
}
}
/**
* Set key times for a number of keys in this channel data
*
* @param InHandles Array of key handles that should have their times set
* @param InKeyTimes Array of new times for each handle of the above array
*/
void SetKeyTimes(TArrayView<const FKeyHandle> InHandles, TArrayView<const FFrameNumber> InKeyTimes)
{
check(InHandles.Num() == InKeyTimes.Num());
for (int32 Index = 0; Index < InHandles.Num(); ++Index)
{
const int32 KeyIndex = GetIndex(InHandles[Index]);
if (KeyIndex != INDEX_NONE)
{
MoveKey(KeyIndex, InKeyTimes[Index]);
}
}
}
/**
* Duplicate a number of keys within this channel data
*
* @param InHandles Array of key handles that should be duplicated
* @param OutNewHandles Array view to receive key handles for each duplicated key. Must exactly mathc the size of InHandles.
*/
void DuplicateKeys(TArrayView<const FKeyHandle> InHandles, TArrayView<FKeyHandle> OutNewHandles)
{
for (int32 Index = 0; Index < InHandles.Num(); ++Index)
{
const int32 KeyIndex = GetIndex(InHandles[Index]);
if (KeyIndex == INDEX_NONE)
{
// we must add a handle even if the supplied handle does not relate to a key in this channel
OutNewHandles[Index] = FKeyHandle::Invalid();
}
else
{
// Do not cache value and time arrays since they can be reallocated during this loop
auto KeyCopy = (*Values)[KeyIndex];
int32 NewKeyIndex = AddKey((*Times)[KeyIndex], MoveTemp(KeyCopy));
OutNewHandles[Index] = GetHandle(NewKeyIndex);
}
}
}
/**
* Delete a number of keys from this channel data
*
* @param InHandles Array of key handles that should be deleted
*/
void DeleteKeys(TArrayView<const FKeyHandle> InHandles)
{
for (int32 Index = 0; Index < InHandles.Num(); ++Index)
{
const int32 KeyIndex = GetIndex(InHandles[Index]);
if (KeyIndex != INDEX_NONE)
{
RemoveKey(KeyIndex);
}
}
}
/**
* Delete keys before or after a specified time
*
* @param InTime Delete keys after this time
* @param bDeleteKeysBefore Whether to delete keys before the specified time
*/
void DeleteKeysFrom(FFrameNumber InTime, bool bDeleteKeysBefore)
{
TArray<FFrameNumber> OutKeyTimes;
TArray<FKeyHandle> OutKeyHandles;
GetKeys(TRange<FFrameNumber>::All(), &OutKeyTimes, &OutKeyHandles);
TArray<FKeyHandle> KeysToRemove;
for (int32 Index = 0; Index < OutKeyTimes.Num(); ++Index)
{
if (bDeleteKeysBefore)
{
if (OutKeyTimes[Index] < InTime)
{
KeysToRemove.Add(OutKeyHandles[Index]);
}
}
else
{
if (OutKeyTimes[Index] > InTime)
{
KeysToRemove.Add(OutKeyHandles[Index]);
}
}
}
DeleteKeys(KeysToRemove);
}
/**
* Remove all the keys from this channel
*/
void Reset()
{
if (OwningChannel && OwningChannel->OnKeyDeletedEvent().IsBound())
{
TArray<FKeyAddOrDeleteEventItem> Items;
for (int32 Index = 0; Index < Times->Num(); ++Index)
{
const FFrameNumber Time = (*Times)[Index];
Items.Add(FKeyAddOrDeleteEventItem(Index, Time));
}
OwningChannel->OnKeyDeletedEvent().Broadcast(OwningChannel, Items);
}
Times->Reset();
Values->Reset();
if (KeyHandles)
{
KeyHandles->Reset();
}
}
private:
/** Pointer to an external array of values, to be kept in sync with FMovieSceneChannelData::Times */
TArray<ValueType>* Values;
};
/**
* Specialization of TMovieSceneChannelData for const value types (limited read-only access to data)
*/
template<typename ValueType>
struct TMovieSceneChannelData<const ValueType>
{
typedef typename TCallTraits<ValueType>::ParamType ParamType;
/**
* Constructor that takes a non-owning pointer to an array of times and values, and a key handle map
*
* @param InTimes A pointer to an array of times that should be operated on by this class. Externally owned.
* @param InValues A pointer to an array of values that should be operated on by this class. Externally owned.
* @param InKeyHandles A key handle map used for persistent, order independent identification of keys
*/
TMovieSceneChannelData(const TArray<FFrameNumber>* InTimes, const TArray<ValueType>* InValues)
: Times(InTimes), Values(InValues)
{
check(Times && Values);
}
/**
* Read-only access to this channel's key times.
*/
FORCEINLINE TArrayView<const FFrameNumber> GetTimes() const
{
return *Times;
}
/**
* Read-only access to this channel's values
*/
FORCEINLINE TArrayView<const ValueType> GetValues() const
{
return *Values;
}
/**
* Attempt to find a key at a given time and tolerance
*
* @param InTime The time at which to search
* @param InTolerance A tolerance of frame numbers to allow either side of the specified time
* @return The index of the key closest to InTime and within InTolerance, INDEX_NONE or Times.Num()
*/
int32 FindKey(FFrameNumber InTime, FFrameNumber InTolerance = 0) const
{
int32 MinIndex = 0, MaxIndex = 0;
UE::MovieScene::FindRange(*Times, InTime, InTolerance, 1, MinIndex, MaxIndex);
return MinIndex;
}
/**
* Find the range of keys that fall around InTime +/- InTolerance up to a maximum
*
* @param InTime The time around which to search
* @param MaxNum A maximum number of times to find, starting with those closest to the predicate time
* @param OutMin The earliest index that met the conditions of the search
* @param OutMax The latest index that met the conditions of the search
* @param InTolerance The tolerance range to search around PredicateTime with
*/
void FindKeys(FFrameNumber InTime, int32 MaxNum, int32& OutMinIndex, int32& OutMaxIndex, FFrameNumber InTolerance) const
{
UE::MovieScene::FindRange(*Times, InTime, InTolerance, MaxNum, OutMinIndex, OutMaxIndex);
}
/**
* Compute the total time range of the channel data.
*
* @return The range of this channel data
*/
TRange<FFrameNumber> GetTotalRange() const
{
return Times->Num() ? TRange<FFrameNumber>((*Times)[0], TRangeBound<FFrameNumber>::Inclusive((*Times)[Times->Num()-1])) : TRange<FFrameNumber>::Empty();
}
private:
/** Pointer to an external array of sorted times. Must be kept in sync with Values. */
const TArray<FFrameNumber>* Times;
/** Pointer to an external array of values, to be kept in sync with Times. */
const TArray<ValueType>* Values;
};