AkReverbDescriptor.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  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. #include "AkReverbDescriptor.h"
  16. #include "AkAudioDevice.h"
  17. #include "AkAcousticTextureSetComponent.h"
  18. #include "AkLateReverbComponent.h"
  19. #include "AkRoomComponent.h"
  20. #include "AkComponentHelpers.h"
  21. #include "AkSettings.h"
  22. #include "Wwise/API/WwiseSpatialAudioAPI.h"
  23. #include "Components/PrimitiveComponent.h"
  24. #include "Rendering/PositionVertexBuffer.h"
  25. #include "Engine/StaticMesh.h"
  26. #include "Components/StaticMeshComponent.h"
  27. #include "Components/ShapeComponent.h"
  28. #include "Components/BrushComponent.h"
  29. #include "PhysicsEngine/BodySetup.h"
  30. #include "PhysicsEngine/ConvexElem.h"
  31. #if AK_USE_PHYSX
  32. #include "PhysXIncludes.h"
  33. #endif
  34. /*=============================================================================
  35. Volume & Area Helpers
  36. =============================================================================*/
  37. // FKBoxElem has its own GetVolume function but it is inaccurate. It uses scale.GetMin() rather than using element-wise multiplication.
  38. float BoxVolume(const FKBoxElem& box, const FVector& scale)
  39. {
  40. return (box.X * scale.X) * (box.Y * scale.Y) * (box.Z * scale.Z);
  41. }
  42. // This is the volume calculated by Unreal in UBodySetup::GetVolume, for box elements. We'll use this to subtract from the total volume, and add the more accurate volume calculated by BoxVolume, above.
  43. float InaccurateBoxVolume(const FKBoxElem& box, const FVector& scale)
  44. {
  45. float MinScale = scale.GetMin();
  46. return (box.X * MinScale) * (box.Y * MinScale) * (box.Z * MinScale);
  47. }
  48. float BoxSurfaceArea(const FKBoxElem& box, const FVector& scale)
  49. {
  50. return box.X * scale.X * box.Y * scale.Y * 2.0f /* top & bottom */
  51. + box.X * scale.X * box.Z * scale.Z * 2.0f /* left & right */
  52. + box.Y * scale.Y * box.Z * scale.Z * 2.0f; /* front & back */
  53. }
  54. float SphereSurfaceArea(const FKSphereElem& sphere, const FVector& scale)
  55. {
  56. return 4.0f * PI * FMath::Pow(sphere.Radius * scale.GetMin(), 2.0f);
  57. }
  58. float CapsuleSurfaceArea(const FKSphylElem& capsule, const FVector& scale)
  59. {
  60. const float r = capsule.Radius * FMath::Min(scale.X, scale.Y);
  61. return 2.0f * PI * r * (2.0f * r + capsule.Length * scale.Z);
  62. }
  63. bool HasSimpleCollisionGeometry(UBodySetup* bodySetup)
  64. {
  65. FKAggregateGeom geometry = bodySetup->AggGeom;
  66. return geometry.BoxElems.Num() > 0 || geometry.ConvexElems.Num() > 0 || geometry.SphereElems.Num() > 0 || geometry.TaperedCapsuleElems.Num() > 0 || geometry.SphylElems.Num() > 0;
  67. }
  68. #if AK_USE_CHAOS
  69. // Copied from BodySetup.cpp
  70. // References:
  71. // http://amp.ece.cmu.edu/Publication/Cha/icip01_Cha.pdf
  72. // http://stackoverflow.com/questions/1406029/how-to-calculate-the-volume-of-a-3d-mesh-object-the-surface-of-which-is-made-up
  73. float FAkReverbDescriptor::SignedVolumeOfTriangle(const FVector& p1, const FVector& p2, const FVector& p3)
  74. {
  75. return FVector::DotProduct(p1, FVector::CrossProduct(p2, p3)) / 6.0f;
  76. }
  77. #endif
  78. void UpdateVolumeAndArea(UBodySetup* bodySetup, const FVector& scale, float& volume, float& surfaceArea)
  79. {
  80. surfaceArea = 0.0f;
  81. // Initially use the Unreal UBodySetup::GetVolume function to calculate volume...
  82. #if UE_5_1_OR_LATER
  83. volume = bodySetup->GetScaledVolume(scale);
  84. #else
  85. volume = bodySetup->GetVolume(scale);
  86. #endif
  87. FKAggregateGeom& geometry = bodySetup->AggGeom;
  88. for (const FKBoxElem& box : geometry.BoxElems)
  89. {
  90. surfaceArea += BoxSurfaceArea(box, scale);
  91. #if (!UE_5_1_OR_LATER)
  92. // ... correct for any FKBoxElem elements in the geometry.
  93. // UBodySetup::GetVolume has an inaccuracy for box elements. It is scaled uniformly by the minimum scale dimension (see FKBoxElem::GetVolume).
  94. // For our purposes we want to scale by each dimension individually.
  95. volume -= InaccurateBoxVolume(box, scale);
  96. volume += BoxVolume(box, scale);
  97. #endif
  98. }
  99. for (const FKConvexElem& convexElem : geometry.ConvexElems)
  100. {
  101. FTransform ScaleTransform = FTransform(FQuat::Identity, FVector::ZeroVector, scale);
  102. int32 numTriangles = convexElem.IndexData.Num() / 3;
  103. for (int32 triIdx = 0; triIdx < numTriangles; ++triIdx)
  104. {
  105. FVector v0 = ScaleTransform.TransformPosition(convexElem.VertexData[convexElem.IndexData[3 * triIdx]]);
  106. FVector v1 = ScaleTransform.TransformPosition(convexElem.VertexData[convexElem.IndexData[3 * triIdx + 1]]);
  107. FVector v2 = ScaleTransform.TransformPosition(convexElem.VertexData[convexElem.IndexData[3 * triIdx + 2]]);
  108. surfaceArea += FAkReverbDescriptor::TriangleArea(v0, v1, v2);
  109. #if AK_USE_CHAOS && !(UE_5_1_OR_LATER)
  110. // FKConvexElem::GetVolume is not implemented with Chaos before UE 5.1
  111. volume += FAkReverbDescriptor::SignedVolumeOfTriangle(v0, v1, v2);
  112. #endif
  113. }
  114. }
  115. for (const FKSphereElem& sphere : geometry.SphereElems)
  116. {
  117. surfaceArea += SphereSurfaceArea(sphere, scale);
  118. }
  119. for (const FKSphylElem& capsule : geometry.SphylElems)
  120. {
  121. surfaceArea += CapsuleSurfaceArea(capsule, scale);
  122. }
  123. }
  124. bool ConvertToAkAcousticTextures(TArray<FAkAcousticTextureParams>& InTexturesParams, TArray<AkAcousticTexture>& OutTextures)
  125. {
  126. bool bAreAbsorptionValuesZero = true;
  127. for (const FAkAcousticTextureParams& params : InTexturesParams)
  128. {
  129. AkAcousticTexture Texture;
  130. Texture.fAbsorptionLow = params.AbsorptionLow();
  131. Texture.fAbsorptionMidLow = params.AbsorptionMidLow();
  132. Texture.fAbsorptionMidHigh = params.AbsorptionMidHigh();
  133. Texture.fAbsorptionHigh = params.AbsorptionHigh();
  134. OutTextures.Add(Texture);
  135. if (Texture.fAbsorptionLow != 0 ||
  136. Texture.fAbsorptionMidLow != 0 ||
  137. Texture.fAbsorptionMidHigh != 0 ||
  138. Texture.fAbsorptionHigh != 0)
  139. {
  140. bAreAbsorptionValuesZero = false;
  141. }
  142. }
  143. return bAreAbsorptionValuesZero;
  144. }
  145. /*=============================================================================
  146. FAkReverbDescriptor:
  147. =============================================================================*/
  148. double FAkReverbDescriptor::TriangleArea(const FVector& v1, const FVector& v2, const FVector& v3)
  149. {
  150. #if UE_5_0_OR_LATER
  151. double Mag = 0.0;
  152. #else
  153. float Mag = 0.0f;
  154. #endif
  155. FVector Dir;
  156. FVector::CrossProduct(v2 - v1, v3 - v1).ToDirectionAndLength(Dir, Mag);
  157. return 0.5 * Mag;
  158. }
  159. bool FAkReverbDescriptor::ShouldEstimateDecay() const
  160. {
  161. if (IsValid(ReverbComponent) && ReverbComponent->AutoAssignAuxBus)
  162. return true;
  163. if (!IsValid(Primitive) || AkComponentHelpers::GetChildComponentOfType<UAkRoomComponent>(*Primitive) == nullptr)
  164. return false;
  165. return true;
  166. }
  167. bool FAkReverbDescriptor::ShouldEstimateDamping() const
  168. {
  169. if (!IsValid(Primitive) || AkComponentHelpers::GetChildComponentOfType<UAkRoomComponent>(*Primitive) == nullptr)
  170. return false;
  171. return true;
  172. }
  173. bool FAkReverbDescriptor::ShouldEstimatePredelay() const
  174. {
  175. if (!IsValid(Primitive) || AkComponentHelpers::GetChildComponentOfType<UAkRoomComponent>(*Primitive) == nullptr)
  176. return false;
  177. return true;
  178. }
  179. bool FAkReverbDescriptor::RequiresUpdates() const
  180. {
  181. return ShouldEstimateDecay() || ShouldEstimateDamping() || ShouldEstimatePredelay();
  182. }
  183. void FAkReverbDescriptor::SetPrimitive(UPrimitiveComponent* primitive)
  184. {
  185. Primitive = primitive;
  186. }
  187. void FAkReverbDescriptor::SetReverbComponent(UAkLateReverbComponent* InReverbComp)
  188. {
  189. ReverbComponent = InReverbComp;
  190. }
  191. void FAkReverbDescriptor::CalculateT60(UAkLateReverbComponent* InReverbComp)
  192. {
  193. if (IsValid(Primitive))
  194. {
  195. PrimitiveVolume = 0.0f;
  196. PrimitiveSurfaceArea = 0.0f;
  197. T60Decay = 0.0f;
  198. if (Primitive != nullptr)
  199. {
  200. FVector scale = Primitive->GetComponentScale();
  201. UBodySetup* primitiveBody = Primitive->GetBodySetup();
  202. if (primitiveBody != nullptr && HasSimpleCollisionGeometry(primitiveBody))
  203. {
  204. UpdateVolumeAndArea(primitiveBody, scale, PrimitiveVolume, PrimitiveSurfaceArea);
  205. }
  206. else
  207. {
  208. if (UBrushComponent* brush = Cast<UBrushComponent>(Primitive))
  209. {
  210. brush->BuildSimpleBrushCollision();
  211. }
  212. else
  213. {
  214. FString PrimitiveName = "";
  215. Primitive->GetName(PrimitiveName);
  216. FString ActorName = "";
  217. AActor* owner = Primitive->GetOwner();
  218. if (owner != nullptr)
  219. owner->GetName(ActorName);
  220. UE_LOG(LogAkAudio, Warning,
  221. TEXT("Primitive component %s on actor %s has no simple collision geometry.%sCalculations for reverb aux bus assignment will use component bounds. This could be less accurate than using simple collision geometry."),
  222. *PrimitiveName, *ActorName, LINE_TERMINATOR);
  223. // only apply scale to local bounds to calculate volume and surface area.
  224. FTransform transform = Primitive->GetComponentTransform();
  225. transform.SetRotation(FQuat::Identity);
  226. transform.SetLocation(FVector::ZeroVector);
  227. FBoxSphereBounds bounds = Primitive->CalcBounds(transform);
  228. FVector boxDimensions = bounds.BoxExtent * 2.0f;
  229. PrimitiveVolume = boxDimensions.X * boxDimensions.Y * boxDimensions.Z;
  230. PrimitiveSurfaceArea += boxDimensions.X * boxDimensions.Y * 2.0f;
  231. PrimitiveSurfaceArea += boxDimensions.X * boxDimensions.Z * 2.0f;
  232. PrimitiveSurfaceArea += boxDimensions.Y * boxDimensions.Z * 2.0f;
  233. }
  234. }
  235. PrimitiveVolume = FMath::Abs(PrimitiveVolume) / AkComponentHelpers::UnrealUnitsPerCubicMeter(Primitive);
  236. PrimitiveSurfaceArea /= AkComponentHelpers::UnrealUnitsPerSquaredMeter(Primitive);
  237. auto* SpatialAudio = IWwiseSpatialAudioAPI::Get();
  238. if (SpatialAudio && PrimitiveVolume > 0.0f && PrimitiveSurfaceArea > 0.0f)
  239. {
  240. float Absorption = AK_SA_MIN_ENVIRONMENT_ABSORPTION;
  241. TArray<FAkAcousticTextureParams> TexturesParams;
  242. TArray<float> SurfaceAreas;
  243. auto TextureSetComponent = InReverbComp->GetAttachedTextureSetComponent();
  244. if (TextureSetComponent)
  245. {
  246. TextureSetComponent->GetTexturesAndSurfaceAreas(TexturesParams, SurfaceAreas);
  247. checkf(TexturesParams.Num() == SurfaceAreas.Num(), TEXT("FAkReverbDescriptor::CalculateT60: TexturesParams.Num (%d) != SurfaceAreas.Num (%d)"), (int)TexturesParams.Num(), (int)SurfaceAreas.Num());
  248. }
  249. // If we have at least one texture specified, we compute an average absorption value.
  250. if(TexturesParams.Num() != 0)
  251. {
  252. TArray<AkAcousticTexture> Textures;
  253. bool bAreAbsorptionValuesZero = ConvertToAkAcousticTextures(TexturesParams, Textures);
  254. if (!bAreAbsorptionValuesZero)
  255. {
  256. AkAcousticTexture AverageTextures;
  257. SpatialAudio->ReverbEstimation->GetAverageAbsorptionValues(&Textures[0], &SurfaceAreas[0], Textures.Num(), AverageTextures);
  258. float AverageAbsorption = (AverageTextures.fAbsorptionLow + AverageTextures.fAbsorptionMidLow + AverageTextures.fAbsorptionMidHigh + AverageTextures.fAbsorptionHigh) / 4.f;
  259. // We only update the absorption value if the value is above 1
  260. if (AverageAbsorption > AK_SA_MIN_ENVIRONMENT_ABSORPTION)
  261. {
  262. Absorption = AverageAbsorption;
  263. }
  264. }
  265. }
  266. // Else we use the Global Decay Absorption Value
  267. else {
  268. UAkSettings* AkSettings = GetMutableDefault<UAkSettings>();
  269. if (AkSettings != nullptr)
  270. {
  271. Absorption = AkSettings->GlobalDecayAbsorption;
  272. }
  273. }
  274. //calcuate t60 using the Sabine equation
  275. SpatialAudio->ReverbEstimation->EstimateT60Decay(PrimitiveVolume, PrimitiveSurfaceArea, Absorption, T60Decay);
  276. }
  277. }
  278. }
  279. if (IsValid(ReverbComponent))
  280. ReverbComponent->UpdateDecayEstimation(T60Decay, PrimitiveVolume, PrimitiveSurfaceArea);
  281. UpdateDecayRTPC();
  282. }
  283. void FAkReverbDescriptor::CalculateTimeToFirstReflection()
  284. {
  285. auto* SpatialAudio = IWwiseSpatialAudioAPI::Get();
  286. if (SpatialAudio && IsValid(Primitive))
  287. {
  288. FTransform transform = Primitive->GetComponentTransform();
  289. transform.SetRotation(FQuat::Identity);
  290. transform.SetLocation(FVector::ZeroVector);
  291. FBoxSphereBounds bounds = Primitive->CalcBounds(transform);
  292. AkVector extentMeters = FAkAudioDevice::FVectorToAKVector(bounds.BoxExtent / AkComponentHelpers::UnrealUnitsPerMeter(Primitive));
  293. SpatialAudio->ReverbEstimation->EstimateTimeToFirstReflection(extentMeters, TimeToFirstReflection);
  294. }
  295. #if WITH_EDITOR
  296. if (IsValid(ReverbComponent))
  297. ReverbComponent->UpdatePredelayEstimation(TimeToFirstReflection);
  298. #endif
  299. UpdatePredelaytRTPC();
  300. }
  301. void FAkReverbDescriptor::CalculateHFDamping(const UAkAcousticTextureSetComponent* acousticTextureSetComponent)
  302. {
  303. HFDamping = 0.0f;
  304. if (IsValid(Primitive))
  305. {
  306. auto* SpatialAudio = IWwiseSpatialAudioAPI::Get();
  307. const UAkSettings* AkSettings = GetDefault<UAkSettings>();
  308. if (SpatialAudio && AkSettings)
  309. {
  310. TArray<FAkAcousticTextureParams> texturesParams;
  311. TArray<float> surfaceAreas;
  312. acousticTextureSetComponent->GetTexturesAndSurfaceAreas(texturesParams, surfaceAreas);
  313. if (texturesParams.Num() == 0)
  314. {
  315. HFDamping = 0.0f;
  316. }
  317. else
  318. {
  319. bool bAreAbsorptionValuesZero = true;
  320. bool bAreSurfaceAreasZero = true;
  321. TArray<AkAcousticTexture> textures;
  322. bAreAbsorptionValuesZero = ConvertToAkAcousticTextures(texturesParams, textures);
  323. for (int idx=0; idx<surfaceAreas.Num(); idx++)
  324. {
  325. if (surfaceAreas[idx] != 0)
  326. {
  327. bAreSurfaceAreasZero = false;
  328. }
  329. }
  330. if (bAreAbsorptionValuesZero || bAreSurfaceAreasZero)
  331. {
  332. HFDamping = 0.0f;
  333. }
  334. else
  335. {
  336. HFDamping = SpatialAudio->ReverbEstimation->EstimateHFDamping(&textures[0], &surfaceAreas[0], textures.Num());
  337. }
  338. }
  339. }
  340. }
  341. #if WITH_EDITOR
  342. if (IsValid(ReverbComponent))
  343. ReverbComponent->UpdateHFDampingEstimation(HFDamping);
  344. #endif
  345. UpdateDampingRTPC();
  346. }
  347. bool FAkReverbDescriptor::GetRTPCRoom(UAkRoomComponent*& room) const
  348. {
  349. if (!IsValid(Primitive))
  350. return false;
  351. room = AkComponentHelpers::GetChildComponentOfType<UAkRoomComponent>(*Primitive);
  352. if (!CanSetRTPCOnRoom(room))
  353. {
  354. room = nullptr;
  355. }
  356. return room != nullptr;
  357. }
  358. bool FAkReverbDescriptor::CanSetRTPCOnRoom(const UAkRoomComponent* room) const
  359. {
  360. if (FAkAudioDevice::Get() == nullptr
  361. || room == nullptr
  362. || !room->HasBeenRegisteredWithWwise()
  363. || room->GetWorld() == nullptr
  364. || (room->GetWorld()->WorldType != EWorldType::Game && room->GetWorld()->WorldType != EWorldType::PIE))
  365. {
  366. return false;
  367. }
  368. return true;
  369. }
  370. void FAkReverbDescriptor::UpdateDecayRTPC() const
  371. {
  372. UAkRoomComponent* room = nullptr;
  373. if (GetRTPCRoom(room))
  374. {
  375. const UAkSettings* AkSettings = GetDefault<UAkSettings>();
  376. if (AkSettings != nullptr && AkSettings->DecayRTPCInUse())
  377. {
  378. room->SetRTPCValue(AkSettings->DecayEstimateRTPC.LoadSynchronous(), T60Decay, 0, AkSettings->DecayEstimateName);
  379. }
  380. }
  381. }
  382. void FAkReverbDescriptor::UpdateDampingRTPC() const
  383. {
  384. UAkRoomComponent* room = nullptr;
  385. if (GetRTPCRoom(room))
  386. {
  387. const UAkSettings* AkSettings = GetDefault<UAkSettings>();
  388. if (AkSettings != nullptr && AkSettings->DampingRTPCInUse())
  389. {
  390. room->SetRTPCValue(AkSettings->HFDampingRTPC.LoadSynchronous(), HFDamping, 0, *AkSettings->HFDampingName);
  391. }
  392. }
  393. }
  394. void FAkReverbDescriptor::UpdatePredelaytRTPC() const
  395. {
  396. UAkRoomComponent* room = nullptr;
  397. if (GetRTPCRoom(room))
  398. {
  399. const UAkSettings* AkSettings = GetDefault<UAkSettings>();
  400. if (AkSettings != nullptr && AkSettings->PredelayRTPCInUse())
  401. {
  402. room->SetRTPCValue(AkSettings->TimeToFirstReflectionRTPC.LoadSynchronous(), TimeToFirstReflection, 0, *AkSettings->TimeToFirstReflectionName);
  403. }
  404. }
  405. }
  406. void FAkReverbDescriptor::UpdateAllRTPCs(const UAkRoomComponent* room) const
  407. {
  408. checkf(room, TEXT("FAkReverbDescriptor::UpdateAllRTPCs: room is nullptr."));
  409. if (CanSetRTPCOnRoom(room))
  410. {
  411. const UAkSettings* AkSettings = GetDefault<UAkSettings>();
  412. if (AkSettings != nullptr && AkSettings->ReverbRTPCsInUse())
  413. {
  414. if (AkSettings->DecayRTPCInUse())
  415. {
  416. room->SetRTPCValue(AkSettings->DecayEstimateRTPC.LoadSynchronous(), T60Decay, 0, AkSettings->DecayEstimateName);
  417. }
  418. if (AkSettings->DampingRTPCInUse())
  419. {
  420. room->SetRTPCValue(AkSettings->HFDampingRTPC.LoadSynchronous(), HFDamping, 0, *AkSettings->HFDampingName);
  421. }
  422. if (AkSettings->PredelayRTPCInUse())
  423. {
  424. room->SetRTPCValue(AkSettings->TimeToFirstReflectionRTPC.LoadSynchronous(), TimeToFirstReflection, 0, *AkSettings->TimeToFirstReflectionName);
  425. }
  426. }
  427. }
  428. }