// Copyright Epic Games, Inc. All Rights Reserved. #include "MovieSceneGeometryCacheSection.h" #include "GeometryCache.h" #include "MovieScene.h" #include "GeometryCacheComponent.h" #include "MovieSceneTimeHelpers.h" #include "MovieSceneGeometryCacheTemplate.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(MovieSceneGeometryCacheSection) #define LOCTEXT_NAMESPACE "MovieSceneGeometryCacheSection" namespace { float GeometryCacheDeprecatedMagicNumber = TNumericLimits::Lowest(); } FMovieSceneGeometryCacheParams::FMovieSceneGeometryCacheParams() { GeometryCacheAsset = nullptr; GeometryCache_DEPRECATED = nullptr; StartOffset_DEPRECATED = GeometryCacheDeprecatedMagicNumber; EndOffset_DEPRECATED = GeometryCacheDeprecatedMagicNumber; PlayRate = 1.f; bReverse = false; } UMovieSceneGeometryCacheSection::UMovieSceneGeometryCacheSection(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { BlendType = EMovieSceneBlendType::Absolute; EvalOptions.EnableAndSetCompletionMode(EMovieSceneCompletionMode::ProjectDefault); #if WITH_EDITOR PreviousPlayRate = Params.PlayRate; #endif } TOptional UMovieSceneGeometryCacheSection::GetOffsetTime() const { return TOptional(Params.FirstLoopStartFrameOffset); } void UMovieSceneGeometryCacheSection::MigrateFrameTimes(FFrameRate SourceRate, FFrameRate DestinationRate) { if (Params.StartFrameOffset.Value > 0) { FFrameNumber NewStartFrameOffset = ConvertFrameTime(FFrameTime(Params.StartFrameOffset), SourceRate, DestinationRate).FloorToFrame(); Params.StartFrameOffset = NewStartFrameOffset; } if (Params.EndFrameOffset.Value > 0) { FFrameNumber NewEndFrameOffset = ConvertFrameTime(FFrameTime(Params.EndFrameOffset), SourceRate, DestinationRate).FloorToFrame(); Params.EndFrameOffset = NewEndFrameOffset; } if (Params.FirstLoopStartFrameOffset.Value > 0) { FFrameNumber NewFirstLoopStartFrameOffset = ConvertFrameTime(FFrameTime(Params.FirstLoopStartFrameOffset), SourceRate, DestinationRate).FloorToFrame(); Params.FirstLoopStartFrameOffset = NewFirstLoopStartFrameOffset; } } UObject* UMovieSceneGeometryCacheSection::GetSourceObject() const { return Params.GeometryCacheAsset; } void UMovieSceneGeometryCacheSection::PostLoad() { Super::PostLoad(); UMovieScene* MovieSceneOuter = GetTypedOuter(); if (!MovieSceneOuter) { return; } FFrameRate DisplayRate = MovieSceneOuter->GetDisplayRate(); FFrameRate TickResolution = MovieSceneOuter->GetTickResolution(); if (Params.StartOffset_DEPRECATED != GeometryCacheDeprecatedMagicNumber) { Params.StartFrameOffset = ConvertFrameTime(FFrameTime::FromDecimal(DisplayRate.AsDecimal() * Params.StartOffset_DEPRECATED), DisplayRate, TickResolution).FrameNumber; Params.StartOffset_DEPRECATED = GeometryCacheDeprecatedMagicNumber; } if (Params.EndOffset_DEPRECATED != GeometryCacheDeprecatedMagicNumber) { Params.EndFrameOffset = ConvertFrameTime(FFrameTime::FromDecimal(DisplayRate.AsDecimal() * Params.EndOffset_DEPRECATED), DisplayRate, TickResolution).FrameNumber; Params.EndOffset_DEPRECATED = GeometryCacheDeprecatedMagicNumber; } if (Params.GeometryCache_DEPRECATED.ResolveObject() != nullptr && Params.GeometryCacheAsset == nullptr) { UGeometryCacheComponent *Comp = Cast(Params.GeometryCache_DEPRECATED.ResolveObject()); if (Comp) { Params.GeometryCacheAsset = (Comp->GetGeometryCache()); } } } void UMovieSceneGeometryCacheSection::Serialize(FArchive& Ar) { Ar.UsingCustomVersion(FSequencerObjectVersion::GUID); Super::Serialize(Ar); } TOptional > UMovieSceneGeometryCacheSection::GetAutoSizeRange() const { FFrameRate FrameRate = GetTypedOuter()->GetTickResolution(); FFrameTime AnimationLength = Params.GetSequenceLength() * FrameRate; int32 IFrameNumber = AnimationLength.FrameNumber.Value + static_cast(AnimationLength.GetSubFrame() + 0.5f); return TRange(GetInclusiveStartFrame(), GetInclusiveStartFrame() + IFrameNumber + 1); } void UMovieSceneGeometryCacheSection::TrimSection(FQualifiedFrameTime TrimTime, bool bTrimLeft, bool bDeleteKeys) { SetFlags(RF_Transactional); if (TryModify()) { if (bTrimLeft) { FFrameRate FrameRate = GetTypedOuter()->GetTickResolution(); Params.FirstLoopStartFrameOffset = HasStartFrame() ? GetFirstLoopStartOffsetAtTrimTime(TrimTime, Params, GetInclusiveStartFrame(), FrameRate) : 0; } Super::TrimSection(TrimTime, bTrimLeft, bDeleteKeys); } } UMovieSceneSection* UMovieSceneGeometryCacheSection::SplitSection(FQualifiedFrameTime SplitTime, bool bDeleteKeys) { const FFrameNumber InitialFirstLoopStartFrameOffset = Params.FirstLoopStartFrameOffset; FFrameRate FrameRate = GetTypedOuter()->GetTickResolution(); const FFrameNumber NewOffset = HasStartFrame() ? GetFirstLoopStartOffsetAtTrimTime(SplitTime, Params, GetInclusiveStartFrame(), FrameRate) : 0; UMovieSceneSection* NewSection = Super::SplitSection(SplitTime, bDeleteKeys); if (NewSection != nullptr) { UMovieSceneGeometryCacheSection* NewGeometrySection = Cast(NewSection); NewGeometrySection->Params.FirstLoopStartFrameOffset = NewOffset; } // Restore original offset modified by splitting Params.FirstLoopStartFrameOffset = InitialFirstLoopStartFrameOffset; return NewSection; } void UMovieSceneGeometryCacheSection::GetSnapTimes(TArray& OutSnapTimes, bool bGetSectionBorders) const { Super::GetSnapTimes(OutSnapTimes, bGetSectionBorders); const FFrameRate FrameRate = GetTypedOuter()->GetTickResolution(); const FFrameNumber StartFrame = GetInclusiveStartFrame(); const FFrameNumber EndFrame = GetExclusiveEndFrame() - 1; // -1 because we don't need to add the end frame twice const float AnimPlayRate = FMath::IsNearlyZero(Params.PlayRate) ? 1.0f : Params.PlayRate; const float SeqLengthSeconds = static_cast(Params.GetSequenceLength() - FrameRate.AsSeconds(Params.StartFrameOffset + Params.EndFrameOffset) / AnimPlayRate); const float FirstLoopSeqLengthInSeconds = static_cast(SeqLengthSeconds - FrameRate.AsSeconds(Params.FirstLoopStartFrameOffset) / AnimPlayRate); const FFrameTime SequenceFrameLength = SeqLengthSeconds * FrameRate; const FFrameTime FirstLoopSequenceFrameLength = FirstLoopSeqLengthInSeconds * FrameRate; if (SequenceFrameLength.FrameNumber > 1) { // Snap to the repeat times bool IsFirstLoop = true; FFrameTime CurrentTime = StartFrame; while (CurrentTime < EndFrame) { OutSnapTimes.Add(CurrentTime.FrameNumber); if (IsFirstLoop) { CurrentTime += FirstLoopSequenceFrameLength; IsFirstLoop = false; } else { CurrentTime += SequenceFrameLength; } } } } float UMovieSceneGeometryCacheSection::MapTimeToAnimation(float ComponentDuration, FFrameTime InPosition, FFrameRate InFrameRate) const { FMovieSceneGeometryCacheSectionTemplateParameters TemplateParams(Params, GetInclusiveStartFrame(), GetExclusiveEndFrame()); return TemplateParams.MapTimeToAnimation(ComponentDuration, InPosition, InFrameRate); } #if WITH_EDITOR void UMovieSceneGeometryCacheSection::PreEditChange(FProperty* PropertyAboutToChange) { // Store the current play rate so that we can compute the amount to compensate the section end time when the play rate changes PreviousPlayRate = Params.PlayRate; Super::PreEditChange(PropertyAboutToChange); } void UMovieSceneGeometryCacheSection::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { // Adjust the duration automatically if the play rate changes if (PropertyChangedEvent.Property != nullptr && PropertyChangedEvent.Property->GetFName() == TEXT("PlayRate")) { float NewPlayRate = Params.PlayRate; if (!FMath::IsNearlyZero(NewPlayRate)) { float CurrentDuration = static_cast(UE::MovieScene::DiscreteSize(GetRange())); float NewDuration = CurrentDuration * (PreviousPlayRate / NewPlayRate); SetEndFrame(GetInclusiveStartFrame() + FMath::FloorToInt(NewDuration)); PreviousPlayRate = NewPlayRate; } } Super::PostEditChangeProperty(PropertyChangedEvent); } #endif float FMovieSceneGeometryCacheParams::GetSequenceLength() const { return GeometryCacheAsset != nullptr ? GeometryCacheAsset->CalculateDuration() : 0.f; } #undef LOCTEXT_NAMESPACE