SoundScene.cpp 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : WWAudio *
  23. * *
  24. * $Archive:: /Commando/Code/WWAudio/SoundScene.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 8/14/01 2:55p $*
  29. * *
  30. * $Revision:: 32 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "soundscene.h"
  36. #include "soundcullobj.h"
  37. #include "logicalsound.h"
  38. #include "logicallistener.h"
  39. #include "chunkio.h"
  40. #include "persistfactory.h"
  41. #include "wwprofile.h"
  42. #include "threads.h"
  43. #include "wwmemlog.h"
  44. DEFINE_AUTO_POOL(SoundSceneClass::AudibleInfoClass, 64);
  45. //////////////////////////////////////////////////////////////////////////////////
  46. // Generic constants
  47. //////////////////////////////////////////////////////////////////////////////////
  48. const int MAX_LOGICAL_LISTENER_UPDATES_PER_FRAME = 4;
  49. //////////////////////////////////////////////////////////////////////////////////
  50. // Save/Load constants
  51. //////////////////////////////////////////////////////////////////////////////////
  52. enum
  53. {
  54. CHUNKID_VARIABLES = 0x00000100,
  55. CHUNKID_STATIC_SOUNDS,
  56. CHUNKID_DYNAMIC_SOUNDS
  57. };
  58. enum
  59. {
  60. VARID_MIN_DIM = 0x01,
  61. VARID_MAX_DIM,
  62. };
  63. ////////////////////////////////////////////////////////////////////////////////////////////////
  64. //
  65. // SoundSceneClass
  66. //
  67. ////////////////////////////////////////////////////////////////////////////////////////////////
  68. SoundSceneClass::SoundSceneClass (void)
  69. : m_Listener (NULL),
  70. m_2ndListener (NULL),
  71. m_MinExtents (-500, -500, -500),
  72. m_MaxExtents (500, 500, 500),
  73. m_IsBatchMode (false)
  74. {
  75. WWMEMLOG(MEM_SOUND);
  76. m_Listener = W3DNEW Listener3DClass;
  77. m_DynamicCullingSystem.Re_Partition (m_MinExtents, m_MaxExtents, 100.00F);
  78. m_LogicalCullingSystem.Re_Partition (m_MinExtents, m_MaxExtents, 100.00F);
  79. m_ListenerCullingSystem.Re_Partition (m_MinExtents, m_MaxExtents, 40.00F);
  80. m_StaticCullingSystem.Re_Partition ();
  81. return ;
  82. }
  83. ////////////////////////////////////////////////////////////////////////////////////////////////
  84. //
  85. // ~SoundSceneClass
  86. //
  87. ////////////////////////////////////////////////////////////////////////////////////////////////
  88. SoundSceneClass::~SoundSceneClass (void)
  89. {
  90. REF_PTR_RELEASE (m_Listener);
  91. REF_PTR_RELEASE (m_2ndListener);
  92. return ;
  93. }
  94. ////////////////////////////////////////////////////////////////////////////////////////////////
  95. //
  96. // Re_Partition
  97. //
  98. ////////////////////////////////////////////////////////////////////////////////////////////////
  99. void
  100. SoundSceneClass::Re_Partition
  101. (
  102. const Vector3 &min_dimension,
  103. const Vector3 &max_dimension
  104. )
  105. {
  106. m_DynamicCullingSystem.Re_Partition (min_dimension, max_dimension, 100.00F);
  107. m_LogicalCullingSystem.Re_Partition (min_dimension, max_dimension, 100.00F);
  108. m_ListenerCullingSystem.Re_Partition (min_dimension, max_dimension, 40.00F);
  109. m_StaticCullingSystem.Re_Partition ();
  110. m_MinExtents = min_dimension;
  111. m_MaxExtents = max_dimension;
  112. return ;
  113. }
  114. ////////////////////////////////////////////////////////////////////////////////////////////////
  115. //
  116. // Collect_Logical_Sounds
  117. //
  118. ////////////////////////////////////////////////////////////////////////////////////////////////
  119. void
  120. SoundSceneClass::Collect_Logical_Sounds (int listener_count)
  121. {
  122. WWPROFILE ("Collect_Logical_Sounds");
  123. uint32 timestamp = ::GetTickCount ();
  124. //
  125. // Determine how many listeners to process
  126. //
  127. int count = listener_count;
  128. int max = MAX_LOGICAL_LISTENER_UPDATES_PER_FRAME;
  129. if ((count < 0) || (count > max)) {
  130. count = max;
  131. }
  132. PriorityMultiListIterator<LogicalListenerClass> priority_queue (&m_LogicalListeners);
  133. LogicalListenerClass *listener = NULL;
  134. //
  135. // Loop over as many of the listeners as we want to process this
  136. // frame.
  137. //
  138. for ( int total = 0;
  139. total < count && priority_queue.Process_Head (&listener);
  140. total ++)
  141. {
  142. LogicalListenerClass::Set_Oldest_Timestamp (listener->Get_Timestamp ());
  143. listener->Set_Timestamp (LogicalListenerClass::Get_New_Timestamp ());
  144. listener->On_Frame_Update ();
  145. //
  146. // Collect a list of the sounds this listener can hear.
  147. //
  148. Vector3 position = listener->Get_Position ();
  149. m_LogicalCullingSystem.Reset_Collection ();
  150. m_LogicalCullingSystem.Collect_Objects (position);
  151. //
  152. // Now loop through the list of sounds this listener can hear
  153. // and notify their callback.
  154. //
  155. SoundCullObjClass * cull_obj;
  156. for ( cull_obj = m_LogicalCullingSystem.Get_First_Collected_Object();
  157. cull_obj != NULL;
  158. cull_obj = m_LogicalCullingSystem.Get_Next_Collected_Object (cull_obj))
  159. {
  160. //
  161. // Get a pointer to the current 'cull-sound' object.
  162. //
  163. LogicalSoundClass *sound_obj = (LogicalSoundClass *)cull_obj->Peek_Sound_Obj ();
  164. //
  165. // Test this sound against the scale associated with the current listener to
  166. // see if the listener can really "hear" the sound.
  167. //
  168. const Vector3 &sound_pos = cull_obj->Get_Bounding_Box ().Center;
  169. Vector3 listener_pos = listener->Get_Position ();
  170. float dropoff_radius = sound_obj->Get_DropOff_Radius ();
  171. float scale = listener->Get_Effective_Scale ();
  172. float test_radius2 = (dropoff_radius * scale) * (dropoff_radius * scale);
  173. if ((listener_pos - sound_pos).Length2 () <= test_radius2) {
  174. //
  175. // Is the sound ready to notify?
  176. //
  177. if (sound_obj->Allow_Notify (timestamp)) {
  178. listener->On_Event (AudioCallbackClass::EVENT_LOGICAL_HEARD, (uint32)listener, (uint32)sound_obj);
  179. }
  180. }
  181. }
  182. }
  183. //
  184. // Loop through and remove any single shot sounds that have
  185. // been completely processed
  186. //
  187. MultiListIterator<LogicalSoundClass> single_shot_it (&m_SingleShotLogicalSounds);
  188. for (single_shot_it.First (); !single_shot_it.Is_Done (); single_shot_it.Next ()) {
  189. LogicalSoundClass *sound_obj = single_shot_it.Peek_Obj ();
  190. //
  191. // Remove this sound if its been completely processed
  192. //
  193. if (sound_obj->Get_Listener_Timestamp () <= LogicalListenerClass::Get_Oldest_Timestamp ()) {
  194. sound_obj->Remove_From_Scene ();
  195. single_shot_it.Remove_Current_Object ();
  196. single_shot_it.Prev ();
  197. }
  198. }
  199. return ;
  200. }
  201. ////////////////////////////////////////////////////////////////////////////////////////////////
  202. //
  203. // Collect_Audible_Sounds
  204. //
  205. ////////////////////////////////////////////////////////////////////////////////////////////////
  206. void
  207. SoundSceneClass::Collect_Audible_Sounds
  208. (
  209. Listener3DClass * listener,
  210. COLLECTED_SOUNDS &list
  211. )
  212. {
  213. WWPROFILE ("Collect_Audible_Sounds");
  214. //
  215. // Collect a list of the audible dynamic sounds
  216. //
  217. Vector3 listener_pos = listener->Get_Position ();
  218. m_DynamicCullingSystem.Reset_Collection ();
  219. m_DynamicCullingSystem.Collect_Objects (listener_pos);
  220. //
  221. // Collect a list of the audible static sounds
  222. //
  223. m_StaticCullingSystem.Reset_Collection ();
  224. m_StaticCullingSystem.Collect_Objects (listener_pos);
  225. //
  226. // Loop through all the dynamic sounds that are currently audible and make sure
  227. // they are 'really' audible. The culling systems just check bounding boxes
  228. // but we need to be able to check attenuation spheres.
  229. //
  230. SoundCullObjClass * cull_obj = NULL;
  231. for ( cull_obj = m_DynamicCullingSystem.Get_First_Collected_Object();
  232. cull_obj != NULL;
  233. cull_obj = m_DynamicCullingSystem.Get_Next_Collected_Object(cull_obj))
  234. {
  235. // Get a pointer to the current 'cull-sound' object
  236. AudibleSoundClass *sound_obj = (AudibleSoundClass *)cull_obj->Peek_Sound_Obj ();
  237. // Perform a quick sphere-cull check to make sure this
  238. // sound should really be audible
  239. Vector3 pos = sound_obj->Get_Position ();
  240. float radius = sound_obj->Get_DropOff_Radius ();
  241. float radius2 = radius * radius;
  242. float length2 = (pos - listener_pos).Length2 ();
  243. if (length2 <= radius2) {
  244. AudibleInfoClass *audible_info = W3DNEW AudibleInfoClass (sound_obj, length2);
  245. list.Add (audible_info);
  246. //
  247. // Update this sound's runtime priority based on its distance
  248. // from the sound emitter.
  249. //
  250. float length = (pos - listener_pos).Quick_Length ();
  251. float priority = (length > 0) ? 1 - (length / radius) : 1.0F;
  252. sound_obj->Set_Runtime_Priority (priority);
  253. }
  254. }
  255. //
  256. // Loop through all the static sounds that are currently audible and make sure
  257. // they are 'really' audible. The culling systems just check bounding boxes
  258. // but we need to be able to check attenuation spheres.
  259. //
  260. for ( cull_obj = m_StaticCullingSystem.Get_First_Collected_Object();
  261. cull_obj != NULL;
  262. cull_obj = m_StaticCullingSystem.Get_Next_Collected_Object(cull_obj))
  263. {
  264. AudibleSoundClass *sound_obj = (AudibleSoundClass *)cull_obj->Peek_Sound_Obj ();
  265. // Perform a quick sphere-cull check to make sure this
  266. // sound should really be audible
  267. Vector3 pos = sound_obj->Get_Position ();
  268. float radius = sound_obj->Get_DropOff_Radius ();
  269. float radius2 = radius * radius;
  270. float length2 = (pos - listener_pos).Length2 ();
  271. if (length2 <= radius2) {
  272. AudibleInfoClass *audible_info = W3DNEW AudibleInfoClass (sound_obj, length2);
  273. list.Add (audible_info);
  274. //
  275. // Update this sound's runtime priority based on its distance
  276. // from the sound emitter.
  277. //
  278. float length = (pos - listener_pos).Quick_Length ();
  279. float priority = (length > 0) ? 1 - (length / radius) : 1.0F;
  280. sound_obj->Set_Runtime_Priority (priority);
  281. }
  282. }
  283. return ;
  284. }
  285. ////////////////////////////////////////////////////////////////////////////////////////////////
  286. //
  287. // On_Frame_Update
  288. //
  289. // Note: This method could be made more efficient by using another data structure besides
  290. // linked lists. However the differece may be negligable due to the low density of 3D sounds
  291. // that are audible at once.
  292. //
  293. ////////////////////////////////////////////////////////////////////////////////////////////////
  294. void
  295. SoundSceneClass::On_Frame_Update (unsigned int milliseconds)
  296. {
  297. WWPROFILE ("On_Frame_Update");
  298. COLLECTED_SOUNDS auxiliary_sounds;
  299. COLLECTED_SOUNDS primary_sounds;
  300. //
  301. // First, collect any auxiliary sounds that are audible
  302. //
  303. if (m_2ndListener != NULL) {
  304. m_2ndListener->On_Frame_Update (milliseconds);
  305. Collect_Audible_Sounds (m_2ndListener, auxiliary_sounds);
  306. }
  307. //
  308. // Update the listener's position/velocity, etc
  309. //
  310. m_Listener->On_Frame_Update (milliseconds);
  311. //
  312. // Collect the primary sounds that are audible
  313. //
  314. Collect_Audible_Sounds (m_Listener, primary_sounds);
  315. //
  316. // Loop through the auxiliary sounds and make sure
  317. // sounds are only played once, either primary or auxiliary.
  318. //
  319. MultiListIterator<AudibleInfoClass> aux_iterator (&auxiliary_sounds);
  320. MultiListIterator<AudibleInfoClass> pri_iterator (&primary_sounds);
  321. AUDIBLE_SOUND_LIST audible_sounds;
  322. for (aux_iterator.First (); !aux_iterator.Is_Done (); aux_iterator.Next ()) {
  323. AudibleInfoClass *aux_info = aux_iterator.Peek_Obj ();
  324. //
  325. // Loop through all the primary sounds and remove any
  326. // that are 'overpowered' by the same sound in the
  327. // other listener.
  328. //
  329. bool found = false;
  330. for (pri_iterator.First (); !pri_iterator.Is_Done () && !found; pri_iterator.Next ()) {
  331. AudibleInfoClass *pri_info = pri_iterator.Peek_Obj ();
  332. //
  333. // Is this sound in both lists?
  334. //
  335. found = (aux_info->sound_obj == pri_info->sound_obj);
  336. if (found) {
  337. if (aux_info->distance2 < pri_info->distance2) {
  338. delete pri_info;
  339. primary_sounds.Remove (pri_info);
  340. } else {
  341. delete aux_info;
  342. auxiliary_sounds.Remove (aux_info);
  343. }
  344. }
  345. }
  346. }
  347. //
  348. // Add the primary audible sounds into the master list
  349. //
  350. for (pri_iterator.First (); !pri_iterator.Is_Done (); pri_iterator.Next ()) {
  351. AudibleInfoClass *pri_info = pri_iterator.Peek_Obj ();
  352. audible_sounds.Add (pri_info->sound_obj);
  353. //
  354. // Let the sound know what it's listener's position is
  355. //
  356. pri_info->sound_obj->Set_Listener_Transform (m_Listener->Get_Transform ());
  357. //
  358. // Free the audible info object
  359. //
  360. pri_iterator.Remove_Current_Object ();
  361. pri_iterator.Prev ();
  362. delete pri_info;
  363. }
  364. //
  365. // Add the auxiliary audible sounds into the master list
  366. //
  367. for (aux_iterator.First (); !aux_iterator.Is_Done (); aux_iterator.Next ()) {
  368. AudibleInfoClass *aux_info = aux_iterator.Peek_Obj ();
  369. audible_sounds.Add (aux_info->sound_obj);
  370. //
  371. // Let the sound know what it's listener's position is
  372. //
  373. aux_info->sound_obj->Set_Listener_Transform (m_2ndListener->Get_Transform ());
  374. //
  375. // Convert the sound to a Pseudo-3D sound that has
  376. // a 'tinny' filter applied to it.
  377. //
  378. /*aux_info.sound_obj->Convert_To_Filtered ();
  379. AudibleSoundClass *tinny_sound = aux_info.sound_obj->As_Converted_Format ();
  380. if (tinny_sound != NULL) {
  381. audible_sounds.Add (tinny_sound);
  382. }*/
  383. //
  384. // Free the audible info object
  385. //
  386. aux_iterator.Remove_Current_Object ();
  387. aux_iterator.Prev ();
  388. delete aux_info;
  389. }
  390. //
  391. // Loop through all the sounds that were audible last frame
  392. // and see if they are still audible this frame.
  393. //
  394. MultiListIterator<AudibleSoundClass> audible_iterator (&m_LastSoundsAudible);
  395. for (audible_iterator.First (); !audible_iterator.Is_Done (); audible_iterator.Next ()) {
  396. AudibleSoundClass *sound_obj = audible_iterator.Peek_Obj ();
  397. //
  398. // Is this sound still audible?
  399. //
  400. if (audible_sounds.Is_In_List (sound_obj)) {
  401. //
  402. // Make sure the sound is playing, then remove it from
  403. // the newly-audible list so we don't process it again
  404. //
  405. sound_obj->Cull_Sound (false);
  406. audible_sounds.Remove (sound_obj);
  407. } else {
  408. //
  409. // If the sound isn't audible any more then remove
  410. // it from the list
  411. //
  412. audible_iterator.Remove_Current_Object ();
  413. audible_iterator.Prev ();
  414. //
  415. // Make sure we cull the sound
  416. //
  417. WWASSERT(sound_obj != NULL);
  418. sound_obj->Cull_Sound (true);
  419. sound_obj->Set_Runtime_Priority (0);
  420. }
  421. }
  422. //
  423. // Loop through all the newly-audible sounds and
  424. // make sure they are playing.
  425. //
  426. MultiListIterator<AudibleSoundClass> newly_audible_it (&audible_sounds);
  427. for (newly_audible_it.First (); !newly_audible_it.Is_Done (); newly_audible_it.Next ()) {
  428. AudibleSoundClass *sound_obj = newly_audible_it.Peek_Obj ();
  429. //
  430. // Make sure the sound has a valid Miles handle (so it can make noise)
  431. //
  432. sound_obj->Cull_Sound (false);
  433. //
  434. // If this sound is still in the scene (it may have 'stopped'
  435. // while it was culled) then start playing it...
  436. //
  437. if (sound_obj->Is_In_Scene ()) {
  438. m_LastSoundsAudible.Add (sound_obj);
  439. }
  440. }
  441. return ;
  442. }
  443. ////////////////////////////////////////////////////////////////////////////////////////////////
  444. //
  445. // Add_Sound
  446. //
  447. ////////////////////////////////////////////////////////////////////////////////////////////////
  448. void
  449. SoundSceneClass::Add_Sound
  450. (
  451. AudibleSoundClass * sound_obj,
  452. bool start_playing
  453. )
  454. {
  455. WWPROFILE ("Add_Sound");
  456. WWMEMLOG(MEM_SOUND);
  457. WWASSERT (sound_obj != NULL);
  458. if (sound_obj != NULL && sound_obj->Is_In_Scene () == false) {
  459. bool cull_sound = true;
  460. // Create a wrapper object for the sound that we can use
  461. // with the different culling systems.
  462. SoundCullObjClass *cullable_sound = W3DNEW SoundCullObjClass;
  463. cullable_sound->Set_Sound_Obj (sound_obj);
  464. sound_obj->Set_Cullable_Wrapper (cullable_sound);
  465. //
  466. // Add this object to the dynamic culling system
  467. //
  468. m_DynamicCullingSystem.Add_Object (cullable_sound);
  469. m_DynamicSounds.Add (cullable_sound);
  470. Update_Sound (cullable_sound);
  471. //
  472. // If the listener can hear this sound, then make sure
  473. // we start it off non-culled
  474. //
  475. if (m_IsBatchMode == false) {
  476. Vector3 listener_pos = m_Listener->Get_Position ();
  477. Vector3 sound_pos = sound_obj->Get_Position ();
  478. float radius = sound_obj->Get_DropOff_Radius ();
  479. float radius2 = radius * radius;
  480. if (((listener_pos - sound_pos).Length2 ()) < radius2) {
  481. cull_sound = false;
  482. m_LastSoundsAudible.Add (sound_obj);
  483. start_playing = true;
  484. sound_obj->Set_Listener_Transform (m_Listener->Get_Transform ());
  485. }
  486. }
  487. //
  488. // Make sure the sound is appropriately culled
  489. //
  490. sound_obj->Cull_Sound (cull_sound);
  491. //
  492. // Start the sound playing if requested
  493. //
  494. if (start_playing) {
  495. sound_obj->Play ();
  496. }
  497. }
  498. return ;
  499. }
  500. ////////////////////////////////////////////////////////////////////////////////////////////////
  501. //
  502. // Remove_Sound
  503. //
  504. // Note: This method should really be rewritten to use a different list type. Linked lists
  505. // are probably too inefficient (especially if we have 100s or 1000s of sounds)
  506. //
  507. ////////////////////////////////////////////////////////////////////////////////////////////////
  508. void
  509. SoundSceneClass::Remove_Sound
  510. (
  511. AudibleSoundClass *sound_obj,
  512. bool stop_playing
  513. )
  514. {
  515. WWPROFILE ("Remove_Sound");
  516. if (sound_obj == NULL) {
  517. return ;
  518. }
  519. //
  520. // Make sure we remove this sound from the list of last audible sounds.
  521. //
  522. if (m_LastSoundsAudible.Is_In_List (sound_obj)) {
  523. m_LastSoundsAudible.Remove (sound_obj);
  524. }
  525. //
  526. // Is this sound really in the scene?
  527. //
  528. SoundCullObjClass *cull_obj = sound_obj->Peek_Cullable_Wrapper ();
  529. if (cull_obj != NULL && m_DynamicSounds.Is_In_List (cull_obj)) {
  530. //
  531. // Stop playing the sound if necessary
  532. //
  533. if (stop_playing) {
  534. sound_obj->Stop ();
  535. }
  536. //
  537. // Flush the sound's cull-wrapper since we are removing it from the scene
  538. //
  539. sound_obj->Set_Cullable_Wrapper (NULL);
  540. //
  541. // Remove this sound from the dynamic culling system
  542. //
  543. m_DynamicCullingSystem.Remove_Object (cull_obj);
  544. m_DynamicSounds.Remove (cull_obj);
  545. //
  546. // Register the sound for deletion at an appropriate time
  547. //
  548. WWAudioThreadsClass::Add_Delayed_Release_Object (cull_obj);
  549. }
  550. return ;
  551. }
  552. ////////////////////////////////////////////////////////////////////////////////////////////////
  553. //
  554. // Add_Static_Sound
  555. //
  556. ////////////////////////////////////////////////////////////////////////////////////////////////
  557. void
  558. SoundSceneClass::Add_Static_Sound
  559. (
  560. AudibleSoundClass * sound_obj,
  561. bool start_playing
  562. )
  563. {
  564. WWPROFILE ("Add_Static_Sound");
  565. WWASSERT (sound_obj != NULL);
  566. if (sound_obj != NULL) {
  567. //
  568. // Check to see if this sound is already in the scene
  569. //
  570. SoundCullObjClass *cull_obj = sound_obj->Peek_Cullable_Wrapper ();
  571. if (cull_obj == NULL) {
  572. //
  573. // Create a wrapper object for the sound that we can use
  574. // with the different culling systems.
  575. //
  576. cull_obj = W3DNEW SoundCullObjClass;
  577. cull_obj->Set_Sound_Obj (sound_obj);
  578. sound_obj->Set_Cullable_Wrapper (cull_obj);
  579. //
  580. // Add this object to the static culling system
  581. //
  582. m_StaticCullingSystem.Add_Object (cull_obj);
  583. m_StaticSounds.Add (cull_obj);
  584. Update_Sound (cull_obj);
  585. //
  586. // If the listener can hear this sound, then make sure
  587. // we start it off non-culled
  588. //
  589. bool cull_sound = true;
  590. if (m_IsBatchMode == false) {
  591. Vector3 listener_pos = m_Listener->Get_Position ();
  592. Vector3 sound_pos = sound_obj->Get_Position ();
  593. float radius = sound_obj->Get_DropOff_Radius ();
  594. float radius2 = radius * radius;
  595. if (((listener_pos - sound_pos).Length2 ()) < radius2) {
  596. cull_sound = false;
  597. m_LastSoundsAudible.Add (sound_obj);
  598. start_playing = true;
  599. sound_obj->Set_Listener_Transform (m_Listener->Get_Transform ());
  600. }
  601. }
  602. //
  603. // Make sure the sound is appropriately culled
  604. //
  605. sound_obj->Cull_Sound (cull_sound);
  606. //
  607. // Start the sound playing if requested
  608. //
  609. if (start_playing) {
  610. sound_obj->Play ();
  611. }
  612. //
  613. // Add a ref to the static sound object
  614. //
  615. //sound_obj->Add_Ref ();
  616. }
  617. }
  618. return ;
  619. }
  620. ////////////////////////////////////////////////////////////////////////////////////////////////
  621. //
  622. // Remove_Static_Sound
  623. //
  624. ////////////////////////////////////////////////////////////////////////////////////////////////
  625. void
  626. SoundSceneClass::Remove_Static_Sound
  627. (
  628. AudibleSoundClass * sound_obj,
  629. bool stop_playing
  630. )
  631. {
  632. WWPROFILE ("Remove_Static_Sound");
  633. if (sound_obj == NULL) {
  634. return ;
  635. }
  636. //
  637. // Make sure we remove this sound from the list of last audible sounds.
  638. //
  639. if (m_LastSoundsAudible.Is_In_List (sound_obj)) {
  640. m_LastSoundsAudible.Remove (sound_obj);
  641. }
  642. //
  643. // Is this sound really in the scene?
  644. //
  645. SoundCullObjClass *cull_obj = sound_obj->Peek_Cullable_Wrapper ();
  646. if (cull_obj != NULL && m_StaticSounds.Is_In_List (cull_obj)) {
  647. //
  648. // Stop playing the sound if necessary
  649. //
  650. if (stop_playing) {
  651. sound_obj->Stop ();
  652. }
  653. //
  654. // Flush the sound's cull-wrapper since we are removing it from the scene
  655. //
  656. sound_obj->Set_Cullable_Wrapper (NULL);
  657. //
  658. // Remove this sound from the static culling system
  659. //
  660. m_StaticCullingSystem.Remove_Object (cull_obj);
  661. m_StaticSounds.Remove (cull_obj);
  662. //
  663. // Register the sound for deletion at an appropriate time
  664. //
  665. WWAudioThreadsClass::Add_Delayed_Release_Object (cull_obj);
  666. }
  667. return ;
  668. }
  669. ////////////////////////////////////////////////////////////////////////////////////////////////
  670. //
  671. // Add_Logical_Sound
  672. //
  673. ////////////////////////////////////////////////////////////////////////////////////////////////
  674. void
  675. SoundSceneClass::Add_Logical_Sound
  676. (
  677. LogicalSoundClass * sound_obj,
  678. bool single_shot
  679. )
  680. {
  681. WWPROFILE ("Add_Logical_Sound");
  682. WWASSERT (sound_obj != NULL);
  683. if (sound_obj != NULL) {
  684. //
  685. // Check to make sure we don't add this sound twice
  686. //
  687. if (Is_Logical_Sound_In_Scene (sound_obj, single_shot) == false) {
  688. sound_obj->Set_Listener_Timestamp (LogicalListenerClass::Get_Newest_Timestamp ());
  689. //
  690. // Create a wrapper object for the sound that we can use
  691. // with the different culling systems.
  692. //
  693. SoundCullObjClass *cullable_sound = W3DNEW SoundCullObjClass;
  694. cullable_sound->Set_Sound_Obj (sound_obj);
  695. sound_obj->Set_Cullable_Wrapper (cullable_sound);
  696. //
  697. // Add this object to the logical culling system
  698. //
  699. m_LogicalCullingSystem.Add_Object (cullable_sound);
  700. //
  701. // Add this sound to our current sounds list
  702. //
  703. if (single_shot) {
  704. m_SingleShotLogicalSounds.Add (sound_obj);
  705. } else {
  706. m_LogicalSounds.Add (sound_obj);
  707. }
  708. //
  709. // Keep a reference on this sound object
  710. //
  711. sound_obj->Add_Ref ();
  712. //
  713. // Make sure the cull-object has the most up-to-date information
  714. // about this sound object's bounding volume
  715. //
  716. Update_Sound (cullable_sound);
  717. }
  718. }
  719. return ;
  720. }
  721. ////////////////////////////////////////////////////////////////////////////////////////////////
  722. //
  723. // Remove_Logical_Sound
  724. //
  725. ////////////////////////////////////////////////////////////////////////////////////////////////
  726. void
  727. SoundSceneClass::Remove_Logical_Sound
  728. (
  729. LogicalSoundClass * sound_obj,
  730. bool single_shot,
  731. bool remove_from_list
  732. )
  733. {
  734. WWPROFILE ("Remove_Logical_Sound");
  735. if (sound_obj == NULL) {
  736. return ;
  737. }
  738. if (single_shot) {
  739. //
  740. // Only do this if the logical sound is really in our list
  741. //
  742. if (m_SingleShotLogicalSounds.Is_In_List (sound_obj)) {
  743. //
  744. // Remove this sound from logical sound list
  745. //
  746. if (remove_from_list) {
  747. m_SingleShotLogicalSounds.Remove (sound_obj);
  748. }
  749. //
  750. // Remove this sound from the culling system
  751. //
  752. SoundCullObjClass *cull_obj = sound_obj->Peek_Cullable_Wrapper ();
  753. m_LogicalCullingSystem.Remove_Object (cull_obj);
  754. //
  755. // Remove this sound obj's wrapper
  756. //
  757. sound_obj->Set_Cullable_Wrapper (NULL);
  758. WWAudioThreadsClass::Add_Delayed_Release_Object (cull_obj);
  759. //
  760. // Release our reference on this object
  761. //
  762. REF_PTR_RELEASE (sound_obj);
  763. }
  764. } else {
  765. //
  766. // Only do this if the logical sound is really in our list
  767. //
  768. if (m_LogicalSounds.Is_In_List (sound_obj)) {
  769. //
  770. // Remove this sound from logical sound list
  771. //
  772. if (remove_from_list) {
  773. m_LogicalSounds.Remove (sound_obj);
  774. }
  775. //
  776. // Remove this sound from the culling system
  777. //
  778. SoundCullObjClass *cull_obj = sound_obj->Peek_Cullable_Wrapper ();
  779. m_LogicalCullingSystem.Remove_Object (cull_obj);
  780. //
  781. // Remove this sound obj's wrapper
  782. //
  783. sound_obj->Set_Cullable_Wrapper (NULL);
  784. WWAudioThreadsClass::Add_Delayed_Release_Object (cull_obj);
  785. //
  786. // Release our reference on this object
  787. //
  788. REF_PTR_RELEASE (sound_obj);
  789. }
  790. }
  791. return ;
  792. }
  793. ////////////////////////////////////////////////////////////////////////////////////////////////
  794. //
  795. // Add_Logical_Listener
  796. //
  797. ////////////////////////////////////////////////////////////////////////////////////////////////
  798. void
  799. SoundSceneClass::Add_Logical_Listener (LogicalListenerClass *listener_obj)
  800. {
  801. WWPROFILE ("Add_Logical_Listener");
  802. WWASSERT (listener_obj != NULL);
  803. if (listener_obj != NULL) {
  804. //
  805. // Add the listener to the 'scene' if its in our list
  806. //
  807. if (m_LogicalListeners.Is_In_List (listener_obj) == false) {
  808. listener_obj->Set_Timestamp (LogicalListenerClass::Get_New_Timestamp ());
  809. m_LogicalListeners.Add_Tail (listener_obj);
  810. listener_obj->Add_Ref ();
  811. }
  812. }
  813. return ;
  814. }
  815. ////////////////////////////////////////////////////////////////////////////////////////////////
  816. //
  817. // Remove_Logical_Listener
  818. //
  819. ////////////////////////////////////////////////////////////////////////////////////////////////
  820. void
  821. SoundSceneClass::Remove_Logical_Listener (LogicalListenerClass *listener_obj)
  822. {
  823. WWPROFILE ("Remove_Logical_Listener");
  824. WWASSERT (listener_obj != NULL);
  825. if (listener_obj != NULL) {
  826. //
  827. // Remove the listener from the 'scene' if its in our list
  828. //
  829. if (m_LogicalListeners.Is_In_List (listener_obj)) {
  830. m_LogicalListeners.Remove (listener_obj);
  831. listener_obj->Release_Ref ();
  832. }
  833. }
  834. return ;
  835. }
  836. ////////////////////////////////////////////////////////////////////////////////////////////////
  837. //
  838. // Update_Sound
  839. //
  840. ////////////////////////////////////////////////////////////////////////////////////////////////
  841. void
  842. SoundSceneClass::Update_Sound (SoundCullObjClass *sound_obj)
  843. {
  844. if (sound_obj != NULL) {
  845. sound_obj->Set_Cull_Box(sound_obj->Get_Bounding_Box());
  846. }
  847. return ;
  848. }
  849. ////////////////////////////////////////////////////////////////////////////////////////////////
  850. //
  851. // Initialize
  852. //
  853. ////////////////////////////////////////////////////////////////////////////////////////////////
  854. void
  855. SoundSceneClass::Initialize (void)
  856. {
  857. m_Listener->Free_Miles_Handle ();
  858. m_Listener->Allocate_Miles_Handle ();
  859. return ;
  860. }
  861. ////////////////////////////////////////////////////////////////////////////////////////////////
  862. //
  863. // Is_Sound_In_Scene
  864. //
  865. ////////////////////////////////////////////////////////////////////////////////////////////////
  866. bool
  867. SoundSceneClass::Is_Sound_In_Scene (AudibleSoundClass *sound_obj, bool all)
  868. {
  869. bool retval = false;
  870. //
  871. // Try to find this sound's cull-object in either the static or dynamic
  872. // lists.
  873. //
  874. SoundCullObjClass *cull_obj = sound_obj->Peek_Cullable_Wrapper ();
  875. if (cull_obj != NULL) {
  876. retval = (m_DynamicSounds.Is_In_List (cull_obj) || m_StaticSounds.Is_In_List (cull_obj));
  877. }
  878. return retval;
  879. }
  880. ////////////////////////////////////////////////////////////////////////////////////////////////
  881. //
  882. // Is_Logical_Sound_In_Scene
  883. //
  884. ////////////////////////////////////////////////////////////////////////////////////////////////
  885. bool
  886. SoundSceneClass::Is_Logical_Sound_In_Scene
  887. (
  888. LogicalSoundClass * sound_obj,
  889. bool single_shot
  890. )
  891. {
  892. bool retval = false;
  893. //
  894. // Check to see if this sound is in either the continuous list or the single shot list.
  895. //
  896. if (single_shot == false) {
  897. retval = m_LogicalSounds.Is_In_List (sound_obj);
  898. } else {
  899. retval = m_SingleShotLogicalSounds.Is_In_List (sound_obj);
  900. }
  901. return retval;
  902. }
  903. ////////////////////////////////////////////////////////////////////////////////////////////////
  904. //
  905. // Save_Static
  906. //
  907. ////////////////////////////////////////////////////////////////////////////////////////////////
  908. bool
  909. SoundSceneClass::Save_Static (ChunkSaveClass &csave)
  910. {
  911. csave.Begin_Chunk (CHUNKID_VARIABLES);
  912. WRITE_MICRO_CHUNK (csave, VARID_MIN_DIM, m_MinExtents);
  913. WRITE_MICRO_CHUNK (csave, VARID_MAX_DIM, m_MaxExtents);
  914. csave.End_Chunk ();
  915. //
  916. // Save the list of static sounds that are currently in the scene
  917. //
  918. csave.Begin_Chunk (CHUNKID_STATIC_SOUNDS);
  919. Save_Static_Sounds (csave);
  920. csave.End_Chunk ();
  921. return true;
  922. }
  923. ////////////////////////////////////////////////////////////////////////////////////////////////
  924. //
  925. // Save_Static_Sounds
  926. //
  927. ////////////////////////////////////////////////////////////////////////////////////////////////
  928. void
  929. SoundSceneClass::Save_Static_Sounds (ChunkSaveClass &csave)
  930. {
  931. Re_Partition (m_MinExtents, m_MaxExtents);
  932. //
  933. // Loop over all the static sounds that are in the scene
  934. // and save each to its own chunk.
  935. //
  936. MultiListIterator<SoundCullObjClass> static_iterator (&m_StaticSounds);
  937. for (static_iterator.First (); !static_iterator.Is_Done (); static_iterator.Next ()) {
  938. SoundCullObjClass *cull_obj = static_iterator.Peek_Obj ();
  939. //
  940. // Get the sound from its cull object
  941. //
  942. AudibleSoundClass *sound_obj = (AudibleSoundClass *)cull_obj->Peek_Sound_Obj ();
  943. if (sound_obj != NULL) {
  944. //
  945. // Have the sound's factory save it
  946. //
  947. csave.Begin_Chunk (sound_obj->Get_Factory ().Chunk_ID ());
  948. sound_obj->Get_Factory ().Save (csave, sound_obj);
  949. csave.End_Chunk ();
  950. }
  951. }
  952. return ;
  953. }
  954. ////////////////////////////////////////////////////////////////////////////////////////////////
  955. //
  956. // Load_Static_Sounds
  957. //
  958. ////////////////////////////////////////////////////////////////////////////////////////////////
  959. void
  960. SoundSceneClass::Load_Static_Sounds (ChunkLoadClass &cload)
  961. {
  962. while (cload.Open_Chunk ()) {
  963. //
  964. // Load this sound from the chunk (if possible)
  965. //
  966. PersistFactoryClass *factory = SaveLoadSystemClass::Find_Persist_Factory (cload.Cur_Chunk_ID ());
  967. if (factory != NULL) {
  968. AudibleSoundClass *sound_obj = (AudibleSoundClass *)factory->Load (cload);
  969. if (sound_obj != NULL) {
  970. sound_obj->Add_To_Scene (true);
  971. REF_PTR_RELEASE (sound_obj);
  972. }
  973. }
  974. cload.Close_Chunk ();
  975. }
  976. return ;
  977. }
  978. ////////////////////////////////////////////////////////////////////////////////////////////////
  979. //
  980. // Load_Static
  981. //
  982. ////////////////////////////////////////////////////////////////////////////////////////////////
  983. bool
  984. SoundSceneClass::Load_Static (ChunkLoadClass &cload)
  985. {
  986. m_IsBatchMode = true;
  987. while (cload.Open_Chunk ()) {
  988. switch (cload.Cur_Chunk_ID ()) {
  989. case CHUNKID_STATIC_SOUNDS:
  990. Load_Static_Sounds (cload);
  991. break;
  992. case CHUNKID_VARIABLES:
  993. {
  994. //
  995. // Read all the variables from their micro-chunks
  996. //
  997. while (cload.Open_Micro_Chunk ()) {
  998. switch (cload.Cur_Micro_Chunk_ID ()) {
  999. READ_MICRO_CHUNK (cload, VARID_MIN_DIM, m_MinExtents);
  1000. READ_MICRO_CHUNK (cload, VARID_MAX_DIM, m_MaxExtents);
  1001. }
  1002. cload.Close_Micro_Chunk ();
  1003. }
  1004. }
  1005. break;
  1006. }
  1007. cload.Close_Chunk ();
  1008. }
  1009. Re_Partition (m_MinExtents, m_MaxExtents);
  1010. On_Frame_Update (0);
  1011. m_IsBatchMode = false;
  1012. return true;
  1013. }
  1014. ////////////////////////////////////////////////////////////////////////////////////////////////
  1015. //
  1016. // Save_Dynamic
  1017. //
  1018. ////////////////////////////////////////////////////////////////////////////////////////////////
  1019. bool
  1020. SoundSceneClass::Save_Dynamic (ChunkSaveClass &csave)
  1021. {
  1022. return true;
  1023. }
  1024. ////////////////////////////////////////////////////////////////////////////////////////////////
  1025. //
  1026. // Load_Dynamic
  1027. //
  1028. ////////////////////////////////////////////////////////////////////////////////////////////////
  1029. bool
  1030. SoundSceneClass::Load_Dynamic (ChunkLoadClass &cload)
  1031. {
  1032. return true;
  1033. }
  1034. ////////////////////////////////////////////////////////////////////////////////////////////////
  1035. //
  1036. // Flush_Scene
  1037. //
  1038. ////////////////////////////////////////////////////////////////////////////////////////////////
  1039. void
  1040. SoundSceneClass::Flush_Scene (void)
  1041. {
  1042. RefMultiListClass<SoundCullObjClass> temp_static;
  1043. RefMultiListClass<SoundCullObjClass> temp_dynamic;
  1044. //
  1045. // Build temporary lists of the static and dynamic sounds
  1046. //
  1047. MultiListIterator<SoundCullObjClass> static_iterator (&m_StaticSounds);
  1048. for (static_iterator.First (); !static_iterator.Is_Done (); static_iterator.Next ()) {
  1049. SoundCullObjClass *cull_obj = static_iterator.Peek_Obj ();
  1050. temp_static.Add (cull_obj);
  1051. }
  1052. MultiListIterator<SoundCullObjClass> dynamic_iterator (&m_DynamicSounds);
  1053. for (dynamic_iterator.First (); !dynamic_iterator.Is_Done (); dynamic_iterator.Next ()) {
  1054. SoundCullObjClass *cull_obj = dynamic_iterator.Peek_Obj ();
  1055. temp_dynamic.Add (cull_obj);
  1056. }
  1057. //
  1058. // Remove all the static sounds from the scene
  1059. //
  1060. RefMultiListIterator<SoundCullObjClass> temp_static_it (&temp_static);
  1061. for (temp_static_it.First (); !temp_static_it.Is_Done (); temp_static_it.Next ()) {
  1062. SoundCullObjClass *cull_obj = temp_static_it.Peek_Obj ();
  1063. AudibleSoundClass *sound_obj = (AudibleSoundClass *)cull_obj->Peek_Sound_Obj ();
  1064. if (sound_obj != NULL) {
  1065. Remove_Static_Sound (sound_obj);
  1066. }
  1067. }
  1068. //
  1069. // Remove all the dynamic sounds from the scene
  1070. //
  1071. RefMultiListIterator<SoundCullObjClass> temp_dynamic_it (&temp_dynamic);
  1072. for (temp_dynamic_it.First (); !temp_dynamic_it.Is_Done (); temp_dynamic_it.Next ()) {
  1073. SoundCullObjClass *cull_obj = temp_dynamic_it.Peek_Obj ();
  1074. AudibleSoundClass *sound_obj = (AudibleSoundClass *)cull_obj->Peek_Sound_Obj ();
  1075. if (sound_obj != NULL) {
  1076. Remove_Sound (sound_obj);
  1077. }
  1078. }
  1079. return ;
  1080. }
  1081. ////////////////////////////////////////////////////////////////////////////////////////////////
  1082. //
  1083. // Set_2nd_Listener
  1084. //
  1085. ////////////////////////////////////////////////////////////////////////////////////////////////
  1086. void
  1087. SoundSceneClass::Set_2nd_Listener (Listener3DClass *listener)
  1088. {
  1089. if (m_2ndListener != NULL) {
  1090. m_2ndListener->On_Removed_From_Scene ();
  1091. }
  1092. if (listener != NULL) {
  1093. listener->On_Added_To_Scene ();
  1094. }
  1095. REF_PTR_SET (m_2ndListener, listener);
  1096. return ;
  1097. }