Files
UnrealEngine/Engine/Plugins/Mutable/Source/CustomizableObjectEditor/Private/MuCOE/SMutableObjectViewer.cpp
2025-05-18 13:04:45 +08:00

371 lines
14 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MuCOE/SMutableObjectViewer.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "Framework/Views/TableViewMetadata.h"
#include "Interfaces/ITargetPlatform.h"
#include "Interfaces/ITargetPlatformManagerModule.h"
#include "MuCO/CustomizableObject.h"
#include "MuCOE/CustomizableObjectCompileRunnable.h"
#include "MuCOE/CustomizableObjectEditorStyle.h"
#include "MuCOE/SMutableCodeViewer.h"
#include "MuCOE/SMutableGraphViewer.h"
#include "Widgets/Docking/SDockTab.h"
#include "Widgets/Input/STextComboBox.h"
#include "Widgets/Input/SNumericDropDown.h"
#include "Widgets/Views/STreeView.h"
class FExtender;
class FUICommandList;
class ITableRow;
class SWidget;
struct FSlateBrush;
#define LOCTEXT_NAMESPACE "SMutableDebugger"
void SMutableObjectViewer::AddReferencedObjects(FReferenceCollector& Collector)
{
Collector.AddReferencedObject(CustomizableObject);
}
FString SMutableObjectViewer::GetReferencerName() const
{
return TEXT("SMutableObjectViewer");
}
void SMutableObjectViewer::Construct(const FArguments& InArgs, UCustomizableObject* InObject)
{
check(InObject);
CustomizableObject = InObject;
// Initialize the debugger compile options
CompileOptions = InObject->GetPrivate()->GetCompileOptions();
{
ITargetPlatformManagerModule* TPM = GetTargetPlatformManager();
const TArray<ITargetPlatform*>& Platforms = TPM->GetActiveTargetPlatforms();
CompileOptions.TargetPlatform = Platforms.IsEmpty() ? nullptr : Platforms[0];
}
FToolBarBuilder ToolbarBuilder(TSharedPtr<const FUICommandList>(), FMultiBoxCustomization::None, TSharedPtr<FExtender>(), true);
ToolbarBuilder.SetLabelVisibility(EVisibility::Visible);
ToolbarBuilder.SetStyle(&FAppStyle::Get(), "SlimToolBar");
ToolbarBuilder.BeginSection("Compilation");
ToolbarBuilder.AddToolBarButton(
FUIAction(FExecuteAction::CreateSP(this, &SMutableObjectViewer::GenerateMutableGraphPressed)),
NAME_None,
LOCTEXT("GenerateMutableGraph", "Unreal to Mutable Graph"),
LOCTEXT("GenerateMutableGraphTooltip", "Generate a mutable graph from the customizable object source graph."),
FSlateIcon(FCustomizableObjectEditorStyle::Get().GetStyleSetName(), "CustomizableObjectDebugger.GenerateMutableGraph", "CustomizableObjectDebugger.GenerateMutableGraph.Small"),
EUserInterfaceActionType::Button
);
ToolbarBuilder.AddToolBarButton(
FUIAction(FExecuteAction::CreateSP(this, &SMutableObjectViewer::CompileMutableCodePressed)),
NAME_None,
LOCTEXT("GenerateMutableCode", "Unreal to Mutable Code"),
LOCTEXT("GenerateMutableCodeTooltipFromGraph", "Generate a mutable code from the customizable object source graph."),
FSlateIcon(FCustomizableObjectEditorStyle::Get().GetStyleSetName(), "CustomizableObjectDebugger.CompileMutableCode", "CustomizableObjectDebugger.CompileMutableCode.Small"),
EUserInterfaceActionType::Button
);
ToolbarBuilder.AddComboButton(
FUIAction(),
FOnGetContent::CreateSP(this, &SMutableObjectViewer::GenerateCompileOptionsMenuContent),
LOCTEXT("Compile_Options_Label", "Compile Options"),
LOCTEXT("Compile_Options_Tooltip", "Change Compile Options"),
TAttribute<FSlateIcon>(),
true);
ToolbarBuilder.EndSection();
ChildSlot
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.VAlign(VAlign_Center)
[
ToolbarBuilder.MakeWidget()
]
+ SVerticalBox::Slot()
.FillHeight(1.0f)
[
SAssignNew(DebuggerContentsBox, SBorder)
]
];
}
void SMutableObjectViewer::GenerateMutableGraphPressed()
{
// Convert from Unreal graph to Mutable graph.
TSharedRef<FCustomizableObjectCompiler> Compiler = MakeShared<FCustomizableObjectCompiler>();
TArray<TSoftObjectPtr<const UTexture>> RuntimeTextures;
TArray<FMutableSourceTextureData> CompilerTextures;
TArray<TSoftObjectPtr<const UStreamableRenderAsset>> RuntimeMeshes;
TArray<FMutableSourceMeshData> CompilerMeshes;
mu::Ptr<mu::Node> RootNode = Compiler->Export(CustomizableObject, CompileOptions, RuntimeTextures, CompilerTextures, RuntimeMeshes, CompilerMeshes);
if (!RootNode)
{
// TODO: Show errors
return;
}
FString DataTag = FString::Printf(TEXT("Mutable Graph : %s"), *CompileOptions.TargetPlatform->PlatformName());
TSharedRef<SMutableGraphViewer> GraphViewer = SNew(SMutableGraphViewer, RootNode)
.DataTag(DataTag)
.ReferencedRuntimeTextures(RuntimeTextures)
.ReferencedCompileTextures(CompilerTextures)
.ReferencedRuntimeMeshes(RuntimeMeshes)
.ReferencedCompileMeshes(CompilerMeshes);
DebuggerContentsBox->ClearContent();
DebuggerContentsBox->SetContent(GraphViewer);
}
void SMutableObjectViewer::CompileMutableCodePressed()
{
TArray<TSoftObjectPtr<const UTexture>> RuntimeTextures;
TArray<FMutableSourceTextureData> CompilerTextures;
TArray<TSoftObjectPtr<const UStreamableRenderAsset>> RuntimeMeshes;
TArray<FMutableSourceMeshData> CompilerMeshes;
if (CompileOptions.bForceLargeLODBias)
{
// Debug compile with many different biases
constexpr int32 MaxBias = 15;
for (int32 Bias = 0; Bias < MaxBias; ++Bias)
{
CompileOptions.DebugBias = Bias;
TSharedRef<FCustomizableObjectCompiler> Compiler = MakeShared<FCustomizableObjectCompiler>();
RuntimeTextures.Empty();
CompilerTextures.Empty();
RuntimeMeshes.Empty();
CompilerMeshes.Empty();
mu::NodePtr RootNode = Compiler->Export(CustomizableObject, CompileOptions, RuntimeTextures, CompilerTextures, RuntimeMeshes, CompilerMeshes);
if (!RootNode)
{
// TODO: Show errors
ensure(false);
return;
}
// Do the compilation to Mutable Code synchronously.
TSharedPtr<FCustomizableObjectCompileRunnable> CompileTask = MakeShareable(new FCustomizableObjectCompileRunnable(RootNode, Compiler));
CompileTask->Options = CompileOptions;
CompileTask->ReferencedTextures = CompilerTextures;
CompileTask->ReferencedMeshes = CompilerMeshes;
CompileTask->Init();
CompileTask->Run();
}
}
// Convert from Unreal graph to Mutable graph.
TSharedRef<FCustomizableObjectCompiler> Compiler = MakeShared<FCustomizableObjectCompiler>();
RuntimeTextures.Empty();
CompilerTextures.Empty();
RuntimeMeshes.Empty();
CompilerMeshes.Empty();
mu::NodePtr RootNode = Compiler->Export(CustomizableObject, CompileOptions, RuntimeTextures, CompilerTextures, RuntimeMeshes, CompilerMeshes);
if (!RootNode)
{
// TODO: Show errors
ensure(false);
return;
}
// Do the compilation to Mutable Code synchronously.
TSharedPtr<FCustomizableObjectCompileRunnable> CompileTask = MakeShareable(new FCustomizableObjectCompileRunnable(RootNode, Compiler));
CompileTask->Options = CompileOptions;
CompileTask->ReferencedTextures = CompilerTextures;
CompileTask->ReferencedMeshes = CompilerMeshes;
CompileTask->Init();
CompileTask->Run();
FString DataTag = FString::Printf( TEXT("Mutable Code : %s opt %d"), *CompileOptions.TargetPlatform->PlatformName(), CompileOptions.OptimizationLevel );
TSharedRef<SMutableCodeViewer> CodeViewer = SNew(SMutableCodeViewer, CompileTask->Model, RuntimeTextures, RuntimeMeshes)
.DataTag(DataTag);
DebuggerContentsBox->ClearContent();
DebuggerContentsBox->SetContent(CodeViewer);
}
TSharedRef<SWidget> SMutableObjectViewer::GenerateCompileOptionsMenuContent()
{
const bool bShouldCloseWindowAfterMenuSelection = false;
FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, nullptr);
// settings
MenuBuilder.BeginSection("Optimization", LOCTEXT("MutableCompileOptimizationHeading", "Optimization"));
{
// Unreal Graph to Mutable Graph options
//-----------------------------------
// Platform
DebugPlatformStrings.Empty();
TSharedPtr<FString> SelectedPlatform;
{
ITargetPlatformManagerModule* TPM = GetTargetPlatformManager();
check(TPM);
ITargetPlatform* CurrentPlatform = NULL;
const TArray<ITargetPlatform*>& Platforms = TPM->GetTargetPlatforms();
for (const ITargetPlatform* Platform : Platforms)
{
TSharedPtr<FString> ThisPlatform = MakeShareable(new FString(Platform->PlatformName()));
DebugPlatformStrings.Add(ThisPlatform);
if (Platform == CompileOptions.TargetPlatform)
{
SelectedPlatform = ThisPlatform;
}
}
}
if (!SelectedPlatform.IsValid() && DebugPlatformStrings.Num())
{
SelectedPlatform = DebugPlatformStrings[0];
}
DebugPlatformCombo =
SNew(STextComboBox)
.OptionsSource(&DebugPlatformStrings)
.InitiallySelectedItem(SelectedPlatform)
.OnSelectionChanged(this, &SMutableObjectViewer::OnChangeDebugPlatform)
;
MenuBuilder.AddWidget(DebugPlatformCombo.ToSharedRef(), LOCTEXT("MutableDebugPlatform", "Target Platform"));
// Compilation options
//-----------------------------------
// Optimisation level
CompileOptimizationStrings.Empty();
CompileOptimizationStrings.Add(MakeShareable(new FString(LOCTEXT("OptimizationNone", "None").ToString())));
CompileOptimizationStrings.Add(MakeShareable(new FString(LOCTEXT("OptimizationMin", "Minimal").ToString())));
CompileOptimizationStrings.Add(MakeShareable(new FString(LOCTEXT("OptimizationMax", "Maximum").ToString())));
CompileOptions.OptimizationLevel = FMath::Min(CompileOptions.OptimizationLevel, CompileOptimizationStrings.Num() - 1);
CompileOptimizationCombo =
SNew(STextComboBox)
.OptionsSource(&CompileOptimizationStrings)
.InitiallySelectedItem(CompileOptimizationStrings[CompileOptions.OptimizationLevel])
.OnSelectionChanged(this, &SMutableObjectViewer::OnChangeCompileOptimizationLevel)
;
MenuBuilder.AddWidget(CompileOptimizationCombo.ToSharedRef(), LOCTEXT("MutableCompileOptimizationLevel", "Optimization Level"));
{
CompileTextureCompressionStrings.Empty();
CompileTextureCompressionStrings.Add(MakeShareable(new FString(LOCTEXT("MutableTextureCompressionNone", "None").ToString())));
CompileTextureCompressionStrings.Add(MakeShareable(new FString(LOCTEXT("MutableTextureCompressionFast", "Fast").ToString())));
CompileTextureCompressionStrings.Add(MakeShareable(new FString(LOCTEXT("MutableTextureCompressionHighQuality", "High Quality").ToString())));
int32 SelectedCompression = FMath::Clamp(int32(CompileOptions.TextureCompression), 0, CompileTextureCompressionStrings.Num() - 1);
CompileTextureCompressionCombo =
SNew(STextComboBox)
.OptionsSource(&CompileTextureCompressionStrings)
.InitiallySelectedItem(CompileTextureCompressionStrings[SelectedCompression])
.OnSelectionChanged(this, &SMutableObjectViewer::OnChangeCompileTextureCompressionType)
;
MenuBuilder.AddWidget(CompileTextureCompressionCombo.ToSharedRef(), LOCTEXT("MutableCompileTextureCompressionType", "Texture Compression"));
}
// Image tiling
// Unfortunately SNumericDropDown doesn't work with integers at the time of writing.
TArray<SNumericDropDown<float>::FNamedValue> TilingOptions;
TilingOptions.Add(SNumericDropDown<float>::FNamedValue(0, FText::FromString(TEXT("0")), FText::FromString(TEXT("Disabled"))));
TilingOptions.Add(SNumericDropDown<float>::FNamedValue(64, FText::FromString(TEXT("64")), FText::FromString(TEXT("64"))));
TilingOptions.Add(SNumericDropDown<float>::FNamedValue(128, FText::FromString(TEXT("128")), FText::FromString(TEXT("128"))));
TilingOptions.Add(SNumericDropDown<float>::FNamedValue(256, FText::FromString(TEXT("256")), FText::FromString(TEXT("256"))));
TilingOptions.Add(SNumericDropDown<float>::FNamedValue(512, FText::FromString(TEXT("512")), FText::FromString(TEXT("512"))));
CompileTilingCombo = SNew(SNumericDropDown<float>)
.DropDownValues(TilingOptions)
.Value_Lambda( [&]() { return float(CompileOptions.ImageTiling); })
.OnValueChanged_Lambda( [&](float Value) { CompileOptions.ImageTiling = int32(Value); } )
;
MenuBuilder.AddWidget(CompileTilingCombo.ToSharedRef(), LOCTEXT("MutableCompileImageTiling", "Image Tiling"));
// Disk as cache
MenuBuilder.AddMenuEntry(
LOCTEXT("Generate_MutableUseDisk", "Enable compiling using the disk as memory."),
LOCTEXT("Generate_MutableUseDiskTooltip", "This is very slow but supports compiling huge objects. It requires a lot of free space in the OS disk."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateLambda([this]() { CompileOptions.bUseDiskCompilation = !CompileOptions.bUseDiskCompilation; }),
FCanExecuteAction(),
FIsActionChecked::CreateLambda([this]() { return CompileOptions.bUseDiskCompilation; })),
NAME_None,
EUserInterfaceActionType::ToggleButton
);
// Debug LODBias
MenuBuilder.AddMenuEntry(
LOCTEXT("ForceLargeLODBias", "Force a large texture LODBias."),
LOCTEXT("ForceLargeLODBiasTooltip", "This is useful to test compilation of special cook modes."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateLambda([this]() { CompileOptions.bForceLargeLODBias = !CompileOptions.bForceLargeLODBias; }),
FCanExecuteAction(),
FIsActionChecked::CreateLambda([this]() { return CompileOptions.bForceLargeLODBias; })),
NAME_None,
EUserInterfaceActionType::ToggleButton
);
}
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
void SMutableObjectViewer::OnChangeCompileOptimizationLevel(TSharedPtr<FString> NewSelection, ESelectInfo::Type SelectInfo)
{
CompileOptions.OptimizationLevel = CompileOptimizationStrings.Find(NewSelection);
}
void SMutableObjectViewer::OnChangeCompileTextureCompressionType(TSharedPtr<FString> NewSelection, ESelectInfo::Type)
{
CompileOptions.TextureCompression = ECustomizableObjectTextureCompression(CompileTextureCompressionStrings.Find(NewSelection));
}
void SMutableObjectViewer::OnChangeDebugPlatform(TSharedPtr<FString> NewSelection, ESelectInfo::Type SelectInfo)
{
if (!CustomizableObject) return;
CompileOptions.TargetPlatform = nullptr;
ITargetPlatformManagerModule* TPM = GetTargetPlatformManager();
check(TPM);
const TArray<ITargetPlatform*>& Platforms = TPM->GetTargetPlatforms();
check(Platforms.Num());
CompileOptions.TargetPlatform = Platforms[0];
for (int32 Index = 1; Index < Platforms.Num(); Index++)
{
if (Platforms[Index]->PlatformName() == *NewSelection)
{
CompileOptions.TargetPlatform = Platforms[Index];
break;
}
}
}
#undef LOCTEXT_NAMESPACE