diff --git a/Content/Pawn/BP_RWTHVRPawn_Default.uasset b/Content/Pawn/BP_RWTHVRPawn_Default.uasset
index cb05e67f79561281c52e04b21e9bd67726871dbb..145ddf5f938d0a3612c7a609aafa75a30d82063f 100644
Binary files a/Content/Pawn/BP_RWTHVRPawn_Default.uasset and b/Content/Pawn/BP_RWTHVRPawn_Default.uasset differ
diff --git a/Content/Pawn/Base/BP_RWTHVRPawn_Base.uasset b/Content/Pawn/Base/BP_RWTHVRPawn_Base.uasset
index 3616819e69aa1ac0f21047b036be9244b1597c76..cba6c2e1875f45603f51e234dbb9b066017654f9 100644
Binary files a/Content/Pawn/Base/BP_RWTHVRPawn_Base.uasset and b/Content/Pawn/Base/BP_RWTHVRPawn_Base.uasset differ
diff --git a/Source/RWTHVRToolkit/Private/Core/RWTHVRGameModeBase.cpp b/Source/RWTHVRToolkit/Private/Core/RWTHVRGameModeBase.cpp
index 4d9b4b6e4dcdb1447e2d9ff98fbfc5400f000589..a30a7ab130e1b84263f54aee3840c91705ef1b42 100644
--- a/Source/RWTHVRToolkit/Private/Core/RWTHVRGameModeBase.cpp
+++ b/Source/RWTHVRToolkit/Private/Core/RWTHVRGameModeBase.cpp
@@ -7,9 +7,15 @@
#include "GameFramework/SpectatorPawn.h"
#include "Kismet/GameplayStatics.h"
#include "Logging/StructuredLog.h"
+#include "Pawn/ClusterRepresentationActor.h"
#include "Utility/RWTHVRUtilities.h"
+ARWTHVRGameModeBase::ARWTHVRGameModeBase(const FObjectInitializer& ObjectInitializer)
+{
+ PlayerStateClass = ARWTHVRPlayerState::StaticClass();
+}
+
FString ARWTHVRGameModeBase::InitNewPlayer(APlayerController* NewPlayerController, const FUniqueNetIdRepl& UniqueId,
const FString& Options, const FString& Portal)
{
@@ -17,6 +23,7 @@ FString ARWTHVRGameModeBase::InitNewPlayer(APlayerController* NewPlayerControlle
// but I don't really want to introduce a hard dependency here.
const FString NodeNameKey = "node";
const FString PrimaryNodeIdKey = "PrimaryNodeId";
+ const FString ClusterIdKey = "ClusterId";
// Check if we're using our custom PlayerState so that we can save the player type there.
// If not, just ingore all related args.
@@ -24,6 +31,7 @@ FString ARWTHVRGameModeBase::InitNewPlayer(APlayerController* NewPlayerControlle
if (State != nullptr)
{
+ int32 ClusterId = -1;
if (UGameplayStatics::HasOption(Options, PrimaryNodeIdKey))
{
const FString PrimaryNodeId = UGameplayStatics::ParseOption(Options, PrimaryNodeIdKey);
@@ -34,10 +42,51 @@ FString ARWTHVRGameModeBase::InitNewPlayer(APlayerController* NewPlayerControlle
? UGameplayStatics::ParseOption(Options, NodeNameKey)
: PrimaryNodeId;
+ ClusterId = UGameplayStatics::HasOption(Options, ClusterIdKey)
+ ? TextKeyUtil::HashString(*UGameplayStatics::ParseOption(Options, ClusterIdKey))
+ : -1;
+
const EPlayerType Type =
NodeName == PrimaryNodeId ? EPlayerType::nDisplayPrimary : EPlayerType::nDisplaySecondary;
State->RequestSetPlayerType(Type);
}
+ else if (GetNetMode() == NM_Standalone && URWTHVRUtilities::IsRoomMountedMode())
+ {
+ ClusterId = 0;
+ }
+
+ // If we're in none-standalone netmode, this is only executed on the server, as the GM only exists there.
+ // On standalone, this is executed on every node.
+
+ State->SetCorrespondingClusterId(ClusterId);
+
+ auto* ClusterRepresentation = ConnectedClusters.Find(ClusterId);
+ if (!ClusterRepresentation)
+ {
+ // No actor there yet, spawn it
+ FActorSpawnParameters SpawnParameters;
+ SpawnParameters.Name = FName(*FString::Printf(TEXT("ClusterRepresentation_%d"), ClusterId));
+ ClusterRepresentation = GetWorld()->SpawnActor<AClusterRepresentationActor>(SpawnParameters);
+ ClusterRepresentation->ClusterId = ClusterId;
+ UE_LOGFMT(Toolkit, Display,
+ "ARWTHVRGameModeBase: Spawned ClusterRepresentationActor {Name} for Cluster {Id}",
+ ClusterRepresentation->GetName(), ClusterId);
+ }
+
+ UE_LOGFMT(Toolkit, Display, "ARWTHVRGameModeBase: Using ClusterRepresentationActor {Name} for Cluster {Id}",
+ ClusterRepresentation->GetName(), ClusterId);
+
+ // Double check for sanity
+ check(ClusterId == ClusterRepresentation->ClusterId);
+
+ State->SetCorrespondingClusterActor(ClusterRepresentation);
+ State->SetCorrespondingClusterId(ClusterId);
+
+ if (State->GetPlayerType() == EPlayerType::nDisplayPrimary)
+ {
+ // We're the owner of the actor!
+ ClusterRepresentation->SetOwner(NewPlayerController);
+ }
}
return Super::InitNewPlayer(NewPlayerController, UniqueId, Options, Portal);
@@ -88,6 +137,5 @@ void ARWTHVRGameModeBase::PostLogin(APlayerController* NewPlayer)
NewPlayer->Possess(ResultPawn);
}
-
Super::PostLogin(NewPlayer);
}
diff --git a/Source/RWTHVRToolkit/Private/Core/RWTHVRPlayerState.cpp b/Source/RWTHVRToolkit/Private/Core/RWTHVRPlayerState.cpp
index 0ad60f0c44a7853df65507cd23b9943c6cd8530b..4ae4795c85058331aeea4a2ff08df643b3cccd05 100644
--- a/Source/RWTHVRToolkit/Private/Core/RWTHVRPlayerState.cpp
+++ b/Source/RWTHVRToolkit/Private/Core/RWTHVRPlayerState.cpp
@@ -3,8 +3,10 @@
#include "Core/RWTHVRPlayerState.h"
+#include "Logging/StructuredLog.h"
#include "Net/UnrealNetwork.h"
#include "Net/Core/PushModel/PushModel.h"
+#include "Utility/RWTHVRUtilities.h"
// Boilerplate, copies properties to new state
void ARWTHVRPlayerState::CopyProperties(class APlayerState* PlayerState)
@@ -17,6 +19,8 @@ void ARWTHVRPlayerState::CopyProperties(class APlayerState* PlayerState)
if (IsValid(RWTHVRPlayerState))
{
RWTHVRPlayerState->SetPlayerType(GetPlayerType());
+ RWTHVRPlayerState->SetCorrespondingClusterId(CorrespondingClusterId);
+ RWTHVRPlayerState->SetCorrespondingClusterActor(CorrespondingClusterActor);
}
}
}
@@ -32,6 +36,8 @@ void ARWTHVRPlayerState::OverrideWith(class APlayerState* PlayerState)
if (IsValid(RWTHVRPlayerState))
{
SetPlayerType(RWTHVRPlayerState->GetPlayerType());
+ SetCorrespondingClusterId(RWTHVRPlayerState->CorrespondingClusterId);
+ SetCorrespondingClusterActor(RWTHVRPlayerState->CorrespondingClusterActor);
}
}
}
@@ -45,6 +51,8 @@ void ARWTHVRPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& O
SharedParams.bIsPushBased = true;
DOREPLIFETIME_WITH_PARAMS_FAST(ARWTHVRPlayerState, PlayerType, SharedParams);
+ DOREPLIFETIME_WITH_PARAMS_FAST(ARWTHVRPlayerState, CorrespondingClusterId, SharedParams);
+ DOREPLIFETIME_WITH_PARAMS_FAST(ARWTHVRPlayerState, CorrespondingClusterActor, SharedParams);
}
void ARWTHVRPlayerState::ServerSetPlayerTypeRpc_Implementation(const EPlayerType NewPlayerType)
@@ -57,6 +65,28 @@ void ARWTHVRPlayerState::SetPlayerType(const EPlayerType NewPlayerType)
MARK_PROPERTY_DIRTY_FROM_NAME(ARWTHVRPlayerState, PlayerType, this);
PlayerType = NewPlayerType;
}
+void ARWTHVRPlayerState::SetCorrespondingClusterId(int32 NewCorrespondingClusterId)
+{
+ if (!HasAuthority())
+ {
+ UE_LOGFMT(Toolkit, Warning, "ARWTHVRPlayerState: Cannot set cluster Id on non-authority!");
+ return;
+ }
+
+ MARK_PROPERTY_DIRTY_FROM_NAME(ARWTHVRPlayerState, CorrespondingClusterId, this);
+ CorrespondingClusterId = NewCorrespondingClusterId;
+}
+void ARWTHVRPlayerState::SetCorrespondingClusterActor(AClusterRepresentationActor* NewCorrespondingClusterActor)
+{
+ if (!HasAuthority())
+ {
+ UE_LOGFMT(Toolkit, Warning, "ARWTHVRPlayerState: Cannot set cluster actor ref on non-authority!");
+ return;
+ }
+
+ MARK_PROPERTY_DIRTY_FROM_NAME(ARWTHVRPlayerState, CorrespondingClusterActor, this);
+ CorrespondingClusterActor = NewCorrespondingClusterActor;
+}
void ARWTHVRPlayerState::RequestSetPlayerType(const EPlayerType NewPlayerType)
{
diff --git a/Source/RWTHVRToolkit/Private/Pawn/ClusterRepresentationActor.cpp b/Source/RWTHVRToolkit/Private/Pawn/ClusterRepresentationActor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f3ec515bdaffef8dfde867961f5f23fe7d4ff1c6
--- /dev/null
+++ b/Source/RWTHVRToolkit/Private/Pawn/ClusterRepresentationActor.cpp
@@ -0,0 +1,142 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "Pawn/ClusterRepresentationActor.h"
+
+#include "DisplayClusterRootActor.h"
+#include "IDisplayCluster.h"
+#include "Config/IDisplayClusterConfigManager.h"
+#include "Core/RWTHVRPlayerState.h"
+#include "Game/IDisplayClusterGameManager.h"
+#include "Kismet/GameplayStatics.h"
+#include "Logging/StructuredLog.h"
+#include "Utility/RWTHVRUtilities.h"
+
+// Sets default values
+AClusterRepresentationActor::AClusterRepresentationActor()
+{
+ // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
+ PrimaryActorTick.bCanEverTick = false;
+ bReplicates = true;
+ SetActorEnableCollision(false);
+
+ SetRootComponent(CreateDefaultSubobject<USceneComponent>(TEXT("Origin")));
+}
+
+void AClusterRepresentationActor::BeginPlay()
+{
+ Super::BeginPlay();
+
+ // We need to identify the correct ClusterRepresentationActor to do the attachment.
+ // 1. Either we are the local net owner -> Primary Node Pawn
+ // This is hard to do as things might not be synchronized yet.
+ // In this case I think this is save to do because either:
+ // - We are in standalone mode, then attach it for all nodes
+ // - If we are a client, this actor has been spawned on the server only. Therefore I assume that if we
+ // have replicated this actor to our client, we're good to go.
+
+ if (!URWTHVRUtilities::IsRoomMountedMode())
+ return;
+
+ // This should give us the first local player controller
+ const auto* PlayerController = UGameplayStatics::GetPlayerController(GetWorld(), 0);
+
+ // Only run this for the local controller - redundant, but double check
+ if (!PlayerController || !PlayerController->IsLocalController())
+ return;
+
+ const auto* PlayerState = PlayerController->GetPlayerState<ARWTHVRPlayerState>();
+ if (!PlayerState)
+ {
+ UE_LOGFMT(
+ Toolkit, Error,
+ "AClusterRepresentationActor::BeginPlay: PlayerState is not valid or not of type ARWTHVRPlayerState.");
+ return;
+ }
+
+ // The local player this is executed on corresponds to this actor
+ if (PlayerState->GetCorrespondingClusterActor() == this)
+ {
+ check(PlayerState->GetCorrespondingClusterId() == ClusterId);
+ UE_LOGFMT(Toolkit, Display, "AClusterRepresentationActor::BeginPlay: Attaching DCRA to {Name} with Id: {Id}.",
+ GetName(), ClusterId);
+
+ AttachDCRA();
+ }
+}
+
+bool AClusterRepresentationActor::AttachDCRA()
+{
+
+#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.
+
+ if (URWTHVRUtilities::IsRoomMountedMode())
+ {
+ UE_LOGFMT(Toolkit, Display, "{Name}: Trying to attach DCRA for ClusterId {Id}", GetName(), ClusterId);
+ auto DCRA = IDisplayCluster::Get().GetGameMgr()->GetRootActor();
+
+ if (!IsValid(DCRA))
+ {
+ UE_LOGFMT(Toolkit, Display, "No Valid DCRA in BeginPlay. Spawning manually.");
+
+ DCRA = SpawnDCRA();
+ if (!IsValid(DCRA))
+ {
+ UE_LOGFMT(Toolkit, Error, "Failed to spawn correct DCRA, cannot attach it to ClusterRepresentation.");
+ return false;
+ }
+ }
+ else // if we just spawned the DCRA, it is not yet the primary one and this check makes no sense
+ {
+ if (!DCRA->IsPrimaryRootActor())
+ {
+ UE_LOGFMT(Toolkit, Error, "Found DCRA {Name} is not the primary DCRA of Cluster with Id {Id}!",
+ DCRA->GetName(), ClusterId);
+ return false;
+ }
+ }
+
+ bool bAttached = DCRA->AttachToActor(this, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
+ UE_LOGFMT(Toolkit, Display, "Attaching {This} to DCRA {DCRA} returned {Res}", GetName(), DCRA->GetName(),
+ bAttached);
+
+ DCRA->SetActorEnableCollision(false);
+ }
+#endif
+ return true;
+}
+
+ADisplayClusterRootActor* AClusterRepresentationActor::SpawnDCRA()
+{
+ UDisplayClusterConfigurationData* ConfigData = IDisplayCluster::Get().GetConfigMgr()->GetConfig();
+
+
+ // Function similar to the one from DisplayClusterGameManager.
+ ADisplayClusterRootActor* RootActor = nullptr;
+ // We need to use generated class as it's the only one available in packaged buidls
+ const FString AssetPath =
+ (ConfigData->Info.AssetPath.EndsWith(TEXT("_C")) ? ConfigData->Info.AssetPath
+ : ConfigData->Info.AssetPath + TEXT("_C"));
+
+ if (UClass* ActorClass = Cast<UClass>(StaticLoadObject(UClass::StaticClass(), nullptr, *AssetPath)))
+ {
+ // Spawn the actor
+ if (AActor* NewActor = GetWorld()->SpawnActor<AActor>(ActorClass))
+ {
+ RootActor = Cast<ADisplayClusterRootActor>(NewActor);
+
+ if (RootActor)
+ {
+ UE_LOG(Toolkit, Log, TEXT("Instantiated DCRA from asset '%s'"), *ConfigData->Info.AssetPath);
+
+ // Override actor settings in case the config file contains some updates
+ RootActor->OverrideFromConfig(ConfigData);
+ }
+ }
+ }
+ return RootActor;
+}
\ No newline at end of file
diff --git a/Source/RWTHVRToolkit/Private/Pawn/RWTHVRPawn.cpp b/Source/RWTHVRToolkit/Private/Pawn/RWTHVRPawn.cpp
index d50578531749b295d79961ac45eab7ebfd097c7e..0d11d17ce60aff2dccac2c6472640409db3f7fb1 100644
--- a/Source/RWTHVRToolkit/Private/Pawn/RWTHVRPawn.cpp
+++ b/Source/RWTHVRToolkit/Private/Pawn/RWTHVRPawn.cpp
@@ -8,8 +8,8 @@
#include "ILiveLinkClient.h"
#include "InputMappingContext.h"
#include "Core/RWTHVRPlayerState.h"
-#include "Kismet/GameplayStatics.h"
#include "Logging/StructuredLog.h"
+#include "Pawn/ClusterRepresentationActor.h"
#include "Pawn/InputExtensionInterface.h"
#include "Pawn/Navigation/CollisionHandlingMovement.h"
#include "Pawn/ReplicatedCameraComponent.h"
@@ -61,7 +61,7 @@ void ARWTHVRPawn::Tick(float DeltaSeconds)
* as connections now send their playertype over.
*/
// This pawn's controller has changed! This is called on both server and owning client. If we are the owning client
-// and the master, request that the DCRA is attached to us.
+// and the master, request that the Cluster is attached to us.
void ARWTHVRPawn::NotifyControllerChanged()
{
Super::NotifyControllerChanged();
@@ -71,7 +71,7 @@ void ARWTHVRPawn::NotifyControllerChanged()
if (HasAuthority())
{
UE_LOG(Toolkit, Display,
- TEXT("ARWTHVRPawn: Player Controller has changed, trying to change DCRA attachment if possible..."));
+ TEXT("ARWTHVRPawn: Player Controller has changed, trying to change Cluster attachment if possible..."));
if (const ARWTHVRPlayerState* State = GetPlayerState<ARWTHVRPlayerState>())
{
const EPlayerType Type = State->GetPlayerType();
@@ -80,8 +80,8 @@ void ARWTHVRPawn::NotifyControllerChanged()
// For all other player types this is a race condition
if (Type == EPlayerType::nDisplayPrimary || GetNetMode() == NM_Standalone)
{
- UE_LOGFMT(Toolkit, Display, "ARWTHVRPawn: Attaching DCRA to Pawn {Pawn}.", GetName());
- AttachDCRAtoPawn();
+ UE_LOGFMT(Toolkit, Display, "ARWTHVRPawn: Attaching Cluster to Pawn {Pawn}.", GetName());
+ AttachClustertoPawn();
}
}
}
@@ -259,31 +259,27 @@ void ARWTHVRPawn::MulticastAddDCSyncComponent_Implementation()
#endif
}
-// Executed on the server only: Finds and attaches the CaveSetup Actor, which contains the DCRA to the Pawn.
+// Executed on the server only: Attaches the ClusterRepresentation Actor, which contains the DCRA to the Pawn.
// It is only executed on the server because attachments are synced to all clients, but not from client to server.
-void ARWTHVRPawn::AttachDCRAtoPawn()
+void ARWTHVRPawn::AttachClustertoPawn()
{
- if (!CaveSetupActorClass || !CaveSetupActorClass->IsValidLowLevelFast())
- {
- UE_LOGFMT(Toolkit, Warning, "No CaveSetup Actor class set in pawn!");
- return;
- }
-
- // Find our CaveSetup actor
- TArray<AActor*> FoundActors;
- UGameplayStatics::GetAllActorsOfClass(GetWorld(), CaveSetupActorClass, FoundActors);
- if (!FoundActors.IsEmpty())
+ if (const ARWTHVRPlayerState* State = GetPlayerState<ARWTHVRPlayerState>())
{
- const auto CaveSetupActor = FoundActors[0];
- FAttachmentTransformRules AttachmentRules = FAttachmentTransformRules::SnapToTargetNotIncludingScale;
- CaveSetupActor->AttachToActor(this, AttachmentRules);
- UE_LOGFMT(Toolkit, Display, "VirtualRealityPawn: Attaching CaveSetup to our pawn!");
+ if (!State->GetCorrespondingClusterActor())
+ UE_LOGFMT(Toolkit, Error,
+ "ARWTHVRPawn::AttachClustertoPawn: GetCorrespondingClusterActor returned null! This won't work on "
+ "the Cave.");
+
+ const FAttachmentTransformRules AttachmentRules = FAttachmentTransformRules::SnapToTargetNotIncludingScale;
+ bool bAttached = State->GetCorrespondingClusterActor()->AttachToComponent(GetRootComponent(), AttachmentRules);
+ //State->GetCorrespondingClusterActor()->OnAttached();
+ UE_LOGFMT(Toolkit, Display, "ARWTHVRPawn: Attaching corresponding cluster actor to our pawn returned: {Attached}", bAttached);
}
else
{
- UE_LOGFMT(Toolkit, Warning,
- "No CaveSetup Actor found which can be attached to the Pawn! This won't work on the Cave.");
+ UE_LOGFMT(Toolkit, Error,
+ "ARWTHVRPawn::AttachClustertoPawn: No ARWTHVRPlayerState set! This won't work on the Cave.");
}
// if (HasAuthority()) // Should always be the case here, but double check
diff --git a/Source/RWTHVRToolkit/Public/Core/RWTHVRGameModeBase.h b/Source/RWTHVRToolkit/Public/Core/RWTHVRGameModeBase.h
index 1469ac36caa9b571b765622766a01ed103b26252..d4b933507d4049967b25c29726320e2335eec11d 100644
--- a/Source/RWTHVRToolkit/Public/Core/RWTHVRGameModeBase.h
+++ b/Source/RWTHVRToolkit/Public/Core/RWTHVRGameModeBase.h
@@ -4,6 +4,7 @@
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
+#include "Pawn/ClusterRepresentationActor.h"
#include "RWTHVRGameModeBase.generated.h"
/**
@@ -15,6 +16,9 @@ UCLASS()
class RWTHVRTOOLKIT_API ARWTHVRGameModeBase : public AGameModeBase
{
GENERATED_BODY()
+public:
+
+ ARWTHVRGameModeBase(const FObjectInitializer& ObjectInitializer);
protected:
/**
@@ -29,4 +33,7 @@ protected:
* possess. If not, spawn a DefaultPawnClass Pawn and Possess it (Should be BP_VirtualRealityPawn to make sense).
*/
virtual void PostLogin(APlayerController* NewPlayer) override;
+
+private:
+ TMap<int32, AClusterRepresentationActor> ConnectedClusters;
};
diff --git a/Source/RWTHVRToolkit/Public/Core/RWTHVRPlayerState.h b/Source/RWTHVRToolkit/Public/Core/RWTHVRPlayerState.h
index 4c709e204428e4cbcd20d782823676c5b8e12c7c..c5e8f46f8716d2f5d6119383affbc03a5e8030c9 100644
--- a/Source/RWTHVRToolkit/Public/Core/RWTHVRPlayerState.h
+++ b/Source/RWTHVRToolkit/Public/Core/RWTHVRPlayerState.h
@@ -7,6 +7,8 @@
#include "GameFramework/PlayerState.h"
#include "RWTHVRPlayerState.generated.h"
+
+class AClusterRepresentationActor;
enum class EPlayerType : uint8;
/**
* Extension of the PlayerState that additionally holds information about what type the player is.
@@ -22,6 +24,15 @@ private:
UPROPERTY(Replicated, Category = PlayerState, BlueprintGetter = GetPlayerType, meta = (AllowPrivateAccess))
EPlayerType PlayerType = EPlayerType::Desktop;
+ /** Replicated cluster ID for this player. Is -1 in case player is not a cluster*/
+ UPROPERTY(Replicated, Category = PlayerState, BlueprintGetter = GetCorrespondingClusterId, meta = (AllowPrivateAccess))
+ int32 CorrespondingClusterId = -1;
+
+ /** Replicated cluster actor for this player. Is nullptr in case player is not a cluster*/
+ UPROPERTY(Replicated, Category = PlayerState, BlueprintGetter = GetCorrespondingClusterActor,
+ meta = (AllowPrivateAccess))
+ AClusterRepresentationActor* CorrespondingClusterActor = nullptr;
+
UFUNCTION(Reliable, Server)
void ServerSetPlayerTypeRpc(EPlayerType NewPlayerType);
@@ -31,6 +42,15 @@ public:
UFUNCTION(BlueprintGetter)
EPlayerType GetPlayerType() const { return PlayerType; }
+ UFUNCTION(BlueprintGetter)
+ int32 GetCorrespondingClusterId() const { return CorrespondingClusterId; }
+
+ UFUNCTION(BlueprintGetter)
+ AClusterRepresentationActor* GetCorrespondingClusterActor() const { return CorrespondingClusterActor; }
+
+ void SetCorrespondingClusterId(int32 NewCorrespondingClusterId);
+ void SetCorrespondingClusterActor(AClusterRepresentationActor* NewCorrespondingClusterActor);
+
UFUNCTION(BlueprintCallable)
void RequestSetPlayerType(EPlayerType NewPlayerType);
diff --git a/Source/RWTHVRToolkit/Public/Pawn/ClusterRepresentationActor.h b/Source/RWTHVRToolkit/Public/Pawn/ClusterRepresentationActor.h
new file mode 100644
index 0000000000000000000000000000000000000000..1db7a420237e35cdb40cac9e35f9ea6fa7fdd5f4
--- /dev/null
+++ b/Source/RWTHVRToolkit/Public/Pawn/ClusterRepresentationActor.h
@@ -0,0 +1,29 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+
+#include "ClusterRepresentationActor.generated.h"
+
+
+class ADisplayClusterRootActor;
+UCLASS()
+class RWTHVRTOOLKIT_API AClusterRepresentationActor : public AActor
+{
+ GENERATED_BODY()
+
+public:
+ // Sets default values for this actor's properties
+ AClusterRepresentationActor();
+
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
+ int32 ClusterId = -1;
+
+ virtual void BeginPlay() override;
+
+
+private:
+ bool AttachDCRA();
+ ADisplayClusterRootActor* SpawnDCRA();
+};
diff --git a/Source/RWTHVRToolkit/Public/Pawn/RWTHVRPawn.h b/Source/RWTHVRToolkit/Public/Pawn/RWTHVRPawn.h
index 260cdc0408e5f2d1ef9d64c7f56ba4325a8ff123..cd81e497181dcfef757b067ccae8c862c022fa34 100644
--- a/Source/RWTHVRToolkit/Public/Pawn/RWTHVRPawn.h
+++ b/Source/RWTHVRToolkit/Public/Pawn/RWTHVRPawn.h
@@ -79,10 +79,6 @@ public:
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Pawn|LiveLink")
bool bWorldTransform = false;
- /* The class which to search for DCRA attachment. TODO: Make this better it's ugly */
- UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Pawn|LiveLink")
- TSubclassOf<AActor> CaveSetupActorClass;
-
protected:
virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;
void AddInputMappingContext(const APlayerController* PC, const UInputMappingContext* Context) const;
@@ -107,8 +103,8 @@ protected:
UFUNCTION(Reliable, NetMulticast)
void MulticastAddDCSyncComponent();
- /* Attaches the DCRA to the pawn */
- void AttachDCRAtoPawn();
+ /* Attaches the Cluster representation to the pawn */
+ void AttachClustertoPawn();
/* Set device specific motion controller sources (None, L/R, Livelink) */
void SetupMotionControllerSources();