Config for building for Quest
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
// 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>;
|
||||
} // 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,139 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
#include "CoreMinimal.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()); \
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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,400 @@
|
||||
// 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"
|
||||
|
||||
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)));
|
||||
|
||||
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->SaveConfig();
|
||||
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,62 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "IOculusXRProjectSetupModule.h"
|
||||
|
||||
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,292 @@
|
||||
// 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"
|
||||
|
||||
/**
|
||||
* 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,96 @@
|
||||
// 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)
|
||||
: Id(InId), DisplayName(InDisplayName), Description(InDescription), Category(InCategory), Severity(InSeverity), Platform(InPlatform)
|
||||
{
|
||||
}
|
||||
|
||||
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>()->SaveConfig();
|
||||
}
|
||||
|
||||
const FName& ISetupRule::GetId() const
|
||||
{
|
||||
return Id;
|
||||
}
|
||||
FText ISetupRule::GetDisplayName() const
|
||||
{
|
||||
return DisplayName;
|
||||
}
|
||||
FText ISetupRule::GetDescription() const
|
||||
{
|
||||
return Description;
|
||||
}
|
||||
ESetupRuleCategory ISetupRule::GetCategory() const
|
||||
{
|
||||
return Category;
|
||||
}
|
||||
ESetupRuleSeverity ISetupRule::GetSeverity() const
|
||||
{
|
||||
return Severity;
|
||||
}
|
||||
|
||||
ESetupRulePlatform ISetupRule::GetPlatform() const
|
||||
{
|
||||
return Platform;
|
||||
}
|
||||
@@ -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,187 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRCompatibilityRules.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "AndroidRuntimeSettings.h"
|
||||
#include "GeneralProjectSettings.h"
|
||||
#include "OculusXRRuleProcessorSubsystem.h"
|
||||
#include "GameFramework/InputSettings.h"
|
||||
#include "OculusXRHMDRuntimeSettings.h"
|
||||
#include "OculusXRPSTUtils.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OculusXRCompatibilityRules"
|
||||
namespace
|
||||
{
|
||||
constexpr int32 MinimumAndroidAPILevel = 32;
|
||||
constexpr int32 TargetAndroidAPILevel = 32;
|
||||
} // 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void FEnablePackageForMetaQuestRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(UAndroidRuntimeSettings, bPackageForMetaQuest, true);
|
||||
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->SaveConfig(CPF_Config, *Settings->GetDefaultConfigFilename());
|
||||
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->SaveConfig(CPF_Config, *Settings->GetDefaultConfigFilename());
|
||||
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->SaveConfig(CPF_Config, *Settings->GetDefaultConfigFilename());
|
||||
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,176 @@
|
||||
// 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;
|
||||
};
|
||||
|
||||
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<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,50 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRPassthroughRules.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "OculusXRHMDRuntimeSettings.h"
|
||||
#include "OculusXRPSTUtils.h"
|
||||
#include "OculusXRPassthroughLayerComponent.h"
|
||||
#include "OculusXRProjectSetupToolModule.h"
|
||||
#include "OculusXRRuleProcessorSubsystem.h"
|
||||
#include "Engine/RendererSettings.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()
|
||||
{
|
||||
return OculusXRPSTUtils::IsComponentOfTypeInWorld<UOculusXRPassthroughLayerComponent>();
|
||||
}
|
||||
|
||||
bool FAllowAlphaToneMapperPassthroughRule::IsApplied() const
|
||||
{
|
||||
URendererSettings* Settings = GetMutableDefault<URendererSettings>();
|
||||
return Settings->bEnableAlphaChannelInPostProcessing == EAlphaChannelMode::AllowThroughTonemapper;
|
||||
}
|
||||
|
||||
bool FAllowAlphaToneMapperPassthroughRule::IsValid()
|
||||
{
|
||||
return OculusXRPSTUtils::IsComponentOfTypeInWorld<UOculusXRPassthroughLayerComponent>();
|
||||
}
|
||||
|
||||
void FAllowAlphaToneMapperPassthroughRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, bEnableAlphaChannelInPostProcessing, EAlphaChannelMode::AllowThroughTonemapper);
|
||||
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,80 @@
|
||||
// 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"
|
||||
|
||||
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,246 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#include "OculusXRRenderingRules.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "AndroidRuntimeSettings.h"
|
||||
#include "EngineUtils.h"
|
||||
#include "OculusXRHMDRuntimeSettings.h"
|
||||
#include "OculusXRPSTUtils.h"
|
||||
#include "OculusXRRuleProcessorSubsystem.h"
|
||||
#include "Engine/PostProcessVolume.h"
|
||||
#include "Engine/RendererSettings.h"
|
||||
|
||||
namespace OculusXRRenderingRules
|
||||
{
|
||||
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 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;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool FDisableLensFlareRule::IsApplied() const
|
||||
{
|
||||
const URendererSettings* Settings = GetMutableDefault<URendererSettings>();
|
||||
for (TActorIterator<APostProcessVolume> ActorItr(GEditor->GetEditorWorldContext().World()); ActorItr; ++ActorItr)
|
||||
{
|
||||
if (ActorItr->Settings.LensFlareIntensity > 0.0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return Settings->bDefaultFeatureLensFlare == 0;
|
||||
}
|
||||
|
||||
void FDisableLensFlareRule::ApplyImpl(bool& OutShouldRestartEditor)
|
||||
{
|
||||
OCULUSXR_UPDATE_SETTINGS(URendererSettings, bDefaultFeatureLensFlare, false);
|
||||
|
||||
for (TActorIterator<APostProcessVolume> ActorItr(GEditor->GetEditorWorldContext().World()); ActorItr; ++ActorItr)
|
||||
{
|
||||
ActorItr->Settings.LensFlareIntensity = 0.0f;
|
||||
}
|
||||
GetMutableDefault<URendererSettings>()->SaveConfig();
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
} // namespace OculusXRRenderingRules
|
||||
@@ -0,0 +1,293 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "OculusXRSetupRule.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 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."),
|
||||
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."),
|
||||
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;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// 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<FEnableMSAARule>(),
|
||||
MakeShared<FEnableOcclusionCullingRule>(),
|
||||
MakeShared<FEnableDynamicFoveationRule>(),
|
||||
#ifdef WITH_OCULUS_BRANCH
|
||||
MakeShared<FEnableDynamicResolutionRule>(),
|
||||
#endif
|
||||
MakeShared<FDisableLensFlareRule>(),
|
||||
MakeShared<FDisablePostProcessingRule>(),
|
||||
MakeShared<FDisableAmbientOcclusionRule>(),
|
||||
MakeShared<FEnableMultiViewRule>(),
|
||||
MakeShared<FEnableStaticLightingRule>(),
|
||||
MakeShared<FDisableMobileShaderStaticAndCSMShadowReceiversRule>(),
|
||||
MakeShared<FDisableMobileShaderAllowDistanceFieldShadowsRule>(),
|
||||
MakeShared<FDisableMobileShaderAllowMovableDirectionalLightsRule>()
|
||||
};
|
||||
} // namespace OculusXRRenderingRules
|
||||
@@ -0,0 +1,169 @@
|
||||
// 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
|
||||
|
||||
BEGIN_DEFINE_SPEC(FOculusXRProjectSetupToolSpec, TEXT("Project Setup Tool"), EAutomationTestFlags::ProductFilter | EAutomationTestFlags::ApplicationContextMask)
|
||||
UOculusXRRuleProcessorSubsystem* ProcessorSubsystem;
|
||||
bool bShouldRestartEditor = false;
|
||||
TSet<FName> RulesThatRequireRestart = {
|
||||
FName("Feature_AllowAlphaToneMapperPassthrough"),
|
||||
FName("Rendering_DisableAmbientOcclusion"),
|
||||
FName("Rendering_DisablePostProcessing"),
|
||||
FName("Rendering_EnableForwardShading"),
|
||||
FName("Rendering_EnableInstancedStereo"),
|
||||
FName("Rendering_EnableMultiView"),
|
||||
FName("Rendering_EnableStaticLighting"),
|
||||
FName("Rendering_MobileShaderAllowDistanceFieldShadows"),
|
||||
FName("Rendering_MobileShaderAllowMovableDirectionalLights"),
|
||||
FName("Rendering_UseHalfPrecisionFloat")
|
||||
};
|
||||
|
||||
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());
|
||||
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,119 @@
|
||||
// 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 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 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,331 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "OculusXRProjectTutorialWidget.h"
|
||||
|
||||
#include "OculusXRProjectSetupToolModule.h"
|
||||
#include "OculusXRPSTEvents.h"
|
||||
#include "OculusXRPSTSettings.h"
|
||||
#include "OculusXRTelemetry.h"
|
||||
#include "Widgets/Layout/SScrollBox.h"
|
||||
#include "Widgets/Input/SButton.h"
|
||||
#include "Styling/SlateStyle.h"
|
||||
#include "Styling/SlateStyleMacros.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>()->SaveConfig();
|
||||
}
|
||||
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,77 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "OculusXRStatusBarWidget.h"
|
||||
|
||||
#include "OculusXRProjectSetupToolModule.h"
|
||||
#include "OculusXRRuleProcessorSubsystem.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");
|
||||
}
|
||||
|
||||
const auto& RuleStatus = RuleProcessorSubsystem->UnAppliedRulesStatus(MetaQuest_All);
|
||||
|
||||
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();
|
||||
};
|
||||
Reference in New Issue
Block a user