AkComponent.cpp 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126
  1. /*******************************************************************************
  2. The content of this file includes portions of the proprietary AUDIOKINETIC Wwise
  3. Technology released in source code form as part of the game integration package.
  4. The content of this file may not be used without valid licenses to the
  5. AUDIOKINETIC Wwise Technology.
  6. Note that the use of the game engine is subject to the Unreal(R) Engine End User
  7. License Agreement at https://www.unrealengine.com/en-US/eula/unreal
  8. License Usage
  9. Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use
  10. this file in accordance with the end user license agreement provided with the
  11. software or, alternatively, in accordance with the terms contained
  12. in a written agreement between you and Audiokinetic Inc.
  13. Copyright (c) 2023 Audiokinetic Inc.
  14. *******************************************************************************/
  15. /*=============================================================================
  16. AkComponent.cpp:
  17. =============================================================================*/
  18. #include "AkComponent.h"
  19. #include "AkAudioDevice.h"
  20. #include "AkAudioEvent.h"
  21. #include "AkAuxBus.h"
  22. #include "AkLateReverbComponent.h"
  23. #include "AkRoomComponent.h"
  24. #include "AkGameplayTypes.h"
  25. #include "AkSettings.h"
  26. #include "AkSpotReflector.h"
  27. #include "AkSwitchValue.h"
  28. #include "AkTrigger.h"
  29. #include "Components/BillboardComponent.h"
  30. #include "DrawDebugHelpers.h"
  31. #include "Engine/Texture2D.h"
  32. #include "Engine/World.h"
  33. #include "GameFramework/PlayerController.h"
  34. #include "Wwise/WwiseExternalSourceManager.h"
  35. #include "Wwise/API/WwiseSoundEngineAPI.h"
  36. #include "Wwise/API/WwiseSpatialAudioAPI.h"
  37. #if WITH_EDITOR
  38. #include "LevelEditorViewport.h"
  39. #include "Editor.h"
  40. #endif
  41. /*------------------------------------------------------------------------------------
  42. Component Helpers
  43. ------------------------------------------------------------------------------------*/
  44. namespace UAkComponentUtils
  45. {
  46. APlayerController* GetAPlayerController(const UActorComponent* Component)
  47. {
  48. const APlayerCameraManager* AsPlayerCameraManager = Cast<APlayerCameraManager>(Component->GetOwner());
  49. return AsPlayerCameraManager ? AsPlayerCameraManager->GetOwningPlayerController() : nullptr;
  50. }
  51. void GetListenerPosition(const UAkComponent* Component, FVector& Location, FVector& Front, FVector& Up)
  52. {
  53. APlayerController* pPlayerController = GetAPlayerController(Component);
  54. if (pPlayerController != nullptr)
  55. {
  56. FVector Right;
  57. pPlayerController->GetAudioListenerPosition(Location, Front, Right);
  58. Up = FVector::CrossProduct(Front, Right);
  59. return;
  60. }
  61. #if WITH_EDITORONLY_DATA
  62. auto& Clients = GEditor->GetAllViewportClients();
  63. static FTransform LastKnownEditorTransform;
  64. for (int i = 0; i < Clients.Num(); i++)
  65. {
  66. FEditorViewportClient* ViewportClient = Clients[i];
  67. UWorld* World = ViewportClient->GetWorld();
  68. if (ViewportClient->Viewport && ViewportClient->Viewport->HasFocus() && World->AllowAudioPlayback())
  69. {
  70. EWorldType::Type WorldType = World->WorldType;
  71. if (WorldType == EWorldType::Editor || WorldType == EWorldType::PIE)
  72. {
  73. LastKnownEditorTransform = FAkAudioDevice::Get()->GetEditorListenerPosition(i);
  74. Location = LastKnownEditorTransform.GetLocation();
  75. Front = LastKnownEditorTransform.GetRotation().GetForwardVector();
  76. Up = LastKnownEditorTransform.GetRotation().GetUpVector();
  77. return;
  78. }
  79. else if (WorldType != EWorldType::Game && WorldType != EWorldType::GamePreview)
  80. {
  81. Location = ViewportClient->GetViewLocation();
  82. Front = ViewportClient->GetViewRotation().Quaternion().GetForwardVector();
  83. Up = ViewportClient->GetViewRotation().Quaternion().GetUpVector();
  84. LastKnownEditorTransform.SetLocation(Location);
  85. LastKnownEditorTransform.SetRotation(ViewportClient->GetViewRotation().Quaternion());
  86. return;
  87. }
  88. }
  89. }
  90. Location = LastKnownEditorTransform.GetLocation();
  91. Front = LastKnownEditorTransform.GetRotation().GetForwardVector();
  92. Up = LastKnownEditorTransform.GetRotation().GetUpVector();
  93. #endif
  94. }
  95. void GetLocationFrontUp(const UAkComponent* Component, FVector& Location, FVector& Front, FVector& Up)
  96. {
  97. if (Component->IsDefaultListener)
  98. {
  99. GetListenerPosition(Component, Location, Front, Up);
  100. }
  101. else
  102. {
  103. auto& Transform = Component->GetComponentTransform();
  104. Location = Transform.GetTranslation();
  105. Front = Transform.GetUnitAxis(EAxis::X);
  106. Up = Transform.GetUnitAxis(EAxis::Z);
  107. }
  108. }
  109. }
  110. AkReverbFadeControl::AkReverbFadeControl(const UAkLateReverbComponent& LateReverbComponent)
  111. : AuxBusId(LateReverbComponent.GetAuxBusId())
  112. , bIsFadingOut(false)
  113. , FadeControlUniqueId((void*)&LateReverbComponent)
  114. , CurrentControlValue(0.f)
  115. , TargetControlValue(LateReverbComponent.SendLevel)
  116. , FadeRate(LateReverbComponent.FadeRate)
  117. , Priority(LateReverbComponent.Priority)
  118. {}
  119. void AkReverbFadeControl::UpdateValues(const UAkLateReverbComponent& LateReverbComponent)
  120. {
  121. AuxBusId = LateReverbComponent.GetAuxBusId();
  122. TargetControlValue = LateReverbComponent.SendLevel;
  123. FadeRate = LateReverbComponent.FadeRate;
  124. Priority = LateReverbComponent.Priority;
  125. }
  126. bool AkReverbFadeControl::Update(float DeltaTime)
  127. {
  128. if (CurrentControlValue != TargetControlValue || bIsFadingOut)
  129. {
  130. // Rate (%/s) * Delta (s) = % for given delta, apply to target.
  131. const float Increment = DeltaTime * FadeRate * TargetControlValue;
  132. if (bIsFadingOut)
  133. {
  134. CurrentControlValue -= Increment;
  135. if (CurrentControlValue <= 0.f)
  136. return false;
  137. }
  138. else
  139. CurrentControlValue = FMath::Min(CurrentControlValue + Increment, TargetControlValue);
  140. }
  141. return true;
  142. }
  143. AkAuxSendValue AkReverbFadeControl::ToAkAuxSendValue() const
  144. {
  145. AkAuxSendValue ret;
  146. ret.listenerID = AK_INVALID_GAME_OBJECT;
  147. ret.auxBusID = AuxBusId;
  148. ret.fControlValue = CurrentControlValue;
  149. return ret;
  150. }
  151. bool AkReverbFadeControl::Prioritize(const AkReverbFadeControl& A, const AkReverbFadeControl& B)
  152. {
  153. if (A.bIsFadingOut == B.bIsFadingOut)
  154. {
  155. if (A.Priority == B.Priority)
  156. {
  157. // Sort by bus id if priority and fade are equal, to ensure comparisons in UAkComponent::NeedToUpdateAuxSends dont lead to continuous aux sends updates, when there are overlapping reverbs.
  158. return A.AuxBusId < B.AuxBusId;
  159. }
  160. return A.Priority > B.Priority;
  161. }
  162. // Ensure the fading out buffers are sent to the end of the array.
  163. return A.bIsFadingOut < B.bIsFadingOut;
  164. }
  165. /*------------------------------------------------------------------------------------
  166. UAkComponent
  167. ------------------------------------------------------------------------------------*/
  168. UAkComponent::UAkComponent(const class FObjectInitializer& ObjectInitializer) :
  169. Super(ObjectInitializer)
  170. {
  171. // Property initialization
  172. DrawFirstOrderReflections = false;
  173. DrawSecondOrderReflections = false;
  174. DrawHigherOrderReflections = false;
  175. DrawDiffraction = false;
  176. EarlyReflectionBusSendGain = 1.f;
  177. StopWhenOwnerDestroyed = true;
  178. bUseReverbVolumes = true;
  179. OcclusionRefreshInterval = 0.2f;
  180. PrimaryComponentTick.bCanEverTick = true;
  181. PrimaryComponentTick.TickGroup = TG_DuringPhysics;
  182. PrimaryComponentTick.bAllowTickOnDedicatedServer = false;
  183. bTickInEditor = true;
  184. bAutoActivate = true;
  185. bNeverNeedsRenderUpdate = true;
  186. bWantsOnUpdateTransform = true;
  187. #if WITH_EDITORONLY_DATA
  188. bVisualizeComponent = true;
  189. #endif
  190. AttenuationScalingFactor = 1.0f;
  191. bAutoDestroy = false;
  192. bUseDefaultListeners = true;
  193. OcclusionCollisionChannel = EAkCollisionChannel::EAKCC_UseIntegrationSettingsDefault;
  194. outerRadius = 0.0f;
  195. innerRadius = 0.0f;
  196. }
  197. ECollisionChannel UAkComponent::GetOcclusionCollisionChannel()
  198. {
  199. return UAkSettings::ConvertOcclusionCollisionChannel(OcclusionCollisionChannel.GetValue());
  200. }
  201. void UAkComponent::PostAssociatedAkEventAndWaitForEndAsync(int32& PlayingID, FLatentActionInfo LatentInfo)
  202. {
  203. PostAkEventAndWaitForEndAsync(AkAudioEvent, PlayingID, LatentInfo);
  204. }
  205. int32 UAkComponent::PostAssociatedAkEventAndWaitForEnd(FLatentActionInfo LatentInfo)
  206. {
  207. return PostAkEventAndWaitForEnd(AkAudioEvent, EventName, LatentInfo);
  208. }
  209. AkPlayingID UAkComponent::PostAkEventByNameWithDelegate(UAkAudioEvent* AkEvent, const FString& in_EventName, int32 CallbackMask,
  210. const FOnAkPostEventCallback& PostEventCallback)
  211. {
  212. AkPlayingID PlayingID = AK_INVALID_PLAYING_ID;
  213. if (AkEvent)
  214. {
  215. return AkEvent->PostOnComponent(this, PostEventCallback, CallbackMask, StopWhenOwnerDestroyed);
  216. }
  217. auto AudioDevice = FAkAudioDevice::Get();
  218. if (AudioDevice)
  219. {
  220. const AkUInt32 ShortID = AudioDevice->GetShortID(AkEvent, in_EventName);
  221. PlayingID = AudioDevice->PostEventOnAkGameObject(ShortID, this, PostEventCallback, CallbackMask, {});
  222. }
  223. return PlayingID;
  224. }
  225. AkPlayingID UAkComponent::PostAkEventByIdWithCallback(const AkUInt32 EventShortID, AkUInt32 Flags /*= 0*/,
  226. AkCallbackFunc UserCallback/*= NULL*/, void * UserCookie /*= NULL*/, const TArray<AkExternalSourceInfo>& ExternalSources)
  227. {
  228. AkPlayingID PlayingID = AK_INVALID_PLAYING_ID;
  229. auto AudioDevice = FAkAudioDevice::Get();
  230. if (AudioDevice)
  231. {
  232. PlayingID = AudioDevice->PostEventOnAkComponent(EventShortID, this, Flags, UserCallback, UserCookie, ExternalSources);
  233. }
  234. return PlayingID;
  235. }
  236. int32 UAkComponent::PostAkEventAndWaitForEnd(class UAkAudioEvent * AkEvent, const FString& in_EventName, FLatentActionInfo LatentInfo)
  237. {
  238. if (LIKELY(!AkEvent))
  239. {
  240. return AkEvent->PostOnComponentAndWait(this, StopWhenOwnerDestroyed, LatentInfo);
  241. }
  242. AkPlayingID PlayingID = AK_INVALID_PLAYING_ID;
  243. auto AudioDevice = FAkAudioDevice::Get();
  244. if (AudioDevice)
  245. {
  246. const AkUInt32 ShortID = AudioDevice->GetShortID(AkEvent, in_EventName);
  247. auto* World = GetWorld();
  248. if (UNLIKELY(!World))
  249. {
  250. UE_LOG(LogAkAudio, Log, TEXT("Failed to post latent AkAudioEvent with actor '%s' world that's not valid."), *GetName());
  251. return AK_INVALID_PLAYING_ID;
  252. }
  253. FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
  254. FWaitEndOfEventAction* LatentAction = new FWaitEndOfEventAction(LatentInfo);
  255. LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, LatentAction);
  256. PlayingID = AudioDevice->PostEventOnComponentWithLatentAction(ShortID, this, LatentAction);
  257. if (PlayingID == AK_INVALID_PLAYING_ID)
  258. {
  259. LatentAction->EventFinished = true;
  260. }
  261. }
  262. return PlayingID;
  263. }
  264. void UAkComponent::PostAkEventAndWaitForEndAsync(UAkAudioEvent* AkEvent, int32& PlayingID, FLatentActionInfo LatentInfo)
  265. {
  266. if (!AkEvent)
  267. {
  268. UE_LOG(LogAkAudio, Warning, TEXT("UAkComponent::PostAkEventAndWaitForEnd: No Event specified!"));
  269. PlayingID = AK_INVALID_PLAYING_ID;
  270. return;
  271. }
  272. PlayingID = AkEvent->PostOnComponentAndWait(this, StopWhenOwnerDestroyed, LatentInfo);
  273. }
  274. int32 UAkComponent::PostAkEvent(UAkAudioEvent* AkEvent, int32 CallbackMask,
  275. const FOnAkPostEventCallback& PostEventCallback, const FString& InEventName)
  276. {
  277. if (LIKELY(IsValid(AkEvent)))
  278. {
  279. return AkEvent->PostOnComponent(this, PostEventCallback, CallbackMask, StopWhenOwnerDestroyed);
  280. }
  281. AkPlayingID playingID = AK_INVALID_PLAYING_ID;
  282. auto AudioDevice = FAkAudioDevice::Get();
  283. if (AudioDevice)
  284. {
  285. playingID = AudioDevice->PostEventOnAkGameObject(AudioDevice->GetShortID(AkEvent, InEventName), this, PostEventCallback, CallbackMask, {});
  286. if (playingID != AK_INVALID_PLAYING_ID)
  287. {
  288. bEventPosted = true;
  289. }
  290. }
  291. return playingID;
  292. }
  293. AkPlayingID UAkComponent::PostAkEvent(UAkAudioEvent* AkEvent, AkUInt32 Flags, AkCallbackFunc UserCallback,
  294. void* UserCookie)
  295. {
  296. if (UNLIKELY(!IsValid(AkEvent)))
  297. {
  298. UE_LOG(LogAkAudio, Error, TEXT("Failed to post invalid AkAudioEvent on component '%s'."), *GetName());
  299. return AK_INVALID_PLAYING_ID;
  300. }
  301. return AkEvent->PostOnComponent(this, nullptr, UserCallback, UserCookie, static_cast<AkCallbackType>(Flags), nullptr, StopWhenOwnerDestroyed);
  302. }
  303. AkRoomID UAkComponent::GetSpatialAudioRoom() const
  304. {
  305. AkRoomID RoomID;
  306. if (CurrentRoom)
  307. {
  308. RoomID = CurrentRoom->GetRoomID();
  309. }
  310. return RoomID;
  311. }
  312. void UAkComponent::PostTrigger(const UAkTrigger* TriggerValue, FString Trigger)
  313. {
  314. if (FAkAudioDevice::Get())
  315. {
  316. auto* SoundEngine = IWwiseSoundEngineAPI::Get();
  317. if (UNLIKELY(!SoundEngine)) return;
  318. if (TriggerValue)
  319. {
  320. SoundEngine->PostTrigger(TriggerValue->TriggerCookedData.TriggerId, GetAkGameObjectID());
  321. }
  322. else
  323. {
  324. SoundEngine->PostTrigger(TCHAR_TO_AK(*Trigger), GetAkGameObjectID());
  325. }
  326. }
  327. }
  328. void UAkComponent::SetSwitch(const UAkSwitchValue* SwitchValue, FString SwitchGroup, FString SwitchState)
  329. {
  330. if (FAkAudioDevice::Get())
  331. {
  332. auto* SoundEngine = IWwiseSoundEngineAPI::Get();
  333. if (UNLIKELY(!SoundEngine)) return;
  334. if (SwitchValue)
  335. {
  336. SoundEngine->SetSwitch(SwitchValue->GroupValueCookedData.GroupId, SwitchValue->GroupValueCookedData.Id, GetAkGameObjectID());
  337. }
  338. else
  339. {
  340. uint32 SwitchGroupID = SoundEngine->GetIDFromString(TCHAR_TO_AK(*SwitchGroup));
  341. uint32 SwitchStateID = SoundEngine->GetIDFromString(TCHAR_TO_AK(*SwitchState));
  342. SoundEngine->SetSwitch(SwitchGroupID, SwitchStateID, GetAkGameObjectID());
  343. }
  344. }
  345. }
  346. void UAkComponent::SetStopWhenOwnerDestroyed(bool bStopWhenOwnerDestroyed)
  347. {
  348. StopWhenOwnerDestroyed = bStopWhenOwnerDestroyed;
  349. }
  350. void UAkComponent::SetListeners(const TArray<UAkComponent*>& NewListeners)
  351. {
  352. auto AudioDevice = FAkAudioDevice::Get();
  353. if (AudioDevice)
  354. {
  355. if (!bUseDefaultListeners)
  356. {
  357. for (auto Listener : Listeners)
  358. {
  359. Listener->Emitters.Remove(this);
  360. }
  361. }
  362. bUseDefaultListeners = false;
  363. Listeners.Reset();
  364. Listeners.Append(NewListeners);
  365. for (auto Listener : Listeners)
  366. {
  367. Listener->Emitters.Add(this);
  368. }
  369. AudioDevice->SetListeners(this, Listeners.Array());
  370. }
  371. }
  372. void UAkComponent::UseReverbVolumes(bool inUseReverbVolumes)
  373. {
  374. bUseReverbVolumes = inUseReverbVolumes;
  375. }
  376. void UAkComponent::UseEarlyReflections(
  377. class UAkAuxBus* AuxBus,
  378. int Order,
  379. float BusSendGain,
  380. float MaxPathLength,
  381. bool SpotReflectors,
  382. const FString& AuxBusName)
  383. {
  384. // Deprecated
  385. }
  386. void UAkComponent::SetEarlyReflectionsAuxBus(const FString& AuxBusName)
  387. {
  388. FAkAudioDevice * AudioDevice = FAkAudioDevice::Get();
  389. if (AudioDevice)
  390. {
  391. AudioDevice->SetEarlyReflectionsAuxBus(this, FAkAudioDevice::GetShortID(nullptr, AuxBusName));
  392. }
  393. }
  394. void UAkComponent::SetEarlyReflectionsVolume(float SendVolume)
  395. {
  396. FAkAudioDevice * AudioDevice = FAkAudioDevice::Get();
  397. if (AudioDevice)
  398. {
  399. AudioDevice->SetEarlyReflectionsVolume(this, SendVolume);
  400. }
  401. }
  402. float UAkComponent::GetAttenuationRadius() const
  403. {
  404. return AkAudioEvent ? AttenuationScalingFactor * AkAudioEvent->MaxAttenuationRadius : 0.f;
  405. }
  406. void UAkComponent::SetOutputBusVolume(float BusVolume)
  407. {
  408. FAkAudioDevice * AudioDevice = FAkAudioDevice::Get();
  409. if (AudioDevice)
  410. {
  411. for (auto It = Listeners.CreateIterator(); It; ++It)
  412. {
  413. AudioDevice->SetGameObjectOutputBusVolume(this, *It, BusVolume);
  414. }
  415. }
  416. }
  417. void UAkComponent::OnRegister()
  418. {
  419. UWorld* CurrentWorld = GetWorld();
  420. if(!IsRegisteredWithWwise && CurrentWorld->WorldType != EWorldType::Inactive && CurrentWorld->WorldType != EWorldType::None)
  421. RegisterGameObject(); // Done before parent so that OnUpdateTransform follows registration and updates position correctly.
  422. ObstructionService.Init(this, OcclusionRefreshInterval);
  423. // It's possible for OnRegister to be called while the WorldType is inactive.
  424. // The game object will be registered again later when the WorldType is active.
  425. FAkAudioDevice * AudioDevice = FAkAudioDevice::Get();
  426. if (AudioDevice && IsRegisteredWithWwise)
  427. {
  428. if (EarlyReflectionAuxBus || !EarlyReflectionAuxBusName.IsEmpty())
  429. {
  430. AkUInt32 AuxBusID = FAkAudioDevice::GetShortID(EarlyReflectionAuxBus, EarlyReflectionAuxBusName);
  431. if (AuxBusID != AK_INVALID_UNIQUE_ID)
  432. AudioDevice->SetEarlyReflectionsAuxBus(this, AuxBusID);
  433. }
  434. if (EarlyReflectionBusSendGain != 1.0)
  435. AudioDevice->SetEarlyReflectionsVolume(this, EarlyReflectionBusSendGain);
  436. }
  437. Super::OnRegister();
  438. #if WITH_EDITORONLY_DATA
  439. UpdateSpriteTexture();
  440. #endif
  441. }
  442. #if WITH_EDITORONLY_DATA
  443. void UAkComponent::UpdateSpriteTexture()
  444. {
  445. if (SpriteComponent)
  446. {
  447. SpriteComponent->SetSprite(LoadObject<UTexture2D>(NULL, TEXT("/Wwise/S_AkComponent.S_AkComponent")));
  448. }
  449. }
  450. #endif
  451. void UAkComponent::OnUnregister()
  452. {
  453. // Route OnUnregister event.
  454. Super::OnUnregister();
  455. // Don't stop audio and clean up component if owner has been destroyed (default behaviour). This function gets
  456. // called from AActor::ClearComponents when an actor gets destroyed which is not usually what we want for one-
  457. // shot sounds.
  458. AActor* Owner = GetOwner();
  459. UWorld* CurrentWorld = GetWorld();
  460. if( !Owner || !CurrentWorld || StopWhenOwnerDestroyed || CurrentWorld->bIsTearingDown || (Owner->GetClass() == APlayerController::StaticClass() && CurrentWorld->WorldType == EWorldType::PIE))
  461. {
  462. Stop();
  463. }
  464. }
  465. void UAkComponent::OnComponentDestroyed( bool bDestroyingHierarchy )
  466. {
  467. UnregisterGameObject();
  468. Super::OnComponentDestroyed(bDestroyingHierarchy);
  469. }
  470. void UAkComponent::ShutdownAfterError( void )
  471. {
  472. UnregisterGameObject();
  473. Super::ShutdownAfterError();
  474. }
  475. bool UAkComponent::NeedToUpdateAuxSends(const TArray<AkAuxSendValue>& NewValues)
  476. {
  477. if (NewValues.Num() != CurrentAuxSendValues.Num())
  478. return true;
  479. for (int32 i = 0; i < NewValues.Num(); i++)
  480. {
  481. if (NewValues[i].listenerID != CurrentAuxSendValues[i].listenerID ||
  482. NewValues[i].auxBusID != CurrentAuxSendValues[i].auxBusID ||
  483. NewValues[i].fControlValue != CurrentAuxSendValues[i].fControlValue)
  484. {
  485. return true;
  486. }
  487. }
  488. return false;
  489. }
  490. void UAkComponent::ApplyAkReverbVolumeList(float DeltaTime)
  491. {
  492. for (int32 Idx = 0; Idx < ReverbFadeControls.Num(); )
  493. {
  494. if (!ReverbFadeControls[Idx].Update(DeltaTime))
  495. ReverbFadeControls.RemoveAt(Idx);
  496. else
  497. ++Idx;
  498. }
  499. if (ReverbFadeControls.Num() > 1)
  500. ReverbFadeControls.Sort(AkReverbFadeControl::Prioritize);
  501. FAkAudioDevice* AkAudioDevice = FAkAudioDevice::Get();
  502. if (AkAudioDevice)
  503. {
  504. TArray<AkAuxSendValue> NewAuxSendValues;
  505. for (int32 Idx = 0; Idx < ReverbFadeControls.Num() && Idx < AkAudioDevice->GetMaxAuxBus(); Idx++)
  506. {
  507. AkAuxSendValue* FoundAuxSend = NewAuxSendValues.FindByPredicate([=](const AkAuxSendValue& ItemInArray) { return ItemInArray.auxBusID == ReverbFadeControls[Idx].AuxBusId; });
  508. if (FoundAuxSend)
  509. {
  510. FoundAuxSend->fControlValue += ReverbFadeControls[Idx].ToAkAuxSendValue().fControlValue;
  511. }
  512. else
  513. {
  514. NewAuxSendValues.Add(ReverbFadeControls[Idx].ToAkAuxSendValue());
  515. }
  516. }
  517. if (NeedToUpdateAuxSends(NewAuxSendValues))
  518. {
  519. AkAudioDevice->SetAuxSends(this, NewAuxSendValues);
  520. CurrentAuxSendValues = NewAuxSendValues;
  521. }
  522. }
  523. }
  524. void UAkComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
  525. {
  526. auto* SoundEngine = IWwiseSoundEngineAPI::Get();
  527. if (UNLIKELY(!SoundEngine)) return;
  528. if (SoundEngine->IsInitialized())
  529. {
  530. Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
  531. FAkAudioDevice* AkAudioDevice = FAkAudioDevice::Get();
  532. // If we're a listener, update our position here instead of in OnUpdateTransform.
  533. // This is because PlayerController->GetAudioListenerPosition caches its value, and it can be out of sync
  534. if (IsDefaultListener && HasMoved())
  535. UpdateGameObjectPosition();
  536. if (AkAudioDevice && AkAudioDevice->WorldSpatialAudioVolumesUpdated(GetWorld()))
  537. {
  538. UpdateSpatialAudioRoom(GetComponentLocation());
  539. // Find and apply all AkReverbVolumes at this location
  540. if (bUseReverbVolumes && AkAudioDevice->GetMaxAuxBus() > 0)
  541. {
  542. UpdateAkLateReverbComponentList(GetComponentLocation());
  543. }
  544. }
  545. if (AkAudioDevice && bUseReverbVolumes && AkAudioDevice->GetMaxAuxBus() > 0)
  546. ApplyAkReverbVolumeList(DeltaTime);
  547. ObstructionService.Tick(Listeners, GetPosition(), GetOwner(), GetSpatialAudioRoom(), GetOcclusionCollisionChannel(), DeltaTime, OcclusionRefreshInterval);
  548. if (bAutoDestroy && bEventPosted && !HasActiveEvents())
  549. {
  550. DestroyComponent();
  551. }
  552. #if !UE_BUILD_SHIPPING
  553. if (DrawFirstOrderReflections || DrawSecondOrderReflections || DrawHigherOrderReflections)
  554. {
  555. DebugDrawReflections();
  556. }
  557. if (DrawDiffraction)
  558. {
  559. DebugDrawDiffraction();
  560. }
  561. #endif
  562. }
  563. }
  564. void UAkComponent::BeginPlay()
  565. {
  566. Super::BeginPlay();
  567. UpdateGameObjectPosition();
  568. // If spawned inside AkReverbVolume(s), we do not want the fade in effect to kick in.
  569. UpdateAkLateReverbComponentList(GetComponentLocation());
  570. for (auto& ReverbFadeControl : ReverbFadeControls)
  571. ReverbFadeControl.ForceCurrentToTargetValue();
  572. SetAttenuationScalingFactor(AttenuationScalingFactor);
  573. if (EnableSpotReflectors)
  574. AAkSpotReflector::UpdateSpotReflectors(this);
  575. }
  576. void UAkComponent::SetAttenuationScalingFactor(float Value)
  577. {
  578. AttenuationScalingFactor = Value;
  579. FAkAudioDevice* AudioDevice = FAkAudioDevice::Get();
  580. if (AudioDevice)
  581. AudioDevice->SetAttenuationScalingFactor(this, AttenuationScalingFactor);
  582. }
  583. void UAkComponent::OnUpdateTransform(EUpdateTransformFlags UpdateTransformFlags, ETeleportType Teleport)
  584. {
  585. Super::OnUpdateTransform(UpdateTransformFlags, Teleport);
  586. // If we're a listener, our position will be updated from Tick instead of here.
  587. // This is because PlayerController->GetAudioListenerPosition caches its value, and it can be out of sync
  588. if(!IsDefaultListener)
  589. UpdateGameObjectPosition();
  590. }
  591. UAkComponent* UAkComponent::GetAkComponent(AkGameObjectID GameObjectID)
  592. {
  593. return GameObjectID == DUMMY_GAMEOBJ ? nullptr : (UAkComponent*)GameObjectID;
  594. }
  595. void UAkComponent::GetAkGameObjectName(FString& Name) const
  596. {
  597. AActor* parentActor = GetOwner();
  598. if (parentActor)
  599. {
  600. #if WITH_EDITOR
  601. Name = parentActor->GetActorLabel() + ".";
  602. #else
  603. Name = parentActor->GetName() + ".";
  604. #endif
  605. }
  606. Name += GetName();
  607. UWorld* CurrentWorld = GetWorld();
  608. switch (CurrentWorld->WorldType)
  609. {
  610. case EWorldType::Editor:
  611. Name += "(Editor)";
  612. break;
  613. case EWorldType::EditorPreview:
  614. Name += "(EditorPreview)";
  615. break;
  616. case EWorldType::GamePreview:
  617. Name += "(GamePreview)";
  618. break;
  619. case EWorldType::Inactive:
  620. Name += "(Inactive)";
  621. break;
  622. }
  623. }
  624. void UAkComponent::PostRegisterGameObject() {}
  625. void UAkComponent::PostUnregisterGameObject() {}
  626. void UAkComponent::RegisterGameObject()
  627. {
  628. FAkAudioDevice* AkAudioDevice = FAkAudioDevice::Get();
  629. if ( AkAudioDevice )
  630. {
  631. if ( bUseDefaultListeners )
  632. {
  633. const auto& DefaultListeners = AkAudioDevice->GetDefaultListeners();
  634. Listeners.Empty(DefaultListeners.Num());
  635. for (auto Listener : DefaultListeners)
  636. {
  637. Listeners.Add(Listener);
  638. // NOTE: We do not add this to Listener's emitter list, the list is only for user specified (non-default) emitters.
  639. }
  640. }
  641. AkAudioDevice->RegisterComponent(this);
  642. IsRegisteredWithWwise = true;
  643. AkAudioDevice->SetGameObjectRadius(this, outerRadius, innerRadius);
  644. }
  645. PostRegisterGameObject();
  646. }
  647. void UAkComponent::UnregisterGameObject()
  648. {
  649. FAkAudioDevice* AkAudioDevice = FAkAudioDevice::Get();
  650. if (AkAudioDevice)
  651. {
  652. AkAudioDevice->UnregisterComponent(this);
  653. IsRegisteredWithWwise = false;
  654. }
  655. for (auto Listener : Listeners)
  656. Listener->Emitters.Remove(this);
  657. for (auto Emitter : Emitters)
  658. Emitter->Listeners.Remove(this);
  659. PostUnregisterGameObject();
  660. }
  661. void UAkComponent::UpdateAkLateReverbComponentList( FVector Loc )
  662. {
  663. FAkAudioDevice* AkAudioDevice = FAkAudioDevice::Get();
  664. if (!AkAudioDevice)
  665. return;
  666. TArray<UAkLateReverbComponent*> FoundComponents = AkAudioDevice->FindLateReverbComponentsAtLocation(Loc, GetWorld());
  667. // Add the new volumes to the current list
  668. for (const auto& LateReverbComponent : FoundComponents)
  669. {
  670. const auto AuxBusId = LateReverbComponent->GetAuxBusId();
  671. const int32 FoundIdx = ReverbFadeControls.IndexOfByPredicate([=](const AkReverbFadeControl& Candidate)
  672. {
  673. return Candidate.FadeControlUniqueId == (void*)LateReverbComponent;
  674. });
  675. if (FoundIdx == INDEX_NONE)
  676. {
  677. // The volume was not found, add it to the list
  678. ReverbFadeControls.Add(AkReverbFadeControl(*LateReverbComponent));
  679. }
  680. else
  681. {
  682. // The volume was found. We still have to check if it is currently fading out, in case we are
  683. // getting back in a volume we just exited.
  684. ReverbFadeControls[FoundIdx].bIsFadingOut = false;
  685. // We need to update the late reverb values in case they have changed on the reverb component.
  686. ReverbFadeControls[FoundIdx].UpdateValues(*LateReverbComponent);
  687. }
  688. }
  689. // Fade out the current volumes not found in the new list
  690. for (auto& ReverbFadeControl : ReverbFadeControls)
  691. {
  692. const int32 FoundIdx = FoundComponents.IndexOfByPredicate([=](const UAkLateReverbComponent* const Candidate)
  693. {
  694. return ReverbFadeControl.FadeControlUniqueId == (void*)Candidate;
  695. });
  696. if (FoundIdx == INDEX_NONE)
  697. ReverbFadeControl.bIsFadingOut = true;
  698. }
  699. }
  700. FVector UAkComponent::GetPosition() const
  701. {
  702. return FAkAudioDevice::AKVector64ToFVector(CurrentSoundPosition.Position());
  703. }
  704. bool UAkComponent::HasMoved()
  705. {
  706. AkSoundPosition soundpos;
  707. FVector Location, Front, Up;
  708. UAkComponentUtils::GetLocationFrontUp(this, Location, Front, Up);
  709. FAkAudioDevice::FVectorsToAKWorldTransform(Location, Front, Up, soundpos);
  710. return CurrentSoundPosition.Position().X != soundpos.Position().X || CurrentSoundPosition.Position().Y != soundpos.Position().Y || CurrentSoundPosition.Position().Z != soundpos.Position().Z ||
  711. CurrentSoundPosition.OrientationTop().X != soundpos.OrientationTop().X || CurrentSoundPosition.OrientationTop().Y != soundpos.OrientationTop().Y || CurrentSoundPosition.OrientationTop().Z != soundpos.OrientationTop().Z ||
  712. CurrentSoundPosition.OrientationFront().X != soundpos.OrientationFront().X || CurrentSoundPosition.OrientationFront().Y != soundpos.OrientationFront().Y || CurrentSoundPosition.OrientationFront().Z != soundpos.OrientationFront().Z;
  713. }
  714. void UAkComponent::UpdateGameObjectPosition()
  715. {
  716. #ifdef _DEBUG
  717. CheckEmitterListenerConsistancy();
  718. #endif
  719. FAkAudioDevice* AkAudioDevice = FAkAudioDevice::Get();
  720. if (IsActive() && AkAudioDevice)
  721. {
  722. if (AllowAudioPlayback())
  723. {
  724. AkSoundPosition soundpos;
  725. FVector Location, Front, Up;
  726. UAkComponentUtils::GetLocationFrontUp(this, Location, Front, Up);
  727. FAkAudioDevice::FVectorsToAKWorldTransform(Location, Front, Up, soundpos);
  728. UpdateSpatialAudioRoom(Location);
  729. AkAudioDevice->SetPosition(this, soundpos);
  730. CurrentSoundPosition = soundpos;
  731. }
  732. // Find and apply all AkReverbVolumes at this location
  733. if (bUseReverbVolumes && AkAudioDevice->GetMaxAuxBus() > 0)
  734. {
  735. UpdateAkLateReverbComponentList(GetComponentLocation());
  736. }
  737. }
  738. }
  739. void UAkComponent::UpdateSpatialAudioRoom(FVector Location)
  740. {
  741. if (IsRegisteredWithWwise)
  742. {
  743. FAkAudioDevice* AkAudioDevice = FAkAudioDevice::Get();
  744. if (AkAudioDevice)
  745. {
  746. AKRESULT result = AK_Fail;
  747. TArray<UAkRoomComponent*> RoomComponents = AkAudioDevice->FindRoomComponentsAtLocation(Location, GetWorld());
  748. if (RoomComponents.Num() == 0)
  749. {
  750. if (AkAudioDevice->WorldHasActiveRooms(GetWorld()) && CurrentRoom != nullptr)
  751. {
  752. CurrentRoom = nullptr;
  753. result = AkAudioDevice->SetInSpatialAudioRoom(GetAkGameObjectID(), GetSpatialAudioRoom());
  754. }
  755. }
  756. else if (CurrentRoom != RoomComponents[0])
  757. {
  758. CurrentRoom = RoomComponents[0];
  759. result = AkAudioDevice->SetInSpatialAudioRoom(GetAkGameObjectID(), GetSpatialAudioRoom());
  760. }
  761. if (EnableSpotReflectors && result == AK_Success)
  762. AAkSpotReflector::UpdateSpotReflectors(this);
  763. }
  764. }
  765. }
  766. const TSet<UAkComponent*>& UAkComponent::GetEmitters()
  767. {
  768. FAkAudioDevice* Device = FAkAudioDevice::Get();
  769. if (Device)
  770. {
  771. auto DefaultListeners = Device->GetDefaultListeners();
  772. if (DefaultListeners.Contains(this))
  773. return Device->GetDefaultEmitters();
  774. else
  775. return Emitters;
  776. }
  777. return Emitters;
  778. }
  779. void UAkComponent::CheckEmitterListenerConsistancy()
  780. {
  781. for (auto Emitter : GetEmitters())
  782. {
  783. check(Emitter->Listeners.Contains(this));
  784. }
  785. for (auto Listener : Listeners)
  786. {
  787. check(Listener->GetEmitters().Contains(this));
  788. }
  789. }
  790. void UAkComponent::_DebugDrawReflections( const AkVector64& akEmitterPos, const AkVector64& akListenerPos, const AkReflectionPathInfo* paths, AkUInt32 uNumPaths) const
  791. {
  792. ::FlushDebugStrings(GWorld);
  793. for (AkInt32 idxPath = uNumPaths-1; idxPath >= 0; --idxPath)
  794. {
  795. const AkReflectionPathInfo& path = paths[idxPath];
  796. unsigned int order = path.numReflections;
  797. if ((DrawFirstOrderReflections && order == 1) ||
  798. (DrawSecondOrderReflections && order == 2) ||
  799. (DrawHigherOrderReflections && order > 2))
  800. {
  801. FColor colorLight;
  802. FColor colorMed;
  803. FColor colorDark;
  804. switch ((order - 1))
  805. {
  806. case 0:
  807. colorLight = FColor(0x9DEBF3);
  808. colorMed = FColor(0x318087);
  809. colorDark = FColor(0x186067);
  810. break;
  811. case 1:
  812. colorLight = FColor(0xFCDBA2);
  813. colorMed = FColor(0xDEAB4E);
  814. colorDark = FColor(0xA97B27);
  815. break;
  816. case 2:
  817. default:
  818. colorLight = FColor(0xFCB1A2);
  819. colorMed = FColor(0xDE674E);
  820. colorDark = FColor(0xA93E27);
  821. break;
  822. }
  823. FColor colorLightGrey(75, 75, 75);
  824. FColor colorMedGrey(50, 50, 50);
  825. FColor colorDarkGrey(35, 35, 35);
  826. const int kPathThickness = 5.f;
  827. const float kRadiusSphere = 25.f;
  828. const int kNumSphereSegments = 8;
  829. const FVector emitterPos = FAkAudioDevice::AKVector64ToFVector(akEmitterPos);
  830. FVector listenerPt = FAkAudioDevice::AKVector64ToFVector(akListenerPos);
  831. for (int idxSeg = path.numPathPoints-1; idxSeg >= 0; --idxSeg)
  832. {
  833. const FVector reflectionPt = FAkAudioDevice::AKVector64ToFVector(path.pathPoint[idxSeg]);
  834. if (idxSeg != path.numPathPoints - 1)
  835. {
  836. // Note: Not drawing the first leg of the path from the listener. Often hard to see because it is typically the camera position.
  837. ::DrawDebugLine(GWorld, listenerPt, reflectionPt, path.isOccluded ? colorLightGrey : colorLight, false, -1.f, (uint8)'\000', kPathThickness / order);
  838. ::DrawDebugSphere(GWorld, reflectionPt, (kRadiusSphere/2) / order, kNumSphereSegments, path.isOccluded ? colorLightGrey : colorLight);
  839. }
  840. else
  841. {
  842. ::DrawDebugSphere(GWorld, reflectionPt, kRadiusSphere / order, kNumSphereSegments, path.isOccluded ? colorMedGrey : colorMed);
  843. }
  844. // Draw image source point. Not as useful as I had hoped.
  845. //const FVector imageSrc = FAkAudioDevice::AKVectorToFVector(path.imageSource);
  846. //::DrawDebugSphere(GWorld, imageSrc, kRadiusSphere/order, kNumSphereSegments, colorDark);
  847. listenerPt = reflectionPt;
  848. }
  849. if (!path.isOccluded)
  850. {
  851. // Finally the last path segment towards the emitter.
  852. ::DrawDebugLine(GWorld, listenerPt, emitterPos, path.isOccluded ? colorLightGrey : colorLight, false, -1.f, (uint8)'\000', kPathThickness / order);
  853. }
  854. }
  855. }
  856. }
  857. void UAkComponent::_DebugDrawDiffraction(const AkVector64& akEmitterPos, const AkVector64& akListenerPos, const AkDiffractionPathInfo* paths, AkUInt32 uNumPaths) const
  858. {
  859. ::FlushDebugStrings(GWorld);
  860. for (AkInt32 idxPath = uNumPaths - 1; idxPath >= 0; --idxPath)
  861. {
  862. const AkDiffractionPathInfo& path = paths[idxPath];
  863. FColor purple(0x492E74);
  864. FColor green(0x267158);
  865. if (path.nodeCount > 0)
  866. {
  867. const int kPathThickness = 5.f;
  868. const float kRadiusSphereMax = 35.f;
  869. const float kRadiusSphereMin = 2.f;
  870. const FVector emitterPos = FAkAudioDevice::AKVector64ToFVector(akEmitterPos);
  871. const FVector listenerPos = FAkAudioDevice::AKVector64ToFVector(akListenerPos);
  872. FVector prevPt = FAkAudioDevice::AKVector64ToFVector(akListenerPos);
  873. for (int idxSeg = 0; idxSeg < (int)path.nodeCount; ++idxSeg)
  874. {
  875. const FVector pt = FAkAudioDevice::AKVector64ToFVector(path.nodes[idxSeg]);
  876. if (idxSeg != 0)
  877. {
  878. ::DrawDebugLine(GWorld, prevPt, pt, green, false, -1.f, (uint8)'\000', kPathThickness);
  879. }
  880. float rad = kRadiusSphereMin + (1.f - path.angles[idxSeg] / PI) * (kRadiusSphereMax - kRadiusSphereMin);
  881. ::DrawDebugSphere(GWorld, pt, rad, 8, path.portals[idxSeg].IsValid() ? green : purple );
  882. prevPt = pt;
  883. }
  884. // Finally the last path segment towards the emitter.
  885. ::DrawDebugLine(GWorld, prevPt, emitterPos, green, false, -1.f, (uint8)'\000', kPathThickness);
  886. }
  887. }
  888. }
  889. void UAkComponent::DebugDrawReflections() const
  890. {
  891. auto* SpatialAudio = IWwiseSpatialAudioAPI::Get();
  892. if (UNLIKELY(!SpatialAudio)) return;
  893. enum { kMaxPaths = 64 };
  894. AkReflectionPathInfo paths[kMaxPaths];
  895. AkUInt32 uNumPaths = kMaxPaths;
  896. AkVector64 listenerPos, emitterPos;
  897. if (SpatialAudio->QueryReflectionPaths(GetAkGameObjectID(), 0, listenerPos, emitterPos, paths, uNumPaths) == AK_Success && uNumPaths > 0)
  898. _DebugDrawReflections(emitterPos, listenerPos, paths, uNumPaths);
  899. }
  900. void UAkComponent::DebugDrawDiffraction() const
  901. {
  902. auto* SpatialAudio = IWwiseSpatialAudioAPI::Get();
  903. if (UNLIKELY(!SpatialAudio)) return;
  904. enum { kMaxPaths = 16 };
  905. AkDiffractionPathInfo paths[kMaxPaths];
  906. AkUInt32 uNumPaths = kMaxPaths;
  907. AkVector64 listenerPos, emitterPos;
  908. if (SpatialAudio->QueryDiffractionPaths(GetAkGameObjectID(), 0, listenerPos, emitterPos, paths, uNumPaths) == AK_Success)
  909. {
  910. if (uNumPaths > 0)
  911. _DebugDrawDiffraction(emitterPos, listenerPos, paths, uNumPaths);
  912. }
  913. }
  914. void UAkComponent::SetGameObjectRadius(float in_outerRadius, float in_innerRadius)
  915. {
  916. outerRadius = in_outerRadius;
  917. innerRadius = in_innerRadius;
  918. FAkAudioDevice* AkAudioDevice = FAkAudioDevice::Get();
  919. if (AkAudioDevice && IsRegisteredWithWwise)
  920. {
  921. AkAudioDevice->SetGameObjectRadius(this, outerRadius, innerRadius);
  922. }
  923. }
  924. void UAkComponent::SetEnableSpotReflectors(bool in_enable)
  925. {
  926. if (EnableSpotReflectors != in_enable)
  927. {
  928. EnableSpotReflectors = in_enable;
  929. AAkSpotReflector::UpdateSpotReflectors(this);
  930. }
  931. }
  932. #if WITH_EDITOR
  933. void UAkComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
  934. {
  935. Super::PostEditChangeProperty(PropertyChangedEvent);
  936. if (PropertyChangedEvent.Property)
  937. {
  938. if ((PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UAkComponent, outerRadius) ||
  939. PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UAkComponent, innerRadius)) &&
  940. PropertyChangedEvent.ChangeType == EPropertyChangeType::ValueSet)
  941. {
  942. if (innerRadius > outerRadius)
  943. innerRadius = outerRadius;
  944. SetGameObjectRadius(outerRadius, innerRadius);
  945. }
  946. if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UAkComponent, EnableSpotReflectors) &&
  947. PropertyChangedEvent.ChangeType == EPropertyChangeType::ValueSet)
  948. {
  949. AAkSpotReflector::UpdateSpotReflectors(this);
  950. }
  951. }
  952. }
  953. #endif