AkReverbDescriptor.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  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. // ... correct for any FKBoxElem elements in the geometry.
  92. // UBodySetup::GetVolume has an inaccuracy for box elements. It is scaled uniformly by the minimum scale dimension (see FKBoxElem::GetVolume).
  93. // For our purposes we want to scale by each dimension individually.
  94. volume -= InaccurateBoxVolume(box, scale);
  95. volume += BoxVolume(box, scale);
  96. }
  97. for (const FKConvexElem& convexElem : geometry.ConvexElems)
  98. {
  99. FTransform ScaleTransform = FTransform(FQuat::Identity, FVector::ZeroVector, scale);
  100. int32 numTriangles = convexElem.IndexData.Num() / 3;
  101. for (int32 triIdx = 0; triIdx < numTriangles; ++triIdx)
  102. {
  103. FVector v0 = ScaleTransform.TransformPosition(convexElem.VertexData[convexElem.IndexData[3 * triIdx]]);
  104. FVector v1 = ScaleTransform.TransformPosition(convexElem.VertexData[convexElem.IndexData[3 * triIdx + 1]]);
  105. FVector v2 = ScaleTransform.TransformPosition(convexElem.VertexData[convexElem.IndexData[3 * triIdx + 2]]);
  106. surfaceArea += FAkReverbDescriptor::TriangleArea(v0, v1, v2);
  107. #if AK_USE_CHAOS && !(UE_5_1_OR_LATER)
  108. // FKConvexElem::GetVolume is not implemented with Chaos before UE 5.1
  109. volume += FAkReverbDescriptor::SignedVolumeOfTriangle(v0, v1, v2);
  110. #endif
  111. }
  112. }
  113. for (const FKSphereElem& sphere : geometry.SphereElems)
  114. {
  115. surfaceArea += SphereSurfaceArea(sphere, scale);
  116. }
  117. for (const FKSphylElem& capsule : geometry.SphylElems)
  118. {
  119. surfaceArea += CapsuleSurfaceArea(capsule, scale);
  120. }
  121. }
  122. /*=============================================================================
  123. FAkReverbDescriptor:
  124. =============================================================================*/
  125. double FAkReverbDescriptor::TriangleArea(const FVector& v1, const FVector& v2, const FVector& v3)
  126. {
  127. #if UE_5_0_OR_LATER
  128. double Mag = 0.0;
  129. #else
  130. float Mag = 0.0f;
  131. #endif
  132. FVector Dir;
  133. FVector::CrossProduct(v2 - v1, v3 - v1).ToDirectionAndLength(Dir, Mag);
  134. return 0.5 * Mag;
  135. }
  136. bool FAkReverbDescriptor::ShouldEstimateDecay() const
  137. {
  138. if (IsValid(ReverbComponent) && ReverbComponent->AutoAssignAuxBus)
  139. return true;
  140. if (!IsValid(Primitive) || AkComponentHelpers::GetChildComponentOfType<UAkRoomComponent>(*Primitive) == nullptr)
  141. return false;
  142. return true;
  143. }
  144. bool FAkReverbDescriptor::ShouldEstimateDamping() const
  145. {
  146. if (!IsValid(Primitive) || AkComponentHelpers::GetChildComponentOfType<UAkRoomComponent>(*Primitive) == nullptr)
  147. return false;
  148. return true;
  149. }
  150. bool FAkReverbDescriptor::ShouldEstimatePredelay() const
  151. {
  152. if (!IsValid(Primitive) || AkComponentHelpers::GetChildComponentOfType<UAkRoomComponent>(*Primitive) == nullptr)
  153. return false;
  154. return true;
  155. }
  156. bool FAkReverbDescriptor::RequiresUpdates() const
  157. {
  158. return ShouldEstimateDecay() || ShouldEstimateDamping() || ShouldEstimatePredelay();
  159. }
  160. void FAkReverbDescriptor::SetPrimitive(UPrimitiveComponent* primitive)
  161. {
  162. Primitive = primitive;
  163. }
  164. void FAkReverbDescriptor::SetReverbComponent(UAkLateReverbComponent* reverbComp)
  165. {
  166. ReverbComponent = reverbComp;
  167. }
  168. void FAkReverbDescriptor::CalculateT60()
  169. {
  170. if (IsValid(Primitive))
  171. {
  172. PrimitiveVolume = 0.0f;
  173. PrimitiveSurfaceArea = 0.0f;
  174. T60Decay = 0.0f;
  175. if (Primitive != nullptr)
  176. {
  177. FVector scale = Primitive->GetComponentScale();
  178. UBodySetup* primitiveBody = Primitive->GetBodySetup();
  179. if (primitiveBody != nullptr && HasSimpleCollisionGeometry(primitiveBody))
  180. {
  181. UpdateVolumeAndArea(primitiveBody, scale, PrimitiveVolume, PrimitiveSurfaceArea);
  182. }
  183. else
  184. {
  185. if (UBrushComponent* brush = Cast<UBrushComponent>(Primitive))
  186. {
  187. brush->BuildSimpleBrushCollision();
  188. }
  189. else
  190. {
  191. FString PrimitiveName = "";
  192. Primitive->GetName(PrimitiveName);
  193. FString ActorName = "";
  194. AActor* owner = Primitive->GetOwner();
  195. if (owner != nullptr)
  196. owner->GetName(ActorName);
  197. UE_LOG(LogAkAudio, Warning,
  198. 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."),
  199. *PrimitiveName, *ActorName, LINE_TERMINATOR);
  200. // only apply scale to local bounds to calculate volume and surface area.
  201. FTransform transform = Primitive->GetComponentTransform();
  202. transform.SetRotation(FQuat::Identity);
  203. transform.SetLocation(FVector::ZeroVector);
  204. FBoxSphereBounds bounds = Primitive->CalcBounds(transform);
  205. FVector boxDimensions = bounds.BoxExtent * 2.0f;
  206. PrimitiveVolume = boxDimensions.X * boxDimensions.Y * boxDimensions.Z;
  207. PrimitiveSurfaceArea += boxDimensions.X * boxDimensions.Y * 2.0f;
  208. PrimitiveSurfaceArea += boxDimensions.X * boxDimensions.Z * 2.0f;
  209. PrimitiveSurfaceArea += boxDimensions.Y * boxDimensions.Z * 2.0f;
  210. }
  211. }
  212. PrimitiveVolume = FMath::Abs(PrimitiveVolume) / AkComponentHelpers::UnrealUnitsPerCubicMeter(Primitive);
  213. PrimitiveSurfaceArea /= AkComponentHelpers::UnrealUnitsPerSquaredMeter(Primitive);
  214. auto* SpatialAudio = IWwiseSpatialAudioAPI::Get();
  215. if (SpatialAudio && PrimitiveVolume > 0.0f && PrimitiveSurfaceArea > 0.0f)
  216. {
  217. float absorption = 0.5f;
  218. UAkSettings* AkSettings = GetMutableDefault<UAkSettings>();
  219. if (AkSettings != nullptr)
  220. absorption = AkSettings->GlobalDecayAbsorption;
  221. //calcuate t60 using the Sabine equation
  222. SpatialAudio->ReverbEstimation->EstimateT60Decay(PrimitiveVolume, PrimitiveSurfaceArea, absorption, T60Decay);
  223. }
  224. }
  225. }
  226. #if WITH_EDITOR
  227. if (IsValid(ReverbComponent))
  228. ReverbComponent->UpdateDecayEstimation(T60Decay, PrimitiveVolume, PrimitiveSurfaceArea);
  229. #endif
  230. UpdateDecayRTPC();
  231. }
  232. void FAkReverbDescriptor::CalculateTimeToFirstReflection()
  233. {
  234. auto* SpatialAudio = IWwiseSpatialAudioAPI::Get();
  235. if (SpatialAudio && IsValid(Primitive))
  236. {
  237. FTransform transform = Primitive->GetComponentTransform();
  238. transform.SetRotation(FQuat::Identity);
  239. transform.SetLocation(FVector::ZeroVector);
  240. FBoxSphereBounds bounds = Primitive->CalcBounds(transform);
  241. AkVector extentMeters = FAkAudioDevice::FVectorToAKVector(bounds.BoxExtent / AkComponentHelpers::UnrealUnitsPerMeter(Primitive));
  242. SpatialAudio->ReverbEstimation->EstimateTimeToFirstReflection(extentMeters, TimeToFirstReflection);
  243. }
  244. #if WITH_EDITOR
  245. if (IsValid(ReverbComponent))
  246. ReverbComponent->UpdatePredelayEstimation(TimeToFirstReflection);
  247. #endif
  248. UpdatePredelaytRTPC();
  249. }
  250. void FAkReverbDescriptor::CalculateHFDamping(const UAkAcousticTextureSetComponent* acousticTextureSetComponent)
  251. {
  252. HFDamping = 0.0f;
  253. if (IsValid(Primitive))
  254. {
  255. auto* SpatialAudio = IWwiseSpatialAudioAPI::Get();
  256. const UAkSettings* AkSettings = GetDefault<UAkSettings>();
  257. if (SpatialAudio && AkSettings)
  258. {
  259. TArray<FAkAcousticTextureParams> texturesParams;
  260. TArray<float> surfaceAreas;
  261. acousticTextureSetComponent->GetTexturesAndSurfaceAreas(texturesParams, surfaceAreas);
  262. if (texturesParams.Num() == 0)
  263. {
  264. HFDamping = 0.0f;
  265. }
  266. else
  267. {
  268. bool areAbsorptionValuesZero = true;
  269. bool areSurfaceAreasZero = true;
  270. int idx = 0;
  271. TArray<AkAcousticTexture> textures;
  272. for (const FAkAcousticTextureParams& params : texturesParams)
  273. {
  274. AkAcousticTexture texture;
  275. texture.fAbsorptionLow = params.AbsorptionLow();
  276. texture.fAbsorptionMidLow = params.AbsorptionMidLow();
  277. texture.fAbsorptionMidHigh = params.AbsorptionMidHigh();
  278. texture.fAbsorptionHigh = params.AbsorptionHigh();
  279. textures.Add(texture);
  280. if (texture.fAbsorptionLow != 0 ||
  281. texture.fAbsorptionMidLow != 0 ||
  282. texture.fAbsorptionMidHigh != 0 ||
  283. texture.fAbsorptionHigh != 0)
  284. {
  285. areAbsorptionValuesZero = false;
  286. }
  287. if (surfaceAreas[idx] != 0)
  288. {
  289. areSurfaceAreasZero = false;
  290. }
  291. idx++;
  292. }
  293. if (areAbsorptionValuesZero || areSurfaceAreasZero)
  294. {
  295. HFDamping = 0.0f;
  296. }
  297. else
  298. {
  299. SpatialAudio->ReverbEstimation->EstimateHFDamping(&textures[0], &surfaceAreas[0], textures.Num(), HFDamping);
  300. }
  301. }
  302. }
  303. }
  304. #if WITH_EDITOR
  305. if (IsValid(ReverbComponent))
  306. ReverbComponent->UpdateHFDampingEstimation(HFDamping);
  307. #endif
  308. UpdateDampingRTPC();
  309. }
  310. bool FAkReverbDescriptor::GetRTPCRoom(UAkRoomComponent*& room) const
  311. {
  312. if (!IsValid(Primitive))
  313. return false;
  314. room = AkComponentHelpers::GetChildComponentOfType<UAkRoomComponent>(*Primitive);
  315. if (!CanSetRTPCOnRoom(room))
  316. {
  317. room = nullptr;
  318. }
  319. return room != nullptr;
  320. }
  321. bool FAkReverbDescriptor::CanSetRTPCOnRoom(const UAkRoomComponent* room) const
  322. {
  323. if (FAkAudioDevice::Get() == nullptr
  324. || room == nullptr
  325. || !room->HasBeenRegisteredWithWwise()
  326. || room->GetWorld() == nullptr
  327. || (room->GetWorld()->WorldType != EWorldType::Game && room->GetWorld()->WorldType != EWorldType::PIE))
  328. {
  329. return false;
  330. }
  331. return true;
  332. }
  333. void FAkReverbDescriptor::UpdateDecayRTPC() const
  334. {
  335. UAkRoomComponent* room = nullptr;
  336. if (GetRTPCRoom(room))
  337. {
  338. const UAkSettings* AkSettings = GetDefault<UAkSettings>();
  339. if (AkSettings != nullptr && AkSettings->DecayRTPCInUse())
  340. {
  341. room->SetRTPCValue(AkSettings->DecayEstimateRTPC.LoadSynchronous(), T60Decay, 0, AkSettings->DecayEstimateName);
  342. }
  343. }
  344. }
  345. void FAkReverbDescriptor::UpdateDampingRTPC() const
  346. {
  347. UAkRoomComponent* room = nullptr;
  348. if (GetRTPCRoom(room))
  349. {
  350. const UAkSettings* AkSettings = GetDefault<UAkSettings>();
  351. if (AkSettings != nullptr && AkSettings->DampingRTPCInUse())
  352. {
  353. room->SetRTPCValue(AkSettings->HFDampingRTPC.LoadSynchronous(), HFDamping, 0, *AkSettings->HFDampingName);
  354. }
  355. }
  356. }
  357. void FAkReverbDescriptor::UpdatePredelaytRTPC() const
  358. {
  359. UAkRoomComponent* room = nullptr;
  360. if (GetRTPCRoom(room))
  361. {
  362. const UAkSettings* AkSettings = GetDefault<UAkSettings>();
  363. if (AkSettings != nullptr && AkSettings->PredelayRTPCInUse())
  364. {
  365. room->SetRTPCValue(AkSettings->TimeToFirstReflectionRTPC.LoadSynchronous(), TimeToFirstReflection, 0, *AkSettings->TimeToFirstReflectionName);
  366. }
  367. }
  368. }
  369. void FAkReverbDescriptor::UpdateAllRTPCs(const UAkRoomComponent* room) const
  370. {
  371. AKASSERT(room != nullptr);
  372. if (CanSetRTPCOnRoom(room))
  373. {
  374. const UAkSettings* AkSettings = GetDefault<UAkSettings>();
  375. if (AkSettings != nullptr && AkSettings->ReverbRTPCsInUse())
  376. {
  377. if (AkSettings->DecayRTPCInUse())
  378. {
  379. room->SetRTPCValue(AkSettings->DecayEstimateRTPC.LoadSynchronous(), T60Decay, 0, AkSettings->DecayEstimateName);
  380. }
  381. if (AkSettings->DampingRTPCInUse())
  382. {
  383. room->SetRTPCValue(AkSettings->HFDampingRTPC.LoadSynchronous(), HFDamping, 0, *AkSettings->HFDampingName);
  384. }
  385. if (AkSettings->PredelayRTPCInUse())
  386. {
  387. room->SetRTPCValue(AkSettings->TimeToFirstReflectionRTPC.LoadSynchronous(), TimeToFirstReflection, 0, *AkSettings->TimeToFirstReflectionName);
  388. }
  389. }
  390. }
  391. }