From 0bf8d3435d9fca18e3ef2ae87171777344d33f4f Mon Sep 17 00:00:00 2001 From: David Gilbert <gilbert@vr.rwth-aachen.de> Date: Tue, 23 Jan 2024 10:00:21 +0100 Subject: [PATCH] refactor(cave): Removes dependency to RWTHVRToolkit. This requires the removal of directly accessing the VRPawn in the CaveOverlay as well as splitting up the Utilities in Cluster and Toolkit specific ones. CaveOverlay now also has implicitly more support for multiple MotionControllers, but needs to be tested. --- RWTHVRCluster.uplugin | 8 +- .../CAVEOverlay/CAVEOverlayController.cpp | 102 ++++++++++------ Source/RWTHVRCluster/Private/CaveSetup.cpp | 7 +- .../Utility/RWTHVRClusterUtilities.cpp | 112 ++++++++++++++++++ .../CAVEOverlay/CAVEOverlayController.h | 28 +++-- .../Public/Utility/RWTHVRClusterUtilities.h | 69 +++++++++++ Source/RWTHVRCluster/RWTHVRCluster.Build.cs | 4 +- 7 files changed, 271 insertions(+), 59 deletions(-) create mode 100644 Source/RWTHVRCluster/Private/Utility/RWTHVRClusterUtilities.cpp create mode 100644 Source/RWTHVRCluster/Public/Utility/RWTHVRClusterUtilities.h diff --git a/RWTHVRCluster.uplugin b/RWTHVRCluster.uplugin index 0297d36..d5a51b0 100644 --- a/RWTHVRCluster.uplugin +++ b/RWTHVRCluster.uplugin @@ -14,6 +14,10 @@ "IsBetaVersion": false, "Installed": false, "EnabledByDefault": true, + "SupportedTargetPlatforms": [ + "Win64", + "Linux" + ], "Modules": [ { "Name": "RWTHVRCluster", @@ -26,10 +30,6 @@ } ], "Plugins": [ - { - "Name": "RWTHVRToolkit", - "Enabled": true - }, { "Name": "nDisplay", "Enabled": true, diff --git a/Source/RWTHVRCluster/Private/CAVEOverlay/CAVEOverlayController.cpp b/Source/RWTHVRCluster/Private/CAVEOverlay/CAVEOverlayController.cpp index 08d44b2..4f27161 100644 --- a/Source/RWTHVRCluster/Private/CAVEOverlay/CAVEOverlayController.cpp +++ b/Source/RWTHVRCluster/Private/CAVEOverlay/CAVEOverlayController.cpp @@ -4,16 +4,15 @@ #include "EnhancedInputComponent.h" #include "IDisplayCluster.h" #include "MotionControllerComponent.h" -#include "Camera/CameraComponent.h" #include "CAVEOverlay/DoorOverlayData.h" +#include "Camera/CameraComponent.h" #include "Cluster/DisplayClusterClusterEvent.h" #include "Cluster/IDisplayClusterClusterManager.h" #include "Components/StaticMeshComponent.h" #include "Engine/CollisionProfile.h" #include "Logging/StructuredLog.h" #include "Materials/MaterialInstanceDynamic.h" -#include "Pawn/RWTHVRPawn.h" -#include "Utility/RWTHVRUtilities.h" +#include "Utility/RWTHVRClusterUtilities.h" DEFINE_LOG_CATEGORY(LogCAVEOverlay); @@ -50,8 +49,8 @@ ACAVEOverlayController::ACAVEOverlayController() SetRootComponent(Root); Tape = CreateMeshComponent("Tape", Root); - SignRightHand = CreateMeshComponent("SignRightHand", Root); - SignLeftHand = CreateMeshComponent("SignLeftHand", Root); + SignsStaticMeshComponents.Reserve(2); + MotionControllers.Reserve(2); } void ACAVEOverlayController::CycleDoorType() @@ -158,11 +157,11 @@ void ACAVEOverlayController::BeginPlay() // Not sure which place would be best... const bool bValidPC = PC && PC->GetLocalPlayer(); - if (!bValidPC || !URWTHVRUtilities::IsRoomMountedMode()) + if (!bValidPC || !URWTHVRClusterUtilities::IsRoomMountedMode()) return; // Input config - if (URWTHVRUtilities::IsPrimaryNode()) + if (URWTHVRClusterUtilities::IsPrimaryNode()) { if (CycleDoorTypeInputAction == nullptr) { @@ -219,9 +218,35 @@ void ACAVEOverlayController::BeginPlay() Overlay->CornerText->SetText(FText::FromString("")); // Get the pawn so we can have access to head and hand positions - VRPawn = Cast<ARWTHVRPawn>(PC->GetPawnOrSpectator()); - if (VRPawn) + if (const auto* VRPawn = Cast<APawn>(PC->GetPawnOrSpectator())) { + PawnCamera = VRPawn->GetComponentByClass<UCameraComponent>(); + auto FoundMotionControllers = VRPawn->K2_GetComponentsByClass(UMotionControllerComponent::StaticClass()); + + for (const auto FoundMotionController : FoundMotionControllers) + { + if (auto* MC = Cast<UMotionControllerComponent>(FoundMotionController); + MC && MC->MotionSource != EName::None) + { + // Create new static mesh for them + auto* SignStaticMeshComp = NewObject<UStaticMeshComponent>(); + SignStaticMeshComp->SetStaticMesh(SignStaticMesh); + SignStaticMeshComp->SetupAttachment(RootComponent); + SignStaticMeshComp->RegisterComponent(); + + MotionControllers.Add(MC); + SignsStaticMeshComponents.Add(SignStaticMeshComp); + SignsMIDs.Add(SignStaticMeshComp->CreateAndSetMaterialInstanceDynamic(0)); + } + } + + if (MotionControllers.Num() != 2) + { + UE_LOGFMT(LogCAVEOverlay, Display, + "Found unexpected number of MotionControllers on Pawn. Expected 2, found {Number}. This might " + "lead to weird results", + MotionControllers.Num()); + } // we're good to go! bInitialized = true; } @@ -230,10 +255,8 @@ void ACAVEOverlayController::BeginPlay() UE_LOGFMT(LogCAVEOverlay, Error, "No VirtualRealityPawn found which we could attach to!"); } - // Create dynamic materials at runtime + // Create dynamic material for tape TapeMaterialDynamic = Tape->CreateDynamicMaterialInstance(0); - RightSignMaterialDynamic = SignRightHand->CreateDynamicMaterialInstance(0); - LeftSignMaterialDynamic = SignLeftHand->CreateDynamicMaterialInstance(0); UE_LOGFMT(LogCAVEOverlay, Display, "CaveOverlay Initialization was successfull."); } @@ -317,41 +340,46 @@ void ACAVEOverlayController::Tick(float DeltaTime) } // Head/Tape Logic - const FVector HeadPosition = VRPawn->HeadCameraComponent->GetRelativeTransform().GetLocation(); - const bool bHeadIsCloseToWall = - FMath::IsWithinInclusive(HeadPosition.GetAbsMax(), WallDistance - WallCloseDistance, WallDistance); - // Only show the tape when close to a wall and not within the door opening - if (bHeadIsCloseToWall && !PositionInDoorOpening(HeadPosition)) + if (PawnCamera) { - Tape->SetVisibility(true); + const FVector HeadPosition = PawnCamera->GetRelativeTransform().GetLocation(); + const bool bHeadIsCloseToWall = + FMath::IsWithinInclusive(HeadPosition.GetAbsMax(), WallDistance - WallCloseDistance, WallDistance); - // Offset the tape in z direction to always be at head height - Tape->SetRelativeLocation(HeadPosition * FVector(0, 0, 1)); - - TapeMaterialDynamic->SetScalarParameterValue("BarrierOpacity", CalculateOpacityFromPosition(HeadPosition)); - - if (FMath::IsWithin(FVector2D(HeadPosition).GetAbsMax(), WallDistance - WallWarningDistance, WallDistance)) + // Only show the tape when close to a wall and not within the door opening + if (bHeadIsCloseToWall && !PositionInDoorOpening(HeadPosition)) { - // in warning distance == red tape - TapeMaterialDynamic->SetVectorParameterValue("StripeColor", FVector(1, 0, 0)); + Tape->SetVisibility(true); + + // Offset the tape in z direction to always be at head height + Tape->SetRelativeLocation(HeadPosition * FVector(0, 0, 1)); + + TapeMaterialDynamic->SetScalarParameterValue("BarrierOpacity", CalculateOpacityFromPosition(HeadPosition)); + + if (FMath::IsWithin(FVector2D(HeadPosition).GetAbsMax(), WallDistance - WallWarningDistance, WallDistance)) + { + // in warning distance == red tape + TapeMaterialDynamic->SetVectorParameterValue("StripeColor", FVector(1, 0, 0)); + } + else + { + // otherwise we're just yellow + TapeMaterialDynamic->SetVectorParameterValue("StripeColor", FVector(1, 1, 0)); + } } else { - // otherwise we're just yellow - TapeMaterialDynamic->SetVectorParameterValue("StripeColor", FVector(1, 1, 0)); + Tape->SetVisibility(false); } } - else - { - Tape->SetVisibility(false); - } // Hand Logic - const FVector RightPosition = VRPawn->RightHand->GetRelativeTransform().GetLocation(); - const FVector LeftPosition = VRPawn->LeftHand->GetRelativeTransform().GetLocation(); + for (int i = 0; i < MotionControllers.Num(); i++) + { + const FVector HandPosition = MotionControllers[i]->GetRelativeLocation(); - // Set the position rotation, opacity, visibility of the hand warning signs. - SetSignsForHand(SignRightHand, RightPosition, RightSignMaterialDynamic); - SetSignsForHand(SignLeftHand, LeftPosition, LeftSignMaterialDynamic); + // Set the position rotation, opacity, visibility of the hand warning signs. + SetSignsForHand(SignsStaticMeshComponents[i], HandPosition, SignsMIDs[i]); + } } diff --git a/Source/RWTHVRCluster/Private/CaveSetup.cpp b/Source/RWTHVRCluster/Private/CaveSetup.cpp index c39e47d..63456d4 100644 --- a/Source/RWTHVRCluster/Private/CaveSetup.cpp +++ b/Source/RWTHVRCluster/Private/CaveSetup.cpp @@ -4,8 +4,7 @@ #include "CaveSetup.h" #include "Logging/StructuredLog.h" -#include "Utility/RWTHVRUtilities.h" - +#include "Utility/RWTHVRClusterUtilities.h" // Sets default values ACaveSetup::ACaveSetup() @@ -22,7 +21,7 @@ void ACaveSetup::BeginPlay() { Super::BeginPlay(); - if (!URWTHVRUtilities::IsRoomMountedMode()) + if (!URWTHVRClusterUtilities::IsRoomMountedMode()) { return; } @@ -41,7 +40,7 @@ void ACaveSetup::BeginPlay() // Apply the DTrack LiveLink Preset. Only do this if we are the primaryNode - if (URWTHVRUtilities::IsPrimaryNode()) + if (URWTHVRClusterUtilities::IsPrimaryNode()) { if (LiveLinkPresetToApplyOnCave && LiveLinkPresetToApplyOnCave->IsValidLowLevelFast()) { diff --git a/Source/RWTHVRCluster/Private/Utility/RWTHVRClusterUtilities.cpp b/Source/RWTHVRCluster/Private/Utility/RWTHVRClusterUtilities.cpp new file mode 100644 index 0000000..f1f2a4c --- /dev/null +++ b/Source/RWTHVRCluster/Private/Utility/RWTHVRClusterUtilities.cpp @@ -0,0 +1,112 @@ +#include "Utility/RWTHVRClusterUtilities.h" + +#include "DisplayClusterConfigurationTypes.h" +#include "DisplayClusterRootActor.h" +#include "IDisplayCluster.h" +#include "Cluster/IDisplayClusterClusterManager.h" +#include "Components/DisplayClusterCameraComponent.h" +#include "Config/IDisplayClusterConfigManager.h" +#include "Engine/Engine.h" +#include "Engine/LocalPlayer.h" +#include "Game/IDisplayClusterGameManager.h" + +DEFINE_LOG_CATEGORY(ClusterPlugin); + +bool URWTHVRClusterUtilities::IsRoomMountedMode() +{ + return IDisplayCluster::Get().GetOperationMode() == EDisplayClusterOperationMode::Cluster; +} + +bool URWTHVRClusterUtilities::IsCave() +{ + 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); +} + +bool URWTHVRClusterUtilities::IsRolv() +{ + 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); +} + +/* Return true on the Primary in cluster mode and in a normal desktop session. Otherwise false */ +bool URWTHVRClusterUtilities::IsPrimaryNode() +{ + 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(); +} + +bool URWTHVRClusterUtilities::IsSecondaryNode() { return !IsPrimaryNode(); } + +FString URWTHVRClusterUtilities::GetNodeName() +{ + return IsRoomMountedMode() ? IDisplayCluster::Get().GetClusterMgr()->GetNodeId() : FString(TEXT("Localhost")); +} + +float URWTHVRClusterUtilities::GetEyeDistance() +{ + const ADisplayClusterRootActor* RootActor = IDisplayCluster::Get().GetGameMgr()->GetRootActor(); + return (RootActor) ? RootActor->GetDefaultCamera()->GetInterpupillaryDistance() : 0.0f; +} + +EDisplayClusterEyeStereoOffset URWTHVRClusterUtilities::GetNodeEyeType() +{ + const ADisplayClusterRootActor* RootActor = IDisplayCluster::Get().GetGameMgr()->GetRootActor(); + return (RootActor) ? RootActor->GetDefaultCamera()->GetStereoOffset() : EDisplayClusterEyeStereoOffset::None; +} + +USceneComponent* URWTHVRClusterUtilities::GetClusterComponent(const FString& Name) +{ + const ADisplayClusterRootActor* RootActor = IDisplayCluster::Get().GetGameMgr()->GetRootActor(); + return (RootActor) ? RootActor->GetComponentByName<USceneComponent>(Name) : nullptr; +} + +USceneComponent* URWTHVRClusterUtilities::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; + } +} diff --git a/Source/RWTHVRCluster/Public/CAVEOverlay/CAVEOverlayController.h b/Source/RWTHVRCluster/Public/CAVEOverlay/CAVEOverlayController.h index 7740522..6d9828e 100644 --- a/Source/RWTHVRCluster/Public/CAVEOverlay/CAVEOverlayController.h +++ b/Source/RWTHVRCluster/Public/CAVEOverlay/CAVEOverlayController.h @@ -8,7 +8,10 @@ #include "CAVEOverlayController.generated.h" +class UMotionControllerComponent; +class UCameraComponent; class ARWTHVRPawn; + DECLARE_LOG_CATEGORY_EXTERN(LogCAVEOverlay, Log, All); /** @@ -91,9 +94,12 @@ private: // Only calculate positions and material values when we're fully initialized. bool bInitialized = false; - // Reference to the currently active pawn that we're tracking positions of. + // Reference to the currently active pawn's camera and hands that we're tracking positions of. + UPROPERTY() + UCameraComponent* PawnCamera; + UPROPERTY() - ARWTHVRPawn* VRPawn; + TArray<UMotionControllerComponent*> MotionControllers; // Cluster Events FOnClusterEventJsonListener ClusterEventListenerDelegate; @@ -114,13 +120,17 @@ public: UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "CAVEOverlay", meta = (AllowPrivateAccess = "true")) UStaticMeshComponent* Tape; - // Right Hand Sign Static Mesh component. Reference to static mesh needs to be set in the corresponding BP. + // Right Hand Sign Static Mesh Reference UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "CAVEOverlay", meta = (AllowPrivateAccess = "true")) - UStaticMeshComponent* SignRightHand; + UStaticMesh* SignStaticMesh; - // Left Hand Sign Static Mesh component. Reference to static mesh needs to be set in the corresponding BP. + // Static Mesh Components for all tracked MotionControllers UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "CAVEOverlay", meta = (AllowPrivateAccess = "true")) - UStaticMeshComponent* SignLeftHand; + TArray<UStaticMeshComponent*> SignsStaticMeshComponents; + + // Static Mesh Components for all tracked MotionControllers + UPROPERTY() + TArray<UMaterialInstanceDynamic*> SignsMIDs; // UI Overlay UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "CAVEOverlay") @@ -136,10 +146,4 @@ public: // Dynamic Materials to control opacity UPROPERTY() UMaterialInstanceDynamic* TapeMaterialDynamic; - - UPROPERTY() - UMaterialInstanceDynamic* RightSignMaterialDynamic; - - UPROPERTY() - UMaterialInstanceDynamic* LeftSignMaterialDynamic; }; diff --git a/Source/RWTHVRCluster/Public/Utility/RWTHVRClusterUtilities.h b/Source/RWTHVRCluster/Public/Utility/RWTHVRClusterUtilities.h new file mode 100644 index 0000000..cf2ad5a --- /dev/null +++ b/Source/RWTHVRCluster/Public/Utility/RWTHVRClusterUtilities.h @@ -0,0 +1,69 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Components/DisplayClusterCameraComponent.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "UObject/ConstructorHelpers.h" + +#include "RWTHVRClusterUtilities.generated.h" + + +/** + * Custom log category for all ClusterPlugin related components + */ +DECLARE_LOG_CATEGORY_EXTERN(ClusterPlugin, 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") +}; + +UCLASS() +class RWTHVRCLUSTER_API URWTHVRClusterUtilities : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + static bool IsRoomMountedMode(); + UFUNCTION(BlueprintPure, Category = "DisplayCluster|Platform") + static bool IsCave(); + 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 EDisplayClusterEyeStereoOffset 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); +}; diff --git a/Source/RWTHVRCluster/RWTHVRCluster.Build.cs b/Source/RWTHVRCluster/RWTHVRCluster.Build.cs index 12b3d9a..98f6200 100644 --- a/Source/RWTHVRCluster/RWTHVRCluster.Build.cs +++ b/Source/RWTHVRCluster/RWTHVRCluster.Build.cs @@ -26,8 +26,8 @@ public class RWTHVRCluster : ModuleRules "Slate", "SlateCore", "LiveLink", - "DisplayCluster", - "RWTHVRToolkit" + "HeadMountedDisplay", // required for MotionControllerComp + "DisplayCluster" } ); -- GitLab