You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Fixes several bugs sharing the same root cause, where code/blueprints were attempting to branch on UVREditorMode::GetHMDDeviceType() or UVREditorInteractor::GetHMDDeviceType(). These calls previously returned the result of IXRTrackingSystem::GetSystemName(), and the call sites were only set up to handle "OculusHMD" or "SteamVR," and were now seeing "OpenXR". Instead, we now return one of those legacy plugin names, depending on the current OpenXR hand controller interaction profile. Longer term, we will want to revisit each of these decision points individually, and take into account support for future unknown XR devices. An alternative subclass of UVREditorMode can now be configured in the editor settings alongside the interactor and teleporter classes, and a new UVirtualScoutingMode subclass splits the mode entry into two phases. This is necessary because OpenXR may not return the correct interaction profile for several frames after the OpenXR session / stereo rendering has started, and we need to defer creation of the interactors, etc. #jira UE-150037 #rb jason.walter, Lauren.Barnes #preflight 6287f49a6c7692ac8cd00f29 [CL 20302573 by zach brockway in ue5-main branch]
291 lines
9.2 KiB
C++
291 lines
9.2 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "VREditorModeManager.h"
|
|
#include "InputCoreTypes.h"
|
|
#include "VREditorMode.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "GameFramework/PlayerController.h"
|
|
#include "GameFramework/WorldSettings.h"
|
|
#include "GameFramework/PlayerInput.h"
|
|
#include "Editor.h"
|
|
#include "HAL/PlatformApplicationMisc.h"
|
|
|
|
#include "EngineGlobals.h"
|
|
#include "LevelEditor.h"
|
|
#include "IHeadMountedDisplay.h"
|
|
#include "IXRTrackingSystem.h"
|
|
#include "EditorWorldExtension.h"
|
|
#include "ViewportWorldInteraction.h"
|
|
#include "VRModeSettings.h"
|
|
#include "Dialogs/Dialogs.h"
|
|
#include "ProjectDescriptor.h"
|
|
#include "Interfaces/IProjectManager.h"
|
|
#include "UnrealEdMisc.h"
|
|
#include "Settings/EditorStyleSettings.h"
|
|
#include "VREditorInteractor.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "VREditor"
|
|
|
|
FVREditorModeManager::FVREditorModeManager() :
|
|
CurrentVREditorMode( nullptr ),
|
|
bEnableVRRequest( false ),
|
|
HMDWornState( EHMDWornState::Unknown ),
|
|
bAddedViewportWorldInteractionExtension( false )
|
|
{
|
|
}
|
|
|
|
FVREditorModeManager::~FVREditorModeManager()
|
|
{
|
|
if (CurrentVREditorMode)
|
|
{
|
|
CurrentVREditorMode->OnVRModeEntryComplete().RemoveAll(this);
|
|
CurrentVREditorMode = nullptr;
|
|
}
|
|
}
|
|
|
|
void FVREditorModeManager::Tick( const float DeltaTime )
|
|
{
|
|
// You can only auto-enter VR if the setting is enabled. Other criteria are that the VR Editor is enabled in experimental settings, that you are not in PIE, and that the editor is foreground.
|
|
IHeadMountedDisplay * const HMD = GEngine != nullptr && GEngine->XRSystem.IsValid() ? GEngine->XRSystem->GetHMDDevice() : nullptr;
|
|
if (GetDefault<UVRModeSettings>()->bEnableAutoVREditMode
|
|
&& HMD
|
|
&& FPlatformApplicationMisc::IsThisApplicationForeground())
|
|
{
|
|
const EHMDWornState::Type LatestHMDWornState = HMD->GetHMDWornState();
|
|
if (HMDWornState != LatestHMDWornState)
|
|
{
|
|
HMDWornState = LatestHMDWornState;
|
|
if (HMDWornState == EHMDWornState::Worn && CurrentVREditorMode == nullptr)
|
|
{
|
|
EnableVREditor( true, false );
|
|
}
|
|
else if (HMDWornState == EHMDWornState::NotWorn && CurrentVREditorMode != nullptr)
|
|
{
|
|
EnableVREditor( false, false );
|
|
}
|
|
}
|
|
}
|
|
|
|
if(CurrentVREditorMode != nullptr && CurrentVREditorMode->WantsToExitMode())
|
|
{
|
|
// For a standard exit, also take the HMD out of stereo mode
|
|
const bool bShouldDisableStereo = true;
|
|
CloseVREditor( bShouldDisableStereo );
|
|
}
|
|
|
|
// Start the VR Editor mode
|
|
if (bEnableVRRequest)
|
|
{
|
|
EnableVREditor(true, false);
|
|
bEnableVRRequest = false;
|
|
}
|
|
}
|
|
|
|
bool FVREditorModeManager::IsTickable() const
|
|
{
|
|
const FProjectDescriptor* CurrentProject = IProjectManager::Get().GetCurrentProject();
|
|
return CurrentProject != nullptr;
|
|
}
|
|
|
|
void FVREditorModeManager::EnableVREditor( const bool bEnable, const bool bForceWithoutHMD )
|
|
{
|
|
// Don't do anything when the current VR Editor is already in the requested state
|
|
if( bEnable != IsVREditorActive() )
|
|
{
|
|
if( bEnable && ( IsVREditorAvailable() || bForceWithoutHMD ))
|
|
{
|
|
UEditorStyleSettings* StyleSettings = GetMutableDefault<UEditorStyleSettings>();
|
|
const UVRModeSettings* VRModeSettings = GetDefault<UVRModeSettings>();
|
|
bool bUsingDefaultInteractors = true;
|
|
if (VRModeSettings)
|
|
{
|
|
const TSoftClassPtr<UVREditorInteractor> InteractorClassSoft = VRModeSettings->InteractorClass;
|
|
InteractorClassSoft.LoadSynchronous();
|
|
|
|
if (InteractorClassSoft.IsValid())
|
|
{
|
|
bUsingDefaultInteractors = (InteractorClassSoft.Get() == UVREditorInteractor::StaticClass());
|
|
}
|
|
}
|
|
|
|
{
|
|
FSuppressableWarningDialog::FSetupInfo SetupInfo(LOCTEXT("VRModeEntry_Message", "VR Mode enables you to work on your project in virtual reality using motion controllers. This feature is still under development, so you may experience bugs or crashes while using it."),
|
|
LOCTEXT("VRModeEntry_Title", "Entering VR Mode - Experimental"), "Warning_VRModeEntry", GEditorSettingsIni);
|
|
|
|
SetupInfo.ConfirmText = LOCTEXT("VRModeEntry_ConfirmText", "Continue");
|
|
SetupInfo.CancelText = LOCTEXT("VRModeEntry_CancelText", "Cancel");
|
|
SetupInfo.bDefaultToSuppressInTheFuture = true;
|
|
FSuppressableWarningDialog VRModeEntryWarning(SetupInfo);
|
|
|
|
if (VRModeEntryWarning.ShowModal() != FSuppressableWarningDialog::Cancel)
|
|
{
|
|
StartVREditorMode(bForceWithoutHMD);
|
|
}
|
|
}
|
|
}
|
|
else if( !bEnable )
|
|
{
|
|
// For a standard exit, take the HMD out of stereo mode
|
|
const bool bShouldDisableStereo = true;
|
|
CloseVREditor( bShouldDisableStereo );
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FVREditorModeManager::IsVREditorActive() const
|
|
{
|
|
return CurrentVREditorMode != nullptr && CurrentVREditorMode->IsActive();
|
|
}
|
|
|
|
const static FName WMRSytemName = FName(TEXT("WindowsMixedRealityHMD"));
|
|
bool FVREditorModeManager::IsVREditorAvailable() const
|
|
{
|
|
if (GEngine->XRSystem.IsValid() && GEngine->XRSystem->GetHMDDevice() && GEngine->XRSystem->GetHMDDevice()->IsHMDEnabled())
|
|
{
|
|
// TODO: UE-71871 Work around for avoiding starting VRMode when using WMR
|
|
FName SystemName = GEngine->XRSystem->GetSystemName();
|
|
const bool bIsWMR = SystemName == WMRSytemName;
|
|
return !bIsWMR && !GEditor->IsPlayingSessionInEditor();
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool FVREditorModeManager::IsVREditorButtonActive() const
|
|
{
|
|
const bool bHasHMDDevice = GEngine->XRSystem.IsValid() && GEngine->XRSystem->GetHMDDevice() && GEngine->XRSystem->GetHMDDevice()->IsHMDEnabled();
|
|
return bHasHMDDevice;
|
|
}
|
|
|
|
|
|
UVREditorMode* FVREditorModeManager::GetCurrentVREditorMode()
|
|
{
|
|
return CurrentVREditorMode;
|
|
}
|
|
|
|
void FVREditorModeManager::AddReferencedObjects( FReferenceCollector& Collector )
|
|
{
|
|
Collector.AddReferencedObject( CurrentVREditorMode );
|
|
}
|
|
|
|
void FVREditorModeManager::HandleModeEntryComplete()
|
|
{
|
|
// Connects the mode UObject's event to the module delegate.
|
|
if (CurrentVREditorMode->IsActuallyUsingVR())
|
|
{
|
|
OnVREditingModeEnterHandle.Broadcast();
|
|
}
|
|
}
|
|
|
|
void FVREditorModeManager::StartVREditorMode( const bool bForceWithoutHMD )
|
|
{
|
|
if (!IsEngineExitRequested())
|
|
{
|
|
UVREditorMode* VRMode = nullptr;
|
|
{
|
|
UWorld* World = GEditor->bIsSimulatingInEditor ? GEditor->PlayWorld : GWorld;
|
|
UEditorWorldExtensionCollection* ExtensionCollection = GEditor->GetEditorWorldExtensionsManager()->GetEditorWorldExtensions(World);
|
|
check(ExtensionCollection != nullptr);
|
|
|
|
// Add viewport world interaction to the collection if not already there
|
|
UViewportWorldInteraction* ViewportWorldInteraction = Cast<UViewportWorldInteraction>(ExtensionCollection->FindExtension(UViewportWorldInteraction::StaticClass()));
|
|
if (ViewportWorldInteraction == nullptr)
|
|
{
|
|
ViewportWorldInteraction = NewObject<UViewportWorldInteraction>(ExtensionCollection);
|
|
check(ViewportWorldInteraction != nullptr);
|
|
|
|
ExtensionCollection->AddExtension(ViewportWorldInteraction);
|
|
bAddedViewportWorldInteractionExtension = true;
|
|
}
|
|
else
|
|
{
|
|
ViewportWorldInteraction->UseVWInteractions();
|
|
}
|
|
|
|
// Create vr editor mode.
|
|
const TSoftClassPtr<UVREditorMode> ModeClassSoft = GetDefault<UVRModeSettings>()->ModeClass;
|
|
ModeClassSoft.LoadSynchronous();
|
|
check(ModeClassSoft.IsValid());
|
|
|
|
VRMode = NewObject<UVREditorMode>(GetTransientPackage(), ModeClassSoft.Get());
|
|
check(VRMode != nullptr);
|
|
VRMode->OnVRModeEntryComplete().AddRaw(this, &FVREditorModeManager::HandleModeEntryComplete);
|
|
ExtensionCollection->AddExtension(VRMode);
|
|
}
|
|
|
|
// Tell the level editor we want to be notified when selection changes
|
|
{
|
|
FLevelEditorModule& LevelEditor = FModuleManager::LoadModuleChecked<FLevelEditorModule>( "LevelEditor" );
|
|
LevelEditor.OnMapChanged().AddRaw( this, &FVREditorModeManager::OnMapChanged );
|
|
}
|
|
|
|
CurrentVREditorMode = VRMode;
|
|
CurrentVREditorMode->SetActuallyUsingVR( !bForceWithoutHMD );
|
|
|
|
CurrentVREditorMode->Enter();
|
|
}
|
|
}
|
|
|
|
void FVREditorModeManager::CloseVREditor( const bool bShouldDisableStereo )
|
|
{
|
|
FLevelEditorModule* LevelEditor = FModuleManager::GetModulePtr<FLevelEditorModule>( "LevelEditor" );
|
|
if( LevelEditor != nullptr )
|
|
{
|
|
LevelEditor->OnMapChanged().RemoveAll( this );
|
|
}
|
|
|
|
if( CurrentVREditorMode != nullptr )
|
|
{
|
|
UViewportWorldInteraction* WorldInteraction = &CurrentVREditorMode->GetWorldInteraction();
|
|
CurrentVREditorMode->Exit( bShouldDisableStereo );
|
|
|
|
UEditorWorldExtensionCollection* Collection = CurrentVREditorMode->GetOwningCollection();
|
|
check(Collection != nullptr);
|
|
Collection->RemoveExtension(CurrentVREditorMode);
|
|
|
|
if (bAddedViewportWorldInteractionExtension)
|
|
{
|
|
Collection->RemoveExtension(WorldInteraction);
|
|
bAddedViewportWorldInteractionExtension = false;
|
|
}
|
|
else
|
|
{
|
|
WorldInteraction->UseLegacyInteractions();
|
|
}
|
|
|
|
if (CurrentVREditorMode->IsActuallyUsingVR())
|
|
{
|
|
OnVREditingModeExitHandle.Broadcast();
|
|
}
|
|
|
|
CurrentVREditorMode = nullptr;
|
|
|
|
}
|
|
}
|
|
|
|
void FVREditorModeManager::SetDirectWorldToMeters( const float NewWorldToMeters )
|
|
{
|
|
GWorld->GetWorldSettings()->WorldToMeters = NewWorldToMeters; //@todo VREditor: Do not use GWorld
|
|
ENGINE_API extern float GNewWorldToMetersScale;
|
|
GNewWorldToMetersScale = 0.0f;
|
|
}
|
|
|
|
void FVREditorModeManager::OnMapChanged( UWorld* World, EMapChangeType MapChangeType )
|
|
{
|
|
if( CurrentVREditorMode && CurrentVREditorMode->IsActive() )
|
|
{
|
|
// When changing maps, we are going to close VR editor mode but then reopen it, so don't take the HMD out of stereo mode
|
|
const bool bShouldDisableStereo = false;
|
|
CloseVREditor( bShouldDisableStereo );
|
|
if (MapChangeType != EMapChangeType::SaveMap)
|
|
{
|
|
bEnableVRRequest = true;
|
|
}
|
|
}
|
|
CurrentVREditorMode = nullptr;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|