diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..1b545cb627f9b82d7fc3edba6aa6998457847ba0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,72 @@ +# Visual Studio 2015 user specific files +.vs/ + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app +*.ipa + +# These project files can be generated by the engine +*.xcodeproj +*.xcworkspace +*.sln +*.suo +*.opensdf +*.sdf +*.VC.db +*.VC.opendb + +# Precompiled Assets +SourceArt/**/*.png +SourceArt/**/*.tga + +# Binary Files +Binaries/* +Plugins/*/Binaries/* + +# Builds +Build/* + +# Whitelist PakBlacklist-<BuildConfiguration>.txt files +!Build/*/ +Build/*/** +!Build/*/PakBlacklist*.txt + +# Don't ignore icon files in Build +!Build/**/*.ico + +# Configuration files generated by the Editor +Saved/* + +# Compiled source files for the engine to use +Intermediate/* +Plugins/*/Intermediate/* + +# Cache files for the editor to use +DerivedDataCache/* +LocalDerivedDataCache/* \ No newline at end of file diff --git a/CAVEOverlay.uplugin b/CAVEOverlay.uplugin new file mode 100644 index 0000000000000000000000000000000000000000..63a2f00daedbadd17666a869f285b3cb906ecf0c --- /dev/null +++ b/CAVEOverlay.uplugin @@ -0,0 +1,35 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "CAVEOverlay", + "Description": "Adds the ability to black out the CAVE door and display the magic tape", + "Category": "Other", + "CreatedBy": "", + "CreatedByURL": "", + "DocsURL": "", + "MarketplaceURL": "", + "SupportURL": "", + "CanContainContent": true, + "IsBetaVersion": false, + "Installed": false, + "Modules": [ + { + "Name": "CAVEOverlay", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ], + "Plugins": [ + { + "Name": "nDisplay", + "Enabled": true + } + ], + "Plugins": [ + { + "Name": "nDisplayExtensions", + "Enabled": true + } + ] +} \ No newline at end of file diff --git a/Content/DoorOverlay.uasset b/Content/DoorOverlay.uasset new file mode 100644 index 0000000000000000000000000000000000000000..ccf451ce839f78c61f120fda328a0b2a3e7779da Binary files /dev/null and b/Content/DoorOverlay.uasset differ diff --git a/Content/Plane.uasset b/Content/Plane.uasset new file mode 100644 index 0000000000000000000000000000000000000000..bcf108519c2d474416a7ccbbd5dedff4c3f967a3 Binary files /dev/null and b/Content/Plane.uasset differ diff --git a/Content/StopMaterial.uasset b/Content/StopMaterial.uasset new file mode 100644 index 0000000000000000000000000000000000000000..72b118af9330aac36c29d3fa8dcd39ae33a3cf74 Binary files /dev/null and b/Content/StopMaterial.uasset differ diff --git a/Content/StopSign.uasset b/Content/StopSign.uasset new file mode 100644 index 0000000000000000000000000000000000000000..567392a66464eb2d06a3ea10d198193f4f94d46b Binary files /dev/null and b/Content/StopSign.uasset differ diff --git a/Content/Stripes.uasset b/Content/Stripes.uasset new file mode 100644 index 0000000000000000000000000000000000000000..12ef411c89bc19b928f1478b336f4738800abd62 Binary files /dev/null and b/Content/Stripes.uasset differ diff --git a/Resources/Icon128.png b/Resources/Icon128.png new file mode 100644 index 0000000000000000000000000000000000000000..cf4bc6653dabb786395c87c375817c682d1997bf Binary files /dev/null and b/Resources/Icon128.png differ diff --git a/Source/CAVEOverlay/CAVEOverlay.Build.cs b/Source/CAVEOverlay/CAVEOverlay.Build.cs new file mode 100644 index 0000000000000000000000000000000000000000..40cb1d85fd006b83741d63f16f23fdd4e1ef483d --- /dev/null +++ b/Source/CAVEOverlay/CAVEOverlay.Build.cs @@ -0,0 +1,57 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class CAVEOverlay : ModuleRules +{ + public CAVEOverlay(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicIncludePaths.AddRange( + new string[] { + // ... add public include paths required here ... + } + ); + + + PrivateIncludePaths.AddRange( + new string[] { + // ... add other private include paths required here ... + } + ); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "UMG", + "DisplayCluster", + "DisplayClusterExtensions", + "InputCore", + "CoreUObject", + "Engine", + "Slate", + "SlateCore" + // ... add other public dependencies that you statically link with here ... + } + ); + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + // ... add private dependencies that you statically link with here ... + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + } +} diff --git a/Source/CAVEOverlay/Private/CAVEOverlay.cpp b/Source/CAVEOverlay/Private/CAVEOverlay.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a980703080b4df9a46a191adffe60e4891886c1f --- /dev/null +++ b/Source/CAVEOverlay/Private/CAVEOverlay.cpp @@ -0,0 +1,20 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +#include "CAVEOverlay.h" + +#define LOCTEXT_NAMESPACE "FCAVEOverlayModule" + +void FCAVEOverlayModule::StartupModule() +{ + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module +} + +void FCAVEOverlayModule::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FCAVEOverlayModule, CAVEOverlay) \ No newline at end of file diff --git a/Source/CAVEOverlay/Private/CAVEOverlayController.cpp b/Source/CAVEOverlay/Private/CAVEOverlayController.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d15ec0f6d11f08e68a9b95abcbea94be4f47b7f6 --- /dev/null +++ b/Source/CAVEOverlay/Private/CAVEOverlayController.cpp @@ -0,0 +1,333 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "CAVEOverlayController.h" +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "DoorOverlayData.h" +#include "IDisplayCluster.h" +#include "Engine/Engine.h" +#include "IXRTrackingSystem.h" +#include "Components/InputComponent.h" +#include "Cluster/IDisplayClusterClusterManager.h" +#include "IDisplayClusterGameManager.h" +#include "UObject/ConstructorHelpers.h" +#include <array> +#include "Components/StaticMeshComponent.h" +#include "Materials/Material.h" +#include "Materials/MaterialInstanceDynamic.h" +#include "DisplayClusterExtensions/Public/VirtualRealityPawn.h" +#include "DisplayCluster/Public/DisplayClusterSceneComponent.h" + +DEFINE_LOG_CATEGORY(LogCAVEOverlay); + +template <std::size_t S> +bool ContainsFString(const std::array<FString, S>& Array, const FString& Entry) +{ + for (FString Current_Entry : Array) + { + if (Current_Entry.Equals(Entry, ESearchCase::IgnoreCase)) return true; + } + return false; +} + +UStaticMeshComponent* ACAVEOverlayController::CreateMeshComponent(const FName& Name, UStaticMesh* Mesh, USceneComponent* Parent) +{ + UStaticMeshComponent* Result = CreateDefaultSubobject<UStaticMeshComponent>(Name); + Result->SetStaticMesh(Mesh); + Result->SetupAttachment(Parent); + Result->SetVisibility(false); + return Result; +} + +template <typename T> +bool LoadAsset(const FString& Path, T* & Result) +{ + ConstructorHelpers::FObjectFinder<T> Loader(*Path); + Result = Loader.Object; + if (!Loader.Succeeded()) UE_LOG(LogCAVEOverlay, Error, TEXT("Could not find %s. Have you renamed it?"), *Path); + return Loader.Succeeded(); +} + +// Sets default values +ACAVEOverlayController::ACAVEOverlayController() +{ + // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. + PrimaryActorTick.bCanEverTick = true; + bAllowTickBeforeBeginPlay = false; + AutoReceiveInput = EAutoReceiveInput::Player0; + + ConstructorHelpers::FClassFinder<UDoorOverlayData> WidgetClassFinder(TEXT("Blueprint'/CAVEOverlay/DoorOverlay'")); + if (WidgetClassFinder.Succeeded()) + { + Overlay_Class = WidgetClassFinder.Class; + } + else + { + UE_LOG(LogCAVEOverlay, Error, TEXT("Could not find the DoorOverlay class. Have you renamed it?")); + } + + //Creation of sub-components + Root = CreateDefaultSubobject<USceneComponent>("DefaultSceneRoot"); + SetRootComponent(Root); + Tape_Root = CreateDefaultSubobject<USceneComponent>("TapeRoot"); + Sign_Root = CreateDefaultSubobject<USceneComponent>("SignRoot"); + Tape_Root->SetupAttachment(Root); + Sign_Root->SetupAttachment(Root); + + //Loading of Materials and Meshes + LoadAsset("/CAVEOverlay/Stripes", Tape_Material); + LoadAsset("/CAVEOverlay/StopMaterial", Sign_Material); + LoadAsset("/CAVEOverlay/Plane", Plane_Mesh_); + + Tape_Negative_Y = CreateMeshComponent("TapeNegY", Plane_Mesh_, Tape_Root); + Tape_Negative_X = CreateMeshComponent("TapeNegX", Plane_Mesh_, Tape_Root); + Tape_Positive_Y = CreateMeshComponent("TapePosY", Plane_Mesh_, Tape_Root); + Tape_Positive_X = CreateMeshComponent("TapePosX", Plane_Mesh_, Tape_Root); + + Sign_Negative_Y = CreateMeshComponent("SignNegY", Plane_Mesh_, Sign_Root); + Sign_Negative_X = CreateMeshComponent("SignNegX", Plane_Mesh_, Sign_Root); + Sign_Positive_Y = CreateMeshComponent("SignPosY", Plane_Mesh_, Sign_Root); + Sign_Positive_X = CreateMeshComponent("SignPosX", Plane_Mesh_, Sign_Root); + + //Set initial Position, Rotation and Scale of Tape + Tape_Negative_Y->SetRelativeLocationAndRotation(FVector(0, -Wall_Distance, 0), FRotator(0, 0, 90)); + Tape_Positive_Y->SetRelativeLocationAndRotation(FVector(0, +Wall_Distance, 0), FRotator(0, 180, 90)); + Tape_Negative_X->SetRelativeLocationAndRotation(FVector(-Wall_Distance, 0, 0), FRotator(0, -90, 90)); + Tape_Positive_X->SetRelativeLocationAndRotation(FVector(+Wall_Distance, 0, 0), FRotator(0, 90, 90)); + + Tape_Negative_Y->SetRelativeScale3D(FVector(Wall_Distance / 100 * 2, 0.15, 1)); + Tape_Positive_Y->SetRelativeScale3D(FVector(Wall_Distance / 100 * 2, 0.15, 1)); + Tape_Negative_X->SetRelativeScale3D(FVector(Wall_Distance / 100 * 2, 0.15, 1)); + Tape_Positive_X->SetRelativeScale3D(FVector(Wall_Distance / 100 * 2, 0.15, 1)); + + //Set initial Position, Rotation and Scale of Signs + Sign_Negative_Y->SetRelativeLocationAndRotation(FVector(0, -Wall_Distance, 0), FRotator(0, 0, 90)); + Sign_Positive_Y->SetRelativeLocationAndRotation(FVector(0, +Wall_Distance, 0), FRotator(0, 180, 90)); + Sign_Negative_X->SetRelativeLocationAndRotation(FVector(-Wall_Distance, 0, 0), FRotator(0, -90, 90)); + Sign_Positive_X->SetRelativeLocationAndRotation(FVector(+Wall_Distance, 0, 0), FRotator(0, 90, 90)); + + Sign_Negative_Y->SetRelativeScale3D(FVector(0.5f)); + Sign_Positive_Y->SetRelativeScale3D(FVector(0.5f)); + Sign_Negative_X->SetRelativeScale3D(FVector(0.5f)); + Sign_Positive_X->SetRelativeScale3D(FVector(0.5f)); +} + +void ACAVEOverlayController::PostInitializeComponents() +{ + Super::PostInitializeComponents(); + + //Create dynamic materials in runtime + Tape_Material_Dynamic_ = UMaterialInstanceDynamic::Create(Tape_Material, Tape_Root); + Sign_Material_Dynamic_ = UMaterialInstanceDynamic::Create(Sign_Material, Sign_Root); + + Tape_Negative_Y->SetMaterial(0, Tape_Material_Dynamic_); + Tape_Negative_X->SetMaterial(0, Tape_Material_Dynamic_); + Tape_Positive_Y->SetMaterial(0, Tape_Material_Dynamic_); + Tape_Positive_X->SetMaterial(0, Tape_Material_Dynamic_); + + Sign_Negative_Y->SetMaterial(0, Sign_Material_Dynamic_); + Sign_Negative_X->SetMaterial(0, Sign_Material_Dynamic_); + Sign_Positive_Y->SetMaterial(0, Sign_Material_Dynamic_); + Sign_Positive_X->SetMaterial(0, Sign_Material_Dynamic_); +} + +void ACAVEOverlayController::CycleDoorType() +{ + Door_Current_Mode = static_cast<EDoor_Mode>((Door_Current_Mode + 1) % DOOR_NUM_MODES); + SetDoorMode(Door_Current_Mode); +} + +void ACAVEOverlayController::SetDoorMode(EDoor_Mode M) +{ + switch (Door_Current_Mode) + { + case EDoor_Mode::DOOR_DEBUG: + case EDoor_Mode::DOOR_PARTIALLY_OPEN: + Door_Current_Opening_Width_Absolute = Door_Opening_Width_Absolute; + if (Screen_Type == SCREEN_DOOR) Overlay->BlackBox->SetRenderScale(FVector2D(0, 1)); + if (Screen_Type == SCREEN_DOOR_PARTIAL) Overlay->BlackBox->SetRenderScale(FVector2D(Door_Opening_Width_Relative, 1)); + if (Screen_Type == SCREEN_MASTER) Overlay->BlackBox->SetRenderScale(FVector2D(0, 1)); + Overlay->BlackBox->SetVisibility(ESlateVisibility::Visible); + break; + case EDoor_Mode::DOOR_OPEN: + Door_Current_Opening_Width_Absolute = Wall_Distance * 2; + if (Screen_Type == SCREEN_DOOR) Overlay->BlackBox->SetRenderScale(FVector2D(1, 1)); + if (Screen_Type == SCREEN_DOOR_PARTIAL) Overlay->BlackBox->SetRenderScale(FVector2D(1, 1)); + if (Screen_Type == SCREEN_MASTER) Overlay->BlackBox->SetRenderScale(FVector2D(1, 1)); + Overlay->BlackBox->SetVisibility(ESlateVisibility::Visible); + break; + case EDoor_Mode::DOOR_CLOSED: + Door_Current_Opening_Width_Absolute = 0; + if (Screen_Type == SCREEN_DOOR) Overlay->BlackBox->SetRenderScale(FVector2D(0, 1)); + if (Screen_Type == SCREEN_DOOR_PARTIAL) Overlay->BlackBox->SetRenderScale(FVector2D(0, 1)); + if (Screen_Type == SCREEN_MASTER) Overlay->BlackBox->SetRenderScale(FVector2D(0, 1)); + Overlay->BlackBox->SetVisibility(ESlateVisibility::Hidden); + break; + default: ; + } + if (Screen_Type == SCREEN_NORMAL) Overlay->BlackBox->SetRenderScale(FVector2D(0, 1)); //no overlay + + UE_LOG(LogCAVEOverlay, Log, TEXT("Switched door state to '%s'. New opening width is %f."), *Door_Mode_Names[Door_Current_Mode], Door_Current_Opening_Width_Absolute); + + if (Screen_Type == SCREEN_MASTER) + { + Overlay->CornerText->SetText(FText::FromString(Door_Mode_Names[Door_Current_Mode])); + } +} + +// Called when the game starts or when spawned +void ACAVEOverlayController::BeginPlay() +{ + Super::BeginPlay(); + + //Read situation + bHMD_Mode = GEngine->XRSystem.IsValid() && GEngine->XRSystem->IsHeadTrackingAllowed(); + bDisplay_Cluster_Mode = IDisplayCluster::Get().GetOperationMode() == EDisplayClusterOperationMode::Cluster; + + if (!bDisplay_Cluster_Mode) return; // Not our business + + //Input config + InputComponent->BindAction("Action4", EInputEvent::IE_Pressed, this, &ACAVEOverlayController::CycleDoorType); + InputComponent->BindKey(EKeys::F10, EInputEvent::IE_Pressed, this, &ACAVEOverlayController::CycleDoorType); + + //Determine the screen-type for later usage + if (IDisplayCluster::Get().GetClusterMgr()->GetNodeId().Equals(Screen_Main, ESearchCase::IgnoreCase)) + { + Screen_Type = SCREEN_MASTER; + } + else if (ContainsFString(Screens_Door, IDisplayCluster::Get().GetClusterMgr()->GetNodeId())) + { + Screen_Type = SCREEN_DOOR; + } + else if (ContainsFString(Screens_Door_Partial, IDisplayCluster::Get().GetClusterMgr()->GetNodeId())) + { + Screen_Type = SCREEN_DOOR_PARTIAL; + } + else + { + Screen_Type = SCREEN_NORMAL; + } + + Overlay = CreateWidget<UDoorOverlayData>(GetWorld()->GetFirstPlayerController(), Overlay_Class); + Overlay->AddToViewport(0); + SetDoorMode(Door_Current_Mode); + Overlay->CornerText->SetText(FText::FromString("")); //Set Text to "" until someone presses the key for the first time + + Player_Pawn = Cast<AVirtualRealityPawn>(GetWorld()->GetFirstPlayerController()->GetPawn()); + RefreshPawnComponents(); + + if (!bAttached && Cave_Origin) + { + AttachToComponent(Cave_Origin, FAttachmentTransformRules::SnapToTargetNotIncludingScale); + bAttached = true; + } +} + +float ACAVEOverlayController::CalculateOpacityFromPosition(FVector Position) +{ + return FMath::Max( + FMath::Clamp((FMath::Abs(Position.X) - (Wall_Distance - Wall_Close_Distance)) / Wall_Fade_Distance, 0.0f, 1.0f), + FMath::Clamp((FMath::Abs(Position.Y) - (Wall_Distance - Wall_Close_Distance)) / Wall_Fade_Distance, 0.0f, 1.0f) + ); +} + +bool ACAVEOverlayController::PositionInDoorOpening(FVector Position) +{ + return FMath::IsWithinInclusive(-Position.X, Wall_Distance + 10 - 20 - Wall_Close_Distance, Wall_Distance + 10) //Overlap both sides 10cm + && FMath::IsWithinInclusive(-Position.Y, Wall_Distance + 10 - Door_Current_Opening_Width_Absolute, Wall_Distance + 10); //Overlap one side 10cm +} + +void ACAVEOverlayController::RefreshPawnComponents() +{ + TArray<UDisplayClusterSceneComponent*> Pawn_Components; + Player_Pawn->GetComponents<UDisplayClusterSceneComponent>(Pawn_Components); + for (UDisplayClusterSceneComponent* c : Pawn_Components) + { + if (c->GetName().Equals("cave_origin", ESearchCase::IgnoreCase)) Cave_Origin = c; + if (c->GetName().Equals("shutter_glasses", ESearchCase::IgnoreCase)) Shutter_Glasses = c; + } +} + +// Called every frame +void ACAVEOverlayController::Tick(float DeltaTime) +{ + Super::Tick(DeltaTime); + + if (!bDisplay_Cluster_Mode) return; // Not our business + + RefreshPawnComponents(); + if (!bAttached && Cave_Origin) + { + AttachToComponent(Cave_Origin, FAttachmentTransformRules::SnapToTargetNotIncludingScale); + bAttached = true; + } + + //FPS Counter + if (Overlay) { + if (Door_Current_Mode == EDoor_Mode::DOOR_DEBUG && ContainsFString(Screens_FPS, IDisplayCluster::Get().GetClusterMgr()->GetNodeId())) + { + Overlay->FPS->SetText(FText::FromString(FString::Printf(TEXT("FPS: %.1f"), 1.0f / DeltaTime))); + } + else + { + Overlay->FPS->SetText(FText::FromString("")); + } + } + + if (!Shutter_Glasses) return; //Display Cluster not initialized + + //Tape Logic + FVector Shutter_Position = Shutter_Glasses->GetRelativeTransform().GetLocation(); + bool bOverlay_Visible = FMath::IsWithinInclusive(Shutter_Position.GetAbsMax(), Wall_Distance - Wall_Close_Distance, Wall_Distance); + + if (bOverlay_Visible && !PositionInDoorOpening(Shutter_Position)) + { + Tape_Root->SetVisibility(true, true); + Tape_Root->SetRelativeLocation(Shutter_Position * FVector(0, 0, 1)); //Only apply Z + + float Tape_Opacity = CalculateOpacityFromPosition(Shutter_Position); + Tape_Material_Dynamic_->SetScalarParameterValue("BarrierOpacity", Tape_Opacity); + + if (FMath::IsWithin(FVector2D(Shutter_Position).GetAbsMax(), Wall_Distance - Wall_Warning_Distance, Wall_Distance)) + { + //in warning distance == red tape + Tape_Material_Dynamic_->SetVectorParameterValue("StripeColor", FVector(1, 0, 0)); + } + else + { + Tape_Material_Dynamic_->SetVectorParameterValue("StripeColor", FVector(1, 1, 0)); + } + } + else + { + Tape_Root->SetVisibility(false, true); + } + + //Sign Logic + UDisplayClusterSceneComponent* Flystick = IDisplayCluster::Get().GetGameMgr()->GetNodeById(TEXT("flystick")); + if (Flystick) + { + FVector Flystick_Position = Flystick->GetRelativeTransform().GetLocation(); + bool bFlystick_In_Door = PositionInDoorOpening(Flystick_Position); + float Sign_Opacity = CalculateOpacityFromPosition(Flystick_Position); + + Sign_Negative_X->SetRelativeLocation(FVector(-Wall_Distance, Flystick_Position.Y, Flystick_Position.Z)); + Sign_Negative_Y->SetRelativeLocation(FVector(Flystick_Position.X, -Wall_Distance, Flystick_Position.Z)); + Sign_Positive_X->SetRelativeLocation(FVector(+Wall_Distance, Flystick_Position.Y, Flystick_Position.Z)); + Sign_Positive_Y->SetRelativeLocation(FVector(Flystick_Position.X, +Wall_Distance, Flystick_Position.Z)); + + Sign_Negative_X->SetVisibility(FMath::IsWithin(-Flystick_Position.X, Wall_Distance - Wall_Close_Distance, Wall_Distance) && !bFlystick_In_Door); + Sign_Negative_Y->SetVisibility(FMath::IsWithin(-Flystick_Position.Y, Wall_Distance - Wall_Close_Distance, Wall_Distance) && !bFlystick_In_Door); + Sign_Positive_X->SetVisibility(FMath::IsWithin(+Flystick_Position.X, Wall_Distance - Wall_Close_Distance, Wall_Distance) && !bFlystick_In_Door); + Sign_Positive_Y->SetVisibility(FMath::IsWithin(+Flystick_Position.Y, Wall_Distance - Wall_Close_Distance, Wall_Distance) && !bFlystick_In_Door); + + Sign_Material_Dynamic_->SetScalarParameterValue("SignOpacity", Sign_Opacity); + } + else + { + Sign_Negative_X->SetVisibility(false); + Sign_Negative_Y->SetVisibility(false); + Sign_Positive_X->SetVisibility(false); + Sign_Positive_Y->SetVisibility(false); + } +} diff --git a/Source/CAVEOverlay/Public/CAVEOverlay.h b/Source/CAVEOverlay/Public/CAVEOverlay.h new file mode 100644 index 0000000000000000000000000000000000000000..9c40bae6cc3ece9f571c7513dbde291ec1670486 --- /dev/null +++ b/Source/CAVEOverlay/Public/CAVEOverlay.h @@ -0,0 +1,15 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleManager.h" + +class FCAVEOverlayModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; diff --git a/Source/CAVEOverlay/Public/CAVEOverlayController.h b/Source/CAVEOverlay/Public/CAVEOverlayController.h new file mode 100644 index 0000000000000000000000000000000000000000..af238828aea072adf42eb07f02e502c6230507b1 --- /dev/null +++ b/Source/CAVEOverlay/Public/CAVEOverlayController.h @@ -0,0 +1,101 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "DoorOverlayData.h" +#include "Engine/Engine.h" +#include <array> +#include "Components/StaticMeshComponent.h" +#include "Materials/Material.h" +#include "Materials/MaterialInstanceDynamic.h" +#include "DisplayClusterExtensions/Public/VirtualRealityPawn.h" +#include "DisplayCluster/Public/DisplayClusterSceneComponent.h" +#include "CAVEOverlayController.generated.h" + +DECLARE_LOG_CATEGORY_EXTERN(LogCAVEOverlay, Log, All); + +UCLASS() +class CAVEOVERLAY_API ACAVEOverlayController : public AActor +{ + GENERATED_BODY() +public: + ACAVEOverlayController(); +protected: + // Called when the game starts or when spawned + virtual void BeginPlay() override; + virtual void PostInitializeComponents() override; + +private: + //Execution Modes + bool bHMD_Mode = false; + bool bDisplay_Cluster_Mode = false; + + //Screen Types + enum EScreen_Type { SCREEN_MASTER, SCREEN_NORMAL, SCREEN_DOOR_PARTIAL, SCREEN_DOOR }; + + EScreen_Type Screen_Type = SCREEN_NORMAL; + const std::array<FString, 4> Screens_Door = {"node_bul_left_eye", "node_bul_right_eye", "node_bll_left_eye", "node_bll_right_eye"}; + const std::array<FString, 4> Screens_Door_Partial = {"node_bur_left_eye", "node_bur_right_eye", "node_blr_left_eye", "node_blr_right_eye"}; + const std::array<FString, 5> Screens_FPS = {"node_rur_left_eye", "node_rur_right_eye", "node_lur_left_eye", "node_lur_right_eye", "node_main"}; + const FString Screen_Main = "node_main"; + + //Door Mode + enum EDoor_Mode { DOOR_PARTIALLY_OPEN = 0, DOOR_OPEN = 1, DOOR_CLOSED = 2, DOOR_DEBUG = 3, DOOR_NUM_MODES = 4 }; + + const FString Door_Mode_Names[DOOR_NUM_MODES] = {"Partially Open", "Open", "Closed", "Debug"}; + EDoor_Mode Door_Current_Mode = DOOR_PARTIALLY_OPEN; + const float Door_Opening_Width_Relative = 0.522; //%, used for the overlay width on the screen + const float Door_Opening_Width_Absolute = 165; //cm, used for the non tape part at the door + const float Wall_Distance = 262.5; //cm, distance from center to a wall, *2 = wall width + const float Wall_Close_Distance = 75; //cm, the distance considered to be too close to the walls + const float Wall_Fade_Distance = 35; //cm, the distance over which the tape is faded + const float Wall_Warning_Distance = 40; //cm, distance on which the tape turns red, measured from wall + float Door_Current_Opening_Width_Absolute = 0; + + //Overlay + TSubclassOf<class UDoorOverlayData> Overlay_Class; + UDoorOverlayData* Overlay; + + //Geometry and Material + UStaticMeshComponent* CreateMeshComponent(const FName& Name, UStaticMesh* Mesh, USceneComponent* Parent); + UMaterial* Tape_Material = nullptr; + UMaterial* Sign_Material = nullptr; + float CalculateOpacityFromPosition(FVector Position); + bool PositionInDoorOpening(FVector Position); + + //Pawn Components + bool bAttached = false; + void RefreshPawnComponents(); + AVirtualRealityPawn* Player_Pawn; + UDisplayClusterSceneComponent* Cave_Origin; + UDisplayClusterSceneComponent* Shutter_Glasses; + +public: + + // Called every frame + virtual void Tick(float DeltaTime) override; + + void CycleDoorType(); + void SetDoorMode(EDoor_Mode M); + + //Signs and Banners + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "CAVEOverlay", meta = (AllowPrivateAccess = "true")) USceneComponent* Root = nullptr; + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "CAVEOverlay", meta = (AllowPrivateAccess = "true")) USceneComponent* Tape_Root = nullptr; + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "CAVEOverlay", meta = (AllowPrivateAccess = "true")) USceneComponent* Sign_Root = nullptr; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "CAVEOverlay", meta = (AllowPrivateAccess = "true")) UStaticMeshComponent* Tape_Negative_Y = nullptr; + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "CAVEOverlay", meta = (AllowPrivateAccess = "true")) UStaticMeshComponent* Tape_Negative_X = nullptr; + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "CAVEOverlay", meta = (AllowPrivateAccess = "true")) UStaticMeshComponent* Tape_Positive_Y = nullptr; + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "CAVEOverlay", meta = (AllowPrivateAccess = "true")) UStaticMeshComponent* Tape_Positive_X = nullptr; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "CAVEOverlay", meta = (AllowPrivateAccess = "true")) UStaticMeshComponent* Sign_Negative_Y = nullptr; + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "CAVEOverlay", meta = (AllowPrivateAccess = "true")) UStaticMeshComponent* Sign_Negative_X = nullptr; + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "CAVEOverlay", meta = (AllowPrivateAccess = "true")) UStaticMeshComponent* Sign_Positive_Y = nullptr; + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "CAVEOverlay", meta = (AllowPrivateAccess = "true")) UStaticMeshComponent* Sign_Positive_X = nullptr; + + UMaterialInstanceDynamic* Tape_Material_Dynamic_ = nullptr; + UMaterialInstanceDynamic* Sign_Material_Dynamic_ = nullptr; + UStaticMesh* Plane_Mesh_ = nullptr; +}; diff --git a/Source/CAVEOverlay/Public/DoorOverlayData.h b/Source/CAVEOverlay/Public/DoorOverlayData.h new file mode 100644 index 0000000000000000000000000000000000000000..97dbb5987b79a5467e8075b1a3a5dba4569a8f2c --- /dev/null +++ b/Source/CAVEOverlay/Public/DoorOverlayData.h @@ -0,0 +1,30 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "TextBlock.h" +#include "Border.h" +#include "DoorOverlayData.generated.h" + +/** + * + */ +UCLASS() +class CAVEOVERLAY_API UDoorOverlayData : public UUserWidget +{ + GENERATED_BODY() + +public: + //These declarations are magically bound to the UMG blueprints elements, + //if they are named the same + UPROPERTY(BlueprintReadWrite, meta = (BindWidget)) + UTextBlock* CornerText; + + UPROPERTY(BlueprintReadWrite, meta = (BindWidget)) + UBorder* BlackBox; + + UPROPERTY(BlueprintReadWrite, meta = (BindWidget)) + UTextBlock* FPS; +};