diff --git a/Content/BP_VirtualRealityPawn.uasset b/Content/BP_VirtualRealityPawn.uasset index d3ab67b1d5e35aa30a09884d6070777d908d76db..d712f953c0b6b7115269a19395b8ec42c8998aed 100644 Binary files a/Content/BP_VirtualRealityPawn.uasset and b/Content/BP_VirtualRealityPawn.uasset differ diff --git a/Content/RWTHVRGameMode.uasset b/Content/RWTHVRGameMode.uasset index 6e3b11f843990e7dd4f9418596c8987874829aa1..bbe367b340d7b98310000099bb7ffa8e3e088b0f 100644 Binary files a/Content/RWTHVRGameMode.uasset and b/Content/RWTHVRGameMode.uasset differ diff --git a/Source/RWTHVRToolkit/Private/Pawn/ContinuousMovementComponent.cpp b/Source/RWTHVRToolkit/Private/Pawn/ContinuousMovementComponent.cpp index af46f80934f02d316a5680f35a1a0a684c809cd3..cf8213737de30479629cc84aeb80dca1cae7a04e 100644 --- a/Source/RWTHVRToolkit/Private/Pawn/ContinuousMovementComponent.cpp +++ b/Source/RWTHVRToolkit/Private/Pawn/ContinuousMovementComponent.cpp @@ -1,6 +1,5 @@ // Fill out your copyright notice in the Description page of Project Settings. - #include "Pawn/ContinuousMovementComponent.h" #include "EnhancedInputComponent.h" @@ -9,6 +8,7 @@ #include "GameFramework/PlayerController.h" #include "Pawn/VRPawnInputConfig.h" #include "Utility/VirtualRealityUtilities.h" +#include "MotionControllerComponent.h" void UContinuousMovementComponent::BeginPlay() { @@ -71,16 +71,6 @@ void UContinuousMovementComponent::SetupInputActions() // bind additional functions for desktop rotations if (UVirtualRealityUtilities::IsDesktopMode()) { - APlayerController* PC = Cast<APlayerController>(VRPawn->GetController()); - if (PC) - { - PC->bShowMouseCursor = true; - PC->bEnableClickEvents = true; - PC->bEnableMouseOverEvents = true; - } else - { - UE_LOG(LogTemp,Error,TEXT("PC Player Controller is invalid")); - } EI->BindAction(DesktopRotation, ETriggerEvent::Started, this, &UContinuousMovementComponent::StartDesktopRotation); EI->BindAction(DesktopRotation, ETriggerEvent::Completed, this, &UContinuousMovementComponent::EndDesktopRotation); EI->BindAction(MoveUp, ETriggerEvent::Triggered,this,&UContinuousMovementComponent::OnBeginUp); @@ -102,8 +92,8 @@ void UContinuousMovementComponent::OnBeginMove(const FInputActionValue& Value) const bool bGazeDirected = UVirtualRealityUtilities::IsDesktopMode() || SteeringMode == EVRSteeringModes::STEER_GAZE_DIRECTED; - const FVector ForwardDir = bGazeDirected ? VRPawn->Head->GetForwardVector() : MovementHand->GetForwardVector(); - const FVector RightDir = bGazeDirected ? VRPawn->Head->GetRightVector() : MovementHand->GetRightVector(); + const FVector ForwardDir = bGazeDirected ? VRPawn->HeadCameraComponent->GetForwardVector() : MovementHand->GetForwardVector(); + const FVector RightDir = bGazeDirected ? VRPawn->HeadCameraComponent->GetRightVector() : MovementHand->GetRightVector(); if (VRPawn->Controller != nullptr) { @@ -133,10 +123,6 @@ void UContinuousMovementComponent::OnBeginTurn(const FInputActionValue& Value) if (TurnValue.X != 0.f) { VRPawn->AddControllerYawInput(TurnRateFactor * TurnValue.X); - if (UVirtualRealityUtilities::IsDesktopMode()) - { - UpdateRightHandForDesktopInteraction(); - } } if (TurnValue.Y != 0.f) @@ -144,7 +130,6 @@ void UContinuousMovementComponent::OnBeginTurn(const FInputActionValue& Value) if (UVirtualRealityUtilities::IsDesktopMode() && bApplyDesktopRotation) { VRPawn->AddControllerPitchInput(TurnRateFactor * -TurnValue.Y); - SetCameraOffset(); } } } @@ -162,28 +147,6 @@ void UContinuousMovementComponent::OnBeginSnapTurn(const FInputActionValue& Valu } } -void UContinuousMovementComponent::SetCameraOffset() const -{ - // this also incorporates the BaseEyeHeight, if set as static offset, - // rotations are still around the center of the pawn (on the floor), so pitch rotations look weird - FVector Location; - FRotator Rotation; - VRPawn->GetActorEyesViewPoint(Location, Rotation); - VRPawn->CameraComponent->SetWorldLocationAndRotation(Location, Rotation); -} - -void UContinuousMovementComponent::UpdateRightHandForDesktopInteraction() -{ - APlayerController* PC = Cast<APlayerController>(VRPawn->GetController()); - if (PC) - { - FVector MouseLocation, MouseDirection; - PC->DeprojectMousePositionToWorld(MouseLocation, MouseDirection); - FRotator HandOrientation = MouseDirection.ToOrientationRotator(); - VRPawn->RightHand->SetWorldRotation(HandOrientation); - } -} - void UContinuousMovementComponent::OnBeginUp(const FInputActionValue& Value) { const float MoveValue = Value.Get<FVector2D>().X; diff --git a/Source/RWTHVRToolkit/Private/Pawn/TeleportationComponent.cpp b/Source/RWTHVRToolkit/Private/Pawn/TeleportationComponent.cpp index b775efcac63506828c7112e3675cad196a652ce5..0fd9bd5a3e170d723b28c39fd047d575c1780542 100644 --- a/Source/RWTHVRToolkit/Private/Pawn/TeleportationComponent.cpp +++ b/Source/RWTHVRToolkit/Private/Pawn/TeleportationComponent.cpp @@ -13,6 +13,7 @@ #include "Kismet/GameplayStatics.h" #include "NiagaraDataInterfaceArrayFunctionLibrary.h" #include "Utility/VirtualRealityUtilities.h" +#include "MotionControllerComponent.h" // Sets default values for this component's properties UTeleportationComponent::UTeleportationComponent() @@ -273,7 +274,7 @@ void UTeleportationComponent::SetCameraOffset() const FVector Location; FRotator Rotation; VRPawn->GetActorEyesViewPoint(Location, Rotation); - VRPawn->CameraComponent->SetWorldLocationAndRotation(Location, Rotation); + VRPawn->HeadCameraComponent->SetWorldLocationAndRotation(Location, Rotation); } void UTeleportationComponent::UpdateRightHandForDesktopInteraction() diff --git a/Source/RWTHVRToolkit/Private/Pawn/UniversalTrackedComponent.cpp b/Source/RWTHVRToolkit/Private/Pawn/UniversalTrackedComponent.cpp deleted file mode 100644 index 18320ca4b9a3a1378918f7df114507550102d0f5..0000000000000000000000000000000000000000 --- a/Source/RWTHVRToolkit/Private/Pawn/UniversalTrackedComponent.cpp +++ /dev/null @@ -1,220 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - -#include "Pawn/UniversalTrackedComponent.h" - -#include "Camera/CameraComponent.h" -#include "Utility/VirtualRealityUtilities.h" -#include "Roles/LiveLinkTransformTypes.h" -#include "ILiveLinkClient.h" - -DEFINE_LOG_CATEGORY(LogUniversalTrackedComponent); - -// Sets default values for this component's properties -UUniversalTrackedComponent::UUniversalTrackedComponent() -{ - PrimaryComponentTick.bCanEverTick = true; - PrimaryComponentTick.TickGroup = ETickingGroup::TG_PrePhysics; - bAlwaysUseLiveLinkTracking = false; -} - -void UUniversalTrackedComponent::SetShowDeviceModel(const bool bShowControllerModel) -{ - if (!UVirtualRealityUtilities::IsHeadMountedMode() || TrackedComponent == nullptr) return; - - bShowDeviceModelInHMD = bShowControllerModel; - Cast<UMotionControllerComponent>(TrackedComponent)->SetShowDeviceModel(bShowDeviceModelInHMD); -} - -void UUniversalTrackedComponent::BeginPlay() -{ - Super::BeginPlay(); - - if (UVirtualRealityUtilities::IsHeadMountedMode()) - { - if (ProxyType == ETrackedComponentType::TCT_HEAD) - { - TrackedComponent = GetOwner()->FindComponentByClass<UCameraComponent>(); - } - else - { - /* Spawn Motion Controller Components in HMD Mode*/ - UMotionControllerComponent* MotionController = Cast<UMotionControllerComponent>(GetOwner()->AddComponentByClass(UMotionControllerComponent::StaticClass(), false, FTransform::Identity, false)); - GetOwner()->AddInstanceComponent(MotionController); // makes it show correctly in the hierarchy - - // Todo: If bAlwaysUseLiveLinkTracking is true, those should be sourced by LiveLink - switch(ProxyType) - { - case ETrackedComponentType::TCT_TRACKER_1: - MotionController->SetTrackingMotionSource(FName("Special_1")); - break; - case ETrackedComponentType::TCT_TRACKER_2: - MotionController->SetTrackingMotionSource(FName("Special_2")); - break; - case ETrackedComponentType::TCT_RIGHT_HAND: - MotionController->SetTrackingMotionSource(FName("Right")); - break; - case ETrackedComponentType::TCT_LEFT_HAND: - MotionController->SetTrackingMotionSource(FName("Left")); - break; - default: break; - } - MotionController->SetShowDeviceModel(bShowDeviceModelInHMD); - - TrackedComponent = MotionController; - } - if (TrackedComponent) - AttachToComponent(Cast<USceneComponent>(TrackedComponent), FAttachmentTransformRules::SnapToTargetIncludingScale); - - } - // Check for bAlwaysUseLiveLinkTracking here too in case we want to use LiveLink in Editor/Desktop mode. - else if (UVirtualRealityUtilities::IsDesktopMode() && !bAlwaysUseLiveLinkTracking) - { - switch(ProxyType) - { - case ETrackedComponentType::TCT_RIGHT_HAND: - case ETrackedComponentType::TCT_LEFT_HAND: - case ETrackedComponentType::TCT_HEAD: - TrackedComponent = GetOwner()->FindComponentByClass<UCameraComponent>(); //All are attached to camera - break; - case ETrackedComponentType::TCT_TRACKER_1: - case ETrackedComponentType::TCT_TRACKER_2: - TrackedComponent = GetOwner()->GetRootComponent(); - break; - } - if (TrackedComponent) AttachToComponent(Cast<USceneComponent>(TrackedComponent), FAttachmentTransformRules::SnapToTargetIncludingScale); - - } - // If we're either in the cave or using LiveLink, set it up here. Might want to differentiate between the two cases later on, - // for now both should be equivalent. Maybe set up some additional stuff automatically like presets etc. - else if (UVirtualRealityUtilities::IsRoomMountedMode() || bAlwaysUseLiveLinkTracking) - { - // Instead of using the clumsy LiveLinkComponentController, we just directly check the LiveLink Data in Tick later on. - // Set up this Component to Tick, and check whether Subject and Role is set. - - // TODO: Check for AttachmentType and automatically get the respective Subject/Role. Need to investigate how those are called by DTrack. - TrackedComponent = this; - bUseLiveLinkTracking = true; // override this in case someone forgot to set it. - if (SubjectRepresentation.Subject.IsNone() || SubjectRepresentation.Role == nullptr) - { - UE_LOG(LogUniversalTrackedComponent, Error, TEXT("UUniversalTrackedComponent::BeginPlay(): No LiveLink Subject or Role is set! Tracking will not work")); - } - SetComponentTickEnabled(true); - } -} - -void UUniversalTrackedComponent::PostLoad() -{ - Super::PostLoad(); - - // This is required in PostLoad (and theoretically in OnComponentCreated too) because just setting this in - // the constructor or PostInitializeProperties will load the CDO's property values. - // Just calling it in BeginPlay() won't let us see the LiveLink preview in the editor. - bTickInEditor = bAlwaysUseLiveLinkTracking; - PrimaryComponentTick.bStartWithTickEnabled = bAlwaysUseLiveLinkTracking; -} - -#if WITH_EDITOR -void UUniversalTrackedComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) -{ - // Catch the change to bAlwaysUseLiveLinkTracking, and disable/enable tick respectively for in-editor tracking. - const FName PropertyName(PropertyChangedEvent.GetPropertyName()); - if(PropertyName == GET_MEMBER_NAME_CHECKED(UUniversalTrackedComponent, bAlwaysUseLiveLinkTracking)) - { - bTickInEditor = bAlwaysUseLiveLinkTracking; - SetComponentTickEnabled(bAlwaysUseLiveLinkTracking); - } - Super::PostEditChangeProperty(PropertyChangedEvent); -} -#endif - - -void UUniversalTrackedComponent::TickComponent(const float DeltaTime, const ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) -{ - Super::TickComponent(DeltaTime, TickType, ThisTickFunction); - - // Check whether we need to update the component with new LiveLink data. This is either the case if - // nDisplay uses bUseLiveLinkTracking (Play Mode), or if bAlwaysUseLiveLinkTracking is true, then we tick both in Play Mode and Editor. - const bool bEvaluateLiveLink = bUseLiveLinkTracking || bAlwaysUseLiveLinkTracking; - - if(!bEvaluateLiveLink || SubjectRepresentation.Subject.IsNone() || SubjectRepresentation.Role == nullptr) - { - return; - } - - // Get the LiveLink interface and evaluate the current existing frame data for the given Subject and Role. - ILiveLinkClient& LiveLinkClient = IModularFeatures::Get().GetModularFeature<ILiveLinkClient>(ILiveLinkClient::ModularFeatureName); - FLiveLinkSubjectFrameData SubjectData; - const bool bHasValidData = LiveLinkClient.EvaluateFrame_AnyThread(SubjectRepresentation.Subject, SubjectRepresentation.Role, SubjectData); - - if(!bHasValidData) - return; - - // Assume we are using a Transform Role to track the components! This is a slightly dangerous assumption, and could be further improved. - const FLiveLinkTransformStaticData* StaticData = SubjectData.StaticData.Cast<FLiveLinkTransformStaticData>(); - const FLiveLinkTransformFrameData* FrameData = SubjectData.FrameData.Cast<FLiveLinkTransformFrameData>(); - - if (StaticData && FrameData) - { - // Finally, apply the transform to this component according to the static data. - ApplyLiveLinkTransform(FrameData->Transform, *StaticData); - } -} - -UMotionControllerComponent* UUniversalTrackedComponent::GetMotionControllerComponentByMotionSource(EControllerHand MotionSource) const -{ - TArray<UActorComponent*> Components; - GetOwner()->GetComponents(UMotionControllerComponent::StaticClass(),Components); - return Cast<UMotionControllerComponent>( - Components.FindByPredicate([&MotionSource](UActorComponent* Current) - { - switch(MotionSource) - { - case EControllerHand::Right: - return Cast<UMotionControllerComponent>(Current)->MotionSource == FName("Right"); - case EControllerHand::Left: - return Cast<UMotionControllerComponent>(Current)->MotionSource == FName("Left"); - default: return true; - } - })[0] - ); -} - -void UUniversalTrackedComponent::ApplyLiveLinkTransform(const FTransform& Transform, const FLiveLinkTransformStaticData& StaticData) -{ - if (bUseLocation && StaticData.bIsLocationSupported) - { - if (bWorldTransform) - { - this->SetWorldLocation(Transform.GetLocation(), bSweep, nullptr, bTeleport ? ETeleportType::TeleportPhysics : ETeleportType::ResetPhysics); - } - else - { - this->SetRelativeLocation(Transform.GetLocation(), bSweep, nullptr, bTeleport ? ETeleportType::TeleportPhysics : ETeleportType::ResetPhysics); - } - } - - if (bUseRotation && StaticData.bIsRotationSupported) - { - if (bWorldTransform) - { - this->SetWorldRotation(Transform.GetRotation(), bSweep, nullptr, bTeleport ? ETeleportType::TeleportPhysics : ETeleportType::ResetPhysics); - } - else - { - this->SetRelativeRotation(Transform.GetRotation(), bSweep, nullptr, bTeleport ? ETeleportType::TeleportPhysics : ETeleportType::ResetPhysics); - } - } - - if (bUseScale && StaticData.bIsScaleSupported) - { - if (bWorldTransform) - { - this->SetWorldScale3D(Transform.GetScale3D()); - } - else - { - this->SetRelativeScale3D(Transform.GetScale3D()); - } - } -} diff --git a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp index 40b293ecdbc5da4bbf250bb32bafd97986000a4a..1d6ac46295f055d38c206a6757c0e0771d8fe103 100644 --- a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp +++ b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp @@ -85,7 +85,7 @@ void UVRPawnMovement::SetHeadComponent(USceneComponent* NewHeadComponent) CapsuleColliderComponent->SetupAttachment(HeadComponent); const float HalfHeight = 80.0f; //this is just an initial value to look good in editor CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, HalfHeight); - CapsuleColliderComponent->SetWorldLocation(FVector(0.0f, 0.0f,HalfHeight)); + CapsuleColliderComponent->SetWorldLocation(FVector(0.0f, 0.0f,-HalfHeight)); } void UVRPawnMovement::SetCapsuleColliderToUserSize() diff --git a/Source/RWTHVRToolkit/Private/Pawn/VirtualRealityPawn.cpp b/Source/RWTHVRToolkit/Private/Pawn/VirtualRealityPawn.cpp index 324da1efed37f74ea74239577279b479bc192854..8cd1fd2992101b33b8f872833c8ecad8134798d8 100644 --- a/Source/RWTHVRToolkit/Private/Pawn/VirtualRealityPawn.cpp +++ b/Source/RWTHVRToolkit/Private/Pawn/VirtualRealityPawn.cpp @@ -4,9 +4,9 @@ #include "Engine/LocalPlayer.h" #include "GameFramework/PlayerController.h" -#include "Pawn/UniversalTrackedComponent.h" #include "EnhancedInputComponent.h" #include "EnhancedInputSubsystems.h" +#include "MotionControllerComponent.h" #include "Camera/CameraComponent.h" #include "Pawn/VRPawnInputConfig.h" #include "Pawn/VRPawnMovement.h" @@ -23,45 +23,54 @@ AVirtualRealityPawn::AVirtualRealityPawn(const FObjectInitializer& ObjectInitial SetRootComponent(CreateDefaultSubobject<USceneComponent>(TEXT("Origin"))); - CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera")); - CameraComponent->SetupAttachment(RootComponent); - CameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, BaseEyeHeight)); //so it is rendered correctly in editor + HeadCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera")); + HeadCameraComponent->SetupAttachment(RootComponent); + HeadCameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, BaseEyeHeight)); //so it is rendered correctly in editor - Head = CreateDefaultSubobject<UUniversalTrackedComponent>(TEXT("Head")); - Head->ProxyType = ETrackedComponentType::TCT_HEAD; - Head->SetupAttachment(RootComponent); - - CapsuleRotationFix = CreateDefaultSubobject<USceneComponent>(TEXT("CapsuleRotationFix")); - CapsuleRotationFix->SetUsingAbsoluteRotation(true); - CapsuleRotationFix->SetupAttachment(Head); - PawnMovement = CreateDefaultSubobject<UVRPawnMovement>(TEXT("Pawn Movement")); PawnMovement->SetUpdatedComponent(RootComponent); - PawnMovement->SetHeadComponent(CapsuleRotationFix); + PawnMovement->SetHeadComponent(HeadCameraComponent); - RightHand = CreateDefaultSubobject<UUniversalTrackedComponent>(TEXT("Right Hand")); - RightHand->ProxyType = ETrackedComponentType::TCT_RIGHT_HAND; - RightHand->AttachementType = EAttachementType::AT_FLYSTICK; + RightHand = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("Right Hand MCC")); RightHand->SetupAttachment(RootComponent); - LeftHand = CreateDefaultSubobject<UUniversalTrackedComponent>(TEXT("Left Hand")); - LeftHand->ProxyType = ETrackedComponentType::TCT_LEFT_HAND; - LeftHand->AttachementType = EAttachementType::AT_HANDTARGET; + LeftHand = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("Left Hand MCC")); LeftHand->SetupAttachment(RootComponent); BasicVRInteraction = CreateDefaultSubobject<UBasicVRInteractionComponent>(TEXT("Basic VR Interaction")); BasicVRInteraction->Initialize(RightHand); - +} + +void AVirtualRealityPawn::Tick(float DeltaSeconds) +{ + Super::Tick(DeltaSeconds); + + if (UVirtualRealityUtilities::IsDesktopMode()) + { + SetCameraOffset(); + UpdateRightHandForDesktopInteraction(); + } } void AVirtualRealityPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) { - const APlayerController* PlayerController = Cast<APlayerController>(GetController()); + APlayerController* PlayerController = Cast<APlayerController>(GetController()); + if (!PlayerController) { + UE_LOG(LogTemp, Error, TEXT("PC Player Controller is invalid")); + return; + } UEnhancedInputLocalPlayerSubsystem* InputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()); if(!InputSubsystem) { UE_LOG(Toolkit,Error,TEXT("[VirtualRealiytPawn.cpp] InputSubsystem IS NOT VALID")); } + + if(UVirtualRealityUtilities::IsDesktopMode()) + { + PlayerController->bShowMouseCursor = true; + PlayerController->bEnableClickEvents = true; + PlayerController->bEnableMouseOverEvents = true; + } InputSubsystem->ClearAllMappings(); @@ -74,8 +83,30 @@ void AVirtualRealityPawn::SetupPlayerInputComponent(UInputComponent* PlayerInput EI->BindAction(Fire, ETriggerEvent::Started, this, &AVirtualRealityPawn::OnBeginFire); EI->BindAction(Fire, ETriggerEvent::Completed, this, &AVirtualRealityPawn::OnEndFire); - EI->BindAction(ToggleNavigationMode,ETriggerEvent::Started,this,&AVirtualRealityPawn::OnToggleNavigationMode); - + EI->BindAction(ToggleNavigationMode,ETriggerEvent::Started,this,&AVirtualRealityPawn::OnToggleNavigationMode); +} + +void AVirtualRealityPawn::UpdateRightHandForDesktopInteraction() +{ + APlayerController* PC = Cast<APlayerController>(GetController()); + if (PC) + { + FVector MouseLocation, MouseDirection; + PC->DeprojectMousePositionToWorld(MouseLocation, MouseDirection); + FRotator HandOrientation = MouseDirection.ToOrientationRotator(); + RightHand->SetWorldRotation(HandOrientation); + RightHand->SetRelativeLocation(HeadCameraComponent->GetRelativeLocation()); + } +} + +void AVirtualRealityPawn::SetCameraOffset() const +{ + // this also incorporates the BaseEyeHeight, if set as static offset, + // rotations are still around the center of the pawn (on the floor), so pitch rotations look weird + FVector Location; + FRotator Rotation; + GetActorEyesViewPoint(Location, Rotation); + HeadCameraComponent->SetWorldLocationAndRotation(Location, Rotation); } // legacy grabbing diff --git a/Source/RWTHVRToolkit/Private/RWTHVRToolkit.cpp b/Source/RWTHVRToolkit/Private/RWTHVRToolkit.cpp index 2cfd23435ddcd3ae6a0f31c66e3a0b07d1f9b0d4..c335e156518151ccef60a186e8a4bc67b0c55903 100644 --- a/Source/RWTHVRToolkit/Private/RWTHVRToolkit.cpp +++ b/Source/RWTHVRToolkit/Private/RWTHVRToolkit.cpp @@ -4,12 +4,24 @@ void FRWTHVRToolkitModule::StartupModule () { + + IModularFeatures& ModularFeatures = IModularFeatures::Get(); + if (ModularFeatures.IsModularFeatureAvailable(ILiveLinkClient::ModularFeatureName)) + { + FLiveLinkClient* LiveLinkClient = static_cast<FLiveLinkClient*>(&IModularFeatures::Get().GetModularFeature<ILiveLinkClient>( + ILiveLinkClient::ModularFeatureName)); + LiveLinkMotionController = MakeUnique<FLiveLinkMotionControllerFix>(*LiveLinkClient); + LiveLinkMotionController->RegisterController(); + } + ConsoleActivation.Register(); } void FRWTHVRToolkitModule::ShutdownModule() { - ConsoleActivation.Unregister(); + ConsoleActivation.Unregister(); + if (LiveLinkMotionController) + LiveLinkMotionController->UnregisterController(); } diff --git a/Source/RWTHVRToolkit/Public/Fixes/LiveLinkMotionControllerFix.h b/Source/RWTHVRToolkit/Public/Fixes/LiveLinkMotionControllerFix.h new file mode 100644 index 0000000000000000000000000000000000000000..3e8bb02b97f5c333281fd3b23df5109f9370b1ce --- /dev/null +++ b/Source/RWTHVRToolkit/Public/Fixes/LiveLinkMotionControllerFix.h @@ -0,0 +1,221 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "Features/IModularFeatures.h" +#include "IMotionController.h" +#include "LiveLinkClient.h" + +#include "Roles/LiveLinkTransformRole.h" +#include "Roles/LiveLinkTransformTypes.h" + + +#define LOCTEXT_NAMESPACE "LiveLinkMotionController" + + +class FLiveLinkMotionControllerFix : public IMotionController +{ + // Internal structure for caching enumerated data + struct FLiveLinkMotionControllerEnumeratedSource + { + // Subject key for talking to live link + FLiveLinkSubjectKey SubjectKey; + + // MotionSource name for interacting with Motion Controller system + FName MotionSource; + + FLiveLinkMotionControllerEnumeratedSource(const FLiveLinkSubjectKey& Key, FName MotionSourceName) : SubjectKey(Key), MotionSource(MotionSourceName) {} + }; + + // Built array of Live Link Sources to give to Motion Controller system + TArray<FLiveLinkMotionControllerEnumeratedSource> EnumeratedSources; + +public: + FLiveLinkMotionControllerFix(FLiveLinkClient& InClient) : Client(InClient) + { + BuildSourceData(); + OnSubjectsChangedHandle = Client.OnLiveLinkSubjectsChanged().AddRaw(this, &FLiveLinkMotionControllerFix::OnSubjectsChangedHandler); + WildcardSource = FGuid::NewGuid(); + } + + ~FLiveLinkMotionControllerFix() + { + Client.OnLiveLinkSubjectsChanged().Remove(OnSubjectsChangedHandle); + OnSubjectsChangedHandle.Reset(); + } + + void RegisterController() + { + IModularFeatures::Get().RegisterModularFeature(GetModularFeatureName(), this); + } + + void UnregisterController() + { + IModularFeatures::Get().UnregisterModularFeature(GetModularFeatureName(), this); + } + + virtual bool GetControllerOrientationAndPosition(const int32 ControllerIndex, const FName MotionSource, FRotator& OutOrientation, FVector& OutPosition, float) const override + { + FLiveLinkSubjectKey SubjectKey = GetSubjectKeyFromMotionSource(MotionSource); + + FLiveLinkSubjectFrameData FrameData; + if (Client.EvaluateFrame_AnyThread(SubjectKey.SubjectName, ULiveLinkTransformRole::StaticClass(), FrameData)) + { + if (FLiveLinkTransformFrameData* TransformFrameData = FrameData.FrameData.Cast<FLiveLinkTransformFrameData>()) + { + OutPosition = TransformFrameData->Transform.GetLocation(); + OutOrientation = TransformFrameData->Transform.GetRotation().Rotator(); + return true; + } + } + return false; + } + + virtual bool GetControllerOrientationAndPosition(const int32 ControllerIndex, const FName MotionSource, FRotator& OutOrientation, FVector& OutPosition, bool& OutbProvidedLinearVelocity, FVector& OutLinearVelocity, bool& OutbProvidedAngularVelocity, FVector& OutAngularVelocityAsAxisAndLength, bool& OutbProvidedLinearAcceleration, FVector& OutLinearAcceleration, float WorldToMetersScale) const override + { + OutbProvidedLinearVelocity = false; + OutbProvidedAngularVelocity = false; + OutbProvidedLinearAcceleration = false; + return GetControllerOrientationAndPosition(ControllerIndex, MotionSource, OutOrientation, OutPosition, WorldToMetersScale); + } + + virtual bool GetControllerOrientationAndPositionForTime(const int32 ControllerIndex, const FName MotionSource, FTimespan Time, bool& OutTimeWasUsed, FRotator& OutOrientation, FVector& OutPosition, bool& OutbProvidedLinearVelocity, FVector& OutLinearVelocity, bool& OutbProvidedAngularVelocity, FVector& OutAngularVelocityAsAxisAndLength, bool& OutbProvidedLinearAcceleration, FVector& OutLinearAcceleration, float WorldToMetersScale) const override + { + OutTimeWasUsed = false; + OutbProvidedLinearVelocity = false; + OutbProvidedAngularVelocity = false; + OutbProvidedLinearAcceleration = false; + return GetControllerOrientationAndPosition(ControllerIndex, MotionSource, OutOrientation, OutPosition, WorldToMetersScale); + } + + float GetCustomParameterValue(const FName MotionSource, FName ParameterName, bool& bValueFound) const override + { + FLiveLinkSubjectKey SubjectKey = GetSubjectKeyFromMotionSource(MotionSource); + + FLiveLinkSubjectFrameData EvaluatedData; + if (Client.EvaluateFrame_AnyThread(SubjectKey.SubjectName, ULiveLinkBasicRole::StaticClass(), EvaluatedData)) + { + if (EvaluatedData.FrameData.GetBaseData()) + { + FLiveLinkBaseFrameData& BaseFrameData = *EvaluatedData.FrameData.GetBaseData(); + + float FoundValue = 0.f; + if (EvaluatedData.StaticData.GetBaseData()->FindPropertyValue(BaseFrameData, ParameterName, FoundValue)) + { + bValueFound = true; + return FoundValue; + } + } + } + + bValueFound = false; + return 0.f; + } + + virtual ETrackingStatus GetControllerTrackingStatus(const int32 ControllerIndex, const FName MotionSource) const override + { + FLiveLinkSubjectKey SubjectKey = GetSubjectKeyFromMotionSource(MotionSource); + + FLiveLinkSubjectFrameData EvaluatedData; + if (Client.EvaluateFrame_AnyThread(SubjectKey.SubjectName, ULiveLinkBasicRole::StaticClass(), EvaluatedData)) + { + return ETrackingStatus::Tracked; + } + return ETrackingStatus::NotTracked; + } + + + virtual FName GetMotionControllerDeviceTypeName() const override + { + static FName LiveLinkMotionControllerName(TEXT("LiveLinkMotionController")); + return LiveLinkMotionControllerName; + } + + // Builds cached source data for passing to motion controller system + void BuildSourceData() + { + TArray<FLiveLinkSubjectKey> SubjectKeys = Client.GetSubjects(true, true); + + TMap<FGuid, TArray<FName>> LiveLinkSubjects; + LiveLinkSubjects.Add(WildcardSource); + + for (const FLiveLinkSubjectKey& Subject : SubjectKeys) + { + LiveLinkSubjects.FindOrAdd(Subject.Source).Add(Subject.SubjectName); + LiveLinkSubjects.FindChecked(WildcardSource).Add(Subject.SubjectName); + } + + TArray<FGuid> SourceGuids; + LiveLinkSubjects.GenerateKeyArray(SourceGuids); + + typedef TPair<FGuid, FText> FHeaderEntry; + TArray<FHeaderEntry> Headers; + Headers.Reserve(SourceGuids.Num()); + + for (const FGuid& Source : SourceGuids) + { + FText SourceName = (Source == WildcardSource) ? LOCTEXT("LiveLinkAnySource", "Any") : Client.GetSourceType(Source); + Headers.Emplace(Source, SourceName); + } + + { + FGuid& CaptureWildcardSource = WildcardSource; + Headers.Sort([CaptureWildcardSource](const FHeaderEntry& A, const FHeaderEntry& B) { return A.Key == CaptureWildcardSource || A.Value.CompareToCaseIgnored(B.Value) <= 0; }); + } + + //Build EnumeratedSources data + EnumeratedSources.Reset(); + EnumeratedSources.Reserve(SubjectKeys.Num()); + + for (const FHeaderEntry& Header : Headers) + { + TArray<FName> Subjects = LiveLinkSubjects.FindChecked(Header.Key); + Subjects.Sort(FNameLexicalLess()); + for (FName Subject : Subjects) + { + FName FullName = *FString::Format(TEXT("{0} ({1})"), { Subject.ToString(), Header.Value.ToString() }); + EnumeratedSources.Emplace(FLiveLinkSubjectKey(Header.Key, Subject), FullName); + } + } + } + + virtual void EnumerateSources(TArray<FMotionControllerSource>& Sources) const override + { + for (const FLiveLinkMotionControllerEnumeratedSource& Source : EnumeratedSources) + { + FMotionControllerSource SourceDesc(Source.MotionSource); +#if WITH_EDITOR + static const FName LiveLinkCategoryName(TEXT("LiveLink")); + SourceDesc.EditorCategory = LiveLinkCategoryName; +#endif + Sources.Add(SourceDesc); + } + } + + virtual bool GetHandJointPosition(const FName MotionSource, int jointIndex, FVector& OutPosition) const override { return false; } + +private: + FLiveLinkSubjectKey GetSubjectKeyFromMotionSource(FName MotionSource) const + { + const FLiveLinkMotionControllerEnumeratedSource* EnumeratedSource = EnumeratedSources.FindByPredicate([&](const FLiveLinkMotionControllerEnumeratedSource& Item) { return Item.MotionSource == MotionSource; }); + if (EnumeratedSource) + { + return EnumeratedSource->SubjectKey; + } + return FLiveLinkSubjectKey(FGuid(), MotionSource); + } + + // Registered with the client and called when client's subjects change + void OnSubjectsChangedHandler() { BuildSourceData(); } + + // Reference to the live link client + FLiveLinkClient& Client; + + // Handle to delegate registered with client so we can update when subject state changes + FDelegateHandle OnSubjectsChangedHandle; + + // Wildcard source, we don't care about the source itself, just the subject name + FGuid WildcardSource; +}; + +#undef LOCTEXT_NAMESPACE diff --git a/Source/RWTHVRToolkit/Public/Pawn/ContinuousMovementComponent.h b/Source/RWTHVRToolkit/Public/Pawn/ContinuousMovementComponent.h index 38bada0c11d392d7e011b4d9d2830c3399197bf2..7ef4d501d729ad9fb800bba06571bb7ee57add3f 100644 --- a/Source/RWTHVRToolkit/Public/Pawn/ContinuousMovementComponent.h +++ b/Source/RWTHVRToolkit/Public/Pawn/ContinuousMovementComponent.h @@ -89,10 +89,10 @@ public: private: UPROPERTY() - UUniversalTrackedComponent* MovementHand; + UMotionControllerComponent* MovementHand; UPROPERTY() - UUniversalTrackedComponent* RotationHand; + UMotionControllerComponent* RotationHand; UPROPERTY() class UInputMappingContext* IMCMovement; @@ -101,11 +101,5 @@ private: UPROPERTY() AVirtualRealityPawn* VRPawn; - - /** - * Fixes camera rotation in desktop mode. - */ - void SetCameraOffset() const; - void UpdateRightHandForDesktopInteraction(); }; diff --git a/Source/RWTHVRToolkit/Public/Pawn/TeleportationComponent.h b/Source/RWTHVRToolkit/Public/Pawn/TeleportationComponent.h index 4521fdd7322e5880f533d674b73ee992ffbf926e..003f6a96f01ca828e2da9f1cde509a01a913d194 100644 --- a/Source/RWTHVRToolkit/Public/Pawn/TeleportationComponent.h +++ b/Source/RWTHVRToolkit/Public/Pawn/TeleportationComponent.h @@ -117,10 +117,10 @@ public: private: UPROPERTY() - UUniversalTrackedComponent* TeleportationHand; + UMotionControllerComponent* TeleportationHand; UPROPERTY() - UUniversalTrackedComponent* RotationHand; + UMotionControllerComponent* RotationHand; UPROPERTY() class UInputMappingContext* IMCMovement; diff --git a/Source/RWTHVRToolkit/Public/Pawn/UniversalTrackedComponent.h b/Source/RWTHVRToolkit/Public/Pawn/UniversalTrackedComponent.h deleted file mode 100644 index 254863d0dec9643ccae920fbc3e6a6b0453d9d0e..0000000000000000000000000000000000000000 --- a/Source/RWTHVRToolkit/Public/Pawn/UniversalTrackedComponent.h +++ /dev/null @@ -1,128 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once - -#include "CoreMinimal.h" -#include "LiveLinkRole.h" -#include "Components/SceneComponent.h" -#include "MotionControllerComponent.h" -#include "UniversalTrackedComponent.generated.h" - -DECLARE_LOG_CATEGORY_EXTERN(LogUniversalTrackedComponent, Log, All); - -struct FLiveLinkTransformStaticData; - -UENUM(BlueprintType) -enum class ETrackedComponentType : uint8 -{ - TCT_TRACKER_1 UMETA(DisplayName = "VIVE Tracker 1"), - TCT_TRACKER_2 UMETA(DisplayName = "VIVE Tracker 2"), - TCT_RIGHT_HAND UMETA(DisplayName = "Right Hand"), - TCT_LEFT_HAND UMETA(DisplayName = "Left Hand"), - TCT_HEAD UMETA(DisplayName = "Head") -}; - -UENUM(BlueprintType) -enum class EAttachementType : uint8 -{ - AT_NONE UMETA(DisplayName = "Not attached"), - AT_HANDTARGET UMETA(DisplayName = "To the right/left hand target"), - AT_FLYSTICK UMETA(DisplayName = "To the Flystick"), - AT_CUSTOM_SUBJECT UMETA(DisplayName = "Custom LiveLink Subject|Role") -}; - -/* - * Acts as an intelligent proxy object, when attached to a Pawn class - * Attaches itself to the specified controller (Camera, MotionController, TrackedDevice) once they are available - * Behaves according to the ETrackedComponentType which has to be set before starting - */ - -UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) -class RWTHVRTOOLKIT_API UUniversalTrackedComponent : public USceneComponent -{ - GENERATED_BODY() - -public: - // Sets default values for this component's properties - UUniversalTrackedComponent(); - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tracking") - ETrackedComponentType ProxyType = ETrackedComponentType::TCT_HEAD; - - /** Set whether LiveLink should always be enabled, even in the editor or desktop mode. Overrides bUseLiveLinkTracking.*/ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tracking", meta = (DisplayName = "Override all tracking with LiveLink")) - bool bAlwaysUseLiveLinkTracking; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tracking|nDisplay") - EAttachementType AttachementType = EAttachementType::AT_NONE; - - /** Set whether nDisplay should use LiveLink tracking. Requires a valid Subject Representation!*/ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tracking|nDisplay") - bool bUseLiveLinkTracking = true; - - /** Set the LiveLink Subject Representation to be used by this component. */ - UPROPERTY(BlueprintReadOnly, EditAnywhere, Category="Tracking|nDisplay|LiveLink", meta = (EditCondition = "AttachementType==EAttachementType::AT_CUSTOM_SUBJECT")) - FLiveLinkSubjectRepresentation SubjectRepresentation; - - /** Set the transform of the component in world space of in its local reference frame. */ - UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Tracking|nDisplay|LiveLink", meta = (EditCondition = "AttachementType==EAttachementType::AT_CUSTOM_SUBJECT")) - bool bWorldTransform = false; - - /** Whether we should set the owning actor's location with the value coming from live link. */ - UPROPERTY(EditAnywhere, Category = "Tracking|nDisplay|LiveLink", meta = (EditCondition = "AttachementType==EAttachementType::AT_CUSTOM_SUBJECT")) - bool bUseLocation = true; - - /** Whether we should set the owning actor's rotation with the value coming from live link. */ - UPROPERTY(EditAnywhere, Category = "Tracking|nDisplay|LiveLink", meta = (EditCondition = "AttachementType==EAttachementType::AT_CUSTOM_SUBJECT")) - bool bUseRotation = true; - - /** Whether we should set the owning actor's scale with the value coming from live link. */ - UPROPERTY(EditAnywhere, Category = "Tracking|nDisplay|LiveLink", meta = (EditCondition = "AttachementType==EAttachementType::AT_CUSTOM_SUBJECT")) - bool bUseScale = true; - - /** - * Whether we sweep to the destination location, triggering overlaps along the way and stopping short of the target if blocked by something. - * Only the root component is swept and checked for blocking collision, child components move without sweeping. If collision is off, this has no effect. - */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Tracking|nDisplay|LiveLink", meta = (EditCondition = "AttachementType==EAttachementType::AT_CUSTOM_SUBJECT")) - bool bSweep = false; - - /** - * Whether we teleport the physics state (if physics collision is enabled for this object). - * If true, physics velocity for this object is unchanged (so ragdoll parts are not affected by change in location). - * If false, physics velocity is updated based on the change in position (affecting ragdoll parts). - * If CCD is on and not teleporting, this will affect objects along the entire sweep volume. - */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Tracking|nDisplay|LiveLink", meta = (EditCondition = "AttachementType==EAttachementType::AT_CUSTOM_SUBJECT")) - bool bTeleport = true; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tracking|HMD", BlueprintSetter=SetShowDeviceModel) - bool bShowDeviceModelInHMD = true; - - UFUNCTION(BlueprintSetter) - void SetShowDeviceModel(const bool bShowControllerModel); - - //~ Begin SceneComponent Interface - virtual void BeginPlay() override; - virtual void PostLoad() override; - virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; - -#if WITH_EDITOR - virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; -#endif - //~ End SceneComponent Interface - -private: - - /** - * @brief Helper function that applies the LiveLink data to this component. Taken from the LiveLink Transform Controller. - * @param Transform Transform read from the LiveLink Client. - * @param StaticData Static data from the LiveLink Subject, defines what is and isn't supported. - * @return void - */ - void ApplyLiveLinkTransform(const FTransform& Transform, const FLiveLinkTransformStaticData& StaticData); - - USceneComponent* TrackedComponent = nullptr; - UMotionControllerComponent* GetMotionControllerComponentByMotionSource(EControllerHand MotionSource) const; - -}; diff --git a/Source/RWTHVRToolkit/Public/Pawn/VirtualRealityPawn.h b/Source/RWTHVRToolkit/Public/Pawn/VirtualRealityPawn.h index 21ba56b809f3c642243650ce6df4774f12cf19c0..36bd4233955a8fdbb0f3eb13d4d6e5a56bed92c1 100644 --- a/Source/RWTHVRToolkit/Public/Pawn/VirtualRealityPawn.h +++ b/Source/RWTHVRToolkit/Public/Pawn/VirtualRealityPawn.h @@ -4,13 +4,12 @@ #include "BasicVRInteractionComponent.h" #include "CoreMinimal.h" -#include "UniversalTrackedComponent.h" #include "Pawn/VRPawnMovement.h" #include "VirtualRealityPawn.generated.h" class UCameraComponent; class ULiveLinkComponentController; - +class UMotionControllerComponent; /** * @@ -21,32 +20,26 @@ class RWTHVRTOOLKIT_API AVirtualRealityPawn : public APawn GENERATED_BODY() public: AVirtualRealityPawn(const FObjectInitializer& ObjectInitializer); + + virtual void Tick(float DeltaSeconds) override; - /* Proxy */ - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn|Proxy Objects") - UUniversalTrackedComponent* Head; - - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn|Proxy Objects") - UUniversalTrackedComponent* RightHand; + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn|MotionControllers") + UMotionControllerComponent* RightHand; - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn|Proxy Objects") - UUniversalTrackedComponent* LeftHand; + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn|MotionControllers") + UMotionControllerComponent* LeftHand; /* Interaction */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn|Interaction") UBasicVRInteractionComponent* BasicVRInteraction; /* Movement */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement") - UVRPawnMovement* PawnMovement; - - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn|Movement") - USceneComponent* CapsuleRotationFix; + UVRPawnMovement* PawnMovement; /* CameraComponent */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn|Camera") - UCameraComponent* CameraComponent; + UCameraComponent* HeadCameraComponent; protected: @@ -72,4 +65,10 @@ protected: UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Pawn|Input") class UInputAction* ToggleNavigationMode; + /** + * Fixes camera rotation in desktop mode. + */ + void SetCameraOffset() const; + void UpdateRightHandForDesktopInteraction(); + }; diff --git a/Source/RWTHVRToolkit/Public/RWTHVRToolkit.h b/Source/RWTHVRToolkit/Public/RWTHVRToolkit.h index fa1843ded226c6fcd438ea1e22f1acdc83632906..c1d682b4edf75806bf8d48debc8eb4bafbb0b06c 100644 --- a/Source/RWTHVRToolkit/Public/RWTHVRToolkit.h +++ b/Source/RWTHVRToolkit/Public/RWTHVRToolkit.h @@ -2,6 +2,7 @@ #include "CoreMinimal.h" #include "Fixes/ActivateConsoleInShipping.h" +#include "Fixes/LiveLinkMotionControllerFix.h" class FRWTHVRToolkitModule : public IModuleInterface @@ -11,5 +12,6 @@ public: virtual void ShutdownModule() override; private: - FActivateConsoleInShipping ConsoleActivation; + FActivateConsoleInShipping ConsoleActivation; + TUniquePtr<FLiveLinkMotionControllerFix> LiveLinkMotionController; };