AkObstructionAndOcclusionService.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  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. AkObstructionAndOcclusionService.cpp:
  17. =============================================================================*/
  18. #include "Wwise/AkObstructionAndOcclusionService.h"
  19. #include "Wwise/Stats/ObstructionOcclusion.h"
  20. #include "WwiseUnrealObjectHelper.h"
  21. #include "WwiseUnrealEngineHelper.h"
  22. #include "Engine/World.h"
  23. #include "Engine/Engine.h"
  24. #include "Components/PrimitiveComponent.h"
  25. #include "Async/Async.h"
  26. #include "GameFramework/PlayerController.h"
  27. #define AK_DEBUG_OCCLUSION_PRINT 0
  28. #if AK_DEBUG_OCCLUSION_PRINT
  29. static int framecounter = 0;
  30. #endif
  31. #define AK_DEBUG_OCCLUSION 0
  32. #if AK_DEBUG_OCCLUSION
  33. #include "DrawDebugHelpers.h"
  34. #endif
  35. FAkObstructionAndOcclusion::FAkObstructionAndOcclusion(float InTargetValue, float InCurrentValue)
  36. : CurrentValue(InCurrentValue)
  37. , TargetValue(InTargetValue)
  38. , Rate(0.0f)
  39. {}
  40. void FAkObstructionAndOcclusion::SetTarget(float InTargetValue)
  41. {
  42. TargetValue = FMath::Clamp(InTargetValue, 0.0f, 1.0f);
  43. const float UAkComponent_OCCLUSION_FADE_RATE = 2.0f; // from 0.0 to 1.0 in 0.5 seconds
  44. Rate = FMath::Sign(TargetValue - CurrentValue) * UAkComponent_OCCLUSION_FADE_RATE;
  45. }
  46. bool FAkObstructionAndOcclusion::Update(float InDeltaTime)
  47. {
  48. auto OldValue = CurrentValue;
  49. if (OldValue != TargetValue)
  50. {
  51. const auto NewValue = OldValue + Rate * InDeltaTime;
  52. if (OldValue > TargetValue)
  53. CurrentValue = FMath::Clamp(NewValue, TargetValue, OldValue);
  54. else
  55. CurrentValue = FMath::Clamp(NewValue, OldValue, TargetValue);
  56. checkf(CurrentValue >= 0.f && CurrentValue <= 1.f, TEXT("FAkObstructionAndOcclusion::Update: CurrentValue (%f) not normalized."), CurrentValue);
  57. return true;
  58. }
  59. return false;
  60. }
  61. bool FAkObstructionAndOcclusion::ReachedTarget()
  62. {
  63. return CurrentValue == TargetValue;
  64. }
  65. //=====================================================================================
  66. // FAkListenerObstructionAndOcclusionPair
  67. //=====================================================================================
  68. FAkObstructionAndOcclusionPair::FAkObstructionAndOcclusionPair()
  69. {
  70. SourceRayCollisions.AddZeroed(NUM_BOUNDING_BOX_TRACE_POINTS);
  71. ListenerRayCollisions.AddZeroed(NUM_BOUNDING_BOX_TRACE_POINTS);
  72. SourceTraceHandles.AddDefaulted(NUM_BOUNDING_BOX_TRACE_POINTS);
  73. ListenerTraceHandles.AddDefaulted(NUM_BOUNDING_BOX_TRACE_POINTS);
  74. }
  75. bool FAkObstructionAndOcclusionPair::Update(float InDeltaTime)
  76. {
  77. if (CurrentCollisionCount != GetCollisionCount())
  78. {
  79. CurrentCollisionCount = GetCollisionCount();
  80. const float ratio = (float)CurrentCollisionCount / NUM_BOUNDING_BOX_TRACE_POINTS;
  81. Occ.SetTarget(ratio);
  82. Obs.SetTarget(ratio);
  83. }
  84. const bool bObsChanged = Obs.Update(InDeltaTime);
  85. const bool bOccChanged = Occ.Update(InDeltaTime);
  86. return bObsChanged || bOccChanged;
  87. }
  88. void FAkObstructionAndOcclusionPair::Reset()
  89. {
  90. for (int i = 0; i < NUM_BOUNDING_BOX_TRACE_POINTS; ++i)
  91. {
  92. SourceRayCollisions[i] = ListenerRayCollisions[i] = false;
  93. }
  94. }
  95. bool FAkObstructionAndOcclusionPair::ReachedTarget()
  96. {
  97. return Obs.ReachedTarget() && Occ.ReachedTarget();
  98. }
  99. void FAkObstructionAndOcclusionPair::AsyncTraceFromSource(const FVector& InSourcePosition, const FVector& InEndPosition, int InBoundingBoxPointIndex, ECollisionChannel InCollisionChannel, UWorld* InWorld, const FCollisionQueryParams& InCollisionParams)
  100. {
  101. ensure(InBoundingBoxPointIndex < NUM_BOUNDING_BOX_TRACE_POINTS);
  102. // Check that we're not stacking another async trace on top of one that hasn't completed yet.
  103. if (!InWorld->IsTraceHandleValid(SourceTraceHandles[InBoundingBoxPointIndex], false))
  104. {
  105. SourceTraceHandles[InBoundingBoxPointIndex] = InWorld->AsyncLineTraceByChannel(EAsyncTraceType::Single, InSourcePosition, InEndPosition, InCollisionChannel, InCollisionParams);
  106. }
  107. }
  108. void FAkObstructionAndOcclusionPair::AsyncTraceFromListener(const FVector& InListenerPosition, const FVector& InEndPosition, int InBoundingBoxPointIndex, ECollisionChannel InCollisionChannel, UWorld* InWorld, const FCollisionQueryParams& InCollisionParams)
  109. {
  110. ensure(InBoundingBoxPointIndex < NUM_BOUNDING_BOX_TRACE_POINTS);
  111. // Check that we're not stacking another async trace on top of one that hasn't completed yet.
  112. if (!InWorld->IsTraceHandleValid(ListenerTraceHandles[InBoundingBoxPointIndex], false))
  113. {
  114. ListenerTraceHandles[InBoundingBoxPointIndex] = InWorld->AsyncLineTraceByChannel(EAsyncTraceType::Single, InListenerPosition, InEndPosition, InCollisionChannel, InCollisionParams);
  115. }
  116. }
  117. int FAkObstructionAndOcclusionPair::GetCollisionCount()
  118. {
  119. int CollisionCount = 0;
  120. for (int i = 0; i < NUM_BOUNDING_BOX_TRACE_POINTS; ++i)
  121. {
  122. CollisionCount += (SourceRayCollisions[i] || ListenerRayCollisions[i]) ? 1 : 0;
  123. }
  124. return CollisionCount;
  125. }
  126. void FAkObstructionAndOcclusionPair::CheckTraceResults(UWorld* InWorld)
  127. {
  128. CheckListenerTraceHandles(InWorld);
  129. CheckSourceTraceHandles(InWorld);
  130. }
  131. void FAkObstructionAndOcclusionPair::CheckListenerTraceHandles(UWorld* InWorld)
  132. {
  133. for (int BoundingBoxPointIndex = 0; BoundingBoxPointIndex < NUM_BOUNDING_BOX_TRACE_POINTS; ++BoundingBoxPointIndex)
  134. {
  135. if (ListenerTraceHandles[BoundingBoxPointIndex]._Data.FrameNumber != 0)
  136. {
  137. FTraceDatum OutData;
  138. if (InWorld->QueryTraceData(ListenerTraceHandles[BoundingBoxPointIndex], OutData))
  139. {
  140. ListenerTraceHandles[BoundingBoxPointIndex]._Data.FrameNumber = 0;
  141. ListenerRayCollisions[BoundingBoxPointIndex] = OutData.OutHits.Num() > 0;
  142. }
  143. }
  144. }
  145. }
  146. void FAkObstructionAndOcclusionPair::CheckSourceTraceHandles(UWorld* InWorld)
  147. {
  148. for (int BoundingBoxPointIndex = 0; BoundingBoxPointIndex < NUM_BOUNDING_BOX_TRACE_POINTS; ++BoundingBoxPointIndex)
  149. {
  150. if (SourceTraceHandles[BoundingBoxPointIndex]._Data.FrameNumber != 0)
  151. {
  152. FTraceDatum OutData;
  153. if (InWorld->QueryTraceData(SourceTraceHandles[BoundingBoxPointIndex], OutData))
  154. {
  155. SourceTraceHandles[BoundingBoxPointIndex]._Data.FrameNumber = 0;
  156. SourceRayCollisions[BoundingBoxPointIndex] = OutData.OutHits.Num() > 0;
  157. }
  158. }
  159. }
  160. }
  161. //=====================================================================================
  162. // AkObstructionAndOcclusionService
  163. //=====================================================================================
  164. void AkObstructionAndOcclusionService::_Init(UWorld* InWorld, float InRefreshInterval)
  165. {
  166. if (InRefreshInterval > 0 && InWorld != nullptr)
  167. LastObstructionAndOcclusionRefresh = InWorld->GetTimeSeconds() + FMath::RandRange(0.0f, InRefreshInterval);
  168. else
  169. LastObstructionAndOcclusionRefresh = -1;
  170. }
  171. void AkObstructionAndOcclusionService::RefreshObstructionAndOcclusion(
  172. const ListenerMap& InListeners,
  173. const PortalMap& InPortals,
  174. const FVector& InSourcePosition,
  175. const AActor* InActor,
  176. const AkRoomID InRoomID,
  177. ECollisionChannel InCollisionChannel,
  178. const float InDeltaTime,
  179. float InOcclusionRefreshInterval)
  180. {
  181. UWorld* CurrentWorld = InActor ? InActor->GetWorld() : nullptr;
  182. if (CurrentWorld == nullptr)
  183. {
  184. return;
  185. }
  186. // Fade the active occlusions
  187. bool StillClearingObsOcc = false;
  188. for (auto It = ListenerObsOccMap.CreateIterator(); It; ++It)
  189. {
  190. AkGameObjectID ListenerID = It->Key;
  191. if (InListeners.Find(ListenerID) == nullptr)
  192. {
  193. It.RemoveCurrent();
  194. continue;
  195. }
  196. FAkObstructionAndOcclusionPair& ObsOccPair = It->Value;
  197. ObsOccPair.CheckTraceResults(CurrentWorld);
  198. if (ObsOccPair.Update(InDeltaTime))
  199. {
  200. SetListenerObstructionAndOcclusion(ObsOccPair, ListenerID);
  201. }
  202. if (bClearingObstructionAndOcclusion)
  203. {
  204. StillClearingObsOcc |= !ObsOccPair.ReachedTarget();
  205. }
  206. }
  207. auto PortalObsOccMap = PortalObsOccMapPerRoom.Find(InRoomID);
  208. if (PortalObsOccMap)
  209. {
  210. for (auto It = PortalObsOccMap->CreateIterator(); It; ++It)
  211. {
  212. AkPortalID PortalID = It->Key;
  213. auto Portal = InPortals.Find(PortalID);
  214. if (Portal == nullptr)
  215. {
  216. It.RemoveCurrent();
  217. continue;
  218. }
  219. FAkObstructionAndOcclusionPair& ObsOccPair = It->Value;
  220. ObsOccPair.CheckTraceResults(CurrentWorld);
  221. if (ObsOccPair.Update(InDeltaTime))
  222. {
  223. SetPortalObstruction(PortalID, ObsOccPair.Obs.CurrentValue);
  224. }
  225. if (bClearingObstructionAndOcclusion)
  226. {
  227. StillClearingObsOcc |= !ObsOccPair.ReachedTarget();
  228. }
  229. }
  230. }
  231. if (bClearingObstructionAndOcclusion)
  232. {
  233. bClearingObstructionAndOcclusion = StillClearingObsOcc;
  234. return;
  235. }
  236. float CurrentTime = CurrentWorld->GetTimeSeconds();
  237. if (CurrentTime < LastObstructionAndOcclusionRefresh && LastObstructionAndOcclusionRefresh - CurrentTime > InOcclusionRefreshInterval)
  238. {
  239. // Occlusion refresh interval was made shorter since the last refresh, we need to re-distribute the next random calculation
  240. LastObstructionAndOcclusionRefresh = CurrentTime + FMath::RandRange(0.0f, InOcclusionRefreshInterval);
  241. }
  242. if (LastObstructionAndOcclusionRefresh == -1 || (CurrentTime - LastObstructionAndOcclusionRefresh) >= InOcclusionRefreshInterval)
  243. {
  244. LastObstructionAndOcclusionRefresh = CurrentTime;
  245. for (auto& Listener : InListeners)
  246. {
  247. auto& MapEntry = ListenerObsOccMap.FindOrAdd(Listener.Key);
  248. MapEntry.Position = Listener.Value.Position;
  249. }
  250. CalculateObstructionAndOcclusionValuesToListeners(InListeners, InSourcePosition, InActor, InCollisionChannel);
  251. for (auto& Portal : InPortals)
  252. {
  253. auto& MapEntry = PortalObsOccMapPerRoom.FindOrAdd(InRoomID).FindOrAdd(Portal.Key);
  254. MapEntry.Position = Portal.Value.Position;
  255. }
  256. CalculateObstructionValuesToPortals(InPortals, InSourcePosition, InActor, InRoomID, InCollisionChannel);
  257. }
  258. }
  259. void _CalculateObstructionAndOcclusionValues(FAkObstructionAndOcclusionPair* InObsOccPair, UWorld* InCurrentWorld, const FVector& InSourcePosition, const FVector& InDestinationPosition, ECollisionChannel InCollisionChannel, FCollisionQueryParams InCollisionParams, bool bInAsync)
  260. {
  261. FHitResult OutHit;
  262. const bool bNowOccluded = InCurrentWorld->LineTraceSingleByChannel(OutHit, InSourcePosition, InDestinationPosition, InCollisionChannel, InCollisionParams);
  263. if (bNowOccluded)
  264. {
  265. FBox BoundingBox;
  266. AActor* HitActor = WwiseUnrealHelper::GetActorFromHitResult(OutHit);
  267. if (HitActor)
  268. {
  269. BoundingBox = HitActor->GetComponentsBoundingBox();
  270. }
  271. else if (OutHit.Component.IsValid())
  272. {
  273. BoundingBox = OutHit.Component->Bounds.GetBox();
  274. }
  275. // Translate the impact point to the bounding box of the obstacle
  276. const FVector Points[] =
  277. {
  278. FVector(OutHit.ImpactPoint.X, BoundingBox.Min.Y, BoundingBox.Min.Z),
  279. FVector(OutHit.ImpactPoint.X, BoundingBox.Min.Y, BoundingBox.Max.Z),
  280. FVector(OutHit.ImpactPoint.X, BoundingBox.Max.Y, BoundingBox.Min.Z),
  281. FVector(OutHit.ImpactPoint.X, BoundingBox.Max.Y, BoundingBox.Max.Z),
  282. FVector(BoundingBox.Min.X, OutHit.ImpactPoint.Y, BoundingBox.Min.Z),
  283. FVector(BoundingBox.Min.X, OutHit.ImpactPoint.Y, BoundingBox.Max.Z),
  284. FVector(BoundingBox.Max.X, OutHit.ImpactPoint.Y, BoundingBox.Min.Z),
  285. FVector(BoundingBox.Max.X, OutHit.ImpactPoint.Y, BoundingBox.Max.Z),
  286. FVector(BoundingBox.Min.X, BoundingBox.Min.Y, OutHit.ImpactPoint.Z),
  287. FVector(BoundingBox.Min.X, BoundingBox.Max.Y, OutHit.ImpactPoint.Z),
  288. FVector(BoundingBox.Max.X, BoundingBox.Min.Y, OutHit.ImpactPoint.Z),
  289. FVector(BoundingBox.Max.X, BoundingBox.Max.Y, OutHit.ImpactPoint.Z)
  290. };
  291. if (bInAsync)
  292. {
  293. for (int PointIndex = 0; PointIndex < NUM_BOUNDING_BOX_TRACE_POINTS; ++PointIndex)
  294. {
  295. auto Point = Points[PointIndex];
  296. InObsOccPair->AsyncTraceFromListener(InDestinationPosition, Point, PointIndex, InCollisionChannel, InCurrentWorld, InCollisionParams);
  297. InObsOccPair->AsyncTraceFromSource(InSourcePosition, Point, PointIndex, InCollisionChannel, InCurrentWorld, InCollisionParams);
  298. }
  299. }
  300. else
  301. {
  302. // Compute the number of "second order paths" that are also obstructed. This will allow us to approximate
  303. // "how obstructed" the source is.
  304. int32 NumObstructedPaths = 0;
  305. for (const auto& Point : Points)
  306. {
  307. if (InCurrentWorld->LineTraceSingleByChannel(OutHit, InDestinationPosition, Point, InCollisionChannel, InCollisionParams) ||
  308. InCurrentWorld->LineTraceSingleByChannel(OutHit, InSourcePosition, Point, InCollisionChannel, InCollisionParams))
  309. ++NumObstructedPaths;
  310. }
  311. // Modulate occlusion by blocked secondary paths.
  312. const float ratio = (float)NumObstructedPaths / NUM_BOUNDING_BOX_TRACE_POINTS;
  313. InObsOccPair->Occ.SetTarget(ratio);
  314. InObsOccPair->Obs.SetTarget(ratio);
  315. }
  316. #if AK_DEBUG_OCCLUSION
  317. check(IsInGameThread());
  318. // Draw bounding box and "second order paths"
  319. //UE_LOG(LogAkAudio, Log, TEXT("Target Occlusion level: %f"), ListenerOcclusionInfo[ListenerIdx].TargetValue);
  320. FlushPersistentDebugLines(InCurrentWorld);
  321. FlushDebugStrings(InCurrentWorld);
  322. DrawDebugBox(InCurrentWorld, BoundingBox.GetCenter(), BoundingBox.GetExtent(), FColor::White, false, 4);
  323. DrawDebugPoint(InCurrentWorld, InDestinationPosition, 10.0f, FColor(0, 255, 0), false, 4);
  324. DrawDebugPoint(InCurrentWorld, InSourcePosition, 10.0f, FColor(0, 255, 0), false, 4);
  325. DrawDebugPoint(InCurrentWorld, OutHit.ImpactPoint, 10.0f, FColor(0, 255, 0), false, 4);
  326. for (int32 i = 0; i < NUM_BOUNDING_BOX_TRACE_POINTS; i++)
  327. {
  328. DrawDebugPoint(InCurrentWorld, Points[i], 10.0f, FColor(255, 255, 0), false, 4);
  329. DrawDebugString(InCurrentWorld, Points[i], FString::Printf(TEXT("%d"), i), nullptr, FColor::White, 4);
  330. DrawDebugLine(InCurrentWorld, Points[i], InDestinationPosition, FColor::Cyan, false, 4);
  331. DrawDebugLine(InCurrentWorld, Points[i], InSourcePosition, FColor::Cyan, false, 4);
  332. }
  333. FColor LineColor = FColor::MakeRedToGreenColorFromScalar(1.0f - InObsOccPair->Occ.TargetValue);
  334. DrawDebugLine(InCurrentWorld, InDestinationPosition, InSourcePosition, LineColor, false, 4);
  335. #endif // AK_DEBUG_OCCLUSION
  336. }
  337. else
  338. {
  339. InObsOccPair->Occ.SetTarget(0.0f);
  340. InObsOccPair->Obs.SetTarget(0.0f);
  341. InObsOccPair->Reset();
  342. }
  343. }
  344. void AkObstructionAndOcclusionService::CalculateObstructionAndOcclusionValuesToListeners(const ListenerMap& InListeners, const FVector& InSourcePosition, const AActor* InActor, ECollisionChannel InCollisionChannel, bool bInAsync /* = true */)
  345. {
  346. SCOPED_WWISEOBSTRUCTIONOCCLUSION_EVENT_3(TEXT("AkObstructionAndOcclusionService::CalculateObstructionAndOcclusionValuesToListeners"));
  347. auto CurrentWorld = InActor->GetWorld();
  348. if (!CurrentWorld)
  349. {
  350. return;
  351. }
  352. static const FName NAME_SoundOcclusion = TEXT("SoundOcclusion");
  353. FCollisionQueryParams CollisionParams(NAME_SoundOcclusion, true, InActor);
  354. auto PlayerController = GEngine->GetFirstLocalPlayerController(CurrentWorld);
  355. if (PlayerController)
  356. {
  357. CollisionParams.AddIgnoredActor(PlayerController->GetViewTarget());
  358. }
  359. for (auto& Listener : InListeners)
  360. {
  361. auto MapEntry = ListenerObsOccMap.Find(Listener.Key);
  362. if (MapEntry == nullptr)
  363. {
  364. continue;
  365. }
  366. const FVector ListenerPosition = MapEntry->Position;
  367. _CalculateObstructionAndOcclusionValues(MapEntry, CurrentWorld, InSourcePosition, ListenerPosition, InCollisionChannel, CollisionParams, bInAsync);
  368. }
  369. }
  370. void AkObstructionAndOcclusionService::CalculateObstructionValuesToPortals(const PortalMap& InPortals, const FVector& InSourcePosition, const AActor* InActor, const AkRoomID InRoomID, ECollisionChannel InCollisionChannel, bool bInAsync/* = true*/)
  371. {
  372. SCOPED_WWISEOBSTRUCTIONOCCLUSION_EVENT_3(TEXT("AkObstructionAndOcclusionService::CalculateObstructionValuesToPortals"));
  373. auto CurrentWorld = InActor->GetWorld();
  374. if (!CurrentWorld)
  375. {
  376. return;
  377. }
  378. static const FName NAME_SoundOcclusion = TEXT("SoundOcclusion");
  379. FCollisionQueryParams CollisionParams(NAME_SoundOcclusion, true, InActor);
  380. auto PlayerController = GEngine->GetFirstLocalPlayerController(CurrentWorld);
  381. if (PlayerController)
  382. {
  383. CollisionParams.AddIgnoredActor(PlayerController->GetViewTarget());
  384. }
  385. for (auto& Portal : InPortals)
  386. {
  387. if (!Portal.Value.EnableObstruction)
  388. {
  389. continue;
  390. }
  391. auto PortalObsOccMap = PortalObsOccMapPerRoom.Find(InRoomID);
  392. if (PortalObsOccMap == nullptr)
  393. {
  394. continue;
  395. }
  396. auto MapEntry = PortalObsOccMap->Find(Portal.Key);
  397. if (MapEntry == nullptr)
  398. {
  399. continue;
  400. }
  401. const FVector PortalPosition = MapEntry->Position;
  402. _CalculateObstructionAndOcclusionValues(MapEntry, CurrentWorld, InSourcePosition, PortalPosition, InCollisionChannel, CollisionParams, bInAsync);
  403. }
  404. }
  405. void AkObstructionAndOcclusionService::SetListenerObstructionAndOcclusion(const ListenerMap& InListeners)
  406. {
  407. SCOPED_WWISEOBSTRUCTIONOCCLUSION_EVENT_3(TEXT("AkObstructionAndOcclusionService::SetListenerObstructionAndOcclusion"));
  408. for (auto& Listener : InListeners)
  409. {
  410. auto ListenerID = Listener.Key;
  411. auto MapEntry = ListenerObsOccMap.Find(ListenerID);
  412. if (MapEntry == nullptr)
  413. {
  414. continue;
  415. }
  416. MapEntry->Occ.CurrentValue = MapEntry->Occ.TargetValue;
  417. SetListenerObstructionAndOcclusion(*MapEntry, ListenerID);
  418. }
  419. }
  420. void AkObstructionAndOcclusionService::SetListenerObstructionAndOcclusion(const FAkObstructionAndOcclusionPair& InObsOccPair, const AkGameObjectID InListenerID)
  421. {
  422. SetObstructionAndOcclusion(InListenerID, InObsOccPair.Obs.CurrentValue);
  423. }
  424. void AkObstructionAndOcclusionService::SetPortalObstruction(const PortalMap& InPortals, const AkRoomID InRoomID)
  425. {
  426. SCOPED_WWISEOBSTRUCTIONOCCLUSION_EVENT_3(TEXT("AkObstructionAndOcclusionService::SetPortalObstruction"));
  427. for (auto& Portal : InPortals)
  428. {
  429. AkPortalID PortalID = Portal.Key;
  430. auto PortalObsOccMap = PortalObsOccMapPerRoom.Find(InRoomID);
  431. if (PortalObsOccMap == nullptr)
  432. {
  433. continue;
  434. }
  435. auto MapEntry = PortalObsOccMap->Find(PortalID);
  436. if (MapEntry == nullptr)
  437. {
  438. continue;
  439. }
  440. SetPortalObstruction(PortalID, MapEntry->Obs.CurrentValue);
  441. }
  442. }
  443. void AkObstructionAndOcclusionService::ClearOcclusionValues()
  444. {
  445. bClearingObstructionAndOcclusion = false;
  446. for (auto& ListenerPack : ListenerObsOccMap)
  447. {
  448. FAkObstructionAndOcclusionPair& Pair = ListenerPack.Value;
  449. Pair.Occ.SetTarget(0.0f);
  450. Pair.Obs.SetTarget(0.0f);
  451. bClearingObstructionAndOcclusion |= !Pair.ReachedTarget();
  452. }
  453. }
  454. void AkObstructionAndOcclusionService::Tick(
  455. const ListenerMap& InListeners,
  456. const PortalMap& InPortals,
  457. const FVector& InSourcePosition,
  458. const AActor* InActor,
  459. const AkRoomID InRoomID,
  460. ECollisionChannel InCollisionChannel,
  461. float InDeltaTime,
  462. float InOcclusionRefreshInterval)
  463. {
  464. // Check Occlusion/Obstruction, if enabled
  465. if (InOcclusionRefreshInterval > 0.0f || bClearingObstructionAndOcclusion)
  466. {
  467. RefreshObstructionAndOcclusion(InListeners, InPortals, InSourcePosition, InActor, InRoomID, InCollisionChannel, InDeltaTime, InOcclusionRefreshInterval);
  468. }
  469. else if (InOcclusionRefreshInterval != PreviousRefreshInterval)
  470. {
  471. // Reset the occlusion obstruction pairs so that the occlusion is correctly recalculated.
  472. for (auto& ListenerPack : ListenerObsOccMap)
  473. {
  474. FAkObstructionAndOcclusionPair& Pair = ListenerPack.Value;
  475. Pair.Reset();
  476. }
  477. if (InOcclusionRefreshInterval <= 0.0f)
  478. ClearOcclusionValues();
  479. }
  480. PreviousRefreshInterval = InOcclusionRefreshInterval;
  481. }
  482. void AkObstructionAndOcclusionService::UpdateObstructionAndOcclusion(
  483. const ListenerMap& InListeners,
  484. const PortalMap& InPortals,
  485. const FVector& InSourcePosition,
  486. const AActor* InActor,
  487. const AkRoomID InRoomID,
  488. ECollisionChannel InCollisionChannel,
  489. float InOcclusionRefreshInterval)
  490. {
  491. SCOPED_WWISEOBSTRUCTIONOCCLUSION_EVENT_2(TEXT("AkObstructionAndOcclusionService::UpdateObstructionAndOcclusion"));
  492. if ((InOcclusionRefreshInterval > 0.f || bClearingObstructionAndOcclusion) && InActor)
  493. {
  494. for (auto& Listener : InListeners)
  495. {
  496. auto& MapEntry = ListenerObsOccMap.FindOrAdd(Listener.Key);
  497. MapEntry.Position = Listener.Value.Position;
  498. }
  499. CalculateObstructionAndOcclusionValuesToListeners(InListeners, InSourcePosition, InActor, InCollisionChannel, false);
  500. for (auto& ListenerPair : ListenerObsOccMap)
  501. {
  502. ListenerPair.Value.Obs.CurrentValue = ListenerPair.Value.Obs.TargetValue;
  503. ListenerPair.Value.Occ.CurrentValue = ListenerPair.Value.Occ.TargetValue;
  504. }
  505. SetListenerObstructionAndOcclusion(InListeners);
  506. CalculateObstructionValuesToPortals(InPortals, InSourcePosition, InActor, InRoomID, InCollisionChannel, false);
  507. auto PortalObsOccMap = PortalObsOccMapPerRoom.Find(InRoomID);
  508. if (PortalObsOccMap)
  509. {
  510. for (auto& PortalPair : *PortalObsOccMap)
  511. {
  512. PortalPair.Value.Obs.CurrentValue = PortalPair.Value.Obs.TargetValue;
  513. }
  514. SetPortalObstruction(InPortals, InRoomID);
  515. }
  516. }
  517. }