From a5752a0c146866f51990fa1501d1ae0f1186268b Mon Sep 17 00:00:00 2001
From: Ehret <jw210150@WIN.RZ.RWTH-AACHEN.DE>
Date: Wed, 12 Apr 2023 14:48:54 +0200
Subject: [PATCH 1/2] remove VRPawn reference from HUDHelper

---
 .../Private/HUD/SFHMDSpectatorHUDHelp.cpp                   | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Source/StudyFrameworkPlugin/Private/HUD/SFHMDSpectatorHUDHelp.cpp b/Source/StudyFrameworkPlugin/Private/HUD/SFHMDSpectatorHUDHelp.cpp
index 10bff55..369a4d0 100644
--- a/Source/StudyFrameworkPlugin/Private/HUD/SFHMDSpectatorHUDHelp.cpp
+++ b/Source/StudyFrameworkPlugin/Private/HUD/SFHMDSpectatorHUDHelp.cpp
@@ -58,15 +58,15 @@ void ASFHMDSpectatorHUDHelp::Tick(float DeltaSeconds)
 
 	//the widget needs to be always be facing away, so we do not see it in the HMD view
 	//we also move it down 10m so it does not get in our way when interacting etc.
-	APlayerController* PlayerController = GetWorld()->GetFirstPlayerController();
-	AVirtualRealityPawn* Pawn = Cast<AVirtualRealityPawn>(PlayerController->AcknowledgedPawn);
-	FVector HeadPos = Pawn->Head->GetComponentLocation();
+	const APlayerCameraManager* CamManager = GetWorld()->GetFirstPlayerController()->PlayerCameraManager;
+	FVector HeadPos = CamManager->GetCameraLocation();
 	SetActorLocation(HeadPos + 1000 * FVector::DownVector);
 	SetActorRotation(FQuat::FindBetweenNormals(FVector(0,0,1),(GetActorLocation()-HeadPos).GetSafeNormal()).Rotator());
 
 
 
 	//Set cursor to the right place
+	APlayerController* PlayerController = GetWorld()->GetFirstPlayerController();
 	USFHUDWidget* HUDWidget = Cast<USFHUDWidget>(WidgetComponent->GetWidget());
 	FVector2D CursorPos = GetAbsoluteLocationForCursorWidgetFromMousePosition(PlayerController, DrawSize);
 	HUDWidget->SetCursorWidgetPosition(CursorPos);
-- 
GitLab


From 04b4bbc4a62ede49a1a3528b9e21de70a8d53efa Mon Sep 17 00:00:00 2001
From: "jonathan.ehret" <ehret@vr.rwth-aachen.de>
Date: Mon, 26 Jun 2023 14:28:59 +0200
Subject: [PATCH 2/2] remove RWTHVRToolkit entirely

---
 .../HUD/SFGlobalFadeGameViewportClient.cpp    |  3 +-
 .../Private/HUD/SFHMDSpectatorHUDHelp.cpp     |  1 -
 .../Private/HUD/SFMasterHUD.cpp               |  7 +--
 .../Private/Help/SFUtils.cpp                  | 26 ++++++++-
 .../Private/Logging/SFLoggingUtils.cpp        |  1 -
 .../Private/SFGameInstance.cpp                | 55 ++++++++++++++++++-
 .../Public/Help/SFUtils.h                     |  3 +
 .../Public/SFGameInstance.h                   | 11 +++-
 .../StudyFrameworkPlugin.Build.cs             |  2 -
 StudyFrameworkPlugin.uplugin                  |  4 --
 10 files changed, 91 insertions(+), 22 deletions(-)

diff --git a/Source/StudyFrameworkPlugin/Private/HUD/SFGlobalFadeGameViewportClient.cpp b/Source/StudyFrameworkPlugin/Private/HUD/SFGlobalFadeGameViewportClient.cpp
index b8298ae..21eef3e 100644
--- a/Source/StudyFrameworkPlugin/Private/HUD/SFGlobalFadeGameViewportClient.cpp
+++ b/Source/StudyFrameworkPlugin/Private/HUD/SFGlobalFadeGameViewportClient.cpp
@@ -7,7 +7,6 @@
 
 #include "Engine/Canvas.h"
 #include "HUD/SFMasterHUD.h"
-#include "Utility/VirtualRealityUtilities.h"
 
 // Link to the Tutorial of the manual Viewport Client
 // https://nerivec.github.io/old-ue4-wiki/pages/global-fade-in-out.html
@@ -77,7 +76,7 @@ void USFGlobalFadeGameViewportClient::DrawScreenFade(UCanvas* Canvas)
 				MasterHUD = Cast<ASFMasterHUD>(PlayerController->GetHUD());
 			}
 
-			if (MasterHUD && UVirtualRealityUtilities::IsMaster())
+			if (MasterHUD && FSFUtils::IsPrimary())
 			{
 				//if we use the HUD let it do the fading, so it can still be seen when faded out
 				MasterHUD->SetBackgroundAlpha(FadeColorTmp.A);
diff --git a/Source/StudyFrameworkPlugin/Private/HUD/SFHMDSpectatorHUDHelp.cpp b/Source/StudyFrameworkPlugin/Private/HUD/SFHMDSpectatorHUDHelp.cpp
index 369a4d0..95b8c03 100644
--- a/Source/StudyFrameworkPlugin/Private/HUD/SFHMDSpectatorHUDHelp.cpp
+++ b/Source/StudyFrameworkPlugin/Private/HUD/SFHMDSpectatorHUDHelp.cpp
@@ -8,7 +8,6 @@
 
 #include "IXRTrackingSystem.h"
 #include "HUD/SFHUDWidget.h"
-#include "Pawn/VirtualRealityPawn.h"
 #include "GameFramework/PlayerController.h"
 #include "Slate/SceneViewport.h"
 
diff --git a/Source/StudyFrameworkPlugin/Private/HUD/SFMasterHUD.cpp b/Source/StudyFrameworkPlugin/Private/HUD/SFMasterHUD.cpp
index cc06658..e379ae9 100644
--- a/Source/StudyFrameworkPlugin/Private/HUD/SFMasterHUD.cpp
+++ b/Source/StudyFrameworkPlugin/Private/HUD/SFMasterHUD.cpp
@@ -8,7 +8,6 @@
 #include "SFPlugin.h"
 #include "Help/SFUtils.h"
 
-#include "Utility/VirtualRealityUtilities.h"
 #include "HeadMountedDisplayFunctionLibrary.h"
 
 
@@ -18,7 +17,7 @@ ASFMasterHUD::ASFMasterHUD()
 
 void ASFMasterHUD::DrawHUD()
 {
-	if (UVirtualRealityUtilities::IsMaster())
+	if (FSFUtils::IsPrimary())
 	{
 		DrawBackground();
 	}
@@ -49,13 +48,13 @@ void ASFMasterHUD::BeginPlay()
 	}
 
 	HMDHUDHelper = nullptr;
-	if (UVirtualRealityUtilities::IsHeadMountedMode())
+	if (FSFUtils::IsHMD())
 	{
 		HMDHUDHelper = Cast<ASFHMDSpectatorHUDHelp>(
 			GetWorld()->SpawnActor(ASFHMDSpectatorHUDHelp::StaticClass()));
 		HUDWidget = Cast<USFHUDWidget>(HMDHUDHelper->CreateWidget(SFWidgetClass));
 	}
-	else if (UVirtualRealityUtilities::IsMaster())
+	else if (FSFUtils::IsPrimary())
 	{
 		HUDWidget = CreateWidget<USFHUDWidget>(GetWorld(), SFWidgetClass);
 	}
diff --git a/Source/StudyFrameworkPlugin/Private/Help/SFUtils.cpp b/Source/StudyFrameworkPlugin/Private/Help/SFUtils.cpp
index 2b90034..bc8c8ac 100644
--- a/Source/StudyFrameworkPlugin/Private/Help/SFUtils.cpp
+++ b/Source/StudyFrameworkPlugin/Private/Help/SFUtils.cpp
@@ -6,18 +6,19 @@
 #include "Misc/MessageDialog.h"
 #include "Misc/FileHelper.h"
 
+#include "AudioDevice.h"
+
 #include "Json.h"
 
 #include "SFPlugin.h"
 #include "SFGameInstance.h"
 
-#include "Utility/VirtualRealityUtilities.h"
 
 #include "IUniversalLogging.h"
 
 void FSFUtils::OpenMessageBox(const FString Text, const bool bError/*=false*/)
 {
-	if (!UVirtualRealityUtilities::IsMaster())
+	if (!IsPrimary())
 	{
 		return;
 	}
@@ -119,3 +120,24 @@ FString FSFUtils::GetStudyFrameworkPath()
 {
 	return FPaths::ProjectDir() + "StudyFramework/";
 }
+
+bool FSFUtils::IsPrimary()
+{
+	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 master
+	}
+	return Manager->IsMaster() || !Manager->IsSlave();
+}
+
+bool FSFUtils::IsHMD()
+{
+	// In editor builds: checks for EdEngine->IsVRPreviewActive()
+	// In packaged builds: checks for `-vr` in commandline or bStartInVR in UGeneralProjectSettings
+	return FAudioDevice::CanUseVRAudioDevice();
+}
diff --git a/Source/StudyFrameworkPlugin/Private/Logging/SFLoggingUtils.cpp b/Source/StudyFrameworkPlugin/Private/Logging/SFLoggingUtils.cpp
index 512da7e..2a886f3 100644
--- a/Source/StudyFrameworkPlugin/Private/Logging/SFLoggingUtils.cpp
+++ b/Source/StudyFrameworkPlugin/Private/Logging/SFLoggingUtils.cpp
@@ -6,7 +6,6 @@
 
 #include "SFGameInstance.h"
 
-#include "Utility/VirtualRealityUtilities.h"
 
 #include "IUniversalLogging.h"
 #include "Help/SFUtils.h"
diff --git a/Source/StudyFrameworkPlugin/Private/SFGameInstance.cpp b/Source/StudyFrameworkPlugin/Private/SFGameInstance.cpp
index f0f25eb..e6ae56d 100644
--- a/Source/StudyFrameworkPlugin/Private/SFGameInstance.cpp
+++ b/Source/StudyFrameworkPlugin/Private/SFGameInstance.cpp
@@ -26,7 +26,13 @@ void USFGameInstance::Init()
 
 	GEngine->GameViewportClientClass = USFGlobalFadeGameViewportClient::StaticClass();
 
-	GoToConditionSyncedEvent.Attach(this);
+	IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
+	if (ClusterManager && !ClusterEventListenerDelegate.IsBound())
+	{
+		ClusterEventListenerDelegate = FOnClusterEventJsonListener::CreateUObject(this, &USFGameInstance::HandleClusterEvent);
+		ClusterManager->AddClusterEventJsonListener(ClusterEventListenerDelegate);
+	}
+
 
 	Instance = this;
 	LogObject = NewObject<USFLogObject>();
@@ -53,7 +59,14 @@ void USFGameInstance::Init()
 
 void USFGameInstance::Shutdown()
 {
-	GoToConditionSyncedEvent.Detach();
+
+	IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
+	if (ClusterManager && ClusterEventListenerDelegate.IsBound())
+	{
+		ClusterManager->RemoveClusterEventJsonListener(ClusterEventListenerDelegate);
+	}
+
+
 	if(ExperimenterWindow)
 	{
 		ExperimenterWindow->DestroyWindow();
@@ -432,11 +445,47 @@ bool USFGameInstance::GoToCondition(const USFCondition* Condition, bool bForced
 		FSFLoggingUtils::Log("[USFGameInstance::GoToCondition()]: Could not load next condition.", true);
 		return false;
 	}
-	GoToConditionSyncedEvent.Send(Condition->UniqueName, bForced);
+	GoToConditionSynced(Condition->UniqueName, bForced);
 	return true;
 }
 
 void USFGameInstance::GoToConditionSynced(FString ConditionName, bool bForced)
+{
+	const EDisplayClusterOperationMode OperationMode = IDisplayCluster::Get().GetOperationMode();
+	if (OperationMode != EDisplayClusterOperationMode::Cluster)
+	{
+		HandleGoToConditionSynced(ConditionName, bForced);
+	}
+	else
+	{
+		IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
+		check(ClusterManager != nullptr);
+
+		FDisplayClusterClusterEventJson ClusterEvent;
+		ClusterEvent.Type = "SFGameInstanceEvent";
+		ClusterEvent.Name = "GoToConditionSynced";
+		TMap<FString, FString> Params;
+		Params.Add("ConditionName", ConditionName);
+		Params.Add("bForced", bForced ?"true":"false");
+		ClusterEvent.Parameters = Params;
+		ClusterEvent.bShouldDiscardOnRepeat = true;
+
+		ClusterManager->EmitClusterEventJson(ClusterEvent, true);
+	}
+}
+
+void USFGameInstance::HandleClusterEvent(const FDisplayClusterClusterEventJson& Event) {
+	if (Event.Type == "SFGameInstanceEvent") {
+		//now we actually react on all cluster nodes:
+		if(Event.Name == "GoToConditionSynced")
+		{
+			HandleGoToConditionSynced(Event.Parameters["ConditionName"], Event.Parameters["bForced"] == "true");
+		}
+	}
+}
+
+
+void USFGameInstance::HandleGoToConditionSynced(FString ConditionName, bool bForced)
 {
 	USFCondition* NextCondition = nullptr;
 	for (USFCondition* Condition : Participant->GetAllConditions())
diff --git a/Source/StudyFrameworkPlugin/Public/Help/SFUtils.h b/Source/StudyFrameworkPlugin/Public/Help/SFUtils.h
index c70caa2..a6fcd43 100644
--- a/Source/StudyFrameworkPlugin/Public/Help/SFUtils.h
+++ b/Source/StudyFrameworkPlugin/Public/Help/SFUtils.h
@@ -28,4 +28,7 @@ public:
 
 	static UWorld* GetWorld();
 	static FString GetStudyFrameworkPath();
+
+	static bool IsPrimary();
+	static bool IsHMD();
 };
\ No newline at end of file
diff --git a/Source/StudyFrameworkPlugin/Public/SFGameInstance.h b/Source/StudyFrameworkPlugin/Public/SFGameInstance.h
index bcc32ff..a83a61f 100644
--- a/Source/StudyFrameworkPlugin/Public/SFGameInstance.h
+++ b/Source/StudyFrameworkPlugin/Public/SFGameInstance.h
@@ -11,7 +11,8 @@
 #include "HUD/SFMasterHUD.h"
 #include "GazeTracking/SFGazeTracker.h"
 
-#include "Events/DisplayClusterEventWrapper.h"
+#include "Cluster/IDisplayClusterClusterManager.h"
+#include "Cluster/DisplayClusterClusterEvent.h"
 
 #include "Widgets/SWindow.h"
 
@@ -164,8 +165,12 @@ protected:
 
 	void EndStudy();
 
-	void GoToConditionSynced(FString ConditionName, bool bForced);
-	DECLARE_DISPLAY_CLUSTER_EVENT(USFGameInstance, GoToConditionSynced);
+	//we use cluster events so GoToConditionSynced can not run out of sync when using nDisplay in cluster mode
+	void GoToConditionSynced(FString ConditionName, bool bForced); //send the cluster event
+	void HandleGoToConditionSynced(FString ConditionName, bool bForced); //process the cluter event
+	FOnClusterEventJsonListener ClusterEventListenerDelegate;
+	void HandleClusterEvent(const FDisplayClusterClusterEventJson& Event);
+
 
 	void RestoreLastParticipantForDebugStart(USFCondition* InStartCondition);
 	//method called by a timer if we want to directly fade in on startup
diff --git a/Source/StudyFrameworkPlugin/StudyFrameworkPlugin.Build.cs b/Source/StudyFrameworkPlugin/StudyFrameworkPlugin.Build.cs
index 2a0da1b..d506a13 100644
--- a/Source/StudyFrameworkPlugin/StudyFrameworkPlugin.Build.cs
+++ b/Source/StudyFrameworkPlugin/StudyFrameworkPlugin.Build.cs
@@ -35,8 +35,6 @@ public class StudyFrameworkPlugin : ModuleRules
                 "CoreUObject",
                 "Engine",
                 "DisplayCluster",
-                "RWTHVRToolkit",
-                "RWTHVRCluster",
                 "InputCore",
                 "Sockets",
                 "Json",
diff --git a/StudyFrameworkPlugin.uplugin b/StudyFrameworkPlugin.uplugin
index 1345244..238dc94 100644
--- a/StudyFrameworkPlugin.uplugin
+++ b/StudyFrameworkPlugin.uplugin
@@ -25,10 +25,6 @@
       "Name": "nDisplay",
       "Enabled": true
     },
-    {
-      "Name": "RWTHVRToolkit",
-      "Enabled": true
-    },
     {
       "Name": "UniversalLogging",
       "Enabled": true
-- 
GitLab