Skip to content

Commit

Permalink
Merged PR 703: Move XRSimulation parts out of the UxtDefaultHandTrack…
Browse files Browse the repository at this point in the history
…erSubsystem.

Move XRSimulation parts out of the UxtDefaultHandTrackerSubsystem.

Simulated hand state was persisting between game sessions in the editor,
which is undesirable. The reason is that UxtDefaultHandTrackerSubsystem is
an engine subsystem which means it persists as long as the editor is running
and so does the simulation state.

Resetting the simulation state in the PostLogin/Logout events does not work,
because these are actually called for every map change. The hand state would
be reset every time a map is loaded, which is also not desirable. (That was
the reason the separate simulation state was introduced in the first place,
so it could persist across map loads).

A LocalPlayerSubsystem solves the issue because the local player is persistent
across maps, but gets destroyed and recreated for each game session.

An added benefit of this change is that the XRSimulation-related functionality
is moved out of the UxtDefaultHandTrackerSubsystem, so it becomes easier to
remove again once the HMD class becomes a viable home for it in the future.

Related work items: #1021
  • Loading branch information
lukastoenneMS committed Mar 11, 2021
2 parents 5931819 + 12dc041 commit 12b9709
Show file tree
Hide file tree
Showing 6 changed files with 298 additions and 189 deletions.
Expand Up @@ -6,10 +6,8 @@
#include "ARSupportInterface.h"
#include "IXRTrackingSystem.h"
#include "SceneViewExtension.h"
#include "UxtXRSimulationViewExtension.h"
#include "UxtXRSimulationSubsystem.h"
#include "XRSimulationActor.h"
#include "XRSimulationRuntimeSettings.h"
#include "XRSimulationState.h"

#include "Camera/CameraComponent.h"
#include "Components/InputComponent.h"
Expand All @@ -21,21 +19,13 @@
#include "Kismet/GameplayStatics.h"
#include "Utils/UxtFunctionLibrary.h"

#if WITH_EDITOR
#include "Editor/EditorEngine.h"
#endif

void UUxtDefaultHandTrackerSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
FUxtDefaultHandTracker::RegisterInputMappings();
AXRSimulationActor::RegisterInputMappings();

PostLoginHandle = FGameModeEvents::GameModePostLoginEvent.AddUObject(this, &UUxtDefaultHandTrackerSubsystem::OnGameModePostLogin);
LogoutHandle = FGameModeEvents::GameModeLogoutEvent.AddUObject(this, &UUxtDefaultHandTrackerSubsystem::OnGameModeLogout);

AXRSimulationActor::RegisterInputMappings();
SimulationState = MakeShareable(new FXRSimulationState());

XRSimulationViewExtension = FSceneViewExtensions::NewExtension<FUxtXRSimulationViewExtension>(this);
}

void UUxtDefaultHandTrackerSubsystem::Deinitialize()
Expand All @@ -46,11 +36,7 @@ void UUxtDefaultHandTrackerSubsystem::Deinitialize()
LogoutHandle.Reset();

FUxtDefaultHandTracker::UnregisterInputMappings();

AXRSimulationActor::UnregisterInputMappings();
SimulationState.Reset();

XRSimulationViewExtension = nullptr;
}

void UUxtDefaultHandTrackerSubsystem::OnGameModePostLogin(AGameModeBase* GameMode, APlayerController* NewPlayer)
Expand Down Expand Up @@ -83,34 +69,6 @@ void UUxtDefaultHandTrackerSubsystem::OnGameModePostLogin(AGameModeBase* GameMod
TickDelegateHandle = FWorldDelegates::OnWorldPreActorTick.AddUObject(this, &UUxtDefaultHandTrackerSubsystem::OnWorldPreActorTick);

IModularFeatures::Get().RegisterModularFeature(IUxtHandTracker::GetModularFeatureName(), &DefaultHandTracker);

bSimulationEnabled = false;
const UXRSimulationRuntimeSettings* Settings = UXRSimulationRuntimeSettings::Get();
if (Settings->bEnableSimulation)
{
// Don't use input simulation in VR preview mode and only in PIE worlds.
bool bIsPreview = false;
#if WITH_EDITOR
if (GIsEditor)
{
UEditorEngine* EdEngine = Cast<UEditorEngine>(GEngine);
if (EdEngine->GetPlayInEditorSessionInfo().IsSet())
{
EPlaySessionPreviewType SessionPreviewType =
EdEngine->GetPlayInEditorSessionInfo()->OriginalRequestParams.SessionPreviewTypeOverride.Get(
EPlaySessionPreviewType::NoPreview);
bIsPreview = SessionPreviewType != EPlaySessionPreviewType::NoPreview;
}
}
#endif
bSimulationEnabled = NewPlayer->GetWorld()->WorldType == EWorldType::PIE && !bIsPreview;
}

if (bSimulationEnabled)
{
GetOrCreateInputSimActor(NewPlayer);
GetOrCreateHmdCameraActor(NewPlayer);
}
}
}

Expand All @@ -129,39 +87,39 @@ void UUxtDefaultHandTrackerSubsystem::OnGameModeLogout(AGameModeBase* GameMode,
PlayerController->InputComponent->AxisBindings.RemoveAll(
[this](const FInputAxisBinding& Binding) -> bool { return Binding.AxisDelegate.IsBoundToObject(this); });
}

DestroyInputSimActor();
DestroyHmdCameraActor();
bSimulationEnabled = false;
}
}
}

void UUxtDefaultHandTrackerSubsystem::OnWorldPreActorTick(UWorld* World, ELevelTick TickType, float DeltaTime)
{
if (bSimulationEnabled)
UUxtXRSimulationSubsystem* XRSimulationSubsystem = nullptr;
if (APlayerController* PlayerController = World->GetFirstPlayerController())
{
// Use simulated data generated by the simulation actor
AXRSimulationActor* SimActor = SimulationActorWeak.Get();
if (ensure(SimActor))
if (ULocalPlayer* LocalPlayer = PlayerController->GetLocalPlayer())
{
SimActor->GetHandData(EControllerHand::Left, DefaultHandTracker.ControllerData_Left);
SimActor->GetHandData(EControllerHand::Right, DefaultHandTracker.ControllerData_Right);

// Update Select/Grip state directly, no input events are used here
SimActor->GetControllerActionState(
EControllerHand::Left, DefaultHandTracker.bIsSelectPressed_Left, DefaultHandTracker.bIsGrabbing_Left);
SimActor->GetControllerActionState(
EControllerHand::Right, DefaultHandTracker.bIsSelectPressed_Right, DefaultHandTracker.bIsGrabbing_Right);

// Head pose is using the XRTrackingSystem as well, force override in the function library
UUxtFunctionLibrary::bUseTestData = true;
FVector HeadPosition;
FQuat HeadRotation;
SimActor->GetHeadPose(HeadRotation, HeadPosition);
UUxtFunctionLibrary::TestHeadPose = FTransform(HeadRotation, HeadPosition);
XRSimulationSubsystem = LocalPlayer->GetSubsystem<UUxtXRSimulationSubsystem>();
}
}

if (XRSimulationSubsystem && XRSimulationSubsystem->IsSimulationEnabled())
{
// Use simulated data generated by the simulation actor
// Update Select/Grip state directly, no input events are used here
XRSimulationSubsystem->GetMotionControllerData(
EControllerHand::Left, DefaultHandTracker.ControllerData_Left, DefaultHandTracker.bIsSelectPressed_Left,
DefaultHandTracker.bIsGrabbing_Left);
XRSimulationSubsystem->GetMotionControllerData(
EControllerHand::Right, DefaultHandTracker.ControllerData_Right, DefaultHandTracker.bIsSelectPressed_Right,
DefaultHandTracker.bIsGrabbing_Right);

// Head pose is using the XRTrackingSystem as well, force override in the function library
FVector HeadPosition;
FQuat HeadRotation;
XRSimulationSubsystem->GetHeadPose(HeadRotation, HeadPosition);
UUxtFunctionLibrary::bUseTestData = true;
UUxtFunctionLibrary::TestHeadPose = FTransform(HeadRotation, HeadPosition);
}
else
{
// True XR system data from devices
Expand Down Expand Up @@ -215,89 +173,3 @@ void UUxtDefaultHandTrackerSubsystem::OnRightGripReleased()
{
DefaultHandTracker.bIsGrabbing_Right = false;
}

AXRSimulationActor* UUxtDefaultHandTrackerSubsystem::GetOrCreateInputSimActor(APlayerController* PlayerController)
{
// Only create one actor
if (SimulationActorWeak.IsValid())
{
return SimulationActorWeak.Get();
}

FActorSpawnParameters p;
p.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
p.bDeferConstruction = true;

UWorld* World = PlayerController->GetWorld();
AXRSimulationActor* SimulationActor = World->SpawnActor<AXRSimulationActor>(p);
SimulationActorWeak = SimulationActor;

SimulationActor->SetSimulationState(SimulationState);

// Explicitly enable input: The simulation actor may be created after loading a map,
// in which case auto-enabling input does not work.
SimulationActor->EnableInput(PlayerController);

// Attach to the player controller so the start location matches the HMD camera, which is relative to the controller.
SimulationActor->AttachToActor(PlayerController, FAttachmentTransformRules::KeepRelativeTransform);

UGameplayStatics::FinishSpawningActor(SimulationActor, FTransform::Identity);

return SimulationActor;
}

void UUxtDefaultHandTrackerSubsystem::DestroyInputSimActor()
{
if (AActor* SimulationActor = SimulationActorWeak.Get())
{
SimulationActor->Destroy();
}
SimulationActorWeak.Reset();
}

AActor* UUxtDefaultHandTrackerSubsystem::GetOrCreateHmdCameraActor(APlayerController* PlayerController)
{
if (HmdCameraActorWeak.IsValid())
{
return HmdCameraActorWeak.Get();
}

FActorSpawnParameters p;
p.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;

UWorld* World = PlayerController->GetWorld();
AActor* HmdCameraActor = World->SpawnActor<AActor>(p);
HmdCameraActorWeak = HmdCameraActor;
#if WITH_EDITOR
HmdCameraActor->SetActorLabel(TEXT("HmdCamera"));
#endif

UCameraComponent* HmdCameraComponent = NewObject<UCameraComponent>(HmdCameraActor, TEXT("HmdCamera"));
HmdCameraActor->AddOwnedComponent(HmdCameraComponent);
HmdCameraActor->SetRootComponent(HmdCameraComponent);
HmdCameraComponent->RegisterComponent();
// Camera should use HMD location
// XXX LockToHmd worked in the previous approach where the actor defined the HMD positions.
// XXX When using this without a HMD we need to attach to the sim actor instead.
/*HmdCameraComponent->bLockToHmd = true;*/
if (ensure(SimulationActorWeak.IsValid()))
{
HmdCameraActor->AttachToActor(SimulationActorWeak.Get(), FAttachmentTransformRules::KeepRelativeTransform);
}

// Set the view target for the player controller to render from the HMD position
PlayerController->SetViewTarget(HmdCameraActor);
// Prevent the controller from resetting the view target when the player is restarted
PlayerController->bAutoManageActiveCameraTarget = false;

return HmdCameraActor;
}

void UUxtDefaultHandTrackerSubsystem::DestroyHmdCameraActor()
{
if (AActor* HmdCameraActor = HmdCameraActorWeak.Get())
{
HmdCameraActor->Destroy();
}
HmdCameraActorWeak.Reset();
}
Expand Up @@ -28,8 +28,6 @@ class UUxtDefaultHandTrackerSubsystem : public UEngineSubsystem
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;

bool IsSimulationEnabled() const { return bSimulationEnabled; }

private:
void OnGameModePostLogin(AGameModeBase* GameMode, APlayerController* NewPlayer);
void OnGameModeLogout(AGameModeBase* GameMode, AController* Exiting);
Expand All @@ -45,38 +43,11 @@ class UUxtDefaultHandTrackerSubsystem : public UEngineSubsystem
void OnRightGripPressed();
void OnRightGripReleased();

AXRSimulationActor* GetOrCreateInputSimActor(APlayerController* PlayerController);
void DestroyInputSimActor();

AActor* GetOrCreateHmdCameraActor(APlayerController* PlayerController);
void DestroyHmdCameraActor();

private:
FUxtDefaultHandTracker DefaultHandTracker;

FDelegateHandle TickDelegateHandle;

FDelegateHandle PostLoginHandle;
FDelegateHandle LogoutHandle;

/** True if input simulation is enabled.
* Data from the simulation actor will be used for hand tracking instead of the XRTrackingSystem API.
*/
bool bSimulationEnabled = false;

/** Actor that handles user input to simulate XR. */
TWeakObjectPtr<AXRSimulationActor> SimulationActorWeak;

/** Simulation state for the input sim actor.
* This is cached by the HMD to keep it persistent across map loads.
*/
TSharedPtr<FXRSimulationState> SimulationState;

/** Actor with a camera component that acts as the view target.
* This is needed because in a non-stereo viewport the camera will not use the XRSystem HMD position on its own.
* Using a separate camera component will use the HMD position though, even if the camera itself is not moved.
*/
TWeakObjectPtr<AActor> HmdCameraActorWeak;

TSharedPtr<class FUxtXRSimulationViewExtension, ESPMode::ThreadSafe> XRSimulationViewExtension;
};

0 comments on commit 12b9709

Please sign in to comment.