/****************************************************************************** * Copyright 2017- Tobii Technology AB. All rights reserved. * * @author Temaran | Fredrik Lindh | fredrik.lindh@tobii.com | https://github.com/Temaran ******************************************************************************/ #include "TobiiGazeFocusableComponent.h" #include "TobiiGTOMBlueprintLibrary.h" #include "TobiiGTOMModule.h" #include "DrawDebugHelpers.h" #include "Engine/Engine.h" #include "Components/PrimitiveComponent.h" #include "Components/WidgetComponent.h" #include "GameFramework/PlayerController.h" #include "HAL/IConsoleManager.h" static TAutoConsoleVariable CVarDrawCleanUIDebug(TEXT("tobii.debug.CleanUI"), 1, TEXT("0 - CleanUI debug visualizations are not displayed. 1 - CleanUI debug visualizations are displayed.")); static TMap> GRegisteredTobiiFocusableComponents; static TSet* GazeFocusPrioSetA = nullptr; static TSet* GazeFocusPrioSetB = nullptr; static bool bUseGazeFocusPrioSetA = true; void UTobiiGazeFocusableComponent::ClearFocusableComponents() { GRegisteredTobiiFocusableComponents.Empty(); } const TMap>& UTobiiGazeFocusableComponent::GetFocusableComponents() { return GRegisteredTobiiFocusableComponents; } void UTobiiGazeFocusableComponent::UpdateGazeFocusPrio(FVector POVActorLocation, int32 MaxNrFocusables) { TMap PrioMap; TSet NonPrioSet; //Determine which primitive components should be considered for gaze focus. for (auto& FocusableElement : GRegisteredTobiiFocusableComponents) { if (FocusableElement.Value.IsValid()) { const UTobiiGazeFocusableComponent* FocusableComponent = FocusableElement.Value.Get(); const AActor* FocusableActor = FocusableComponent->GetOwner(); if (FocusableActor != nullptr) { TArray PrimitiveComponents = FocusableActor->GetComponentsByClass(UPrimitiveComponent::StaticClass()); //TArray PrimativeComponents = FocusableActor->GetComponents for (UActorComponent* Comp : PrimitiveComponents) { UPrimitiveComponent* PrimitiveComponent = (UPrimitiveComponent*)Comp; if (PrimitiveComponent == nullptr) { continue; } bool bSkipComponent = false; float MaxDistance = 0.0f; float Priority = 0.0f; for (const FName& CurrentTag : PrimitiveComponent->ComponentTags) { if (CurrentTag == FTobiiPrimitiveComponentGazeFocusTags::NotGazeFocusableTag) { bSkipComponent = true; break; } FString CurrentTagString = CurrentTag.ToString(); if (CurrentTagString.StartsWith(FTobiiPrimitiveComponentGazeFocusTags::GazeFocusableMaximumDistanceTag)) { FString Argument = CurrentTagString.RightChop(FTobiiPrimitiveComponentGazeFocusTags::GazeFocusableMaximumDistanceTag.Len()); MaxDistance = FCString::Atof(*Argument); } if (CurrentTagString.StartsWith(FTobiiPrimitiveComponentGazeFocusTags::GazeFocusablePriorityTag)) { FString Argument = CurrentTagString.RightChop(FTobiiPrimitiveComponentGazeFocusTags::GazeFocusablePriorityTag.Len()); Priority = FCString::Atof(*Argument); } } if (bSkipComponent) { continue; } if (MaxDistance == 0.0f) { MaxDistance = FocusableComponent->DefaultMaxFocusDistance; } if (Priority == 0.0f) { Priority = FocusableComponent->DefaultFocusPriority; } bool bAcceptComponent = true; if (MaxDistance != 0.0f) { float Distance = FVector::Distance(POVActorLocation, PrimitiveComponent->GetComponentLocation()); if (Distance > MaxDistance) { bAcceptComponent = false; } } if (bAcceptComponent) { if (Priority == 0.0f) { NonPrioSet.Add(PrimitiveComponent->ComponentId); } else { PrioMap.Add(PrimitiveComponent->ComponentId, Priority); } } } } } } PrioMap.ValueSort(TGreater()); TSet* NewGazeFocusPrioSet = new TSet(); MaxNrFocusables -= 1; //Since we test before add, take that into account for (auto& SortedElement : PrioMap) { if (NewGazeFocusPrioSet->Num() >= MaxNrFocusables) { break; } NewGazeFocusPrioSet->Add(SortedElement.Key); } for (auto NonPrioElement : NonPrioSet) { if (NewGazeFocusPrioSet->Num() >= MaxNrFocusables) { break; } NewGazeFocusPrioSet->Add(NonPrioElement); } //Destroy old set, set our new one and flip our record pointer so the one we swapped into becomes the active one. TSet*& ListPtr = bUseGazeFocusPrioSetA ? GazeFocusPrioSetB : GazeFocusPrioSetA; if (ListPtr != nullptr) { delete ListPtr; } ListPtr = NewGazeFocusPrioSet; bUseGazeFocusPrioSetA = !bUseGazeFocusPrioSetA; } const TSet* UTobiiGazeFocusableComponent::GetGazeFocusPrioSet() { return bUseGazeFocusPrioSetA ? GazeFocusPrioSetA : GazeFocusPrioSetB; } bool UTobiiGazeFocusableComponent::IsPrimitiveFocusable(UPrimitiveComponent* Primitive) { if (Primitive == nullptr || !Primitive->IsVisible() || Primitive->ComponentHasTag(FTobiiPrimitiveComponentGazeFocusTags::NotGazeFocusableTag)) { //Override disabled return false; } else if (Primitive->ComponentHasTag(FTobiiPrimitiveComponentGazeFocusTags::GazeFocusableTag)) { //Override enabled return true; } else if (Primitive->GetOwner()) { UTobiiGazeFocusableComponent* GazeFocusable = (UTobiiGazeFocusableComponent*)Primitive->GetOwner()->GetComponentByClass(UTobiiGazeFocusableComponent::StaticClass()); if (GazeFocusable != nullptr && GazeFocusable->bDefaultIsFocusable) { return true; } } return false; } bool UTobiiGazeFocusableComponent::GetMaxFocusDistanceForPrimitive(UPrimitiveComponent* Primitive, float& OutMaxDistance) { if (Primitive == nullptr) { return false; } bool bMaxDistanceFound = false; for (const FName& CurrentTag : Primitive->ComponentTags) { FString CurrentTagString = CurrentTag.ToString(); if (CurrentTagString.StartsWith(FTobiiPrimitiveComponentGazeFocusTags::GazeFocusableMaximumDistanceTag)) { FString Argument = CurrentTagString.RightChop(FTobiiPrimitiveComponentGazeFocusTags::GazeFocusableMaximumDistanceTag.Len()); OutMaxDistance = FCString::Atof(*Argument); bMaxDistanceFound = OutMaxDistance != 0.0f; //Atof returns 0.0 on failure } } if (!bMaxDistanceFound && Primitive->GetOwner()) { UTobiiGazeFocusableComponent* GazeFocusableComponent = (UTobiiGazeFocusableComponent*)Primitive->GetOwner()->GetComponentByClass(UTobiiGazeFocusableComponent::StaticClass()); if (GazeFocusableComponent != nullptr && GazeFocusableComponent->DefaultMaxFocusDistance > FLT_EPSILON) { OutMaxDistance = GazeFocusableComponent->DefaultMaxFocusDistance; bMaxDistanceFound = true; } } return bMaxDistanceFound; } float UTobiiGazeFocusableComponent::GetFocusPriorityForPrimitive(UPrimitiveComponent* Primitive) { float Priority = 0.0f; bool bPriorityFound = false; if (Primitive == nullptr) { return Priority; } for (const FName& CurrentTag : Primitive->ComponentTags) { FString CurrentTagString = CurrentTag.ToString(); if (CurrentTagString.StartsWith(FTobiiPrimitiveComponentGazeFocusTags::GazeFocusablePriorityTag)) { FString Argument = CurrentTagString.RightChop(FTobiiPrimitiveComponentGazeFocusTags::GazeFocusablePriorityTag.Len()); Priority = FCString::Atof(*Argument); bPriorityFound = Priority != 0.0f; //Atof returns 0.0 on failure } } if (!bPriorityFound && Primitive->GetOwner()) { UTobiiGazeFocusableComponent* GazeFocusableComponent = (UTobiiGazeFocusableComponent*)Primitive->GetOwner()->GetComponentByClass(UTobiiGazeFocusableComponent::StaticClass()); if (GazeFocusableComponent != nullptr) { Priority = GazeFocusableComponent->DefaultFocusPriority; } } return Priority; } FName UTobiiGazeFocusableComponent::GetFocusLayerForPrimitive(UPrimitiveComponent* Primitive) { FName FocusLayer = FName("Default"); bool bFocusLayerFound = false; if (Primitive == nullptr) { return FocusLayer; } for (const FName& CurrentTag : Primitive->ComponentTags) { FString CurrentTagString = CurrentTag.ToString(); if (CurrentTagString.StartsWith(FTobiiPrimitiveComponentGazeFocusTags::GazeFocusableLayerTag)) { FString Argument = CurrentTagString.RightChop(FTobiiPrimitiveComponentGazeFocusTags::GazeFocusableLayerTag.Len()); Argument.TrimStartAndEndInline(); if (!Argument.IsEmpty()) { FocusLayer = FName(*Argument); bFocusLayerFound = true; } } } if (!bFocusLayerFound && Primitive->GetOwner()) { UTobiiGazeFocusableComponent* GazeFocusableComponent = (UTobiiGazeFocusableComponent*)Primitive->GetOwner()->GetComponentByClass(UTobiiGazeFocusableComponent::StaticClass()); if (GazeFocusableComponent != nullptr) { FocusLayer = GazeFocusableComponent->DefaultFocusLayer; } } return FocusLayer; } bool UTobiiGazeFocusableComponent::IsWidgetFocusable(UTobiiGazeFocusableWidget* GazeFocusableWidget) { if (GazeFocusableWidget == nullptr || !GazeFocusableWidget->IsVisible() || !GazeFocusableWidget->bIsGazeFocusable) { return false; } else if (GazeFocusableWidget->GetWorldSpaceHostWidgetComponent() != nullptr && GazeFocusableWidget->GetWorldSpaceHostWidgetComponent()->GetOwner() != nullptr) { if (!GazeFocusableWidget->GetWorldSpaceHostWidgetComponent()->IsVisible()) { return false; } UTobiiGazeFocusableComponent* GazeFocusable = (UTobiiGazeFocusableComponent*)GazeFocusableWidget->GetWorldSpaceHostWidgetComponent()->GetOwner()->GetComponentByClass(UTobiiGazeFocusableComponent::StaticClass()); if (GazeFocusable != nullptr) { return GazeFocusable->bDefaultIsFocusable; } } return true; } bool UTobiiGazeFocusableComponent::GetMaxFocusDistanceForWidget(UTobiiGazeFocusableWidget* GazeFocusableWidget, float& OutMaxDistance) { if (GazeFocusableWidget == nullptr) { return false; } bool bMaxDistanceFound = false; if (GazeFocusableWidget->MaxFocusDistance >= FLT_EPSILON) { OutMaxDistance = GazeFocusableWidget->MaxFocusDistance; bMaxDistanceFound = true; } else if (GazeFocusableWidget->GetWorldSpaceHostWidgetComponent() != nullptr && GazeFocusableWidget->GetWorldSpaceHostWidgetComponent()->GetOwner() != nullptr) { UTobiiGazeFocusableComponent* GazeFocusableComponent = (UTobiiGazeFocusableComponent*)GazeFocusableWidget->GetWorldSpaceHostWidgetComponent()->GetOwner()->GetComponentByClass(UTobiiGazeFocusableComponent::StaticClass()); if (GazeFocusableComponent != nullptr && GazeFocusableComponent->DefaultMaxFocusDistance > FLT_EPSILON) { OutMaxDistance = GazeFocusableComponent->DefaultMaxFocusDistance; bMaxDistanceFound = true; } } return bMaxDistanceFound; } float UTobiiGazeFocusableComponent::GetFocusPriorityForWidget(UTobiiGazeFocusableWidget* GazeFocusableWidget) { if (GazeFocusableWidget == nullptr) { return 0.0f; } if (GazeFocusableWidget->GazeFocusPriority >= 0.0f) { return GazeFocusableWidget->GazeFocusPriority; } else if (GazeFocusableWidget->GetWorldSpaceHostWidgetComponent() != nullptr && GazeFocusableWidget->GetWorldSpaceHostWidgetComponent()->GetOwner() != nullptr) { UTobiiGazeFocusableComponent* GazeFocusableComponent = (UTobiiGazeFocusableComponent*)GazeFocusableWidget->GetWorldSpaceHostWidgetComponent()->GetOwner()->GetComponentByClass(UTobiiGazeFocusableComponent::StaticClass()); if (GazeFocusableComponent) { return GazeFocusableComponent->DefaultFocusPriority; } } return 0.0f; } FName UTobiiGazeFocusableComponent::GetFocusLayerForWidget(UTobiiGazeFocusableWidget* GazeFocusableWidget) { FName FocusLayer = FName("Default"); if (GazeFocusableWidget == nullptr) { return FocusLayer; } if (!GazeFocusableWidget->FocusLayer.IsEmpty()) { FocusLayer = FName(*GazeFocusableWidget->FocusLayer); } else if (GazeFocusableWidget->GetWorldSpaceHostWidgetComponent() != nullptr && GazeFocusableWidget->GetWorldSpaceHostWidgetComponent()->GetOwner() != nullptr) { UTobiiGazeFocusableComponent* GazeFocusableComponent = (UTobiiGazeFocusableComponent*)GazeFocusableWidget->GetWorldSpaceHostWidgetComponent()->GetOwner()->GetComponentByClass(UTobiiGazeFocusableComponent::StaticClass()); if (GazeFocusableComponent != nullptr) { FocusLayer = GazeFocusableComponent->DefaultFocusLayer; } } return FocusLayer; } ////////////////////////////////////////////// UTobiiGazeFocusableComponent::UTobiiGazeFocusableComponent() : bDefaultIsFocusable(true) , DefaultFocusPriority(0.0f) , DefaultMaxFocusDistance(0.0f) , DefaultFocusLayer("Default") , bWidgetsRefreshedOnce(false) { PrimaryComponentTick.bCanEverTick = true; } void UTobiiGazeFocusableComponent::PrimitiveReceivedGazeFocus(UPrimitiveComponent* FocusedComponent) { OnPrimitiveReceivedGazeFocus.Broadcast(FocusedComponent); } void UTobiiGazeFocusableComponent::PrimitiveLostGazeFocus(UPrimitiveComponent* FocusedComponent) { OnPrimitiveLostGazeFocus.Broadcast(FocusedComponent); } void UTobiiGazeFocusableComponent::WidgetReceivedGazeFocus( UTobiiGazeFocusableWidget* FocusedWidget) { OnWidgetReceivedGazeFocus.Broadcast(FocusedWidget); } void UTobiiGazeFocusableComponent::WidgetLostGazeFocus(UTobiiGazeFocusableWidget* FocusedWidget) { OnWidgetLostGazeFocus.Broadcast(FocusedWidget); } const TArray>& UTobiiGazeFocusableComponent::GetFocusableWidgetsForPrimitiveComponent(UPrimitiveComponent* PrimitiveComponent) { if (PrimitiveComponent == nullptr || !PrimitiveMap.Contains(PrimitiveComponent->GetUniqueID())) { static TArray> DefaultArray; return DefaultArray; } return PrimitiveMap[PrimitiveComponent->GetUniqueID()]; } void UTobiiGazeFocusableComponent::BeginPlay() { Super::BeginPlay(); bWidgetsRefreshedOnce = false; GRegisteredTobiiFocusableComponents.Add(GetUniqueID(), this); } void UTobiiGazeFocusableComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) { GRegisteredTobiiFocusableComponents.Remove(GetUniqueID()); Super::EndPlay(EndPlayReason); } void UTobiiGazeFocusableComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); if (GEngine == nullptr || GEngine->GameViewport == nullptr || GEngine->GameViewport->GetGameViewport() == nullptr) { return; } if (!bWidgetsRefreshedOnce) { bWidgetsRefreshedOnce = true; RefreshOwnedWidgets(); } } void UTobiiGazeFocusableComponent::RefreshOwnedWidgets() { AllFocusableWidgets.Empty(); PrimitiveMap.Empty(); AActor* Owner = GetOwner(); if (Owner != nullptr) { TArray WidgetComponents = Owner->GetComponentsByClass(UWidgetComponent::StaticClass()); for (UActorComponent* Component : WidgetComponents) { UWidgetComponent* WidgetComponent = (UWidgetComponent*)Component; TArray>& NewFocusableArray = PrimitiveMap.Add(WidgetComponent->GetUniqueID(), TArray>()); GatherGazeFocusableWidgets(WidgetComponent->GetUserWidgetObject(), NewFocusableArray, WidgetComponent); } } } void UTobiiGazeFocusableComponent::GatherGazeFocusableWidgets(UWidget* Parent, TArray>& WidgetArray, UWidgetComponent* OptionalHostWidgetComponent) { UUserWidget* UserWidget = Cast(Parent); if (UserWidget != nullptr) { GatherGazeFocusableWidgets(UserWidget->GetRootWidget(), WidgetArray, OptionalHostWidgetComponent); } else { UTobiiGazeFocusableWidget* CleanUIContainer = Cast(Parent); if (CleanUIContainer != nullptr) { CleanUIContainer->RegisterWidgetToGTOM(OptionalHostWidgetComponent); AllFocusableWidgets.Add(CleanUIContainer->GetUniqueID(), CleanUIContainer); WidgetArray.Add(CleanUIContainer); } UPanelWidget* Panel = Cast(Parent); if (Panel != nullptr) { for (int32 ChildIdx = 0; ChildIdx < Panel->GetChildrenCount(); ChildIdx++) { GatherGazeFocusableWidgets(Panel->GetChildAt(ChildIdx), WidgetArray, OptionalHostWidgetComponent); } } } }