Skip to content
Snippets Groups Projects
Select Git revision
  • 20504cb36128a7bb4d0dbf3ca1ca19ae781814cb
  • dev/5.3 default
  • feature/scaleTeleport
  • feature/scaleAndHeightTeleport
  • 5.3
  • 5.0
  • TempNav
  • fix/DisplayClusterTemplateCode
  • fix_5.3/DisplayClusterTemplateFix
  • ReworkedToolkit
  • dev/5.2
  • feature/make_interaction_ray_accesible
  • dev/5.1
  • 4.27
  • 4.26 protected
  • 4.22
  • 4.21
17 results

VirtualRealityPawn.cpp

Blame
  • user avatar
    Ehret authored
    rename to VRPawnMovement and add a ghost mode (+comments for all modes) and move all functionality to be called within TickComponent
    489a5013
    History
    VirtualRealityPawn.cpp 15.56 KiB
    #include "VirtualRealityPawn.h"
    
    #include "Camera/CameraComponent.h"
    #include "Engine/Engine.h"
    #include "Engine/World.h"
    #include "Game/IDisplayClusterGameManager.h"
    #include "GameFramework/InputSettings.h"
    #include "GameFramework/WorldSettings.h"
    #include "Kismet/GameplayStatics.h"
    #include "DisplayClusterSettings.h"
    #include "IDisplayCluster.h"
    #include "Components/SphereComponent.h"
    #include "DrawDebugHelpers.h"
    #include "Math/Vector.h"
    #include "VirtualRealityUtilities.h"
    
    #include "GrabbingBehaviorComponent.h"
    #include "Grabable.h"
    #include "Clickable.h"
    
    
    AVirtualRealityPawn::AVirtualRealityPawn(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
    {
    	bUseControllerRotationYaw = true;
    	bUseControllerRotationPitch = true;
    	bUseControllerRotationRoll = true;
    
    	AutoPossessPlayer = EAutoReceiveInput::Player0; // Necessary for receiving motion controller events.
    
    	VRMovement = CreateDefaultSubobject<UVRPawnMovement>(TEXT("WalkingMovement"));
    	VRMovement->SetUpdatedComponent(RootComponent);
    	VRMovement->SetCameraComponent(CameraComponent);
    
    	RotatingMovement = CreateDefaultSubobject<URotatingMovementComponent>(TEXT("RotatingMovement"));
    	RotatingMovement->UpdatedComponent = RootComponent;
    	RotatingMovement->bRotationInLocalSpace = false;
    	RotatingMovement->PivotTranslation = FVector::ZeroVector;
    	RotatingMovement->RotationRate = FRotator::ZeroRotator;
    
    	Head = CreateDefaultSubobject<USceneComponent>(TEXT("Head"));
    	Head->SetupAttachment(RootComponent);
    	RightHand = CreateDefaultSubobject<USceneComponent>(TEXT("RightHand"));
    	RightHand->SetupAttachment(RootComponent);
    	LeftHand = CreateDefaultSubobject<USceneComponent>(TEXT("LeftHand"));
    	LeftHand->SetupAttachment(RootComponent);
    
    	HmdLeftMotionController = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("HmdLeftMotionController"));
    	HmdLeftMotionController->SetupAttachment(RootComponent);
    	HmdLeftMotionController->SetTrackingSource(EControllerHand::Left);
    	HmdLeftMotionController->SetShowDeviceModel(true);
    	HmdLeftMotionController->SetVisibility(false);
    
    	HmdRightMotionController = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("HmdRightMotionController"));
    	HmdRightMotionController->SetupAttachment(RootComponent);
    	HmdRightMotionController->SetTrackingSource(EControllerHand::Right);
    	HmdRightMotionController->SetShowDeviceModel(true);
    	HmdRightMotionController->SetVisibility(false);
    
    	HmdTracker1 = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("HmdTracker1"));
    	HmdTracker1->SetupAttachment(RootComponent);
    	HmdTracker1->SetTrackingSource(EControllerHand::Special_1);
    	HmdTracker1->SetShowDeviceModel(true);
    	HmdTracker1->SetVisibility(false);
    
    	HmdTracker2 = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("HmdTracker2"));
    	HmdTracker2->SetupAttachment(RootComponent);
    	HmdTracker2->SetTrackingSource(EControllerHand::Special_2);
    	HmdTracker2->SetShowDeviceModel(true);
    	HmdTracker2->SetVisibility(false);
    }
    
    void AVirtualRealityPawn::OnForward_Implementation(float Value)
    {
    	if (RightHand)
    	{
    		AddMovementInput(RightHand->GetForwardVector(), Value);
    	}
    }
    
    void AVirtualRealityPawn::OnRight_Implementation(float Value)
    {
    	if (RightHand)
    	{
    		AddMovementInput(RightHand->GetRightVector(), Value);
    	}
    }
    
    void AVirtualRealityPawn::OnTurnRate_Implementation(float Rate)
    {
    	AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds() * CustomTimeDilation);
    }
    
    void AVirtualRealityPawn::OnLookUpRate_Implementation(float Rate)
    {
    	if (UVirtualRealityUtilities::IsRoomMountedMode())
    	{
    		// User-centered projection causes simulation sickness on look up interaction hence not implemented.
    	}
    	else
    	{
    		AddControllerPitchInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds() * CustomTimeDilation);
    	}
    }
    
    float AVirtualRealityPawn::GetBaseTurnRate() const
    {
    	return BaseTurnRate;
    }
    
    void AVirtualRealityPawn::SetBaseTurnRate(float Value)
    {
    	BaseTurnRate = Value;
    }
    
    URotatingMovementComponent* AVirtualRealityPawn::GetRotatingMovementComponent()
    {
    	return RotatingMovement;
    }
    
    UDisplayClusterSceneComponent* AVirtualRealityPawn::GetFlystickComponent()
    {
    	return Flystick;
    }
    
    UDisplayClusterSceneComponent* AVirtualRealityPawn::GetRightHandtargetComponent()
    {
    	return RightHandTarget;
    }
    
    UDisplayClusterSceneComponent* AVirtualRealityPawn::GetLeftHandtargetComponent()
    {
    	return LeftHandTarget;
    }
    
    UMotionControllerComponent* AVirtualRealityPawn::GetHmdLeftMotionControllerComponent()
    {
    	return HmdLeftMotionController;
    }
    
    UMotionControllerComponent* AVirtualRealityPawn::GetHmdRightMotionControllerComponent()
    {
    	return HmdRightMotionController;
    }
    
    UMotionControllerComponent* AVirtualRealityPawn::GetHmdTracker1MotionControllerComponent()
    {
    	return HmdTracker1;
    }
    
    UMotionControllerComponent* AVirtualRealityPawn::GetHmdTracker2MotionControllerComponent()
    {
    	return HmdTracker2;
    }
    
    USceneComponent* AVirtualRealityPawn::GetHeadComponent()
    {
    	return Head;
    }
    
    USceneComponent* AVirtualRealityPawn::GetLeftHandComponent()
    {
    	return LeftHand;
    }
    
    USceneComponent* AVirtualRealityPawn::GetRightHandComponent()
    {
    	return RightHand;
    }
    
    USceneComponent* AVirtualRealityPawn::GetTrackingOriginComponent()
    {
    	return TrackingOrigin;
    }
    
    USceneComponent* AVirtualRealityPawn::GetCaveCenterComponent()
    {
    	return CaveCenter;
    }
    
    USceneComponent* AVirtualRealityPawn::GetShutterGlassesComponent()
    {
    	return ShutterGlasses;
    }
    
    void AVirtualRealityPawn::ClusterExecute(const FString& Command)
    {
      FDisplayClusterClusterEvent event;
      event.Name = "NDisplayCMD: " + Command;
      event.Type = "NDisplayCMD";
      event.Category = "VRPawn";
      event.Parameters.Add("Command", Command);
      IDisplayCluster::Get().GetClusterMgr()->EmitClusterEvent(event, false);
    }
    
    void AVirtualRealityPawn::HandleClusterEvent(const FDisplayClusterClusterEvent& Event)
    {
    	if (Event.Category.Equals("VRPawn") && Event.Type.Equals("NDisplayCMD") && Event.Parameters.Contains("Command"))
    	{
    		GEngine->Exec(GetWorld(), *Event.Parameters["Command"]);
    	}
    }
    
    void AVirtualRealityPawn::BeginPlay()
    {
    	Super::BeginPlay();
    
    	// Display cluster settings apply to all setups (PC, HMD, CAVE/ROLV) despite the unfortunate name due to being an UE4 internal.
    	TArray<AActor*> SettingsActors;
    	UGameplayStatics::GetAllActorsOfClass(GetWorld(), ADisplayClusterSettings::StaticClass(), SettingsActors);
    	if (SettingsActors.Num() > 0)
    	{
    		ADisplayClusterSettings* Settings = Cast<ADisplayClusterSettings>(SettingsActors[0]);
    		VRMovement->MaxSpeed = Settings->MovementMaxSpeed;
    		VRMovement->Acceleration = Settings->MovementAcceleration;
    		VRMovement->Deceleration = Settings->MovementDeceleration;
    		VRMovement->TurningBoost = Settings->MovementTurningBoost;
    		BaseTurnRate = Settings->RotationSpeed;
    	}
    
    	if (UVirtualRealityUtilities::IsRoomMountedMode())
    	{
    		UInputSettings::GetInputSettings()->RemoveAxisMapping(FInputAxisKeyMapping("TurnRate", EKeys::MouseX));
    		UInputSettings::GetInputSettings()->RemoveAxisMapping(FInputAxisKeyMapping("LookUpRate", EKeys::MouseY));
    
    		InitRoomMountedComponentReferences();
    	}
    	else if (UVirtualRealityUtilities::IsHeadMountedMode())
    	{
    		UInputSettings::GetInputSettings()->RemoveAxisMapping(FInputAxisKeyMapping("TurnRate", EKeys::MouseX));
    		UInputSettings::GetInputSettings()->RemoveAxisMapping(FInputAxisKeyMapping("LookUpRate", EKeys::MouseY));
    
    		HmdLeftMotionController->SetVisibility(ShowHMDControllers);
    		HmdRightMotionController->SetVisibility(ShowHMDControllers);
    		if (HmdTracker1->IsActive()) {
    			HmdTracker1->SetVisibility(ShowHMDControllers);
    		}
    		if (HmdTracker2->IsActive()) {
    			HmdTracker2->SetVisibility(ShowHMDControllers);
    		}
    
    		LeftHand->AttachToComponent(HmdLeftMotionController, FAttachmentTransformRules::SnapToTargetIncludingScale);
    		RightHand->AttachToComponent(HmdRightMotionController, FAttachmentTransformRules::SnapToTargetIncludingScale);
    		Head->AttachToComponent(GetCameraComponent(), FAttachmentTransformRules::SnapToTargetIncludingScale);
    	}
    	else //Desktop
    	{
    		Head->AttachToComponent(GetCameraComponent(), FAttachmentTransformRules::SnapToTargetIncludingScale);
    
    		//also attach the hands to the camera component so we can use them for interaction
    		LeftHand->AttachToComponent(GetCameraComponent(), FAttachmentTransformRules::SnapToTargetIncludingScale);
    		RightHand->AttachToComponent(GetCameraComponent(), FAttachmentTransformRules::SnapToTargetIncludingScale);
    
    
    		//move to eyelevel
    		GetCameraComponent()->SetRelativeLocation(FVector(0, 0, 160));
    	}
    
    	//In ADisplayClusterPawn::BeginPlay() input is disabled on all slaves, so we cannot react to button presses, e.g. on the flystick correctly.
    	//Therefore, we activate it again:
    	UWorld* World = GetWorld();
    	if (World)
    	{
    		APlayerController* PlayerController = World->GetFirstPlayerController();
    		if (PlayerController)
    		{
    			this->EnableInput(PlayerController);
    		}
    	}
    
    	// Register cluster event listeners
    	IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
    	if (ClusterManager && !ClusterEventListenerDelegate.IsBound())
    	{
    		ClusterEventListenerDelegate = FOnClusterEventListener::CreateUObject(this, &AVirtualRealityPawn::HandleClusterEvent);
    		ClusterManager->AddClusterEventListener(ClusterEventListenerDelegate);
    	}
    
    	CollisionComponent->SetCollisionProfileName(FName("NoCollision"));
    	CollisionComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
    }
    
    void AVirtualRealityPawn::EndPlay(const EEndPlayReason::Type EndPlayReason)
    {
    	IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
    	if (ClusterManager && ClusterEventListenerDelegate.IsBound())
    	{
    		ClusterManager->RemoveClusterEventListener(ClusterEventListenerDelegate);
    	}
    
    	Super::EndPlay(EndPlayReason);
    }
    
    void AVirtualRealityPawn::Tick(float DeltaSeconds)
    {
    	Super::Tick(DeltaSeconds);
    
    	// if an actor is grabbed and a behavior is defined move move him accordingly  
    	if (GrabbedActor != nullptr)
    	{
    		UGrabbingBehaviorComponent* Behavior = GrabbedActor->FindComponentByClass<UGrabbingBehaviorComponent>();
    
    		// if our Grabable Actor is not constrained
    		if (Behavior != nullptr)
    		{	
    			// specifies the hand in space
    			FVector HandPos = this->RightHand->GetComponentLocation();	
    			FQuat HandQuat = this->RightHand->GetComponentQuat();
    
    			Behavior->HandleNewPositionAndDirection(HandPos, HandQuat); 
    		}
    	}
    
    	//Flystick might not be available at start, hence is checked every frame.
    	InitRoomMountedComponentReferences();
    }
    
    void AVirtualRealityPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
    {
    	Super::SetupPlayerInputComponent(PlayerInputComponent);
    	if (PlayerInputComponent)
    	{
    		PlayerInputComponent->BindAxis("MoveForward", this, &AVirtualRealityPawn::OnForward);
    		PlayerInputComponent->BindAxis("MoveRight", this, &AVirtualRealityPawn::OnRight);
    		PlayerInputComponent->BindAxis("TurnRate", this, &AVirtualRealityPawn::OnTurnRate);
    		PlayerInputComponent->BindAxis("LookUpRate", this, &AVirtualRealityPawn::OnLookUpRate);
    
    		// function bindings for grabbing and releasing
    		PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AVirtualRealityPawn::OnBeginFire);
    		PlayerInputComponent->BindAction("Fire", IE_Released, this, &AVirtualRealityPawn::OnEndFire);
    	}
    }
    
    void AVirtualRealityPawn::OnBeginFire_Implementation()
    {
    	// start and end point for raytracing
    	FTwoVectors StartEnd = GetHandRay(MaxClickDistance);	
    	FVector Start = StartEnd.v1;
    	FVector End   = StartEnd.v2;	
    
    	// will be filled by the Line Trace Function
    	FHitResult Hit;
    	AActor* HitActor;
    
    	//if hit was not found return  
    	FCollisionObjectQueryParams Params;
    	if (!GetWorld()->LineTraceSingleByObjectType(Hit, Start, End, Params))
    		return;
    
    	HitActor = Hit.GetActor();
    	
    	// try to cast HitActor int a Grabable if not succeeded will become a nullptr
    	IGrabable*  GrabableActor  = Cast<IGrabable>(HitActor);
    	IClickable* ClickableActor = Cast<IClickable>(HitActor);
    
    	if (GrabableActor != nullptr && Hit.Distance < MaxGrabDistance)
    	{
    		// call grabable actors function so he reacts to our grab
    		GrabableActor->OnGrabbed_Implementation();
    		
    
    		UGrabbingBehaviorComponent* Behavior = HitActor->FindComponentByClass<UGrabbingBehaviorComponent>();
    		if ( Behavior == nullptr)
    			HandlePhysicsAndAttachActor(HitActor);
    
    		// we save the grabbedActor in a general form to access all of AActors functions easily later
    		GrabbedActor = HitActor;
    	}
    	else if (ClickableActor != nullptr && Hit.Distance < MaxClickDistance)
    	{
    		ClickableActor->OnClicked_Implementation(Hit.Location);
    	}
    }
    
    void AVirtualRealityPawn::HandlePhysicsAndAttachActor(AActor* HitActor)
    {
    	UPrimitiveComponent* PhysicsComp = HitActor->FindComponentByClass<UPrimitiveComponent>();	
    	
    	bDidSimulatePhysics = PhysicsComp->IsSimulatingPhysics(); // remember if we need to tun physics back on or not	
    	PhysicsComp->SetSimulatePhysics(false);
    	FAttachmentTransformRules Rules = FAttachmentTransformRules::KeepWorldTransform;
    	Rules.bWeldSimulatedBodies = true;
    	HitActor->AttachToComponent(this->RightHand, Rules);
    }
    
    void AVirtualRealityPawn::OnEndFire_Implementation() {
    
    	// if we didnt grab anyone there is no need to release
    	if (GrabbedActor == nullptr)
    		return;
    
    	// let the grabbed object reacot to release
    	Cast<IGrabable>(GrabbedActor)->OnReleased_Implementation();
    
    	// Detach the Actor
    
    	UPrimitiveComponent* PhysicsComp = GrabbedActor->FindComponentByClass<UPrimitiveComponent>();
    	UGrabbingBehaviorComponent* Behavior = GrabbedActor->FindComponentByClass<UGrabbingBehaviorComponent>();
    	if (Behavior == nullptr)
    	{
    		GrabbedActor->GetRootComponent()->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
    		PhysicsComp->SetSimulatePhysics(bDidSimulatePhysics);
    	}
    
    	// forget about the actor
    	GrabbedActor = nullptr;
    }
    
    FTwoVectors AVirtualRealityPawn::GetHandRay(float Length)
    {
    	FVector Start = this->RightHand->GetComponentLocation();
    	FVector Direction = this->RightHand->GetForwardVector();
    	FVector End = Start + Length * Direction;
    
    	return FTwoVectors(Start, End);
    }
    
    
    UPawnMovementComponent* AVirtualRealityPawn::GetMovementComponent() const
    {
    	return VRMovement;
    }
    
    void AVirtualRealityPawn::InitRoomMountedComponentReferences()
    {
    	if (!UVirtualRealityUtilities::IsRoomMountedMode()) return;
    
    	//check whether the nodes already exist (otherwise GetClusterComponent() returns nullptr and prints a warning) and assign them
    	if (!TrackingOrigin) TrackingOrigin = UVirtualRealityUtilities::GetClusterComponent("cave_origin");
    	if (!TrackingOrigin) TrackingOrigin = UVirtualRealityUtilities::GetClusterComponent("rolv_origin");
    	if (!CaveCenter) CaveCenter = UVirtualRealityUtilities::GetClusterComponent("cave_center");
    	if (!ShutterGlasses)
    	{
    		ShutterGlasses = UVirtualRealityUtilities::GetClusterComponent("shutter_glasses");
    		Head->AttachToComponent(ShutterGlasses, FAttachmentTransformRules::SnapToTargetIncludingScale);
    	}
    	if (!Flystick)
    	{
    		Flystick = UVirtualRealityUtilities::GetClusterComponent("flystick");
    		if (AttachRightHandInCAVE == EAttachementType::AT_FLYSTICK)
    			RightHand->AttachToComponent(Flystick, FAttachmentTransformRules::SnapToTargetIncludingScale);
    		if (AttachLeftHandInCAVE == EAttachementType::AT_FLYSTICK)
    			LeftHand->AttachToComponent(Flystick, FAttachmentTransformRules::SnapToTargetIncludingScale);
    	}
    	if (!LeftHandTarget)
    	{
    		LeftHandTarget = UVirtualRealityUtilities::GetClusterComponent("left_hand_target");
    		if (AttachLeftHandInCAVE == EAttachementType::AT_HANDTARGET)
    			LeftHand->AttachToComponent(LeftHandTarget, FAttachmentTransformRules::SnapToTargetIncludingScale);
    	}
    	if (!RightHandTarget)
    	{
    		RightHandTarget = UVirtualRealityUtilities::GetClusterComponent("right_hand_target");
    		if (AttachRightHandInCAVE == EAttachementType::AT_HANDTARGET)
    			RightHand->AttachToComponent(RightHandTarget, FAttachmentTransformRules::SnapToTargetIncludingScale);
    	}
    }
    
    void AVirtualRealityPawn::SetNavigationMode(EVRNavigationModes Mode)
    {
    	VRMovement->NavigationMode = Mode;
    }