123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026 |
- /*******************************************************************************
- The content of this file includes portions of the proprietary AUDIOKINETIC Wwise
- Technology released in source code form as part of the game integration package.
- The content of this file may not be used without valid licenses to the
- AUDIOKINETIC Wwise Technology.
- Note that the use of the game engine is subject to the Unreal(R) Engine End User
- License Agreement at https://www.unrealengine.com/en-US/eula/unreal
-
- License Usage
-
- Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use
- this file in accordance with the end user license agreement provided with the
- software or, alternatively, in accordance with the terms contained
- in a written agreement between you and Audiokinetic Inc.
- Copyright (c) 2023 Audiokinetic Inc.
- *******************************************************************************/
- /*=============================================================================
- AAkAcousticPortal.cpp:
- =============================================================================*/
- #include "AkAcousticPortal.h"
- #include "AkAudioDevice.h"
- #include "AkComponentHelpers.h"
- #include "AkSpatialAudioHelper.h"
- #include "AkSpatialAudioDrawUtils.h"
- #include "Components/BrushComponent.h"
- #include "Model.h"
- #include "EngineUtils.h"
- #include "AkRoomComponent.h"
- #include "AkComponent.h"
- #include "AkCustomVersion.h"
- #include "Kismet/KismetMathLibrary.h"
- #include "AkSpatialAudioVolume.h"
- // A standard AAkAcousticPortal is based on a cube brush with verts at [+/-]100 X,Y,Z.
- static const float kDefaultBrushExtents = 100.f;
- // min portal size, in cm. For raycasts
- static const float kMinPortalSize = 10.0f;
- #if WITH_EDITOR
- #include "AkDrawPortalComponent.h"
- #include "AkAudioStyle.h"
- #include "LevelEditorViewport.h"
- #endif
- UAkPortalComponent::UAkPortalComponent(const class FObjectInitializer& ObjectInitializer) :
- Super(ObjectInitializer)
- {
- ObstructionRefreshInterval = 0.f;
- PortalState = InitialState;
- bUseAttachParentBound = true;
- FrontRoom = nullptr;
- BackRoom = nullptr;
- PrimaryComponentTick.bCanEverTick = true;
- PrimaryComponentTick.bStartWithTickEnabled = true;
- bTickInEditor = true;
- #if WITH_EDITOR
- bWantsOnUpdateTransform = true;
- bWantsInitializeComponent = true;
- #else
- bWantsOnUpdateTransform = bDynamic;
- #endif
- #if WITH_EDITOR
- if (AkSpatialAudioHelper::GetObjectReplacedEvent())
- {
- AkSpatialAudioHelper::GetObjectReplacedEvent()->AddUObject(this, &UAkPortalComponent::HandleObjectsReplaced);
- }
- #endif
- }
- void UAkPortalComponent::OnRegister()
- {
- Super::OnRegister();
- SetRelativeTransform(FTransform::Identity);
- InitializeParent();
- UpdateConnectedRooms();
- UWorld* world = GetWorld();
- if (world != nullptr)
- SetSpatialAudioPortal();
- #if WITH_EDITOR
- if (GetDefault<UAkSettings>()->VisualizeRoomsAndPortals)
- {
- InitializeDrawComponent();
- }
- #endif
- }
- void UAkPortalComponent::OnUnregister()
- {
- Super::OnUnregister();
- #if WITH_EDITOR
- if (!HasAnyFlags(RF_Transient))
- {
- DestroyTextVisualizers();
- }
- #endif
- FAkAudioDevice * Dev = FAkAudioDevice::Get();
- if (Dev != nullptr)
- {
- Dev->RemoveSpatialAudioPortal(this);
- }
- }
- #if WITH_EDITOR
- void UAkPortalComponent::BeginDestroy()
- {
- Super::BeginDestroy();
- if (AkSpatialAudioHelper::GetObjectReplacedEvent())
- {
- AkSpatialAudioHelper::GetObjectReplacedEvent()->RemoveAll(this);
- }
- }
- void UAkPortalComponent::HandleObjectsReplaced(const TMap<UObject*, UObject*>& ReplacementMap)
- {
- if (ReplacementMap.Contains(Parent))
- {
- InitializeParent();
- }
- if (ReplacementMap.Contains(FrontRoom) || ReplacementMap.Contains(BackRoom))
- {
- UpdateConnectedRooms();
- }
- }
- void UAkPortalComponent::InitializeComponent()
- {
- Super::InitializeComponent();
- RegisterVisEnabledCallback();
- }
- void UAkPortalComponent::OnComponentCreated()
- {
- Super::OnComponentCreated();
- RegisterVisEnabledCallback();
- }
- void UAkPortalComponent::PostLoad()
- {
- Super::PostLoad();
- RegisterVisEnabledCallback();
- }
- void UAkPortalComponent::OnComponentDestroyed(bool bDestroyingHierarchy)
- {
- UAkSettings* AkSettings = GetMutableDefault<UAkSettings>();
- AkSettings->OnShowRoomsPortalsChanged.Remove(ShowPortalsChangedHandle);
- ShowPortalsChangedHandle.Reset();
- DestroyDrawComponent();
- }
- #endif // WITH_EDITOR
- bool UAkPortalComponent::MoveComponentImpl(
- const FVector & Delta,
- const FQuat & NewRotation,
- bool bSweep,
- FHitResult * Hit,
- EMoveComponentFlags MoveFlags,
- ETeleportType Teleport)
- {
- if (AkComponentHelpers::DoesMovementRecenterChild(this, Parent, Delta))
- Super::MoveComponentImpl(Delta, NewRotation, bSweep, Hit, MoveFlags, Teleport);
- return false;
- }
- void UAkPortalComponent::OnUpdateTransform(EUpdateTransformFlags UpdateTransformFlags, ETeleportType Teleport)
- {
- PortalNeedUpdated = true;
- #if WITH_EDITOR
- UpdateTextLocRotVis();
- #endif
- }
- #if WITH_EDITOR
- void UAkPortalComponent::RegisterVisEnabledCallback()
- {
- if (!ShowPortalsChangedHandle.IsValid())
- {
- UAkSettings* AkSettings = GetMutableDefault<UAkSettings>();
- ShowPortalsChangedHandle = AkSettings->OnShowRoomsPortalsChanged.AddLambda([this, AkSettings]()
- {
- if (AkSettings->VisualizeRoomsAndPortals)
- {
- InitializeDrawComponent();
- }
- else
- {
- DestroyDrawComponent();
- }
- });
- }
- }
- void UAkPortalComponent::InitializeDrawComponent()
- {
- if (AActor* Owner = GetOwner())
- {
- if (DrawPortalComponent == nullptr)
- {
- DrawPortalComponent = NewObject<UDrawPortalComponent>(Owner, NAME_None, RF_Transactional | RF_TextExportTransient);
- DrawPortalComponent->SetupAttachment(this);
- DrawPortalComponent->SetIsVisualizationComponent(true);
- DrawPortalComponent->CreationMethod = CreationMethod;
- DrawPortalComponent->RegisterComponentWithWorld(GetWorld());
- DrawPortalComponent->MarkRenderStateDirty();
- }
- }
- }
- void UAkPortalComponent::DestroyDrawComponent()
- {
- if (DrawPortalComponent != nullptr)
- {
- DrawPortalComponent->DestroyComponent();
- DrawPortalComponent = nullptr;
- }
- }
- #endif
- void UAkPortalComponent::InitializeParent()
- {
- USceneComponent* SceneParent = GetAttachParent();
- if (SceneParent != nullptr)
- {
- Parent = Cast<UPrimitiveComponent>(SceneParent);
- if (!Parent)
- {
- AkComponentHelpers::LogAttachmentError(this, SceneParent, "UPrimitiveComponent");
- }
- #if WITH_EDITOR
- DestroyTextVisualizers();
- InitTextVisualizers();
- UpdateRoomNames();
- UpdateTextLocRotVis();
- #endif
- }
- }
- void UAkPortalComponent::SetSpatialAudioPortal()
- {
- FAkAudioDevice* AkAudioDevice = FAkAudioDevice::Get();
- if (AkAudioDevice != nullptr)
- {
- AkAudioDevice->SetSpatialAudioPortal(this);
- PortalNeedUpdated = false;
- }
- }
- void UAkPortalComponent::OpenPortal()
- {
- if (PortalState == AkAcousticPortalState::Closed)
- {
- PortalState = AkAcousticPortalState::Open;
- SetSpatialAudioPortal();
- }
- }
- void UAkPortalComponent::ClosePortal()
- {
- if (PortalState == AkAcousticPortalState::Open)
- {
- PortalState = AkAcousticPortalState::Closed;
- SetSpatialAudioPortal();
- }
- }
- AkAcousticPortalState UAkPortalComponent::GetCurrentState() const
- {
- return PortalState;
- }
- void UAkPortalComponent::BeginPlay()
- {
- Super::BeginPlay();
- // If we're PIE, or somehow otherwise in a game world in editor, simulate the bDynamic behaviour.
- #if WITH_EDITOR
- UWorld* world = GetWorld();
- if (world != nullptr && (world->WorldType == EWorldType::Type::Game || world->WorldType == EWorldType::Type::PIE))
- bWantsOnUpdateTransform = bDynamic;
- #endif
- UpdateConnectedRooms();
- ResetPortalState();
- FAkAudioDevice * Dev = FAkAudioDevice::Get();
- if (Dev != nullptr)
- {
- ObstructionService.Init(this, ObstructionRefreshInterval);
- }
- }
- void UAkPortalComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction * ThisTickFunction)
- {
- if (PortalNeedUpdated)
- {
- UpdateConnectedRooms();
- SetSpatialAudioPortal();
- }
- if (GetCurrentState() == AkAcousticPortalState::Open)
- {
- FAkAudioDevice* AkAudioDevice = FAkAudioDevice::Get();
- if (AkAudioDevice)
- {
- UAkComponent* Listener = AkAudioDevice->GetSpatialAudioListener();
- if (Listener != nullptr)
- {
- AkRoomID listenerRoom = Listener->GetSpatialAudioRoom();
- UAkComponentSet set;
- set.Add(Listener);
- ObstructionService.Tick(set, GetOwner()->GetActorLocation(), GetOwner(), listenerRoom, ObstructionCollisionChannel, DeltaTime, ObstructionRefreshInterval);
- }
- }
- }
- #if WITH_EDITOR
- UWorld* World = GetWorld();
- if (World && (World->WorldType == EWorldType::Editor || World->WorldType == EWorldType::PIE))
- {
- // Only show the text renderer for selected actors.
- if (GetOwner()->IsSelected() && !bWasSelected)
- {
- bWasSelected = true;
- UpdateTextVisibility();
- }
- if (!GetOwner()->IsSelected() && bWasSelected)
- {
- bWasSelected = false;
- UpdateTextVisibility();
- }
- }
- #endif
- }
- void UAkPortalComponent::ResetPortalState()
- {
- PortalState = InitialState;
- SetSpatialAudioPortal();
- }
- bool UAkPortalComponent::UpdateConnectedRooms()
- {
- /* Keep note of the rooms and validity before the update. */
- AkRoomID previousFront = GetFrontRoom();
- AkRoomID previousBack = GetBackRoom();
- /* Update the room connections */
- FrontRoom = nullptr;
- BackRoom = nullptr;
- FAkAudioDevice * Dev = FAkAudioDevice::Get();
- FindConnectedComponents(Dev->GetRoomIndex(), FrontRoom, BackRoom);
- LastRoomsUpdate = GetWorld()->GetTimeSeconds();
- PreviousLocation = GetComponentLocation();
- PreviousRotation = GetComponentRotation();
- const bool bRoomsChanged = GetFrontRoom() != previousFront || GetBackRoom() != previousBack;
- #if WITH_EDITOR
- if (bRoomsChanged)
- UpdateRoomNames();
- UpdateTextLocRotVis();
- #endif
- /* Return true if any room connection has changed. */
- return bRoomsChanged;
- }
- UPrimitiveComponent* UAkPortalComponent::GetPrimitiveParent() const
- {
- return Parent;
- }
- FVector UAkPortalComponent::GetExtent() const
- {
- FBoxSphereBounds ComponentBounds = Bounds;
- if (Parent != nullptr)
- {
- FTransform Transform (Parent->GetComponentTransform());
- Transform.SetRotation(FQuat::Identity);
- ComponentBounds = Parent->CalcBounds(Transform);
- }
- return ComponentBounds.BoxExtent;
- }
- AkRoomID UAkPortalComponent::GetFrontRoom() const { return FrontRoom == nullptr ? AkRoomID() : FrontRoom->GetRoomID(); }
- AkRoomID UAkPortalComponent::GetBackRoom() const { return BackRoom == nullptr ? AkRoomID() : BackRoom->GetRoomID(); }
- template <typename tComponent>
- void UAkPortalComponent::FindConnectedComponents(FAkEnvironmentIndex& RoomIndex, tComponent*& out_pFront, tComponent*& out_pBack)
- {
- out_pFront = nullptr;
- out_pBack = nullptr;
- FAkAudioDevice* pAudioDevice = FAkAudioDevice::Get();
- if (pAudioDevice != nullptr && Parent != nullptr)
- {
- float x = GetExtent().X;
- FVector frontVector(x, 0.f, 0.f);
- FTransform toWorld = Parent->GetComponentTransform();
- toWorld.SetScale3D(FVector(1.0f));
- FVector frontPoint = toWorld.TransformPosition(frontVector);
- FVector backPoint = toWorld.TransformPosition(-1 * frontVector);
- TArray<tComponent*> front = RoomIndex.Query<tComponent>(frontPoint, GetWorld());
- if (front.Num() > 0)
- out_pFront = front[0];
- TArray<tComponent*> back = RoomIndex.Query<tComponent>(backPoint, GetWorld());
- if (back.Num() > 0)
- out_pBack = back[0];
- }
- }
- #if WITH_EDITOR
- bool UAkPortalComponent::AreTextVisualizersInitialized() const
- {
- return FrontRoomText != nullptr || BackRoomText != nullptr;
- }
- void UAkPortalComponent::InitTextVisualizers()
- {
- if (!HasAnyFlags(RF_Transient))
- {
- FString NamePrefix = GetOwner()->GetName() + GetName();
- UMaterialInterface* mat = Cast<UMaterialInterface>(FAkAudioStyle::GetAkForegroundTextMaterial());
- FrontRoomText = NewObject<UTextRenderComponent>(GetOuter(), *(NamePrefix + "_FrontRoomName"));
- BackRoomText = NewObject<UTextRenderComponent>(GetOuter(), *(NamePrefix + "_BackRoomName"));
- FrontRoomText->SetText(FText::FromString(""));
- BackRoomText->SetText(FText::FromString(""));
- TArray<UTextRenderComponent*> TextComponents{ FrontRoomText, BackRoomText };
- for (UTextRenderComponent* Text : TextComponents)
- {
- if (mat != nullptr)
- Text->SetTextMaterial(mat);
- Text->RegisterComponentWithWorld(GetWorld());
- Text->AttachToComponent(this, FAttachmentTransformRules::KeepWorldTransform);
- Text->SetRelativeLocation(FVector(0.0f, 0.0f, 0.0f));
- Text->bIsEditorOnly = true;
- Text->bSelectable = true;
- Text->bAlwaysRenderAsText = true;
- Text->SetHorizontalAlignment(EHTA_Center);
- Text->SetWorldScale3D(FVector(1.0f));
- }
- FrontRoomText->SetVerticalAlignment(EVRTA_TextTop);
- BackRoomText->SetVerticalAlignment(EVRTA_TextBottom);
- }
- }
- void UAkPortalComponent::DestroyTextVisualizers()
- {
- if (FrontRoomText != nullptr)
- {
- FrontRoomText->DestroyComponent();
- FrontRoomText = nullptr;
- }
- if (BackRoomText != nullptr)
- {
- BackRoomText->DestroyComponent();
- BackRoomText = nullptr;
- }
- }
- void UAkPortalComponent::UpdateRoomNames()
- {
- if (!Parent || HasAnyFlags(RF_Transient) || !AreTextVisualizersInitialized())
- return;
- if (FrontRoomText != nullptr)
- {
- FrontRoomText->SetText(FText::FromString(""));
- if (FrontRoom != nullptr)
- FrontRoomText->SetText(FText::FromString(FrontRoom->GetRoomName()));
- }
- if (BackRoomText != nullptr)
- {
- BackRoomText->SetText(FText::FromString(""));
- if (BackRoom != nullptr)
- BackRoomText->SetText(FText::FromString(BackRoom->GetRoomName()));
- }
- }
- void UAkPortalComponent::UpdateTextRotations() const
- {
- if (Parent == nullptr || !AreTextVisualizersInitialized())
- return;
- FVector BoxExtent = Parent->CalcBounds(FTransform()).BoxExtent;
- const FTransform T = Parent->GetComponentTransform();
- AkDrawBounds DrawBounds(T, BoxExtent);
- // Setup the font normal to orient the text.
- FVector Front = DrawBounds.FLD() - DrawBounds.BLD();
- Front.Normalize();
- FVector Up = DrawBounds.FLU() - DrawBounds.FLD();
- Up.Normalize();
- if (FrontRoomText != nullptr)
- FrontRoomText->SetVerticalAlignment(EVRTA_TextTop);
- if (BackRoomText != nullptr)
- BackRoomText->SetVerticalAlignment(EVRTA_TextBottom);
- if (FVector::DotProduct(FVector::UpVector, Up) < 0.0f)
- {
- if (FrontRoomText != nullptr)
- FrontRoomText->SetVerticalAlignment(EVRTA_TextBottom);
- if (BackRoomText != nullptr)
- BackRoomText->SetVerticalAlignment(EVRTA_TextTop);
- Up *= -1.0f;
- }
- // Choose to point both text components towards the front or back of the portal, depending on the position of the camera, so that they are both always readable.
- FVector CamToCentre;
- if (GCurrentLevelEditingViewportClient != nullptr)
- {
- CamToCentre = GCurrentLevelEditingViewportClient->GetViewLocation() - Parent->Bounds.Origin;
- if (FVector::DotProduct(CamToCentre, Front) < 0.0f)
- Front *= -1.0f;
- }
- if (FrontRoomText != nullptr)
- FrontRoomText->SetWorldRotation(UKismetMathLibrary::MakeRotFromXZ(Front, Up));
- if (BackRoomText != nullptr)
- BackRoomText->SetWorldRotation(UKismetMathLibrary::MakeRotFromXZ(Front, Up));
- }
- void UAkPortalComponent::UpdateTextVisibility()
- {
- if (!AreTextVisualizersInitialized())
- return;
- bool Visible = false;
- if (GetWorld() != nullptr)
- {
- EWorldType::Type WorldType = GetWorld()->WorldType;
- if (WorldType == EWorldType::Editor)
- {
- Visible = GetOwner() != nullptr && GetOwner()->IsSelected();
- }
- else if (WorldType == EWorldType::EditorPreview)
- {
- Visible = true;
- }
- }
- if (GetOwner() != nullptr)
- {
- if (BackRoomText != nullptr)
- BackRoomText->SetVisibility(Visible);
- if (FrontRoomText != nullptr)
- FrontRoomText->SetVisibility(Visible);
- }
- }
- void UAkPortalComponent::UpdateTextLocRotVis()
- {
- if (Parent == nullptr || !AreTextVisualizersInitialized())
- return;
- FVector BoxExtent = Parent->CalcBounds(FTransform()).BoxExtent;
- const FTransform T = Parent->GetComponentTransform();
- AkDrawBounds DrawBounds(T, BoxExtent);
- float PortalWidth = 0.0f;
- FVector Right;
- (DrawBounds.FRD() - DrawBounds.FLD()).ToDirectionAndLength(Right, PortalWidth);
- // Setup the font normal to orient the text.
- FVector Front = DrawBounds.FLD() - DrawBounds.BLD();
- FVector Up = DrawBounds.FLU() - DrawBounds.FLD();
- if (FrontRoomText != nullptr)
- {
- FrontRoomText->SetWorldScale3D(FVector(1.0f));
- // Get a point at the top center of the local axis-aligned bounds, to position the front room text.
- //FVector Top = Parent->Bounds.Origin + FVector(0.0f, 0.0f, Parent->Bounds.BoxExtent.Z);
- FVector Top = Parent->Bounds.Origin;// +Front + Up;
- // Add a depth offset so that the text sits out at the top front of the portal
- Top += Front * 0.5f;
- Top += Up * 0.5f;
- FrontRoomText->SetWorldLocation(Top);
- const FVector FrontTextWorldSize = FrontRoomText->GetTextWorldSize();
- const float TextWidth = FrontTextWorldSize.GetAbsMax();
- if (TextWidth > PortalWidth && PortalWidth > 0.0f)
- FrontRoomText->SetWorldScale3D(FVector(PortalWidth / TextWidth));
- }
- if (BackRoomText != nullptr)
- {
- BackRoomText->SetWorldScale3D(FVector(1.0f));
- // Get a point at the bottom center of the local axis-aligned bounds, to position the back room text.
- //FVector Bottom = Parent->Bounds.Origin - FVector(0.0f, 0.0f, Parent->Bounds.BoxExtent.Z);
- FVector Bottom = Parent->Bounds.Origin;
- // Add a depth offset so that the text sits out at the bottom back of the portal
- Bottom -= Front * 0.5f;
- Bottom -= Up * 0.5f;
- BackRoomText->SetWorldLocation(Bottom);
- const FVector BackTextWorldSize = BackRoomText->GetTextWorldSize();
- const float TextWidth = BackTextWorldSize.GetAbsMax();
- if (TextWidth > PortalWidth && PortalWidth > 0.0f)
- BackRoomText->SetWorldScale3D(FVector(PortalWidth / TextWidth));
- }
- UpdateTextRotations();
- UpdateTextVisibility();
- }
- #endif
- /*------------------------------------------------------------------------------------
- AAkAcousticPortal
- ------------------------------------------------------------------------------------*/
- AAkAcousticPortal::AAkAcousticPortal(const class FObjectInitializer& ObjectInitializer) :
- Super(ObjectInitializer)
- {
- // Property initialization
- static const FName CollisionProfileName(TEXT("OverlapAll"));
- GetBrushComponent()->SetCollisionProfileName(CollisionProfileName);
- bColored = true;
- BrushColor = FColor(255, 196, 137, 255);
- InitialState = AkAcousticPortalState::Open;
- PrimaryActorTick.bCanEverTick = true;
- PrimaryActorTick.TickGroup = TG_DuringPhysics;
- PrimaryActorTick.bAllowTickOnDedicatedServer = false;
- static const FName PortalComponentName = TEXT("PortalComponent");
- Portal = ObjectInitializer.CreateDefaultSubobject<UAkPortalComponent>(this, PortalComponentName);
- Portal->AttachToComponent(GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
- #if WITH_EDITOR
- CollisionChannel = EAkCollisionChannel::EAKCC_UseIntegrationSettingsDefault;
- #endif
- }
- void AAkAcousticPortal::OpenPortal()
- {
- if (Portal != nullptr)
- {
- Portal->OpenPortal();
- }
- else
- {
- UE_LOG(LogAkAudio, Warning, TEXT("AAkAcousticPortal %s called OpenPortal with uninitialized portal component."), *GetName());
- }
- }
- void AAkAcousticPortal::ClosePortal()
- {
- if (Portal != nullptr)
- {
- Portal->ClosePortal();
- }
- else
- {
- UE_LOG(LogAkAudio, Warning, TEXT("AAkAcousticPortal %s called ClosePortal with uninitialized portal component."), *GetName());
- }
- }
- AkAcousticPortalState AAkAcousticPortal::GetCurrentState() const
- {
- if (Portal != nullptr)
- return Portal->GetCurrentState();
- UE_LOG(LogAkAudio, Warning, TEXT("AAkAcousticPortal %s called GetCurrentState with uninitialized portal component."), *GetName());
- return AkAcousticPortalState::Closed;
- }
- AkRoomID AAkAcousticPortal::GetFrontRoom() const
- {
- if (Portal != nullptr)
- return Portal->GetFrontRoom();
- UE_LOG(LogAkAudio, Warning, TEXT("AAkAcousticPortal %s called GetFrontRoom with uninitialized portal component."), *GetName());
- return AkRoomID();
- }
- AkRoomID AAkAcousticPortal::GetBackRoom() const
- {
- if (Portal != nullptr)
- return Portal->GetBackRoom();
- UE_LOG(LogAkAudio, Warning, TEXT("AAkAcousticPortal %s called GetBackRoom with uninitialized portal component."), *GetName());
- return AkRoomID();
- }
- void AAkAcousticPortal::PostRegisterAllComponents()
- {
- Super::PostRegisterAllComponents();
- if (bRequiresStateMigration)
- {
- if (Portal != nullptr)
- {
- Portal->InitialState = InitialState;
- bRequiresStateMigration = false;
- }
- }
-
- if (bRequiresTransformMigration)
- {
- FVector right = FVector(0.0f, 1.0f, 0.0f);
- FVector left = FVector(0.0f, -1.0f, 0.0f);
- FTransform actorTransform = GetActorTransform();
- /* get the local 'front' (with respect to Y). */
- FVector localYFront = (actorTransform.TransformPosition(right) - actorTransform.TransformPosition(left));
- localYFront.Normalize();
- FVector scale = GetActorScale3D();
- SetActorScale3D(FVector(scale.Y, scale.X, scale.Z));
- /* get the local front, using Unreal coordinate orientation. */
- FVector localXFront = GetActorForwardVector();
- /* get the local up vector around which to rotate. */
- FVector localUp = FVector::CrossProduct(localYFront, localXFront);
- /* rotate the local front vector around the local up, such that it points along the 'true' local front, in Unreal terms. */
- localXFront = localXFront.RotateAngleAxis(-90.0f, localUp);
- /* Set up new local axes such that localUp remains constant, local front is changed to localXFront, and the local right is calculated from these two. */
- SetActorRotation(UKismetMathLibrary::MakeRotFromXZ(localXFront, localUp));
- bRequiresTransformMigration = false;
- }
- }
- void AAkAcousticPortal::PostLoad()
- {
- Super::PostLoad();
- const int32 AkVersion = GetLinkerCustomVersion(FAkCustomVersion::GUID);
- if (AkVersion < FAkCustomVersion::SpatialAudioExtentAPIChange)
- {
- bRequiresTransformMigration = true;
- }
- if (AkVersion < FAkCustomVersion::SpatialAudioComponentisation)
- {
- bRequiresStateMigration = true;
- }
- }
- void AAkAcousticPortal::Serialize(FArchive& Ar)
- {
- Ar.UsingCustomVersion(FAkCustomVersion::GUID);
- Super::Serialize(Ar);
- }
- #if WITH_EDITOR
- ECollisionChannel AAkAcousticPortal::GetCollisionChannel()
- {
- return UAkSettings::ConvertFitToGeomCollisionChannel(CollisionChannel.GetValue());
- }
- void AAkAcousticPortal::FitRaycast()
- {
- static const FName NAME_SAV_Fit = TEXT("AAkAcousticPortalRaycast");
- UWorld* World = GEngine->GetWorldFromContextObjectChecked(this);
- if (!World)
- return;
- TArray<TTuple<float, FVector, FVector>> hits;
- // Ray length - DetectionRadius X current scale.
- float RayLength = GetDetectionRadius();
- FCollisionQueryParams CollisionParams(NAME_SAV_Fit, true, this);
- FVector RaycastOrigin = bUseSavedRaycastOrigin ? SavedRaycastOrigin : GetActorLocation();
- float Offset = 2.f / kNumRaycasts;
- float Increment = PI * (3.f - sqrtf(5.f));
- TArray< FHitResult > OutHits;
- for (int i = 0; i < kNumRaycasts; ++i)
- {
- float x = ((i * Offset) - 1) + (Offset / 2);
- float r = sqrtf(1.f - powf(x, 2.f));
- float phi = ((i + 1) % kNumRaycasts) * Increment;
- float y = cosf(phi) * r;
- float z = sinf(phi) * r;
- FVector to = RaycastOrigin + FVector(x, y, z) * RayLength;
- OutHits.Empty();
- World->LineTraceMultiByObjectType(OutHits, RaycastOrigin, to, (int)GetCollisionChannel(), CollisionParams);
- if (OutHits.Num() > 0)
- {
- bool bHit = false;
- FVector ImpactPoint0;
- FVector ImpactNormal0;
- for (auto& res : OutHits)
- {
- if (res.IsValidBlockingHit() &&
- !AkSpatialAudioHelper::IsAkSpatialAudioActorClass(AkSpatialAudioHelper::GetActorFromHitResult(res)))
- {
- bHit = true;
- ImpactPoint0 = res.ImpactPoint;
- ImpactNormal0 = res.ImpactNormal;
- break;
- }
- }
- if (bHit)
- {
- OutHits.Empty();
- World->LineTraceMultiByObjectType(OutHits, ImpactPoint0, ImpactPoint0 + ImpactNormal0 * RayLength, (int)GetCollisionChannel(), CollisionParams);
- bHit = false;
- FVector ImpactPoint1;
- for (auto& res : OutHits)
- {
- if (res.IsValidBlockingHit() &&
- res.Distance > kMinPortalSize &&
- !AkSpatialAudioHelper::IsAkSpatialAudioActorClass(AkSpatialAudioHelper::GetActorFromHitResult(res)))
- {
- bHit = true;
- ImpactPoint1 = res.ImpactPoint;
- break;
- }
- }
- if (bHit)
- {
- float distance = (ImpactPoint0 - ImpactPoint1).Size();
- hits.Emplace(MakeTuple(distance, ImpactPoint0, ImpactPoint1));
- }
-
- }
- }
- }
- auto SortPredicate = [](TTuple<float, FVector, FVector>& A, TTuple<float, FVector, FVector>& B) { return A.Get<0>() < B.Get<0>(); };
- Algo::Sort(hits, SortPredicate);
-
- static const float kDotEpsilon = 0.1f;
- static const float kLineIntersectThresh = 2.0f;
- float minDist = FLT_MAX;
- int Best0 = INT_MAX;
- int Best1 = INT_MAX;
- bool bIntersects = false;
- for (int i = 0; i < hits.Num() && !bIntersects; ++i)
- {
- FVector& pti = hits[i].Get<1>();
- FVector vi = hits[i].Get<2>() - hits[i].Get<1>();
- FVector diri;
- float leni;
- vi.ToDirectionAndLength(diri, leni);
- for (int j = i + 1; j < hits.Num() && !bIntersects; ++j)
- {
- FVector& ptj = hits[j].Get<1>();
- FVector vj = hits[j].Get<2>() - hits[j].Get<1>();
- FVector dirj;
- float lenj;
- vj.ToDirectionAndLength(dirj, lenj);
- if (FMath::Abs(FVector::DotProduct(diri, dirj)) < kDotEpsilon)
- {
- float proj_ji = FVector::DotProduct((ptj - pti), diri);
- if (proj_ji > 0.f && proj_ji < leni)
- {
- float proj_ij = FVector::DotProduct((pti - ptj), dirj);
- if (proj_ij > 0.f && proj_ij < lenj)
- {
- FVector p0 = pti + proj_ji * diri;
- FVector p1 = ptj + proj_ij * dirj;
- float dist = (p0 - p1).Size();
- if (dist < minDist)
- {
- minDist = dist;
- Best0 = i;
- Best1 = j;
-
- if (dist < kLineIntersectThresh)
- {
- //Assuming here we found a pretty good result, bail out so as to favor smaller portals over bigger ones.
- bIntersects = true;
- }
- }
- }
- }
- }
- }
- }
- if (bIntersects)
- {
- BestFit[0] = hits[Best0].Get<1>();
- BestFit[1] = hits[Best0].Get<2>();
- BestFit[2] = hits[Best1].Get<1>();
- BestFit[3] = hits[Best1].Get<2>();
- BestFitValid = true;
- }
- else
- {
- // We will hold on to the best fit points, as long as they are within the detection radius.
- BestFitValid = FVector::DistSquared(RaycastOrigin, (BestFit[0] + BestFit[1] + BestFit[2] + BestFit[3]) / 4.f) < DetectionRadius * DetectionRadius;
- }
- }
- void AAkAcousticPortal::FitPortal()
- {
- if (!BestFitValid)
- return;
- FVector center;
- FVector front;
- FVector side;
- FVector up;
- FVector scale;
-
- FVector& pti = BestFit[0];
- FVector vi = BestFit[1] - BestFit[0];
- FVector diri;
- float leni;
- vi.ToDirectionAndLength(diri, leni);
- FVector& ptj = BestFit[2];
- FVector vj = BestFit[3] - BestFit[2];
- FVector dirj;
- float lenj;
- vj.ToDirectionAndLength(dirj, lenj);
- float proj_ji = FVector::DotProduct((ptj - pti), diri);
- if (proj_ji > 0.f && proj_ji < leni)
- {
- float proj_ij = FVector::DotProduct((pti - ptj), dirj);
- if (proj_ij > 0.f && proj_ij < lenj)
- {
- FVector p0 = pti + proj_ji * diri;
- FVector p1 = ptj + proj_ij * dirj;
- center = pti - proj_ij * dirj;
- center += diri * leni / 2.f + dirj * lenj / 2.f;
- front = FVector::CrossProduct(diri, dirj);
- side = diri;
- up = dirj;
- scale.Y = leni / 2.f;
- scale.Z = lenj / 2.f;
- scale /= kDefaultBrushExtents;
- scale.X = GetActorScale3D().X;
- auto* RC = GetRootComponent();
- if (RC)
- {
- RC->SetWorldLocation(center);
- FRotator rotation = FRotationMatrix::MakeFromXZ(front, up).Rotator();
- RC->SetWorldRotation(rotation);
- RC->SetWorldScale3D(scale);
- }
- }
- }
- }
- void AAkAcousticPortal::PostEditMove(bool bFinished)
- {
- Super::PostEditMove(bFinished);
- if (FitToGeometry)
- {
- FitRaycast();
-
- IsDragging = !bFinished;
-
- if (bFinished)
- {
- bUseSavedRaycastOrigin = false;
-
- FitPortal();
- }
- }
- }
- void AAkAcousticPortal::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
- {
- Super::PostEditChangeProperty(PropertyChangedEvent);
- if (PropertyChangedEvent.Property)
- {
- if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(AAkAcousticPortal, FitToGeometry))
- {
- ClearBestFit();
- if (FitToGeometry)
- {
- FitRaycast();
- FitPortal();
- }
- }
- if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(AAkAcousticPortal, DetectionRadius))
- {
- if (FitToGeometry)
- {
- if (!bUseSavedRaycastOrigin)
- {
- // Cache the actor position to get consistant results over multiple updates, since FitPortal() changes the actor location.
- SavedRaycastOrigin = GetActorLocation();
- bUseSavedRaycastOrigin = true;
- }
- FitRaycast();
- FitPortal();
- }
- }
- }
- }
- void AAkAcousticPortal::ClearBestFit()
- {
- BestFit[0] = FVector::ZeroVector;
- BestFit[1] = FVector::ZeroVector;
- BestFit[2] = FVector::ZeroVector;
- BestFit[3] = FVector::ZeroVector;
- BestFitValid = false;
- }
- #endif // WITH_EDITOR
|