Files
UnrealEngine/Engine/Plugins/MovieScene/MovieRenderPipeline/Source/MovieRenderPipelineSettings/Private/MoviePipelineConsoleVariableSetting.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

400 lines
12 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MoviePipelineConsoleVariableSetting.h"
#include "MoviePipelineQueue.h"
#include "MovieRenderPipelineCoreModule.h"
#include "MovieScene.h"
#include "LevelSequence.h"
#include "MovieSceneCommonHelpers.h"
#include "HAL/IConsoleManager.h"
#include "Engine/World.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Misc/DefaultValueHelper.h"
#include "Sections/MovieSceneConsoleVariableTrackInterface.h"
#include "Sections/MovieSceneCVarSection.h"
#include "Tracks/MovieSceneCVarTrack.h"
#include "Tracks/MovieSceneSubTrack.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(MoviePipelineConsoleVariableSetting)
#define LOCTEXT_NAMESPACE "MoviePipelineConsoleVariableSetting"
namespace UE
{
namespace MoviePipeline
{
static void SetValue(IConsoleVariable* InCVar, float InValue)
{
check(InCVar);
// When Set is called on a cvar the value is turned into a string. With very large
// floats this is turned into scientific notation. If the cvar is later retrieved as
// an integer, the scientific notation doesn't parse into integer correctly. We'll
// cast to integer first (to avoid scientific notation) if we know the cvar is an integer.
if (InCVar->IsVariableInt())
{
InCVar->SetWithCurrentPriority(static_cast<int32>(InValue), NAME_None, ECVF_SetByConsole, ECVF_SetByScalability);
}
else if (InCVar->IsVariableBool())
{
InCVar->SetWithCurrentPriority(InValue != 0.f ? true : false, NAME_None, ECVF_SetByConsole, ECVF_SetByScalability);
}
else
{
InCVar->SetWithCurrentPriority(InValue, NAME_None, ECVF_SetByConsole, ECVF_SetByScalability);
}
}
/**
* Determine if the given UMovieScene contains any CVar tracks that are not muted, and also have an
* active section with CVars that are set. Sub-sequences will be searched for CVar tracks as well.
*/
static bool IsCVarTrackPresent(const UMovieScene* InMovieScene)
{
if (!InMovieScene)
{
return false;
}
for (UMovieSceneTrack* Track : InMovieScene->GetTracks())
{
// Process CVar tracks. Return immediately if any of the CVar tracks contain CVars that are set.
// If this is the case, sub tracks don't need to be searched.
if (Track->IsA<UMovieSceneCVarTrack>())
{
const UMovieSceneCVarTrack* CVarTrack = Cast<UMovieSceneCVarTrack>(Track);
for (const UMovieSceneSection* Section : CVarTrack->GetAllSections())
{
const UMovieSceneCVarSection* CVarSection = Cast<UMovieSceneCVarSection>(Section);
if (!CVarSection || !MovieSceneHelpers::IsSectionKeyable(CVarSection))
{
continue;
}
// Does this CVar track have anything in it?
if (!CVarSection->ConsoleVariableCollections.IsEmpty() || !CVarSection->ConsoleVariables.ValuesByCVar.IsEmpty())
{
return true;
}
}
}
// Process sub tracks (which could potentially contain other sequences with CVar tracks)
if (Track->IsA<UMovieSceneSubTrack>())
{
const UMovieSceneSubTrack* SubTrack = Cast<UMovieSceneSubTrack>(Track);
for (const UMovieSceneSection* Section : SubTrack->GetAllSections())
{
const UMovieSceneSubSection* SubSection = Cast<UMovieSceneSubSection>(Section);
if (!SubSection || !MovieSceneHelpers::IsSectionKeyable(SubSection))
{
continue;
}
// Recurse into sub-sequences
if (const UMovieSceneSequence* SubSequence = SubSection->GetSequence())
{
if (IsCVarTrackPresent(SubSequence->GetMovieScene()))
{
return true;
}
}
}
}
}
return false;
}
}
}
#if WITH_EDITOR
FText UMoviePipelineConsoleVariableSetting::GetFooterText(UMoviePipelineExecutorJob* InJob) const
{
if (!InJob)
{
return FText();
}
const ULevelSequence* LoadedSequence = Cast<ULevelSequence>(InJob->Sequence.TryLoad());
if (!LoadedSequence)
{
return FText();
}
if (!UE::MoviePipeline::IsCVarTrackPresent(LoadedSequence->MovieScene))
{
return FText();
}
return FText(LOCTEXT(
"SequencerCvarWarning",
"The current job contains a Level Sequence with a Console Variables Track, additional settings are configured in Sequencer."));
}
FString UMoviePipelineConsoleVariableSetting::ResolvePresetValue(const FString& InCVarName) const
{
FString ResolvedValue;
// Iterate the presets in reverse; cvars in presets at the end of the array take precedence.
for (int32 Index = ConsoleVariablePresets.Num() - 1; Index >= 0; --Index)
{
const TScriptInterface<IMovieSceneConsoleVariableTrackInterface>& Preset = ConsoleVariablePresets[Index];
if (!Preset)
{
continue;
}
const bool bOnlyIncludeChecked = true;
TArray<TPair<FString, FString>> PresetCVars;
Preset->GetConsoleVariablesForTrack(bOnlyIncludeChecked, PresetCVars);
for (const TPair<FString, FString>& CVar : PresetCVars)
{
if (CVar.Key.Equals(InCVarName))
{
// Found a matching cvar in the preset
return CVar.Value;
}
}
}
return FString();
}
FString UMoviePipelineConsoleVariableSetting::ResolveDisabledValue(const FMoviePipelineConsoleVariableEntry& InEntry) const
{
// There may be multiple cvar overrides w/ the same name, so even if the provided entry is disabled, we still need
// to look through the other entries for cvars with the same name. Iterate in reverse, since cvars at the end of the
// array take precedence.
for (int32 Index = CVars.Num() - 1; Index >= 0; --Index)
{
const FMoviePipelineConsoleVariableEntry& CVarEntry = CVars[Index];
if (CVarEntry.Name.Equals(InEntry.Name) && CVarEntry.bIsEnabled)
{
return FString::SanitizeFloat(CVarEntry.Value);
}
}
// If no override value was found, look for a value from the presets
const FString PresetValue = ResolvePresetValue(InEntry.Name);
if (!PresetValue.IsEmpty())
{
return PresetValue;
}
FConsoleVariablesEditorModule& ConsoleVariablesEditorModule = FConsoleVariablesEditorModule::Get();
const TWeakPtr<FConsoleVariablesEditorCommandInfo> WeakCommandInfo = ConsoleVariablesEditorModule.FindCommandInfoByName(InEntry.Name);
// Fall back to the startup value of the cvar
const TSharedPtr<FConsoleVariablesEditorCommandInfo> CommandInfo = WeakCommandInfo.Pin();
if (CommandInfo.IsValid())
{
return CommandInfo->StartupValueAsString;
}
return FString();
}
FMoviePipelineConsoleVariableEntry* UMoviePipelineConsoleVariableSetting::GetCVarAtIndex(const int32 InIndex)
{
if (!CVars.IsValidIndex(InIndex))
{
return nullptr;
}
return &CVars[InIndex];
}
#endif // WITH_EDITOR
void UMoviePipelineConsoleVariableSetting::SetupForPipelineImpl(UMoviePipeline* InPipeline)
{
ApplyCVarSettings(true);
}
void UMoviePipelineConsoleVariableSetting::TeardownForPipelineImpl(UMoviePipeline* InPipeline)
{
ApplyCVarSettings(false);
}
void UMoviePipelineConsoleVariableSetting::ApplyCVarSettings(const bool bOverrideValues)
{
if (bOverrideValues)
{
MergeInPresetConsoleVariables();
PreviousConsoleVariableValues.Reset();
PreviousConsoleVariableValues.SetNumZeroed(MergedConsoleVariables.Num());
}
int32 Index = 0;
for(const FMoviePipelineConsoleVariableEntry& CVarEntry : MergedConsoleVariables)
{
// We don't use the shared macro here because we want to soft-warn the user instead of tripping an ensure over missing cvar values.
const FString TrimmedCvar = CVarEntry.Name.TrimStartAndEnd();
IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(*TrimmedCvar);
if (CVar)
{
if (bOverrideValues)
{
PreviousConsoleVariableValues[Index] = CVar->GetFloat();
UE::MoviePipeline::SetValue(CVar, CVarEntry.Value);
UE_LOG(LogMovieRenderPipeline, Log, TEXT("Applying CVar \"%s\" PreviousValue: %f NewValue: %f"), *CVarEntry.Name, PreviousConsoleVariableValues[Index], CVarEntry.Value);
}
else
{
UE::MoviePipeline::SetValue(CVar, PreviousConsoleVariableValues[Index]);
UE_LOG(LogMovieRenderPipeline, Log, TEXT("Restoring CVar \"%s\" PreviousValue: %f NewValue: %f"), *CVarEntry.Name, CVarEntry.Value, PreviousConsoleVariableValues[Index]);
}
}
else
{
UE_LOG(LogMovieRenderPipeline, Warning, TEXT("Failed to apply CVar \"%s\" due to no cvar by that name. Ignoring."), *CVarEntry.Name);
}
Index++;
}
if (bOverrideValues)
{
for (const FString& Command : StartConsoleCommands)
{
UE_LOG(LogMovieRenderPipeline, Log, TEXT("Executing Console Command \"%s\" before shot starts."), *Command);
UKismetSystemLibrary::ExecuteConsoleCommand(GetWorld(), Command, nullptr);
}
}
else
{
for (const FString& Command : EndConsoleCommands)
{
UE_LOG(LogMovieRenderPipeline, Log, TEXT("Executing Console Command \"%s\" after shot ends."), *Command);
UKismetSystemLibrary::ExecuteConsoleCommand(GetWorld(), Command, nullptr);
}
}
}
TArray<FMoviePipelineConsoleVariableEntry> UMoviePipelineConsoleVariableSetting::GetConsoleVariables() const
{
return CVars;
}
bool UMoviePipelineConsoleVariableSetting::RemoveConsoleVariable(const FString& Name, const bool bRemoveAllInstances)
{
if (bRemoveAllInstances)
{
const int32 NumRemoved = CVars.RemoveAll([&Name](const FMoviePipelineConsoleVariableEntry& Entry)
{
return Entry.Name.Equals(Name);
});
return NumRemoved != 0;
}
const int32 LastMatch = CVars.FindLastByPredicate([&Name](const FMoviePipelineConsoleVariableEntry& Entry)
{
return Entry.Name.Equals(Name);
});
if (LastMatch != INDEX_NONE)
{
CVars.RemoveAt(LastMatch);
return true;
}
return false;
}
bool UMoviePipelineConsoleVariableSetting::AddOrUpdateConsoleVariable(const FString& Name, const float Value)
{
const int32 LastMatch = CVars.FindLastByPredicate([&Name](const FMoviePipelineConsoleVariableEntry& Entry)
{
return Entry.Name.Equals(Name);
});
if (LastMatch != INDEX_NONE)
{
CVars[LastMatch].Value = Value;
return true;
}
CVars.Add(FMoviePipelineConsoleVariableEntry(Name, Value));
return true;
}
bool UMoviePipelineConsoleVariableSetting::AddConsoleVariable(const FString& Name, const float Value)
{
CVars.Add(FMoviePipelineConsoleVariableEntry(Name, Value));
return true;
}
bool UMoviePipelineConsoleVariableSetting::UpdateConsoleVariableEnableState(const FString& Name, const bool bIsEnabled)
{
const int32 LastMatch = CVars.FindLastByPredicate([&Name](const FMoviePipelineConsoleVariableEntry& Entry)
{
return Entry.Name.Equals(Name);
});
if (LastMatch != INDEX_NONE)
{
CVars[LastMatch].bIsEnabled = bIsEnabled;
return true;
}
return false;
}
void UMoviePipelineConsoleVariableSetting::MergeInPresetConsoleVariables()
{
MergedConsoleVariables.Reset();
// Merge in the presets
for (const TScriptInterface<IMovieSceneConsoleVariableTrackInterface>& Preset : ConsoleVariablePresets)
{
if (!Preset)
{
UE_LOG(LogMovieRenderPipeline, Warning, TEXT("Invalid CVar preset specified. Ignoring."));
continue;
}
const bool bOnlyIncludeChecked = true;
TArray<TTuple<FString, FString>> PresetCVars;
Preset->GetConsoleVariablesForTrack(bOnlyIncludeChecked, PresetCVars);
for (const TTuple<FString, FString>& CVarPair : PresetCVars)
{
float CVarFloatValue = 0.0f;
bool bCvarBoolValue = false;
if (FDefaultValueHelper::ParseFloat(CVarPair.Value, CVarFloatValue))
{
MergedConsoleVariables.Add(FMoviePipelineConsoleVariableEntry(CVarPair.Key, CVarFloatValue));
}
else if (FDefaultValueHelper::ParseBool(CVarPair.Value, bCvarBoolValue))
{
MergedConsoleVariables.Add(FMoviePipelineConsoleVariableEntry(CVarPair.Key, bCvarBoolValue ? 1.0f : 0.0f));
}
else
{
UE_LOG(LogMovieRenderPipeline, Warning, TEXT("Failed to apply CVar \"%s\" (from preset \"%s\") because value could not be parsed into a float. Ignoring."),
*CVarPair.Key, *Preset.GetObject()->GetName());
}
}
}
// Merge in the overrides
for (const FMoviePipelineConsoleVariableEntry& Entry : CVars)
{
if (!Entry.bIsEnabled)
{
continue;
}
MergedConsoleVariables.Add(Entry);
}
}
#undef LOCTEXT_NAMESPACE