diff --git a/Content/BP_VirtualRealityPawn.uasset b/Content/BP_VirtualRealityPawn.uasset new file mode 100644 index 0000000000000000000000000000000000000000..de6a205060c6131ae6724b3246de1ef437f2b6ec Binary files /dev/null and b/Content/BP_VirtualRealityPawn.uasset differ diff --git a/Content/Components/Movement/ContinuousMovementComponent.uasset b/Content/Components/Movement/ContinuousMovementComponent.uasset new file mode 100644 index 0000000000000000000000000000000000000000..9e004acae980e0517082dc4b43a66d1606b46956 Binary files /dev/null and b/Content/Components/Movement/ContinuousMovementComponent.uasset differ diff --git a/Content/Components/Movement/IMC_MovementLeftHand.uasset b/Content/Components/Movement/IMC_MovementLeftHand.uasset new file mode 100644 index 0000000000000000000000000000000000000000..6762960518fa317c48d0effb0e173a6f09f2e763 Binary files /dev/null and b/Content/Components/Movement/IMC_MovementLeftHand.uasset differ diff --git a/Content/Components/Movement/IMC_MovementRightHand.uasset b/Content/Components/Movement/IMC_MovementRightHand.uasset new file mode 100644 index 0000000000000000000000000000000000000000..c848335f083150cd0393bb64dac0495a9aaa949e Binary files /dev/null and b/Content/Components/Movement/IMC_MovementRightHand.uasset differ diff --git a/Content/Input/IMC_RWTH_Base.uasset b/Content/Input/IMC_RWTH_Base.uasset new file mode 100644 index 0000000000000000000000000000000000000000..9f2e4864bdd16cd30a452191d9ac298cd579e460 Binary files /dev/null and b/Content/Input/IMC_RWTH_Base.uasset differ diff --git a/Content/Input/InputActions/IA_DesktopRotation.uasset b/Content/Input/InputActions/IA_DesktopRotation.uasset new file mode 100644 index 0000000000000000000000000000000000000000..b1abe311863a7a6f076c4e2c2bd2d39dd306386d Binary files /dev/null and b/Content/Input/InputActions/IA_DesktopRotation.uasset differ diff --git a/Content/Input/InputActions/IA_Fire.uasset b/Content/Input/InputActions/IA_Fire.uasset new file mode 100644 index 0000000000000000000000000000000000000000..aefe36aa0a33175490fbaf9b0b3a07ec2944a358 Binary files /dev/null and b/Content/Input/InputActions/IA_Fire.uasset differ diff --git a/Content/Input/InputActions/IA_Grab.uasset b/Content/Input/InputActions/IA_Grab.uasset new file mode 100644 index 0000000000000000000000000000000000000000..d35442dff6c80662339e90fd71f0d084a42e7be6 Binary files /dev/null and b/Content/Input/InputActions/IA_Grab.uasset differ diff --git a/Content/Input/InputActions/IA_LookUpRate.uasset b/Content/Input/InputActions/IA_LookUpRate.uasset new file mode 100644 index 0000000000000000000000000000000000000000..852c1b411c071ef591c0b9108d9b4d9c856c6c09 Binary files /dev/null and b/Content/Input/InputActions/IA_LookUpRate.uasset differ diff --git a/Content/Input/InputActions/IA_Move.uasset b/Content/Input/InputActions/IA_Move.uasset new file mode 100644 index 0000000000000000000000000000000000000000..2423ae65f120424dbd02c40d85d242ccd9832e18 Binary files /dev/null and b/Content/Input/InputActions/IA_Move.uasset differ diff --git a/Content/Input/InputActions/IA_MoveForward.uasset b/Content/Input/InputActions/IA_MoveForward.uasset new file mode 100644 index 0000000000000000000000000000000000000000..1ada444b1705599dd9a36f797c71fba69c0524df Binary files /dev/null and b/Content/Input/InputActions/IA_MoveForward.uasset differ diff --git a/Content/Input/InputActions/IA_MoveRight.uasset b/Content/Input/InputActions/IA_MoveRight.uasset new file mode 100644 index 0000000000000000000000000000000000000000..05aa4aff2ace49f2aaf67558affa2877d43a1f69 Binary files /dev/null and b/Content/Input/InputActions/IA_MoveRight.uasset differ diff --git a/Content/Input/InputActions/IA_MoveUp.uasset b/Content/Input/InputActions/IA_MoveUp.uasset new file mode 100644 index 0000000000000000000000000000000000000000..6f38343e58e4bddd4b43fd94258fa9c4929a4765 Binary files /dev/null and b/Content/Input/InputActions/IA_MoveUp.uasset differ diff --git a/Content/Input/InputActions/IA_Turn.uasset b/Content/Input/InputActions/IA_Turn.uasset new file mode 100644 index 0000000000000000000000000000000000000000..480126b060800e5a122f0f0f2f6c6bdefc7a7469 Binary files /dev/null and b/Content/Input/InputActions/IA_Turn.uasset differ diff --git a/Content/Input/InputActions/IA_TurnRate.uasset b/Content/Input/InputActions/IA_TurnRate.uasset new file mode 100644 index 0000000000000000000000000000000000000000..d5e0153f09345b265bacba76e67eafc4ee5e5775 Binary files /dev/null and b/Content/Input/InputActions/IA_TurnRate.uasset differ diff --git a/Content/Input/RWTHVRPawnInputConfig.uasset b/Content/Input/RWTHVRPawnInputConfig.uasset new file mode 100644 index 0000000000000000000000000000000000000000..ad08269ac07bbd98e93ddee7572a9262a0a32c63 Binary files /dev/null and b/Content/Input/RWTHVRPawnInputConfig.uasset differ diff --git a/Content/RWTHVRGameMode.uasset b/Content/RWTHVRGameMode.uasset index ea7345658120735d8871d7bf4d19a7abddd7c4d5..c3904e554dfa66dd200f0e61e318efac52b09a3a 100644 Binary files a/Content/RWTHVRGameMode.uasset and b/Content/RWTHVRGameMode.uasset differ diff --git a/Content/TestMap.umap b/Content/TestMap.umap new file mode 100644 index 0000000000000000000000000000000000000000..7e7113328a4bc24bfdcbdc7f61a83af3a8cc305f Binary files /dev/null and b/Content/TestMap.umap differ diff --git a/Source/RWTHVRToolkit/Private/Pawn/ContinuousMovementComponent.cpp b/Source/RWTHVRToolkit/Private/Pawn/ContinuousMovementComponent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c931c7c281a5050d4a11ea5e9e22621548fa5ac2 --- /dev/null +++ b/Source/RWTHVRToolkit/Private/Pawn/ContinuousMovementComponent.cpp @@ -0,0 +1,393 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Pawn/ContinuousMovementComponent.h" + +#include "DrawDebugHelpers.h" +#include "EnhancedInputComponent.h" +#include "EnhancedInputSubsystems.h" +#include "Camera/CameraComponent.h" +#include "Components/CapsuleComponent.h" +#include "Engine/LocalPlayer.h" +#include "GameFramework/PlayerController.h" +#include "Pawn/VRPawnInputConfig.h" +#include "Utility/VirtualRealityUtilities.h" + +UContinuousMovementComponent::UContinuousMovementComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) +{ + CapsuleColliderComponent = CreateDefaultSubobject<UCapsuleComponent>(TEXT("CapsuleCollider")); + CapsuleColliderComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); + CapsuleColliderComponent->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Overlap); + CapsuleColliderComponent->SetCollisionResponseToChannel(ECollisionChannel::ECC_WorldStatic, ECollisionResponse::ECR_Block); + CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, 80.0f); + + VRPawn = Cast<AVirtualRealityPawn>(GetOwner()); +} + +void UContinuousMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction){ + + SetCapsuleColliderToUserSize(); + + FVector PositionChange = GetPendingInputVector(); + + if (NavigationMode == EVRNavigationModes::NAV_WALK) + { + PositionChange.Z = 0.0f; + ConsumeInputVector(); + AddInputVector(PositionChange); + } + + if(NavigationMode == EVRNavigationModes::NAV_FLY || NavigationMode == EVRNavigationModes::NAV_WALK) + { + MoveByGravityOrStepUp(DeltaTime); + CheckForPhysWalkingCollision(); + + if(CheckForVirtualMovCollision(PositionChange, DeltaTime)) + { + ConsumeInputVector(); + } + } + + if(NavigationMode == EVRNavigationModes::NAV_NONE) + { + ConsumeInputVector(); + } + + Super::TickComponent(DeltaTime, TickType, ThisTickFunction); + + LastHeadPosition = HeadComponent->GetComponentLocation(); +} + +void UContinuousMovementComponent::BeginPlay() +{ + Super::BeginPlay(); + + SetUpdatedComponent(VRPawn->GetRootComponent()); + SetHeadComponent(VRPawn->CapsuleRotationFix); + + SetupInputActions(); + +} + +bool UContinuousMovementComponent::CheckForVirtualMovCollision(FVector PositionChange, float DeltaTime) +{ + FVector ProbePosition = PositionChange.GetSafeNormal() * GetMaxSpeed() * DeltaTime; + FHitResult FHitResultVR; + CapsuleColliderComponent->AddWorldOffset(ProbePosition, true, &FHitResultVR); + if (FVector::Distance(FHitResultVR.Location, CapsuleColliderComponent->GetComponentLocation()) < CapsuleColliderComponent->GetScaledCapsuleRadius()) + { + return true; + } + return false; +} + +void UContinuousMovementComponent::SetHeadComponent(USceneComponent* NewHeadComponent) +{ + HeadComponent = 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)); +} + +void UContinuousMovementComponent::SetCapsuleColliderToUserSize() +{ + float CharachterSize = abs(UpdatedComponent->GetComponentLocation().Z - HeadComponent->GetComponentLocation().Z); + + if (CharachterSize > MaxStepHeight) + { + float ColliderHeight = CharachterSize - MaxStepHeight; + float ColliderHalfHeight = ColliderHeight / 2.0f; + if (ColliderHalfHeight <= CapsuleRadius) + {//Make the collider to a Sphere + CapsuleColliderComponent->SetCapsuleSize(ColliderHalfHeight, ColliderHalfHeight); + } + else + {//Make the collider to a Capsule + CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, ColliderHalfHeight); + } + + CapsuleColliderComponent->SetWorldLocation(HeadComponent->GetComponentLocation()); + CapsuleColliderComponent->AddWorldOffset(FVector(0, 0, -ColliderHalfHeight)); + CapsuleColliderComponent->SetWorldRotation(FRotator(0, 0, 1)); + } + else + { + CapsuleColliderComponent->SetWorldLocation(HeadComponent->GetComponentLocation()); + CapsuleColliderComponent->SetWorldRotation(FRotator(0, 0, 1)); + } +} + +void UContinuousMovementComponent::CheckForPhysWalkingCollision() +{ + FVector CurrentHeadPosition = HeadComponent->GetComponentLocation(); + FVector Direction = CurrentHeadPosition - LastHeadPosition; + FHitResult FHitResultPhys; + CapsuleColliderComponent->AddWorldOffset(Direction, true, &FHitResultPhys); + + if (FHitResultPhys.bBlockingHit) + { + UpdatedComponent->AddLocalOffset(FHitResultPhys.Normal*FHitResultPhys.PenetrationDepth); + } +} + +void UContinuousMovementComponent::MoveByGravityOrStepUp(float DeltaSeconds) +{ + FVector StartLineTraceUnderCollider = CapsuleColliderComponent->GetComponentLocation(); + StartLineTraceUnderCollider.Z -= CapsuleColliderComponent->GetScaledCapsuleHalfHeight(); + FHitResult HitDetailsMultiLineTrace = CreateMultiLineTrace(FVector(0, 0, -1), StartLineTraceUnderCollider, CapsuleColliderComponent->GetScaledCapsuleRadius() / 4.0f, false); + float DistanceDifference = abs(MaxStepHeight - HitDetailsMultiLineTrace.Distance); + //Going up (in Fly and Walk Mode) + if ((HitDetailsMultiLineTrace.bBlockingHit && HitDetailsMultiLineTrace.Distance < MaxStepHeight)) + { + ShiftVertically(DistanceDifference, UpSteppingAcceleration, DeltaSeconds, 1); + } + //Gravity (only in Walk Mode) + else if (NavigationMode==EVRNavigationModes::NAV_WALK && ((HitDetailsMultiLineTrace.bBlockingHit && HitDetailsMultiLineTrace.Distance > MaxStepHeight) || (HitDetailsMultiLineTrace.GetActor() == nullptr && HitDetailsMultiLineTrace.Distance != -1.0f))) + { + ShiftVertically(DistanceDifference, GravityAcceleration, DeltaSeconds, -1); + } +} + +void UContinuousMovementComponent::ShiftVertically(float DiffernceDistance, float VerticalAcceleration, float DeltaSeconds, int Direction) +{ + VerticalSpeed += VerticalAcceleration * DeltaSeconds; + if (VerticalSpeed*DeltaSeconds < DiffernceDistance) + { + UpdatedComponent->AddLocalOffset(FVector(0.f, 0.f, Direction * VerticalSpeed * DeltaSeconds)); + } + else + { + UpdatedComponent->AddLocalOffset(FVector(0.f, 0.f, Direction * DiffernceDistance)); + VerticalSpeed = 0; + } +} + +void UContinuousMovementComponent::SetupInputActions() +{ + // simple way of changing the handedness + if(bMoveWithRightHand) + { + MovementHand = VRPawn->RightHand; + RotationHand = VRPawn->LeftHand; + IMCMovement = IMCMovementRight; + } else + { + MovementHand = VRPawn->LeftHand; + RotationHand = VRPawn->RightHand; + IMCMovement = IMCMovementLeft; + } + + const APlayerController* PlayerController = Cast<APlayerController>(VRPawn->GetController()); + UEnhancedInputLocalPlayerSubsystem* InputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()); + if(!InputSubsystem) + { + UE_LOG(LogTemp,Error,TEXT("InputSubsystem IS NOT VALID")); + return; + } + + InputSubsystem->ClearAllMappings(); + + // add Input Mapping context + InputSubsystem->AddMappingContext(IMCMovement,0); + + UEnhancedInputComponent* EI = Cast<UEnhancedInputComponent>(VRPawn->InputComponent); + if(!EI) + { + UE_LOG(LogTemp,Error,TEXT("Cannot cast Input Component to Enhanced Inpu Component in VRPawnMovement")); + return; + } + + // walking + EI->BindAction(InputActions->Move, ETriggerEvent::Triggered, this, &UContinuousMovementComponent::OnBeginMove); + + // turning + if(bSnapTurn && !UVirtualRealityUtilities::IsDesktopMode()) + { + EI->BindAction(InputActions->Turn, ETriggerEvent::Started, this, &UContinuousMovementComponent::OnBeginSnapTurn); + } else + { + EI->BindAction(InputActions->Turn, ETriggerEvent::Triggered, this, &UContinuousMovementComponent::OnBeginTurn); + } + + // bind functions for desktop rotations only on holding down right mouse + if (UVirtualRealityUtilities::IsDesktopMode()) + { + APlayerController* PC = Cast<APlayerController>(VRPawn->GetController()); + if (PC) + { + PC->bShowMouseCursor = true; + PC->bEnableClickEvents = true; + PC->bEnableMouseOverEvents = true; + } + EI->BindAction(InputActions->DesktopRotation, ETriggerEvent::Started, this, &UContinuousMovementComponent::StartDesktopRotation); + EI->BindAction(InputActions->DesktopRotation, ETriggerEvent::Completed, this, &UContinuousMovementComponent::EndDesktopRotation); + } +} + + + +FHitResult UContinuousMovementComponent::CreateLineTrace(FVector Direction, const FVector Start, bool Visibility) +{ + //Re-initialize hit info + FHitResult HitDetails = FHitResult(ForceInit); + + FVector End = ((Direction * 1000.f) + Start); + // additional trace parameters + FCollisionQueryParams TraceParams(FName(TEXT("InteractTrace")), true, NULL); + TraceParams.bTraceComplex = true; //to use complex collision on whatever we interact with to provide better precision. + TraceParams.bReturnPhysicalMaterial = true; //to provide details about the physical material, if one exists on the thing we hit, to come back in our hit result. + + if (Visibility) + DrawDebugLine(GetWorld(), Start, End, FColor::Green, false, 1, 0, 1); + + if (GetWorld()->LineTraceSingleByChannel(HitDetails, Start, End, ECC_Visibility, TraceParams)) + { + if (HitDetails.bBlockingHit) + { + } + } + return HitDetails; +} + +FHitResult UContinuousMovementComponent::CreateMultiLineTrace(FVector Direction, const FVector Start, float Radius, bool Visibility) +{ + TArray<FVector> StartVectors; + TArray<FHitResult> OutHits; + FHitResult HitDetailsMultiLineTrace; + HitDetailsMultiLineTrace.Distance = -1.0f;//(Distance=-1) not existing, but to know if this Variable not Initialized(when all Traces not compatible) + + StartVectors.Add(Start); //LineTraceCenter + StartVectors.Add(Start + FVector(0, -Radius, 0)); //LineTraceLeft + StartVectors.Add(Start + FVector(0, +Radius, 0)); //LineTraceRight + StartVectors.Add(Start + FVector(+Radius, 0, 0)); //LineTraceFront + StartVectors.Add(Start + FVector(-Radius, 0, 0)); //LineTraceBehind + + bool IsBlockingHitAndSameActor = true; + bool IsAllNothingHiting = true; + // loop through TArray + for (FVector& Vector : StartVectors) + { + FHitResult OutHit = CreateLineTrace(Direction, Vector, Visibility); + OutHits.Add(OutHit); + IsBlockingHitAndSameActor &= (OutHit.GetActor() == OutHits[0].GetActor()); //If all Hiting the same Object, then you are (going up/down) or (walking) + IsAllNothingHiting &= (OutHit.GetActor() == nullptr); //If all Hiting nothing, then you are falling + } + + if (IsBlockingHitAndSameActor || IsAllNothingHiting) + HitDetailsMultiLineTrace = OutHits[0]; + + return HitDetailsMultiLineTrace; +} + +void UContinuousMovementComponent::StartDesktopRotation() +{ + bApplyDesktopRotation = true; +} + +void UContinuousMovementComponent::EndDesktopRotation() +{ + bApplyDesktopRotation = false; +} + +void UContinuousMovementComponent::OnBeginMove(const FInputActionValue& Value) +{ + const FVector ForwardDir = UVirtualRealityUtilities::IsDesktopMode() ? VRPawn->Head->GetForwardVector() : MovementHand->GetForwardVector(); + const FVector RightDir = UVirtualRealityUtilities::IsDesktopMode() ? VRPawn->Head->GetRightVector() : MovementHand->GetRightVector(); + + if (VRPawn->Controller != nullptr) + { + const FVector2D MoveValue = Value.Get<FVector2D>(); + const FRotator MovementRotation(0, VRPawn->Controller->GetControlRotation().Yaw, 0); + + // Forward/Backward direction + if (MoveValue.X != 0.f) + { + VRPawn->AddMovementInput(ForwardDir, MoveValue.X); + } + + // Right/Left direction + if (MoveValue.Y != 0.f) + { + VRPawn->AddMovementInput(RightDir, MoveValue.Y); + } + } +} + +void UContinuousMovementComponent::OnBeginTurn(const FInputActionValue& Value) +{ + if(UVirtualRealityUtilities::IsDesktopMode() && !bApplyDesktopRotation) return; + if (VRPawn->Controller != nullptr) + { + const FVector2D TurnValue = Value.Get<FVector2D>(); + + if (TurnValue.X != 0.f) + { + VRPawn->AddControllerYawInput(TurnRateFactor * TurnValue.X); + if (UVirtualRealityUtilities::IsDesktopMode()) + { + UpdateRightHandForDesktopInteraction(); + } + } + + if (TurnValue.Y != 0.f) + { + if (UVirtualRealityUtilities::IsDesktopMode() && bApplyDesktopRotation) + { + VRPawn->AddControllerPitchInput(TurnRateFactor * TurnValue.Y); + SetCameraOffset(); + } + } + } +} + +void UContinuousMovementComponent::OnBeginSnapTurn(const FInputActionValue& Value) +{ + const FVector2D TurnValue = Value.Get<FVector2D>(); + + if (TurnValue.X != 0.f) + { + VRPawn->AddControllerYawInput(SnapTurnAngle); + } +} + +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>(GetController()); + if (PC) + { + FVector MouseLocation, MouseDirection; + PC->DeprojectMousePositionToWorld(MouseLocation, MouseDirection); + FRotator HandOrientation = MouseDirection.ToOrientationRotator(); + VRPawn->RightHand->SetWorldRotation(HandOrientation); + } +} + +/*void UContinuousMovementComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + FName PropertyName = (PropertyChangedEvent.Property != NULL) ? PropertyChangedEvent.Property->GetFName() : NAME_None; + + if (PropertyName == GET_MEMBER_NAME_CHECKED(AVirtualRealityPawn, bMoveWithRightHand) || + PropertyName == GET_MEMBER_NAME_CHECKED(AVirtualRealityPawn, bSnapTurn)) + { + // if we want to change input bindings, we need to setup the player input again to load a different mapping context, + // or assign a different function to an input action. + // This is automatically done by restarting the pawn(calling SetupPlayerInputComponent() directly results in crashes) + // only do this in the editor + #if WITH_EDITOR + VRPawn->Restart(); +#endif + } +}*/ \ No newline at end of file diff --git a/Source/RWTHVRToolkit/Private/Pawn/VRPawnInputConfig.cpp b/Source/RWTHVRToolkit/Private/Pawn/VRPawnInputConfig.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5444bd50ff7178c8a118f670e8eeaaf3b7fa4b41 --- /dev/null +++ b/Source/RWTHVRToolkit/Private/Pawn/VRPawnInputConfig.cpp @@ -0,0 +1,5 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Pawn/VRPawnInputConfig.h" + diff --git a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp deleted file mode 100644 index 4b32da4736f6a6e3ea7b9423fac8540926985126..0000000000000000000000000000000000000000 --- a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp +++ /dev/null @@ -1,193 +0,0 @@ - -#include "Pawn/VRPawnMovement.h" -#include "DrawDebugHelpers.h" - -UVRPawnMovement::UVRPawnMovement(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) -{ - CapsuleColliderComponent = CreateDefaultSubobject<UCapsuleComponent>(TEXT("CapsuleCollider")); - CapsuleColliderComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); - CapsuleColliderComponent->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Overlap); - CapsuleColliderComponent->SetCollisionResponseToChannel(ECollisionChannel::ECC_WorldStatic, ECollisionResponse::ECR_Block); - CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, 80.0f); -} - -void UVRPawnMovement::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction){ - - SetCapsuleColliderToUserSize(); - - FVector PositionChange = GetPendingInputVector(); - - if (NavigationMode == EVRNavigationModes::NAV_WALK) - { - PositionChange.Z = 0.0f; - ConsumeInputVector(); - AddInputVector(PositionChange); - } - - if(NavigationMode == EVRNavigationModes::NAV_FLY || NavigationMode == EVRNavigationModes::NAV_WALK) - { - MoveByGravityOrStepUp(DeltaTime); - CheckForPhysWalkingCollision(); - - if(CheckForVirtualMovCollision(PositionChange, DeltaTime)) - { - ConsumeInputVector(); - } - } - - if(NavigationMode == EVRNavigationModes::NAV_NONE) - { - ConsumeInputVector(); - } - - Super::TickComponent(DeltaTime, TickType, ThisTickFunction); - - LastHeadPosition = HeadComponent->GetComponentLocation(); -} - -bool UVRPawnMovement::CheckForVirtualMovCollision(FVector PositionChange, float DeltaTime) -{ - FVector ProbePosition = PositionChange.GetSafeNormal() * GetMaxSpeed() * DeltaTime; - FHitResult FHitResultVR; - CapsuleColliderComponent->AddWorldOffset(ProbePosition, true, &FHitResultVR); - if (FVector::Distance(FHitResultVR.Location, CapsuleColliderComponent->GetComponentLocation()) < CapsuleColliderComponent->GetScaledCapsuleRadius()) - { - return true; - } - return false; -} - -void UVRPawnMovement::SetHeadComponent(USceneComponent* NewHeadComponent) -{ - HeadComponent = 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)); -} - -void UVRPawnMovement::SetCapsuleColliderToUserSize() -{ - float CharachterSize = abs(UpdatedComponent->GetComponentLocation().Z - HeadComponent->GetComponentLocation().Z); - - if (CharachterSize > MaxStepHeight) - { - float ColliderHeight = CharachterSize - MaxStepHeight; - float ColliderHalfHeight = ColliderHeight / 2.0f; - if (ColliderHalfHeight <= CapsuleRadius) - {//Make the collider to a Sphere - CapsuleColliderComponent->SetCapsuleSize(ColliderHalfHeight, ColliderHalfHeight); - } - else - {//Make the collider to a Capsule - CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, ColliderHalfHeight); - } - - CapsuleColliderComponent->SetWorldLocation(HeadComponent->GetComponentLocation()); - CapsuleColliderComponent->AddWorldOffset(FVector(0, 0, -ColliderHalfHeight)); - CapsuleColliderComponent->SetWorldRotation(FRotator(0, 0, 1)); - } - else - { - CapsuleColliderComponent->SetWorldLocation(HeadComponent->GetComponentLocation()); - CapsuleColliderComponent->SetWorldRotation(FRotator(0, 0, 1)); - } -} - -void UVRPawnMovement::CheckForPhysWalkingCollision() -{ - FVector CurrentHeadPosition = HeadComponent->GetComponentLocation(); - FVector Direction = CurrentHeadPosition - LastHeadPosition; - FHitResult FHitResultPhys; - CapsuleColliderComponent->AddWorldOffset(Direction, true, &FHitResultPhys); - - if (FHitResultPhys.bBlockingHit) - { - UpdatedComponent->AddLocalOffset(FHitResultPhys.Normal*FHitResultPhys.PenetrationDepth); - } -} - -void UVRPawnMovement::MoveByGravityOrStepUp(float DeltaSeconds) -{ - FVector StartLineTraceUnderCollider = CapsuleColliderComponent->GetComponentLocation(); - StartLineTraceUnderCollider.Z -= CapsuleColliderComponent->GetScaledCapsuleHalfHeight(); - FHitResult HitDetailsMultiLineTrace = CreateMultiLineTrace(FVector(0, 0, -1), StartLineTraceUnderCollider, CapsuleColliderComponent->GetScaledCapsuleRadius() / 4.0f, false); - float DistanceDifference = abs(MaxStepHeight - HitDetailsMultiLineTrace.Distance); - //Going up (in Fly and Walk Mode) - if ((HitDetailsMultiLineTrace.bBlockingHit && HitDetailsMultiLineTrace.Distance < MaxStepHeight)) - { - ShiftVertically(DistanceDifference, UpSteppingAcceleration, DeltaSeconds, 1); - } - //Gravity (only in Walk Mode) - else if (NavigationMode==EVRNavigationModes::NAV_WALK && ((HitDetailsMultiLineTrace.bBlockingHit && HitDetailsMultiLineTrace.Distance > MaxStepHeight) || (HitDetailsMultiLineTrace.GetActor() == nullptr && HitDetailsMultiLineTrace.Distance != -1.0f))) - { - ShiftVertically(DistanceDifference, GravityAcceleration, DeltaSeconds, -1); - } -} - -void UVRPawnMovement::ShiftVertically(float DiffernceDistance, float VerticalAcceleration, float DeltaSeconds, int Direction) -{ - VerticalSpeed += VerticalAcceleration * DeltaSeconds; - if (VerticalSpeed*DeltaSeconds < DiffernceDistance) - { - UpdatedComponent->AddLocalOffset(FVector(0.f, 0.f, Direction * VerticalSpeed * DeltaSeconds)); - } - else - { - UpdatedComponent->AddLocalOffset(FVector(0.f, 0.f, Direction * DiffernceDistance)); - VerticalSpeed = 0; - } -} - -FHitResult UVRPawnMovement::CreateLineTrace(FVector Direction, const FVector Start, bool Visibility) -{ - //Re-initialize hit info - FHitResult HitDetails = FHitResult(ForceInit); - - FVector End = ((Direction * 1000.f) + Start); - // additional trace parameters - FCollisionQueryParams TraceParams(FName(TEXT("InteractTrace")), true, NULL); - TraceParams.bTraceComplex = true; //to use complex collision on whatever we interact with to provide better precision. - TraceParams.bReturnPhysicalMaterial = true; //to provide details about the physical material, if one exists on the thing we hit, to come back in our hit result. - - if (Visibility) - DrawDebugLine(GetWorld(), Start, End, FColor::Green, false, 1, 0, 1); - - if (GetWorld()->LineTraceSingleByChannel(HitDetails, Start, End, ECC_Visibility, TraceParams)) - { - if (HitDetails.bBlockingHit) - { - } - } - return HitDetails; -} - -FHitResult UVRPawnMovement::CreateMultiLineTrace(FVector Direction, const FVector Start, float Radius, bool Visibility) -{ - TArray<FVector> StartVectors; - TArray<FHitResult> OutHits; - FHitResult HitDetailsMultiLineTrace; - HitDetailsMultiLineTrace.Distance = -1.0f;//(Distance=-1) not existing, but to know if this Variable not Initialized(when all Traces not compatible) - - StartVectors.Add(Start); //LineTraceCenter - StartVectors.Add(Start + FVector(0, -Radius, 0)); //LineTraceLeft - StartVectors.Add(Start + FVector(0, +Radius, 0)); //LineTraceRight - StartVectors.Add(Start + FVector(+Radius, 0, 0)); //LineTraceFront - StartVectors.Add(Start + FVector(-Radius, 0, 0)); //LineTraceBehind - - bool IsBlockingHitAndSameActor = true; - bool IsAllNothingHiting = true; - // loop through TArray - for (FVector& Vector : StartVectors) - { - FHitResult OutHit = CreateLineTrace(Direction, Vector, Visibility); - OutHits.Add(OutHit); - IsBlockingHitAndSameActor &= (OutHit.GetActor() == OutHits[0].GetActor()); //If all Hiting the same Object, then you are (going up/down) or (walking) - IsAllNothingHiting &= (OutHit.GetActor() == nullptr); //If all Hiting nothing, then you are falling - } - - if (IsBlockingHitAndSameActor || IsAllNothingHiting) - HitDetailsMultiLineTrace = OutHits[0]; - - return HitDetailsMultiLineTrace; -} \ No newline at end of file diff --git a/Source/RWTHVRToolkit/Private/Pawn/VirtualRealityPawn.cpp b/Source/RWTHVRToolkit/Private/Pawn/VirtualRealityPawn.cpp index d65ca8ab248d384fd9094b38b0f5cc1936947ccd..3676a7900a0d529223eeb5416d2a9aea6031994c 100644 --- a/Source/RWTHVRToolkit/Private/Pawn/VirtualRealityPawn.cpp +++ b/Source/RWTHVRToolkit/Private/Pawn/VirtualRealityPawn.cpp @@ -2,11 +2,15 @@ #include "Pawn/VirtualRealityPawn.h" -#include "GameFramework/InputSettings.h" -#include "GameFramework/PlayerInput.h" +#include "AITypes.h" +#include "Engine/LocalPlayer.h" +#include "GameFramework/PlayerController.h" #include "Pawn/UniversalTrackedComponent.h" #include "Utility/VirtualRealityUtilities.h" -#include "Pawn/VRPawnMovement.h" +#include "EnhancedInputComponent.h" +#include "EnhancedInputSubsystems.h" +#include "Camera/CameraComponent.h" +#include "Pawn/VRPawnInputConfig.h" AVirtualRealityPawn::AVirtualRealityPawn(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) @@ -15,7 +19,6 @@ AVirtualRealityPawn::AVirtualRealityPawn(const FObjectInitializer& ObjectInitial bUseControllerRotationPitch = true; bUseControllerRotationRoll = true; BaseEyeHeight = 160.0f; - AutoPossessPlayer = EAutoReceiveInput::Player0; // Necessary for receiving motion controller events. SetRootComponent(CreateDefaultSubobject<USceneComponent>(TEXT("Origin"))); @@ -32,14 +35,17 @@ AVirtualRealityPawn::AVirtualRealityPawn(const FObjectInitializer& ObjectInitial CapsuleRotationFix->SetUsingAbsoluteRotation(true); CapsuleRotationFix->SetupAttachment(Head); - PawnMovement = CreateDefaultSubobject<UVRPawnMovement>(TEXT("Pawn Movement")); + /*PawnMovement = CreateDefaultSubobject<UVRPawnMovement>(TEXT("Pawn Movement")); PawnMovement->SetUpdatedComponent(RootComponent); - PawnMovement->SetHeadComponent(CapsuleRotationFix); + PawnMovement->SetHeadComponent(CapsuleRotationFix);*/ RightHand = CreateDefaultSubobject<UUniversalTrackedComponent>(TEXT("Right Hand")); RightHand->ProxyType = ETrackedComponentType::TCT_RIGHT_HAND; RightHand->AttachementType = EAttachementType::AT_FLYSTICK; RightHand->SetupAttachment(RootComponent); + + auto MCRight = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("MC Right")); + MCRight->SetTrackingSource(EControllerHand::Right); LeftHand = CreateDefaultSubobject<UUniversalTrackedComponent>(TEXT("Left Hand")); LeftHand->ProxyType = ETrackedComponentType::TCT_LEFT_HAND; @@ -48,23 +54,62 @@ AVirtualRealityPawn::AVirtualRealityPawn(const FObjectInitializer& ObjectInitial BasicVRInteraction = CreateDefaultSubobject<UBasicVRInteractionComponent>(TEXT("Basic VR Interaction")); BasicVRInteraction->Initialize(RightHand); + } void AVirtualRealityPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) { - Super::SetupPlayerInputComponent(PlayerInputComponent); - if (!PlayerInputComponent) return; - PlayerInputComponent->BindAxis("MoveForward", this, &AVirtualRealityPawn::OnForward); - PlayerInputComponent->BindAxis("MoveRight", this, &AVirtualRealityPawn::OnRight); - PlayerInputComponent->BindAxis("MoveUp", this, &AVirtualRealityPawn::OnUp); - PlayerInputComponent->BindAxis("TurnRate", this, &AVirtualRealityPawn::OnTurnRate); - PlayerInputComponent->BindAxis("LookUpRate", this, &AVirtualRealityPawn::OnLookUpRate); + // simple way of changing the handedness + /*if(bMoveWithRightHand) + { + MovementHand = RightHand; + RotationHand = LeftHand; + IMCMovement = IMCMovementRight; + } else + { + MovementHand = LeftHand; + RotationHand = RightHand; + IMCMovement = IMCMovementLeft; + }*/ + + const APlayerController* PlayerController = Cast<APlayerController>(GetController()); + UEnhancedInputLocalPlayerSubsystem* InputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()); + if(!InputSubsystem) + { + UE_LOG(LogTemp,Error,TEXT("InputSubsystem IS NOT VALID")); + } + + InputSubsystem->ClearAllMappings(); - // function bindings for grabbing and releasing - PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AVirtualRealityPawn::OnBeginFire); - PlayerInputComponent->BindAction("Fire", IE_Released, this, &AVirtualRealityPawn::OnEndFire); + // add Input Mapping context + //InputSubsystem->AddMappingContext(IMCMovement,0); + InputSubsystem->AddMappingContext(IMCBase,0); + + UEnhancedInputComponent* EI = Cast<UEnhancedInputComponent>(PlayerInputComponent); + + // old function bindings for grabbing and releasing + EI->BindAction(InputActions->Fire, ETriggerEvent::Started, this, &AVirtualRealityPawn::OnBeginFire); + EI->BindAction(InputActions->Fire, ETriggerEvent::Completed, this, &AVirtualRealityPawn::OnEndFire); + + // grabbing + EI->BindAction(InputActions->Grab, ETriggerEvent::Started, this, &AVirtualRealityPawn::OnBeginGrab); + EI->BindAction(InputActions->Grab, ETriggerEvent::Completed, this, &AVirtualRealityPawn::OnEndGrab); + + /*// walking + EI->BindAction(InputActions->Move, ETriggerEvent::Triggered, this, &AVirtualRealityPawn::OnBeginMove);*/ + /* + // turning + if(bSnapTurn && !UVirtualRealityUtilities::IsDesktopMode()) + { + EI->BindAction(InputActions->Turn, ETriggerEvent::Started, this, &AVirtualRealityPawn::OnBeginSnapTurn); + } else + { + EI->BindAction(InputActions->Turn, ETriggerEvent::Triggered, this, &AVirtualRealityPawn::OnBeginTurn); + } + */ + // bind functions for desktop rotations only on holding down right mouse if (UVirtualRealityUtilities::IsDesktopMode()) { @@ -75,12 +120,12 @@ void AVirtualRealityPawn::SetupPlayerInputComponent(UInputComponent* PlayerInput PC->bEnableClickEvents = true; PC->bEnableMouseOverEvents = true; } - PlayerInputComponent->BindAction("EnableDesktopRotation", IE_Pressed, this, &AVirtualRealityPawn::StartDesktopRotation); - PlayerInputComponent->BindAction("EnableDesktopRotation", IE_Released, this, &AVirtualRealityPawn::EndDesktopRotation); + /*EI->BindAction(InputActions->DesktopRotation, ETriggerEvent::Started, this, &AVirtualRealityPawn::StartDesktopRotation); + EI->BindAction(InputActions->DesktopRotation, ETriggerEvent::Completed, this, &AVirtualRealityPawn::EndDesktopRotation);*/ } } -void AVirtualRealityPawn::StartDesktopRotation() +/*void AVirtualRealityPawn::StartDesktopRotation() { bApplyDesktopRotation = true; } @@ -88,9 +133,9 @@ void AVirtualRealityPawn::StartDesktopRotation() void AVirtualRealityPawn::EndDesktopRotation() { bApplyDesktopRotation = false; -} +}*/ -void AVirtualRealityPawn::SetCameraOffset() const +/*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 @@ -98,9 +143,9 @@ void AVirtualRealityPawn::SetCameraOffset() const FRotator Rotation; GetActorEyesViewPoint(Location, Rotation); CameraComponent->SetWorldLocationAndRotation(Location, Rotation); -} +}*/ -void AVirtualRealityPawn::UpdateRightHandForDesktopInteraction() +/*void AVirtualRealityPawn::UpdateRightHandForDesktopInteraction() { APlayerController* PC = Cast<APlayerController>(GetController()); if (PC) @@ -110,76 +155,126 @@ void AVirtualRealityPawn::UpdateRightHandForDesktopInteraction() FRotator HandOrientation = MouseDirection.ToOrientationRotator(); RightHand->SetWorldRotation(HandOrientation); } -} +}*/ -void AVirtualRealityPawn::OnForward_Implementation(float Value) +/*void AVirtualRealityPawn::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { - //the right hand is rotated on desktop to follow the cursor so it's forward is also changing with cursor position - if (RightHand && !UVirtualRealityUtilities::IsDesktopMode()) - { - AddMovementInput(RightHand->GetForwardVector(), Value); - } - else if (Head) + Super::PostEditChangeProperty(PropertyChangedEvent); + + FName PropertyName = (PropertyChangedEvent.Property != NULL) ? PropertyChangedEvent.Property->GetFName() : NAME_None; + + if (PropertyName == GET_MEMBER_NAME_CHECKED(AVirtualRealityPawn, bMoveWithRightHand) || + PropertyName == GET_MEMBER_NAME_CHECKED(AVirtualRealityPawn, bSnapTurn)) { - AddMovementInput(Head->GetForwardVector(), Value); + // if we want to change input bindings, we need to setup the player input again to load a different mapping context, + // or assign a different function to an input action. + // This is automatically done by restarting the pawn(calling SetupPlayerInputComponent() directly results in crashes) + // only do this in the editor + #if WITH_EDITOR + Restart(); + #endif } -} +}*/ + -void AVirtualRealityPawn::OnRight_Implementation(float Value) +void AVirtualRealityPawn::OnUp(const FInputActionValue& Value) { + const float MoveValue = Value.Get<FVector2D>().X; + UE_LOG(LogTemp,Warning,TEXT("MoveUp: %f"),MoveValue); //the right hand is rotated on desktop to follow the cursor so it's forward is also changing with cursor position if (RightHand && !UVirtualRealityUtilities::IsDesktopMode()) { - AddMovementInput(RightHand->GetRightVector(), Value); + AddMovementInput(RightHand->GetUpVector(), MoveValue); } else if (Head) { - AddMovementInput(Head->GetRightVector(), Value); + AddMovementInput(Head->GetUpVector(), MoveValue); } } -void AVirtualRealityPawn::OnUp_Implementation(float Value) +// legacy grabbing +void AVirtualRealityPawn::OnBeginFire(const FInputActionValue& Value) { - //the right hand is rotated on desktop to follow the cursor so it's forward is also changing with cursor position - if (RightHand && !UVirtualRealityUtilities::IsDesktopMode()) - { - AddMovementInput(RightHand->GetUpVector(), Value); - } - else if (Head) - { - AddMovementInput(Head->GetUpVector(), Value); - } + UE_LOG(LogTemp,Warning,TEXT("BeginFire")); + BasicVRInteraction->BeginInteraction(); } -void AVirtualRealityPawn::OnTurnRate_Implementation(float Rate) +// legacy grabbing +void AVirtualRealityPawn::OnEndFire(const FInputActionValue& Value) { - /* Turning the user externally will make them sick */ - if (UVirtualRealityUtilities::IsDesktopMode() && bApplyDesktopRotation) + UE_LOG(LogTemp,Warning,TEXT("EndFire")); + BasicVRInteraction->EndInteraction(); +} + +void AVirtualRealityPawn::OnBeginGrab(const FInputActionValue& Value) +{ + UE_LOG(LogTemp,Warning,TEXT("BeginGrab")); +} + +void AVirtualRealityPawn::OnEndGrab(const FInputActionValue& Value) +{ + UE_LOG(LogTemp,Warning,TEXT("EndGrab")); +} + +/*void AVirtualRealityPawn::OnBeginMove(const FInputActionValue& Value) +{ + const FVector ForwardDir = UVirtualRealityUtilities::IsDesktopMode() ? Head->GetForwardVector() : MovementHand->GetForwardVector(); + const FVector RightDir = UVirtualRealityUtilities::IsDesktopMode() ? Head->GetRightVector() : MovementHand->GetRightVector(); + + if (Controller != nullptr) { - AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds() * CustomTimeDilation); + const FVector2D MoveValue = Value.Get<FVector2D>(); + const FRotator MovementRotation(0, Controller->GetControlRotation().Yaw, 0); + + // Forward/Backward direction + if (MoveValue.X != 0.f) + { + AddMovementInput(ForwardDir, MoveValue.X); + } + + // Right/Left direction + if (MoveValue.Y != 0.f) + { + AddMovementInput(RightDir, MoveValue.Y); + } } - if (UVirtualRealityUtilities::IsDesktopMode()) +}*/ + +/*void AVirtualRealityPawn::OnBeginTurn(const FInputActionValue& Value) +{ + if(UVirtualRealityUtilities::IsDesktopMode() && !bApplyDesktopRotation) return; + if (Controller != nullptr) { - UpdateRightHandForDesktopInteraction(); + const FVector2D TurnValue = Value.Get<FVector2D>(); + + if (TurnValue.X != 0.f) + { + AddControllerYawInput(TurnRateFactor * TurnValue.X); + if (UVirtualRealityUtilities::IsDesktopMode()) + { + UpdateRightHandForDesktopInteraction(); + } + } + + if (TurnValue.Y != 0.f) + { + if (UVirtualRealityUtilities::IsDesktopMode() && bApplyDesktopRotation) + { + AddControllerPitchInput(TurnRateFactor * TurnValue.Y); + SetCameraOffset(); + } + } } } -void AVirtualRealityPawn::OnLookUpRate_Implementation(float Rate) +void AVirtualRealityPawn::OnBeginSnapTurn(const FInputActionValue& Value) { - /* Turning the user externally will make them sick */ - if (UVirtualRealityUtilities::IsDesktopMode() && bApplyDesktopRotation) + const FVector2D TurnValue = Value.Get<FVector2D>(); + + if (TurnValue.X != 0.f) { - AddControllerPitchInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds() * CustomTimeDilation); - SetCameraOffset(); + AddControllerYawInput(SnapTurnAngle); } -} +}*/ -void AVirtualRealityPawn::OnBeginFire_Implementation() -{ - BasicVRInteraction->BeginInteraction(); -} -void AVirtualRealityPawn::OnEndFire_Implementation() -{ - BasicVRInteraction->EndInteraction(); -} diff --git a/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h b/Source/RWTHVRToolkit/Public/Pawn/ContinuousMovementComponent.h similarity index 55% rename from Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h rename to Source/RWTHVRToolkit/Public/Pawn/ContinuousMovementComponent.h index 298799e58c5a0e3e5b9d03649ae3b71b94d1c87c..fb335133431678cc9a7e4bd312bc8cc8a23ea959 100644 --- a/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h +++ b/Source/RWTHVRToolkit/Public/Pawn/ContinuousMovementComponent.h @@ -1,73 +1,149 @@ -#pragma once - -#include "CoreMinimal.h" -#include "GameFramework/FloatingPawnMovement.h" -#include "Components/CapsuleComponent.h" -#include "Camera/CameraComponent.h" - -#include "VRPawnMovement.generated.h" - -/* - * This Movement component is needed since in VR not only the pawn itself (UpdatedComponent) is moved but also the - * user herself can walk and thereby move the CameraComponent, which can also lead to collisions or e.g. going up steps - * - * The four modes are: - * None: No controller movement is applied and no corrections regarding steps or collisions with walls are done - * Ghost: The same as above but now the Inputs can be used for unconstrained flying (also through objects) - * Fly: The user can fly but not through walls etc. When the user walks against a wall the scene is moved with her to avoid walking through - * The user can also walk up stairs with a maximum step height of MaxStepHeight - * Walk: Additionally to Fly now gravity keeps the user on the floor - */ - -UENUM(BlueprintType) -enum class EVRNavigationModes : uint8 -{ - NAV_NONE UMETA(DisplayName = "None (no controller movement)"), - NAV_GHOST UMETA(DisplayName = "Ghost (flying, also through walls)"), - NAV_FLY UMETA(DisplayName = "Fly (prohibiting collisions)"), - NAV_WALK UMETA(DisplayName = "Walk (gravity and prohibiting collisions)") -}; - -UCLASS() -class RWTHVRTOOLKIT_API UVRPawnMovement : public UFloatingPawnMovement -{ - GENERATED_UCLASS_BODY() - -public: - - virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, - FActorComponentTickFunction* ThisTickFunction) override; - - void SetHeadComponent(USceneComponent* NewHeadComponent); - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") - EVRNavigationModes NavigationMode = EVRNavigationModes::NAV_WALK; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") - float MaxStepHeight = 40.0f; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") - float GravityAcceleration = 981.0f; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") - float UpSteppingAcceleration = 500.0f; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") - float CapsuleRadius = 40.0f; - -private: - FHitResult CreateLineTrace(FVector Direction, const FVector Start, bool Visibility); - FHitResult CreateMultiLineTrace(FVector Direction, const FVector Start, float Radius, bool Visibility); - void SetCapsuleColliderToUserSize(); - void CheckForPhysWalkingCollision(); - bool CheckForVirtualMovCollision(FVector PositionChange, float DeltaTime); - void MoveByGravityOrStepUp(float DeltaSeconds); - void ShiftVertically(float DiffernceDistance, float VerticalAcceleration, float DeltaSeconds, int Direction); - //(direction = Down = -1), (direction = Up = 1) - - UPROPERTY(VisibleAnywhere) UCapsuleComponent* CapsuleColliderComponent = nullptr; - UPROPERTY() USceneComponent* HeadComponent = nullptr; - - float VerticalSpeed = 0.0f; - FVector LastHeadPosition; -}; +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/FloatingPawnMovement.h" +#include "Components/CapsuleComponent.h" +#include "Pawn/VirtualRealityPawn.h" + +#include "ContinuousMovementComponent.generated.h" + + +/* + * This Movement component is needed since in VR not only the pawn itself (UpdatedComponent) is moved but also the + * user herself can walk and thereby move the CameraComponent, which can also lead to collisions or e.g. going up steps + * + * The four modes are: + * None: No controller movement is applied and no corrections regarding steps or collisions with walls are done + * Ghost: The same as above but now the Inputs can be used for unconstrained flying (also through objects) + * Fly: The user can fly but not through walls etc. When the user walks against a wall the scene is moved with her to avoid walking through + * The user can also walk up stairs with a maximum step height of MaxStepHeight + * Walk: Additionally to Fly now gravity keeps the user on the floor + */ + +UENUM(BlueprintType) +enum class EVRNavigationModes : uint8 +{ + NAV_NONE UMETA(DisplayName = "None (no controller movement)"), + NAV_GHOST UMETA(DisplayName = "Ghost (flying, also through walls)"), + NAV_FLY UMETA(DisplayName = "Fly (prohibiting collisions)"), + NAV_WALK UMETA(DisplayName = "Walk (gravity and prohibiting collisions)") +}; + + +/** + * + */ +UCLASS(Blueprintable) +class RWTHVRTOOLKIT_API UContinuousMovementComponent : public UFloatingPawnMovement +{ + GENERATED_BODY() + +public: + + UContinuousMovementComponent(const FObjectInitializer& ObjectInitializer); + + virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, + FActorComponentTickFunction* ThisTickFunction) override; + + virtual void BeginPlay() override; + + void SetHeadComponent(USceneComponent* NewHeadComponent); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") + EVRNavigationModes NavigationMode = EVRNavigationModes::NAV_WALK; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") + float MaxStepHeight = 40.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") + float GravityAcceleration = 981.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") + float UpSteppingAcceleration = 500.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") + float CapsuleRadius = 40.0f; + + + /*Movement Input*/ + UFUNCTION(BlueprintCallable) + void OnBeginMove(const FInputActionValue& Value); + + UFUNCTION(BlueprintCallable) + void OnBeginTurn(const FInputActionValue& Value); + + UFUNCTION(BlueprintCallable) + void OnBeginSnapTurn(const FInputActionValue& Value); + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "VR Movement|Input") + class UInputMappingContext* IMCMovementRight; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "VR Movement|Input") + class UInputMappingContext* IMCMovementLeft; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "VR Movement|Input") + class UVRPawnInputConfig* InputActions; + + /*Desktop Testing*/ + // the idea is that you have to hold the right mouse button to do rotations + UFUNCTION() + void StartDesktopRotation(); + + UFUNCTION() + void EndDesktopRotation(); + + bool bApplyDesktopRotation = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") + bool bMoveWithRightHand = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") + bool bSnapTurn = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement", meta=(EditCondition="!bSnapTurn")) + float TurnRateFactor = 1.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement", meta=(EditCondition="bSnapTurn",ClampMin=0,ClampMax=360)) + float SnapTurnAngle = 22.5; + +private: + FHitResult CreateLineTrace(FVector Direction, const FVector Start, bool Visibility); + FHitResult CreateMultiLineTrace(FVector Direction, const FVector Start, float Radius, bool Visibility); + void SetCapsuleColliderToUserSize(); + void CheckForPhysWalkingCollision(); + bool CheckForVirtualMovCollision(FVector PositionChange, float DeltaTime); + void MoveByGravityOrStepUp(float DeltaSeconds); + void ShiftVertically(float DiffernceDistance, float VerticalAcceleration, float DeltaSeconds, int Direction); + //(direction = Down = -1), (direction = Up = 1) + + UPROPERTY(VisibleAnywhere) UCapsuleComponent* CapsuleColliderComponent = nullptr; + UPROPERTY() USceneComponent* HeadComponent = nullptr; + + float VerticalSpeed = 0.0f; + FVector LastHeadPosition; + + + UPROPERTY() + UUniversalTrackedComponent* MovementHand; + + UPROPERTY() + UUniversalTrackedComponent* RotationHand; + + UPROPERTY() + class UInputMappingContext* IMCMovement; + + void SetupInputActions(); + + AVirtualRealityPawn* VRPawn; + + /*virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;*/ + + /** + * Fixes camera rotation in desktop mode. + */ + void SetCameraOffset() const; + void UpdateRightHandForDesktopInteraction(); + +}; diff --git a/Source/RWTHVRToolkit/Public/Pawn/VRPawnInputConfig.h b/Source/RWTHVRToolkit/Public/Pawn/VRPawnInputConfig.h new file mode 100644 index 0000000000000000000000000000000000000000..a75687f3b1fd07dbdb595d3bd3eacc429116fbc7 --- /dev/null +++ b/Source/RWTHVRToolkit/Public/Pawn/VRPawnInputConfig.h @@ -0,0 +1,40 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "InputAction.h" +#include "Engine/DataAsset.h" +#include "VRPawnInputConfig.generated.h" + +/** + * + */ +UCLASS() +class RWTHVRTOOLKIT_API UVRPawnInputConfig : public UDataAsset +{ + GENERATED_BODY() + +public: + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) + UInputAction* MoveUp; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) + UInputAction* DesktopRotation; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) + UInputAction* Fire; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) + UInputAction* Grab; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) + UInputAction* Move; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) + UInputAction* Turn; + + + +}; diff --git a/Source/RWTHVRToolkit/Public/Pawn/VirtualRealityPawn.h b/Source/RWTHVRToolkit/Public/Pawn/VirtualRealityPawn.h index c1c0789f9f48b5f5a6d03a9b9d7448cd7d00cd4b..79f7af9b8729e98943aef606f2001291956ef0df 100644 --- a/Source/RWTHVRToolkit/Public/Pawn/VirtualRealityPawn.h +++ b/Source/RWTHVRToolkit/Public/Pawn/VirtualRealityPawn.h @@ -4,16 +4,15 @@ #include "BasicVRInteractionComponent.h" #include "CoreMinimal.h" -#include "GameFramework/DefaultPawn.h" #include "UniversalTrackedComponent.h" -#include "VRPawnMovement.h" #include "VirtualRealityPawn.generated.h" +class UCameraComponent; class ULiveLinkComponentController; /** * */ -UCLASS() +UCLASS(Abstract) class RWTHVRTOOLKIT_API AVirtualRealityPawn : public APawn { GENERATED_BODY() @@ -21,15 +20,34 @@ public: AVirtualRealityPawn(const FObjectInitializer& ObjectInitializer); /* Proxy */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Proxy Objects") UUniversalTrackedComponent* Head; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Proxy Objects") UUniversalTrackedComponent* RightHand; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Proxy Objects") UUniversalTrackedComponent* LeftHand; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Proxy Objects") + UUniversalTrackedComponent* Head; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Proxy Objects") + UUniversalTrackedComponent* RightHand; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Proxy Objects") + UUniversalTrackedComponent* LeftHand; /* Interaction */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Interaction") UBasicVRInteractionComponent* BasicVRInteraction; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Interaction") + UBasicVRInteractionComponent* BasicVRInteraction; /* Movement */ - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn|Movement") UVRPawnMovement* PawnMovement; + /*UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn|Movement") + UVRPawnMovement* PawnMovement;*/ + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement") + bool bMoveWithRightHand = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement") + bool bSnapTurn = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement", meta=(EditCondition="!bSnapTurn")) + float TurnRateFactor = 1.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement", meta=(EditCondition="bSnapTurn",ClampMin=0,ClampMax=360)) + float SnapTurnAngle = 22.5; /** Workaround dummy component to prevent the Capsule from rotating in the editor, if LiveLink tracking is being used. * This happens due to the rotation of the Capsule being set only while in Play Mode (instead of using e.g. absolute rotation). @@ -38,32 +56,79 @@ public: * The dummy seems to fix this, because its absolute rotation just catches all parent rotations and prevents them from * overriding any of the capsules'. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement") USceneComponent* CapsuleRotationFix; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement") float BaseTurnRate = 45.0f; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement") + USceneComponent* CapsuleRotationFix; + /* CameraComponent */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Camera") UCameraComponent* CameraComponent; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Camera") + UCameraComponent* CameraComponent; + protected: virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override; /* Movement */ - UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Movement") void OnForward(float Value); - UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Movement") void OnRight(float Value); - UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Movement") void OnUp(float Value); - UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Movement") void OnTurnRate(float Rate); - UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Movement") void OnLookUpRate(float Rate); + UFUNCTION(BlueprintCallable, Category = "Pawn|Movement") + void OnUp(const FInputActionValue& Value); /* Interaction */ - UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Interaction") void OnBeginFire(); - UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Interaction") void OnEndFire(); + UFUNCTION(BlueprintCallable, Category = "Pawn|Interaction") + void OnBeginFire(const FInputActionValue& Value); + + UFUNCTION(BlueprintCallable, Category = "Pawn|Interaction") + void OnEndFire(const FInputActionValue& Value); + + UFUNCTION(BlueprintCallable, Category = "Pawn|Interaction") + void OnBeginGrab(const FInputActionValue& Value); + + UFUNCTION(BlueprintCallable, Category = "Pawn|Interaction") + void OnEndGrab(const FInputActionValue& Value); + + /* + UFUNCTION(BlueprintCallable, Category = "Pawn|Interaction") + void OnBeginMove(const FInputActionValue& Value); + + UFUNCTION(BlueprintCallable, Category = "Pawn|Interaction") + void OnBeginTurn(const FInputActionValue& Value); + UFUNCTION(BlueprintCallable, Category = "Pawn|Interaction") + void OnBeginSnapTurn(const FInputActionValue& Value); + */ + + /* Input */ + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Pawn|Input") + class UInputMappingContext* IMCBase; + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Pawn|Input") + class UVRPawnInputConfig* InputActions; + /*Desktop Testing*/ // the idea is that you have to hold the right mouse button to do rotations - UFUNCTION() void StartDesktopRotation(); - UFUNCTION() void EndDesktopRotation(); - bool bApplyDesktopRotation = false; + /*UFUNCTION() + void StartDesktopRotation(); + + UFUNCTION() + void EndDesktopRotation(); + + bool bApplyDesktopRotation = false;*/ + + /** + * Fixes camera rotation in desktop mode. + */ + /*void SetCameraOffset() const; + void UpdateRightHandForDesktopInteraction();*/ + +private: + /*UPROPERTY() + UUniversalTrackedComponent* MovementHand; + + UPROPERTY() + UUniversalTrackedComponent* RotationHand; + + UPROPERTY() + class UInputMappingContext* IMCMovement;*/ + + /*virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;*/ - void SetCameraOffset() const; - void UpdateRightHandForDesktopInteraction(); }; diff --git a/Source/RWTHVRToolkit/RWTHVRToolkit.Build.cs b/Source/RWTHVRToolkit/RWTHVRToolkit.Build.cs index 7a3abd662bc3d1c68586b690556f8bec4a52384e..8345d954abb2ade18d2974e89a1ca9ace8c42c12 100644 --- a/Source/RWTHVRToolkit/RWTHVRToolkit.Build.cs +++ b/Source/RWTHVRToolkit/RWTHVRToolkit.Build.cs @@ -28,7 +28,8 @@ public class RWTHVRToolkit : ModuleRules "DeveloperSettings", "HTTP", "LiveLink", - "LiveLinkInterface" + "LiveLinkInterface", + "EnhancedInput" } );