Skip to content
Snippets Groups Projects
Commit 436b3049 authored by Sebastian Pape's avatar Sebastian Pape
Browse files

Merge branch 'feature/VRPawn426' into 'develop'

Feature/vr pawn426

See merge request VR-Group/unreal-development/ndisplayextensions!28
parents a06e48ee 68bd0d54
No related branches found
No related tags found
No related merge requests found
Showing
with 886 additions and 719 deletions
// Fill out your copyright notice in the Description page of Project Settings.
#include "BasicVRInteractionComponent.h"
#include "Clickable.h"
#include "Grabable.h"
#include "GrabbingBehaviorComponent.h"
// Sets default values for this component's properties
UBasicVRInteractionComponent::UBasicVRInteractionComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
// ...
}
void UBasicVRInteractionComponent::BeginInteraction()
{
if(!InteractionRayEmitter) return;
// start and end point for raytracing
const FTwoVectors StartEnd = GetHandRay(MaxClickDistance);
const FVector Start = StartEnd.v1;
const FVector End = StartEnd.v2;
// will be filled by the Line Trace Function
FHitResult Hit;
//if hit was not found return
const FCollisionObjectQueryParams Params;
if (!GetWorld()->LineTraceSingleByObjectType(Hit, Start, End, Params))
return;
AActor* HitActor = Hit.GetActor();
// try to cast HitActor into 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();
// save it for later, is needed every tick
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 UBasicVRInteractionComponent::EndInteraction()
{
if(!InteractionRayEmitter) return;
// if we didnt grab anyone there is no need to release
if (GrabbedActor == nullptr)
return;
// let the grabbed object react to release
Cast<IGrabable>(GrabbedActor)->OnReleased_Implementation();
// Detach the Actor
if (GrabbedActor->FindComponentByClass<UGrabbingBehaviorComponent>() == nullptr)
{
GrabbedActor->GetRootComponent()->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
GrabbedActor->FindComponentByClass<UPrimitiveComponent>()->SetSimulatePhysics(bDidSimulatePhysics);
}
// forget about the actor
GrabbedActor = nullptr;
Behavior = nullptr;
}
// Called every frame
void UBasicVRInteractionComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// if an actor is grabbed and a behavior is defined move move him accordingly
if (GrabbedActor == nullptr || InteractionRayEmitter == nullptr) return;
// if our Grabable Actor is not constrained
if (Behavior != nullptr)
{
// specifies the hand in space
const FVector HandPos = InteractionRayEmitter->GetComponentLocation();
const FQuat HandQuat = InteractionRayEmitter->GetComponentQuat();
Behavior->HandleNewPositionAndDirection(HandPos, HandQuat);
}
}
void UBasicVRInteractionComponent::Initialize(USceneComponent* RayEmitter, float InMaxGrabDistance, float InMaxClickDistance)
{
if(RayEmitter != nullptr) return; /* Return if already initialized */
InteractionRayEmitter = RayEmitter;
MaxGrabDistance = InMaxGrabDistance;
MaxClickDistance = InMaxClickDistance;
}
void UBasicVRInteractionComponent::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(EAttachmentRule::KeepWorld, true);
HitActor->AttachToComponent(InteractionRayEmitter, Rules);
}
FTwoVectors UBasicVRInteractionComponent::GetHandRay(const float Length) const
{
const FVector Start = InteractionRayEmitter->GetComponentLocation();
const FVector Direction = InteractionRayEmitter->GetForwardVector();
const FVector End = Start + Length * Direction;
return FTwoVectors(Start, End);
}
...@@ -4,11 +4,11 @@ ...@@ -4,11 +4,11 @@
void FDisplayClusterExtensionsModule::StartupModule () void FDisplayClusterExtensionsModule::StartupModule ()
{ {
StereoDeviceFix.Register();
} }
void FDisplayClusterExtensionsModule::ShutdownModule() void FDisplayClusterExtensionsModule::ShutdownModule()
{ {
StereoDeviceFix.Unregister();
} }
#undef LOCTEXT_NAMESPACE #undef LOCTEXT_NAMESPACE
......
#include "FixNDisplayStereoDevice.h"
void FFixNDisplayStereoDevice::Register()
{
On_Post_World_Initialization_Delegate.BindRaw(this, &FFixNDisplayStereoDevice::OnSessionStart);
StartHandle = FWorldDelegates::OnPostWorldInitialization.Add(On_Post_World_Initialization_Delegate);
On_Pre_World_Finish_Destroy_Delegate.BindRaw(this, &FFixNDisplayStereoDevice::OnSessionEnd);
EndHandle = FWorldDelegates::OnPreWorldFinishDestroy.Add(On_Pre_World_Finish_Destroy_Delegate);
}
void FFixNDisplayStereoDevice::Unregister()
{
FWorldDelegates::OnPostWorldInitialization.Remove(StartHandle);
FWorldDelegates::OnPreWorldFinishDestroy.Remove(EndHandle);
}
void FFixNDisplayStereoDevice::OnSessionStart(UWorld*, const UWorld::InitializationValues)
{
/* Store handle before it is released */
if(GEngine)
{
StoredRenderingDevice = GEngine->StereoRenderingDevice;
}
}
void FFixNDisplayStereoDevice::OnSessionEnd(UWorld*)
{
/* Restore handle after it was released */
if(GEngine)
{
GEngine->StereoRenderingDevice = StoredRenderingDevice;
}
}
// Fill out your copyright notice in the Description page of Project Settings.
#include "UniversalTrackedComponent.h"
#include "Camera/CameraComponent.h"
#include "VirtualRealityUtilities.h"
// Sets default values for this component's properties
UUniversalTrackedComponent::UUniversalTrackedComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
}
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));
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;
}
}else if(UVirtualRealityUtilities::IsDesktopMode()){
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(TrackedComponent, FAttachmentTransformRules::SnapToTargetIncludingScale);
}
void UUniversalTrackedComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if(TrackedComponent) return; /* Already attached */
/* Test for presence every frame and add as soon as they are there */
if (UVirtualRealityUtilities::IsRoomMountedMode())
{
switch(ProxyType)
{
case ETrackedComponentType::TCT_RIGHT_HAND:
case ETrackedComponentType::TCT_LEFT_HAND:
TrackedComponent = GetComponentForSelectedAttachment(AttachementType);
break;
case ETrackedComponentType::TCT_HEAD:
TrackedComponent = GetOwner()->FindComponentByClass<UCameraComponent>();
break;
case ETrackedComponentType::TCT_TRACKER_1:
case ETrackedComponentType::TCT_TRACKER_2:
TrackedComponent = GetOwner()->GetRootComponent();
break;
}
if(TrackedComponent) AttachToComponent(TrackedComponent, FAttachmentTransformRules::SnapToTargetIncludingScale);
}
}
UMotionControllerComponent* UUniversalTrackedComponent::GetMotionControllerComponentByMotionSource(EControllerHand MotionSource)
{
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]
);
}
USceneComponent* UUniversalTrackedComponent::GetComponentForSelectedAttachment(EAttachementType AttachmentType) const
{
switch (AttachmentType)
{
case EAttachementType::AT_NONE: return nullptr;
case EAttachementType::AT_HANDTARGET:
if(ProxyType == ETrackedComponentType::TCT_RIGHT_HAND) return UVirtualRealityUtilities::GetNamedClusterComponent(ENamedClusterComponent::NCC_CAVE_RHT);
if(ProxyType == ETrackedComponentType::TCT_LEFT_HAND) return UVirtualRealityUtilities::GetNamedClusterComponent(ENamedClusterComponent::NCC_CAVE_LHT);
break;
case EAttachementType::AT_FLYSTICK:
return UVirtualRealityUtilities::GetNamedClusterComponent(ENamedClusterComponent::NCC_FLYSTICK);
break;
}
return nullptr;
}

#include "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_Ignore);
CapsuleColliderComponent->SetCollisionResponseToChannel(ECollisionChannel::ECC_WorldStatic, ECollisionResponse::ECR_Block);
CapsuleColliderComponent->SetCapsuleSize(40.0f, 96.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);
LastCameraPosition = CameraComponent->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::SetCameraComponent(UCameraComponent* NewCameraComponent)
{
CameraComponent = NewCameraComponent;
CapsuleColliderComponent->SetupAttachment(CameraComponent);
}
void UVRPawnMovement::SetCapsuleColliderToUserSize()
{
float CharachterSize = abs(UpdatedComponent->GetComponentLocation().Z - CameraComponent->GetComponentLocation().Z);
if (CharachterSize > MaxStepHeight)
{
float ColliderHeight = CharachterSize - MaxStepHeight;
float ColliderHalfHeight = ColliderHeight / 2.0f;
float ColliderRadius = 40.0f;
if (ColliderHalfHeight <= ColliderRadius)
{//Make the collider to a Sphere
CapsuleColliderComponent->SetCapsuleSize(ColliderHalfHeight, ColliderHalfHeight);
}
else
{//Make the collider to a Capsule
CapsuleColliderComponent->SetCapsuleSize(ColliderRadius, ColliderHalfHeight);
}
CapsuleColliderComponent->SetWorldLocation(CameraComponent->GetComponentLocation());
CapsuleColliderComponent->AddWorldOffset(FVector(0, 0, -ColliderHalfHeight));
CapsuleColliderComponent->SetWorldRotation(FRotator(0, 0, 1));
}
else
{
CapsuleColliderComponent->SetWorldLocation(CameraComponent->GetComponentLocation());
CapsuleColliderComponent->SetWorldRotation(FRotator(0, 0, 1));
}
}
void UVRPawnMovement::CheckForPhysWalkingCollision()
{
FVector CurrentCameraPosition = CameraComponent->GetComponentLocation();
FVector Direction = CurrentCameraPosition - LastCameraPosition;
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.Actor == 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.Actor == OutHits[0].Actor); //If all Hiting the same Object, then you are (going up/down) or (walking)
IsAllNothingHiting &= (OutHit.Actor == nullptr); //If all Hiting nothing, then you are falling
}
if (IsBlockingHitAndSameActor || IsAllNothingHiting)
HitDetailsMultiLineTrace = OutHits[0];
return HitDetailsMultiLineTrace;
}
\ No newline at end of file
...@@ -3,6 +3,5 @@ ...@@ -3,6 +3,5 @@
AVirtualRealityGameMode::AVirtualRealityGameMode() : Super() AVirtualRealityGameMode::AVirtualRealityGameMode() : Super()
{ {
// if (!bIsDisplayClusterActive) return;
DefaultPawnClass = AVirtualRealityPawn::StaticClass(); DefaultPawnClass = AVirtualRealityPawn::StaticClass();
} }
...@@ -22,13 +22,19 @@ bool UVirtualRealityUtilities::IsHeadMountedMode() ...@@ -22,13 +22,19 @@ bool UVirtualRealityUtilities::IsHeadMountedMode()
return GEngine->XRSystem.IsValid() && GEngine->XRSystem->IsHeadTrackingAllowed(); return GEngine->XRSystem.IsValid() && GEngine->XRSystem->IsHeadTrackingAllowed();
} }
/* Return true on the Master in cluster mode and in a normal desktop session. Otherwise false */
bool UVirtualRealityUtilities::IsMaster() bool UVirtualRealityUtilities::IsMaster()
{ {
IDisplayClusterClusterManager* manager = IDisplayCluster::Get().GetClusterMgr(); if (!IDisplayCluster::IsAvailable())
if (manager == nullptr) // no manager means we are not in clustermode and therefore master {
return true; return true;
}
return manager->IsMaster(); IDisplayClusterClusterManager* Manager = IDisplayCluster::Get().GetClusterMgr();
if (Manager == nullptr)
{
return true; // if we are not in cluster mode, we are always the master
}
return Manager->IsMaster() || !Manager->IsSlave();
} }
bool UVirtualRealityUtilities::IsSlave() bool UVirtualRealityUtilities::IsSlave()
...@@ -54,3 +60,27 @@ UDisplayClusterSceneComponent* UVirtualRealityUtilities::GetClusterComponent(con ...@@ -54,3 +60,27 @@ UDisplayClusterSceneComponent* UVirtualRealityUtilities::GetClusterComponent(con
{ {
return IDisplayCluster::Get().GetGameMgr()->GetRootActor()->GetComponentById(Name); return IDisplayCluster::Get().GetGameMgr()->GetRootActor()->GetComponentById(Name);
} }
UDisplayClusterSceneComponent* UVirtualRealityUtilities::GetNamedClusterComponent(const ENamedClusterComponent& Component)
{
switch(Component)
{
case ENamedClusterComponent::NCC_CAVE_ORIGIN: return GetClusterComponent("cave_origin");
case ENamedClusterComponent::NCC_CAVE_CENTER: return GetClusterComponent("cave_center");
case ENamedClusterComponent::NCC_CAVE_LHT: return GetClusterComponent("left_hand_target");
case ENamedClusterComponent::NCC_CAVE_RHT: return GetClusterComponent("right_hand_target");
case ENamedClusterComponent::NCC_SHUTTERGLASSES: return GetClusterComponent("shutter_glasses");
case ENamedClusterComponent::NCC_ROLV_ORIGIN: return GetClusterComponent("rolv_origin");
case ENamedClusterComponent::NCC_FLYSTICK: return GetClusterComponent("flystick");
case ENamedClusterComponent::NCC_TDW_ORIGIN: return GetClusterComponent("tdw_origin_floor");
case ENamedClusterComponent::NCC_TDW_CENTER: return GetClusterComponent("tdw_center");
case ENamedClusterComponent::NCC_TRACKING_ORIGIN:
UDisplayClusterSceneComponent* Result;
if((Result = GetClusterComponent("cave_origin"))) return Result;
if((Result = GetClusterComponent("rolv_origin"))) return Result;
if((Result = GetClusterComponent("tdw_origin_floor"))) return Result;
return nullptr;
default: return nullptr;
}
}
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "BasicVRInteractionComponent.generated.h"
class UGrabbingBehaviorComponent;
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class DISPLAYCLUSTEREXTENSIONS_API UBasicVRInteractionComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UBasicVRInteractionComponent();
UFUNCTION(BlueprintCallable) void BeginInteraction();
UFUNCTION(BlueprintCallable) void EndInteraction();
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
UPROPERTY(BlueprintReadWrite) float MaxGrabDistance = 50;
UPROPERTY(BlueprintReadWrite) float MaxClickDistance = 500;
UFUNCTION(BlueprintCallable) void Initialize(USceneComponent* RayEmitter, float InMaxGrabDistance = 50, float InMaxClickDistance = 500);
UFUNCTION(BlueprintCallable, BlueprintPure) AActor* GetGrabbedActor() const { return GrabbedActor; }
UFUNCTION(BlueprintCallable, BlueprintPure) USceneComponent* GetInteractionRayEmitter() const { return InteractionRayEmitter; }
private:
/* indicates if the grabbed actor was simulating physics before we grabbed it */
UPROPERTY() bool bDidSimulatePhysics;
/* Holding a reference to the actor that is currently being grabbed */
UPROPERTY() AActor* GrabbedActor;
UPROPERTY() UGrabbingBehaviorComponent* Behavior = nullptr;
UPROPERTY() USceneComponent* InteractionRayEmitter = nullptr;
void HandlePhysicsAndAttachActor(AActor* HitActor);
FTwoVectors GetHandRay(float Length) const;
};
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "FixNDisplayStereoDevice.h"
#include "Modules/ModuleManager.h" #include "Modules/ModuleManager.h"
class FDisplayClusterExtensionsModule : public IModuleInterface class FDisplayClusterExtensionsModule : public IModuleInterface
{ {
public: public:
virtual void StartupModule () override; virtual void StartupModule () override;
virtual void ShutdownModule() override; virtual void ShutdownModule() override;
private:
FFixNDisplayStereoDevice StereoDeviceFix;
}; };
#pragma once
#include "CoreMinimal.h"
#include "Engine/World.h"
#include "FixNDisplayStereoDevice.generated.h"
/**
* This fixes the behavior of nDisplay DisplayClusterRenderManager, which resets the global stereo render device
* This is problematic, since it resets the pointer (L128) to the StereoRenderDevice, even if it does not re-initialize (L113) it.
* This results in a fine first start, but crashes on the second VR start. For Desktop this seems to still function fine.
*
* For this behavior a Pull-Request is in progress. See https://github.com/EpicGames/UnrealEngine/pull/7738
*/
USTRUCT()
struct DISPLAYCLUSTEREXTENSIONS_API FFixNDisplayStereoDevice
{
GENERATED_BODY()
void Register();
void Unregister();
private:
void OnSessionStart(UWorld*, const UWorld::InitializationValues);
void OnSessionEnd(UWorld*);
TSharedPtr< class IStereoRendering, ESPMode::ThreadSafe > StoredRenderingDevice;
TBaseDelegate<void, UWorld*, const UWorld::InitializationValues> On_Post_World_Initialization_Delegate;
TBaseDelegate<void, UWorld*> On_Pre_World_Finish_Destroy_Delegate;
FDelegateHandle StartHandle;
FDelegateHandle EndHandle;
};
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/SceneComponent.h"
#include "MotionControllerComponent.h"
#include "UniversalTrackedComponent.generated.h"
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")
};
/*
* 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 DISPLAYCLUSTEREXTENSIONS_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;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tracking|nDisplay") EAttachementType AttachementType = EAttachementType::AT_NONE;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tracking|HMD", BlueprintSetter=SetShowDeviceModel) bool bShowDeviceModelInHMD = true;
UFUNCTION(BlueprintSetter)
void SetShowDeviceModel(const bool bShowControllerModel);
virtual void BeginPlay() override;
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
private:
USceneComponent* TrackedComponent = nullptr;
UMotionControllerComponent* GetMotionControllerComponentByMotionSource(EControllerHand MotionSource);
USceneComponent* GetComponentForSelectedAttachment(EAttachementType AttachmentType) const;
};
#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 DISPLAYCLUSTEREXTENSIONS_API UVRPawnMovement : public UFloatingPawnMovement
{
GENERATED_UCLASS_BODY()
public:
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) override;
void SetCameraComponent(UCameraComponent* NewCameraComponent);
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;
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() UCapsuleComponent* CapsuleColliderComponent = nullptr;
UPROPERTY() UCameraComponent* CameraComponent = nullptr;
float VerticalSpeed = 0.0f;
FVector LastCameraPosition;
};
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once #pragma once
#include "BasicVRInteractionComponent.h"
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "Cluster/DisplayClusterClusterEvent.h" #include "GameFramework/DefaultPawn.h"
#include "Cluster/IDisplayClusterClusterManager.h"
#include "Components/DisplayClusterSceneComponent.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/FloatingPawnMovement.h"
#include "GameFramework/PawnMovementComponent.h"
#include "GameFramework/RotatingMovementComponent.h" #include "GameFramework/RotatingMovementComponent.h"
#include "UniversalTrackedComponent.h"
#include "MotionControllerComponent.h" #include "VRPawnMovement.h"
#include "VirtualRealityPawn.generated.h" #include "VirtualRealityPawn.generated.h"
UENUM(BlueprintType) /**
enum class EVRNavigationModes : uint8 *
{ */
nav_mode_none UMETA(DisplayName = "Navigation Mode None"),
nav_mode_walk UMETA(DisplayName = "Navigation Mode Walk"),
nav_mode_fly UMETA(DisplayName = "Navigation Mode Fly")
};
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")
};
UCLASS() UCLASS()
class DISPLAYCLUSTEREXTENSIONS_API AVirtualRealityPawn : public APawn class DISPLAYCLUSTEREXTENSIONS_API AVirtualRealityPawn : public APawn
{ {
GENERATED_UCLASS_BODY() GENERATED_BODY()
public: public:
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn") void OnForward(float Value); AVirtualRealityPawn(const FObjectInitializer& ObjectInitializer);
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn") void OnRight(float Value);
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn") void OnTurnRate(float Rate);
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn") void OnLookUpRate(float Rate);
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn") void OnBeginFire();
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn") void OnEndFire();
UFUNCTION(Category = "Pawn") float GetBaseTurnRate() const;
UFUNCTION(Category = "Pawn") void SetBaseTurnRate(float Value);
UFUNCTION(Category = "Pawn") UFloatingPawnMovement* GetFloatingPawnMovement();
UFUNCTION(Category = "Pawn") URotatingMovementComponent* GetRotatingMovementComponent();
//Bunch of Getter Functions for components to avoid users having to know the names
UFUNCTION(Category = "Pawn") UDisplayClusterSceneComponent* GetFlystickComponent();
UFUNCTION(Category = "Pawn") UDisplayClusterSceneComponent* GetRightHandtargetComponent();
UFUNCTION(Category = "Pawn") UDisplayClusterSceneComponent* GetLeftHandtargetComponent();
UFUNCTION(Category = "Pawn") UMotionControllerComponent* GetHmdLeftMotionControllerComponent();
UFUNCTION(Category = "Pawn") UMotionControllerComponent* GetHmdRightMotionControllerComponent();
UFUNCTION(Category = "Pawn") UMotionControllerComponent* GetHmdTracker1MotionControllerComponent();
UFUNCTION(Category = "Pawn") UMotionControllerComponent* GetHmdTracker2MotionControllerComponent();
UFUNCTION(Category = "Pawn") USceneComponent* GetHeadComponent(); /* Proxy */
UFUNCTION(Category = "Pawn") USceneComponent* GetLeftHandComponent(); UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Proxy Objects") UUniversalTrackedComponent* Head;
UFUNCTION(Category = "Pawn") USceneComponent* GetRightHandComponent(); 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* Tracker1;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Proxy Objects") UUniversalTrackedComponent* Tracker2;
/* Interaction */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Interaction") UBasicVRInteractionComponent* BasicVRInteraction;
UFUNCTION(Category = "Pawn") USceneComponent* GetTrackingOriginComponent(); /* Movement */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn|Movement") UVRPawnMovement* PawnMovement;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement") float BaseTurnRate = 45.0f;
private: /* CameraComponent */
UFUNCTION(Category = "Pawn") USceneComponent* GetCaveCenterComponent(); UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Camera") UCameraComponent* CameraComponent;
UFUNCTION(Category = "Pawn") USceneComponent* GetShutterGlassesComponent();
UFUNCTION(Category = "Pawn") FTwoVectors GetHandRay(float Distance);
UFUNCTION(Category = "Pawn") void HandlePhysicsAndAttachActor(AActor* HitActor);
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn") EVRNavigationModes NavigationMode = EVRNavigationModes::nav_mode_fly;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn") float MaxStepHeight = 40.0f;
protected: protected:
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
virtual void Tick(float DeltaSeconds) override;
virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override; virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;
virtual UPawnMovementComponent* GetMovementComponent() const override;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn", meta = (AllowPrivateAccess = "true")) float BaseTurnRate = 45.0f;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) UFloatingPawnMovement* Movement = nullptr;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) URotatingMovementComponent* RotatingMovement = nullptr;
// Use only when handling cross-device (PC, HMD, CAVE/ROLV) compatibility manually. CAVE/ROLV flystick.
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) UDisplayClusterSceneComponent* Flystick = nullptr;
// Use only when handling cross-device (PC, HMD, CAVE/ROLV) compatibility manually. CAVE/ROLV flystick.
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) UDisplayClusterSceneComponent* RightHandTarget = nullptr;
// Use only when handling cross-device (PC, HMD, CAVE/ROLV) compatibility manually. CAVE/ROLV flystick.
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) UDisplayClusterSceneComponent* LeftHandTarget = nullptr;
// Use only when handling cross-device (PC, HMD, CAVE/ROLV) compatibility manually. HMD left motion controller.
UPROPERTY() UMotionControllerComponent* HmdLeftMotionController = nullptr;
// Use only when handling cross-device (PC, HMD, CAVE/ROLV) compatibility manually. HMD right motion controller.
UPROPERTY() UMotionControllerComponent* HmdRightMotionController = nullptr;
// used only for HMDs, tested with the additional Vive Trackers
UPROPERTY() UMotionControllerComponent* HmdTracker1 = nullptr;
UPROPERTY() UMotionControllerComponent* HmdTracker2 = nullptr;
// PC: Camera, HMD: Camera, CAVE/ROLV: Shutter glasses.
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) USceneComponent* Head = nullptr;
// PC: RootComponent, HMD: HmdLeftMotionController , CAVE/ROLV: regarding to AttachRightHandInCAVE. Useful for line trace (e.g. for holding objects).
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) USceneComponent* RightHand = nullptr;
// PC: RootComponent, HMD: HmdRightMotionController, CAVE/ROLV: regarding to AttachLeftHandInCAVE. Useful for line trace (e.g. for holding objects).
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) USceneComponent* LeftHand = nullptr;
// Holding the Cave/rolv Origin Component that is attached to this Pawn
UPROPERTY() USceneComponent* TrackingOrigin = nullptr;
// Holding the Cave Center Component that is attached to this Pawn, it is needed for the internal transform of nDisplay
UPROPERTY() USceneComponent* CaveCenter = nullptr;
// Holding the Shutter Glasses Component that is attached to this Pawn
UPROPERTY() USceneComponent* ShutterGlasses = nullptr;
// Holding a reference to the actor that is currently being grabbed
UPROPERTY() AActor* GrabbedActor;
// indicates if the grabbed actor was simulating physics before we grabbed it
UPROPERTY() bool bDidSimulatePhysics;
UPROPERTY(EditAnywhere) float MaxGrabDistance = 50;
UPROPERTY(EditAnywhere) float MaxClickDistance = 500;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn") bool ShowHMDControllers = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn") EAttachementType AttachRightHandInCAVE = EAttachementType::AT_FLYSTICK;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn") EAttachementType AttachLeftHandInCAVE = EAttachementType::AT_NONE;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn", meta = (AllowPrivateAccess = "true")) UCapsuleComponent* CapsuleColliderComponent = nullptr; /* Movement */
private: UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Movement") void OnForward(float Value);
float DeltaTime = 0.0f; UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Movement") void OnRight(float Value);
float VerticalSpeed = 0.0f; UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Movement") void OnTurnRate(float Rate);
UPROPERTY() float GravityAcceleration = 981.0f; UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Movement") void OnLookUpRate(float Rate);
UPROPERTY() float UpSteppingAcceleration = 500.0f;
FVector LastCameraPosition;
FHitResult CreateLineTrace(FVector Direction, const FVector Start, bool Visibility); /* Interaction */
FHitResult CreateMultiLineTrace(FVector Direction, const FVector Start, float Radius, bool Visibility); UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Interaction") void OnBeginFire();
void SetCapsuleColliderCharacterSizeVR(); UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Interaction") void OnEndFire();
void CheckForPhysWalkingCollision();
void HandleMovementInput(float Value, FVector Direction);
void VRWalkingMode(float Value, FVector Direction);
void VRFlyingMode(float Value, FVector Direction);
void MoveByGravityOrStepUp(float DeltaSeconds);
void ShiftVertically(float DiffernceDistance, float Acceleration, float DeltaSeconds, int Direction);//(direction = Down = -1), (direction = Up = 1)
void InitRoomMountedComponentReferences();
}; };
...@@ -5,6 +5,28 @@ ...@@ -5,6 +5,28 @@
#include "Kismet/BlueprintFunctionLibrary.h" #include "Kismet/BlueprintFunctionLibrary.h"
#include "VirtualRealityUtilities.generated.h" #include "VirtualRealityUtilities.generated.h"
UENUM(BlueprintType)
enum class ENamedClusterComponent : uint8
{
/* CAVE Specific */
NCC_CAVE_ORIGIN UMETA(DisplayName = "CAVE Origin"),
NCC_CAVE_CENTER UMETA(DisplayName = "CAVE Center"),
NCC_CAVE_LHT UMETA(DisplayName = "CAVE Left Hand Target"),
NCC_CAVE_RHT UMETA(DisplayName = "CAVE Right Hand Target"),
/* ROLV Specific */
NCC_ROLV_ORIGIN UMETA(DisplayName = "ROLV Origin"),
/* TDW Specific */
NCC_TDW_ORIGIN UMETA(DisplayName = "TDW Origin"),
NCC_TDW_CENTER UMETA(DisplayName = "TDW Center"),
/* Non Specific */
NCC_SHUTTERGLASSES UMETA(DisplayName = "CAVE/ROLV/TDW Shutter Glasses"),
NCC_FLYSTICK UMETA(DisplayName = "CAVE/ROLV/TDW Flystick"),
NCC_TRACKING_ORIGIN UMETA(DisplayName = "CAVE/ROLV/TDW Origin")
};
UCLASS() UCLASS()
class DISPLAYCLUSTEREXTENSIONS_API UVirtualRealityUtilities : public UBlueprintFunctionLibrary class DISPLAYCLUSTEREXTENSIONS_API UVirtualRealityUtilities : public UBlueprintFunctionLibrary
{ {
...@@ -25,4 +47,5 @@ public: ...@@ -25,4 +47,5 @@ public:
//Get Compenent of Display Cluster by it's name, which is specified in the nDisplay config //Get Compenent of Display Cluster by it's name, which is specified in the nDisplay config
UFUNCTION(BlueprintPure, BlueprintCallable, Category = "DisplayCluster") static UDisplayClusterSceneComponent* GetClusterComponent(const FString& Name); UFUNCTION(BlueprintPure, BlueprintCallable, Category = "DisplayCluster") static UDisplayClusterSceneComponent* GetClusterComponent(const FString& Name);
UFUNCTION(BlueprintPure, BlueprintCallable, Category = "DisplayCluster") static UDisplayClusterSceneComponent* GetNamedClusterComponent(const ENamedClusterComponent& Component);
}; };
...@@ -20,14 +20,11 @@ ...@@ -20,14 +20,11 @@
"Type": "Runtime", "Type": "Runtime",
"LoadingPhase": "Default" "LoadingPhase": "Default"
}, },
{ {
"Name": "DisplayClusterExtensionsEditor", "Name": "DisplayClusterExtensionsEditor",
"Type": "Editor", "Type": "Editor",
"LoadingPhase": "PostEngineInit" "LoadingPhase": "PostEngineInit"
} }
], ],
"Plugins": [ "Plugins": [
{ {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment