/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** 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 *** *********************************************************************************************** * * * Project Name : WWAudio * * * * $Archive:: /Commando/Code/WWAudio/SoundSceneObj.cpp $* * * * $Modtime:: 11/02/01 11:58a $* * * * $Revision:: 16 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "SoundSceneObj.h" #include "camera.h" #include "rendobj.h" #include "persistfactory.h" #include "SoundChunkIDs.h" #include "utils.h" ////////////////////////////////////////////////////////////////////////////////// // Save/Load constants ////////////////////////////////////////////////////////////////////////////////// enum { CHUNKID_VARIABLES = 0x03270459, CHUNKID_BASE_CLASS }; enum { VARID_ATTACHED_OBJ = 0x01, VARID_ATTACHED_BONE, VARID_USER_DATA, VARID_USER_OBJ, VARID_ID }; ////////////////////////////////////////////////////////////////////////////////// // Static member initialization ////////////////////////////////////////////////////////////////////////////////// DynamicVectorClass SoundSceneObjClass::m_GlobalSoundList; uint32 SoundSceneObjClass::m_NextAvailableID = SOUND_OBJ_START_ID; CriticalSectionClass SoundSceneObjClass::m_IDListMutex; ////////////////////////////////////////////////////////////////////////////////// // Mutex managment ////////////////////////////////////////////////////////////////////////////////// /* class HandleMgrClass { public: HandleMgrClass (void) { SoundSceneObjClass::m_IDListMutex = ::CreateMutex (NULL, FALSE, NULL); } ~HandleMgrClass (void) { ::CloseHandle (SoundSceneObjClass::m_IDListMutex); } }; HandleMgrClass _GlobalMutexHandleMgr; */ //////////////////////////////////////////////////////////////////////////////////////////////// // // SoundSceneObjClass // //////////////////////////////////////////////////////////////////////////////////////////////// SoundSceneObjClass::SoundSceneObjClass (void) : m_Scene (NULL), m_PhysWrapper (NULL), m_pCallback (NULL), m_AttachedObject (NULL), m_UserData (0), m_UserObj (NULL), m_ID (SOUND_OBJ_DEFAULT_ID), m_RegisteredEvents (AudioCallbackClass::EVENT_NONE) { m_ID = m_NextAvailableID ++; Register_Sound_Object (this); return ; } //////////////////////////////////////////////////////////////////////////////////////////////// // // SoundSceneObjClass // //////////////////////////////////////////////////////////////////////////////////////////////// SoundSceneObjClass::SoundSceneObjClass (const SoundSceneObjClass &src) : m_Scene (NULL), m_PhysWrapper (NULL), m_pCallback (NULL), m_AttachedObject (NULL), m_UserData (0), m_UserObj (NULL), m_ID (SOUND_OBJ_DEFAULT_ID), m_RegisteredEvents (AudioCallbackClass::EVENT_NONE) { m_ID = m_NextAvailableID ++; (*this) = src; Register_Sound_Object (this); return ; } //////////////////////////////////////////////////////////////////////////////////////////////// // // ~SoundSceneObjClass // //////////////////////////////////////////////////////////////////////////////////////////////// SoundSceneObjClass::~SoundSceneObjClass (void) { Register_Callback (AudioCallbackClass::EVENT_NONE, NULL); REF_PTR_RELEASE (m_UserObj); REF_PTR_RELEASE (m_AttachedObject); Unregister_Sound_Object (this); return ; } //////////////////////////////////////////////////////////////////////////////////////////////// // // operator= // //////////////////////////////////////////////////////////////////////////////////////////////// const SoundSceneObjClass & SoundSceneObjClass::operator= (const SoundSceneObjClass &src) { m_Scene = src.m_Scene; m_pCallback = src.m_pCallback; m_RegisteredEvents = src.m_RegisteredEvents; Attach_To_Object (src.m_AttachedObject, src.m_AttachedBone); PersistClass::operator= ((const PersistClass &)src); return (*this); } //////////////////////////////////////////////////////////////////////////////////////////////// // // Attach_To_Object // //////////////////////////////////////////////////////////////////////////////////////////////// void SoundSceneObjClass::Attach_To_Object ( RenderObjClass * render_obj, const char * bone_name ) { REF_PTR_SET (m_AttachedObject, render_obj); if (m_AttachedObject != NULL && bone_name != NULL) { m_AttachedBone = m_AttachedObject->Get_Bone_Index (bone_name); } else { m_AttachedBone = -1; } return ; } //////////////////////////////////////////////////////////////////////////////////////////////// // // Attach_To_Object // //////////////////////////////////////////////////////////////////////////////////////////////// void SoundSceneObjClass::Attach_To_Object ( RenderObjClass * render_obj, int bone_index ) { if (m_AttachedObject != render_obj || m_AttachedBone != bone_index) { // // Record the attachment // REF_PTR_SET (m_AttachedObject, render_obj); m_AttachedBone = bone_index; // // Update the transform // Apply_Auto_Position (); } return ; } ////////////////////////////////////////////////////////////////////////////// // // Apply_Auto_Position // ////////////////////////////////////////////////////////////////////////////// void SoundSceneObjClass::Apply_Auto_Position (void) { // If the sound is attached to an object, then update its transform // based on this link. if (m_AttachedObject != NULL) { // Determine which transform to use Matrix3D transform (1); if (m_AttachedBone >= 0) { transform = m_AttachedObject->Get_Bone_Transform (m_AttachedBone); } else { transform = m_AttachedObject->Get_Transform (); // // Convert the camera's transform to an object transform // if (m_AttachedObject->Class_ID () == RenderObjClass::CLASSID_CAMERA) { Matrix3D cam_to_world (Vector3 (0, 0, -1), Vector3 (-1, 0, 0), Vector3 (0, 1, 0), Vector3 (0, 0, 0)); transform = transform * cam_to_world; } } // Update the sound's transform Set_Transform (transform); } return ; } ////////////////////////////////////////////////////////////////////////////////// // // Save // ////////////////////////////////////////////////////////////////////////////////// bool SoundSceneObjClass::Save (ChunkSaveClass &csave) { csave.Begin_Chunk (CHUNKID_BASE_CLASS); PersistClass::Save (csave); csave.End_Chunk (); csave.Begin_Chunk (CHUNKID_VARIABLES); WRITE_MICRO_CHUNK (csave, VARID_ATTACHED_OBJ, m_AttachedObject); WRITE_MICRO_CHUNK (csave, VARID_ATTACHED_BONE, m_AttachedBone); WRITE_MICRO_CHUNK (csave, VARID_USER_DATA, m_UserData); WRITE_MICRO_CHUNK (csave, VARID_USER_OBJ, m_UserObj); WRITE_MICRO_CHUNK (csave, VARID_ID, m_ID); csave.End_Chunk (); return true; } ////////////////////////////////////////////////////////////////////////////////// // // Load // ////////////////////////////////////////////////////////////////////////////////// bool SoundSceneObjClass::Load (ChunkLoadClass &cload) { uint32 id = SOUND_OBJ_DEFAULT_ID; while (cload.Open_Chunk ()) { switch (cload.Cur_Chunk_ID ()) { case CHUNKID_BASE_CLASS: PersistClass::Load (cload); break; case CHUNKID_VARIABLES: { // // Read all the variables from their micro-chunks // while (cload.Open_Micro_Chunk ()) { switch (cload.Cur_Micro_Chunk_ID ()) { READ_MICRO_CHUNK (cload, VARID_ATTACHED_OBJ, m_AttachedObject); READ_MICRO_CHUNK (cload, VARID_ATTACHED_BONE, m_AttachedBone); READ_MICRO_CHUNK (cload, VARID_USER_DATA, m_UserData); READ_MICRO_CHUNK (cload, VARID_USER_OBJ, m_UserObj); READ_MICRO_CHUNK (cload, VARID_ID, id); } cload.Close_Micro_Chunk (); } } break; } cload.Close_Chunk (); } // // Set the ID (this will cause the sound object to // be re-inserted in the master sorted list) // if (id != SOUND_OBJ_DEFAULT_ID) { Set_ID (id); } // // Max sure the next available ID is the largest ID in existence // m_NextAvailableID = max (m_NextAvailableID, m_ID + 1); // // We need to 'swizzle' the attached object pointer. We saved the pointer's // value, and need to map it (hopefully) to the new value. // if (m_AttachedObject != NULL) { SaveLoadSystemClass::Request_Ref_Counted_Pointer_Remap ((RefCountClass **)&m_AttachedObject); } return true; } ////////////////////////////////////////////////////////////////////////////////// // // On_Frame_Update // ////////////////////////////////////////////////////////////////////////////////// bool SoundSceneObjClass::On_Frame_Update (unsigned int /*milliseconds*/) { Apply_Auto_Position (); return true; } ////////////////////////////////////////////////////////////////////////////////// // // Set_ID // ////////////////////////////////////////////////////////////////////////////////// void SoundSceneObjClass::Set_ID (uint32 id) { // // Remove the sound object from our sorted list // Unregister_Sound_Object (this); // // Change the sound object's ID // m_ID = id; // // Reinsert the sound object in our sorted list // Register_Sound_Object (this); return ; } ////////////////////////////////////////////////////////////////////////////////// // // Register_Sound_Object // ////////////////////////////////////////////////////////////////////////////////// void SoundSceneObjClass::Register_Sound_Object (SoundSceneObjClass *sound_obj) { int sound_id = sound_obj->Get_ID (); CriticalSectionClass::LockClass lock(m_IDListMutex); // // Special case a non-ID // if (sound_id == SOUND_OBJ_DEFAULT_ID) { m_GlobalSoundList.Insert (0, sound_obj); } else { // // Check to ensure the object isn't already in the list // int index = 0; if (Find_Sound_Object (sound_id, &index) == false) { // // Insert the object into the list // m_GlobalSoundList.Insert (index, sound_obj); } } return ; } ////////////////////////////////////////////////////////////////////////////////// // // Unregister_Sound_Object // ////////////////////////////////////////////////////////////////////////////////// void SoundSceneObjClass::Unregister_Sound_Object (SoundSceneObjClass *sound_obj) { CriticalSectionClass::LockClass lock(m_IDListMutex); // // Try to find the object in the list // int index = 0; if (Find_Sound_Object (sound_obj->Get_ID (), &index)) { // // Remove the object from the list // m_GlobalSoundList.Delete (index); } return ; } ////////////////////////////////////////////////////////////////////////////////// // // Find_Sound_Object // ////////////////////////////////////////////////////////////////////////////////// bool SoundSceneObjClass::Find_Sound_Object (uint32 id_to_find, int *index) { CriticalSectionClass::LockClass lock(m_IDListMutex); bool found = false; (*index) = 0; int min_index = 0; int max_index = m_GlobalSoundList.Count () - 1; // // Keep looping until we've closed the window of possiblity // bool keep_going = (max_index >= min_index); while (keep_going) { // // Calculate what slot we are currently looking at // int curr_index = min_index + ((max_index - min_index) / 2); uint32 curr_id = m_GlobalSoundList[curr_index]->Get_ID (); // // Did we find the right slot? // if (id_to_find == curr_id) { (*index) = curr_index; keep_going = false; found = true; } else { // // Stop if we've narrowed the window to one entry // keep_going = (max_index > min_index); // // Move the window to the appropriate side // of the test index. // if (id_to_find < curr_id) { max_index = curr_index - 1; (*index) = curr_index; } else { min_index = curr_index + 1; (*index) = curr_index + 1; } } } return found; }