From 376c1fea94c9c06e3b9a787359af360b682cebfc Mon Sep 17 00:00:00 2001
From: David Gilbert <gilbert@vr.rwth-aachen.de>
Date: Tue, 23 Jan 2024 10:49:47 +0100
Subject: [PATCH] refactor(cave): Optionally enable RWTHVRCluster Plugin. If
 enabled, Utilities forwards to cluster specific utilities and the pawn gets a
 cluster sync component spawned at runtime. Removes Cluster specific input
 from general imc

---
 Content/Input/Default_IMC/IMC_General.uasset  | Bin 21212 -> 20559 bytes
 RWTHVRToolkit.uplugin                         |   9 ++
 .../RWTHVRToolkit/Private/Pawn/RWTHVRPawn.cpp |  16 +-
 .../Private/Utility/RWTHVRUtilities.cpp       | 146 ++----------------
 Source/RWTHVRToolkit/Public/Pawn/RWTHVRPawn.h |   8 +-
 .../Public/Utility/RWTHVRUtilities.h          |  51 +-----
 Source/RWTHVRToolkit/RWTHVRToolkit.Build.cs   |  13 +-
 7 files changed, 46 insertions(+), 197 deletions(-)

diff --git a/Content/Input/Default_IMC/IMC_General.uasset b/Content/Input/Default_IMC/IMC_General.uasset
index 4b74905bb2a24bd5cad5ddb52693c01da084dc76..8de5bc1e14890320eb9d9781c7f5c683ee22b42f 100644
GIT binary patch
literal 20559
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
XKgfoI%qhEP5y*0Czb0(kAxJ#{7}o=9

literal 21212
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
JVOv!}>H&c4G0gw~

diff --git a/RWTHVRToolkit.uplugin b/RWTHVRToolkit.uplugin
index 9cee8222..3629dc72 100644
--- a/RWTHVRToolkit.uplugin
+++ b/RWTHVRToolkit.uplugin
@@ -27,6 +27,15 @@
 		}
 	],
 	"Plugins": [
+		{
+			"Name": "RWTHVRCluster",
+			"Enabled": true,
+			"Optional": true,
+			"SupportedTargetPlatforms": [
+				"Win64",
+				"Linux"
+			]
+		},
 		{
 			"Name": "LiveLink",
 			"Enabled": true
diff --git a/Source/RWTHVRToolkit/Private/Pawn/RWTHVRPawn.cpp b/Source/RWTHVRToolkit/Private/Pawn/RWTHVRPawn.cpp
index 5a84da66..28eb764a 100644
--- a/Source/RWTHVRToolkit/Private/Pawn/RWTHVRPawn.cpp
+++ b/Source/RWTHVRToolkit/Private/Pawn/RWTHVRPawn.cpp
@@ -16,6 +16,10 @@
 #include "Roles/LiveLinkTransformTypes.h"
 #include "Utility/RWTHVRUtilities.h"
 
+#if PLATFORM_SUPPORTS_CLUSTER
+#include "Components/DisplayClusterSceneComponentSyncParent.h"
+#endif
+
 ARWTHVRPawn::ARWTHVRPawn(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
 {
 	BaseEyeHeight = 160.0f;
@@ -36,15 +40,19 @@ ARWTHVRPawn::ARWTHVRPawn(const FObjectInitializer& ObjectInitializer) : Super(Ob
 
 	LeftHand = CreateDefaultSubobject<UReplicatedMotionControllerComponent>(TEXT("Left Hand MCC"));
 	LeftHand->SetupAttachment(RootComponent);
-	
-#if 0	
+}
+void ARWTHVRPawn::BeginPlay()
+{
+	Super::BeginPlay();
+
+#if PLATFORM_SUPPORTS_CLUSTER
 	// Add an nDisplay Parent Sync Component. It syncs the parent's transform from master to clients.
 	// This is required because for collision based movement, it can happen that the physics engine
 	// for some reason acts different on the nodes, therefore leading to a potential desync when
 	// e.g. colliding with an object while moving.
-	SyncComponent =
-		CreateDefaultSubobject<UDisplayClusterSceneComponentSyncParent>(TEXT("Parent Display Cluster Sync Component"));
+	SyncComponent = NewObject<UDisplayClusterSceneComponentSyncParent>();
 	SyncComponent->SetupAttachment(RootComponent);
+	SyncComponent->RegisterComponent();
 #endif
 }
 
diff --git a/Source/RWTHVRToolkit/Private/Utility/RWTHVRUtilities.cpp b/Source/RWTHVRToolkit/Private/Utility/RWTHVRUtilities.cpp
index 77330f03..f43fc658 100644
--- a/Source/RWTHVRToolkit/Private/Utility/RWTHVRUtilities.cpp
+++ b/Source/RWTHVRToolkit/Private/Utility/RWTHVRUtilities.cpp
@@ -1,15 +1,5 @@
 #include "Utility/RWTHVRUtilities.h"
 
-#if PLATFORM_SUPPORTS_NDISPLAY
-#include "DisplayClusterConfigurationTypes.h"
-#include "DisplayClusterRootActor.h"
-#include "IDisplayCluster.h"
-#include "Cluster/IDisplayClusterClusterManager.h"
-#include "Components/DisplayClusterCameraComponent.h"
-#include "Config/IDisplayClusterConfigManager.h"
-#include "Game/IDisplayClusterGameManager.h"
-#endif
-
 #include "AudioDevice.h"
 #include "IHeadMountedDisplay.h"
 #include "IXRTrackingSystem.h"
@@ -17,6 +7,10 @@
 #include "Engine/LocalPlayer.h"
 #include "Kismet/GameplayStatics.h"
 
+#if PLATFORM_SUPPORTS_CLUSTER
+#include "Utility/RWTHVRClusterUtilities.h"
+#endif
+
 
 DEFINE_LOG_CATEGORY(Toolkit);
 
@@ -25,15 +19,6 @@ bool URWTHVRUtilities::IsDesktopMode()
 	return !IsRoomMountedMode() && !IsHeadMountedMode();
 }
 
-bool URWTHVRUtilities::IsRoomMountedMode()
-{
-#if PLATFORM_SUPPORTS_NDISPLAY
-	return IDisplayCluster::Get().GetOperationMode() == EDisplayClusterOperationMode::Cluster;
-#else
-	return false;
-#endif
-}
-
 bool URWTHVRUtilities::IsHeadMountedMode()
 {
 	// In editor builds: checks for EdEngine->IsVRPreviewActive()
@@ -41,65 +26,20 @@ bool URWTHVRUtilities::IsHeadMountedMode()
 	return FAudioDevice::CanUseVRAudioDevice();
 }
 
-bool URWTHVRUtilities::IsCave()
+bool URWTHVRUtilities::IsRoomMountedMode()
 {
-#if PLATFORM_SUPPORTS_NDISPLAY
-	if (!IsRoomMountedMode())
-		return false;
-
-	const UDisplayClusterConfigurationData* ClusterConfig = IDisplayCluster::Get().GetConfigMgr()->GetConfig();
-	return ClusterConfig->CustomParameters.Contains("Hardware_Platform")
-		&& ClusterConfig->CustomParameters.Find("Hardware_Platform")->Equals("aixcave", ESearchCase::IgnoreCase);
-#else
-	return false;
+#if PLATFORM_SUPPORTS_CLUSTER
+	URWTHVRClusterUtilities::IsRoomMountedMode();
 #endif
-}
-
-bool URWTHVRUtilities::IsRolv()
-{
-#if PLATFORM_SUPPORTS_NDISPLAY
-	if (!IsRoomMountedMode())
-		return false;
-
-	const UDisplayClusterConfigurationData* ClusterConfig = IDisplayCluster::Get().GetConfigMgr()->GetConfig();
-	return ClusterConfig->CustomParameters.Contains("Hardware_Platform")
-		&& ClusterConfig->CustomParameters.Find("Hardware_Platform")->Equals("ROLV", ESearchCase::IgnoreCase);
-#else
 	return false;
-#endif
 }
 
-/* Return true on the Primary in cluster mode and in a normal desktop session. Otherwise false */
 bool URWTHVRUtilities::IsPrimaryNode()
 {
-#if PLATFORM_SUPPORTS_NDISPLAY
-	if (!IDisplayCluster::IsAvailable())
-	{
-		return true;
-	}
-	IDisplayClusterClusterManager* Manager = IDisplayCluster::Get().GetClusterMgr();
-	if (Manager == nullptr)
-	{
-		return true; // if we are not in cluster mode, we are always the primary node
-	}
-	return Manager->IsPrimary() || !Manager->IsSecondary();
-#else
-    return true;
-#endif
-}
-
-bool URWTHVRUtilities::IsSecondaryNode()
-{
-	return !IsPrimaryNode();
-}
-
-FString URWTHVRUtilities::GetNodeName()
-{
-#if PLATFORM_SUPPORTS_NDISPLAY
-	return IsRoomMountedMode() ? IDisplayCluster::Get().GetClusterMgr()->GetNodeId() : FString(TEXT("Localhost"));
-#else
-	return FString(TEXT("Localhost"));
+#if PLATFORM_SUPPORTS_CLUSTER
+	URWTHVRClusterUtilities::IsPrimaryNode();
 #endif
+	return false;
 }
 
 float URWTHVRUtilities::GetEyeDistance()
@@ -108,71 +48,7 @@ float URWTHVRUtilities::GetEyeDistance()
 	{
 		return GEngine->XRSystem->GetHMDDevice()->GetInterpupillaryDistance();
 	}
-	else
-	{
-#if PLATFORM_SUPPORTS_NDISPLAY
-		const ADisplayClusterRootActor* RootActor = IDisplayCluster::Get().GetGameMgr()->GetRootActor();
-		return (RootActor) ? RootActor->GetDefaultCamera()->GetInterpupillaryDistance() : 0.0f;
-#else
-	    return 0.0f;
-#endif
-	}
-}
-
-EEyeStereoOffset URWTHVRUtilities::GetNodeEyeType()
-{
-#if PLATFORM_SUPPORTS_NDISPLAY
-	const ADisplayClusterRootActor* RootActor = IDisplayCluster::Get().GetGameMgr()->GetRootActor();
-	return static_cast<EEyeStereoOffset>((RootActor)
-		                                     ? RootActor->GetDefaultCamera()->GetStereoOffset()
-		                                     : EDisplayClusterEyeStereoOffset::None);
-#else
-	return EEyeStereoOffset::None;
-#endif
-}
-
-USceneComponent* URWTHVRUtilities::GetClusterComponent(const FString& Name)
-{
-#if PLATFORM_SUPPORTS_NDISPLAY
-	const ADisplayClusterRootActor* RootActor = IDisplayCluster::Get().GetGameMgr()->GetRootActor();
-	return (RootActor) ? RootActor->GetComponentByName<USceneComponent>(Name) : nullptr;
-#else
-	return nullptr;
-#endif
-}
-
-USceneComponent* URWTHVRUtilities::GetNamedClusterComponent(const ENamedClusterComponent& Component)
-{
-	switch (Component)
-	{
-	case ENamedClusterComponent::NCC_CAVE_ORIGIN:
-		return GetClusterComponent("cave_origin");
-	case ENamedClusterComponent::NCC_CAVE_CENTER:
-		return GetClusterComponent("cave_center");
-	case ENamedClusterComponent::NCC_CAVE_LHT:
-		return GetClusterComponent("left_hand_target");
-	case ENamedClusterComponent::NCC_CAVE_RHT:
-		return GetClusterComponent("right_hand_target");
-	case ENamedClusterComponent::NCC_SHUTTERGLASSES:
-		return GetClusterComponent("shutter_glasses");
-	case ENamedClusterComponent::NCC_ROLV_ORIGIN:
-		return GetClusterComponent("rolv_origin");
-	case ENamedClusterComponent::NCC_FLYSTICK:
-		return GetClusterComponent("flystick");
-	case ENamedClusterComponent::NCC_CALIBRATIO:
-		return GetClusterComponent("calibratio");
-	case ENamedClusterComponent::NCC_TRACKING_ORIGIN:
-		USceneComponent* Result;
-		if ((Result = GetClusterComponent("cave_origin")))
-			return Result;
-		if ((Result = GetClusterComponent("rolv_origin")))
-			return Result;
-		if ((Result = GetClusterComponent("tdw_origin_floor")))
-			return Result;
-		return nullptr;
-	default:
-		return nullptr;
-	}
+	return 0;
 }
 
 void URWTHVRUtilities::ShowErrorAndQuit(UWorld* WorldContext, const FString& Message)
diff --git a/Source/RWTHVRToolkit/Public/Pawn/RWTHVRPawn.h b/Source/RWTHVRToolkit/Public/Pawn/RWTHVRPawn.h
index b171d3f1..5b67bd31 100644
--- a/Source/RWTHVRToolkit/Public/Pawn/RWTHVRPawn.h
+++ b/Source/RWTHVRToolkit/Public/Pawn/RWTHVRPawn.h
@@ -6,10 +6,6 @@
 #include "LiveLinkRole.h"
 #include "Pawn/Navigation/CollisionHandlingMovement.h"
 
-#if 0
-#include "Components/DisplayClusterSceneComponentSyncParent.h"
-#endif
-
 #include "RWTHVRPawn.generated.h"
 
 class UInputMappingContext;
@@ -29,6 +25,8 @@ class RWTHVRTOOLKIT_API ARWTHVRPawn : public APawn
 public:
 	ARWTHVRPawn(const FObjectInitializer& ObjectInitializer);
 
+	virtual void BeginPlay() override;
+
 	virtual void Tick(float DeltaSeconds) override;
 
 	virtual void NotifyControllerChanged() override;
@@ -56,7 +54,7 @@ public:
 	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn|Camera")
 	UCameraComponent* HeadCameraComponent;
 
-	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn|Camera")
+	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn")
 	USceneComponent* SyncComponent;
 
 	// LiveLink functionality
diff --git a/Source/RWTHVRToolkit/Public/Utility/RWTHVRUtilities.h b/Source/RWTHVRToolkit/Public/Utility/RWTHVRUtilities.h
index e0f6d7e4..ef9f3f0c 100644
--- a/Source/RWTHVRToolkit/Public/Utility/RWTHVRUtilities.h
+++ b/Source/RWTHVRToolkit/Public/Utility/RWTHVRUtilities.h
@@ -2,7 +2,6 @@
 
 #include "CoreMinimal.h"
 #include "Kismet/BlueprintFunctionLibrary.h"
-#include "UObject/ConstructorHelpers.h"
 
 #include "RWTHVRUtilities.generated.h"
 
@@ -12,33 +11,6 @@
  */
 DECLARE_LOG_CATEGORY_EXTERN(Toolkit, Log, All);
 
-UENUM(BlueprintType)
-enum class ENamedClusterComponent : uint8
-{
-	/* CAVE Specific */
-	NCC_CAVE_ORIGIN UMETA(DisplayName = "CAVE Origin"),
-	NCC_CAVE_CENTER UMETA(DisplayName = "CAVE Center"),
-	NCC_CAVE_LHT UMETA(DisplayName = "CAVE Left Hand Target"),
-	NCC_CAVE_RHT UMETA(DisplayName = "CAVE Right Hand Target"),
-
-	/* ROLV Specific */
-	NCC_ROLV_ORIGIN UMETA(DisplayName = "ROLV Origin"),
-
-	/* Non Specific */
-	NCC_CALIBRATIO UMETA(DisplayName = "Calibratio Motion to Photon Measurement Device"),
-	NCC_SHUTTERGLASSES UMETA(DisplayName = "CAVE/ROLV/TDW Shutter Glasses"),
-	NCC_FLYSTICK UMETA(DisplayName = "CAVE/ROLV/TDW Flystick"),
-	NCC_TRACKING_ORIGIN UMETA(DisplayName = "CAVE/ROLV/TDW Origin")
-};
-
-UENUM()
-enum class EEyeStereoOffset
-{
-	None,
-	Left,
-	Right
-};
-
 UCLASS()
 class RWTHVRTOOLKIT_API URWTHVRUtilities : public UBlueprintFunctionLibrary
 {
@@ -48,37 +20,16 @@ public:
 	UFUNCTION(BlueprintPure, Category = "DisplayCluster|Platform")
 	static bool IsDesktopMode();
 	UFUNCTION(BlueprintPure, Category = "DisplayCluster|Platform")
-	static bool IsRoomMountedMode();
-	UFUNCTION(BlueprintPure, Category = "DisplayCluster|Platform")
 	static bool IsHeadMountedMode();
 	UFUNCTION(BlueprintPure, Category = "DisplayCluster|Platform")
-	static bool IsCave();
+	static bool IsRoomMountedMode();
 	UFUNCTION(BlueprintPure, Category = "DisplayCluster|Platform")
-	static bool IsRolv();
-
-	UFUNCTION(BlueprintPure, Category = "DisplayCluster")
 	static bool IsPrimaryNode();
-	UFUNCTION(BlueprintPure, Category = "DisplayCluster")
-	static bool IsSecondaryNode();
 
-	UFUNCTION(BlueprintPure, Category = "DisplayCluster")
-	static FString GetNodeName();
 	/* Distance in meters */
 	UFUNCTION(BlueprintPure, Category = "DisplayCluster")
 	static float GetEyeDistance();
 
-	UFUNCTION(BlueprintPure, Category = "DisplayCluster")
-	static EEyeStereoOffset GetNodeEyeType();
-
-	//Get Component of Display Cluster by it's name, which is specified in the nDisplay config
-	UE_DEPRECATED(5.4, "GetClusterComponent has been removed because it is obsolete.")
-	UFUNCTION(BlueprintPure, BlueprintCallable, Category = "DisplayCluster", meta = (DeprecatedFunction))
-	static USceneComponent* GetClusterComponent(const FString& Name);
-
-	UE_DEPRECATED(5.4, "GetNamedClusterComponent has been removed because it is obsolete.")
-	UFUNCTION(BlueprintPure, BlueprintCallable, Category = "DisplayCluster", meta = (DeprecatedFunction))
-	static USceneComponent* GetNamedClusterComponent(const ENamedClusterComponent& Component);
-
 	UFUNCTION(BlueprintCallable)
 	static void ShowErrorAndQuit(UWorld* WorldContext, const FString& Message);
 };
diff --git a/Source/RWTHVRToolkit/RWTHVRToolkit.Build.cs b/Source/RWTHVRToolkit/RWTHVRToolkit.Build.cs
index ba27e93d..a949553a 100644
--- a/Source/RWTHVRToolkit/RWTHVRToolkit.Build.cs
+++ b/Source/RWTHVRToolkit/RWTHVRToolkit.Build.cs
@@ -50,9 +50,16 @@ public class RWTHVRToolkit : ModuleRules
 			new string[]{}
 		);
 
-		PublicDefinitions.Add(IsPluginEnabledForTarget("nDisplay", base.Target)
-			? "PLATFORM_SUPPORTS_NDISPLAY=1"
-			: "PLATFORM_SUPPORTS_NDISPLAY=0");
+		if (IsPluginEnabledForTarget("RWTHVRCluster", base.Target))
+		{
+			PrivateDependencyModuleNames.Add("RWTHVRCluster");
+			PrivateDependencyModuleNames.Add("DisplayCluster");
+			PublicDefinitions.Add("PLATFORM_SUPPORTS_CLUSTER=1");
+		}
+		else
+		{			
+			PublicDefinitions.Add("PLATFORM_SUPPORTS_CLUSTER=0");
+		}
 	}
 	
 	private static bool IsPluginEnabledForTarget(string PluginName, ReadOnlyTargetRules Target)
-- 
GitLab