Android build settings + metaxr
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
using UnrealBuildTool;
|
||||
using System.IO;
|
||||
|
||||
public class OculusXRProjectSetupTool : ModuleRules
|
||||
{
|
||||
public OculusXRProjectSetupTool(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
|
||||
bUseUnity = true;
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
"OculusXRHMD/Private",
|
||||
"OculusXRUncookedOnly/Private",
|
||||
Path.Combine(EngineDirectory, "Source/Developer/Android/AndroidPlatformEditor/Private")
|
||||
});
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine"
|
||||
}
|
||||
);
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Projects",
|
||||
"UnrealEd",
|
||||
"LevelEditor",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"EditorStyle",
|
||||
"EngineSettings",
|
||||
"OculusXRHMD",
|
||||
"OculusXRMovement",
|
||||
"OculusXRPassthrough",
|
||||
"OculusXRAnchors",
|
||||
"OculusXRScene",
|
||||
"OculusXRUncookedOnly",
|
||||
"AndroidRuntimeSettings",
|
||||
"AndroidPlatformEditor",
|
||||
"LauncherServices",
|
||||
"ToolWidgets",
|
||||
"WorkspaceMenuStructure",
|
||||
"PluginBrowser",
|
||||
"ToolMenus",
|
||||
"RHI",
|
||||
"BlueprintGraph",
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "OculusXRTelemetry.h"
|
||||
|
||||
namespace OculusXRTelemetry::Events
|
||||
{
|
||||
using FProjectSetupToolIgnore = TMarker<191964172>;
|
||||
using FProjectSetupToolFix = TMarker<191966457>;
|
||||
using FProjectSetupToolOption = TMarker<191964194>;
|
||||
using FProjectSetupToolSummary = TMarker<191966987>;
|
||||
using FProjectSetupToolOpen = TMarker<191967598>;
|
||||
using FProjectSetupToolClose = TMarker<191957393>;
|
||||
using FProjectSetupToolNext = TMarker<191956372>;
|
||||
using FProjectSetupToolPrev = TMarker<191956161>;
|
||||
using FProjectSetupToolTutorialClose = TMarker<191962723>;
|
||||
using FProjectSetupToolReportIssue = TMarker<191957866>;
|
||||
} // namespace OculusXRTelemetry::Events
|
||||
|
||||
namespace OculusXRTelemetry::Annotations
|
||||
{
|
||||
constexpr const char* Uid = "Uid";
|
||||
constexpr const char* Level = "Level";
|
||||
constexpr const char* Type = "Type";
|
||||
constexpr const char* Value = "Value";
|
||||
constexpr const char* BuildTargetGroup = "BuildTargetGroup";
|
||||
constexpr const char* Count = "Count";
|
||||
constexpr const char* Group = "Group";
|
||||
constexpr const char* Origin = "Origin";
|
||||
constexpr const char* TutorialCompleted = "TutorialCompleted";
|
||||
} // namespace OculusXRTelemetry::Annotations
|
||||
@@ -0,0 +1,162 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
#include "CoreMinimal.h"
|
||||
#include "OculusXRProjectSetupToolModule.h"
|
||||
#include "UObject/UObjectIterator.h"
|
||||
#include "Styling/SlateStyle.h"
|
||||
#include "Brushes/SlateImageBrush.h"
|
||||
|
||||
#define OCULUSXR_UPDATE_SETTINGS(SettingsClass, PropertyName, PropertyValue) \
|
||||
{ \
|
||||
SettingsClass* Settings = GetMutableDefault<SettingsClass>(); \
|
||||
Settings->PropertyName = PropertyValue; \
|
||||
Settings->UpdateSinglePropertyInConfigFile( \
|
||||
Settings->GetClass()->FindPropertyByName( \
|
||||
GET_MEMBER_NAME_CHECKED(SettingsClass, PropertyName)), \
|
||||
Settings->GetDefaultConfigFilename()); \
|
||||
Settings->TryUpdateDefaultConfigFile(); \
|
||||
}
|
||||
|
||||
namespace OculusXRPSTUtils
|
||||
{
|
||||
/**
|
||||
* Return if there is a component of a given type in the world.
|
||||
*/
|
||||
template <typename T>
|
||||
bool IsComponentOfTypeInWorld()
|
||||
{
|
||||
for (TObjectIterator<T> Iterator; Iterator;)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if there is a component of a given type in the world that matches the condition.
|
||||
*
|
||||
* Usage :
|
||||
* IsComponentOfTypeInWorld<T>([](T* Component){ return Component->SomeProperty == DesiredValue; })
|
||||
*/
|
||||
template <typename T>
|
||||
bool IsComponentOfTypeInWorld(TFunction<bool(T*)> Condition)
|
||||
{
|
||||
for (TObjectIterator<T> Iterator; Iterator; ++Iterator)
|
||||
{
|
||||
if (Condition(*Iterator))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline const char* ToString(ESetupRuleSeverity Severity)
|
||||
{
|
||||
switch (Severity)
|
||||
{
|
||||
case ESetupRuleSeverity::Critical:
|
||||
return "critical";
|
||||
case ESetupRuleSeverity::Performance:
|
||||
return "performance";
|
||||
case ESetupRuleSeverity::Warning:
|
||||
return "warning";
|
||||
default:
|
||||
UE_LOG(LogTemp, Error, TEXT("Not covered Severity enum. %d"), Severity);
|
||||
check(false);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
inline const char* ToString(ESetupRuleCategory Category)
|
||||
{
|
||||
switch (Category)
|
||||
{
|
||||
case ESetupRuleCategory::Compatibility:
|
||||
return "Compatibility";
|
||||
case ESetupRuleCategory::Rendering:
|
||||
return "Rendering";
|
||||
case ESetupRuleCategory::Quality:
|
||||
return "Quality";
|
||||
case ESetupRuleCategory::Physics:
|
||||
return "Physics";
|
||||
case ESetupRuleCategory::Plugins:
|
||||
return "Plugins";
|
||||
case ESetupRuleCategory::Features:
|
||||
return "Features";
|
||||
case ESetupRuleCategory::Miscellaneous:
|
||||
return "Miscellaneous";
|
||||
default:
|
||||
UE_LOG(LogTemp, Error, TEXT("Not covered Category enum. %d"), Category);
|
||||
check(false);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
inline const char* ToString(ESetupRulePlatform Platform)
|
||||
{
|
||||
FString Result = "";
|
||||
if ((Platform & ESetupRulePlatform::MetaLink) == ESetupRulePlatform::MetaLink)
|
||||
Result += " PC Link";
|
||||
if ((Platform & ESetupRulePlatform::MetaQuest_2) == ESetupRulePlatform::MetaQuest_2)
|
||||
Result += " Quest 2";
|
||||
if ((Platform & ESetupRulePlatform::MetaQuest_3) == ESetupRulePlatform::MetaQuest_3)
|
||||
Result += " Quest 3";
|
||||
if ((Platform & ESetupRulePlatform::MetaQuest_Pro) == ESetupRulePlatform::MetaQuest_Pro)
|
||||
Result += " Quest Pro";
|
||||
return TCHAR_TO_ANSI(*Result);
|
||||
}
|
||||
|
||||
inline const char* GetDisplayName(ESetupRulePlatform Platform)
|
||||
{
|
||||
if ((Platform & MetaQuest_All) == MetaQuest_All)
|
||||
return "All Quest";
|
||||
if ((Platform & ESetupRulePlatform::MetaLink) == ESetupRulePlatform::MetaLink)
|
||||
return "PC Link";
|
||||
if ((Platform & ESetupRulePlatform::MetaQuest_2) == ESetupRulePlatform::MetaQuest_2)
|
||||
return "Quest 2";
|
||||
if ((Platform & ESetupRulePlatform::MetaQuest_3) == ESetupRulePlatform::MetaQuest_3)
|
||||
return "Quest 3";
|
||||
if ((Platform & ESetupRulePlatform::MetaQuest_Pro) == ESetupRulePlatform::MetaQuest_Pro)
|
||||
return "Quest Pro";
|
||||
return "";
|
||||
}
|
||||
|
||||
inline void SetBrushStyle(const TSharedPtr<FSlateStyleSet>& Style, const ESetupRulePlatform Platform)
|
||||
{
|
||||
FString RelativePath = "PlatformQuest3"; // Quest3 and All Quest
|
||||
FVector2d Size{ 32.f, 32.f };
|
||||
if (Platform == ESetupRulePlatform::MetaLink)
|
||||
{
|
||||
RelativePath = "PlatformDesktop";
|
||||
Size = { 16.f, 16.f };
|
||||
}
|
||||
|
||||
if (Platform == ESetupRulePlatform::MetaQuest_Pro || Platform == ESetupRulePlatform::MetaQuest_2)
|
||||
{
|
||||
RelativePath = "PlatformQuest2";
|
||||
}
|
||||
|
||||
Style->Set(GetDisplayName(Platform),
|
||||
new FSlateVectorImageBrush(Style->RootToContentDir(RelativePath, TEXT(".svg")),
|
||||
Size));
|
||||
}
|
||||
inline bool ShouldRuleBeSkipped(const SetupRulePtr& Rule, ESetupRulePlatform Platform, const TSet<ESetupRuleSeverity>& Severities)
|
||||
{
|
||||
return !Rule->IsValid() || Rule->IsIgnored() || !Severities.Contains(Rule->GetSeverity()) || (Rule->GetPlatform() & Platform) != Platform;
|
||||
}
|
||||
|
||||
inline void LogErrorForUnAppliedRules(const TArray<SetupRulePtr>& UnAppliedRules)
|
||||
{
|
||||
if (!UnAppliedRules.IsEmpty())
|
||||
{
|
||||
UE_LOG(LogProjectSetupTool, Error, TEXT("Following critical rules are not applied:\n%s"),
|
||||
*FString::JoinBy(
|
||||
UnAppliedRules,
|
||||
TEXT("\n"),
|
||||
[](const SetupRulePtr Rule) { return Rule->GetDisplayName().ToString(); }));
|
||||
UE_LOG(LogProjectSetupTool, Error, TEXT("To fix them open `Tools > Meta XR Project Setup Tool`"));
|
||||
}
|
||||
}
|
||||
} // namespace OculusXRPSTUtils
|
||||
@@ -0,0 +1,409 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRProjectSetupToolModule.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "OculusXRPSTEvents.h"
|
||||
#include "OculusXRPSTSettings.h"
|
||||
#include "OculusXRPSTUtils.h"
|
||||
#include "OculusXRRuleProcessorSubsystem.h"
|
||||
#include "OculusXRTelemetry.h"
|
||||
#include "WorkspaceMenuStructure.h"
|
||||
#include "WorkspaceMenuStructureModule.h"
|
||||
#include "Brushes/SlateImageBrush.h"
|
||||
#include "Styling/SlateStyle.h"
|
||||
#include "Interfaces/IPluginManager.h"
|
||||
#include "Styling/SlateStyleRegistry.h"
|
||||
#include "Widget/OculusXRProjectSetupToolWidget.h"
|
||||
#include "Widget/OculusXRStatusBarWidget.h"
|
||||
#include "Interfaces/IMainFrameModule.h"
|
||||
#include "Widget/OculusXRProjectTutorialWidget.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
#include "Framework/Docking/TabManager.h"
|
||||
#include "ToolMenus.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Widgets/Docking/SDockTab.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogProjectSetupTool);
|
||||
#define LOCTEXT_NAMESPACE "OculusXRProjectSetupToolModule"
|
||||
|
||||
IMPLEMENT_MODULE(FOculusXRProjectSetupToolModule, OculusXRProjectSetupTool)
|
||||
|
||||
/** Style set */
|
||||
TSharedPtr<FSlateStyleSet> IconStyle = nullptr;
|
||||
|
||||
const char* MetaLogo = "ProjectSetupTool.MetaLogo";
|
||||
const FName ProjectSetupToolTabName = FName("OculusXRProjectSetupTool");
|
||||
/**
|
||||
* Perform module initialization
|
||||
*/
|
||||
void FOculusXRProjectSetupToolModule::StartupModule()
|
||||
{
|
||||
UE_LOG(LogProjectSetupTool, Display, TEXT("StartupModule: OculusXRProjectSetupTool"));
|
||||
RegisterConsoleCommands();
|
||||
|
||||
// Prepare the main project setup tool tab
|
||||
if (IsRunningCommandlet())
|
||||
{
|
||||
return;
|
||||
}
|
||||
RegisterStyleWithStyleRegistry();
|
||||
RegisterProjectSetupToolWithTabManager();
|
||||
RegisterStatusBarWidgetWithToolMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform module cleanup
|
||||
*/
|
||||
void FOculusXRProjectSetupToolModule::ShutdownModule()
|
||||
{
|
||||
UnregisterConsoleCommands();
|
||||
if (IsRunningCommandlet())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UnregisterStyleWithStyleRegistry();
|
||||
FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(ProjectSetupToolTabName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the project setup tool
|
||||
*/
|
||||
void FOculusXRProjectSetupToolModule::ShowProjectSetupTool(const FString& Origin)
|
||||
{
|
||||
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
||||
FGlobalTabmanager::Get()->TryInvokeTab(ProjectSetupToolTabName);
|
||||
TriggerOrigin = Origin;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
||||
|
||||
TSharedPtr<FSlateStyleSet> FOculusXRProjectSetupToolModule::GetSlateStyle()
|
||||
{
|
||||
return IconStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register console commands
|
||||
*/
|
||||
void FOculusXRProjectSetupToolModule::RegisterConsoleCommands()
|
||||
{
|
||||
ConsoleCommands.Add(IConsoleManager::Get().RegisterConsoleCommand(TEXT("vr.oculus.ApplyRule"), TEXT("Applies a rule.\n"), FConsoleCommandWithArgsDelegate::CreateStatic(&FOculusXRProjectSetupToolModule::ProcessApplyRuleCommand), ECVF_Default));
|
||||
|
||||
ConsoleCommands.Add(IConsoleManager::Get().RegisterConsoleCommand(TEXT("vr.oculus.IsRuleApplied"), TEXT("Determines whether a rule is applied.\n"), FConsoleCommandWithArgsDelegate::CreateStatic(&FOculusXRProjectSetupToolModule::ProcessIsRuleAppliedCommand), ECVF_Default));
|
||||
|
||||
ConsoleCommands.Add(IConsoleManager::Get().RegisterConsoleCommand(TEXT("vr.oculus.ListAppliedRules"), TEXT("Lists all applied rules.\n"), FConsoleCommandDelegate::CreateStatic(&FOculusXRProjectSetupToolModule::ProcessListAppliedRulesCommand), ECVF_Default));
|
||||
|
||||
ConsoleCommands.Add(IConsoleManager::Get().RegisterConsoleCommand(TEXT("vr.oculus.ListRules"), TEXT("Lists all registered rules.\n"), FConsoleCommandDelegate::CreateStatic(&FOculusXRProjectSetupToolModule::ProcessListRulesCommand), ECVF_Default));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister console commands
|
||||
*/
|
||||
void FOculusXRProjectSetupToolModule::UnregisterConsoleCommands()
|
||||
{
|
||||
for (const auto ConsoleCommand : ConsoleCommands)
|
||||
{
|
||||
IConsoleManager::Get().UnregisterConsoleObject(ConsoleCommand);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register styles
|
||||
*/
|
||||
void FOculusXRProjectSetupToolModule::RegisterStyleWithStyleRegistry() const
|
||||
{
|
||||
const FString PluginContentRoot = IPluginManager::Get().FindPlugin("OculusXR")->GetBaseDir() / TEXT("Resources");
|
||||
|
||||
IconStyle = MakeShared<FSlateStyleSet>("OculusXRProjectSetupToolStyle");
|
||||
IconStyle->SetContentRoot(PluginContentRoot);
|
||||
IconStyle->Set(MetaLogo,
|
||||
new FSlateVectorImageBrush(IconStyle->RootToContentDir("MetaLogo", TEXT(".svg")),
|
||||
FVector2D(32.0f, 32.0f)));
|
||||
|
||||
IconStyle->Set("ProjectSetupTool.MetaQuestBackground",
|
||||
new FSlateImageBrush(IconStyle->RootToContentDir("MetaQuestBackground", TEXT(".png")),
|
||||
FVector2D(480.f, 570.f)));
|
||||
|
||||
IconStyle->Set("ProjectSetupTool.RedDot",
|
||||
new FSlateVectorImageBrush(IconStyle->RootToContentDir("RedDot", TEXT(".svg")),
|
||||
FVector2D(32.0f, 32.0f)));
|
||||
|
||||
IconStyle->Set("ProjectSetupTool.YellowDot",
|
||||
new FSlateVectorImageBrush(IconStyle->RootToContentDir("YellowDot", TEXT(".svg")),
|
||||
FVector2D(32.0f, 32.0f)));
|
||||
|
||||
IconStyle->Set("ProjectSetupTool.GreenDot",
|
||||
new FSlateVectorImageBrush(IconStyle->RootToContentDir("GreenDot", TEXT(".svg")),
|
||||
FVector2D(32.0f, 32.0f)));
|
||||
|
||||
IconStyle->Set("ProjectSetupTool.GreyDot",
|
||||
new FSlateVectorImageBrush(IconStyle->RootToContentDir("GreyDot", TEXT(".svg")),
|
||||
FVector2D(32.0f, 32.0f)));
|
||||
|
||||
IconStyle->Set("ProjectSetupTool.WhiteDot",
|
||||
new FSlateVectorImageBrush(IconStyle->RootToContentDir("WhiteDot", TEXT(".svg")),
|
||||
FVector2D(32.0f, 32.0f)));
|
||||
|
||||
IconStyle->Set("ProjectSetupTool.FeedbackIcon",
|
||||
new FSlateVectorImageBrush(IconStyle->RootToContentDir("BugIcon", TEXT(".svg")),
|
||||
FVector2D(20.0f, 20.0f)));
|
||||
|
||||
OculusXRPSTUtils::SetBrushStyle(IconStyle, ESetupRulePlatform::MetaLink);
|
||||
OculusXRPSTUtils::SetBrushStyle(IconStyle, ESetupRulePlatform::MetaQuest_2);
|
||||
OculusXRPSTUtils::SetBrushStyle(IconStyle, ESetupRulePlatform::MetaQuest_3);
|
||||
OculusXRPSTUtils::SetBrushStyle(IconStyle, ESetupRulePlatform::MetaQuest_Pro);
|
||||
OculusXRPSTUtils::SetBrushStyle(IconStyle, MetaQuest_All);
|
||||
|
||||
FSlateStyleRegistry::RegisterSlateStyle(*IconStyle.Get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister styles
|
||||
*/
|
||||
void FOculusXRProjectSetupToolModule::UnregisterStyleWithStyleRegistry() const
|
||||
{
|
||||
if (IconStyle.IsValid())
|
||||
{
|
||||
FSlateStyleRegistry::UnRegisterSlateStyle(*IconStyle.Get());
|
||||
IconStyle.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register tab widget
|
||||
*/
|
||||
void FOculusXRProjectSetupToolModule::RegisterProjectSetupToolWithTabManager()
|
||||
{
|
||||
FGlobalTabmanager::Get()->RegisterNomadTabSpawner(
|
||||
ProjectSetupToolTabName,
|
||||
FOnSpawnTab::CreateRaw(this,
|
||||
&FOculusXRProjectSetupToolModule::OnSpawnProjectSetupToolTab))
|
||||
.SetDisplayName(LOCTEXT("ProjectSetupToolTab_Title", "Meta XR Project Setup Tool"))
|
||||
.SetTooltipText(LOCTEXT("ProjectSetupToolTab_Tooltip", "Meta XR Project Setup tool"))
|
||||
.SetGroup(WorkspaceMenu::GetMenuStructure().GetToolsCategory())
|
||||
.SetIcon(FSlateIcon(IconStyle->GetStyleSetName(), MetaLogo));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register status bar widget
|
||||
*/
|
||||
void FOculusXRProjectSetupToolModule::RegisterStatusBarWidgetWithToolMenu() const
|
||||
{
|
||||
UToolMenu* Menu = UToolMenus::Get()->ExtendMenu(TEXT("LevelEditor.StatusBar.ToolBar"));
|
||||
|
||||
FToolMenuSection& ProjectSetupToolSection = Menu->AddSection(TEXT("ProjectSetupTool"), FText::GetEmpty(), FToolMenuInsert(NAME_None, EToolMenuInsertType::First));
|
||||
|
||||
ProjectSetupToolSection.AddEntry(
|
||||
FToolMenuEntry::InitWidget(TEXT("ProjectSetupTool"), SNew(SOculusXRStatusBarWidget), FText::GetEmpty(), true, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process 'ApplyRule' console command
|
||||
*/
|
||||
void FOculusXRProjectSetupToolModule::ProcessApplyRuleCommand(const TArray<FString>& Arguments)
|
||||
{
|
||||
if (Arguments.Num() != 1)
|
||||
{
|
||||
UE_LOG(LogProjectSetupTool, Warning, TEXT("Expected only 1 argument"));
|
||||
}
|
||||
|
||||
const UOculusXRRuleProcessorSubsystem* RuleProcessorSubsystem = GEngine->GetEngineSubsystem<UOculusXRRuleProcessorSubsystem>();
|
||||
const FString& ConsoleRuleId = Arguments[0];
|
||||
const auto& Rule = RuleProcessorSubsystem->GetRule(FName(ConsoleRuleId));
|
||||
|
||||
if (Rule != nullptr)
|
||||
{
|
||||
bool ShouldRestart;
|
||||
Rule->Apply(ShouldRestart);
|
||||
|
||||
UE_LOG(LogProjectSetupTool, Display, TEXT("Applied rule <%s>"), *ConsoleRuleId);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogProjectSetupTool, Warning, TEXT("Cannot apply unknown rule <%s>"), *ConsoleRuleId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process 'IsRuleApplied' console command
|
||||
*/
|
||||
void FOculusXRProjectSetupToolModule::ProcessIsRuleAppliedCommand(const TArray<FString>& Arguments)
|
||||
{
|
||||
if (Arguments.Num() != 1)
|
||||
{
|
||||
UE_LOG(LogProjectSetupTool, Warning, TEXT("Expected only 1 argument"));
|
||||
}
|
||||
|
||||
const UOculusXRRuleProcessorSubsystem* RuleProcessorSubsystem = GEngine->GetEngineSubsystem<UOculusXRRuleProcessorSubsystem>();
|
||||
const FString& ConsoleRuleId = Arguments[0];
|
||||
const auto& Rule = RuleProcessorSubsystem->GetRule(FName(ConsoleRuleId));
|
||||
if (Rule != nullptr)
|
||||
{
|
||||
UE_LOG(LogProjectSetupTool, Display, TEXT("Rule <%s> is %s"), *ConsoleRuleId, Rule->IsApplied() ? TEXT("applied") : TEXT("not applied"));
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogProjectSetupTool, Warning, TEXT("Cannot query unknown rule <%s>"), *ConsoleRuleId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process 'ListAppliedRules' console command
|
||||
*/
|
||||
void FOculusXRProjectSetupToolModule::ProcessListAppliedRulesCommand()
|
||||
{
|
||||
const UOculusXRRuleProcessorSubsystem* RuleProcessorSubsystem = GEngine->GetEngineSubsystem<UOculusXRRuleProcessorSubsystem>();
|
||||
|
||||
uint32 Index = 0;
|
||||
|
||||
for (auto& Rule : RuleProcessorSubsystem->GetRules())
|
||||
{
|
||||
if (Rule->IsApplied())
|
||||
{
|
||||
UE_LOG(LogProjectSetupTool, Display, TEXT("Applied rule <%s>"), *(Rule->GetId().ToString()));
|
||||
++Index;
|
||||
}
|
||||
}
|
||||
UE_LOG(LogProjectSetupTool, Display, TEXT("There are %d applied rules"), Index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process 'ListRules' console command
|
||||
*/
|
||||
void FOculusXRProjectSetupToolModule::ProcessListRulesCommand()
|
||||
{
|
||||
const UOculusXRRuleProcessorSubsystem* RuleProcessorSubsystem = GEngine->GetEngineSubsystem<UOculusXRRuleProcessorSubsystem>();
|
||||
|
||||
UE_LOG(LogProjectSetupTool, Display, TEXT("There are %d registered rules"), RuleProcessorSubsystem->GetRules().Num());
|
||||
UE_LOG(LogProjectSetupTool, Display, TEXT("|%60hs|%60hs|%20hs|%20hs|%10hs|"), "Rule id", "Display Name",
|
||||
"Category", "Severity", "Is Ignored");
|
||||
|
||||
for (const auto& RegisteredRule : RuleProcessorSubsystem->GetRules())
|
||||
{
|
||||
UE_LOG(
|
||||
LogProjectSetupTool,
|
||||
Display,
|
||||
TEXT("|%60ls|%60s|%20hs|%20hs|%10hs|"),
|
||||
*RegisteredRule->GetId().ToString(),
|
||||
*RegisteredRule->GetDisplayName().ToString(),
|
||||
OculusXRPSTUtils::ToString(RegisteredRule->GetCategory()),
|
||||
OculusXRPSTUtils::ToString(RegisteredRule->GetSeverity()),
|
||||
RegisteredRule->IsIgnored() ? "yes" : "no");
|
||||
}
|
||||
}
|
||||
|
||||
void FOculusXRProjectSetupToolModule::ProcessIgnoreRuleCommand(const TArray<FString>& Arguments)
|
||||
{
|
||||
if (Arguments.Num() != 1)
|
||||
{
|
||||
UE_LOG(LogProjectSetupTool, Warning, TEXT("Expected only 1 argument"));
|
||||
}
|
||||
|
||||
const UOculusXRRuleProcessorSubsystem* RuleProcessorSubsystem = GEngine->GetEngineSubsystem<UOculusXRRuleProcessorSubsystem>();
|
||||
const FString& ConsoleRuleId = Arguments[0];
|
||||
const auto& Rule = RuleProcessorSubsystem->GetRule(FName(ConsoleRuleId));
|
||||
if (Rule != nullptr)
|
||||
{
|
||||
Rule->SetIgnoreRule(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogProjectSetupTool, Warning, TEXT("Cannot query unknown rule <%s>"), *ConsoleRuleId);
|
||||
}
|
||||
}
|
||||
void FOculusXRProjectSetupToolModule::ProcessUnIgnoreRuleCommand(const TArray<FString>& Arguments)
|
||||
{
|
||||
if (Arguments.Num() != 1)
|
||||
{
|
||||
UE_LOG(LogProjectSetupTool, Warning, TEXT("Expected only 1 argument"));
|
||||
}
|
||||
|
||||
const UOculusXRRuleProcessorSubsystem* RuleProcessorSubsystem = GEngine->GetEngineSubsystem<UOculusXRRuleProcessorSubsystem>();
|
||||
const FString& ConsoleRuleId = Arguments[0];
|
||||
const auto& Rule = RuleProcessorSubsystem->GetRule(FName(ConsoleRuleId));
|
||||
if (Rule != nullptr)
|
||||
{
|
||||
Rule->SetIgnoreRule(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogProjectSetupTool, Warning, TEXT("Cannot query unknown rule <%s>"), *ConsoleRuleId);
|
||||
}
|
||||
}
|
||||
void FOculusXRProjectSetupToolModule::ProcessUnIgnoreAllRulesCommand()
|
||||
{
|
||||
const UOculusXRRuleProcessorSubsystem* RuleProcessorSubsystem = GEngine->GetEngineSubsystem<UOculusXRRuleProcessorSubsystem>();
|
||||
|
||||
for (const auto& Rule : RuleProcessorSubsystem->GetRules())
|
||||
{
|
||||
Rule->SetIgnoreRule(false);
|
||||
}
|
||||
}
|
||||
|
||||
void FOculusXRProjectSetupToolModule::OnWidgetClosed()
|
||||
{
|
||||
const auto& Platform = static_cast<ESetupRulePlatform>(GetMutableDefault<UOculusXRPSTSettings>()->CurrentPlatform);
|
||||
const OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FProjectSetupToolClose> CloseEvent;
|
||||
const auto& Annotated = CloseEvent
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::BuildTargetGroup, OculusXRPSTUtils::ToString(Platform))
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::Origin, TCHAR_TO_ANSI(*TriggerOrigin));
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawn the project setup tool tab
|
||||
*/
|
||||
TSharedRef<SDockTab> FOculusXRProjectSetupToolModule::OnSpawnProjectSetupToolTab(const FSpawnTabArgs& SpawnTabArgs)
|
||||
{
|
||||
SpawnTutorialWindowIfNeeded();
|
||||
const auto& Platform = static_cast<ESetupRulePlatform>(GetMutableDefault<UOculusXRPSTSettings>()->CurrentPlatform);
|
||||
const OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FProjectSetupToolOpen> OpenEvent;
|
||||
const auto& Annotated = OpenEvent
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::BuildTargetGroup, OculusXRPSTUtils::ToString(Platform))
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::Origin, TCHAR_TO_ANSI(*TriggerOrigin));
|
||||
TriggerOrigin = "Menu";
|
||||
TSharedRef<SDockTab> DockTab = SNew(SDockTab)
|
||||
.TabRole(ETabRole::NomadTab)
|
||||
.OnTabClosed(SDockTab::FOnTabClosedCallback::CreateLambda([this](TSharedRef<SDockTab>) {
|
||||
OnWidgetClosed();
|
||||
}))
|
||||
.Label(NSLOCTEXT("MetaXRProjectSetupTool", "MetaXRPSTTitle", "Meta XR Project Setup Tool"))
|
||||
[SNew(SOculusXRProjectSetupToolWidget)];
|
||||
|
||||
DockTab->SetTabIcon(IconStyle->GetBrush(MetaLogo));
|
||||
return DockTab;
|
||||
}
|
||||
|
||||
void FOculusXRProjectSetupToolModule::SpawnTutorialWindowIfNeeded() const
|
||||
{
|
||||
const auto Settings = GetMutableDefault<UOculusXRPSTSettings>();
|
||||
if (Settings->bShowGuidedTutorial)
|
||||
{
|
||||
TSharedPtr<SWindow> ParentWindow;
|
||||
|
||||
if (FModuleManager::Get().IsModuleLoaded("MainFrame"))
|
||||
{
|
||||
const IMainFrameModule& MainFrame = FModuleManager::LoadModuleChecked<IMainFrameModule>("MainFrame");
|
||||
ParentWindow = MainFrame.GetParentWindow();
|
||||
}
|
||||
const TSharedRef<SWindow> Window = SNew(SWindow)
|
||||
.Title(LOCTEXT("Meta XR Project Setup Tool", "Meta XR Project Setup Tool"))
|
||||
.SizingRule(ESizingRule::Autosized)
|
||||
.SupportsMaximize(false)
|
||||
.SupportsMinimize(false)[SNew(SOculusXRTutorialWindow)];
|
||||
|
||||
Window->SetOnWindowClosed(FOnWindowClosed::CreateLambda([](const TSharedRef<SWindow>&) {
|
||||
auto Settings = GetMutableDefault<UOculusXRPSTSettings>();
|
||||
Settings->bShowGuidedTutorial = false;
|
||||
Settings->TryUpdateDefaultConfigFile();
|
||||
const OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FProjectSetupToolTutorialClose> ClosedEvent;
|
||||
const auto& Annotated = ClosedEvent
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::TutorialCompleted, Settings->bGuidedTutorialComplete ? "true" : "false");
|
||||
}));
|
||||
FSlateApplication::Get().AddModalWindow(Window, ParentWindow, false);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "IOculusXRProjectSetupModule.h"
|
||||
|
||||
class FSlateStyleSet;
|
||||
class SDockTab;
|
||||
class FSpawnTabArgs;
|
||||
struct IConsoleCommand;
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogProjectSetupTool, Log, All);
|
||||
|
||||
/**
|
||||
* The module for the implementation of the Project Setup Tool
|
||||
*/
|
||||
class FOculusXRProjectSetupToolModule : public IOculusXRProjectSetupToolModule
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* IModuleInterface implementation
|
||||
*/
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
/** Show the project setup tool window */
|
||||
virtual void ShowProjectSetupTool(const FString& Origin) override;
|
||||
|
||||
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
||||
static TSharedPtr<FSlateStyleSet> GetSlateStyle();
|
||||
|
||||
private:
|
||||
/** Register and unregister console commands */
|
||||
void RegisterConsoleCommands();
|
||||
void UnregisterConsoleCommands();
|
||||
|
||||
/** Register and unregister styles */
|
||||
void RegisterStyleWithStyleRegistry() const;
|
||||
void UnregisterStyleWithStyleRegistry() const;
|
||||
|
||||
/** Register tool extensions */
|
||||
void RegisterProjectSetupToolWithTabManager();
|
||||
void RegisterStatusBarWidgetWithToolMenu() const;
|
||||
|
||||
/** Process functions for all the console commands */
|
||||
static void ProcessApplyRuleCommand(const TArray<FString>& Arguments);
|
||||
static void ProcessIsRuleAppliedCommand(const TArray<FString>& Arguments);
|
||||
static void ProcessListAppliedRulesCommand();
|
||||
static void ProcessListRulesCommand();
|
||||
static void ProcessIgnoreRuleCommand(const TArray<FString>& Arguments);
|
||||
static void ProcessUnIgnoreRuleCommand(const TArray<FString>& Arguments);
|
||||
static void ProcessUnIgnoreAllRulesCommand();
|
||||
|
||||
void OnWidgetClosed();
|
||||
void SpawnTutorialWindowIfNeeded() const;
|
||||
|
||||
/** Spawn function for creating the project setup tool tab */
|
||||
TSharedRef<SDockTab> OnSpawnProjectSetupToolTab(const FSpawnTabArgs& SpawnTabArgs);
|
||||
|
||||
/** All registered console commands */
|
||||
TArray<IConsoleCommand*> ConsoleCommands{};
|
||||
|
||||
/** If PST is triggered from toolbar*/
|
||||
FString TriggerOrigin = "Menu";
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,293 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRRuleProcessorSubsystem.h"
|
||||
|
||||
#include "LightComponentBase.h"
|
||||
#include "OculusXRProjectSetupToolModule.h"
|
||||
#include "OculusXRPSTEvents.h"
|
||||
#include "OculusXRPSTSettings.h"
|
||||
#include "OculusXRPSTUtils.h"
|
||||
#include "OculusXRTelemetry.h"
|
||||
#include "Developer/LauncherServices/Public/ILauncherServicesModule.h"
|
||||
#include "Rules/OculusXRAnchorsRules.h"
|
||||
#include "Rules/OculusXRCompatibilityRules.h"
|
||||
#include "Rules/OculusXRMovementRules.h"
|
||||
#include "Rules/OculusXRPassthroughRules.h"
|
||||
#include "Rules/OculusXRRenderingRules.h"
|
||||
#include "Rules/OculusXRPluginRules.h"
|
||||
#include "Editor.h"
|
||||
|
||||
/**
|
||||
* Initialize the subsystem. USubsystem override
|
||||
*/
|
||||
void UOculusXRRuleProcessorSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
Super::Initialize(Collection);
|
||||
|
||||
PopulateDynamicLights();
|
||||
|
||||
// Register rules
|
||||
RegisterRules(OculusXRRenderingRules::RenderingRules_Table);
|
||||
RegisterRules(OculusXRPluginRules::PluginRules_Table);
|
||||
RegisterRules(OculusXRCompatibilityRules::CompatibilityRules_Table);
|
||||
RegisterRules(OculusXRPassthroughRules::PassthroughRules_Table);
|
||||
RegisterRules(OculusXRMovementRules::MovementRules_Table);
|
||||
RegisterRules(OculusXRAnchorsRules::AnchorRules_Table);
|
||||
|
||||
// Register on Launcher Callback
|
||||
ILauncherServicesModule& ProjectLauncherServicesModule = FModuleManager::LoadModuleChecked<ILauncherServicesModule>(
|
||||
"LauncherServices");
|
||||
LauncherCallbackHandle = ProjectLauncherServicesModule.OnCreateLauncherDelegate.AddUObject(
|
||||
this, &UOculusXRRuleProcessorSubsystem::OnLauncherCreated);
|
||||
// Show errors after play in editor is over.
|
||||
FEditorDelegates::PrePIEEnded.AddUObject(this, &UOculusXRRuleProcessorSubsystem::OnPIEEnded);
|
||||
|
||||
// Update if rules are ignored. Note: At time of the rules construction it is early to fetch settings
|
||||
const auto& IgnoredRules = GetMutableDefault<UOculusXRPSTSettings>()->IgnoredRules;
|
||||
for (const auto& Rule : Rules)
|
||||
{
|
||||
Rule->SetIgnoreRule(IgnoredRules.Contains(Rule->GetId()), false);
|
||||
}
|
||||
|
||||
SendSummaryEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* De-initializes the subsystem. USubsystem override
|
||||
*/
|
||||
void UOculusXRRuleProcessorSubsystem::Deinitialize()
|
||||
{
|
||||
SendSummaryEvent();
|
||||
Super::Deinitialize();
|
||||
if (LauncherCallbackHandle.IsValid())
|
||||
{
|
||||
ILauncherServicesModule& ProjectLauncherServicesModule = FModuleManager::LoadModuleChecked<
|
||||
ILauncherServicesModule>(
|
||||
"LauncherServices");
|
||||
ProjectLauncherServicesModule.OnCreateLauncherDelegate.Remove(LauncherCallbackHandle);
|
||||
LauncherCallbackHandle.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a rule
|
||||
*/
|
||||
bool UOculusXRRuleProcessorSubsystem::RegisterRule(const SetupRulePtr& Rule)
|
||||
{
|
||||
if (Rule == nullptr)
|
||||
{
|
||||
UE_LOG(LogProjectSetupTool, Error, TEXT("RegisterRule: Cannot register nullptr"));
|
||||
return false;
|
||||
}
|
||||
bool bIsAlreadyRegistred = false;
|
||||
Rules.Add(Rule, &bIsAlreadyRegistred);
|
||||
if (bIsAlreadyRegistred)
|
||||
{
|
||||
UE_LOG(LogProjectSetupTool, Warning, TEXT("RegisterRule: rule with id <%s> has already been registered"),
|
||||
*(Rule->GetId().ToString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
UE_LOG(LogProjectSetupTool, Display, TEXT("RegisterRule: added rule with id <%s>"), *(Rule->GetId().ToString()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a rule
|
||||
*/
|
||||
bool UOculusXRRuleProcessorSubsystem::UnregisterRule(const SetupRulePtr& Rule)
|
||||
{
|
||||
if (Rule == nullptr)
|
||||
{
|
||||
UE_LOG(LogProjectSetupTool, Error, TEXT("RegisterRule: Cannot deregister nullptr"));
|
||||
return false;
|
||||
}
|
||||
const auto Id = Rule->GetId();
|
||||
if (!Rules.Contains(Id))
|
||||
{
|
||||
UE_LOG(LogProjectSetupTool, Warning, TEXT("UnregisterRule: rule with id <%s> has not been registered"),
|
||||
*Id.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
UE_LOG(LogProjectSetupTool, Display, TEXT("UnregisterRule: removed rule with id <%s>"), *Id.ToString());
|
||||
|
||||
Rules.Remove(Id);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister all rules
|
||||
*/
|
||||
void UOculusXRRuleProcessorSubsystem::UnregisterAllRules()
|
||||
{
|
||||
UE_LOG(LogProjectSetupTool, Display, TEXT("UnregisterRule: removed all rules"));
|
||||
|
||||
Rules.Empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all rules
|
||||
*/
|
||||
const TSet<SetupRulePtr, FSetupRuleKeyFunc>& UOculusXRRuleProcessorSubsystem::GetRules() const
|
||||
{
|
||||
return Rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch rule with given `Id`
|
||||
*/
|
||||
SetupRulePtr UOculusXRRuleProcessorSubsystem::GetRule(const FName& Id) const
|
||||
{
|
||||
const auto Found = Rules.Find(Id);
|
||||
if (Found == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return *Found;
|
||||
}
|
||||
|
||||
bool UOculusXRRuleProcessorSubsystem::DynamicLightsExistInProject() const
|
||||
{
|
||||
return DynamicLights.Num() > 0;
|
||||
}
|
||||
|
||||
void UOculusXRRuleProcessorSubsystem::SendSummaryEvent()
|
||||
{
|
||||
SendSummaryEvent(ESetupRulePlatform::MetaLink);
|
||||
SendSummaryEvent(ESetupRulePlatform::MetaQuest_2);
|
||||
SendSummaryEvent(ESetupRulePlatform::MetaQuest_3);
|
||||
SendSummaryEvent(ESetupRulePlatform::MetaQuest_Pro);
|
||||
}
|
||||
|
||||
void UOculusXRRuleProcessorSubsystem::SendSummaryEvent(ESetupRulePlatform Platform) const
|
||||
{
|
||||
const auto& Status = UnAppliedRulesStatus(Platform);
|
||||
const char* Level = Status.PendingRequiredRulesCount > 0 ? "Critical" : "Recommended";
|
||||
const char* Value = TCHAR_TO_ANSI(*FString::FromInt(Status.PendingRequiredRulesCount > 0 ? Status.PendingRequiredRulesCount : Status.PendingRecommendedRulesCount));
|
||||
const char* Total = TCHAR_TO_ANSI(
|
||||
*FString::FromInt(Status.PendingRequiredRulesCount + Status.PendingRecommendedRulesCount));
|
||||
const OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FProjectSetupToolSummary> SummaryEvent;
|
||||
const auto& CriticalAnnotated = SummaryEvent
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::Level, Level)
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::Value, Value)
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::Count, Total)
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::BuildTargetGroup, OculusXRPSTUtils::ToString(Platform));
|
||||
}
|
||||
|
||||
void UOculusXRRuleProcessorSubsystem::Refresh()
|
||||
{
|
||||
PopulateDynamicLights();
|
||||
SendSummaryEvent();
|
||||
}
|
||||
|
||||
UOculusXRRuleProcessorSubsystem::RuleStatus UOculusXRRuleProcessorSubsystem::UnAppliedRulesStatus(
|
||||
ESetupRulePlatform Platform) const
|
||||
{
|
||||
RuleStatus Status{};
|
||||
for (const auto& Rule : Rules)
|
||||
{
|
||||
if (Rule->IsApplied())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (OculusXRPSTUtils::ShouldRuleBeSkipped(Rule, Platform, { ESetupRuleSeverity::Critical, ESetupRuleSeverity::Performance, ESetupRuleSeverity::Warning }))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Rule->GetSeverity() == ESetupRuleSeverity::Critical)
|
||||
{
|
||||
++Status.PendingRequiredRulesCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
++Status.PendingRecommendedRulesCount;
|
||||
}
|
||||
}
|
||||
return Status;
|
||||
}
|
||||
|
||||
void UOculusXRRuleProcessorSubsystem::PopulateDynamicLights()
|
||||
{
|
||||
DynamicLights.Empty();
|
||||
|
||||
for (TObjectIterator<ULightComponentBase> LightItr; LightItr; ++LightItr)
|
||||
{
|
||||
const AActor* owner = LightItr->GetOwner();
|
||||
if (owner != nullptr && (owner->IsRootComponentStationary() || owner->IsRootComponentMovable()) && !owner->IsHiddenEd() && LightItr->IsVisible() && owner->IsEditable() && owner->IsSelectable() && LightItr->GetWorld() == GEditor->GetEditorWorldContext().World())
|
||||
{
|
||||
DynamicLights.Add(LightItr->GetFullGroupName(false), TWeakObjectPtr<ULightComponentBase>(*LightItr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOculusXRRuleProcessorSubsystem::RegisterRules(const TArray<SetupRulePtr>& InRules)
|
||||
{
|
||||
for (const auto& Rule : InRules)
|
||||
{
|
||||
if (!RegisterRule(Rule))
|
||||
{
|
||||
UE_LOG(LogProjectSetupTool, Error, TEXT("Cannot register rule <%s>"), *Rule->GetId().ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOculusXRRuleProcessorSubsystem::OnLauncherCreated(ILauncherRef Launcher)
|
||||
{
|
||||
// Add callback for when launcher worker is started
|
||||
Launcher->FLauncherWorkerStartedDelegate.AddUObject(
|
||||
this, &UOculusXRRuleProcessorSubsystem::OnLauncherWorkerStarted);
|
||||
}
|
||||
|
||||
void UOculusXRRuleProcessorSubsystem::OnLauncherWorkerStarted(ILauncherWorkerPtr LauncherWorker,
|
||||
ILauncherProfileRef Profile)
|
||||
{
|
||||
if (!GetMutableDefault<UOculusXRPSTSettings>()->bStopBuildOnUnAppliedCriticalItems)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const TArray<FString> Platforms = Profile.Get().GetCookedPlatforms();
|
||||
ESetupRulePlatform RulePlatforms = ESetupRulePlatform::None;
|
||||
if (Platforms.Contains("Android_ASTC"))
|
||||
{
|
||||
RulePlatforms |= MetaQuest_All;
|
||||
}
|
||||
|
||||
if (Platforms.Contains("Windows"))
|
||||
{
|
||||
RulePlatforms |= ESetupRulePlatform::MetaLink;
|
||||
}
|
||||
|
||||
const auto& UnAppliedRules = UnAppliedRulesForPlatform(RulePlatforms, { ESetupRuleSeverity::Critical });
|
||||
|
||||
OculusXRPSTUtils::LogErrorForUnAppliedRules(UnAppliedRules);
|
||||
if (!UnAppliedRules.IsEmpty())
|
||||
{
|
||||
LauncherWorker->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void UOculusXRRuleProcessorSubsystem::OnPIEEnded(bool bIsSimulating)
|
||||
{
|
||||
const auto& UnAppliedRules = UnAppliedRulesForPlatform(ESetupRulePlatform::MetaLink, { ESetupRuleSeverity::Critical });
|
||||
|
||||
OculusXRPSTUtils::LogErrorForUnAppliedRules(UnAppliedRules);
|
||||
}
|
||||
|
||||
TArray<SetupRulePtr> UOculusXRRuleProcessorSubsystem::UnAppliedRulesForPlatform(ESetupRulePlatform Platform, const TSet<ESetupRuleSeverity>& Severities) const
|
||||
{
|
||||
TArray<SetupRulePtr> UnAppliedRules = {};
|
||||
for (const auto Rule : Rules)
|
||||
{
|
||||
if (!OculusXRPSTUtils::ShouldRuleBeSkipped(Rule, Platform, Severities) && !Rule->IsApplied())
|
||||
{
|
||||
UnAppliedRules.Add(Rule);
|
||||
}
|
||||
}
|
||||
|
||||
return UnAppliedRules;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRSetupRule.h"
|
||||
|
||||
#include "OculusXRPSTEvents.h"
|
||||
#include "OculusXRPSTSettings.h"
|
||||
#include "OculusXRPSTUtils.h"
|
||||
|
||||
ISetupRule::ISetupRule(
|
||||
const FName& InId,
|
||||
const FText& InDisplayName,
|
||||
const FText& InDescription,
|
||||
const ESetupRuleCategory InCategory,
|
||||
const ESetupRuleSeverity InSeverity,
|
||||
const ESetupRulePlatform InPlatform,
|
||||
const bool InShowApply)
|
||||
: Id(InId), DisplayName(InDisplayName), Description(InDescription), Category(InCategory), Severity(InSeverity), Platform(InPlatform), bShowApply(InShowApply)
|
||||
{
|
||||
InfoUrl = FString();
|
||||
}
|
||||
|
||||
ISetupRule::ISetupRule(
|
||||
const FName& InId,
|
||||
const FText& InDisplayName,
|
||||
const FText& InDescription,
|
||||
const FString& InInfoUrl,
|
||||
const ESetupRuleCategory InCategory,
|
||||
const ESetupRuleSeverity InSeverity,
|
||||
const ESetupRulePlatform InPlatform,
|
||||
const bool InShowApply)
|
||||
: Id(InId), DisplayName(InDisplayName), Description(InDescription), InfoUrl(InInfoUrl), Category(InCategory), Severity(InSeverity), Platform(InPlatform), bShowApply(InShowApply)
|
||||
{
|
||||
}
|
||||
|
||||
void ISetupRule::Apply(bool& ShouldRestartEditor)
|
||||
{
|
||||
const OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FProjectSetupToolFix> FixedEvent;
|
||||
const auto& Annotated = FixedEvent
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::Uid, TCHAR_TO_ANSI(*Id.ToString()))
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::Level, OculusXRPSTUtils::ToString(Severity))
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::Group, OculusXRPSTUtils::ToString(Category))
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::BuildTargetGroup, OculusXRPSTUtils::ToString(static_cast<ESetupRulePlatform>(Platform)))
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::Value, "true");
|
||||
ApplyImpl(ShouldRestartEditor);
|
||||
}
|
||||
|
||||
bool ISetupRule::IsValid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ISetupRule::IsIgnored() const
|
||||
{
|
||||
return bIsIgnored;
|
||||
}
|
||||
|
||||
void ISetupRule::SetIgnoreRule(bool bIgnore, bool bSendMetrics)
|
||||
{
|
||||
if (bSendMetrics)
|
||||
{
|
||||
const OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FProjectSetupToolIgnore> IgnoreEvent;
|
||||
const auto& Annotated = IgnoreEvent
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::Uid, TCHAR_TO_ANSI(*Id.ToString()))
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::Level, OculusXRPSTUtils::ToString(Severity))
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::Group, OculusXRPSTUtils::ToString(Category))
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::BuildTargetGroup, OculusXRPSTUtils::ToString(static_cast<ESetupRulePlatform>(Platform)))
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::Value, bIgnore ? "true" : "false");
|
||||
}
|
||||
|
||||
if (bIsIgnored == bIgnore)
|
||||
{
|
||||
return;
|
||||
}
|
||||
bIsIgnored = bIgnore;
|
||||
|
||||
if (bIgnore)
|
||||
{
|
||||
GetMutableDefault<UOculusXRPSTSettings>()->IgnoredRules.Add(Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetMutableDefault<UOculusXRPSTSettings>()->IgnoredRules.Remove(Id);
|
||||
}
|
||||
GetMutableDefault<UOculusXRPSTSettings>()->TryUpdateDefaultConfigFile();
|
||||
}
|
||||
|
||||
const FName& ISetupRule::GetId() const
|
||||
{
|
||||
return Id;
|
||||
}
|
||||
FText ISetupRule::GetDisplayName() const
|
||||
{
|
||||
return DisplayName;
|
||||
}
|
||||
FText ISetupRule::GetDescription() const
|
||||
{
|
||||
return Description;
|
||||
}
|
||||
FString ISetupRule::GetInfoUrl() const
|
||||
{
|
||||
return InfoUrl;
|
||||
}
|
||||
ESetupRuleCategory ISetupRule::GetCategory() const
|
||||
{
|
||||
return Category;
|
||||
}
|
||||
ESetupRuleSeverity ISetupRule::GetSeverity() const
|
||||
{
|
||||
return Severity;
|
||||
}
|
||||
|
||||
ESetupRulePlatform ISetupRule::GetPlatform() const
|
||||
{
|
||||
return Platform;
|
||||
}
|
||||
|
||||
bool ISetupRule::ShowApply() const
|
||||
{
|
||||
return bShowApply;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRAnchorsRules.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "OculusXRHMDRuntimeSettings.h"
|
||||
#include "OculusXRAnchorComponents.h"
|
||||
#include "OculusXRPSTUtils.h"
|
||||
#include "OculusXRSceneActor.h"
|
||||
|
||||
namespace OculusXRAnchorsRules
|
||||
{
|
||||
bool FEnableAnchorSupportRule::IsApplied() const
|
||||
{
|
||||
const UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
|
||||
|
||||
return Settings->bAnchorSupportEnabled;
|
||||
}
|
||||
|
||||
void FEnableAnchorSupportRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(UOculusXRHMDRuntimeSettings, bAnchorSupportEnabled, true);
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
bool FEnableAnchorSupportRule::IsValid()
|
||||
{
|
||||
return OculusXRPSTUtils::IsComponentOfTypeInWorld<UOculusXRBaseAnchorComponent>() || OculusXRPSTUtils::IsComponentOfTypeInWorld<AOculusXRSceneActor>();
|
||||
}
|
||||
|
||||
bool FEnableSceneSupportRule::IsApplied() const
|
||||
{
|
||||
const UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
|
||||
|
||||
return Settings->bSceneSupportEnabled;
|
||||
}
|
||||
|
||||
void FEnableSceneSupportRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(UOculusXRHMDRuntimeSettings, bSceneSupportEnabled, true);
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
bool FEnableSceneSupportRule::IsValid()
|
||||
{
|
||||
return OculusXRPSTUtils::IsComponentOfTypeInWorld<AOculusXRSceneActor>();
|
||||
}
|
||||
} // namespace OculusXRAnchorsRules
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
#include "OculusXRSetupRule.h"
|
||||
|
||||
/*
|
||||
* Collection of rules related to anchors. Can be extended as needed
|
||||
*/
|
||||
namespace OculusXRAnchorsRules
|
||||
{
|
||||
class FEnableAnchorSupportRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnableAnchorSupportRule()
|
||||
: ISetupRule(
|
||||
"Feature_EnableAnchorSupport",
|
||||
NSLOCTEXT("OculusXRAnchorsRules", "EnableAnchorSupport_DisplayName", "Enable Anchor Support"),
|
||||
NSLOCTEXT("OculusXRAnchorsRules", "EnableAnchorSupport_Description", "Anchor support must be enabled when using anchor features"),
|
||||
ESetupRuleCategory::Features,
|
||||
ESetupRuleSeverity::Critical) {}
|
||||
virtual bool IsApplied() const override;
|
||||
virtual bool IsValid() override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FEnableSceneSupportRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnableSceneSupportRule()
|
||||
: ISetupRule(
|
||||
"Feature_EnableSceneSupport",
|
||||
NSLOCTEXT("OculusXRAnchorsRules", "EnableSceneSupport_DisplayName", "Enable Scene Support"),
|
||||
NSLOCTEXT("OculusXRAnchorsRules", "EnableSceneSupport_Description", "Scene support must be enabled when using scene features"),
|
||||
ESetupRuleCategory::Features,
|
||||
ESetupRuleSeverity::Critical) {}
|
||||
virtual bool IsApplied() const override;
|
||||
virtual bool IsValid() override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
inline TArray<SetupRulePtr> AnchorRules_Table{
|
||||
MakeShared<FEnableAnchorSupportRule>(),
|
||||
MakeShared<FEnableSceneSupportRule>()
|
||||
};
|
||||
} // namespace OculusXRAnchorsRules
|
||||
@@ -0,0 +1,367 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRCompatibilityRules.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "AndroidRuntimeSettings.h"
|
||||
#include "AndroidSDKSettings.h"
|
||||
#include "GeneralProjectSettings.h"
|
||||
#include "ISettingsModule.h"
|
||||
#include "ISettingsCategory.h"
|
||||
#include "ISettingsContainer.h"
|
||||
#include "ISettingsSection.h"
|
||||
#include "OculusXRRuleProcessorSubsystem.h"
|
||||
#include "GameFramework/InputSettings.h"
|
||||
#include "OculusXRHMDRuntimeSettings.h"
|
||||
#include "OculusXRPSTUtils.h"
|
||||
#include "Misc/EngineVersionComparison.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRCompatibilityRules"
|
||||
namespace
|
||||
{
|
||||
constexpr int32 MinimumAndroidAPILevel = 32; // With Quest 1 support ending in Jan 2025, API level 29 is no longer supported.
|
||||
constexpr int32 TargetAndroidAPILevel = 32; // Target API 32 or higher is required to submit to the Meta Quest Store. See https://developer.oculus.com/blog/meta-quest-apps-android-12l-june-30/
|
||||
constexpr char AndroidNDKVersionNumber[] = "25.1.8937393";
|
||||
|
||||
// SDK Max API level is determined by reviewing SetupAndroid.bat for each UE version.
|
||||
// SDK Min API Level is determined by Quest Store requirements as noted above.
|
||||
#if UE_VERSION_OLDER_THAN(5, 4, 0)
|
||||
constexpr char AndroidSDKAPIMinLevel[] = "android-32";
|
||||
constexpr char AndroidSDKAPIMaxLevel[] = "android-32";
|
||||
constexpr int32 AndroidSDKAPIMinLevelInt = 32;
|
||||
constexpr int32 AndroidSDKAPIMaxLevelInt = 32;
|
||||
|
||||
constexpr char AndroidNDKAPIMinLevel[] = "android-32";
|
||||
constexpr char AndroidNDKAPIMaxLevel[] = "android-32";
|
||||
constexpr int32 AndroidNDKAPIMinLevelInt = 32;
|
||||
constexpr int32 AndroidNDKAPIMaxLevelInt = 32;
|
||||
#elif UE_VERSION_OLDER_THAN(5, 5, 0)
|
||||
constexpr char AndroidSDKAPIMinLevel[] = "android-32";
|
||||
constexpr char AndroidSDKAPIMaxLevel[] = "android-33";
|
||||
constexpr int32 AndroidSDKAPIMinLevelInt = 32;
|
||||
constexpr int32 AndroidSDKAPIMaxLevelInt = 33;
|
||||
|
||||
constexpr char AndroidNDKAPIMinLevel[] = "android-32";
|
||||
constexpr char AndroidNDKAPIMaxLevel[] = "android-33";
|
||||
constexpr int32 AndroidNDKAPIMinLevelInt = 32;
|
||||
constexpr int32 AndroidNDKAPIMaxLevelInt = 33;
|
||||
#else // 5.5 and newer
|
||||
constexpr char AndroidSDKAPIMinLevel[] = "android-32";
|
||||
constexpr char AndroidSDKAPIMaxLevel[] = "android-34";
|
||||
constexpr int32 AndroidSDKAPIMinLevelInt = 32;
|
||||
constexpr int32 AndroidSDKAPIMaxLevelInt = 34;
|
||||
|
||||
constexpr char AndroidNDKAPIMinLevel[] = "android-32";
|
||||
constexpr char AndroidNDKAPIMaxLevel[] = "android-33"; // API level 34 is not supported by NDK 25.1.8937393
|
||||
constexpr int32 AndroidNDKAPIMinLevelInt = 32;
|
||||
constexpr int32 AndroidNDKAPIMaxLevelInt = 33;
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
namespace OculusXRCompatibilityRules
|
||||
{
|
||||
|
||||
FUseAndroidSDKMinimumRule::FUseAndroidSDKMinimumRule()
|
||||
: ISetupRule(
|
||||
"Compatibility_UseAndroidSDKMinimum",
|
||||
LOCTEXT("UseAndroidSDKMinimum_DisplayName", "Use Android SDK Minimum Version"),
|
||||
FText::Format(
|
||||
LOCTEXT("UseAndroidSDKMinimum_Description", "Minimum Android API level must be at least {0}."),
|
||||
MinimumAndroidAPILevel),
|
||||
ESetupRuleCategory::Compatibility,
|
||||
ESetupRuleSeverity::Critical,
|
||||
MetaQuest_All) {}
|
||||
|
||||
bool FUseAndroidSDKMinimumRule::IsApplied() const
|
||||
{
|
||||
const UAndroidRuntimeSettings* Settings = GetMutableDefault<UAndroidRuntimeSettings>();
|
||||
|
||||
return Settings->MinSDKVersion >= MinimumAndroidAPILevel;
|
||||
}
|
||||
|
||||
void FUseAndroidSDKMinimumRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(UAndroidRuntimeSettings, MinSDKVersion, MinimumAndroidAPILevel);
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
FUseAndroidSDKTargetRule::FUseAndroidSDKTargetRule()
|
||||
: ISetupRule(
|
||||
"Compatibility_UseAndroidSDKTarget",
|
||||
LOCTEXT("UseAndroidSDKTarget_DisplayName", "Use Android SDK Target Version"),
|
||||
FText::Format(
|
||||
LOCTEXT("UseAndroidSDKTarget_Description", "Target Android API level must be at least {0}."),
|
||||
TargetAndroidAPILevel),
|
||||
ESetupRuleCategory::Compatibility,
|
||||
ESetupRuleSeverity::Critical,
|
||||
MetaQuest_All) {}
|
||||
|
||||
bool FUseAndroidSDKTargetRule::IsApplied() const
|
||||
{
|
||||
const UAndroidRuntimeSettings* Settings = GetMutableDefault<UAndroidRuntimeSettings>();
|
||||
|
||||
return Settings->TargetSDKVersion >= TargetAndroidAPILevel;
|
||||
}
|
||||
|
||||
void FUseAndroidSDKTargetRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(UAndroidRuntimeSettings, TargetSDKVersion, TargetAndroidAPILevel);
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
FUseAndroidSDKLevelRule::FUseAndroidSDKLevelRule()
|
||||
: ISetupRule(
|
||||
"Compatibility_UseAndroidSDKLevel",
|
||||
LOCTEXT("UseAndroidSDKLevel_DisplayName", "Use Android SDK Level"),
|
||||
FText::Format(
|
||||
LOCTEXT("UseAndroidSDKLevel_Description", "Android SDK level should be set between {0} and {1} prior to packaging apks."),
|
||||
FText::AsCultureInvariant(AndroidSDKAPIMinLevel), FText::AsCultureInvariant(AndroidSDKAPIMaxLevel)),
|
||||
TEXT("https://developer.oculus.com/blog/meta-quest-apps-android-12l-june-30/"),
|
||||
ESetupRuleCategory::Compatibility,
|
||||
ESetupRuleSeverity::Critical,
|
||||
MetaQuest_All,
|
||||
true) {}
|
||||
|
||||
bool FUseAndroidSDKLevelRule::IsApplied() const
|
||||
{
|
||||
const UAndroidSDKSettings* Settings = GetMutableDefault<UAndroidSDKSettings>();
|
||||
FString SDKAPILevel = Settings->SDKAPILevel;
|
||||
|
||||
if (SDKAPILevel.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (SDKAPILevel.Equals(TEXT("latest")) || SDKAPILevel.Equals(TEXT("matchndk")))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!SDKAPILevel.Left(8).Equals(TEXT("android-")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (FCString::Atoi(*SDKAPILevel.Right(2)) < AndroidSDKAPIMinLevelInt)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (FCString::Atoi(*SDKAPILevel.Right(2)) > AndroidSDKAPIMaxLevelInt)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SaveSDKSettings()
|
||||
{
|
||||
ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings");
|
||||
if (!SettingsModule)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ISettingsContainerPtr SettingsContainer = SettingsModule->GetContainer("Project");
|
||||
if (!SettingsContainer.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
ISettingsCategoryPtr SettingsCategory = SettingsContainer->GetCategory("Platforms");
|
||||
if (!SettingsCategory.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
ISettingsSectionPtr SettingsSection = SettingsCategory->GetSection("AndroidSDK");
|
||||
if (!SettingsSection.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
TWeakObjectPtr<UObject> SettingsObject = SettingsSection->GetSettingsObject();
|
||||
if (!SettingsObject.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SettingsObject->UpdateGlobalUserConfigFile();
|
||||
}
|
||||
|
||||
void FUseAndroidSDKLevelRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OutShouldRestartEditor = false;
|
||||
OCULUSXR_UPDATE_SETTINGS(UAndroidSDKSettings, SDKAPILevel, FText::AsCultureInvariant(AndroidSDKAPIMinLevel).ToString());
|
||||
SaveSDKSettings();
|
||||
}
|
||||
|
||||
FUseAndroidNDKLevelRule::FUseAndroidNDKLevelRule()
|
||||
: ISetupRule(
|
||||
"Compatibility_UseAndroidNDKLevel",
|
||||
LOCTEXT("UseAndroidNDKLevel_DisplayName", "Use Android NDK Level"),
|
||||
FText::Format(
|
||||
LOCTEXT("UseAndroidNDKLevel_Description", "Android NDK level should be set between {0} and {1} prior to packaging apks."),
|
||||
FText::AsCultureInvariant(AndroidNDKAPIMinLevel), FText::AsCultureInvariant(AndroidNDKAPIMaxLevel)),
|
||||
TEXT("https://developer.oculus.com/blog/meta-quest-apps-must-target-android-10-starting-september-29/"),
|
||||
ESetupRuleCategory::Compatibility,
|
||||
ESetupRuleSeverity::Critical,
|
||||
MetaQuest_All,
|
||||
true) {}
|
||||
|
||||
bool FUseAndroidNDKLevelRule::IsApplied() const
|
||||
{
|
||||
const UAndroidSDKSettings* Settings = GetMutableDefault<UAndroidSDKSettings>();
|
||||
FString NDKAPILevel = Settings->NDKAPILevel;
|
||||
|
||||
if (NDKAPILevel.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (NDKAPILevel.Equals(TEXT("latest")))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!NDKAPILevel.Left(8).Equals(TEXT("android-")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (FCString::Atoi(*NDKAPILevel.Right(2)) < AndroidNDKAPIMinLevelInt)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (FCString::Atoi(*NDKAPILevel.Right(2)) > AndroidNDKAPIMaxLevelInt)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FUseAndroidNDKLevelRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OutShouldRestartEditor = false;
|
||||
OCULUSXR_UPDATE_SETTINGS(UAndroidSDKSettings, NDKAPILevel, FText::AsCultureInvariant(AndroidNDKAPIMinLevel).ToString());
|
||||
SaveSDKSettings();
|
||||
}
|
||||
|
||||
bool FUseArm64CPURule::IsApplied() const
|
||||
{
|
||||
const UAndroidRuntimeSettings* Settings = GetMutableDefault<UAndroidRuntimeSettings>();
|
||||
|
||||
return Settings->bBuildForArm64 && !Settings->bBuildForX8664;
|
||||
}
|
||||
|
||||
void FUseArm64CPURule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(UAndroidRuntimeSettings, bBuildForArm64, true);
|
||||
OCULUSXR_UPDATE_SETTINGS(UAndroidRuntimeSettings, bBuildForX8664, false);
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
bool FEnablePackageForMetaQuestRule::IsApplied() const
|
||||
{
|
||||
const UAndroidRuntimeSettings* Settings = GetMutableDefault<UAndroidRuntimeSettings>();
|
||||
|
||||
return Settings->bPackageForMetaQuest && !Settings->bSupportsVulkanSM5 && !Settings->bBuildForES31 && Settings->ExtraApplicationSettings.Find("com.oculus.supportedDevices") != INDEX_NONE;
|
||||
}
|
||||
|
||||
void FEnablePackageForMetaQuestRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(UAndroidRuntimeSettings, bPackageForMetaQuest, true);
|
||||
OCULUSXR_UPDATE_SETTINGS(UAndroidRuntimeSettings, bSupportsVulkanSM5, false);
|
||||
OCULUSXR_UPDATE_SETTINGS(UAndroidRuntimeSettings, bBuildForES31, false);
|
||||
|
||||
UAndroidRuntimeSettings* Settings = GetMutableDefault<UAndroidRuntimeSettings>();
|
||||
if (Settings->ExtraApplicationSettings.Find("com.oculus.supportedDevices") == INDEX_NONE)
|
||||
{
|
||||
const FString SupportedDevicesValue("quest|quest2|questpro");
|
||||
Settings->ExtraApplicationSettings.Append("<meta-data android:name=\"com.oculus.supportedDevices\" android:value=\"" + SupportedDevicesValue + "\" />");
|
||||
Settings->UpdateSinglePropertyInConfigFile(Settings->GetClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UAndroidRuntimeSettings, ExtraApplicationSettings)), Settings->GetDefaultConfigFilename());
|
||||
}
|
||||
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
bool FQuest2SupportedDeviceRule::IsApplied() const
|
||||
{
|
||||
const UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
|
||||
|
||||
return Settings->SupportedDevices.Contains(EOculusXRSupportedDevices::Quest2);
|
||||
}
|
||||
|
||||
void FQuest2SupportedDeviceRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
|
||||
|
||||
Settings->SupportedDevices.Add(EOculusXRSupportedDevices::Quest2);
|
||||
// UpdateSinglePropertyInConfigFile does not support arrays
|
||||
Settings->TryUpdateDefaultConfigFile();
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
bool FQuestProSupportedDeviceRule::IsApplied() const
|
||||
{
|
||||
const UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
|
||||
|
||||
return Settings->SupportedDevices.Contains(EOculusXRSupportedDevices::QuestPro);
|
||||
}
|
||||
|
||||
void FQuestProSupportedDeviceRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
|
||||
|
||||
Settings->SupportedDevices.Add(EOculusXRSupportedDevices::QuestPro);
|
||||
// UpdateSinglePropertyInConfigFile does not support arrays
|
||||
Settings->TryUpdateDefaultConfigFile();
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
bool FQuest3SupportedDeviceRule::IsApplied() const
|
||||
{
|
||||
const UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
|
||||
|
||||
return Settings->SupportedDevices.Contains(EOculusXRSupportedDevices::Quest3);
|
||||
}
|
||||
|
||||
void FQuest3SupportedDeviceRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
|
||||
|
||||
Settings->SupportedDevices.Add(EOculusXRSupportedDevices::Quest3);
|
||||
// UpdateSinglePropertyInConfigFile does not support arrays
|
||||
Settings->TryUpdateDefaultConfigFile();
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
bool FEnableFullscreenRule::IsApplied() const
|
||||
{
|
||||
const UAndroidRuntimeSettings* Settings = GetMutableDefault<UAndroidRuntimeSettings>();
|
||||
|
||||
return Settings->bFullScreen;
|
||||
}
|
||||
|
||||
void FEnableFullscreenRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(UAndroidRuntimeSettings, bFullScreen, true);
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
bool FEnableStartInVRRule::IsApplied() const
|
||||
{
|
||||
const UGeneralProjectSettings* Settings = GetDefault<UGeneralProjectSettings>();
|
||||
|
||||
return Settings->bStartInVR != 0;
|
||||
}
|
||||
|
||||
void FEnableStartInVRRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(UGeneralProjectSettings, bStartInVR, true);
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
bool FDisableTouchInterfaceRule::IsApplied() const
|
||||
{
|
||||
const UInputSettings* Settings = GetDefault<UInputSettings>();
|
||||
|
||||
return Settings->DefaultTouchInterface.IsNull();
|
||||
}
|
||||
|
||||
void FDisableTouchInterfaceRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(UInputSettings, DefaultTouchInterface, nullptr);
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
} // namespace OculusXRCompatibilityRules
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,199 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
#include "OculusXRSetupRule.h"
|
||||
|
||||
// Collection of rules related to compatibility. Can be extended as needed
|
||||
namespace OculusXRCompatibilityRules
|
||||
{
|
||||
|
||||
class FUseAndroidSDKMinimumRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FUseAndroidSDKMinimumRule();
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FUseAndroidSDKTargetRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FUseAndroidSDKTargetRule();
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
void SaveSDKSettings();
|
||||
|
||||
class FUseAndroidSDKLevelRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FUseAndroidSDKLevelRule();
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FUseAndroidNDKLevelRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FUseAndroidNDKLevelRule();
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FUseArm64CPURule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FUseArm64CPURule()
|
||||
: ISetupRule(
|
||||
"Compatibility_UseArm64CPU",
|
||||
NSLOCTEXT("OculusXRCompatibilityRules", "UseArm64CPU_DisplayName", "Use Arm64 CPU Architecture"),
|
||||
NSLOCTEXT("OculusXRCompatibilityRules", "UseArm64CPU_Description", "Meta Quest store requires 64-bit applications"),
|
||||
ESetupRuleCategory::Compatibility,
|
||||
ESetupRuleSeverity::Critical,
|
||||
MetaQuest_All) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FEnablePackageForMetaQuestRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnablePackageForMetaQuestRule()
|
||||
: ISetupRule(
|
||||
"Compatibility_UsePackageForMetaQuest",
|
||||
NSLOCTEXT("OculusXRCompatibilityRules", "UsePackageForQuest_DisplayName", "Enable Package for Meta Quest devices"),
|
||||
NSLOCTEXT("OculusXRCompatibilityRules", "UsePackageForQuest_Description", "\"Package for Meta Quest devices\" must be enabled."),
|
||||
ESetupRuleCategory::Compatibility,
|
||||
ESetupRuleSeverity::Critical,
|
||||
MetaQuest_All) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FQuest2SupportedDeviceRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FQuest2SupportedDeviceRule()
|
||||
: ISetupRule(
|
||||
"Compatibility_UsePackageForQuest2",
|
||||
NSLOCTEXT("OculusXRCompatibilityRules", "UsePackageForQuest_DisplayName", "Use Package for Quest2"),
|
||||
NSLOCTEXT("OculusXRCompatibilityRules", "UsePackageForQuest_Description", "Meta Quest2 must be added to \"Supported Meta Quest Devices\"."),
|
||||
ESetupRuleCategory::Compatibility,
|
||||
ESetupRuleSeverity::Critical,
|
||||
ESetupRulePlatform::MetaQuest_2) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FQuestProSupportedDeviceRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FQuestProSupportedDeviceRule()
|
||||
: ISetupRule(
|
||||
"Compatibility_UsePackageForQuestPro",
|
||||
NSLOCTEXT("OculusXRCompatibilityRules", "UsePackageForQuest_DisplayName", "Use Package for QuestPro"),
|
||||
NSLOCTEXT("OculusXRCompatibilityRules", "UsePackageForQuest_Description", "Meta QuestPro must be added to \"Supported Meta Quest Devices\"."),
|
||||
ESetupRuleCategory::Compatibility,
|
||||
ESetupRuleSeverity::Critical,
|
||||
ESetupRulePlatform::MetaQuest_Pro) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FQuest3SupportedDeviceRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FQuest3SupportedDeviceRule()
|
||||
: ISetupRule(
|
||||
"Compatibility_UsePackageForQuest3",
|
||||
NSLOCTEXT("OculusXRCompatibilityRules", "UsePackageForQuest_DisplayName", "Use Package for Quest3"),
|
||||
NSLOCTEXT("OculusXRCompatibilityRules", "UsePackageForQuest_Description", "Meta Quest3 must be added to \"Supported Meta Quest Devices\"."),
|
||||
ESetupRuleCategory::Compatibility,
|
||||
ESetupRuleSeverity::Critical,
|
||||
ESetupRulePlatform::MetaQuest_3) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FEnableFullscreenRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnableFullscreenRule()
|
||||
: ISetupRule(
|
||||
"Compatibility_EnableFullscreen",
|
||||
NSLOCTEXT("OculusXRCompatibilityRules", "EnableFullscreen_DisplayName", "Enable Fullscreen"),
|
||||
NSLOCTEXT("OculusXRCompatibilityRules", "EnableFullscreen_Description", "Android fullscreen must be enabled for VR"),
|
||||
ESetupRuleCategory::Compatibility,
|
||||
ESetupRuleSeverity::Warning,
|
||||
MetaQuest_All) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FEnableStartInVRRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnableStartInVRRule()
|
||||
: ISetupRule(
|
||||
"Compatibility_EnableStartInVR",
|
||||
NSLOCTEXT("OculusXRCompatibilityRules", "EnableStartInVR_DisplayName", "Enable Start in VR"),
|
||||
NSLOCTEXT("OculusXRCompatibilityRules", "EnableStartInVR_Description", "Enable the \"Start in VR\" setting to ensure your app starts in VR. (You can also ignore this and pass -vr at the command line"),
|
||||
ESetupRuleCategory::Compatibility,
|
||||
ESetupRuleSeverity::Warning) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FDisableTouchInterfaceRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FDisableTouchInterfaceRule()
|
||||
: ISetupRule(
|
||||
"Compatibility_DisableTouchInterface",
|
||||
NSLOCTEXT("OculusXRCompatibilityRules", "DisableTouchInterface_DisplayName", "Disable Touch Interface"),
|
||||
NSLOCTEXT("OculusXRCompatibilityRules", "DisableTouchInterface_Description", "Touch interface will interfere with correct VR input behavior"),
|
||||
ESetupRuleCategory::Compatibility,
|
||||
ESetupRuleSeverity::Critical) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
inline TArray<SetupRulePtr> CompatibilityRules_Table{
|
||||
MakeShared<FUseAndroidSDKMinimumRule>(),
|
||||
MakeShared<FUseAndroidSDKTargetRule>(),
|
||||
MakeShared<FUseAndroidSDKLevelRule>(),
|
||||
MakeShared<FUseAndroidNDKLevelRule>(),
|
||||
MakeShared<FUseArm64CPURule>(),
|
||||
MakeShared<FEnablePackageForMetaQuestRule>(),
|
||||
MakeShared<FQuest2SupportedDeviceRule>(),
|
||||
MakeShared<FQuestProSupportedDeviceRule>(),
|
||||
MakeShared<FQuest3SupportedDeviceRule>(),
|
||||
MakeShared<FEnableFullscreenRule>(),
|
||||
MakeShared<FEnableStartInVRRule>(),
|
||||
MakeShared<FDisableTouchInterfaceRule>()
|
||||
};
|
||||
} // namespace OculusXRCompatibilityRules
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRMovementRules.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "OculusXRHMDRuntimeSettings.h"
|
||||
#include "OculusXRPSTUtils.h"
|
||||
#include "OculusXRBodyTrackingComponent.h"
|
||||
#include "OculusXREyeTrackingComponent.h"
|
||||
#include "OculusXRFaceTrackingComponent.h"
|
||||
#include "OculusXRProjectSetupToolModule.h"
|
||||
#include "OculusXRRuleProcessorSubsystem.h"
|
||||
|
||||
namespace OculusXRMovementRules
|
||||
{
|
||||
bool FEnableBodyTrackingRule::IsApplied() const
|
||||
{
|
||||
const UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
|
||||
return Settings->bBodyTrackingEnabled;
|
||||
}
|
||||
|
||||
void FEnableBodyTrackingRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(UOculusXRHMDRuntimeSettings, bBodyTrackingEnabled, true);
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
bool FEnableBodyTrackingRule::IsValid()
|
||||
{
|
||||
return OculusXRPSTUtils::IsComponentOfTypeInWorld<UOculusXRBodyTrackingComponent>();
|
||||
}
|
||||
|
||||
bool FEnableFaceTrackingRule::IsApplied() const
|
||||
{
|
||||
const UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
|
||||
|
||||
return Settings->bFaceTrackingEnabled;
|
||||
}
|
||||
|
||||
void FEnableFaceTrackingRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(UOculusXRHMDRuntimeSettings, bFaceTrackingEnabled, true);
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
bool FEnableFaceTrackingRule::IsValid()
|
||||
{
|
||||
return OculusXRPSTUtils::IsComponentOfTypeInWorld<UOculusXRFaceTrackingComponent>();
|
||||
}
|
||||
|
||||
bool FEnableEyeTrackingRule::IsApplied() const
|
||||
{
|
||||
const UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
|
||||
|
||||
return Settings->bEyeTrackingEnabled;
|
||||
}
|
||||
|
||||
void FEnableEyeTrackingRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(UOculusXRHMDRuntimeSettings, bEyeTrackingEnabled, true);
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
bool FEnableEyeTrackingRule::IsValid()
|
||||
{
|
||||
return OculusXRPSTUtils::IsComponentOfTypeInWorld<UOculusXREyeTrackingComponent>();
|
||||
}
|
||||
} // namespace OculusXRMovementRules
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
#include "OculusXRSetupRule.h"
|
||||
|
||||
/*
|
||||
* Collection of rules related to movement SDK. Can be extended as needed
|
||||
*/
|
||||
namespace OculusXRMovementRules
|
||||
{
|
||||
class FEnableBodyTrackingRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnableBodyTrackingRule()
|
||||
: ISetupRule(
|
||||
"Feature_EnableBodyTracking",
|
||||
NSLOCTEXT("OculusXRMovementRules", "EnableBodyTracking_DisplayName", "Enable Body Tracking"),
|
||||
NSLOCTEXT("OculusXRMovementRules", "EnableBodyTracking_Description", "Body tracking must be enabled when using body tracking features"),
|
||||
ESetupRuleCategory::Features,
|
||||
ESetupRuleSeverity::Critical) {}
|
||||
virtual bool IsApplied() const override;
|
||||
virtual bool IsValid() override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FEnableFaceTrackingRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnableFaceTrackingRule()
|
||||
: ISetupRule(
|
||||
"Feature_EnableFaceTracking",
|
||||
NSLOCTEXT("OculusXRMovementRules", "EnableFaceTracking_DisplayName", "Enable Face Tracking"),
|
||||
NSLOCTEXT("OculusXRMovementRules", "EnableFaceTracking_Description", "Face tracking must be enabled when using face tracking features"),
|
||||
ESetupRuleCategory::Features,
|
||||
ESetupRuleSeverity::Critical) {}
|
||||
virtual bool IsApplied() const override;
|
||||
virtual bool IsValid() override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FEnableEyeTrackingRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnableEyeTrackingRule()
|
||||
: ISetupRule(
|
||||
"Feature_EnableEyeTracking",
|
||||
NSLOCTEXT("OculusXRMovementRules", "EnableEyeTracking_DisplayName", "Enable Eye Tracking"),
|
||||
NSLOCTEXT("OculusXRMovementRules", "EnableEyeTracking_Description", "Eye tracking must be enabled when using eye tracking features"),
|
||||
ESetupRuleCategory::Features,
|
||||
ESetupRuleSeverity::Critical) {}
|
||||
virtual bool IsApplied() const override;
|
||||
virtual bool IsValid() override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
inline TArray<SetupRulePtr> MovementRules_Table{
|
||||
MakeShared<FEnableBodyTrackingRule>(),
|
||||
MakeShared<FEnableFaceTrackingRule>(),
|
||||
MakeShared<FEnableEyeTrackingRule>()
|
||||
};
|
||||
} // namespace OculusXRMovementRules
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRPassthroughRules.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "OculusXRHMDRuntimeSettings.h"
|
||||
#include "OculusXRPSTUtils.h"
|
||||
#include "OculusXRPassthroughLayerComponent.h"
|
||||
#include "BPNode_InitializePersistentPassthrough.h"
|
||||
#include "OculusXRProjectSetupToolModule.h"
|
||||
#include "OculusXRRuleProcessorSubsystem.h"
|
||||
#include "Engine/RendererSettings.h"
|
||||
#include "EdGraph/EdGraphPin.h"
|
||||
#include "K2Node.h"
|
||||
|
||||
namespace OculusXRPassthroughRules
|
||||
{
|
||||
bool FEnablePassthroughRule::IsApplied() const
|
||||
{
|
||||
const UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
|
||||
|
||||
return Settings->bInsightPassthroughEnabled;
|
||||
}
|
||||
|
||||
void FEnablePassthroughRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(UOculusXRHMDRuntimeSettings, bInsightPassthroughEnabled, true);
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
bool FEnablePassthroughRule::IsValid()
|
||||
{
|
||||
if (OculusXRPSTUtils::IsComponentOfTypeInWorld<UOculusXRPassthroughLayerComponent>()
|
||||
|| OculusXRPSTUtils::IsComponentOfTypeInWorld<UBPNode_InitializePersistentPassthrough>(
|
||||
[](UBPNode_InitializePersistentPassthrough* n) {
|
||||
return n && n->GetExecPin() && n->GetExecPin()->LinkedTo.Num() > 0;
|
||||
}))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FAllowAlphaToneMapperPassthroughRule::IsApplied() const
|
||||
{
|
||||
URendererSettings* Settings = GetMutableDefault<URendererSettings>();
|
||||
#if UE_VERSION_OLDER_THAN(5, 5, 0)
|
||||
return Settings->bEnableAlphaChannelInPostProcessing == EAlphaChannelMode::AllowThroughTonemapper;
|
||||
#else
|
||||
return Settings->bEnableAlphaChannelInPostProcessing;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool FAllowAlphaToneMapperPassthroughRule::IsValid()
|
||||
{
|
||||
const UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
|
||||
|
||||
return Settings->bInsightPassthroughEnabled || Settings->SystemSplashBackground == ESystemSplashBackgroundType::Contextual;
|
||||
}
|
||||
|
||||
void FAllowAlphaToneMapperPassthroughRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
#if UE_VERSION_OLDER_THAN(5, 5, 0)
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, bEnableAlphaChannelInPostProcessing, EAlphaChannelMode::AllowThroughTonemapper);
|
||||
#else
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, bEnableAlphaChannelInPostProcessing, true);
|
||||
#endif
|
||||
OutShouldRestartEditor = true;
|
||||
}
|
||||
} // namespace OculusXRPassthroughRules
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
#include "OculusXRSetupRule.h"
|
||||
|
||||
/*
|
||||
* Collection of rules related to passthrough. Can be extended as needed
|
||||
*/
|
||||
namespace OculusXRPassthroughRules
|
||||
{
|
||||
class FEnablePassthroughRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnablePassthroughRule()
|
||||
: ISetupRule(
|
||||
"Feature_EnablePassthrough",
|
||||
NSLOCTEXT("OculusXRPassthroughRules", "EnablePassthrough_DisplayName", "Enable Passthrough"),
|
||||
NSLOCTEXT("OculusXRPassthroughRules", "EnablePassthrough_Description", "Passthrough must be enabled when using passthrough features"),
|
||||
ESetupRuleCategory::Features,
|
||||
ESetupRuleSeverity::Critical) {}
|
||||
virtual bool IsApplied() const override;
|
||||
virtual bool IsValid() override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FAllowAlphaToneMapperPassthroughRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FAllowAlphaToneMapperPassthroughRule()
|
||||
: ISetupRule(
|
||||
"Feature_AllowAlphaToneMapperPassthrough",
|
||||
NSLOCTEXT("OculusXRPassthroughRules", "AllowAlphaToneMapperPassthrough_DisplayName", "Enable passing alpha channel through tonemapper"),
|
||||
NSLOCTEXT("OculusXRPassthroughRules", "AllowAlphaToneMapperPassthrough_Description", "For passthrough to work over Link alpha channel must be passed through tonemapper."),
|
||||
ESetupRuleCategory::Features,
|
||||
ESetupRuleSeverity::Warning) {}
|
||||
virtual bool IsApplied() const override;
|
||||
virtual bool IsValid() override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
inline TArray<SetupRulePtr> PassthroughRules_Table{
|
||||
MakeShared<FEnablePassthroughRule>(),
|
||||
MakeShared<FAllowAlphaToneMapperPassthroughRule>()
|
||||
};
|
||||
} // namespace OculusXRPassthroughRules
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRPluginRules.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "OculusXRHMDRuntimeSettings.h"
|
||||
#include "OculusXRPSTUtils.h"
|
||||
#include "Editor/GameProjectGeneration/Public/GameProjectGenerationModule.h"
|
||||
#include "Interfaces/IPluginManager.h"
|
||||
#include "Interfaces/IProjectManager.h"
|
||||
#include "Misc/MessageDialog.h"
|
||||
|
||||
namespace OculusXRPluginRules
|
||||
{
|
||||
namespace
|
||||
{
|
||||
bool IsPluginEnabled(const FString& PluginName)
|
||||
{
|
||||
const auto Plugin = IPluginManager::Get().FindPlugin(PluginName);
|
||||
if (!Plugin)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Plugin->IsEnabled();
|
||||
}
|
||||
|
||||
bool DisablePlugin(const FString& PluginName)
|
||||
{
|
||||
FText FailMessage;
|
||||
bool bSuccess = IProjectManager::Get().SetPluginEnabled(
|
||||
PluginName, false, FailMessage);
|
||||
const bool bIsProjectDirty = IProjectManager::Get().IsCurrentProjectDirty();
|
||||
if (bSuccess && bIsProjectDirty)
|
||||
{
|
||||
FGameProjectGenerationModule::Get().TryMakeProjectFileWriteable(FPaths::GetProjectFilePath());
|
||||
bSuccess = IProjectManager::Get().SaveCurrentProjectToDisk(FailMessage);
|
||||
}
|
||||
if (!bSuccess)
|
||||
{
|
||||
FMessageDialog::Open(EAppMsgType::Ok, FailMessage);
|
||||
}
|
||||
|
||||
return bSuccess && !bIsProjectDirty;
|
||||
}
|
||||
} // namespace
|
||||
bool FUseRecommendedXRAPIRule::IsApplied() const
|
||||
{
|
||||
const UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
|
||||
return Settings->XrApi == EOculusXRXrApi::OVRPluginOpenXR;
|
||||
}
|
||||
|
||||
void FUseRecommendedXRAPIRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(UOculusXRHMDRuntimeSettings, XrApi, EOculusXRXrApi::OVRPluginOpenXR);
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
bool FDisableOculusVRRule::IsApplied() const
|
||||
{
|
||||
return bApplied || !IsPluginEnabled(PluginName);
|
||||
}
|
||||
|
||||
void FDisableOculusVRRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OutShouldRestartEditor = DisablePlugin(PluginName);
|
||||
bApplied = OutShouldRestartEditor;
|
||||
}
|
||||
|
||||
bool FDisableSteamVRRule::IsApplied() const
|
||||
{
|
||||
return bApplied || !IsPluginEnabled(PluginName);
|
||||
}
|
||||
|
||||
void FDisableSteamVRRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OutShouldRestartEditor = DisablePlugin(PluginName);
|
||||
bApplied = OutShouldRestartEditor;
|
||||
}
|
||||
} // namespace OculusXRPluginRules
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
#include "OculusXRSetupRule.h"
|
||||
|
||||
// Collection of rules related to plugins. Can be extended as needed
|
||||
namespace OculusXRPluginRules
|
||||
{
|
||||
class FUseRecommendedXRAPIRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FUseRecommendedXRAPIRule()
|
||||
: ISetupRule("Plugin_UseRecommendedXRAPI",
|
||||
NSLOCTEXT("OculusXRPluginRules", "UseRecommendedXRAPI_DisplayName", "Use Recommended XR API"),
|
||||
NSLOCTEXT("OculusXRPluginRules", "UseRecommendedXRAPI_Description", "It is currently recommended to use OVRPlugin + OpenXR for the XR API"),
|
||||
ESetupRuleCategory::Plugins,
|
||||
ESetupRuleSeverity::Warning) {}
|
||||
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FDisableOculusVRRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FDisableOculusVRRule()
|
||||
: ISetupRule("Plugin_DisableOculusVR",
|
||||
NSLOCTEXT("OculusXRPluginRules", "DisableOculusVR_DisplayName", "Disable OculusVR Plugin"),
|
||||
NSLOCTEXT("OculusXRPluginRules", "DisableOculusVR_Description", "The OculusVR plugin is deprecated and should be disabled"),
|
||||
ESetupRuleCategory::Plugins,
|
||||
ESetupRuleSeverity::Warning) {}
|
||||
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
|
||||
private:
|
||||
FString PluginName = "OculusVR";
|
||||
bool bApplied = false;
|
||||
};
|
||||
|
||||
class FDisableSteamVRRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FDisableSteamVRRule()
|
||||
: ISetupRule("Plugin_DisableSteamVR",
|
||||
NSLOCTEXT("OculusXRPluginRules", "DisableSteamVR_DisplayName", "Disable SteamVR Plugin"),
|
||||
NSLOCTEXT("OculusXRPluginRules", "DisableSteamVR_Description", "The SteamVR plugin is deprecated and should be disabled"),
|
||||
ESetupRuleCategory::Plugins,
|
||||
ESetupRuleSeverity::Warning) {}
|
||||
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
|
||||
private:
|
||||
FString PluginName = "SteamVR";
|
||||
bool bApplied = false;
|
||||
};
|
||||
|
||||
// All defined plugin rules. Add new rules to this table for them to be auto-registered
|
||||
inline TArray<SetupRulePtr> PluginRules_Table{
|
||||
MakeShared<FUseRecommendedXRAPIRule>(),
|
||||
MakeShared<FDisableOculusVRRule>(),
|
||||
MakeShared<FDisableSteamVRRule>()
|
||||
};
|
||||
} // namespace OculusXRPluginRules
|
||||
@@ -0,0 +1,406 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRRenderingRules.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "AndroidRuntimeSettings.h"
|
||||
#include "DataDrivenShaderPlatformInfo.h"
|
||||
#include "EngineUtils.h"
|
||||
#include "OculusXRHMDRuntimeSettings.h"
|
||||
#include "OculusXRPSTUtils.h"
|
||||
#include "OculusXRRuleProcessorSubsystem.h"
|
||||
#include "Engine/PostProcessVolume.h"
|
||||
#include "Engine/RendererSettings.h"
|
||||
#include "Misc/EngineVersionComparison.h"
|
||||
#include "Editor.h"
|
||||
#include "Kismet/KismetSystemLibrary.h"
|
||||
|
||||
namespace OculusXRRenderingRules
|
||||
{
|
||||
namespace
|
||||
{
|
||||
FPreviewPlatformInfo GetAndroidPreviewPlatformInfo()
|
||||
{
|
||||
const FName AndroidPlatformName(TEXT("AndroidVulkan_Preview"));
|
||||
|
||||
const EShaderPlatform ShaderPlatform = FDataDrivenShaderPlatformInfo::GetShaderPlatformFromName(AndroidPlatformName);
|
||||
|
||||
const ERHIFeatureLevel::Type FeatureLevel = GetMaxSupportedFeatureLevel(ShaderPlatform);
|
||||
|
||||
const auto& AllPreviewPlatforms = FDataDrivenPlatformInfoRegistry::GetAllPreviewPlatformMenuItems();
|
||||
|
||||
for (const auto& Platform : AllPreviewPlatforms)
|
||||
{
|
||||
if (Platform.PreviewShaderPlatformName == AndroidPlatformName)
|
||||
{
|
||||
return FPreviewPlatformInfo(FeatureLevel, ShaderPlatform, Platform.PlatformName, Platform.ShaderFormat, Platform.DeviceProfileName,
|
||||
true, Platform.PreviewShaderPlatformName);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
} // namespace
|
||||
bool FUseVulkanRule::IsApplied() const
|
||||
{
|
||||
const UAndroidRuntimeSettings* Settings = GetMutableDefault<UAndroidRuntimeSettings>();
|
||||
|
||||
return Settings->bSupportsVulkan && !Settings->bBuildForES31;
|
||||
}
|
||||
|
||||
void FUseVulkanRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(UAndroidRuntimeSettings, bSupportsVulkan, true);
|
||||
OCULUSXR_UPDATE_SETTINGS(UAndroidRuntimeSettings, bBuildForES31, false);
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
bool FUseHalfPrecisionFloatRule::IsApplied() const
|
||||
{
|
||||
const URendererSettings* Settings = GetMutableDefault<URendererSettings>();
|
||||
|
||||
return Settings->MobileFloatPrecisionMode == EMobileFloatPrecisionMode::Half;
|
||||
}
|
||||
|
||||
void FUseHalfPrecisionFloatRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, MobileFloatPrecisionMode, EMobileFloatPrecisionMode::Half);
|
||||
OutShouldRestartEditor = true;
|
||||
}
|
||||
|
||||
bool FEnableInstancedStereoRule::IsApplied() const
|
||||
{
|
||||
const URendererSettings* Settings = GetMutableDefault<URendererSettings>();
|
||||
return Settings->bMultiView != 0;
|
||||
}
|
||||
|
||||
void FEnableInstancedStereoRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, bMultiView, 1);
|
||||
OutShouldRestartEditor = true;
|
||||
}
|
||||
|
||||
bool FEnableForwardShadingRule::IsApplied() const
|
||||
{
|
||||
const URendererSettings* Settings = GetMutableDefault<URendererSettings>();
|
||||
|
||||
return Settings->MobileShadingPath == EMobileShadingPath::Forward;
|
||||
}
|
||||
|
||||
void FEnableForwardShadingRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, MobileShadingPath, EMobileShadingPath::Forward);
|
||||
OutShouldRestartEditor = true;
|
||||
}
|
||||
|
||||
bool FEnablePCForwardShadingRule::IsApplied() const
|
||||
{
|
||||
const URendererSettings* Settings = GetMutableDefault<URendererSettings>();
|
||||
|
||||
return Settings->bForwardShading;
|
||||
}
|
||||
|
||||
void FEnablePCForwardShadingRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, bForwardShading, true);
|
||||
OutShouldRestartEditor = true;
|
||||
}
|
||||
|
||||
bool FEnableMSAARule::IsApplied() const
|
||||
{
|
||||
const URendererSettings* Settings = GetMutableDefault<URendererSettings>();
|
||||
return Settings->MobileAntiAliasing == EMobileAntiAliasingMethod::MSAA
|
||||
&& Settings->MSAASampleCount == ECompositingSampleCount::Four;
|
||||
}
|
||||
|
||||
void FEnableMSAARule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, MobileAntiAliasing, EMobileAntiAliasingMethod::MSAA);
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, MSAASampleCount, ECompositingSampleCount::Four);
|
||||
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
bool FEnableOcclusionCullingRule::IsApplied() const
|
||||
{
|
||||
const URendererSettings* Settings = GetMutableDefault<URendererSettings>();
|
||||
|
||||
return Settings->bOcclusionCulling;
|
||||
}
|
||||
|
||||
void FEnableOcclusionCullingRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, bOcclusionCulling, 1);
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
bool FEnableDynamicFoveationRule::IsApplied() const
|
||||
{
|
||||
const UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
|
||||
|
||||
return Settings->bDynamicFoveatedRendering;
|
||||
}
|
||||
|
||||
void FEnableDynamicFoveationRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(UOculusXRHMDRuntimeSettings, bDynamicFoveatedRendering, true);
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
#ifdef WITH_OCULUS_BRANCH
|
||||
bool FEnableDynamicResolutionRule::IsApplied() const
|
||||
{
|
||||
const UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
|
||||
|
||||
return Settings->bDynamicResolution;
|
||||
}
|
||||
|
||||
void FEnableDynamicResolutionRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(UOculusXRHMDRuntimeSettings, bDynamicResolution, true);
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
bool FEnableMobileUniformLocalLightsRule::IsApplied() const
|
||||
{
|
||||
return GetMutableDefault<URendererSettings>()->bMobileUniformLocalLights;
|
||||
}
|
||||
|
||||
void FEnableMobileUniformLocalLightsRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
if (GetMutableDefault<URendererSettings>()->bMobileSupportGPUScene)
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("Failed to enable MobileUniformLocalLights because MobileUniformLocalLights is incompatible with GPUScene."));
|
||||
return;
|
||||
}
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, bMobileUniformLocalLights, true);
|
||||
OutShouldRestartEditor = true;
|
||||
}
|
||||
|
||||
bool FEnableEmulatedUniformBuffersRule::IsApplied() const
|
||||
{
|
||||
const URendererSettings* Settings = GetMutableDefault<URendererSettings>();
|
||||
return Settings->bVulkanUseEmulatedUBs;
|
||||
}
|
||||
|
||||
void FEnableEmulatedUniformBuffersRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
if (GetMutableDefault<URendererSettings>()->bMobileSupportGPUScene)
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("Failed to enable EmulatedUniformBuffers because EmulatedUniformBuffers is incompatible with GPUScene."));
|
||||
return;
|
||||
}
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, bVulkanUseEmulatedUBs, true);
|
||||
OutShouldRestartEditor = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool FDisableLensFlareRule::IsApplied() const
|
||||
{
|
||||
const URendererSettings* Settings = GetMutableDefault<URendererSettings>();
|
||||
if (!Settings->bMobilePostProcessing)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (TActorIterator<APostProcessVolume> ActorItr(GEditor->GetEditorWorldContext().World()); ActorItr; ++ActorItr)
|
||||
{
|
||||
if (ActorItr->Settings.bOverride_LensFlareIntensity && ActorItr->Settings.LensFlareIntensity > 0.0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return Settings->bDefaultFeatureLensFlare == 0;
|
||||
}
|
||||
|
||||
void FDisableLensFlareRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, bDefaultFeatureLensFlare, false);
|
||||
|
||||
UKismetSystemLibrary::BeginTransaction("ProjectSetupTool", NSLOCTEXT("OculusXRRenderingRules", "DisableLensFlare", "Disable Lens Flare"), nullptr);
|
||||
for (TActorIterator<APostProcessVolume> ActorItr(GEditor->GetEditorWorldContext().World()); ActorItr; ++ActorItr)
|
||||
{
|
||||
if (ActorItr->Settings.bOverride_LensFlareIntensity)
|
||||
{
|
||||
UKismetSystemLibrary::TransactObject(*ActorItr);
|
||||
ActorItr->Settings.bOverride_LensFlareIntensity = false;
|
||||
}
|
||||
}
|
||||
UKismetSystemLibrary::EndTransaction();
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
bool FDisablePostProcessingRule::IsApplied() const
|
||||
{
|
||||
const URendererSettings* Settings = GetMutableDefault<URendererSettings>();
|
||||
|
||||
return Settings->bMobilePostProcessing == 0;
|
||||
}
|
||||
|
||||
void FDisablePostProcessingRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, bMobilePostProcessing, 0);
|
||||
OutShouldRestartEditor = true;
|
||||
}
|
||||
|
||||
bool FDisableAmbientOcclusionRule::IsApplied() const
|
||||
{
|
||||
const URendererSettings* Settings = GetMutableDefault<URendererSettings>();
|
||||
|
||||
return Settings->bMobileAmbientOcclusion == 0;
|
||||
}
|
||||
|
||||
void FDisableAmbientOcclusionRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, bMobileAmbientOcclusion, 0);
|
||||
OutShouldRestartEditor = true;
|
||||
}
|
||||
|
||||
bool FEnableMultiViewRule::IsApplied() const
|
||||
{
|
||||
return GetMutableDefault<URendererSettings>()->bMobileMultiView != 0;
|
||||
}
|
||||
|
||||
void FEnableMultiViewRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, bMobileMultiView, 1);
|
||||
OutShouldRestartEditor = true;
|
||||
}
|
||||
|
||||
bool FEnableStaticLightingRule::IsApplied() const
|
||||
{
|
||||
return GetMutableDefault<URendererSettings>()->bAllowStaticLighting;
|
||||
}
|
||||
|
||||
void FEnableStaticLightingRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, bAllowStaticLighting, true);
|
||||
OutShouldRestartEditor = true;
|
||||
}
|
||||
|
||||
bool FDisableMobileShaderStaticAndCSMShadowReceiversRule::IsApplied() const
|
||||
{
|
||||
return !GetMutableDefault<URendererSettings>()->bMobileEnableStaticAndCSMShadowReceivers;
|
||||
}
|
||||
|
||||
void FDisableMobileShaderStaticAndCSMShadowReceiversRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, bMobileEnableStaticAndCSMShadowReceivers, false);
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
bool FDisableMobileShaderStaticAndCSMShadowReceiversRule::IsValid()
|
||||
{
|
||||
const UOculusXRRuleProcessorSubsystem* RuleProcessorSubsystem = GEngine->GetEngineSubsystem<UOculusXRRuleProcessorSubsystem>();
|
||||
return !RuleProcessorSubsystem->DynamicLightsExistInProject();
|
||||
}
|
||||
|
||||
bool FDisableMobileShaderAllowDistanceFieldShadowsRule::IsApplied() const
|
||||
{
|
||||
return !GetMutableDefault<URendererSettings>()->bMobileAllowDistanceFieldShadows;
|
||||
}
|
||||
|
||||
void FDisableMobileShaderAllowDistanceFieldShadowsRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, bMobileAllowDistanceFieldShadows, false);
|
||||
OutShouldRestartEditor = true;
|
||||
}
|
||||
|
||||
bool FDisableMobileShaderAllowDistanceFieldShadowsRule::IsValid()
|
||||
{
|
||||
const UOculusXRRuleProcessorSubsystem* RuleProcessorSubsystem = GEngine->GetEngineSubsystem<UOculusXRRuleProcessorSubsystem>();
|
||||
return !RuleProcessorSubsystem->DynamicLightsExistInProject();
|
||||
}
|
||||
#if UE_VERSION_OLDER_THAN(5, 5, 0)
|
||||
bool FDisableMobileShaderAllowMovableDirectionalLightsRule::IsApplied() const
|
||||
{
|
||||
return !GetMutableDefault<URendererSettings>()->bMobileAllowMovableDirectionalLights;
|
||||
}
|
||||
|
||||
void FDisableMobileShaderAllowMovableDirectionalLightsRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, bMobileAllowMovableDirectionalLights, false);
|
||||
OutShouldRestartEditor = true;
|
||||
}
|
||||
|
||||
bool FDisableMobileShaderAllowMovableDirectionalLightsRule::IsValid()
|
||||
{
|
||||
const UOculusXRRuleProcessorSubsystem* RuleProcessorSubsystem = GEngine->GetEngineSubsystem<UOculusXRRuleProcessorSubsystem>();
|
||||
return !RuleProcessorSubsystem->DynamicLightsExistInProject();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WITH_OCULUS_BRANCH
|
||||
bool FDisableMobileGPUSceneRule::IsApplied() const
|
||||
{
|
||||
const URendererSettings* RenderSettings = GetMutableDefault<URendererSettings>();
|
||||
// check if GPUScene conflicts with any existing features: EUB or ULL
|
||||
return !((RenderSettings->bMobileSupportGPUScene && RenderSettings->bVulkanUseEmulatedUBs) || (RenderSettings->bMobileSupportGPUScene && RenderSettings->bMobileUniformLocalLights));
|
||||
}
|
||||
|
||||
void FDisableMobileGPUSceneRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, bMobileSupportGPUScene, false);
|
||||
OutShouldRestartEditor = true;
|
||||
}
|
||||
|
||||
bool FDisableMobileGPUSceneRule::IsValid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
FUseAndroidVulkanPreviewPlatform::FUseAndroidVulkanPreviewPlatform()
|
||||
: ISetupRule("Rendering_UseAndroidVulkanPreviewPlatform",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "UseAndroidVulkanPreviewPlatform_DisplayName", "Use Android Vulkan Preview Platform"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "UseAndroidVulkanPreviewPlatform_Description", "Android Vulkan Mobile Preview Platform is necessery for correct behaviour of passthrough over Link."),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Warning,
|
||||
ESetupRulePlatform::MetaLink)
|
||||
{
|
||||
AndroidVulkanPreview = GetAndroidPreviewPlatformInfo();
|
||||
}
|
||||
|
||||
bool FUseAndroidVulkanPreviewPlatform::IsApplied() const
|
||||
{
|
||||
FName CurrentPlatformName;
|
||||
if (!GEditor->GetPreviewPlatformName(CurrentPlatformName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return CurrentPlatformName == AndroidVulkanPreview.PreviewPlatformName;
|
||||
}
|
||||
|
||||
bool FUseAndroidVulkanPreviewPlatform::IsValid()
|
||||
{
|
||||
const UOculusXRHMDRuntimeSettings* Settings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
|
||||
return Settings->bInsightPassthroughEnabled;
|
||||
}
|
||||
|
||||
void FUseAndroidVulkanPreviewPlatform::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
GEditor->SetPreviewPlatform(AndroidVulkanPreview, true);
|
||||
OutShouldRestartEditor = false;
|
||||
}
|
||||
|
||||
FDisableMobileMoveableSpotlightShadowsRule::FDisableMobileMoveableSpotlightShadowsRule()
|
||||
: ISetupRule("Rendering_MobileMoveableSpotlightShadows",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "MobileMoveableSpotlightShadows_DisplayName", "Mobile Moveable Spotlight Shadows"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "MobileMoveableSpotlightShadows_Description", "Mobile Movable Spotlights Shadows are not supported with Mobile Multi-View enabled."),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Critical,
|
||||
MetaQuest_All)
|
||||
{
|
||||
}
|
||||
|
||||
bool FDisableMobileMoveableSpotlightShadowsRule::IsApplied() const
|
||||
{
|
||||
const URendererSettings* RenderSettings = GetMutableDefault<URendererSettings>();
|
||||
return RenderSettings->bMobileMultiView ? !RenderSettings->bMobileAllowMovableSpotlightShadows : true;
|
||||
}
|
||||
|
||||
void FDisableMobileMoveableSpotlightShadowsRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, bMobileAllowMovableSpotlightShadows, false);
|
||||
OutShouldRestartEditor = true;
|
||||
}
|
||||
} // namespace OculusXRRenderingRules
|
||||
@@ -0,0 +1,403 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "OculusXRSetupRule.h"
|
||||
#include "Editor/EditorEngine.h"
|
||||
#include "Misc/EngineVersionComparison.h"
|
||||
|
||||
// Collection of rules related to rendering. Can be extended as needed
|
||||
namespace OculusXRRenderingRules
|
||||
{
|
||||
class FUseVulkanRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FUseVulkanRule()
|
||||
: ISetupRule("Rendering_UseVulkan",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "UseVulkan_DisplayName", "Use Vulkan Rendering Backend"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "UseVulkan_Description", "Oculus recommends using Vulkan as the rendering backend for all mobile apps."),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance,
|
||||
MetaQuest_All) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FUseHalfPrecisionFloatRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FUseHalfPrecisionFloatRule()
|
||||
: ISetupRule("Rendering_UseHalfPrecisionFloat",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "UseHalfPrecisionFloat_DisplayName", "Use Half Precision Float"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "UseHalfPrecisionFloat_Description", "Half precision float provides increased shader performance."),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance,
|
||||
MetaQuest_All) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FEnableInstancedStereoRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnableInstancedStereoRule()
|
||||
: ISetupRule("Rendering_EnableInstancedStereo",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnableInstancedStereo_DisplayName", "Enable Instanced Stereo"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnableInstancedStereo_Description", "Instanced stereo substantially reduces draw calls, and improves rendering performance."),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance,
|
||||
ESetupRulePlatform::MetaLink) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FEnableForwardShadingRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnableForwardShadingRule()
|
||||
: ISetupRule("Rendering_EnableForwardShading",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnableForwardShading_DisplayName", "Enable Forward Shading"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnableForwardShading_Description", "Forward shading is often better suited for VR rendering."),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance,
|
||||
ESetupRulePlatform::MetaQuest_2) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FEnablePCForwardShadingRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnablePCForwardShadingRule()
|
||||
: ISetupRule("Rendering_EnablePCForwardShading",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnablePCForwardShading_DisplayName", "Enable PC Forward Shading"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnablePCForwardShading_Description", "Forward shading is often better suited for VR rendering."),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance,
|
||||
ESetupRulePlatform::MetaLink) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FEnableMSAARule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnableMSAARule()
|
||||
: ISetupRule("Rendering_EnableMSAA",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnableMSAA_DisplayName", "Enable MSAA"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnableMSAA_Description", "MSAA provides higher quality antialiasing at a reasonable cost."),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance,
|
||||
MetaQuest_All) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FEnableOcclusionCullingRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnableOcclusionCullingRule()
|
||||
: ISetupRule("Rendering_EnableOcclusionCulling",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnableOcclusionCulling_DisplayName", "Enable Occlusion Culling"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnableOcclusionCulling_Description", "Occlusion culling can provide significant performance gains."),
|
||||
TEXT("https://developers.meta.com/horizon/documentation/unreal/po-fork-engine-perf"),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FEnableDynamicFoveationRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnableDynamicFoveationRule()
|
||||
: ISetupRule("Rendering_EnableDynamicFoveation",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnableDynamicFoveation_DisplayName", "Enable Dynamic Foveation"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnableDynamicFoveation_Description", "Dynamic foveated rendering significantly reduces rendering cost."),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance,
|
||||
MetaQuest_All) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
#ifdef WITH_OCULUS_BRANCH
|
||||
class FEnableDynamicResolutionRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnableDynamicResolutionRule()
|
||||
: ISetupRule("Rendering_EnableDynamicResolution",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnableDynamicResolution_DisplayName", "Enable Dynamic Resolution"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnableDynamicResolution_Description", "Dynamic resolution rendering significantly reduces rendering cost."),
|
||||
TEXT("https://developers.meta.com/horizon/documentation/unreal/dynamic-resolution-unreal"),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance,
|
||||
MetaQuest_All) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FEnableMobileUniformLocalLightsRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnableMobileUniformLocalLightsRule()
|
||||
: ISetupRule("Rendering_MobileUniformLocalLights",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "MobileUniformLocalLights_DisplayName", "Enable OR Ignore MobileUniformLocalLights"),
|
||||
NSLOCTEXT("OculusXRRenderingRules",
|
||||
"MobileUniformLocalLights_Description",
|
||||
"MobileUniformLocalLights might perform better than default LightGrid shading in small number of dynamic local lights cases.\nEither Enable MobileUniformLocalLights in ProjectSettings or Ignore this rule here.\nNOTE: MobileUniformLocalLights is incompatible with GPUScene."),
|
||||
TEXT("https://developers.meta.com/horizon/documentation/unreal/po-fork-engine-perf"),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance,
|
||||
MetaQuest_All,
|
||||
false) {}
|
||||
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
class FEnableEmulatedUniformBuffersRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnableEmulatedUniformBuffersRule()
|
||||
: ISetupRule("Rendering_EnableEmulatedUniformBuffers",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnableEmulatedUniformBuffers_DisplayName", "Enable Emulated Uniform Buffers"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnableEmulatedUniformBuffers_Description", "Optimizes performance by consolidating constant buffers into a single global uniform buffer for improved shader compiler optimization.\nNOTE: EmulatedUniformBuffers is incompatible with GPUScene."),
|
||||
TEXT("https://developers.meta.com/horizon/documentation/unreal/po-fork-engine-perf"),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance,
|
||||
MetaQuest_All) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
#endif
|
||||
|
||||
class FDisableLensFlareRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FDisableLensFlareRule()
|
||||
: ISetupRule("Rendering_DisableLensFlare",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "DisableLensFlare_DisplayName", "Disable Lens Flare"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "DisableLensFlare_Description", "Lens flare can be expensive and exhibit visible artifacts in VR."),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FDisablePostProcessingRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FDisablePostProcessingRule()
|
||||
: ISetupRule("Rendering_DisablePostProcessing",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "DisablePostProcessing_DisplayName", "Disable Post Processing"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "DisablePostProcessing_Description", "Mobile HDR has performance and stability issues in VR. We strongly recommend disabling it."),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance,
|
||||
MetaQuest_All) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FDisableAmbientOcclusionRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FDisableAmbientOcclusionRule()
|
||||
: ISetupRule("Rendering_DisableAmbientOcclusion",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "DisableAmbientOcclusion_DisplayName", "Disable Ambient Occlusion"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "DisableAmbientOcclusion_Description", "Ambient occlusion has performance issues. We recommend disabling it."),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance,
|
||||
MetaQuest_All) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FEnableMultiViewRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnableMultiViewRule()
|
||||
: ISetupRule("Rendering_EnableMultiView",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnableMultiView_DisplayName", "Enable Mobile Multiveiw"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnableMultiView_Description", "Enable mobile multi-view and direct mobile multi-view to significantly reduce CPU overhead."),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance,
|
||||
MetaQuest_All) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FEnableStaticLightingRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FEnableStaticLightingRule()
|
||||
: ISetupRule("Rendering_EnableStaticLighting",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnableStaticLighting_DisplayName", "Enable Static Lighting"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "EnableStaticLighting_Description", "Static lighting should be disallowed only if project is intended to be 100% dynamically lit."),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance,
|
||||
All_Platforms) {}
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FDisableMobileShaderStaticAndCSMShadowReceiversRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FDisableMobileShaderStaticAndCSMShadowReceiversRule()
|
||||
: ISetupRule(
|
||||
"Rendering_MobileShaderStaticAndCSMShadowReceivers",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "MobileShaderStaticAndCSMShadowReceivers_DisplayName", "Disable Support Combined Static and CSM Shadowing"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "MobileShaderStaticAndCSMShadowReceivers_Description", "The project does not contain any stationary lights. Support Combined Static and CSM Shadowing can be disabled to reduce shader permutations."),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance) {}
|
||||
|
||||
virtual bool IsApplied() const override;
|
||||
virtual bool IsValid() override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
class FDisableMobileShaderAllowDistanceFieldShadowsRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FDisableMobileShaderAllowDistanceFieldShadowsRule()
|
||||
: ISetupRule("Rendering_MobileShaderAllowDistanceFieldShadows",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "MobileShaderAllowDistanceFieldShadows_DisplayName", "Disable Support Support Distance Field Shadows"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "MobileShaderAllowDistanceFieldShadows_Description", "The project does not contain any stationary lights. Support Support Distance Field Shadows can be disabled to reduce shader permutations."),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance,
|
||||
MetaQuest_All) {}
|
||||
|
||||
virtual bool IsApplied() const override;
|
||||
virtual bool IsValid() override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
#if UE_VERSION_OLDER_THAN(5, 5, 0)
|
||||
class FDisableMobileShaderAllowMovableDirectionalLightsRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FDisableMobileShaderAllowMovableDirectionalLightsRule()
|
||||
: ISetupRule("Rendering_MobileShaderAllowMovableDirectionalLights",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "MobileShaderAllowMovableDirectionalLights_DisplayName", "Disable Support Movable Directional Lights"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "MobileShaderAllowMovableDirectionalLights_Description", "The project does not contain any movable lights. Support Movable Directional Lights can be disabled to reduce shader permutations."),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance,
|
||||
MetaQuest_All) {}
|
||||
|
||||
virtual bool IsApplied() const override;
|
||||
virtual bool IsValid() override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef WITH_OCULUS_BRANCH
|
||||
class FDisableMobileGPUSceneRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FDisableMobileGPUSceneRule()
|
||||
: ISetupRule("Rendering_MobileGPUScene",
|
||||
NSLOCTEXT("OculusXRRenderingRules", "MobileGPUSceneLights_DisplayName", "Disable Support GPUScene"),
|
||||
NSLOCTEXT("OculusXRRenderingRules", "MobileGPUSceneLights_Description", "GPUScene is not compatible with MobileUniformLocalLights and EmulatedUniformBuffers."),
|
||||
ESetupRuleCategory::Rendering,
|
||||
ESetupRuleSeverity::Performance,
|
||||
MetaQuest_All) {}
|
||||
|
||||
virtual bool IsApplied() const override;
|
||||
virtual bool IsValid() override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
#endif
|
||||
|
||||
class FUseAndroidVulkanPreviewPlatform final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FUseAndroidVulkanPreviewPlatform();
|
||||
virtual bool IsApplied() const override;
|
||||
virtual bool IsValid() override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
|
||||
private:
|
||||
FPreviewPlatformInfo AndroidVulkanPreview;
|
||||
};
|
||||
|
||||
class FDisableMobileMoveableSpotlightShadowsRule final : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FDisableMobileMoveableSpotlightShadowsRule();
|
||||
virtual bool IsApplied() const override;
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) override;
|
||||
};
|
||||
|
||||
// All defined rendering rules. Add new rules to this table for them to be auto-registered
|
||||
inline TArray<SetupRulePtr> RenderingRules_Table{
|
||||
MakeShared<FUseVulkanRule>(),
|
||||
MakeShared<FUseHalfPrecisionFloatRule>(),
|
||||
MakeShared<FEnableInstancedStereoRule>(),
|
||||
MakeShared<FEnableForwardShadingRule>(),
|
||||
MakeShared<FEnablePCForwardShadingRule>(),
|
||||
MakeShared<FEnableMSAARule>(),
|
||||
MakeShared<FEnableOcclusionCullingRule>(),
|
||||
MakeShared<FEnableDynamicFoveationRule>(),
|
||||
#ifdef WITH_OCULUS_BRANCH
|
||||
MakeShared<FEnableDynamicResolutionRule>(),
|
||||
MakeShared<FEnableMobileUniformLocalLightsRule>(),
|
||||
MakeShared<FEnableEmulatedUniformBuffersRule>(),
|
||||
MakeShared<FDisableMobileGPUSceneRule>(),
|
||||
#endif
|
||||
MakeShared<FDisableLensFlareRule>(),
|
||||
MakeShared<FDisablePostProcessingRule>(),
|
||||
MakeShared<FDisableAmbientOcclusionRule>(),
|
||||
MakeShared<FEnableMultiViewRule>(),
|
||||
MakeShared<FEnableStaticLightingRule>(),
|
||||
MakeShared<FDisableMobileShaderStaticAndCSMShadowReceiversRule>(),
|
||||
MakeShared<FDisableMobileShaderAllowDistanceFieldShadowsRule>(),
|
||||
#if UE_VERSION_OLDER_THAN(5, 5, 0)
|
||||
MakeShared<FDisableMobileShaderAllowMovableDirectionalLightsRule>(),
|
||||
#endif
|
||||
MakeShared<FUseAndroidVulkanPreviewPlatform>(),
|
||||
MakeShared<FDisableMobileMoveableSpotlightShadowsRule>()
|
||||
};
|
||||
} // namespace OculusXRRenderingRules
|
||||
@@ -0,0 +1,179 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "Misc/AutomationTest.h"
|
||||
#include "OculusXRRuleProcessorSubsystem.h"
|
||||
#include "OculusXRSetupRule.h"
|
||||
#include "Rules/OculusXRAnchorsRules.h"
|
||||
#include "Rules/OculusXRCompatibilityRules.h"
|
||||
#include "Rules/OculusXRMovementRules.h"
|
||||
#include "Rules/OculusXRPassthroughRules.h"
|
||||
#include "Rules/OculusXRPluginRules.h"
|
||||
#include "Rules/OculusXRRenderingRules.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
const char* TestRule_Id = "test_id";
|
||||
const FText TestRule_DisName = FText::FromString("Test Display");
|
||||
const FText TestRule_Desc = FText::FromString("Test Desc");
|
||||
} // namespace
|
||||
|
||||
#if UE_VERSION_OLDER_THAN(5, 5, 0)
|
||||
BEGIN_DEFINE_SPEC(FOculusXRProjectSetupToolSpec, TEXT("Project Setup Tool"), EAutomationTestFlags::ProductFilter | EAutomationTestFlags::ApplicationContextMask)
|
||||
#else
|
||||
BEGIN_DEFINE_SPEC(FOculusXRProjectSetupToolSpec, TEXT("Project Setup Tool"), EAutomationTestFlags::ProductFilter | EAutomationTestFlags_ApplicationContextMask)
|
||||
#endif
|
||||
|
||||
UOculusXRRuleProcessorSubsystem* ProcessorSubsystem;
|
||||
bool bShouldRestartEditor = false;
|
||||
TSet<FName> RulesThatRequireRestart = {
|
||||
FName("Feature_AllowAlphaToneMapperPassthrough"),
|
||||
FName("Rendering_DisableAmbientOcclusion"),
|
||||
FName("Rendering_DisablePostProcessing"),
|
||||
FName("Rendering_EnableEmulatedUniformBuffers"),
|
||||
FName("Rendering_EnableForwardShading"),
|
||||
FName("Rendering_EnablePCForwardShading"),
|
||||
FName("Rendering_EnableInstancedStereo"),
|
||||
FName("Rendering_EnableMultiView"),
|
||||
FName("Rendering_EnableStaticLighting"),
|
||||
FName("Rendering_MobileShaderAllowDistanceFieldShadows"),
|
||||
FName("Rendering_MobileShaderAllowMovableDirectionalLights"),
|
||||
FName("Rendering_MobileGPUScene"),
|
||||
FName("Rendering_MobileUniformLocalLights"),
|
||||
FName("Rendering_UseHalfPrecisionFloat"),
|
||||
FName("Rendering_MobileMoveableSpotlightShadows")
|
||||
};
|
||||
|
||||
void Setup();
|
||||
END_DEFINE_SPEC(FOculusXRProjectSetupToolSpec)
|
||||
|
||||
void FOculusXRProjectSetupToolSpec::Setup()
|
||||
{
|
||||
BeforeEach([this] {
|
||||
ProcessorSubsystem = GEngine->GetEngineSubsystem<UOculusXRRuleProcessorSubsystem>();
|
||||
bShouldRestartEditor = false;
|
||||
});
|
||||
}
|
||||
|
||||
class FMockRule : public ISetupRule
|
||||
{
|
||||
public:
|
||||
FMockRule()
|
||||
: ISetupRule(TestRule_Id, TestRule_DisName, TestRule_Desc, ESetupRuleCategory::Miscellaneous, ESetupRuleSeverity::Warning)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool IsApplied() const override
|
||||
{
|
||||
return bIsApplied;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& ShouldRestartEditor) override
|
||||
{
|
||||
bIsApplied = true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool bIsApplied = false;
|
||||
};
|
||||
|
||||
void FOculusXRProjectSetupToolSpec::Define()
|
||||
{
|
||||
Describe(TEXT("Rule Processor"), [this] {
|
||||
Setup();
|
||||
|
||||
It(TEXT("Rule registered and unregistered successfully"), [this] {
|
||||
const auto RuleNum = ProcessorSubsystem->GetRules().Num();
|
||||
const SetupRulePtr mockRule = MakeShared<FMockRule>();
|
||||
TestTrue(TEXT("Rule added"), ProcessorSubsystem->RegisterRule(mockRule));
|
||||
TestEqual(TEXT("After rule is added"), ProcessorSubsystem->GetRules().Num(), RuleNum + 1);
|
||||
|
||||
TestTrue(TEXT("Rule removed"), ProcessorSubsystem->UnregisterRule(mockRule));
|
||||
TestEqual(TEXT("After rule is removed"), ProcessorSubsystem->GetRules().Num(), RuleNum);
|
||||
});
|
||||
|
||||
It(TEXT("Rule applied"), [this] {
|
||||
const SetupRulePtr mockRule = MakeShared<FMockRule>();
|
||||
|
||||
// apply rule
|
||||
TestFalse(TEXT("Rule is not applied yet"), mockRule->IsApplied());
|
||||
mockRule->Apply(bShouldRestartEditor);
|
||||
|
||||
TestTrue(TEXT("Rule applied"), mockRule->IsApplied());
|
||||
});
|
||||
|
||||
It(TEXT("Rule ignored"), [this] {
|
||||
const SetupRulePtr mockRule = MakeShared<FMockRule>();
|
||||
// ignore rule
|
||||
TestFalse(TEXT("Rule is not ignored yet"), mockRule->IsIgnored());
|
||||
mockRule->SetIgnoreRule(true);
|
||||
TestTrue(TEXT("Rule ignored"), mockRule->IsIgnored());
|
||||
});
|
||||
});
|
||||
|
||||
Describe(TEXT("Rendering rules"), [this] {
|
||||
for (auto Rule : OculusXRRenderingRules::RenderingRules_Table)
|
||||
{
|
||||
It(TEXT("Test " + Rule->GetId().ToString()), [this, Rule] {
|
||||
Rule->Apply(bShouldRestartEditor);
|
||||
TestTrue(TEXT("Rule is applied"), Rule->IsApplied());
|
||||
TestEqual(TEXT("Restart is pending"), RulesThatRequireRestart.Contains(Rule->GetId()), bShouldRestartEditor);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Describe(TEXT("Plugin rules"), [this] {
|
||||
for (auto Rule : OculusXRPluginRules::PluginRules_Table)
|
||||
{
|
||||
It(TEXT("Test " + Rule->GetId().ToString()), [this, Rule] {
|
||||
Rule->Apply(bShouldRestartEditor);
|
||||
TestTrue(TEXT("Rule is applied"), Rule->IsApplied());
|
||||
TestEqual(TEXT("Restart is pending"), RulesThatRequireRestart.Contains(Rule->GetId()), bShouldRestartEditor);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Describe(TEXT("Compatibility rules"), [this] {
|
||||
for (auto Rule : OculusXRCompatibilityRules::CompatibilityRules_Table)
|
||||
{
|
||||
It(TEXT("Test " + Rule->GetId().ToString()), [this, Rule] {
|
||||
Rule->Apply(bShouldRestartEditor);
|
||||
TestTrue(TEXT("Rule is applied"), Rule->IsApplied() || !Rule->ShowApply());
|
||||
TestEqual(TEXT("Restart is pending"), RulesThatRequireRestart.Contains(Rule->GetId()), bShouldRestartEditor);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Describe(TEXT("Anchor rules"), [this] {
|
||||
for (auto Rule : OculusXRAnchorsRules::AnchorRules_Table)
|
||||
{
|
||||
It(TEXT("Test " + Rule->GetId().ToString()), [this, Rule] {
|
||||
Rule->Apply(bShouldRestartEditor);
|
||||
TestTrue(TEXT("Rule is applied"), Rule->IsApplied());
|
||||
TestEqual(TEXT("Restart is pending"), RulesThatRequireRestart.Contains(Rule->GetId()), bShouldRestartEditor);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Describe(TEXT("Movement rules"), [this] {
|
||||
for (auto Rule : OculusXRMovementRules::MovementRules_Table)
|
||||
{
|
||||
It(TEXT("Test " + Rule->GetId().ToString()), [this, Rule] {
|
||||
Rule->Apply(bShouldRestartEditor);
|
||||
TestTrue(TEXT("Rule is applied"), Rule->IsApplied());
|
||||
TestEqual(TEXT("Restart is pending"), RulesThatRequireRestart.Contains(Rule->GetId()), bShouldRestartEditor);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Describe(TEXT("Passthrough rules"), [this] {
|
||||
for (auto Rule : OculusXRPassthroughRules::PassthroughRules_Table)
|
||||
{
|
||||
It(TEXT("Test " + Rule->GetId().ToString()), [this, Rule] {
|
||||
Rule->Apply(bShouldRestartEditor);
|
||||
TestTrue(TEXT("Rule is applied"), Rule->IsApplied());
|
||||
TestEqual(TEXT("Restart is pending"), RulesThatRequireRestart.Contains(Rule->GetId()), bShouldRestartEditor);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,124 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
#include "DetailColumnSizeData.h"
|
||||
#include "Widgets/DeclarativeSyntaxSupport.h"
|
||||
#include "Widgets/SCompoundWidget.h"
|
||||
#include "Widgets/Layout/SScrollBox.h"
|
||||
#include "OculusXRRuleProcessorSubsystem.h"
|
||||
|
||||
class STextBlock;
|
||||
class FActiveTimerHandle;
|
||||
/**
|
||||
* Slate widget for the Project Setup Tool main tab
|
||||
*/
|
||||
class SOculusXRProjectSetupToolWidget : public SCompoundWidget
|
||||
{
|
||||
SLATE_BEGIN_ARGS(SOculusXRProjectSetupToolWidget) {}
|
||||
SLATE_END_ARGS()
|
||||
|
||||
/** Construct the slate layout for the widget */
|
||||
|
||||
void Construct(const FArguments& InArgs);
|
||||
|
||||
private:
|
||||
enum class ERulesSection : uint8
|
||||
{
|
||||
Filter,
|
||||
Required,
|
||||
Recommended,
|
||||
Applied,
|
||||
Ignored
|
||||
};
|
||||
|
||||
/** Build the main layout */
|
||||
void BuildLayout(const TSharedPtr<SVerticalBox>& RootContainer);
|
||||
|
||||
/** Build the title section layout*/
|
||||
void BuildTitleSectionLayout(const TSharedPtr<SVerticalBox>& RootContainer);
|
||||
|
||||
/** Build the filter section layout */
|
||||
void BuildFilterSectionLayout(const TSharedPtr<SVerticalBox>& RootContainer, const uint32 PlatformFilterIndex);
|
||||
|
||||
/** Build a container for a rules section */
|
||||
TSharedPtr<SVerticalBox> BuildRulesContainerLayout(const TSharedPtr<SScrollBox>& RootContainer, ERulesSection Section, const FText& SectionTitle);
|
||||
|
||||
/** Build a single rules item */
|
||||
void BuildRowItemLayout(const TSharedPtr<SVerticalBox>& SectionContentContainer, ERulesSection Section, const SetupRulePtr& Rule, const uint32 PlatformFilterIndex);
|
||||
|
||||
/** Build the rules section layouts */
|
||||
void BuildRequiredRulesSectionLayout(const TSharedPtr<SScrollBox>& RootContainer);
|
||||
void BuildRecommendedRulesSectionLayout(const TSharedPtr<SScrollBox>& RootContainer);
|
||||
void BuildAppliedRulesSectionLayout(const TSharedPtr<SScrollBox>& RootContainer);
|
||||
void BuildIgnoredRulesSectionLayout(const TSharedPtr<SScrollBox>& RootContainer);
|
||||
|
||||
/** Platform filter callbacks */
|
||||
FReply OnPlatformFilterChanged(ESetupRulePlatform ItemSelected);
|
||||
|
||||
/** Button handling callbacks */
|
||||
FReply OnApplyRuleClicked(SetupRulePtr Rule);
|
||||
static bool OnApplyRuleEnabled(ERulesSection Section);
|
||||
|
||||
FReply OnReportIssueClicked();
|
||||
|
||||
FReply OnMoreInfoClicked(SetupRulePtr Rule);
|
||||
|
||||
FReply OnApplyAllRulesClicked(ERulesSection Section);
|
||||
bool OnApplyAllRulesEnabled(ERulesSection Section) const;
|
||||
|
||||
void OnIgnoreRuleClicked(SetupRulePtr Rule, ERulesSection Section);
|
||||
static bool OnIgnoreRuleEnabled(ERulesSection Section);
|
||||
|
||||
/** Rule visibility callback */
|
||||
EVisibility OnRowVisibility(ERulesSection Section, ESetupRulePlatform CurrentPlatform, SetupRulePtr Rule) const;
|
||||
|
||||
/** Expander callbacks */
|
||||
const FSlateBrush* GetHeaderExpanderImage(ERulesSection Section) const;
|
||||
static EVisibility OnHeaderExpanderVisibility(ERulesSection Section);
|
||||
FReply OnHeaderExpanderClicked(ERulesSection Section);
|
||||
|
||||
/** Restart Editor Notice Visibility */
|
||||
EVisibility OnRestartEditorNoticeVisibility() const;
|
||||
/** Restart Editor Button Clicked */
|
||||
FReply OnRestartEditorButtonClicked();
|
||||
|
||||
/** Perform refresh */
|
||||
static void Refresh();
|
||||
|
||||
/** Register/Unregister timer */
|
||||
void UpdateActiveTimer(bool Register);
|
||||
|
||||
/** Update the status of rules */
|
||||
void UpdateProjectStatus();
|
||||
|
||||
/** Update the status string */
|
||||
void UpdateProjectStatusString() const;
|
||||
|
||||
/** Root container */
|
||||
TSharedPtr<SVerticalBox> RootContainerWidget{};
|
||||
|
||||
/** Column size data */
|
||||
TSharedPtr<FDetailColumnSizeData> ColumnSizeData{};
|
||||
|
||||
/** Platform filter */
|
||||
TArray<ESetupRulePlatform> PlatformFilters{};
|
||||
uint32 CurrentPlatformFilterIndex{};
|
||||
|
||||
/** Active timer handle */
|
||||
TWeakPtr<FActiveTimerHandle> ActiveTimerHandle;
|
||||
|
||||
/** Restart pending after rule application */
|
||||
bool bShowButtonToRestart = false;
|
||||
|
||||
/** Expanded/collapsed state for each section */
|
||||
TArray<bool> BIsSectionExpanded{ true, true, true, true, true };
|
||||
|
||||
/** Status string */
|
||||
TSharedPtr<STextBlock> ProjectStatusWidget;
|
||||
|
||||
/** Rules status */
|
||||
UOculusXRRuleProcessorSubsystem::RuleStatus RuleStatus{};
|
||||
};
|
||||
@@ -0,0 +1,334 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRProjectTutorialWidget.h"
|
||||
|
||||
#include "OculusXRProjectSetupToolModule.h"
|
||||
#include "OculusXRPSTEvents.h"
|
||||
#include "OculusXRPSTSettings.h"
|
||||
#include "OculusXRTelemetry.h"
|
||||
#include "Widgets/SOverlay.h"
|
||||
#include "Widgets/Images/SImage.h"
|
||||
#include "Widgets/Input/SButton.h"
|
||||
#include "Widgets/Layout/SBox.h"
|
||||
#include "Widgets/Layout/SScrollBox.h"
|
||||
#include "Widgets/Text/STextBlock.h"
|
||||
#include "Styling/SlateStyle.h"
|
||||
#include "Styling/SlateStyleMacros.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRProjectTutorialWidget"
|
||||
|
||||
static constexpr int32 NumTutorialPages = 3;
|
||||
|
||||
/**
|
||||
* Construct the layout for the project setup tool tab
|
||||
*
|
||||
* @param InArgs [in] the arguments associated with this tool
|
||||
*/
|
||||
void SOculusXRTutorialWindow::Construct(const FArguments& InArgs)
|
||||
{
|
||||
// Construct the layout
|
||||
RootContainerWidget = SNew(SVerticalBox);
|
||||
|
||||
BuildGuidedTutorialLayout(RootContainerWidget);
|
||||
|
||||
ChildSlot
|
||||
[SNew(SBox)
|
||||
.WidthOverride(1080)
|
||||
.HeightOverride(600)
|
||||
[RootContainerWidget.ToSharedRef()]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the guided tutorial layout
|
||||
*/
|
||||
void SOculusXRTutorialWindow::BuildGuidedTutorialLayout(const TSharedPtr<SVerticalBox>& RootContainer)
|
||||
{
|
||||
// Construct the row of buttons
|
||||
const TSharedPtr<SHorizontalBox> PreviousNextWidget = SNew(SHorizontalBox);
|
||||
|
||||
PreviousNextWidget->AddSlot()
|
||||
.AutoWidth()
|
||||
[SNew(SButton)
|
||||
.OnClicked(this, &SOculusXRTutorialWindow::OnPreviousClicked)
|
||||
.IsEnabled(this, &SOculusXRTutorialWindow::OnPreviousEnabled)
|
||||
.Text(LOCTEXT("Previous", "Previous"))];
|
||||
|
||||
PreviousNextWidget->AddSlot()
|
||||
.AutoWidth()
|
||||
.Padding(10, 0, 0, 0)
|
||||
[SNew(SButton)
|
||||
.OnClicked(this, &SOculusXRTutorialWindow::OnNextClicked)
|
||||
.IsEnabled(this, &SOculusXRTutorialWindow::OnNextEnabled)
|
||||
.Text(LOCTEXT("Next", "Next"))];
|
||||
|
||||
const TSharedPtr<SHorizontalBox> SkipWidget = SNew(SHorizontalBox);
|
||||
|
||||
SkipWidget->AddSlot()
|
||||
.AutoWidth()
|
||||
[SNew(SButton)
|
||||
.OnClicked(this, &SOculusXRTutorialWindow::OnSkipClicked)
|
||||
.ButtonStyle(&FAppStyle::Get().GetWidgetStyle<FButtonStyle>("PrimaryButton"))
|
||||
.Text_Lambda([this]() {
|
||||
if (GuidedTutorialPageIndex == NumTutorialPages - 1)
|
||||
{
|
||||
return LOCTEXT("Close", "Close");
|
||||
}
|
||||
return LOCTEXT("Skip", "Skip");
|
||||
})];
|
||||
|
||||
const TSharedPtr<SHorizontalBox> PaginationWidget = SNew(SHorizontalBox);
|
||||
|
||||
for (int i = 0; i < NumTutorialPages; ++i)
|
||||
{
|
||||
PaginationWidget->AddSlot()
|
||||
.Padding(4.0f, 0.0f)
|
||||
[SNew(SImage)
|
||||
.DesiredSizeOverride(FVector2D(8.0f, 8.0f))
|
||||
.Image_Raw(this, &SOculusXRTutorialWindow::GetPaginationImageForIndex, i)];
|
||||
}
|
||||
|
||||
const TSharedPtr<SHorizontalBox> ButtonsWidget = SNew(SHorizontalBox);
|
||||
|
||||
ButtonsWidget->AddSlot()
|
||||
.HAlign(HAlign_Left)
|
||||
[PreviousNextWidget.ToSharedRef()];
|
||||
|
||||
ButtonsWidget->AddSlot()
|
||||
.Padding(0.0f, 7.0f)
|
||||
.HAlign(HAlign_Center)
|
||||
[PaginationWidget.ToSharedRef()];
|
||||
|
||||
ButtonsWidget->AddSlot()
|
||||
.HAlign(HAlign_Right)
|
||||
[SkipWidget.ToSharedRef()];
|
||||
|
||||
// Construct the text widget
|
||||
const TSharedPtr<SVerticalBox> TextWidget = SNew(SVerticalBox);
|
||||
AddTutorialPage1(TextWidget);
|
||||
AddTutorialPage2(TextWidget);
|
||||
AddTutorialPage3(TextWidget);
|
||||
|
||||
// Construct the image panel
|
||||
const TSharedPtr<SOverlay> ImagePanel = SNew(SOverlay);
|
||||
|
||||
ImagePanel->AddSlot()
|
||||
[SNew(SImage)
|
||||
.Image(FOculusXRProjectSetupToolModule::GetSlateStyle()->GetBrush("ProjectSetupTool.MetaQuestBackground"))];
|
||||
|
||||
// Construct the instruction panel
|
||||
const TSharedPtr<SOverlay> InstructionsPanel = SNew(SOverlay);
|
||||
|
||||
InstructionsPanel->AddSlot()
|
||||
[SNew(SVerticalBox)
|
||||
+ SVerticalBox::Slot()
|
||||
.Padding(40)
|
||||
.VAlign(VAlign_Center)
|
||||
[TextWidget.ToSharedRef()]];
|
||||
|
||||
InstructionsPanel->AddSlot()
|
||||
[SNew(SVerticalBox)
|
||||
+ SVerticalBox::Slot()
|
||||
.Padding(40, 20)
|
||||
.VAlign(VAlign_Bottom)
|
||||
[ButtonsWidget.ToSharedRef()]];
|
||||
|
||||
// Construct the main panel
|
||||
|
||||
const TSharedPtr<SHorizontalBox> MainPanel = SNew(SHorizontalBox);
|
||||
|
||||
MainPanel->AddSlot()
|
||||
[ImagePanel.ToSharedRef()];
|
||||
|
||||
MainPanel->AddSlot()
|
||||
[InstructionsPanel.ToSharedRef()];
|
||||
|
||||
// Add to the root container
|
||||
|
||||
RootContainer->AddSlot()
|
||||
[MainPanel.ToSharedRef()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Previous button clicked
|
||||
*/
|
||||
FReply SOculusXRTutorialWindow::OnPreviousClicked()
|
||||
{
|
||||
--GuidedTutorialPageIndex;
|
||||
|
||||
if (GuidedTutorialPageIndex < 0)
|
||||
{
|
||||
GuidedTutorialPageIndex = 0;
|
||||
}
|
||||
|
||||
const OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FProjectSetupToolPrev> Previous;
|
||||
const auto& Annotated = Previous
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::Value,
|
||||
TCHAR_TO_ANSI(*FString::FromInt(GuidedTutorialPageIndex)));
|
||||
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Previous button enabled
|
||||
*/
|
||||
bool SOculusXRTutorialWindow::OnPreviousEnabled() const
|
||||
{
|
||||
return GuidedTutorialPageIndex > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Next button clicked
|
||||
*/
|
||||
FReply SOculusXRTutorialWindow::OnNextClicked()
|
||||
{
|
||||
++GuidedTutorialPageIndex;
|
||||
|
||||
if (GuidedTutorialPageIndex == NumTutorialPages - 1)
|
||||
{
|
||||
GetMutableDefault<UOculusXRPSTSettings>()->bGuidedTutorialComplete = true;
|
||||
GetMutableDefault<UOculusXRPSTSettings>()->TryUpdateDefaultConfigFile();
|
||||
}
|
||||
const OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FProjectSetupToolNext> NextEvent;
|
||||
const auto& Annotated = NextEvent
|
||||
.AddAnnotation(OculusXRTelemetry::Annotations::Value,
|
||||
TCHAR_TO_ANSI(*FString::FromInt(GuidedTutorialPageIndex)));
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Next button enabled
|
||||
*/
|
||||
bool SOculusXRTutorialWindow::OnNextEnabled() const
|
||||
{
|
||||
return GuidedTutorialPageIndex < NumTutorialPages - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip button clicked
|
||||
*/
|
||||
FReply SOculusXRTutorialWindow::OnSkipClicked() const
|
||||
{
|
||||
FSlateApplication::Get().FindWidgetWindow(AsShared())->RequestDestroyWindow();
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the image to use for the pagination indicator
|
||||
*/
|
||||
const FSlateBrush* SOculusXRTutorialWindow::GetPaginationImageForIndex(int32 PageIndex) const
|
||||
{
|
||||
if (PageIndex == GuidedTutorialPageIndex)
|
||||
{
|
||||
return FOculusXRProjectSetupToolModule::GetSlateStyle()->GetBrush("ProjectSetupTool.WhiteDot");
|
||||
}
|
||||
|
||||
return FOculusXRProjectSetupToolModule::GetSlateStyle()->GetBrush("ProjectSetupTool.GreyDot");
|
||||
}
|
||||
|
||||
void SOculusXRTutorialWindow::AddTutorialPage1(const TSharedPtr<SVerticalBox>& TextWidget) const
|
||||
{
|
||||
TextWidget->AddSlot()
|
||||
.AutoHeight()
|
||||
[SNew(STextBlock)
|
||||
.AutoWrapText(true)
|
||||
.Font(DEFAULT_FONT("Regular", 24))
|
||||
.Visibility_Lambda([this]() { return GuidedTutorialPageIndex == 0 ? EVisibility::Visible : EVisibility::Collapsed; })
|
||||
.Text(LOCTEXT("Title1", "Welcome to the Meta XR Project Setup Tool"))];
|
||||
|
||||
TextWidget->AddSlot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 20, 0, 0)
|
||||
[SNew(STextBlock)
|
||||
.AutoWrapText(true)
|
||||
.Font(DEFAULT_FONT("Regular", 12))
|
||||
.Visibility_Lambda([this]() { return GuidedTutorialPageIndex == 0 ? EVisibility::Visible : EVisibility::Collapsed; })
|
||||
.Text(LOCTEXT("Body1",
|
||||
"The Unreal Project Setup Tool can help you quickly configure projects using the Meta XR Plugin. This tool guides you through the necessary steps so you can start developing faster. The Unreal Project Setup Tool tests a registry of rules called Configuration Tasks. We provide default rules to make your project Quest Ready."))];
|
||||
}
|
||||
|
||||
void SOculusXRTutorialWindow::AddTutorialPage2(const TSharedPtr<SVerticalBox>& TextWidget) const
|
||||
{
|
||||
|
||||
TextWidget->AddSlot()
|
||||
.AutoHeight()
|
||||
[SNew(STextBlock)
|
||||
.AutoWrapText(true)
|
||||
.Font(DEFAULT_FONT("Regular", 24))
|
||||
.Visibility_Lambda([this]() { return GuidedTutorialPageIndex == 1 ? EVisibility::Visible : EVisibility::Collapsed; })
|
||||
.Text(LOCTEXT("Title2", "Here are the key things to know about Unreal Project Setup Tool"))];
|
||||
|
||||
TextWidget->AddSlot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 10, 0, 0)
|
||||
[SNew(STextBlock)
|
||||
.AutoWrapText(true)
|
||||
.Font(DEFAULT_FONT("Bold", 12))
|
||||
.Visibility_Lambda([this]() { return GuidedTutorialPageIndex == 1 ? EVisibility::Visible : EVisibility::Collapsed; })
|
||||
.Text(LOCTEXT("Body2.1", "Actions"))];
|
||||
TextWidget->AddSlot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 3, 0, 0)
|
||||
[SNew(STextBlock)
|
||||
.AutoWrapText(true)
|
||||
.Font(DEFAULT_FONT("Regular", 12))
|
||||
.Visibility_Lambda([this]() { return GuidedTutorialPageIndex == 1 ? EVisibility::Visible : EVisibility::Collapsed; })
|
||||
.Text(LOCTEXT("Body2.2", "A Configuration Task is regularly checked for its validation. You can interact directly with a Task in the following ways."))];
|
||||
|
||||
TextWidget->AddSlot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 3, 0, 0)
|
||||
[SNew(STextBlock)
|
||||
.AutoWrapText(true)
|
||||
.Font(DEFAULT_FONT("Bold", 12))
|
||||
.Visibility_Lambda([this]() { return GuidedTutorialPageIndex == 1 ? EVisibility::Visible : EVisibility::Collapsed; })
|
||||
.Text(LOCTEXT("Body2.3", "Fix/Apply"))];
|
||||
|
||||
TextWidget->AddSlot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 3, 0, 0)
|
||||
[SNew(STextBlock)
|
||||
.AutoWrapText(true)
|
||||
.Font(DEFAULT_FONT("Regular", 12))
|
||||
.Visibility_Lambda([this]() { return GuidedTutorialPageIndex == 1 ? EVisibility::Visible : EVisibility::Collapsed; })
|
||||
.Text(LOCTEXT("Body2.3", "Manually call the fix delegate for this Task in order to resolve the issue. This action is only available for tasks that are not already validated."))];
|
||||
|
||||
TextWidget->AddSlot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 3, 0, 0)
|
||||
[SNew(STextBlock)
|
||||
.AutoWrapText(true)
|
||||
.Font(DEFAULT_FONT("Bold", 12))
|
||||
.Visibility_Lambda([this]() { return GuidedTutorialPageIndex == 1 ? EVisibility::Visible : EVisibility::Collapsed; })
|
||||
.Text(LOCTEXT("Body2.4", "Ignore / Unignore"))];
|
||||
|
||||
TextWidget->AddSlot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 3, 0, 0)
|
||||
[SNew(STextBlock)
|
||||
.AutoWrapText(true)
|
||||
.Font(DEFAULT_FONT("Regular", 12))
|
||||
.Visibility_Lambda([this]() { return GuidedTutorialPageIndex == 1 ? EVisibility::Visible : EVisibility::Collapsed; })
|
||||
.Text(LOCTEXT("Body2.5", "This moves the task to another category that will get ignored for both checks and fixes. This gives the control back to developers who may not want to be forced to follow some guidelines or even requirements in some specific cases."))];
|
||||
}
|
||||
|
||||
void SOculusXRTutorialWindow::AddTutorialPage3(const TSharedPtr<SVerticalBox>& TextWidget) const
|
||||
{
|
||||
TextWidget->AddSlot()
|
||||
.AutoHeight()
|
||||
[SNew(STextBlock)
|
||||
.AutoWrapText(true)
|
||||
.Font(DEFAULT_FONT("Bold", 24))
|
||||
.Visibility_Lambda([this]() { return GuidedTutorialPageIndex == 2 ? EVisibility::Visible : EVisibility::Collapsed; })
|
||||
.Text(LOCTEXT("Title3", "You’re good to go! Start developing faster with Unreal Project Setup Tool"))];
|
||||
|
||||
TextWidget->AddSlot()
|
||||
.AutoHeight()
|
||||
.Padding(0, 20, 0, 0)
|
||||
[SNew(STextBlock)
|
||||
.AutoWrapText(true)
|
||||
.Font(DEFAULT_FONT("Regular", 12))
|
||||
.Visibility_Lambda([this]() { return GuidedTutorialPageIndex == 2 ? EVisibility::Visible : EVisibility::Collapsed; })
|
||||
.Text(LOCTEXT("Body3", "You can check Unreal Project Setup Tool from the Tools menu bar, Meta XR Plugin Settings Page or from the Meta icon on the bottom bar. The tool proactively checks for configuration changes."))];
|
||||
}
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
#include "Widgets/DeclarativeSyntaxSupport.h"
|
||||
#include "Widgets/SCompoundWidget.h"
|
||||
#include "Widgets/Layout/SScrollBox.h"
|
||||
|
||||
class FActiveTimerHandle;
|
||||
/**
|
||||
* Slate widget for the tutorial
|
||||
*/
|
||||
class SOculusXRTutorialWindow : public SCompoundWidget
|
||||
{
|
||||
SLATE_BEGIN_ARGS(SOculusXRTutorialWindow) {}
|
||||
SLATE_END_ARGS()
|
||||
|
||||
/** Construct the slate layout for the widget */
|
||||
void Construct(const FArguments& InArgs);
|
||||
|
||||
private:
|
||||
/** Build the guided tutorial layout */
|
||||
void BuildGuidedTutorialLayout(const TSharedPtr<SVerticalBox>& RootContainer);
|
||||
|
||||
FReply OnPreviousClicked();
|
||||
bool OnPreviousEnabled() const;
|
||||
|
||||
FReply OnNextClicked();
|
||||
bool OnNextEnabled() const;
|
||||
|
||||
FReply OnSkipClicked() const;
|
||||
|
||||
const FSlateBrush* GetPaginationImageForIndex(int32 PageIndex) const;
|
||||
|
||||
void AddTutorialPage1(const TSharedPtr<SVerticalBox>& TextWidget) const;
|
||||
void AddTutorialPage2(const TSharedPtr<SVerticalBox>& TextWidget) const;
|
||||
void AddTutorialPage3(const TSharedPtr<SVerticalBox>& TextWidget) const;
|
||||
|
||||
/** Root container */
|
||||
TSharedPtr<SVerticalBox> RootContainerWidget{};
|
||||
|
||||
/** Current tutorial page */
|
||||
int32 GuidedTutorialPageIndex = 0;
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRStatusBarWidget.h"
|
||||
|
||||
#include "OculusXRProjectSetupToolModule.h"
|
||||
#include "OculusXRPSTSettings.h"
|
||||
#include "OculusXRRuleProcessorSubsystem.h"
|
||||
#include "Styling/AppStyle.h"
|
||||
#include "Styling/SlateStyle.h"
|
||||
#include "Widgets/SBoxPanel.h"
|
||||
#include "Widgets/SOverlay.h"
|
||||
#include "Widgets/Images/SImage.h"
|
||||
#include "Widgets/Input/SButton.h"
|
||||
#include "Engine/Engine.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRStatusBarWidget"
|
||||
|
||||
/**
|
||||
* Construct the layout for the status bar widget
|
||||
*
|
||||
* @param InArgs [in] the arguments associated with this tool
|
||||
*/
|
||||
void SOculusXRStatusBarWidget::Construct(const FArguments& InArgs)
|
||||
{
|
||||
ChildSlot
|
||||
[SNew(SHorizontalBox)
|
||||
+ SHorizontalBox::Slot()
|
||||
[SNew(SButton)
|
||||
.HAlign(HAlign_Center)
|
||||
.VAlign(VAlign_Center)
|
||||
.ButtonStyle(FAppStyle::Get(), "SimpleButton")
|
||||
.ToolTipText(LOCTEXT("StatusBarWidget_Tooltip", "Launch Meta XR Project Setup tool"))
|
||||
.OnClicked_Lambda([]() -> FReply {
|
||||
IOculusXRProjectSetupToolModule::Get().ShowProjectSetupTool("Toolbar");
|
||||
return FReply::Handled();
|
||||
})
|
||||
.ContentPadding(0)
|
||||
[SNew(SOverlay)
|
||||
+ SOverlay::Slot()
|
||||
[SNew(SHorizontalBox)
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
[SNew(SImage)
|
||||
.Image(FOculusXRProjectSetupToolModule::GetSlateStyle()->GetBrush("ProjectSetupTool.MetaLogo"))
|
||||
.DesiredSizeOverride(FVector2D(22.0f, 22.0f))]]
|
||||
+ SOverlay::Slot()
|
||||
[SNew(SHorizontalBox)
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(16, 4, -2, 10)
|
||||
[SNew(SImage)
|
||||
.Image_Static(SOculusXRStatusBarWidget::GetDotImage)
|
||||
.DesiredSizeOverride(FVector2D(8.0f, 8.0f))]]]]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the correct image to use
|
||||
*/
|
||||
const FSlateBrush* SOculusXRStatusBarWidget::GetDotImage()
|
||||
{
|
||||
const UOculusXRRuleProcessorSubsystem* RuleProcessorSubsystem = GEngine->GetEngineSubsystem<UOculusXRRuleProcessorSubsystem>();
|
||||
|
||||
if (RuleProcessorSubsystem == nullptr)
|
||||
{
|
||||
return FOculusXRProjectSetupToolModule::GetSlateStyle()->GetBrush("ProjectSetupTool.GreenDot");
|
||||
}
|
||||
|
||||
auto Platform = GetDefault<UOculusXRPSTSettings>()->CurrentPlatform;
|
||||
|
||||
const auto& RuleStatus = RuleProcessorSubsystem->UnAppliedRulesStatus(static_cast<ESetupRulePlatform>(Platform));
|
||||
|
||||
if (RuleStatus.PendingRequiredRulesCount > 0)
|
||||
{
|
||||
return FOculusXRProjectSetupToolModule::GetSlateStyle()->GetBrush("ProjectSetupTool.RedDot");
|
||||
}
|
||||
|
||||
if (RuleStatus.PendingRecommendedRulesCount > 0)
|
||||
{
|
||||
return FOculusXRProjectSetupToolModule::GetSlateStyle()->GetBrush("ProjectSetupTool.YellowDot");
|
||||
}
|
||||
|
||||
return FOculusXRProjectSetupToolModule::GetSlateStyle()->GetBrush("ProjectSetupTool.GreenDot");
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
#include "Widgets/DeclarativeSyntaxSupport.h"
|
||||
#include "Widgets/SCompoundWidget.h"
|
||||
|
||||
/**
|
||||
* Slate widget for the widget used to show any outstanding issues in the status bar
|
||||
*/
|
||||
class SOculusXRStatusBarWidget : public SCompoundWidget
|
||||
{
|
||||
SLATE_BEGIN_ARGS(SOculusXRStatusBarWidget) {}
|
||||
SLATE_END_ARGS()
|
||||
|
||||
/** Construct the slate layout for the widget */
|
||||
|
||||
void Construct(const FArguments& InArgs);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Determine the correct image to use
|
||||
*/
|
||||
static const FSlateBrush* GetDotImage();
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// IOculusXRProjectSetupToolModule
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The public interface to this module.
|
||||
*/
|
||||
class IOculusXRProjectSetupToolModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Singleton-like access to this module's interface. This is just for convenience!
|
||||
* Beware of calling this during the shutdown phase, though. Your module might have been unloaded already.
|
||||
*
|
||||
* @return Returns singleton instance, loading the module on demand if needed
|
||||
*/
|
||||
static inline IOculusXRProjectSetupToolModule& Get()
|
||||
{
|
||||
return FModuleManager::LoadModuleChecked<IOculusXRProjectSetupToolModule>("OculusXRProjectSetupTool");
|
||||
}
|
||||
|
||||
/** Show the project setup tool window */
|
||||
virtual void ShowProjectSetupTool(const FString& Origin) = 0;
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "OculusXRSetupRule.h"
|
||||
#include "OculusXRPSTSettings.generated.h"
|
||||
|
||||
/**
|
||||
* Meta XR Project Setup tool Settings
|
||||
*/
|
||||
UCLASS(config = EditorPerProjectUserSettings)
|
||||
class OCULUSXRPROJECTSETUPTOOL_API UOculusXRPSTSettings : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
/**
|
||||
* @brief Ignored rules by developer
|
||||
*/
|
||||
UPROPERTY(config)
|
||||
TSet<FName> IgnoredRules = {};
|
||||
|
||||
/**
|
||||
* @brief Selected platform for development
|
||||
*/
|
||||
UPROPERTY(config)
|
||||
uint32 CurrentPlatform = static_cast<uint32>(MetaQuest_All);
|
||||
|
||||
/**
|
||||
* @brief If tools should periodically check if list of rules and rules' status
|
||||
*/
|
||||
UPROPERTY(config, EditAnywhere, Category = MetaXR)
|
||||
bool bBackGroundChecks = true;
|
||||
|
||||
/**
|
||||
* @brief If build should fail if critical rule is not applied
|
||||
*/
|
||||
UPROPERTY(config, EditAnywhere, Category = MetaXR)
|
||||
bool bStopBuildOnUnAppliedCriticalItems = false;
|
||||
|
||||
/**
|
||||
* @brief If guided tutorial has been completed/skipped
|
||||
*/
|
||||
UPROPERTY(config, EditAnywhere, Category = MetaXR)
|
||||
bool bGuidedTutorialComplete = false;
|
||||
|
||||
/**
|
||||
* @brief If guided tutorial showed
|
||||
*/
|
||||
UPROPERTY(config, EditAnywhere, Category = MetaXR)
|
||||
bool bShowGuidedTutorial = true;
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "OculusXRSetupRule.h"
|
||||
#include "Developer/LauncherServices/Public/ILauncher.h"
|
||||
#include "Subsystems/EngineSubsystem.h"
|
||||
#include "OculusXRRuleProcessorSubsystem.generated.h"
|
||||
|
||||
class ULightComponentBase;
|
||||
|
||||
/**
|
||||
* The rule processor handles registration and querying of rules
|
||||
*/
|
||||
UCLASS()
|
||||
class OCULUSXRPROJECTSETUPTOOL_API UOculusXRRuleProcessorSubsystem final : public UEngineSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
struct RuleStatus
|
||||
{
|
||||
unsigned PendingRequiredRulesCount = 0;
|
||||
unsigned PendingRecommendedRulesCount = 0;
|
||||
};
|
||||
/**
|
||||
* Initialize the subsystem. USubsystem override
|
||||
*/
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
|
||||
/**
|
||||
* De-initializes the subsystem. USubsystem override
|
||||
*/
|
||||
virtual void Deinitialize() override;
|
||||
|
||||
/**
|
||||
* Register a rule
|
||||
*
|
||||
* @return true if successfully registered
|
||||
*/
|
||||
bool RegisterRule(const SetupRulePtr& Rule);
|
||||
|
||||
/**
|
||||
* Unregister a rule
|
||||
*
|
||||
* @return true if successfully unregistered
|
||||
*/
|
||||
bool UnregisterRule(const SetupRulePtr& Rule);
|
||||
|
||||
/**
|
||||
* Unregister all rules
|
||||
*/
|
||||
void UnregisterAllRules();
|
||||
|
||||
/**
|
||||
* Fetch all rules
|
||||
*/
|
||||
const TSet<SetupRulePtr, FSetupRuleKeyFunc>& GetRules() const;
|
||||
|
||||
/**
|
||||
* Fetch rule with given `Id`
|
||||
*/
|
||||
SetupRulePtr GetRule(const FName& Id) const;
|
||||
|
||||
/**
|
||||
* Returns if there are dynamic lights in project
|
||||
*/
|
||||
bool DynamicLightsExistInProject() const;
|
||||
|
||||
void SendSummaryEvent();
|
||||
|
||||
void SendSummaryEvent(ESetupRulePlatform Platform) const;
|
||||
/**
|
||||
* Refresh state
|
||||
*/
|
||||
void Refresh();
|
||||
|
||||
/**
|
||||
* Returns number of not applied critical and recommended rules
|
||||
*/
|
||||
RuleStatus UnAppliedRulesStatus(ESetupRulePlatform Platform) const;
|
||||
|
||||
private:
|
||||
void PopulateDynamicLights();
|
||||
void RegisterRules(const TArray<SetupRulePtr>& Rules);
|
||||
|
||||
//** A set containing all the registered rules
|
||||
TSet<SetupRulePtr, FSetupRuleKeyFunc> Rules = {};
|
||||
|
||||
// Dynamic lights in project
|
||||
TMap<FString, TWeakObjectPtr<ULightComponentBase>> DynamicLights;
|
||||
|
||||
// Launcher handles
|
||||
FDelegateHandle LauncherCallbackHandle;
|
||||
void OnLauncherCreated(ILauncherRef Launcher);
|
||||
void OnLauncherWorkerStarted(ILauncherWorkerPtr LauncherWorker, ILauncherProfileRef Profile);
|
||||
|
||||
void OnPIEEnded(bool bIsSimulating);
|
||||
|
||||
TArray<SetupRulePtr> UnAppliedRulesForPlatform(ESetupRulePlatform Platform, const TSet<ESetupRuleSeverity>& Severities) const;
|
||||
};
|
||||
@@ -0,0 +1,166 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
#include "OculusXRSetupRule.generated.h"
|
||||
|
||||
/**
|
||||
* Rule categories
|
||||
*/
|
||||
enum class ESetupRuleCategory : uint8
|
||||
{
|
||||
Compatibility = 0,
|
||||
Rendering = 1,
|
||||
Quality = 2,
|
||||
Physics = 3,
|
||||
Plugins = 4,
|
||||
Features = 5,
|
||||
Miscellaneous = 6
|
||||
};
|
||||
|
||||
/**
|
||||
* Rule severities
|
||||
*/
|
||||
enum class ESetupRuleSeverity : uint8
|
||||
{
|
||||
Warning = 0,
|
||||
Performance = 1,
|
||||
Critical = 2
|
||||
};
|
||||
|
||||
/**
|
||||
* Rule platforms
|
||||
*/
|
||||
|
||||
UENUM()
|
||||
enum class ESetupRulePlatform : uint32
|
||||
{
|
||||
// None
|
||||
None = 0,
|
||||
// Link
|
||||
MetaLink = 0x1 UMETA(DisplayName = "Link"),
|
||||
|
||||
// Quest
|
||||
MetaQuest_2 = 0x2 UMETA(DisplayName = "Quest 2"),
|
||||
MetaQuest_3 = 0x4 UMETA(DisplayName = "Quest 3"),
|
||||
MetaQuest_Pro = 0x8 UMETA(DisplayName = "Quest Pro")
|
||||
};
|
||||
|
||||
ENUM_CLASS_FLAGS(ESetupRulePlatform)
|
||||
|
||||
static constexpr ESetupRulePlatform MetaQuest_All = ESetupRulePlatform::MetaQuest_2 | ESetupRulePlatform::MetaQuest_3 | ESetupRulePlatform::MetaQuest_Pro;
|
||||
|
||||
static constexpr ESetupRulePlatform All_Platforms = MetaQuest_All | ESetupRulePlatform::MetaLink;
|
||||
|
||||
class OCULUSXRPROJECTSETUPTOOL_API ISetupRule
|
||||
{
|
||||
public:
|
||||
ISetupRule(
|
||||
const FName& InId,
|
||||
const FText& InDisplayName,
|
||||
const FText& InDescription,
|
||||
const ESetupRuleCategory InCategory,
|
||||
const ESetupRuleSeverity InSeverity,
|
||||
const ESetupRulePlatform InPlatform = All_Platforms,
|
||||
const bool InShowApply = true);
|
||||
ISetupRule(
|
||||
const FName& InId,
|
||||
const FText& InDisplayName,
|
||||
const FText& InDescription,
|
||||
const FString& InInfoUrl,
|
||||
const ESetupRuleCategory InCategory,
|
||||
const ESetupRuleSeverity InSeverity,
|
||||
const ESetupRulePlatform InPlatform = All_Platforms,
|
||||
const bool InShowApply = true);
|
||||
virtual ~ISetupRule() = default;
|
||||
virtual bool IsApplied() const = 0;
|
||||
|
||||
// Returns true if rule is valid. For example, Rule that checks if passthrough enabled is checked and can be applied only if PassthroughComponent is added.
|
||||
virtual bool IsValid();
|
||||
|
||||
bool IsIgnored() const;
|
||||
void SetIgnoreRule(bool bIgnore, bool bSendMetrics = true);
|
||||
|
||||
const FName& GetId() const;
|
||||
FText GetDisplayName() const;
|
||||
FText GetDescription() const;
|
||||
FString GetInfoUrl() const;
|
||||
|
||||
ESetupRuleCategory GetCategory() const;
|
||||
ESetupRuleSeverity GetSeverity() const;
|
||||
ESetupRulePlatform GetPlatform() const;
|
||||
|
||||
bool ShowApply() const;
|
||||
|
||||
void Apply(bool& OutShouldRestartEditor);
|
||||
|
||||
protected:
|
||||
virtual void ApplyImpl(bool& OutShouldRestartEditor) = 0;
|
||||
|
||||
private:
|
||||
/** Id for the rule */
|
||||
FName Id;
|
||||
|
||||
/** Display name of the rule */
|
||||
FText DisplayName;
|
||||
|
||||
/** Description of the rule */
|
||||
FText Description;
|
||||
|
||||
/** Url to launch for help with rule */
|
||||
FString InfoUrl;
|
||||
|
||||
/** Category of the rule */
|
||||
ESetupRuleCategory Category;
|
||||
|
||||
/** Severity of the rule */
|
||||
ESetupRuleSeverity Severity;
|
||||
|
||||
/** Platforms the rule applies to */
|
||||
ESetupRulePlatform Platform;
|
||||
|
||||
/** Is rule ignored */
|
||||
bool bIsIgnored = false;
|
||||
|
||||
/** Show apply button */
|
||||
bool bShowApply = true;
|
||||
};
|
||||
|
||||
typedef TSharedPtr<ISetupRule, ESPMode::ThreadSafe> SetupRulePtr;
|
||||
|
||||
/**
|
||||
* Setup rule symbol database hash.
|
||||
*/
|
||||
struct FSetupRuleKeyFunc
|
||||
{
|
||||
enum
|
||||
{
|
||||
bAllowDuplicateKeys = 0
|
||||
};
|
||||
typedef TCallTraits<FName>::ParamType KeyInitType;
|
||||
typedef TCallTraits<SetupRulePtr>::ParamType ElementInitType;
|
||||
|
||||
/**
|
||||
* @return The key used to index the given element.
|
||||
*/
|
||||
static FORCEINLINE KeyInitType GetSetKey(ElementInitType Element)
|
||||
{
|
||||
return Element->GetId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if the keys match.
|
||||
*/
|
||||
static FORCEINLINE bool Matches(KeyInitType A, KeyInitType B)
|
||||
{
|
||||
return A == B;
|
||||
}
|
||||
|
||||
/** Calculates a hash index for a key. */
|
||||
static FORCEINLINE uint32 GetKeyHash(KeyInitType Key)
|
||||
{
|
||||
return GetTypeHash(Key);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user