You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
========================== 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]
353 lines
8.9 KiB
C++
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;
|
|
} |