ATLAudioObject.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <ATLAudioObject.h>
  9. #include <AzCore/Casting/numeric_cast.h>
  10. #include <AzCore/Console/ILogger.h>
  11. #include <AzCore/std/chrono/chrono.h>
  12. #include <MathConversion.h>
  13. #include <SoundCVars.h>
  14. #include <ATLUtils.h>
  15. #if !defined(AUDIO_RELEASE)
  16. // Debug Draw
  17. #include <AzCore/std/string/conversions.h>
  18. #include <AzFramework/Entity/EntityDebugDisplayBus.h>
  19. #include <AzFramework/Viewport/ViewportBus.h>
  20. #include <AzFramework/Viewport/ViewportScreen.h>
  21. #include <Atom/RPI.Public/View.h>
  22. #include <Atom/RPI.Public/ViewportContext.h>
  23. #include <Atom/RPI.Public/ViewportContextBus.h>
  24. #include <Atom/RPI.Public/WindowContext.h>
  25. #endif // !AUDIO_RELEASE
  26. namespace Audio
  27. {
  28. ///////////////////////////////////////////////////////////////////////////////////////////////////
  29. void CATLAudioObjectBase::TriggerInstanceStarting(TAudioTriggerInstanceID triggerInstanceId, TAudioControlID audioControlId)
  30. {
  31. SATLTriggerInstanceState& triggerInstState = m_cTriggers.emplace(triggerInstanceId, SATLTriggerInstanceState()).first->second;
  32. triggerInstState.nTriggerID = audioControlId;
  33. triggerInstState.nFlags |= eATS_STARTING;
  34. }
  35. ///////////////////////////////////////////////////////////////////////////////////////////////////
  36. void CATLAudioObjectBase::TriggerInstanceStarted(TAudioTriggerInstanceID triggerInstanceId, void* owner)
  37. {
  38. auto iter = m_cTriggers.find(triggerInstanceId);
  39. if (iter != m_cTriggers.end())
  40. {
  41. SATLTriggerInstanceState& instState = iter->second;
  42. if (instState.numPlayingEvents > 0)
  43. {
  44. instState.nFlags &= ~eATS_STARTING;
  45. instState.pOwner = owner;
  46. if (instState.pOwner)
  47. {
  48. AudioTriggerNotificationBus::QueueEvent(
  49. TriggerNotificationIdType{ instState.pOwner },
  50. &AudioTriggerNotificationBus::Events::ReportTriggerStarted,
  51. iter->second.nTriggerID);
  52. }
  53. }
  54. else
  55. {
  56. TriggerInstanceFinished(iter);
  57. }
  58. }
  59. }
  60. ///////////////////////////////////////////////////////////////////////////////////////////////////
  61. void CATLAudioObjectBase::TriggerInstanceFinished(TObjectTriggerStates::const_iterator iter)
  62. {
  63. if (iter->second.pOwner)
  64. {
  65. AudioTriggerNotificationBus::QueueEvent(
  66. TriggerNotificationIdType{ iter->second.pOwner },
  67. &AudioTriggerNotificationBus::Events::ReportTriggerFinished,
  68. iter->second.nTriggerID);
  69. }
  70. m_cTriggers.erase(iter);
  71. }
  72. ///////////////////////////////////////////////////////////////////////////////////////////////////
  73. void CATLAudioObjectBase::EventStarted(const CATLEvent* const atlEvent)
  74. {
  75. m_cActiveEvents.insert(atlEvent->GetID());
  76. m_cTriggerImpls.emplace(atlEvent->m_nTriggerImplID, SATLTriggerImplState());
  77. auto iter = m_cTriggers.find(atlEvent->m_nTriggerInstanceID);
  78. if (iter != m_cTriggers.end())
  79. {
  80. if (atlEvent->m_audioEventState == eAES_PLAYING)
  81. {
  82. ++(iter->second.numPlayingEvents);
  83. }
  84. IncrementRefCount();
  85. }
  86. }
  87. ///////////////////////////////////////////////////////////////////////////////////////////////////
  88. void CATLAudioObjectBase::EventFinished(const CATLEvent* const atlEvent)
  89. {
  90. m_cActiveEvents.erase(atlEvent->GetID());
  91. auto iter = m_cTriggers.find(atlEvent->m_nTriggerInstanceID);
  92. if (iter != m_cTriggers.end())
  93. {
  94. SATLTriggerInstanceState& instState = iter->second;
  95. AZ_Assert(instState.numPlayingEvents > 0, "EventFinished - Trigger instances being decremented too many times!");
  96. if (--instState.numPlayingEvents == 0
  97. && (instState.nFlags & eATS_STARTING) == 0)
  98. {
  99. TriggerInstanceFinished(iter);
  100. }
  101. DecrementRefCount();
  102. }
  103. }
  104. ///////////////////////////////////////////////////////////////////////////////////////////////////
  105. void CATLAudioObjectBase::SetSwitchState(const TAudioControlID nSwitchID, const TAudioSwitchStateID nStateID)
  106. {
  107. m_cSwitchStates[nSwitchID] = nStateID;
  108. }
  109. ///////////////////////////////////////////////////////////////////////////////////////////////////
  110. void CATLAudioObjectBase::SetRtpc(const TAudioControlID nRtpcID, const float fValue)
  111. {
  112. m_cRtpcs[nRtpcID] = fValue;
  113. }
  114. ///////////////////////////////////////////////////////////////////////////////////////////////////
  115. void CATLAudioObjectBase::SetEnvironmentAmount(const TAudioEnvironmentID nEnvironmentID, const float fAmount)
  116. {
  117. if (fAmount > 0.0f)
  118. {
  119. m_cEnvironments[nEnvironmentID] = fAmount;
  120. }
  121. else
  122. {
  123. m_cEnvironments.erase(nEnvironmentID);
  124. }
  125. }
  126. ///////////////////////////////////////////////////////////////////////////////////////////////////
  127. const TObjectTriggerImplStates& CATLAudioObjectBase::GetTriggerImpls() const
  128. {
  129. return m_cTriggerImpls;
  130. }
  131. ///////////////////////////////////////////////////////////////////////////////////////////////////
  132. const TObjectRtpcMap& CATLAudioObjectBase::GetRtpcs() const
  133. {
  134. return m_cRtpcs;
  135. }
  136. ///////////////////////////////////////////////////////////////////////////////////////////////////
  137. const TObjectEnvironmentMap& CATLAudioObjectBase::GetEnvironments() const
  138. {
  139. return m_cEnvironments;
  140. }
  141. ///////////////////////////////////////////////////////////////////////////////////////////////////
  142. void CATLAudioObjectBase::ClearRtpcs()
  143. {
  144. m_cRtpcs.clear();
  145. }
  146. ///////////////////////////////////////////////////////////////////////////////////////////////////
  147. void CATLAudioObjectBase::ClearEnvironments()
  148. {
  149. m_cEnvironments.clear();
  150. }
  151. ///////////////////////////////////////////////////////////////////////////////////////////////////
  152. bool CATLAudioObjectBase::HasActiveEvents() const
  153. {
  154. for (const auto& triggerInstance : m_cTriggers)
  155. {
  156. if (triggerInstance.second.numPlayingEvents != 0)
  157. {
  158. return true;
  159. }
  160. }
  161. return false;
  162. }
  163. ///////////////////////////////////////////////////////////////////////////////////////////////////
  164. TObjectTriggerInstanceSet CATLAudioObjectBase::GetTriggerInstancesByOwner(void* const owner) const
  165. {
  166. AZ_Assert(owner, "Retrieving a filtered list of trigger instances requires a non-null Owner pointer!");
  167. TObjectTriggerInstanceSet filteredTriggers;
  168. for (auto& triggerInstanceState : m_cTriggers)
  169. {
  170. if (triggerInstanceState.second.pOwner == owner)
  171. {
  172. filteredTriggers.insert(triggerInstanceState.first);
  173. }
  174. }
  175. return filteredTriggers;
  176. }
  177. ///////////////////////////////////////////////////////////////////////////////////////////////////
  178. void CATLAudioObjectBase::Update([[maybe_unused]] const float fUpdateIntervalMS, [[maybe_unused]] const SATLWorldPosition& rListenerPosition)
  179. {
  180. }
  181. ///////////////////////////////////////////////////////////////////////////////////////////////////
  182. void CATLAudioObjectBase::Clear()
  183. {
  184. m_cActiveEvents.clear();
  185. m_cTriggers.clear();
  186. m_cTriggerImpls.clear();
  187. m_cSwitchStates.clear();
  188. m_cRtpcs.clear();
  189. m_cEnvironments.clear();
  190. m_nRefCounter = 0;
  191. }
  192. ///////////////////////////////////////////////////////////////////////////////////////////////////
  193. void CATLAudioObject::Clear()
  194. {
  195. CATLAudioObjectBase::Clear();
  196. m_oPosition = SATLWorldPosition();
  197. m_raycastProcessor.Reset();
  198. }
  199. ///////////////////////////////////////////////////////////////////////////////////////////////////
  200. void CATLAudioObject::Update(const float fUpdateIntervalMS, const SATLWorldPosition& rListenerPosition)
  201. {
  202. CATLAudioObjectBase::Update(fUpdateIntervalMS, rListenerPosition);
  203. if (CanRunRaycasts())
  204. {
  205. m_raycastProcessor.Update(fUpdateIntervalMS);
  206. m_raycastProcessor.Run(rListenerPosition);
  207. }
  208. }
  209. ///////////////////////////////////////////////////////////////////////////////////////////////////
  210. void CATLAudioObject::SetPosition(const SATLWorldPosition& oNewPosition)
  211. {
  212. m_oPosition = oNewPosition;
  213. }
  214. ///////////////////////////////////////////////////////////////////////////////////////////////////
  215. void CATLAudioObject::SetVelocityTracking(const bool bTrackingOn)
  216. {
  217. if (bTrackingOn)
  218. {
  219. m_oPreviousPosition = m_oPosition;
  220. m_nFlags |= eAOF_TRACK_VELOCITY;
  221. }
  222. else
  223. {
  224. m_nFlags &= ~eAOF_TRACK_VELOCITY;
  225. }
  226. }
  227. ///////////////////////////////////////////////////////////////////////////////////////////////////
  228. void CATLAudioObject::UpdateVelocity(float const fUpdateIntervalMS)
  229. {
  230. const AZ::Vector3 cPositionDelta = m_oPosition.GetPositionVec() - m_oPreviousPosition.GetPositionVec();
  231. const float fCurrentVelocity = (1000.0f * cPositionDelta.GetLength()) / fUpdateIntervalMS; // fCurrentVelocity is given in units per second
  232. if (AZ::GetAbs(fCurrentVelocity - m_fPreviousVelocity) > Audio::CVars::s_VelocityTrackingThreshold)
  233. {
  234. m_fPreviousVelocity = fCurrentVelocity;
  235. Audio::ObjectRequest::SetParameterValue setParameter;
  236. setParameter.m_audioObjectId = GetID();
  237. setParameter.m_parameterId = ATLInternalControlIDs::ObjectSpeedRtpcID;
  238. setParameter.m_value = fCurrentVelocity;
  239. AZ::Interface<IAudioSystem>::Get()->PushRequest(AZStd::move(setParameter));
  240. }
  241. m_oPreviousPosition = m_oPosition;
  242. }
  243. ///////////////////////////////////////////////////////////////////////////////////////////////////
  244. void CATLAudioObject::SetRaycastCalcType(const ObstructionType calcType)
  245. {
  246. m_raycastProcessor.SetType(calcType);
  247. switch (calcType)
  248. {
  249. case ObstructionType::Ignore:
  250. AudioRaycastNotificationBus::Handler::BusDisconnect();
  251. break;
  252. case ObstructionType::SingleRay:
  253. [[fallthrough]];
  254. case ObstructionType::MultiRay:
  255. AudioRaycastNotificationBus::Handler::BusConnect(GetID());
  256. break;
  257. default:
  258. break;
  259. }
  260. }
  261. ///////////////////////////////////////////////////////////////////////////////////////////////////
  262. void CATLAudioObject::RunRaycasts(const SATLWorldPosition& listenerPos)
  263. {
  264. m_raycastProcessor.Run(listenerPos);
  265. }
  266. ///////////////////////////////////////////////////////////////////////////////////////////////////
  267. bool CATLAudioObject::CanRunRaycasts() const
  268. {
  269. return Audio::CVars::s_EnableRaycasts // This is the CVar to enable/disable audio raycasts.
  270. && Audio::CVars::s_RaycastMinDistance < Audio::CVars::s_RaycastMaxDistance
  271. && m_raycastProcessor.CanRun();
  272. }
  273. ///////////////////////////////////////////////////////////////////////////////////////////////////
  274. void CATLAudioObject::GetObstOccData(SATLSoundPropagationData& data) const
  275. {
  276. data.fObstruction = m_raycastProcessor.GetObstruction();
  277. data.fOcclusion = m_raycastProcessor.GetOcclusion();
  278. }
  279. ///////////////////////////////////////////////////////////////////////////////////////////////////
  280. void CATLAudioObject::OnAudioRaycastResults(const AudioRaycastResult& result)
  281. {
  282. // Pull in the results to the raycast processor...
  283. AZ_Assert(result.m_audioObjectId != INVALID_AUDIO_OBJECT_ID, "Audio Raycast Results - audio object id is invalid!\n");
  284. AZ_Assert(result.m_rayIndex < s_maxRaysPerObject, "Audio Raycast Results - ray index is out of bounds (index: %zu)!\n", result.m_rayIndex);
  285. AZ_Assert(result.m_result.size() <= s_maxHitResultsPerRaycast, "Audio Raycast Results - too many hits returned (hits: %zu)!\n", result.m_result.size());
  286. RaycastInfo& info = m_raycastProcessor.m_rayInfos[result.m_rayIndex];
  287. if (!info.m_pending)
  288. {
  289. // This may mean that an audio object was recycled (reset) and then reused.
  290. // Need to investigate this further.
  291. return;
  292. }
  293. info.m_pending = false;
  294. info.m_hits.clear();
  295. info.m_numHits = 0;
  296. for (auto& hit : result.m_result)
  297. {
  298. if (hit.m_distance > 0.f)
  299. {
  300. info.m_hits.push_back(hit);
  301. info.m_numHits++;
  302. }
  303. }
  304. info.UpdateContribution();
  305. info.m_cached = true;
  306. info.m_cacheTimerMs = Audio::CVars::s_RaycastCacheTimeMs;
  307. }
  308. ///////////////////////////////////////////////////////////////////////////////////////////////////
  309. void RaycastInfo::UpdateContribution()
  310. {
  311. // This calculates the contribution of a single raycast. This calculation can be updated
  312. // as needed to suit a user's needs. This is provided as a first example.
  313. // Based on the number of hits reported, add values from the sequence: 1/2 + 1/4 + 1/8 + ...
  314. float newContribution = 0.f;
  315. for (AZ::u16 hit = 0; hit < m_numHits; ++hit)
  316. {
  317. newContribution += std::pow(2.f, -aznumeric_cast<float>(hit + 1));
  318. }
  319. m_contribution = newContribution;
  320. }
  321. ///////////////////////////////////////////////////////////////////////////////////////////////////
  322. float RaycastInfo::GetDistanceScaledContribution() const
  323. {
  324. // Max extent is the s_RaycastMaxDistance, and use the distance embedded in the raycast request as a percent (inverse).
  325. // Objects closer to the listener will have greater contribution amounts.
  326. // Objects farther away will contribute less obstruction/occlusion, but distance attenuation will be the larger contributing factor.
  327. const float maxDistance = static_cast<float>(Audio::CVars::s_RaycastMaxDistance);
  328. float clampedDistance = AZ::GetClamp(m_raycastRequest.m_distance, 0.f, maxDistance);
  329. float distanceScale = 1.f - (clampedDistance / maxDistance);
  330. // Scale the contribution amount by (inverse) distance.
  331. return distanceScale * m_contribution;
  332. }
  333. ///////////////////////////////////////////////////////////////////////////////////////////////////
  334. float RaycastInfo::GetNearestHitDistance() const
  335. {
  336. float minDistance = m_raycastRequest.m_distance;
  337. for (const auto& hit : m_hits)
  338. {
  339. minDistance = AZ::GetMin(minDistance, hit.m_distance);
  340. }
  341. return minDistance;
  342. }
  343. // static
  344. bool RaycastProcessor::s_raycastsEnabled = false;
  345. ///////////////////////////////////////////////////////////////////////////////////////////////////
  346. RaycastProcessor::RaycastProcessor(const TAudioObjectID objectId, const SATLWorldPosition& objectPosition)
  347. : m_rayInfos(s_maxRaysPerObject, RaycastInfo())
  348. , m_position(objectPosition)
  349. , m_obstructionValue(Audio::CVars::s_RaycastSmoothFactor, s_epsilon)
  350. , m_occlusionValue(Audio::CVars::s_RaycastSmoothFactor, s_epsilon)
  351. , m_audioObjectId(objectId)
  352. , m_obstOccType(ObstructionType::Ignore)
  353. {
  354. }
  355. ///////////////////////////////////////////////////////////////////////////////////////////////////
  356. void RaycastProcessor::Update(float elapsedMs)
  357. {
  358. if (m_obstOccType == ObstructionType::SingleRay || m_obstOccType == ObstructionType::MultiRay)
  359. {
  360. // First ray is direct-path obstruction value...
  361. m_obstructionValue.SetNewTarget(m_rayInfos[0].GetDistanceScaledContribution());
  362. if (m_obstOccType == ObstructionType::MultiRay)
  363. {
  364. float occlusion = 0.f;
  365. for (size_t i = 1; i < s_maxRaysPerObject; ++i)
  366. {
  367. occlusion += m_rayInfos[i].GetDistanceScaledContribution();
  368. }
  369. // Average of the occlusion rays' contributions...
  370. occlusion /= aznumeric_cast<float>(s_maxRaysPerObject - 1);
  371. m_occlusionValue.SetNewTarget(occlusion);
  372. }
  373. // Tick down the cache timers, when expired mark them dirty...
  374. for (auto& rayInfo : m_rayInfos)
  375. {
  376. if (rayInfo.m_cached)
  377. {
  378. rayInfo.m_cacheTimerMs -= elapsedMs;
  379. rayInfo.m_cached = (rayInfo.m_cacheTimerMs > 0.f);
  380. }
  381. }
  382. }
  383. m_obstructionValue.Update(Audio::CVars::s_RaycastSmoothFactor);
  384. m_occlusionValue.Update(Audio::CVars::s_RaycastSmoothFactor);
  385. }
  386. ///////////////////////////////////////////////////////////////////////////////////////////////////
  387. void RaycastProcessor::Reset()
  388. {
  389. m_obstructionValue.Reset();
  390. m_occlusionValue.Reset();
  391. for (auto& rayInfo : m_rayInfos)
  392. {
  393. rayInfo.Reset();
  394. }
  395. }
  396. ///////////////////////////////////////////////////////////////////////////////////////////////////
  397. void RaycastProcessor::SetType(ObstructionType calcType)
  398. {
  399. if (calcType == m_obstOccType)
  400. {
  401. // No change to type, no need to reset any data.
  402. return;
  403. }
  404. if (calcType == ObstructionType::Ignore)
  405. {
  406. // Reset the target values when turning off raycasts (set to IGNORE).
  407. m_obstructionValue.Reset();
  408. m_occlusionValue.Reset();
  409. }
  410. // Otherwise, switching to a new type we can allow the obst/occ values from before to smooth
  411. // to new targets as they become available. Hence no reset.
  412. for (auto& rayInfo : m_rayInfos)
  413. {
  414. rayInfo.Reset();
  415. }
  416. m_obstOccType = calcType;
  417. }
  418. ///////////////////////////////////////////////////////////////////////////////////////////////////
  419. bool RaycastProcessor::CanRun() const
  420. {
  421. return s_raycastsEnabled // This enable/disable is set via ISystem events.
  422. && m_obstOccType != ObstructionType::Ignore;
  423. }
  424. ///////////////////////////////////////////////////////////////////////////////////////////////////
  425. void RaycastProcessor::Run(const SATLWorldPosition& listenerPosition)
  426. {
  427. const AZ::Vector3 listener = listenerPosition.GetPositionVec();
  428. const AZ::Vector3 source = m_position.GetPositionVec();
  429. const AZ::Vector3 ray = source - listener;
  430. const float distance = ray.GetLength();
  431. // Prevent raycast when individual sources are not within the allowed distance range...
  432. if (Audio::CVars::s_RaycastMinDistance >= distance || distance >= Audio::CVars::s_RaycastMaxDistance)
  433. {
  434. Reset();
  435. return;
  436. }
  437. const AZ::Vector3 up = AZ::Vector3::CreateAxisZ();
  438. const AZ::Vector3 side = ray.GetNormalized().Cross(up);
  439. // Spread out the side rays based on the percentage the ray distance is of the maximum distance.
  440. // The begin of the rays spread by [0.f, 1.f] in the side direction.
  441. // The end of the rays spread by [1.f, 10.f] in the side direction.
  442. constexpr float spreadDistanceMinExtent = 1.f;
  443. constexpr float spreadDistanceMaxExtent = 10.f;
  444. constexpr float spreadDistanceDelta = spreadDistanceMaxExtent - spreadDistanceMinExtent;
  445. const float rayDistancePercent = (distance / Audio::CVars::s_RaycastMaxDistance);
  446. const float spreadDist = spreadDistanceMinExtent + rayDistancePercent * spreadDistanceDelta;
  447. // Cast ray 0, the direct obstruction ray.
  448. CastRay(listener, source, 0);
  449. if (m_obstOccType == ObstructionType::MultiRay)
  450. {
  451. // Cast ray 1, an indirect occlusion ray.
  452. CastRay(listener, source + up, 1);
  453. // Cast ray 2, an indirect occlusion ray.
  454. CastRay(listener, source - up, 2);
  455. // Cast ray 3, an indirect occlusion ray.
  456. CastRay(listener + side * rayDistancePercent, source + side * spreadDist, 3);
  457. // Cast ray 4, an indirect occlusion ray.
  458. CastRay(listener - side * rayDistancePercent, source - side * spreadDist, 4);
  459. }
  460. }
  461. ///////////////////////////////////////////////////////////////////////////////////////////////////
  462. void RaycastProcessor::CastRay(const AZ::Vector3& origin, const AZ::Vector3& dest, const AZ::u16 rayIndex)
  463. {
  464. AZ_Assert(rayIndex < s_maxRaysPerObject, "RaycastProcessor::CastRay - ray index is out of bounds!\n");
  465. RaycastInfo& rayInfo = m_rayInfos[rayIndex];
  466. if (rayInfo.m_pending || rayInfo.m_cached)
  467. {
  468. // A raycast is already in flight OR
  469. // A raycast result was received recently and is still considered valid.
  470. return;
  471. }
  472. rayInfo.m_raycastRequest.m_start = origin;
  473. rayInfo.m_raycastRequest.m_direction = dest - origin;
  474. rayInfo.m_raycastRequest.m_distance = rayInfo.m_raycastRequest.m_direction.NormalizeSafeWithLength();
  475. rayInfo.m_raycastRequest.m_maxResults = s_maxHitResultsPerRaycast;
  476. rayInfo.m_raycastRequest.m_reportMultipleHits = true;
  477. // Mark as pending
  478. rayInfo.m_pending = true;
  479. AudioRaycastRequest request(rayInfo.m_raycastRequest, m_audioObjectId, rayIndex);
  480. AudioRaycastRequestBus::Broadcast(&AudioRaycastRequestBus::Events::PushAudioRaycastRequest, request);
  481. }
  482. void RaycastProcessor::SetupTestRay(AZ::u16 rayIndex)
  483. {
  484. if (rayIndex < s_maxRaysPerObject)
  485. {
  486. // Set the pending flag to true, so the results aren't discarded.
  487. m_rayInfos[rayIndex].m_pending = true;
  488. // Set the distance in the request structure so it doesn't have the default.
  489. m_rayInfos[rayIndex].m_raycastRequest.m_distance = (Audio::CVars::s_RaycastMaxDistance / 4.f);
  490. }
  491. }
  492. #if !defined(AUDIO_RELEASE)
  493. ///////////////////////////////////////////////////////////////////////////////////////////////////
  494. void CATLAudioObjectBase::CheckBeforeRemoval(const CATLDebugNameStore* const pDebugNameStore)
  495. {
  496. // warn if there is activity on an object being cleared
  497. if (!m_cActiveEvents.empty())
  498. {
  499. const char* const sEventString = GetEventIDs("; ").c_str();
  500. AZLOG_NOTICE(
  501. "Events are active on an object (ID: %llu) being released! #Events: %zu EventIDs: %s", GetID(), m_cActiveEvents.size(),
  502. sEventString);
  503. }
  504. if (!m_cTriggers.empty())
  505. {
  506. const char* const sTriggerString = GetTriggerNames("; ", pDebugNameStore).c_str();
  507. AZLOG_NOTICE(
  508. "Triggers are active on an object (ID: %llu) being released! #Triggers: %zu TriggerNames: %s", GetID(),
  509. m_cTriggers.size(), sTriggerString);
  510. }
  511. }
  512. using TTriggerCountMap = ATLMapLookupType<TAudioControlID, size_t>;
  513. ///////////////////////////////////////////////////////////////////////////////////////////////////
  514. AZStd::string CATLAudioObjectBase::GetTriggerNames(const char* const sSeparator, const CATLDebugNameStore* const pDebugNameStore)
  515. {
  516. AZStd::string triggersString;
  517. TTriggerCountMap cTriggerCounts;
  518. for (auto& trigger : m_cTriggers)
  519. {
  520. ++cTriggerCounts[trigger.second.nTriggerID];
  521. }
  522. for (auto& triggerCount : cTriggerCounts)
  523. {
  524. const char* const pName = pDebugNameStore->LookupAudioTriggerName(triggerCount.first);
  525. if (pName)
  526. {
  527. const size_t nInstances = triggerCount.second;
  528. if (nInstances == 1)
  529. {
  530. triggersString = AZStd::string::format("%s%s%s", triggersString.c_str(), pName, sSeparator);
  531. }
  532. else
  533. {
  534. triggersString = AZStd::string::format("%s%s(%zu inst.)%s", triggersString.c_str(), pName, nInstances, sSeparator);
  535. }
  536. }
  537. }
  538. return triggersString;
  539. }
  540. ///////////////////////////////////////////////////////////////////////////////////////////////////
  541. AZStd::string CATLAudioObjectBase::GetEventIDs(const char* const sSeparator)
  542. {
  543. AZStd::string eventsString;
  544. for (auto activeEvent : m_cActiveEvents)
  545. {
  546. eventsString = AZStd::string::format("%s%llu%s", eventsString.c_str(), activeEvent, sSeparator);
  547. }
  548. return eventsString;
  549. }
  550. ///////////////////////////////////////////////////////////////////////////////////////////////////
  551. const float CATLAudioObjectBase::CStateDebugDrawData::fMinAlpha = 0.5f;
  552. const float CATLAudioObjectBase::CStateDebugDrawData::fMaxAlpha = 1.0f;
  553. const int CATLAudioObjectBase::CStateDebugDrawData::nMaxToMinUpdates = 100;
  554. ///////////////////////////////////////////////////////////////////////////////////////////////////
  555. CATLAudioObjectBase::CStateDebugDrawData::CStateDebugDrawData(const TAudioSwitchStateID nState)
  556. : nCurrentState(nState)
  557. , fCurrentAlpha(fMaxAlpha)
  558. {
  559. }
  560. ///////////////////////////////////////////////////////////////////////////////////////////////////
  561. CATLAudioObjectBase::CStateDebugDrawData::~CStateDebugDrawData()
  562. {
  563. }
  564. ///////////////////////////////////////////////////////////////////////////////////////////////////
  565. void CATLAudioObjectBase::CStateDebugDrawData::Update(const TAudioSwitchStateID nNewState)
  566. {
  567. if ((nNewState == nCurrentState) && (fCurrentAlpha > fMinAlpha))
  568. {
  569. fCurrentAlpha -= (fMaxAlpha - fMinAlpha) / nMaxToMinUpdates;
  570. }
  571. else if (nNewState != nCurrentState)
  572. {
  573. nCurrentState = nNewState;
  574. fCurrentAlpha = fMaxAlpha;
  575. }
  576. }
  577. ///////////////////////////////////////////////////////////////////////////////////////////////////
  578. bool ConvertOjbectWorldPosToScreenCoords(AZ::Vector3& position)
  579. {
  580. auto viewportContextMgr = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
  581. if (!viewportContextMgr)
  582. {
  583. return false;
  584. }
  585. AZ::RPI::ViewportContextPtr viewportContext = viewportContextMgr->GetDefaultViewportContext();
  586. if (!viewportContext)
  587. {
  588. return false;
  589. }
  590. AZ::RPI::ViewPtr view = viewportContext->GetDefaultView();
  591. if (!view)
  592. {
  593. return false;
  594. }
  595. const AZ::RHI::Viewport& viewport = viewportContext->GetWindowContext()->GetViewport();
  596. position = AzFramework::WorldToScreenNdc(position, view->GetWorldToViewMatrixAsMatrix3x4(), view->GetViewToClipMatrix());
  597. position.SetX(position.GetX() * viewport.GetWidth());
  598. position.SetY((1.f - position.GetY()) * viewport.GetHeight());
  599. return true;
  600. }
  601. ///////////////////////////////////////////////////////////////////////////////////////////////////
  602. void CATLAudioObject::DrawDebugInfo(
  603. AzFramework::DebugDisplayRequests& debugDisplay,
  604. [[maybe_unused]] const AZ::Vector3& vListenerPos,
  605. [[maybe_unused]] const CATLDebugNameStore* const pDebugNameStore) const
  606. {
  607. if (!m_cTriggers.empty())
  608. {
  609. // Inspect triggers and apply filter (if set)...
  610. TTriggerCountMap triggerCounts;
  611. auto triggerFilter = static_cast<AZ::CVarFixedString>(CVars::s_AudioTriggersDebugFilter);
  612. AZStd::to_lower(triggerFilter.begin(), triggerFilter.end());
  613. for (auto& trigger : m_cTriggers)
  614. {
  615. AZStd::string triggerName(pDebugNameStore->LookupAudioTriggerName(trigger.second.nTriggerID));
  616. AZStd::to_lower(triggerName.begin(), triggerName.end());
  617. if (AudioDebugDrawFilter(triggerName, triggerFilter))
  618. {
  619. ++triggerCounts[trigger.second.nTriggerID];
  620. }
  621. }
  622. // Early out for this object if all trigger names were filtered out.
  623. if (triggerCounts.empty())
  624. {
  625. return;
  626. }
  627. const AZ::Vector3 pos3d(m_oPosition.GetPositionVec());
  628. AZ::Vector3 screenPos{ pos3d };
  629. if (!ConvertOjbectWorldPosToScreenCoords(screenPos))
  630. {
  631. return;
  632. }
  633. if (screenPos.GetZ() < 0.5f)
  634. {
  635. return;
  636. }
  637. if (CVars::s_debugDrawOptions.AreAllFlagsActive(static_cast<AZ::u32>(DebugDraw::Options::DrawObjects)))
  638. {
  639. const float radius = 0.05f;
  640. const AZ::Color sphereColor{ 1.f, 0.1f, 0.1f, 1.f };
  641. debugDisplay.SetColor(sphereColor);
  642. debugDisplay.DrawWireSphere(pos3d, radius);
  643. }
  644. AZStd::string str;
  645. const AZ::Color brightColor{ 0.9f, 0.9f, 0.9f, 1.f };
  646. const AZ::Color normalColor(0.75f, 0.75f, 0.75f, 1.f);
  647. const AZ::Color dimmedColor(0.5f, 0.5f, 0.5f, 1.f);
  648. const float distance = pos3d.GetDistance(vListenerPos);
  649. const float fontSize = 0.75f;
  650. const float lineHeight = 15.f;
  651. float posX = screenPos.GetX();
  652. float posY = screenPos.GetY();
  653. if (CVars::s_debugDrawOptions.AreAllFlagsActive(static_cast<AZ::u32>(DebugDraw::Options::ObjectLabels)))
  654. {
  655. SATLSoundPropagationData obstOccData;
  656. GetObstOccData(obstOccData);
  657. str = AZStd::string::format(
  658. "%s ID: %llu RefCnt: %2zu Dist: %4.1f m", pDebugNameStore->LookupAudioObjectName(GetID()), GetID(), GetRefCount(),
  659. distance);
  660. debugDisplay.SetColor(brightColor);
  661. debugDisplay.Draw2dTextLabel(posX, posY, fontSize, str.c_str());
  662. posY += lineHeight;
  663. str = AZStd::string::format(" Obst: %.3f Occl: %.3f", obstOccData.fObstruction, obstOccData.fOcclusion);
  664. debugDisplay.SetColor(normalColor);
  665. debugDisplay.Draw2dTextLabel(posX, posY, fontSize, str.c_str());
  666. }
  667. if (CVars::s_debugDrawOptions.AreAllFlagsActive(static_cast<AZ::u32>(DebugDraw::Options::ObjectTriggers)))
  668. {
  669. posY += lineHeight;
  670. debugDisplay.SetColor(brightColor);
  671. debugDisplay.Draw2dTextLabel(posX, posY, fontSize, "Triggers:");
  672. debugDisplay.SetColor(normalColor);
  673. for (auto& triggerCount : triggerCounts)
  674. {
  675. auto triggerName = pDebugNameStore->LookupAudioTriggerName(triggerCount.first);
  676. if (triggerName)
  677. {
  678. posY += lineHeight;
  679. str = AZStd::string::format(" %s (count = %zu)", triggerName, triggerCount.second);
  680. debugDisplay.Draw2dTextLabel(posX, posY, fontSize, str.c_str());
  681. }
  682. }
  683. }
  684. if (CVars::s_debugDrawOptions.AreAllFlagsActive(static_cast<AZ::u32>(DebugDraw::Options::ObjectStates)))
  685. {
  686. posY += lineHeight;
  687. debugDisplay.SetColor(brightColor);
  688. debugDisplay.Draw2dTextLabel(posX, posY, fontSize, "Switches:");
  689. for (auto& switchState : m_cSwitchStates)
  690. {
  691. const auto switchId = switchState.first;
  692. const auto stateId = switchState.second;
  693. auto switchName = pDebugNameStore->LookupAudioSwitchName(switchId);
  694. auto stateName = pDebugNameStore->LookupAudioSwitchStateName(switchId, stateId);
  695. if (switchName && stateName)
  696. {
  697. CStateDebugDrawData& stateDrawData = m_cStateDrawInfoMap[switchId];
  698. stateDrawData.Update(stateId);
  699. AZ::Color switchColor{ 0.8f, 0.8f, 0.8f, stateDrawData.fCurrentAlpha };
  700. posY += lineHeight;
  701. str = AZStd::string::format(" %s : %s", switchName, stateName);
  702. debugDisplay.SetColor(switchColor);
  703. debugDisplay.Draw2dTextLabel(posX, posY, fontSize, str.c_str());
  704. }
  705. }
  706. }
  707. if (CVars::s_debugDrawOptions.AreAllFlagsActive(static_cast<AZ::u32>(DebugDraw::Options::ObjectRtpcs)))
  708. {
  709. posY += lineHeight;
  710. debugDisplay.SetColor(brightColor);
  711. debugDisplay.Draw2dTextLabel(posX, posY, fontSize, "Parameters:");
  712. debugDisplay.SetColor(normalColor);
  713. for (auto& param : m_cRtpcs)
  714. {
  715. const float value = param.second;
  716. auto paramName = pDebugNameStore->LookupAudioRtpcName(param.first);
  717. if (paramName)
  718. {
  719. posY += lineHeight;
  720. str = AZStd::string::format(" %s = %4.2f", paramName, value);
  721. debugDisplay.Draw2dTextLabel(posX, posY, fontSize, str.c_str());
  722. }
  723. }
  724. }
  725. if (CVars::s_debugDrawOptions.AreAllFlagsActive(static_cast<AZ::u32>(DebugDraw::Options::ObjectEnvironments)))
  726. {
  727. posY += lineHeight;
  728. debugDisplay.SetColor(brightColor);
  729. debugDisplay.Draw2dTextLabel(posX, posY, fontSize, "Environments:");
  730. debugDisplay.SetColor(normalColor);
  731. for (auto& environment : m_cEnvironments)
  732. {
  733. const float value = environment.second;
  734. auto envName = pDebugNameStore->LookupAudioEnvironmentName(environment.first);
  735. if (envName)
  736. {
  737. posY += lineHeight;
  738. str = AZStd::string::format(" %s = %.3f", envName, value);
  739. debugDisplay.Draw2dTextLabel(posX, posY, fontSize, str.c_str());
  740. }
  741. }
  742. }
  743. }
  744. }
  745. ///////////////////////////////////////////////////////////////////////////////////////////////////
  746. void RaycastProcessor::DrawObstructionRays(AzFramework::DebugDisplayRequests& debugDisplay) const
  747. {
  748. static const AZ::Color obstructedRayColor(0.8f, 0.08f, 0.f, 1.f);
  749. static const AZ::Color freeRayColor(0.08f, 0.8f, 0.f, 1.f);
  750. static const AZ::Color hitSphereColor(1.f, 0.27f, 0.f, 0.8f);
  751. static const AZ::Color obstructedRayLabelColor(1.f, 0.f, 0.02f, 0.9f);
  752. static const AZ::Color freeRayLabelColor(0.f, 1.f, 0.02f, 0.9f);
  753. static const float hitSphereRadius = 0.02f;
  754. if (!CanRun())
  755. {
  756. return;
  757. }
  758. AZStd::string str;
  759. const float textSize = 0.7f;
  760. const bool drawRays = CVars::s_debugDrawOptions.AreAllFlagsActive(static_cast<AZ::u32>(DebugDraw::Options::DrawRays));
  761. const bool drawLabels = CVars::s_debugDrawOptions.AreAllFlagsActive(static_cast<AZ::u32>(DebugDraw::Options::RayLabels));
  762. size_t numRays = m_obstOccType == ObstructionType::SingleRay ? 1 : s_maxRaysPerObject;
  763. for (size_t rayIndex = 0; rayIndex < numRays; ++rayIndex)
  764. {
  765. const RaycastInfo& rayInfo = m_rayInfos[rayIndex];
  766. const AZ::Vector3 rayEnd = rayInfo.m_raycastRequest.m_start + rayInfo.m_raycastRequest.m_direction * rayInfo.GetNearestHitDistance();
  767. if (drawRays)
  768. {
  769. const bool rayObstructed = (rayInfo.m_numHits > 0);
  770. const AZ::Color& rayColor = (rayObstructed ? obstructedRayColor : freeRayColor);
  771. if (rayObstructed)
  772. {
  773. debugDisplay.SetColor(hitSphereColor);
  774. debugDisplay.DrawWireSphere(rayEnd, hitSphereRadius);
  775. }
  776. debugDisplay.DrawLine(rayInfo.m_raycastRequest.m_start, rayEnd, freeRayColor.GetAsVector4(), rayColor.GetAsVector4());
  777. }
  778. if (drawLabels)
  779. {
  780. AZ::Vector3 screenPos{ rayEnd };
  781. if (ConvertOjbectWorldPosToScreenCoords(screenPos) && screenPos.GetZ() >= 0.5f)
  782. {
  783. float lerpValue = rayInfo.m_contribution;
  784. AZ::Color labelColor = freeRayLabelColor.Lerp(obstructedRayLabelColor, lerpValue);
  785. str = AZStd::string::format((rayIndex == 0) ? "Obst: %.2f" : "Occl: %.2f", rayInfo.GetDistanceScaledContribution());
  786. debugDisplay.SetColor(labelColor);
  787. debugDisplay.Draw2dTextLabel(screenPos.GetX(), screenPos.GetY() - 12.0f, textSize, str.c_str());
  788. }
  789. }
  790. }
  791. }
  792. #endif // !AUDIO_RELEASE
  793. } // namespace Audio