621 lines
21 KiB
C++
621 lines
21 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "Channels/ChannelCurveModel.h"
|
|
|
|
#include "Algo/BinarySearch.h"
|
|
#include "Channels/MovieSceneChannelData.h"
|
|
#include "Channels/MovieSceneChannelProxy.h"
|
|
#include "Channels/MovieSceneFloatChannel.h"
|
|
#include "Channels/MovieSceneIntegerChannel.h"
|
|
#include "Channels/MovieSceneByteChannel.h"
|
|
#include "Containers/UnrealString.h"
|
|
#include "CurveDataAbstraction.h"
|
|
#include "CurveDrawInfo.h"
|
|
#include "CurveEditorScreenSpace.h"
|
|
#include "Curves/RealCurve.h"
|
|
#include "Delegates/Delegate.h"
|
|
#include "HAL/PlatformCrt.h"
|
|
#include "ISequencer.h"
|
|
#include "Math/NumericLimits.h"
|
|
#include "Math/UnrealMathUtility.h"
|
|
#include "Math/Vector2D.h"
|
|
#include "Misc/AssertionMacros.h"
|
|
#include "Misc/FrameNumber.h"
|
|
#include "Misc/FrameRate.h"
|
|
#include "Misc/FrameTime.h"
|
|
#include "Misc/Optional.h"
|
|
#include "MovieScene.h"
|
|
#include "MovieSceneSection.h"
|
|
#include "Styling/AppStyle.h"
|
|
#include "Styling/ISlateStyle.h"
|
|
#include "Templates/UnrealTemplate.h"
|
|
|
|
class UObject;
|
|
struct FMovieSceneChannelMetaData;
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
FChannelCurveModel<ChannelType, ChannelValue, KeyType>::FChannelCurveModel(TMovieSceneChannelHandle<ChannelType> InChannel, UMovieSceneSection* OwningSection, TWeakPtr<ISequencer> InWeakSequencer)
|
|
{
|
|
ChannelHandle = InChannel;
|
|
WeakSection = OwningSection;
|
|
WeakSequencer = InWeakSequencer;
|
|
LastSignature = OwningSection->GetSignature();
|
|
|
|
if (FMovieSceneChannelProxy* ChannelProxy = InChannel.GetChannelProxy())
|
|
{
|
|
OnDestroyHandle = ChannelProxy->OnDestroy.AddRaw(this, &FChannelCurveModel<ChannelType, ChannelValue, KeyType>::FixupCurve);
|
|
}
|
|
|
|
SupportedViews = ECurveEditorViewID::Absolute | ECurveEditorViewID::Normalized | ECurveEditorViewID::Stacked;
|
|
}
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
FChannelCurveModel<ChannelType, ChannelValue, KeyType>::FChannelCurveModel(TMovieSceneChannelHandle<ChannelType> InChannel, UMovieSceneSection* InOwningSection, UObject* InOwningObject, TWeakPtr<ISequencer> InWeakSequencer)
|
|
: FChannelCurveModel(InChannel, InOwningSection, InWeakSequencer)
|
|
{
|
|
WeakOwningObject = InOwningObject;
|
|
}
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
FChannelCurveModel<ChannelType, ChannelValue, KeyType>::~FChannelCurveModel()
|
|
{
|
|
if (FMovieSceneChannelProxy* ChannelProxy = ChannelHandle.GetChannelProxy())
|
|
{
|
|
ChannelProxy->OnDestroy.Remove(OnDestroyHandle);
|
|
}
|
|
}
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
FTransform2d FChannelCurveModel<ChannelType, ChannelValue, KeyType>::GetCurveTransform() const
|
|
{
|
|
FTransform2d Transform;
|
|
|
|
const FMovieSceneChannelMetaData* MetaData = ChannelHandle.GetMetaData();
|
|
const UMovieSceneSection* Section = WeakSection.Get();
|
|
|
|
if (MetaData && Section)
|
|
{
|
|
FFrameNumber Offset = MetaData->GetOffsetTime(Section);
|
|
if (Offset != 0)
|
|
{
|
|
FFrameRate TickResolution = Section->GetTypedOuter<UMovieScene>()->GetTickResolution();
|
|
Transform = Concatenate(Transform, FVector2d(-Offset / TickResolution, 0.0));
|
|
}
|
|
}
|
|
return Transform;
|
|
}
|
|
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
const void* FChannelCurveModel<ChannelType, ChannelValue, KeyType>::GetCurve() const
|
|
{
|
|
return ChannelHandle.Get();
|
|
}
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
void FChannelCurveModel<ChannelType, ChannelValue, KeyType>::Modify()
|
|
{
|
|
if (UObject* Object = GetOwningObject())
|
|
{
|
|
Object->Modify();
|
|
}
|
|
LastSignature.Invalidate();
|
|
}
|
|
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
void FChannelCurveModel<ChannelType, ChannelValue, KeyType>::DrawCurve(const FCurveEditor& CurveEditor, const FCurveEditorScreenSpace& ScreenSpace, TArray<TTuple<double, double>>& OutInterpolatingPoints) const
|
|
{
|
|
ChannelType* Channel = ChannelHandle.Get();
|
|
UMovieSceneSection* Section = WeakSection.Get();
|
|
|
|
if (Channel && Section)
|
|
{
|
|
FFrameRate TickResolution = Section->GetTypedOuter<UMovieScene>()->GetTickResolution();
|
|
|
|
TMovieSceneChannelData<ChannelValue> ChannelData = Channel->GetData();
|
|
TArrayView<const FFrameNumber> Times = ChannelData.GetTimes();
|
|
TArrayView<const ChannelValue> Values = ChannelData.GetValues();
|
|
|
|
const double DisplayOffset = GetInputDisplayOffset();
|
|
const double StartTimeSeconds = ScreenSpace.GetInputMin() - DisplayOffset;
|
|
const double EndTimeSeconds = ScreenSpace.GetInputMax() - DisplayOffset;
|
|
|
|
const FFrameNumber StartFrame = (StartTimeSeconds * TickResolution).FloorToFrame();
|
|
const FFrameNumber EndFrame = (EndTimeSeconds * TickResolution).CeilToFrame();
|
|
|
|
const int32 StartingIndex = Algo::UpperBound(Times, StartFrame);
|
|
const int32 EndingIndex = Algo::LowerBound(Times, EndFrame);
|
|
|
|
// Add the lower bound of the visible space
|
|
const bool bValidRange = StartingIndex < EndingIndex;
|
|
//if we aren't just doing the default constant then we need to sample
|
|
const bool PreNotConstant = (Channel->PreInfinityExtrap != RCCE_None && Channel->PreInfinityExtrap != RCCE_Constant);
|
|
const bool PostNotConstant = (Channel->PostInfinityExtrap != RCCE_None && Channel->PostInfinityExtrap != RCCE_Constant);
|
|
if (bValidRange && (PreNotConstant || PostNotConstant))
|
|
{
|
|
const FFrameRate DisplayResolution = Section->GetTypedOuter<UMovieScene>()->GetDisplayRate();
|
|
const FFrameNumber StartTimeInDisplay = FFrameRate::TransformTime(FFrameTime(StartFrame), TickResolution, DisplayResolution).FloorToFrame();
|
|
const FFrameNumber EndTimeInDisplay = FFrameRate::TransformTime(FFrameTime(EndFrame), TickResolution, DisplayResolution).CeilToFrame();
|
|
|
|
double Value = 0.0;
|
|
TOptional<double> PreviousValue;
|
|
for (FFrameNumber DisplayFrameNumber = StartTimeInDisplay; DisplayFrameNumber <= EndTimeInDisplay; ++DisplayFrameNumber)
|
|
{
|
|
FFrameNumber TickFrameNumber = FFrameRate::TransformTime(FFrameTime(DisplayFrameNumber), DisplayResolution, TickResolution).FrameNumber;
|
|
Evaluate(TickFrameNumber / TickResolution, Value);
|
|
if (PreviousValue.IsSet() && PreviousValue.GetValue() != Value)
|
|
{
|
|
OutInterpolatingPoints.Add(MakeTuple(TickFrameNumber / TickResolution, PreviousValue.GetValue()));
|
|
}
|
|
OutInterpolatingPoints.Add(MakeTuple(TickFrameNumber / TickResolution, Value));
|
|
PreviousValue = Value;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bValidRange)
|
|
{
|
|
OutInterpolatingPoints.Add(MakeTuple(StartFrame / TickResolution, GetKeyValue(Values, StartingIndex)));
|
|
}
|
|
|
|
TOptional<double> PreviousValue;
|
|
for (int32 KeyIndex = StartingIndex; KeyIndex < EndingIndex; ++KeyIndex)
|
|
{
|
|
double Value = GetKeyValue(Values, KeyIndex);
|
|
if (PreviousValue.IsSet() && PreviousValue.GetValue() != Value)
|
|
{
|
|
OutInterpolatingPoints.Add(MakeTuple(Times[KeyIndex] / TickResolution, PreviousValue.GetValue()));
|
|
}
|
|
|
|
OutInterpolatingPoints.Add(MakeTuple(Times[KeyIndex] / TickResolution, Value));
|
|
PreviousValue = Value;
|
|
}
|
|
|
|
// Add the upper bound of the visible space
|
|
if (bValidRange)
|
|
{
|
|
OutInterpolatingPoints.Add(MakeTuple(EndFrame / TickResolution, GetKeyValue(Values, EndingIndex - 1)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
void FChannelCurveModel<ChannelType, ChannelValue, KeyType>::GetKeys(double MinTime, double MaxTime, double MinValue, double MaxValue, TArray<FKeyHandle>& OutKeyHandles) const
|
|
{
|
|
ChannelType* Channel = ChannelHandle.Get();
|
|
UMovieSceneSection* Section = WeakSection.Get();
|
|
|
|
if (Channel && Section)
|
|
{
|
|
FFrameRate TickResolution = Section->GetTypedOuter<UMovieScene>()->GetTickResolution();
|
|
|
|
TMovieSceneChannelData<ChannelValue> ChannelData = Channel->GetData();
|
|
TArrayView<const FFrameNumber> Times = ChannelData.GetTimes();
|
|
TArrayView<const ChannelValue> Values = ChannelData.GetValues();
|
|
|
|
const FFrameNumber StartFrame = MinTime <= MIN_int32 ? MIN_int32 : (MinTime * TickResolution).CeilToFrame();
|
|
const FFrameNumber EndFrame = MaxTime >= MAX_int32 ? MAX_int32 : (MaxTime * TickResolution).FloorToFrame();
|
|
|
|
const int32 StartingIndex = Algo::LowerBound(Times, StartFrame);
|
|
const int32 EndingIndex = Algo::UpperBound(Times, EndFrame);
|
|
|
|
for (int32 KeyIndex = StartingIndex; KeyIndex < EndingIndex; ++KeyIndex)
|
|
{
|
|
if (GetKeyValue(Values, KeyIndex) >= MinValue && GetKeyValue(Values, KeyIndex) <= MaxValue)
|
|
{
|
|
OutKeyHandles.Add(ChannelData.GetHandle(KeyIndex));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
void FChannelCurveModel<ChannelType, ChannelValue, KeyType>::GetKeyDrawInfo(ECurvePointType PointType, const FKeyHandle InKeyHandle, FKeyDrawInfo& OutDrawInfo) const
|
|
{
|
|
OutDrawInfo.Brush = FAppStyle::Get().GetBrush("Sequencer.KeyDiamond");
|
|
OutDrawInfo.ScreenSize = FVector2D(10, 10);
|
|
}
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
void FChannelCurveModel<ChannelType, ChannelValue, KeyType>::GetKeyPositions(TArrayView<const FKeyHandle> InKeys, TArrayView<FKeyPosition> OutKeyPositions) const
|
|
{
|
|
ChannelType* Channel = ChannelHandle.Get();
|
|
UMovieSceneSection* Section = WeakSection.Get();
|
|
|
|
if (Channel && Section)
|
|
{
|
|
FFrameRate TickResolution = Section->GetTypedOuter<UMovieScene>()->GetTickResolution();
|
|
|
|
TMovieSceneChannelData<ChannelValue> ChannelData = Channel->GetData();
|
|
TArrayView<const FFrameNumber> Times = ChannelData.GetTimes();
|
|
TArrayView<const ChannelValue> Values = ChannelData.GetValues();
|
|
|
|
for (int32 Index = 0; Index < InKeys.Num(); ++Index)
|
|
{
|
|
int32 KeyIndex = ChannelData.GetIndex(InKeys[Index]);
|
|
if (KeyIndex != INDEX_NONE)
|
|
{
|
|
OutKeyPositions[Index].InputValue = Times[KeyIndex] / TickResolution;
|
|
OutKeyPositions[Index].OutputValue = GetKeyValue(Values, KeyIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
void FChannelCurveModel<ChannelType, ChannelValue, KeyType>::SetKeyPositions(TArrayView<const FKeyHandle> InKeys, TArrayView<const FKeyPosition> InKeyPositions, EPropertyChangeType::Type ChangeType)
|
|
{
|
|
UE::MovieScene::FScopedSignedObjectModifyDefer Defer;
|
|
|
|
ChannelType* Channel = ChannelHandle.Get();
|
|
UMovieSceneSection* Section = WeakSection.Get();
|
|
const FMovieSceneChannelMetaData* MetaData = ChannelHandle.GetMetaData();
|
|
|
|
UMovieSceneSignedObject* SignedOwner = Cast<UMovieSceneSignedObject>(WeakOwningObject.Get());
|
|
if (!SignedOwner)
|
|
{
|
|
SignedOwner = WeakSection.Get();
|
|
}
|
|
|
|
if (Channel && MetaData && Section && !IsReadOnly())
|
|
{
|
|
TMovieSceneChannelData<ChannelValue> ChannelData = Channel->GetData();
|
|
const TArrayView<const FFrameNumber> Times = ChannelData.GetTimes();
|
|
if (Times.IsEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
Section->MarkAsChanged();
|
|
|
|
FFrameRate TickResolution = Section->GetTypedOuter<UMovieScene>()->GetTickResolution();
|
|
FFrameNumber KeyOffset = 0;
|
|
|
|
const int32 LastIndex = Times.Num()-1;
|
|
const FFrameNumber FirstTime = Times[0];
|
|
const FFrameNumber LastTime = Times[LastIndex];
|
|
|
|
// Expand to frame first, then offset
|
|
if (InKeyPositions.Num() > 0)
|
|
{
|
|
double Min = TNumericLimits<double>::Max();
|
|
double Max = TNumericLimits<double>::Lowest();
|
|
|
|
for (const FKeyPosition& Value : InKeyPositions)
|
|
{
|
|
Min = FMath::Min(Min, Value.InputValue);
|
|
Max = FMath::Max(Max, Value.InputValue);
|
|
}
|
|
|
|
FFrameNumber Offset = MetaData->GetOffsetTime(Section);
|
|
FFrameNumber MinTime = (Min * TickResolution).RoundToFrame() + Offset;
|
|
FFrameNumber MaxTime = (Max * TickResolution).RoundToFrame() + Offset;
|
|
|
|
if (!Section->GetRange().Contains(MinTime))
|
|
{
|
|
Section->ExpandToFrame(MinTime);
|
|
KeyOffset += MetaData->GetOffsetTime(Section) - Offset;
|
|
}
|
|
|
|
if (Min != Max && !Section->GetRange().Contains(MaxTime))
|
|
{
|
|
Section->ExpandToFrame(MaxTime);
|
|
}
|
|
}
|
|
|
|
for (int32 Index = 0; Index < InKeys.Num(); ++Index)
|
|
{
|
|
int32 KeyIndex = ChannelData.GetIndex(InKeys[Index]);
|
|
if (KeyIndex != INDEX_NONE)
|
|
{
|
|
FFrameNumber NewTime = (InKeyPositions[Index].InputValue * TickResolution).RoundToFrame() - KeyOffset;
|
|
|
|
const bool bRemoveDuplicates = ChangeType == EPropertyChangeType::ValueSet;
|
|
KeyIndex = ChannelData.MoveKey(KeyIndex, NewTime, bRemoveDuplicates);
|
|
SetKeyValue(KeyIndex, InKeyPositions[Index].OutputValue);
|
|
}
|
|
}
|
|
|
|
Channel->PostEditChange();
|
|
if(WeakSequencer.IsValid())
|
|
{
|
|
WeakSequencer.Pin()->OnChannelChanged().Broadcast(MetaData, Section);
|
|
}
|
|
CurveModifiedDelegate.Broadcast();
|
|
|
|
SignedOwner->MarkAsChanged();
|
|
}
|
|
}
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
void FChannelCurveModel<ChannelType, ChannelValue, KeyType>::GetCurveAttributes(FCurveAttributes& OutCurveAttributes) const
|
|
{
|
|
OutCurveAttributes.SetPreExtrapolation(RCCE_None);
|
|
OutCurveAttributes.SetPostExtrapolation(RCCE_None);
|
|
}
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
void FChannelCurveModel<ChannelType, ChannelValue, KeyType>::GetTimeRange(double& MinTime, double& MaxTime) const
|
|
{
|
|
ChannelType* Channel = ChannelHandle.Get();
|
|
UMovieSceneSection* Section = WeakSection.Get();
|
|
|
|
if (Channel && Section)
|
|
{
|
|
TArrayView<const FFrameNumber> Times = Channel->GetData().GetTimes();
|
|
if (Times.Num() == 0)
|
|
{
|
|
MinTime = 0.f;
|
|
MaxTime = 0.f;
|
|
}
|
|
else
|
|
{
|
|
FFrameRate TickResolution = Section->GetTypedOuter<UMovieScene>()->GetTickResolution();
|
|
double ToTime = TickResolution.AsInterval();
|
|
MinTime = static_cast<double>(Times[0].Value) * ToTime;
|
|
MaxTime = static_cast<double>(Times[Times.Num() - 1].Value) * ToTime;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
void FChannelCurveModel<ChannelType, ChannelValue, KeyType>::GetValueRange(double& MinValue, double& MaxValue) const
|
|
{
|
|
ChannelType* Channel = ChannelHandle.Get();
|
|
UMovieSceneSection* Section = WeakSection.Get();
|
|
|
|
if (Channel && Section)
|
|
{
|
|
TArrayView<const FFrameNumber> Times = Channel->GetData().GetTimes();
|
|
TArrayView<const ChannelValue> Values = Channel->GetData().GetValues();
|
|
|
|
if (Times.Num() == 0)
|
|
{
|
|
// If there are no keys we just use the default value for the channel, defaulting to zero if there is no default.
|
|
MinValue = MaxValue = Channel->GetDefault().Get(0.f);
|
|
}
|
|
else
|
|
{
|
|
FFrameRate TickResolution = Section->GetTypedOuter<UMovieScene>()->GetTickResolution();
|
|
double ToTime = TickResolution.AsInterval();
|
|
int32 LastKeyIndex = Values.Num() - 1;
|
|
MinValue = MaxValue = GetKeyValue(Values, 0);
|
|
|
|
for (int32 i = 0; i < Values.Num(); i++)
|
|
{
|
|
double Key = GetKeyValue(Values, i);
|
|
|
|
MinValue = FMath::Min(MinValue, Key);
|
|
MaxValue = FMath::Max(MaxValue, Key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
int32 FChannelCurveModel<ChannelType, ChannelValue, KeyType>::GetNumKeys() const
|
|
{
|
|
ChannelType* Channel = ChannelHandle.Get();
|
|
|
|
if (Channel)
|
|
{
|
|
TArrayView<const FFrameNumber> Times = Channel->GetData().GetTimes();
|
|
|
|
return Times.Num();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
void FChannelCurveModel<ChannelType, ChannelValue, KeyType>::GetNeighboringKeys(const FKeyHandle InKeyHandle, TOptional<FKeyHandle>& OutPreviousKeyHandle, TOptional<FKeyHandle>& OutNextKeyHandle) const
|
|
{
|
|
ChannelType* Channel = ChannelHandle.Get();
|
|
|
|
if (Channel)
|
|
{
|
|
TMovieSceneChannelData<ChannelValue> ChannelData = Channel->GetData();
|
|
|
|
const int32 KeyIndex = ChannelData.GetIndex(InKeyHandle);
|
|
if (KeyIndex != INDEX_NONE)
|
|
{
|
|
if (KeyIndex - 1 >= 0)
|
|
{
|
|
OutPreviousKeyHandle = ChannelData.GetHandle(KeyIndex - 1);
|
|
}
|
|
|
|
if (KeyIndex + 1 < ChannelData.GetTimes().Num())
|
|
{
|
|
OutNextKeyHandle = ChannelData.GetHandle(KeyIndex + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
bool FChannelCurveModel<ChannelType, ChannelValue, KeyType>::Evaluate(double Time, double& OutValue) const
|
|
{
|
|
ChannelType* Channel = ChannelHandle.Get();
|
|
UMovieSceneSection* Section = WeakSection.Get();
|
|
|
|
if (Channel && Section)
|
|
{
|
|
FFrameRate TickResolution = Section->GetTypedOuter<UMovieScene>()->GetTickResolution();
|
|
|
|
KeyType ThisValue = 0.f;
|
|
if (Channel->Evaluate(Time * TickResolution, ThisValue))
|
|
{
|
|
OutValue = ThisValue;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
void FChannelCurveModel<ChannelType, ChannelValue, KeyType>::AddKeys(TArrayView<const FKeyPosition> InKeyPositions, TArrayView<const FKeyAttributes> InKeyAttributes, TArrayView<TOptional<FKeyHandle>>* OutKeyHandles)
|
|
{
|
|
check(InKeyPositions.Num() == InKeyAttributes.Num() && (!OutKeyHandles || OutKeyHandles->Num() == InKeyPositions.Num()));
|
|
|
|
ChannelType* Channel = ChannelHandle.Get();
|
|
UMovieSceneSection* Section = WeakSection.Get();
|
|
const FMovieSceneChannelMetaData* MetaData = ChannelHandle.GetMetaData();
|
|
|
|
UMovieSceneSignedObject* SignedOwner = Cast<UMovieSceneSignedObject>(WeakOwningObject.Get());
|
|
if (!SignedOwner)
|
|
{
|
|
SignedOwner = WeakSection.Get();
|
|
}
|
|
|
|
if (Channel && MetaData && Section && !IsReadOnly())
|
|
{
|
|
SignedOwner->Modify();
|
|
|
|
TMovieSceneChannelData<ChannelValue> ChannelData = Channel->GetData();
|
|
FFrameRate TickResolution = Section->GetTypedOuter<UMovieScene>()->GetTickResolution();
|
|
|
|
TArray<FKeyHandle> NewKeyHandles;
|
|
NewKeyHandles.SetNumUninitialized(InKeyPositions.Num());
|
|
|
|
FFrameNumber MinFrame = TNumericLimits<FFrameNumber>::Max();
|
|
FFrameNumber MaxFrame = TNumericLimits<FFrameNumber>::Min();
|
|
for (int32 Index = 0; Index < InKeyPositions.Num(); ++Index)
|
|
{
|
|
FKeyPosition Position = InKeyPositions[Index];
|
|
|
|
FFrameNumber Time = (Position.InputValue * TickResolution).RoundToFrame();
|
|
MinFrame = FMath::Min(MinFrame, Time);
|
|
MaxFrame = FMath::Max(MaxFrame, Time);
|
|
|
|
ChannelValue Value = (ChannelValue)(Position.OutputValue);
|
|
|
|
FKeyHandle NewHandle = ChannelData.UpdateOrAddKey(Time, Value);
|
|
if (NewHandle != FKeyHandle::Invalid())
|
|
{
|
|
NewKeyHandles[Index] = NewHandle;
|
|
|
|
if (OutKeyHandles)
|
|
{
|
|
(*OutKeyHandles)[Index] = NewHandle;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (InKeyPositions.Num() > 0)
|
|
{
|
|
FFrameNumber Offset = MetaData->GetOffsetTime(Section);
|
|
Section->ExpandToFrame(MinFrame + Offset);
|
|
}
|
|
|
|
// We reuse SetKeyAttributes here as there is complex logic determining which parts of the attributes are valid to set.
|
|
// For now we need to duplicate the new key handle array due to API mismatch. This will auto calculate tangents if needed.
|
|
SetKeyAttributes(NewKeyHandles, InKeyAttributes);
|
|
Channel->PostEditChange();
|
|
if (WeakSequencer.IsValid())
|
|
{
|
|
WeakSequencer.Pin()->OnChannelChanged().Broadcast(MetaData, Section);
|
|
}
|
|
CurveModifiedDelegate.Broadcast();
|
|
}
|
|
}
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
void FChannelCurveModel<ChannelType, ChannelValue, KeyType>::RemoveKeys(TArrayView<const FKeyHandle> InKeys, double InCurrentTime)
|
|
{
|
|
ChannelType* Channel = ChannelHandle.Get();
|
|
UMovieSceneSection* Section = WeakSection.Get();
|
|
UMovieSceneSignedObject* SignedOwner = Cast<UMovieSceneSignedObject>(WeakOwningObject.Get());
|
|
if (!SignedOwner)
|
|
{
|
|
SignedOwner = WeakSection.Get();
|
|
}
|
|
|
|
|
|
if (Channel && Section && !IsReadOnly())
|
|
{
|
|
SignedOwner->Modify();
|
|
|
|
TMovieSceneChannelData<ChannelValue> ChannelData = Channel->GetData();
|
|
double CurrentValue = 0.0;
|
|
Evaluate(InCurrentTime, CurrentValue);
|
|
|
|
for (FKeyHandle Handle : InKeys)
|
|
{
|
|
int32 KeyIndex = ChannelData.GetIndex(Handle);
|
|
if (KeyIndex != INDEX_NONE)
|
|
{
|
|
ChannelData.RemoveKey(KeyIndex);
|
|
}
|
|
}
|
|
if (Channel->GetNumKeys() == 0)
|
|
{
|
|
using CurveValueType = typename ChannelType::CurveValueType;
|
|
CurveValueType ValueAsCurveValue = (CurveValueType)(CurrentValue); //needed for double to other type conversions
|
|
Channel->SetDefault(ValueAsCurveValue);
|
|
}
|
|
|
|
Channel->PostEditChange();
|
|
if (WeakSequencer.IsValid())
|
|
{
|
|
const FMovieSceneChannelMetaData* MetaData = ChannelHandle.GetMetaData();
|
|
WeakSequencer.Pin()->OnChannelChanged().Broadcast(MetaData, Section);
|
|
}
|
|
CurveModifiedDelegate.Broadcast();
|
|
}
|
|
}
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
bool FChannelCurveModel<ChannelType, ChannelValue, KeyType>::IsReadOnly() const
|
|
{
|
|
UMovieSceneSection* Section = WeakSection.Get();
|
|
if (Section)
|
|
{
|
|
return Section->IsReadOnly();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
void FChannelCurveModel<ChannelType, ChannelValue, KeyType>::FixupCurve()
|
|
{
|
|
if (UMovieSceneSection* Section = WeakSection.Get())
|
|
{
|
|
FMovieSceneChannelProxy* NewChannelProxy = &Section->GetChannelProxy();
|
|
ChannelHandle = NewChannelProxy->CopyHandle(ChannelHandle);
|
|
OnDestroyHandle = NewChannelProxy->OnDestroy.AddRaw(this, &FChannelCurveModel<ChannelType, ChannelValue, KeyType>::FixupCurve);
|
|
}
|
|
}
|
|
|
|
|
|
template <class ChannelType, class ChannelValue, class KeyType>
|
|
void FChannelCurveModel<ChannelType, ChannelValue, KeyType>::GetCurveColorObjectAndName(UObject** OutObject, FString& OutName) const
|
|
{
|
|
if (UMovieSceneSection* Section = WeakSection.Get())
|
|
{
|
|
*OutObject = Section->GetImplicitObjectOwner();
|
|
|
|
if (const FMovieSceneChannelMetaData* MetaData = ChannelHandle.GetMetaData())
|
|
{
|
|
OutName = FString::Printf(TEXT( "%s.%s" ), *MetaData->Group.ToString(), *MetaData->DisplayText.ToString());
|
|
return;
|
|
}
|
|
OutName = GetIntentionName();
|
|
return;
|
|
}
|
|
// Just call base if it doesn't work
|
|
FCurveModel::GetCurveColorObjectAndName(OutObject, OutName);
|
|
}
|
|
|
|
|
|
// Explicit template instantiation
|
|
template class FChannelCurveModel<FMovieSceneDoubleChannel, FMovieSceneDoubleValue, double>;
|
|
template class FChannelCurveModel<FMovieSceneFloatChannel, FMovieSceneFloatValue, float>;
|
|
template class FChannelCurveModel<FMovieSceneIntegerChannel, int32, int32>;
|
|
template class FChannelCurveModel<FMovieSceneBoolChannel, bool, bool>;
|
|
template class FChannelCurveModel<FMovieSceneByteChannel, uint8, uint8>;
|