Skip to content
Snippets Groups Projects
Select Git revision
  • 4.22
  • master default protected
  • develop protected
  • 4.22.1 protected
4 results

CAVEOverlayController.cpp

Blame
  • CAVEOverlayController.cpp 12.58 KiB
    // Fill out your copyright notice in the Description page of Project Settings.
    
    
    #include "CAVEOverlayController.h"
    
    DEFINE_LOG_CATEGORY(CAVEOverlayLog);
    
    template<std::size_t SIZE> bool containsFString(const std::array<FString, SIZE> &a, const FString &s) {
    	for (FString cs : a) {
    		if (cs.Equals(s, 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(CAVEOverlayLog, 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(CAVEOverlayLog, Error, TEXT("Could not find the DoorOverlay class. Have you renamed it?"));
    	}
    
    	//Creation of subcomponents
    	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<DOOR_MODE>((door_current_mode_ + 1) % DOOR_NUM_MODES);
    	SetDoorMode(door_current_mode_);
    }
    
    void ACAVEOverlayController::SetDoorMode(DOOR_MODE m)
    {
    	switch (door_current_mode_) {
    	case DOOR_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 DOOR_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 DOOR_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;
    	}
    	if (screen_type_ == SCREEN_NORMAL) overlay_->BlackBox->SetRenderScale(FVector2D(0, 1)); //no overlay
    
    	UE_LOG(CAVEOverlayLog, 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
    	hmd_mode_ = GEngine->XRSystem.IsValid() && GEngine->XRSystem->IsHeadTrackingAllowed();
    	display_cluster_mode_ = IDisplayCluster::Get().GetOperationMode() == EDisplayClusterOperationMode::Cluster;
    
    	if (!display_cluster_mode_) return; // Not our business
    
    	//Input config
    	InputComponent->BindKey(FKey("nDisplayButton4"), EInputEvent::IE_Pressed, this, &ACAVEOverlayController::CycleDoorType);
    	//
    	//InputComponent->BindKey(EKeys::F10, EInputEvent::IE_Pressed, this, &ACAVEOverlayController::CycleDoorType);
    
    	//Determine the screentype 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 (!attached_ && cave_origin_) {
    		AttachToComponent(cave_origin_, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
    		attached_ = 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 (!display_cluster_mode_) return; // Not our business
    
    	refreshPawnComponents();
    	if (!attached_ && cave_origin_) {
    		AttachToComponent(cave_origin_, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
    		attached_ = true;
    	}
    
    	if (!shutter_glasses_) return; //Display Cluster not initialized
    
    	//Tape Logic
    	FVector shutter_position = shutter_glasses_->GetRelativeTransform().GetLocation();
    	bool overlay_visible = FMath::IsWithinInclusive(shutter_position.GetAbsMax(), wall_distance_ - wall_close_distance_, wall_distance_);
    
    	if (overlay_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 flystick_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_) && !flystick_in_door);
    		sign_negative_y->SetVisibility(FMath::IsWithin(-flystick_position.Y, wall_distance_ - wall_close_distance_, wall_distance_) && !flystick_in_door);
    		sign_positive_x->SetVisibility(FMath::IsWithin(+flystick_position.X, wall_distance_ - wall_close_distance_, wall_distance_) && !flystick_in_door);
    		sign_positive_y->SetVisibility(FMath::IsWithin(+flystick_position.Y, wall_distance_ - wall_close_distance_, wall_distance_) && !flystick_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);
    	}
    }