Files
UnrealEngineUWP/Engine/Source/Runtime/LevelSequence/Private/LevelSequencePlayer.cpp
Max Chen dfad80bd9e Copying //UE4/Dev-Sequencer to Dev-Main (//UE4/Dev-Main)
==========================
MAJOR FEATURES + CHANGES
==========================

Change 2800717 on 2015/12/11 by Max.Chen

	Sequencer: Sort the key times for drawing to fix path trajectory.
	#jira UE-24331

Change 2803299 on 2015/12/15 by Max.Chen

	Sequencer: Fix property names so that they're the display names. For example, "DepthOfFieldFStop" now reads as "Aperture F Stop"

Change 2804586 on 2015/12/15 by Max.Chen

	Sequencer: Add zoom in/out with shortcuts underscore and equals.

Change 2811823 on 2015/12/23 by Max.Preussner

	Editor: Added UI action for creating new content browser folders; code cleanup; removed dead code

	Based on GitHub PR #1809 by artemavrin (https://github.com/EpicGames/UnrealEngine/pull/1809)

	#github: 1809

Change 2811839 on 2015/12/23 by Max.Preussner

	StereoPanorama: Code cleanup pass

	Based on GitHub PR# 1756 by ETayrienHBO (https://github.com/EpicGames/UnrealEngine/pull/1756)

	Also:
	- NULL to nullptr
	- namespaced enums to enum classes
	- consistent whitespace, line breaks and parentheses

	#github: 1756

Change 2819172 on 2016/01/07 by Andrew.Rodham

	Sequencer: Marquee and move modes are now automatically activated based on sequencer hotspot

Change 2819176 on 2016/01/07 by Andrew.Rodham

	Sequencer: Various cosmetic fixes

	  - Added icons to tracks
	  - Removed SAnimationOutlinerTreeNode dependency from FSequencerDisplayNode (to enable future customization of shot/event track etc)
	  - Added spacer nodes between top level display nodes
	  - Various hover states and highlights

Change 2819445 on 2016/01/07 by Andrew.Rodham

	Sequencer: Rendering out a capture from the composition graph now renders at the correct size even if r.ScreenPercentage is not 100.
	#jira UE-24920

Change 2820747 on 2016/01/08 by Andrew.Rodham

	Sequencer: Added option to close the editor when capturing starts
	#jira UE-21932

Change 2827701 on 2016/01/13 by Max.Preussner

	Media: Updating audio track specs each frame to better support streaming media and variable streams.

Change 2828465 on 2016/01/14 by Max.Preussner

	Media: Better visualization of unknown media durations

Change 2828469 on 2016/01/14 by Max.Preussner

	Media: Checking URL scheme on URLs that didn't pass the file extension filter

Change 2834888 on 2016/01/19 by Max.Preussner

	Core: TQueue modernization pass

Change 2834934 on 2016/01/19 by Max.Preussner

	Core: Implemented TTripleBuffer for triple buffers.

Change 2834950 on 2016/01/19 by Max.Preussner

	Core: Added unit tests for TTripleBuffer dirty flag

Change 2835488 on 2016/01/20 by Max.Preussner

	Core: More descriptive method names, initialization constructor, unit tests for TTripleBuffer

Change 2837515 on 2016/01/20 by Max.Chen

	Sequencer: Command line options for custom passes.

Change 2837517 on 2016/01/20 by Max.Chen

	Sequencer: Fix crash in visibility track instance on PIE.

Change 2837518 on 2016/01/20 by Max.Chen

	Sequencer: Add option to lock to frame rate while playing.
	#jira UETOOL-475

Change 2837523 on 2016/01/20 by Max.Chen

	Sequencer: Capture thumbnail on level sequence asset save.

Change 2837527 on 2016/01/20 by Max.Chen

	Sequencer: Added preroll for subsequences. Refactor instance update to combine data in EMovieSceneUpdateData.
	#jira UE-25380

Change 2837537 on 2016/01/20 by Max.Chen

	Sequencer: Add sequencer transport controls back into viewports.
	#jira UE-25460

Change 2837561 on 2016/01/20 by Max.Chen

	Sequencer: Added ability to convert a possessable to a spawnable

	  - This option is available for any root-level possessable object bindings
	  - It will currently delete the existing possessable (we could make this behaviour optional in future)
	  - There is currently no check to sett if the actor is possessed by subsequent sub-sequences. If this is the case, using a possessable, or externally owned spawnable would be a better bet.

Change 2837565 on 2016/01/20 by Max.Chen

[CL 2858958 by Max Chen in Main branch]
2016-02-08 13:35:28 -05:00

353 lines
8.9 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "LevelSequencePCH.h"
#include "LevelSequencePlayer.h"
#include "MovieScene.h"
#include "MovieSceneSequence.h"
#include "MovieSceneSequenceInstance.h"
#include "LevelSequenceSpawnRegister.h"
struct FTickAnimationPlayers : public FTickableGameObject
{
TArray<TWeakObjectPtr<ULevelSequencePlayer>> ActiveInstances;
virtual bool IsTickable() const override
{
return ActiveInstances.Num() != 0;
}
virtual TStatId GetStatId() const override
{
RETURN_QUICK_DECLARE_CYCLE_STAT(FTickAnimationPlayers, STATGROUP_Tickables);
}
virtual void Tick(float DeltaSeconds) override
{
for (int32 Index = 0; Index < ActiveInstances.Num();)
{
if (auto* Player = ActiveInstances[Index].Get())
{
Player->Update(DeltaSeconds);
++Index;
}
else
{
ActiveInstances.RemoveAt(Index, 1, false);
}
}
}
};
struct FAutoDestroyAnimationTicker
{
FAutoDestroyAnimationTicker()
{
FCoreDelegates::OnPreExit.AddLambda([&]{
Impl.Reset();
});
}
void Add(ULevelSequencePlayer* Player)
{
if (!Impl.IsValid())
{
Impl.Reset(new FTickAnimationPlayers);
}
Impl->ActiveInstances.Add(Player);
}
TUniquePtr<FTickAnimationPlayers> Impl;
} GAnimationPlayerTicker;
/* ULevelSequencePlayer structors
*****************************************************************************/
ULevelSequencePlayer::ULevelSequencePlayer(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, LevelSequence(nullptr)
, bIsPlaying(false)
, TimeCursorPosition(0.0f)
, StartTime(0.f)
, EndTime(0.f)
, CurrentNumLoops(0)
, bHasCleanedUpSequence(false)
{
SpawnRegister = MakeShareable(new FLevelSequenceSpawnRegister);
}
/* ULevelSequencePlayer interface
*****************************************************************************/
ULevelSequencePlayer* ULevelSequencePlayer::CreateLevelSequencePlayer(UObject* WorldContextObject, ULevelSequence* InLevelSequence, FLevelSequencePlaybackSettings Settings)
{
if (InLevelSequence == nullptr)
{
return nullptr;
}
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject);
check(World != nullptr);
ULevelSequencePlayer* NewPlayer = NewObject<ULevelSequencePlayer>(GetTransientPackage(), NAME_None, RF_Transient);
check(NewPlayer != nullptr);
NewPlayer->Initialize(InLevelSequence, World, Settings);
// Automatically tick this player
GAnimationPlayerTicker.Add(NewPlayer);
return NewPlayer;
}
bool ULevelSequencePlayer::IsPlaying() const
{
return bIsPlaying;
}
void ULevelSequencePlayer::Pause()
{
bIsPlaying = false;
}
void ULevelSequencePlayer::Stop()
{
bIsPlaying = false;
TimeCursorPosition = PlaybackSettings.PlayRate < 0.f ? GetLength() : 0.f;
CurrentNumLoops = 0;
// todo: Trigger an event?
}
void ULevelSequencePlayer::Play()
{
if ((LevelSequence == nullptr) || !World.IsValid())
{
return;
}
// @todo Sequencer playback: Should we recreate the instance every time?
// We must not recreate the instance since it holds stateful information (such as which objects it has spawned). Recreating the instance would break any
if (!RootMovieSceneInstance.IsValid())
{
RootMovieSceneInstance = MakeShareable(new FMovieSceneSequenceInstance(*LevelSequence));
RootMovieSceneInstance->RefreshInstance(*this);
}
bIsPlaying = true;
bHasCleanedUpSequence = false;
UpdateMovieSceneInstance(TimeCursorPosition, TimeCursorPosition);
}
void ULevelSequencePlayer::PlayLooping(int32 NumLoops)
{
PlaybackSettings.LoopCount = NumLoops;
Play();
}
float ULevelSequencePlayer::GetPlaybackPosition() const
{
return TimeCursorPosition;
}
void ULevelSequencePlayer::SetPlaybackPosition(float NewPlaybackPosition)
{
float LastTimePosition = TimeCursorPosition;
TimeCursorPosition = NewPlaybackPosition;
OnCursorPositionChanged();
UpdateMovieSceneInstance(TimeCursorPosition, LastTimePosition);
}
float ULevelSequencePlayer::GetLength() const
{
return EndTime - StartTime;
}
float ULevelSequencePlayer::GetPlayRate() const
{
return PlaybackSettings.PlayRate;
}
void ULevelSequencePlayer::SetPlayRate(float PlayRate)
{
PlaybackSettings.PlayRate = PlayRate;
}
void ULevelSequencePlayer::SetPlaybackRange( const float NewStartTime, const float NewEndTime )
{
StartTime = NewStartTime;
EndTime = FMath::Max(NewEndTime, StartTime);
TimeCursorPosition = FMath::Clamp(TimeCursorPosition, 0.f, GetLength());
}
void ULevelSequencePlayer::OnCursorPositionChanged()
{
float Length = GetLength();
// Handle looping or stopping
if (TimeCursorPosition >= Length || TimeCursorPosition < 0)
{
if (PlaybackSettings.LoopCount < 0 || CurrentNumLoops < PlaybackSettings.LoopCount)
{
++CurrentNumLoops;
const float Overplay = FMath::Fmod(TimeCursorPosition, Length);
TimeCursorPosition = Overplay < 0 ? Length + Overplay : Overplay;
SpawnRegister->ForgetExternallyOwnedSpawnedObjects(*this);
}
else
{
// Stop playing without modifying the playback position
// @todo: trigger an event?
bIsPlaying = false;
CurrentNumLoops = 0;
}
}
}
/* ULevelSequencePlayer implementation
*****************************************************************************/
void ULevelSequencePlayer::Initialize(ULevelSequence* InLevelSequence, UWorld* InWorld, const FLevelSequencePlaybackSettings& Settings)
{
LevelSequence = InLevelSequence;
World = InWorld;
PlaybackSettings = Settings;
if (UMovieScene* MovieScene = LevelSequence->GetMovieScene())
{
TRange<float> PlaybackRange = MovieScene->GetPlaybackRange();
SetPlaybackRange(PlaybackRange.GetLowerBoundValue(), PlaybackRange.GetUpperBoundValue());
}
// Ensure everything is set up, ready for playback
Stop();
}
/* IMovieScenePlayer interface
*****************************************************************************/
void ULevelSequencePlayer::GetRuntimeObjects(TSharedRef<FMovieSceneSequenceInstance> MovieSceneInstance, const FGuid& ObjectId, TArray<UObject*>& OutObjects) const
{
UObject* FoundObject = MovieSceneInstance->FindObject(ObjectId, *this);
if (FoundObject)
{
OutObjects.Add(FoundObject);
}
}
void ULevelSequencePlayer::UpdateCameraCut(UObject* CameraObject, UObject* UnlockIfCameraObject) const
{
// skip missing player controller
APlayerController* PC = World->GetGameInstance()->GetFirstLocalPlayerController();
if (PC == nullptr)
{
return;
}
// skip same view target
AActor* ViewTarget = PC->GetViewTarget();
if (CameraObject == ViewTarget)
{
return;
}
// skip unlocking if the current view target differs
ACameraActor* UnlockIfCameraActor = Cast<ACameraActor>(UnlockIfCameraObject);
if ((CameraObject == nullptr) && (UnlockIfCameraActor != ViewTarget))
{
return;
}
// override the player controller's view target
ACameraActor* CameraActor = Cast<ACameraActor>(CameraObject);
FViewTargetTransitionParams TransitionParams;
PC->SetViewTarget(CameraActor, TransitionParams);
if (PC->PlayerCameraManager)
{
PC->PlayerCameraManager->bClientSimulatingViewTarget = (CameraActor != nullptr);
PC->PlayerCameraManager->bGameCameraCutThisFrame = true;
}
}
void ULevelSequencePlayer::SetViewportSettings(const TMap<FViewportClient*, EMovieSceneViewportParams>& ViewportParamsMap)
{
}
void ULevelSequencePlayer::GetViewportSettings(TMap<FViewportClient*, EMovieSceneViewportParams>& ViewportParamsMap) const
{
}
EMovieScenePlayerStatus::Type ULevelSequencePlayer::GetPlaybackStatus() const
{
return bIsPlaying ? EMovieScenePlayerStatus::Playing : EMovieScenePlayerStatus::Stopped;
}
void ULevelSequencePlayer::AddOrUpdateMovieSceneInstance(UMovieSceneSection& MovieSceneSection, TSharedRef<FMovieSceneSequenceInstance> InstanceToAdd)
{
}
void ULevelSequencePlayer::RemoveMovieSceneInstance(UMovieSceneSection& MovieSceneSection, TSharedRef<FMovieSceneSequenceInstance> InstanceToRemove)
{
}
TSharedRef<FMovieSceneSequenceInstance> ULevelSequencePlayer::GetRootMovieSceneSequenceInstance() const
{
return RootMovieSceneInstance.ToSharedRef();
}
UObject* ULevelSequencePlayer::GetPlaybackContext() const
{
return World.Get();
}
void ULevelSequencePlayer::Update(const float DeltaSeconds)
{
float LastTimePosition = TimeCursorPosition;
if (bIsPlaying)
{
TimeCursorPosition += DeltaSeconds * PlaybackSettings.PlayRate;
OnCursorPositionChanged();
UpdateMovieSceneInstance(TimeCursorPosition, LastTimePosition);
}
else if (!bHasCleanedUpSequence && TimeCursorPosition >= GetLength())
{
UpdateMovieSceneInstance(TimeCursorPosition, LastTimePosition);
bHasCleanedUpSequence = true;
SpawnRegister->DestroyAllOwnedObjects(*this);
}
}
void ULevelSequencePlayer::UpdateMovieSceneInstance(float CurrentPosition, float PreviousPosition)
{
if(RootMovieSceneInstance.IsValid())
{
EMovieSceneUpdateData UpdateData(CurrentPosition + StartTime, PreviousPosition + StartTime);
RootMovieSceneInstance->Update(UpdateData, *this);
#if WITH_EDITOR
OnLevelSequencePlayerUpdate.Broadcast(*this, CurrentPosition, PreviousPosition);
#endif
}
}
IMovieSceneSpawnRegister& ULevelSequencePlayer::GetSpawnRegister()
{
return *SpawnRegister;
}