Files
UnrealEngineUWP/Engine/Source/Runtime/MovieSceneCapture/Private/AutomatedLevelSequenceCapture.cpp
Andrew Grant d077828526 Copying //UE4/Orion-Staging to Dev-Main (Originating from //Orion/Main at CL-2777663)
#lockdown Nick.Penwarden

 Change 2777555 on 2015/11/23 by Antony.Carter

	Friend List Sub Menu restyling to new designs

	#RB Nicholas.Davies
	#TESTS Check 3 sub menus of friends list (Online Status, Friends List, Settings) still function correctly with new styling.

Change 2777506 on 2015/11/23 by Andrew.Rodham

	Sequencer: Copy/Paste command binding is no longer active if the seuqnece widget is not focused

	This addresses UE-23423

	#tests Tested copy/paste inside and outside of sequencer
	#codereview Max.Chen
	#rb Max.Chen

Change 2777505 on 2015/11/23 by Andrew.Rodham

	Sequencer: Undoing the addition of a spawnable now ensures its actor instance is deleted correctly

	This addresses UE-23450

	#tests tested the repro steps on the bug
	#codereview Max.Chen
	#rb Max.Chen

Change 2777489 on 2015/11/23 by Andrew.Rodham

	Sequencer: Workflow optimizations for spawnables

	 - Editing a property on an instance of a spawnable now automatically propagates to the spawnable defaults, provided the property is not keyed
	 - Fixed a few cases where spawnables were left lingering around while scrubbing or switching between sub-sequences
	 - Fixed the root sequence instance being evaluated when there was a sub-sequence focused.
	 - Selection states are now remembered for spawnable objects when they are destroyed/re-spawned

	#codereview Max.Chen
	#tests tested loks of object types as spawnables in PIE and in editor
	#rb Max.Chen

Change 2777321 on 2015/11/23 by Terence.Burns

	Updated the usage of World->UpdateStreamingLevels to FlushStreaming levels on the advice of Dmitriy. Need this to ensure that the streaming is completed before we send it off to lightmass.

	#rb Dmitriy.Dyomin
	#Tests Run the RebuildLightmaps UAT script.

Change 2777091 on 2015/11/22 by Andrew.Grant

	Changed "inappropriate outmost" warning on package load to an error. At the very least we want this for a day or two on Orion to surface errors quickly, but may be a good thing to make a standard error since it indicates something that's likely broken.

	#rb none
	#tests Golden path in game, cooked content
	#codereview Nick.Penwarden, Michael.Noland

Change 2777037 on 2015/11/22 by Laurent.Delayen

	Additional debug info to track down https://jira.ol.epicgames.net/browse/OR-9675

	#rb martin.wilson
	#codereview martin.wilson
	#tests Golden path (PIE) + compiled for PS4

Change 2777030 on 2015/11/22 by Sam.Zamani

	#online,externalui,ps4
	- added footer option for closing the embedded web browser

	#rb none
	#tests exec command to try on ps4

Change 2777019 on 2015/11/22 by Marcus.Wassmer

	Possible fix for OR-9851
	#rb none
	#test GoldenPath, PS4
	#codereview Nick.Darnell,Matt.Kuhlenschmidt

Change 2776932 on 2015/11/22 by Max.Chen

	Sequencer: Fix editor selection so that it's not modified in response to the sequencer outliner tree node changing selection if the user is not explicitly selecting in the tree.

	#RB none
	#tests Select an actor that Sequencer doesn't control and it shouldn't deselect.

Change 2776900 on 2015/11/21 by Marcus.Wassmer

	HighQuality particle lights.
	#rb Brian.Karis
	#test GoldenPath, HQ Particles w/wo ShadowCasting.
	#codereview Olaf.Piesche, Simon.Tovey, Tim.Elek

Change 2776868 on 2015/11/21 by Brian.Karis

	Reduced temporal aa responsiveness back where it was.

Change 2776867 on 2015/11/21 by Brian.Karis

	Removed shading terminator bias meant for shadow map acne but it made character faces look worse.

Change 2776840 on 2015/11/21 by Brian.Karis

	Hair indirect lighting implemented.

	Improvements to hair shading model. No longer uses backlit parameter.

	#rb marcus.wassmer
	#tests editor

Change 2776748 on 2015/11/21 by Max.Preussner

	Sequencer: Continued to implement track label editor

	Note: still disabled, because there are a couple remaining issues

	#codereview: max.chen
	#rb: max.chen
	#test: Editor, Runtime

Change 2776493 on 2015/11/20 by Max.Preussner

	Sequencer: Wrapped the node tree context menu actions for editing in an 'Edit' section

	#codereview: max.chen
	#rb: max.chen
2015-11-24 16:45:24 -05:00

312 lines
9.3 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "MovieSceneCapturePCH.h"
#include "LevelSequencePlayer.h"
#include "AutomatedLevelSequenceCapture.h"
#include "ErrorCodes.h"
#include "ActiveMovieSceneCaptures.h"
UAutomatedLevelSequenceCapture::UAutomatedLevelSequenceCapture(const FObjectInitializer& Init)
: Super(Init)
{
#if WITH_EDITORONLY_DATA == 0
if (!HasAnyFlags(RF_ClassDefaultObject))
{
checkf(false, TEXT("Automated level sequence captures can only be used in editor builds."));
}
#else
bUseCustomStartFrame = false;
StartFrame = 0;
bUseCustomEndFrame = false;
EndFrame = 1;
WarmUpFrameCount = 0;
DelayBeforeWarmUp = 0.0f;
RemainingDelaySeconds = 0.0f;
RemainingWarmUpFrames = 0;
#endif
}
#if WITH_EDITORONLY_DATA
void UAutomatedLevelSequenceCapture::SetLevelSequenceAsset(FString AssetPath)
{
LevelSequenceAsset = MoveTemp(AssetPath);
}
void UAutomatedLevelSequenceCapture::SetLevelSequenceActor(ALevelSequenceActor* InActor)
{
LevelSequenceActor = InActor;
LevelSequenceActorId = LevelSequenceActor.GetUniqueID().GetGuid();
}
void UAutomatedLevelSequenceCapture::Initialize(TWeakPtr<FSceneViewport> InViewport)
{
// Apply command-line overrides from parent class first. This needs to be called before setting up the capture strategy with the desired frame rate.
Super::Initialize(InViewport);
// Apply command-line overrides
{
FString LevelSequenceAssetPath;
if( FParse::Value( FCommandLine::Get(), TEXT( "-LevelSequence=" ), LevelSequenceAssetPath ) )
{
LevelSequenceAsset.SetPath( LevelSequenceAssetPath );
LevelSequenceActorId = FGuid();
}
int32 StartFrameOverride;
if( FParse::Value( FCommandLine::Get(), TEXT( "-MovieStartFrame=" ), StartFrameOverride ) )
{
bUseCustomStartFrame = true;
StartFrame = StartFrameOverride;
}
int32 EndFrameOverride;
if( FParse::Value( FCommandLine::Get(), TEXT( "-MovieEndFrame=" ), EndFrameOverride ) )
{
bUseCustomEndFrame = true;
EndFrame = EndFrameOverride;
}
int32 WarmUpFrameCountOverride;
if( FParse::Value( FCommandLine::Get(), TEXT( "-MovieWarmUpFrames=" ), WarmUpFrameCountOverride ) )
{
WarmUpFrameCount = WarmUpFrameCountOverride;
}
float DelayBeforeWarmUpOverride;
if( FParse::Value( FCommandLine::Get(), TEXT( "-MovieDelayBeforeWarmUp=" ), DelayBeforeWarmUpOverride ) )
{
DelayBeforeWarmUp = DelayBeforeWarmUpOverride;
}
}
// Ensure the LevelSequence is up to date with the LevelSequenceActorId (Level sequence is only there for a nice UI)
LevelSequenceActor = FUniqueObjectGuid(LevelSequenceActorId);
ALevelSequenceActor* Actor = LevelSequenceActor.Get();
// If we don't have a valid actor, attempt to find a level sequence actor in the world that references this asset
if( Actor == nullptr )
{
if( LevelSequenceAsset.IsValid() )
{
ULevelSequence* Asset = Cast<ULevelSequence>( LevelSequenceAsset.TryLoad() );
if( Asset != nullptr )
{
for( auto It = TActorIterator<ALevelSequenceActor>( InViewport.Pin()->GetClient()->GetWorld() ); It; ++It )
{
if( It->LevelSequence == LevelSequenceAsset )
{
// Found it!
Actor = *It;
this->LevelSequenceActor = Actor;
break;
}
}
}
}
}
if (Actor)
{
// Make sure we're not playing yet (in case AutoPlay was called from BeginPlay)
if( Actor->SequencePlayer != nullptr && Actor->SequencePlayer->IsPlaying() )
{
Actor->SequencePlayer->Stop();
}
Actor->bAutoPlay = false;
// Bind to the event so we know when to capture a frame
if( Actor->SequencePlayer != nullptr )
{
OnPlayerUpdatedBinding = Actor->SequencePlayer->OnSequenceUpdated().AddUObject( this, &UAutomatedLevelSequenceCapture::SequenceUpdated );
}
}
CaptureState = ELevelSequenceCaptureState::DelayBeforeWarmUp;
RemainingDelaySeconds = FMath::Max( 0.0f, DelayBeforeWarmUp );
CaptureStrategy = MakeShareable(new FFixedTimeStepCaptureStrategy(Settings.FrameRate));
}
void UAutomatedLevelSequenceCapture::SetupFrameRange()
{
ALevelSequenceActor* Actor = LevelSequenceActor.Get();
if( Actor )
{
ULevelSequence* LevelSequence = Cast<ULevelSequence>( Actor->LevelSequence.TryLoad() );
if( LevelSequence != nullptr )
{
UMovieScene* MovieScene = LevelSequence->GetMovieScene();
if( MovieScene != nullptr )
{
const int32 SequenceStartFrame = FMath::RoundToInt( MovieScene->GetPlaybackRange().GetLowerBoundValue() * Settings.FrameRate );
const int32 SequenceEndFrame = FMath::Max( SequenceStartFrame, FMath::RoundToInt( MovieScene->GetPlaybackRange().GetUpperBoundValue() * Settings.FrameRate ) );
// Default to playing back the sequence's stored playback range
int32 PlaybackStartFrame = SequenceStartFrame;
int32 PlaybackEndFrame = SequenceEndFrame;
if( bUseCustomStartFrame )
{
PlaybackStartFrame = Settings.bUseRelativeFrameNumbers ? ( SequenceStartFrame + StartFrame ) : StartFrame;
}
if( !Settings.bUseRelativeFrameNumbers )
{
// NOTE: The frame number will be an offset from the first frame that we start capturing on, not the frame
// that we start playback at (in the case of WarmUpFrameCount being non-zero). So we'll cache out frame
// number offset before adjusting for the warm up frames.
this->FrameNumberOffset = PlaybackStartFrame;
}
if( bUseCustomEndFrame )
{
PlaybackEndFrame = FMath::Max( PlaybackStartFrame, Settings.bUseRelativeFrameNumbers ? ( SequenceEndFrame + EndFrame ) : EndFrame );
}
// We always add 1 to the number of frames we want to capture, because we want to capture both the start and end frames (which if the play range is 0, would still yield a single frame)
this->FrameCount = ( PlaybackEndFrame - PlaybackStartFrame ) + 1;
RemainingWarmUpFrames = FMath::Max( WarmUpFrameCount, 0 );
if( RemainingWarmUpFrames > 0 )
{
// We were asked to playback additional frames before we start capturing
PlaybackStartFrame -= RemainingWarmUpFrames;
}
// Override the movie scene's playback range
Actor->SequencePlayer->SetPlaybackRange(
(float)PlaybackStartFrame / (float)Settings.FrameRate,
(float)PlaybackEndFrame / (float)Settings.FrameRate );
}
}
}
}
void UAutomatedLevelSequenceCapture::Tick(float DeltaSeconds)
{
const bool bAnyFramesToCapture = OutstandingFrameCount > 0;
Super::Tick(DeltaSeconds);
ALevelSequenceActor* Actor = LevelSequenceActor.Get();
if (!Actor)
{
ULevelSequence* Asset = Cast<ULevelSequence>(LevelSequenceAsset.TryLoad());
if (Asset)
{
// Spawn a new actor
Actor = GWorld->SpawnActor<ALevelSequenceActor>();
Actor->SetSequence(Asset);
// Ensure it doesn't loop (-1 is indefinite)
Actor->PlaybackSettings.LoopCount = 0;
LevelSequenceActor = Actor;
}
else
{
FPlatformMisc::RequestExit(FMovieSceneCaptureExitCodes::AssetNotFound);
}
}
if (Actor && Actor->SequencePlayer)
{
// First off we'll stage the sequence. This just means we'll play the first frame of the animation
// and then pause it immediately
if( CaptureState == ELevelSequenceCaptureState::Staging )
{
Actor->SequencePlayer->Play();
Actor->SequencePlayer->Pause();
CaptureState = ELevelSequenceCaptureState::DelayBeforeWarmUp;
}
// Then we'll just wait a little bit. We'll delay the specified number of seconds before capturing to allow any
// textures to stream in or post processing effects to settle.
if( CaptureState == ELevelSequenceCaptureState::DelayBeforeWarmUp )
{
RemainingDelaySeconds -= DeltaSeconds;
if( RemainingDelaySeconds <= 0.0f )
{
RemainingDelaySeconds = 0.0f;
// Start warming up. Even if we're not capturing yet, this will make sure we're rendering at a
// fixed frame rate.
StartWarmup();
// Wait a frame to go by after we've set the fixed time step, so that the animation starts
// playback at a consistent time
CaptureState = ELevelSequenceCaptureState::ReadyToWarmUp;
}
}
else if( CaptureState == ELevelSequenceCaptureState::ReadyToWarmUp )
{
Actor->SequencePlayer->Play();
CaptureState = ELevelSequenceCaptureState::WarmingUp;
}
if( CaptureState == ELevelSequenceCaptureState::WarmingUp )
{
// Count down our warm up frames
if( RemainingWarmUpFrames == 0 )
{
CaptureState = ELevelSequenceCaptureState::FinishedWarmUp;
// It's time to start capturing!
StartCapture();
}
else
{
// Not ready to capture just yet
--RemainingWarmUpFrames;
}
}
if( bAnyFramesToCapture && OutstandingFrameCount == 0 )
{
// If we hit this, then we've rendered out the last frame and can stop!
StopCapture();
}
}
}
void UAutomatedLevelSequenceCapture::OnCaptureStopped()
{
ALevelSequenceActor* Actor = LevelSequenceActor.Get();
if( Actor != nullptr )
{
if( Actor->SequencePlayer != nullptr )
{
Actor->SequencePlayer->OnSequenceUpdated().Remove( OnPlayerUpdatedBinding );
}
}
Close();
FPlatformMisc::RequestExit(0);
}
void UAutomatedLevelSequenceCapture::SequenceUpdated(const ULevelSequencePlayer& Player, float CurrentTime, float PreviousTime)
{
if( bCapturing )
{
// Save the previous rendered frame
CaptureFrame( LastFrameDelta );
// Get ready to capture the next rendered frame
PrepareForScreenshot();
}
}
void UAutomatedLevelSequenceCapture::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
{
LevelSequenceActorId = LevelSequenceActor.GetUniqueID().GetGuid();
}
#endif