//
// Asura_StreamingBackgroundSound_System.cpp
//
// Created by Richard Rice - 08/01/04
// 
// Copyright (C) 2004 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"
#include "Sound/Asura_StreamingBackgroundSound_System.h"

#include "Camera/Asura_Camera.h"
#include "Core/Asura_AABB_Tree.h"
#include "Core/Asura_Timers.h"
#include "Core/Memory/Asura_ResourceAllocator.h"
#include "Cutscene/Asura_Cutscene_System.h"
#include "Debugging/Asura_Assert.h"
#include "Entity/Sound/Asura_ServerEntity_StreamingBackgroundSoundController.h"
#include "Network/Asura_Message.h"
#include "Sound/Asura_Audio_System.h"
#include "Sound/Asura_Audio_Voice.h"
#include "Sound/Asura_Dynamic_Music_System.h"
#include "Sound/Asura_Music.h"
#include "Sound/Asura_Phonon.h"
#include "Sound/Asura_Sound.h"
#include "Sound/Asura_Sound_Mixer.h"
#include "Volume/Asura_VolumeGroup.h"

#ifdef ASURA_USE_DEBUGOVERLAY
#include "Debugging/Asura_DebugOverlay.h"
#endif

#ifdef ASURA_TOOLS
#include "Tools_System.h"
#include "EntityTemplate/Tools_EntityTemplate_System.h"
#include "Lighting/Tools_LightSets.h"
#include "Load&Save/LevelExport/Tools_Level_Export.h"
#endif


static const int g_iNoSoundIndex = -1;
static const int g_iDefaultSoundIndex = -2;
static const int g_iDefaultSoundPriority = -1;

//speed at which a sound should fade in/out
static const float g_fFadeSpeed = 2.0f;

static const int g_iVeryBigValue = 100000;

#ifdef ASURA_TOOLS
Asura_TemplateHashTree<Asura_DefaultStreamingBackgroundSound> Asura_StreamingBackgroundSound_System::s_xDefaultSoundTree;
#else
Asura_DefaultStreamingBackgroundSound Asura_StreamingBackgroundSound_System::s_xDefaultSound;
#endif

int Asura_StreamingBackgroundSound_System::s_iNumberOfSounds = 0;
Asura_StreamingBackgroundSound* Asura_StreamingBackgroundSound_System::s_pxSounds = 0;
Asura_AABB_Tree* Asura_StreamingBackgroundSound_System::s_pxVolumeTree = 0;

u_int Asura_StreamingBackgroundSound_System::s_uFlags;
bool Asura_StreamingBackgroundSound_System::s_bFadeOut;
u_int Asura_StreamingBackgroundSound_System::s_uNextID = 1;

#ifdef ASURA_TOOLS
bool Asura_StreamingBackgroundSound_System::s_bDisabled = false;
#endif

struct FoundBackgroundSound
{
	void Reset()
	{
		m_iIndex = g_iNoSoundIndex;
		m_iPriority = -2;
		m_fWeight = 0.0f;
		m_uFilenameHash = 0;
	}
	
	int m_iIndex;
	int m_iPriority;
	float m_fWeight;
	Asura_Hash_ID m_uFilenameHash;
};

static Asura_Vector_3 g_xTestPosition;
static FoundBackgroundSound g_axDesiredSound[2];


struct ActiveBackgroundSound
{
	void Initialise()
	{
		m_iIndex = g_iNoSoundIndex;
		m_iPriority = 0;
		m_iSoundHandle = ASURA_INVALID_HANDLE;
		m_uFilenameHash = ASURA_HASH_ID_UNSET;
	}

	void PlaySound(int iIndex,Asura_Hash_ID uFilenameHash, int iPriority, int iSortOrder);
	void StartSound();
	void StopSound();//stops the sound , but leaves slot in use
	void StopAndFreeSound();//stops the sound and frees the slot
	void UpdateVolume(float fTargetVolume);

	void Pause();//(MKR - 29.08.06)
	void Unpause();//(MKR - 29.08.06)

	bool IsFree(){return m_iIndex == g_iNoSoundIndex;}

	
	////////////////////////////
	int m_iIndex;//index of sound being played in this slot
	int m_iSortOrder;//another priority setting ,based on the order in the 'desired sound' array
	int m_iPriority;
	Asura_Hash_ID m_uFilenameHash;
	Asura_Hash_ID m_uMixer;

	Asura_Handle m_iSoundHandle; //sound handle of streaming sound
	float m_fPreMixerVolume;//volume before taking into account the mixers
	float m_fOverallVolume;//volume set in the sound system

	bool m_bShouldStop;//indicates that this sound should fade out and stop
	bool m_bShouldReleaseHandle;//should release handle to allow another sound to use it
	bool m_bFailedToPlay;//failed to play for reasons other than lack of handle
};

static const u_int g_MaxActiveSounds = 2;
static ActiveBackgroundSound g_axActiveSound[g_MaxActiveSounds];


void Asura_StreamingBackgroundSound_System::Initialise()
{
	#ifdef ASURA_TOOLS
	s_xDefaultSoundTree.Reset();
	#else
	s_xDefaultSound.Initialise();
	#endif

	
	for(u_int i = 0; i < g_MaxActiveSounds; i++)
	{
		g_axActiveSound[i].Initialise();
		g_axDesiredSound[i].Reset();
	}

	s_uFlags = 0;
	s_bFadeOut = false;

#ifdef ASURA_USE_DEBUGOVERLAY
	if( !Asura_DebugOverlay::ModeIsRegistered( ASURA_DO_STREAMING_SOUND ) )
	{
		Asura_DebugOverlay::RegisterMode( "StreamingSound", ASURA_DO_STREAMING_SOUND, DoStreamingBackgroundSoundDebugOverlay, NULL );
	}	
#endif
}

void Asura_StreamingBackgroundSound_System::Shutdown()
{
	Asura_ResourcePushType(ASURA_RESOURCE_STREAMING_BACKGROUND_SOUND);
	{
		for(u_int i=0;i<g_MaxActiveSounds;i++)
		{
			g_axActiveSound[i].StopAndFreeSound();
			g_axDesiredSound[i].Reset();
		}

		for(int i=0; i<s_iNumberOfSounds; i++)
		{
			s_pxSounds[i].Shutdown();
		}

		Asura_ResourceDeallocateArray(s_pxSounds);
		s_pxSounds = 0;
		s_iNumberOfSounds = 0;

		delete s_pxVolumeTree;
		s_pxVolumeTree = 0;
	}
	Asura_ResourcePopType();
}

void Asura_StreamingBackgroundSound_System::Reset()
{
	Shutdown();
	Initialise();
}

//(MKR - 29.08.06)
void Asura_StreamingBackgroundSound_System::Unpause()
{
	#ifndef ASURA_USE_AUDIO_SYSTEM
	for(int i=0;i<g_MaxActiveSounds;i++)
	{
		g_axActiveSound[i].Unpause();
	}
	#endif
}

//(MKR - 29.08.06)
void Asura_StreamingBackgroundSound_System::Pause()
{
	#ifndef ASURA_USE_AUDIO_SYSTEM
	for(int i=0;i<g_MaxActiveSounds;i++)
	{
		g_axActiveSound[i].Pause();
	}
	#endif
}

void Asura_StreamingBackgroundSound_System::Update()
{
	const Asura_DefaultStreamingBackgroundSound* pxDefaultSound = GetDefaultSound();
	
	if(pxDefaultSound->m_xFilename.IsBlank() && !s_iNumberOfSounds)
	{
		return;
	}
	

	//determine if the system is disabled for one reason or another.
	bool bDisabled = false;
	#ifdef ASURA_TOOLS
	if(s_bDisabled) bDisabled = true;
	#endif

	//Use the same flag as the phonon system for disabling this at the moment
	//(Except while in tools since we now have a new flag for doing it seperately)
	//RAR 19/09/2006
	if(Asura_Phonon_System::ArePhononsDisabled())
	{
		#ifdef ASURA_TOOLS
		if(!Tools_System::IsActive()) bDisabled = true;
		#else
		bDisabled = true;
		#endif
	}

	if(Asura_Cutscene_System::ShouldPauseStreamsForCutsceneSoundTrack()) bDisabled = true;
	
	if(bDisabled)
	{
		StopAll();
		return;
	}

	Asura_Camera::SetCameraIndex(0);
	g_xTestPosition = *Asura_Camera::GetPosition();
	
	if(!s_bFadeOut)//if fading out , just let it finish fading before checking sounds again
	{
		if( ShouldStopDuringMusic() && IsMusicPlaying() )
		{
			StopAll();
		}
		else
		{
			//check the current desired sounds to see if they are still valid
			bool bAnyStopped = false;
			for(int i=0;i<2;i++)
			{
				int iIndex = g_axDesiredSound[i].m_iIndex;
				if(iIndex < 0)
				{
					#ifdef ASURA_TOOLS
					//Make sure the default sound hasn't changed since it was started
					if(GetDefaultSound()->m_uFilenameHash != g_axDesiredSound[i].m_uFilenameHash) bAnyStopped = true;
					#endif

				}
				else
				{
					Asura_Assert(iIndex<s_iNumberOfSounds,"");

					//is the position still in the outer volume for this sound?
					const Asura_StreamingBackgroundSound* pxSound = &s_pxSounds[iIndex];
					if(!pxSound->Contains(g_xTestPosition))
					{
						//this sound should no longer be playing
						//don't stop it just yet however , in case we have moved into another volume
						//that uses the same wav file
						bAnyStopped = true;
					}
					#ifdef ASURA_TOOLS
					if(!pxSound->IsMemberOfCurrentLightSet()) bAnyStopped = true;
					#endif
					if(!pxSound->IsActive()) bAnyStopped = true;
				}
			}
	
			static float s_fTimer = 0.0f;
			s_fTimer -= Asura_Timers::GetFrameTime();
			//check which sounds hould be playing periodically , or if we have stopped playing one this frame
			if(bAnyStopped || s_fTimer < 0.0f)
			{
				//need to check if any other sounds should be playing instead
				TestVolumeTree();
				s_fTimer = 0.25f;//reset time till we check again.
			}
		}

		//flag each of the active sounds to stop temporarily , in case they aren't determined to be wanted
		for(u_int i=0;i<g_MaxActiveSounds;i++)
		{
			g_axActiveSound[i].m_bShouldStop = true;
		}

		//attempt to play each of the desired sounds, which may rescue the active sounds from stopping after all
		for(int i=0;i<2;i++)
		{
		 	if(g_axDesiredSound[i].m_iIndex!=g_iNoSoundIndex)
			{
				AddToActiveSounds(g_axDesiredSound[i].m_iIndex, i);
			}
		}

		//start/stop active sounds depending on the number of handles available
		UpdatePlayingStatus();
	}

	//update the volumes of the active sounds
	for(u_int i=0;i<g_MaxActiveSounds;i++)
	{
		int iIndex = g_axActiveSound[i].m_iIndex;
		if( iIndex == g_iNoSoundIndex) continue;

		float fTargetVolume = 1.0f;
		if(s_bFadeOut || g_axActiveSound[i].m_bShouldStop)
		{
			g_axActiveSound[i].m_bShouldStop = true;
			fTargetVolume = 0.0f;
		}
		else if(g_axActiveSound[i].m_bShouldReleaseHandle)
		{
			fTargetVolume = 0.0f;
		}
		else
		{
			//determine the other currently desired sound (if any)
			const FoundBackgroundSound* pxOtherSound = 0;
			for(int j=0; j<2; j++)
			{
				if(g_axDesiredSound[j].m_iIndex != g_axActiveSound[i].m_iIndex && g_axDesiredSound[j].m_iIndex != g_iNoSoundIndex)
				{
					pxOtherSound = &g_axDesiredSound[j];
				}
			}
			
			if(iIndex == g_iDefaultSoundIndex)
			{
				fTargetVolume = pxDefaultSound->m_fVolume;
			}
			else
			{
				float fWeight = s_pxSounds[iIndex].GetVolumeWeighting(g_xTestPosition);
				fTargetVolume = s_pxSounds[iIndex].m_fVolume*fWeight;

				if(g_axActiveSound[i].m_uFilenameHash == pxDefaultSound->m_uFilenameHash &&
				   !pxOtherSound)
				{
					//Playing the same sound as the default sound , and no other sound , so add on the component of volume
					//that would have been got from the default sound
					fTargetVolume += (1-fWeight)*pxDefaultSound->m_fVolume;
				}

			}

			if(pxOtherSound && pxOtherSound->m_iIndex >= 0)
			{
				if( (pxOtherSound->m_iPriority > g_axActiveSound[i].m_iPriority) ||
				    (pxOtherSound->m_iPriority == g_axActiveSound[i].m_iPriority && pxOtherSound->m_uFilenameHash == ASURA_HASH_ID_UNSET) )
				{
					//Other sound either has higher priority or is an anti-sound with the same priority.
					//In both cases scale down this sound according to how far within the other this is.
					float fOtherWeight = s_pxSounds[pxOtherSound->m_iIndex].GetVolumeWeighting(g_xTestPosition);
					fTargetVolume *= 1.0f-fOtherWeight;
				}
			}
		}
		g_axActiveSound[i].UpdateVolume(fTargetVolume);
	}

	if(s_bFadeOut)
	{
		//See if everything has finished fading out
		bool bAnyPlaying = false;
		for(u_int i=0; i<g_MaxActiveSounds; i++)
		{
			if(g_axActiveSound[i].m_iIndex != g_iNoSoundIndex)
			{
				bAnyPlaying = true;
				break;
			}
		}

		if(!bAnyPlaying)
		{
			//everything has stopped, so clear the fade flag
			s_bFadeOut = false;
		}
	}
}

#ifdef ASURA_USE_DEBUGOVERLAY
void Asura_StreamingBackgroundSound_System::DoStreamingBackgroundSoundDebugOverlay( void* const pxIgnore )
{
	int iSortedPriorityIndex[g_MaxActiveSounds];

	// sort active sound indices based on priority
	if (g_axActiveSound[0].m_iPriority >= g_axActiveSound[1].m_iPriority)
	{
		iSortedPriorityIndex[0] = 0;
		iSortedPriorityIndex[1] = 1;
	}
	else
	{
		iSortedPriorityIndex[0] = 1;
		iSortedPriorityIndex[1] = 0;
	}

	u_int uActiveSoundCount = 0;
	for (u_int uActiveSoundIndex = 0; uActiveSoundIndex < g_MaxActiveSounds; uActiveSoundIndex++)
	{
		u_int i = iSortedPriorityIndex[uActiveSoundIndex];
		if(g_axActiveSound[i].m_iIndex != g_iNoSoundIndex)
		{
			Asura_String xFilename("Unknown");
			Asura_String xDefaultString("");

			if(g_axActiveSound[i].m_iIndex == g_iDefaultSoundIndex)
			{
				xFilename = GetDefaultSound()->m_xFilename;
				xDefaultString = " (Default)";
			}
			else
			{
				Asura_StreamingBackgroundSound* pxSound = GetSound(g_axActiveSound[i].m_iIndex);
				if (pxSound)
				{
					xFilename = pxSound->m_xFilename;
					if (!xFilename.GetString())
					{
						xFilename = "Unknown";
					}
				}
			}

			Asura_DebugOverlay::DrawConsoleText( ASURA_DO_STREAMING_SOUND, ">>>>>>>> Active Sound %d%s <<<<<<<<", (uActiveSoundCount + 1), xDefaultString.GetString() );
			uActiveSoundCount++;

			Asura_DebugOverlay::DrawConsoleText( ASURA_DO_STREAMING_SOUND, "Filename : %s", xFilename.GetString());
			Asura_DebugOverlay::DrawConsoleText( ASURA_DO_STREAMING_SOUND, "Priority : %d", g_axActiveSound[i].m_iPriority);
			Asura_DebugOverlay::DrawConsoleText( ASURA_DO_STREAMING_SOUND, "Volume   : %f", g_axActiveSound[i].m_fOverallVolume);

			#ifdef ASURA_USE_AUDIO_SYSTEM
			if(g_axActiveSound[i].m_iSoundHandle != ASURA_INVALID_HANDLE)
			{
				Asura_Audio_Voice* const pxVoice = Asura_Audio_System::GetVoice(g_axActiveSound[i].m_iSoundHandle);

				if(pxVoice)
				{
					float fPlayTime = pxVoice->GetPlayPosition();
					if (!pxVoice->IsPlaying() || (fPlayTime < 0.0f) || (fPlayTime > 1800.0f))
					{
						Asura_DebugOverlay::DrawConsoleText( ASURA_DO_STREAMING_SOUND, "Play Pos : --:--");
					}
					else
					{
						const int iPlayMinutes = static_cast<int>( fPlayTime / 60.0f);
						const int iPlaySeconds = static_cast<int>( fPlayTime ) % 60;
						Asura_DebugOverlay::DrawConsoleText( ASURA_DO_STREAMING_SOUND, "Play Pos : %02d:%02d", iPlayMinutes, iPlaySeconds);
					}
				}
			}
			#endif

			Asura_DebugOverlay::DrawConsoleText( ASURA_DO_STREAMING_SOUND, " " );
		}
	}
}

#endif // ASURA_USE_DEBUGOVERLAY

void Asura_StreamingBackgroundSound_System::TestVolumeTree()
{
	//initialise array for test results
	for(int i=0;i<2;i++)
	{
		g_axDesiredSound[i].m_iIndex = g_iNoSoundIndex;
		g_axDesiredSound[i].m_iPriority = -2;
		g_axDesiredSound[i].m_uFilenameHash = 0;
	}
	
	if(!GetDefaultSound()->m_xFilename.IsBlank())
	{
		//add the default sound as one of the found sounds , but with lowest weighting
		g_axDesiredSound[0].m_iIndex = g_iDefaultSoundIndex;
		g_axDesiredSound[0].m_fWeight = 0;
		g_axDesiredSound[0].m_iPriority = g_iDefaultSoundPriority;
		g_axDesiredSound[0].m_uFilenameHash = GetDefaultSound()->m_uFilenameHash;
	}

	#ifdef ASURA_TOOLS
	if(!s_pxVolumeTree && s_iNumberOfSounds)
	{
		UpdateVolumeTree();
	}
	#endif


	if(s_pxVolumeTree)
	{
		Asura_Bounding_Box xTestBox;
		xTestBox.MinX = xTestBox.MaxX = g_xTestPosition.x;
		xTestBox.MinY = xTestBox.MaxY = g_xTestPosition.y;
		xTestBox.MinZ = xTestBox.MaxZ = g_xTestPosition.z;

		//search the volume tree to see which volumes we are in
		s_pxVolumeTree->TestTree(xTestBox,VolumeTreeCallback);

	}
}

bool Asura_StreamingBackgroundSound_System::VolumeTreeCallback(u_short usIndex)
{
	Asura_Assert(usIndex<s_iNumberOfSounds,"Invalid sound index");
	if (usIndex>=s_iNumberOfSounds) { return true; }

	const Asura_StreamingBackgroundSound* pxSound = &s_pxSounds[usIndex];

	if(!pxSound->Contains(g_xTestPosition)) return true;

	#ifdef ASURA_TOOLS
	//ignore sounds that aren't part of the current 'light' set
	if(!pxSound->IsMemberOfCurrentLightSet()) return true;
	#endif

	//ignore inactive sounds
	if(!pxSound->IsActive()) return true;

		
	//determine how far in volume test position is
	float fWeight = pxSound->GetVolumeWeighting(g_xTestPosition);
	if(fWeight <= 0) return true;

	int iPriority = pxSound->m_iPriority;

	
	//check that the same sound hasn't already been added
	for(int iSlot = 0;iSlot<2;iSlot++)
	{
		if(g_axDesiredSound[iSlot].m_iIndex != g_iNoSoundIndex &&
		   g_axDesiredSound[iSlot].m_uFilenameHash == pxSound->m_uFilenameHash)
		{
			//same sound , so take whichever we are most within
			if(fWeight > g_axDesiredSound[iSlot].m_fWeight)
			if(ComparePriorityAndWeight(iPriority, fWeight, g_axDesiredSound[iSlot].m_iPriority, g_axDesiredSound[iSlot].m_fWeight) > 0)
			{
				g_axDesiredSound[iSlot].m_iIndex = usIndex;
				g_axDesiredSound[iSlot].m_fWeight = fWeight;
				g_axDesiredSound[iSlot].m_iPriority = iPriority;
			}
			return true;
		}
	}
	
	//add this sound to the list of found sounds , sorting so that the first entry has the highest weight
	for(int iSlot = 0;iSlot<2;iSlot++)
	{
		if(ComparePriorityAndWeight(iPriority, fWeight, g_axDesiredSound[iSlot].m_iPriority, g_axDesiredSound[iSlot].m_fWeight) > 0)
		{
			if(iSlot == 0)
			{
				g_axDesiredSound[1] = g_axDesiredSound[0];
			}

			g_axDesiredSound[iSlot].m_iIndex = usIndex;
			g_axDesiredSound[iSlot].m_uFilenameHash = pxSound->m_uFilenameHash;
			g_axDesiredSound[iSlot].m_fWeight = fWeight;
			g_axDesiredSound[iSlot].m_iPriority = iPriority;

			break;
		}
	}
	
	return true;
}

void Asura_StreamingBackgroundSound_System::AddToActiveSounds(int iIndex, int iSortOrder)
{
	Asura_Hash_ID uHashID = (iIndex<0) ? GetDefaultSound()->m_uFilenameHash : s_pxSounds[iIndex].m_uFilenameHash;
	int iPriority = (iIndex < 0) ? g_iDefaultSoundPriority : s_pxSounds[iIndex].m_iPriority;

	//search for an active sound already playing this file ,or for a free slot
	int iFreeSlot = -1;
	for(u_int i=0;i<g_MaxActiveSounds;i++)
	{
		if(g_axActiveSound[i].m_uFilenameHash == uHashID)
		{
			//sound was already playing , so no need to start it again
			g_axActiveSound[i].m_iIndex = iIndex;
			g_axActiveSound[i].m_iPriority = iPriority;
			g_axActiveSound[i].m_iSortOrder = iSortOrder;
			g_axActiveSound[i].m_bShouldStop = false;
			return;
		}
		if(g_axActiveSound[i].m_iIndex == g_iNoSoundIndex && iFreeSlot == -1)
		{
			iFreeSlot = i;
		}
	}

	if(iFreeSlot!=-1)
	{
		//start the sound in this slot
		g_axActiveSound[iFreeSlot].PlaySound(iIndex,uHashID,iPriority,iSortOrder);
		
	}
}

const char* Asura_StreamingBackgroundSound_System::GetSoundFilenameToPlay(int iIndex)
{
	Asura_Assert(iIndex == g_iDefaultSoundIndex || (iIndex>=0 && iIndex < s_iNumberOfSounds), "");
	if(iIndex >= s_iNumberOfSounds) return 0;

	const char* szSoundName = (iIndex<0) ? GetDefaultSound()->m_xFilename.GetString() : s_pxSounds[iIndex].m_xFilename.GetString();
	
	return szSoundName;
}

void Asura_StreamingBackgroundSound_System::UpdatePlayingStatus()
{
	//see how many handles are available
	int iNumHandlesUsed = 0;
	for(u_int i=0;i<g_MaxActiveSounds;i++)
	{
		ActiveBackgroundSound& xSound = g_axActiveSound[i];
		
		if(xSound.IsFree()) continue;

		if(xSound.m_iSoundHandle != ASURA_INVALID_HANDLE)
		{
			iNumHandlesUsed++;
		}
	}

#ifdef ASURA_USE_AUDIO_SYSTEM
	int iNumHandlesFree = g_iVeryBigValue;
#else
	int iNumHandlesFree = Asura_Sound_System::GetMaxStreamingSounds() - Asura_Sound_System::GetNumberActiveStreamingSounds();
#endif

	int iNumHandlesAvailable = iNumHandlesFree + iNumHandlesUsed;

	//any non-stopping sound with a low enough sort order can have a sound handle , the others will have to fade out
	for(u_int i=0;i<g_MaxActiveSounds;i++)
	{
		ActiveBackgroundSound& xSound = g_axActiveSound[i];
		if(xSound.IsFree()) continue;

		if(!xSound.m_bShouldStop && xSound.m_iSortOrder<iNumHandlesAvailable)
		{
			xSound.m_bShouldReleaseHandle = false;
			if(xSound.m_iSoundHandle == ASURA_INVALID_HANDLE)
			{
				//start playing if there are handles available , and this sound hasn't already failed for other reasons
				if(iNumHandlesFree && !xSound.m_bFailedToPlay)
				{
					iNumHandlesFree--;
					xSound.StartSound();
				}
			}
		}
		else
		{
			xSound.m_bShouldReleaseHandle = true;
		}
	}
}

#ifdef ASURA_TOOLS
int Asura_StreamingBackgroundSound_System::CreateSound(const Asura_Vector_3& xPosition, u_int uID /*=0*/)
{
	Asura_ResourcePushType(ASURA_RESOURCE_STREAMING_BACKGROUND_SOUND);
	{
		//create an enlarged array of sounds
		Asura_StreamingBackgroundSound* pxNewSounds;
		Asura_ResourceAllocateArray(pxNewSounds, s_iNumberOfSounds+1);

		//and copy across from the old array
		for(int i=0;i<s_iNumberOfSounds;i++)
		{
			pxNewSounds[i] = s_pxSounds[i];
		}
		//replace with the new array
		Asura_ResourceDeallocateArray(s_pxSounds);
		s_pxSounds = pxNewSounds;
	}
	Asura_ResourcePopType();

	//initialise the new sound
	int iIndex = s_iNumberOfSounds++;
	s_pxSounds[iIndex].Initialise();
	s_pxSounds[iIndex].m_uFilenameHash = ASURA_HASH_ID_UNSET;
	s_pxSounds[iIndex].m_fVolume = 1.0f;
	s_pxSounds[iIndex].m_iType = ASURA_STREAMINGBACKGROUNDSOUNDTYPE_CUBOID;
	s_pxSounds[iIndex].m_xInnerBox.Reset();
	s_pxSounds[iIndex].m_xInnerBox.Set(xPosition);
	s_pxSounds[iIndex].m_xOuterBox.Reset();
	s_pxSounds[iIndex].m_xOuterBox.Set(xPosition);
	s_pxSounds[iIndex].m_xOrient.Identity();
	s_pxSounds[iIndex].m_fInnerRadius = 5.0f;
	s_pxSounds[iIndex].m_fOuterRadius = 20.0f;
	s_pxSounds[iIndex].m_xPosition = xPosition;
	s_pxSounds[iIndex].m_fInnerHeight = 5.0f;
	s_pxSounds[iIndex].m_fOuterHeight = 5.0f;
	s_pxSounds[iIndex].m_uMixer = 0;
	s_pxSounds[iIndex].m_uLightSets = 0xffffffff;
	s_pxSounds[iIndex].m_uTemplateGuid = ASURA_GUID_UNREGISTERED;
	s_pxSounds[iIndex].m_iTemplateVersion = 0;
	s_pxSounds[iIndex].m_uFlags = ASURA_STREAMINGBACKGROUNDSOUNDFLAG_ACTIVE;
	s_pxSounds[iIndex].m_iPriority = 0;
	s_pxSounds[iIndex].m_uID = uID ? uID : GenerateID();
	s_pxSounds[iIndex].m_uGuid = ASURA_GUID_UNREGISTERED;

	InvalidateVolumeTree();
	return iIndex;	
}

void Asura_StreamingBackgroundSound_System::DeleteSound(int iIndex)
{
	Asura_Assert(iIndex>=0 && iIndex<s_iNumberOfSounds,"");
	
	//copy the last sound down to take this ones place
	s_pxSounds[iIndex].Shutdown();
	s_pxSounds[iIndex] = s_pxSounds[s_iNumberOfSounds-1];
	s_iNumberOfSounds--;

	StopAll();
	InvalidateVolumeTree();	
}

int Asura_StreamingBackgroundSound_System::GetSoundIndexFromID(u_int uID)
{
	for(int i=0; i<s_iNumberOfSounds; i++)
	{
		if(s_pxSounds[i].m_uID == uID)
		{
			return i;
		}
	}
	return -1;
}

Asura_StreamingBackgroundSound* Asura_StreamingBackgroundSound_System::GetSoundFromID(u_int uID)
{
	int iIndex = GetSoundIndexFromID(uID);
	if(iIndex >= 0)
	{
		return &s_pxSounds[iIndex];
	}
	return 0;
}

void Asura_StreamingBackgroundSound_System::InvalidateVolumeTree()
{
	delete s_pxVolumeTree;
	s_pxVolumeTree = 0;
}

void Asura_StreamingBackgroundSound_System::UpdateVolumeTree()
{
	//delete the old tree
	delete s_pxVolumeTree;
	s_pxVolumeTree = 0;
	if(!s_iNumberOfSounds) return;

	//and create a new one
	s_pxVolumeTree = Asura_AABB_Tree_Creator::Create(s_iNumberOfSounds,FillVolumeTreeCallback);
}


bool Asura_StreamingBackgroundSound_System::FillVolumeTreeCallback(u_int uNumObjects,Asura_Bounding_Box* pxBoxes,Asura_Vector_3* pxCentres)
{
	int iDestIndex = 0;


	for(int i=0;i<s_iNumberOfSounds;i++)
	{
		if(Tools_Level_Export::IsExport())
		{
			//Skip sounds that aren't going to be saved when this gets called during exports
			if(!s_pxSounds[i].IsMemberOfCurrentLightSet()) continue;
		}
		
		if(s_pxSounds[i].IsSphere())
		{
			//spherical volume
			const float fRadius = s_pxSounds[i].m_fOuterRadius;

			pxBoxes[iDestIndex].MaxX = s_pxSounds[i].m_xPosition.x + fRadius;
			pxBoxes[iDestIndex].MaxY = s_pxSounds[i].m_xPosition.y + fRadius;
			pxBoxes[iDestIndex].MaxZ = s_pxSounds[i].m_xPosition.z + fRadius;

			pxBoxes[iDestIndex].MinX = s_pxSounds[i].m_xPosition.x - fRadius;
			pxBoxes[iDestIndex].MinY = s_pxSounds[i].m_xPosition.y - fRadius;
			pxBoxes[iDestIndex].MinZ = s_pxSounds[i].m_xPosition.z - fRadius;


		}
		else if(s_pxSounds[i].IsCylinder())
		{
			// Calculate a bounding box for a cylinder oriented up the y-axis.
			Asura_Bounding_Box& xBB = pxBoxes[iDestIndex];
			xBB.MinX = -s_pxSounds[i].m_fOuterRadius;
			xBB.MaxX = s_pxSounds[i].m_fOuterRadius;
			xBB.MinY = -s_pxSounds[i].m_fOuterHeight/2.0f;
			xBB.MaxY = s_pxSounds[i].m_fOuterHeight/2.0f;
			xBB.MinZ = -s_pxSounds[i].m_fOuterRadius;
			xBB.MaxZ = s_pxSounds[i].m_fOuterRadius;
			// Rotate it.
			if(s_pxSounds[i].m_xOrient.w != 1.0f)
			{
				Asura_Matrix_3x3 xMat;
				s_pxSounds[i].m_xOrient.ToMatrix(xMat);
				xBB.FromOBB(xBB, xMat);
			}
			// Displace to cylinder position.
			xBB.Displace(s_pxSounds[i].m_xPosition);
		}
		else if(s_pxSounds[i].m_xOrient.w == 1.0f)
		{
			pxBoxes[iDestIndex] = s_pxSounds[i].m_xOuterBox;
		}
		else
		{
			//using orientation
			Asura_Matrix_3x3 xMat;
			s_pxSounds[i].m_xOrient.ToMatrix(xMat);

		    const Asura_Vector_3 xCentre  = s_pxSounds[i].m_xOuterBox.CalculateCentre();
		    const Asura_Vector_3 xExtents = s_pxSounds[i].m_xOuterBox.CalculateExtents();

		    float fHalfAABBExtentX = 0.5f * (   Asura_Maths::Abs(xMat.e11 * xExtents.x)
		                                      + Asura_Maths::Abs(xMat.e12 * xExtents.y)
		                                      + Asura_Maths::Abs(xMat.e13 * xExtents.z) );

		    float fHalfAABBExtentY = 0.5f * (   Asura_Maths::Abs(xMat.e21 * xExtents.x)
		                                      + Asura_Maths::Abs(xMat.e22 * xExtents.y)
		                                      + Asura_Maths::Abs(xMat.e23 * xExtents.z) );

		    float fHalfAABBExtentZ = 0.5f * (   Asura_Maths::Abs(xMat.e31 * xExtents.x)
		                                      + Asura_Maths::Abs(xMat.e32 * xExtents.y)
		                                      + Asura_Maths::Abs(xMat.e33 * xExtents.z) );

		    pxBoxes[iDestIndex].MinX = -fHalfAABBExtentX;
		    pxBoxes[iDestIndex].MaxX =  fHalfAABBExtentX;
		    pxBoxes[iDestIndex].MinY = -fHalfAABBExtentY;
		    pxBoxes[iDestIndex].MaxY =  fHalfAABBExtentY;
		    pxBoxes[iDestIndex].MinZ = -fHalfAABBExtentZ;
		    pxBoxes[iDestIndex].MaxZ =  fHalfAABBExtentZ;

		    pxBoxes[iDestIndex].Displace( xCentre );
		}
		//add a small amount onto the volumes in order to make sure that the volumes in the AABB
		//tree wil contain the actual volumes
		pxBoxes[iDestIndex].MaxX += 0.5f;
		pxBoxes[iDestIndex].MaxY += 0.5f;
		pxBoxes[iDestIndex].MaxZ += 0.5f;
		pxBoxes[iDestIndex].MinX -= 0.5f;
		pxBoxes[iDestIndex].MinY -= 0.5f;
		pxBoxes[iDestIndex].MinZ -= 0.5f;

		pxCentres[iDestIndex] = pxBoxes[iDestIndex].CalculateCentre();

		iDestIndex++;
	}
	
	Asura_Assert(iDestIndex == (int)uNumObjects,"Error building tree for streaming background sound system.");
	return true;

}

#endif // ASURA_TOOLS

void Asura_StreamingBackgroundSound_System::StopAll()
{
	for(u_int i=0;i<g_MaxActiveSounds;i++)
	{
		g_axActiveSound[i].StopAndFreeSound();
		g_axDesiredSound[i].Reset();
	}
	s_bFadeOut = false;
}

void Asura_StreamingBackgroundSound_System::NotifyMusicStarting(bool bCrossFade)
{
	// TODO: Should we have this functionality for the dynamic music system? seems like a good idea
	#ifndef ASURA_USE_AUDIO_SYSTEM
	//determine number of sounds being used by this system
	int iActiveSounds = 0;
	for(int i=0;i<g_MaxActiveSounds;i++)
	{
		if(g_axActiveSound[i].m_iSoundHandle != ASURA_INVALID_HANDLE) iActiveSounds++;	
	}

	if(!iActiveSounds) return;//wasn't playing anything anyway

	//does a sound need to be stopped to make room for music?
	bool bMusicSystemNeedsAnotherHandle = true;
	if(bCrossFade && Asura_Music_System::GetNumberOfSoundHandlesUsed()==2)
	{
		//Music system is already using 2 sound handles ,so won't be needing any more
		bMusicSystemNeedsAnotherHandle = false;		
	}
	else if(Asura_Music_System::GetNumberOfSoundHandlesUsed()!=0)
	{
		//Not doing a crossfade ,and already has a sound handle.. so won't need another
		bMusicSystemNeedsAnotherHandle = false;		

	}

	if(bMusicSystemNeedsAnotherHandle)
	{
		if(Asura_Sound_System::GetMaxStreamingSounds() == Asura_Sound_System::GetNumberActiveStreamingSounds())
		{
			if(iActiveSounds == 1)
			{
				//stop the only sound being played
				StopAll();
				return;
			}
			else
			{
				//stop the quietest sound
				if(g_axActiveSound[0].m_fOverallVolume<g_axActiveSound[1].m_fOverallVolume)
				{
					g_axActiveSound[0].StopAndFreeSound();			
				}
				else
				{
					g_axActiveSound[1].StopAndFreeSound();			
				}
			}
		}
	}

	#endif	// #ifndef ASURA_USE_AUDIO_SYSTEM

	//begin fade out on remaining sounds if not already doing so
	if(ShouldStopDuringMusic())
	{
		s_bFadeOut = true;
	}
}

float Asura_StreamingBackgroundSound_System::GetMixerVolume(int iIndex)
{
	Asura_Hash_ID uMixer = (iIndex<0) ? GetDefaultSound()->m_uMixer : s_pxSounds[iIndex].m_uMixer;
	if(!uMixer) uMixer = uMIXER_HASHID_STREAMINGSOUNDS;


	const Asura_Sound_Mixer_Channel* pxChannel = Asura_Sound_Mixer_System::FindChannel(uMixer);	
	return Asura_Sound_Mixer_System::GetMixerVolume(pxChannel);
}

#ifdef ASURA_TOOLS
Asura_DefaultStreamingBackgroundSound* Asura_StreamingBackgroundSound_System::GetDefaultSound()
{
	u_int uLightSets;
	if(s_uFlags & ASURA_STREAMINGSOUNDSYSTEMFLAG_DEFAULTPERLIGHTSET)
	{
		uLightSets = Tools_LightSets::GetCurrentLightSets();
	}
	else
	{
		uLightSets = 1;
	}

	return GetDefaultSound(uLightSets);
}

Asura_DefaultStreamingBackgroundSound* Asura_StreamingBackgroundSound_System::GetDefaultSound(u_int uLightSets, bool bCreate /*=true*/)
{
	Asura_DefaultStreamingBackgroundSound* pxSound = s_xDefaultSoundTree.GetDataByHash(uLightSets);
	if(!pxSound && bCreate)
	{
		//create a default entry for these light sets
		pxSound = s_xDefaultSoundTree.AddNode(uLightSets);
		Asura_Assert(pxSound,"Failed to add node");
		pxSound->Initialise();
	}

	return pxSound;
}

#endif //ASURA_TOOLS


void Asura_StreamingBackgroundSound_System::HandleMessage(const Asura_Message* pxMessage)
{
	switch(pxMessage->GetMsgID())
	{
		case ASURA_MSG_STREAMINGBACKGROUNDSOUND_FLAGS :
		{
			const Asura_Message_StreamingBackgroundSoundFlags* pxData = reinterpret_cast<const Asura_Message_StreamingBackgroundSoundFlags*>(pxMessage->GetData());

			Asura_StreamingBackgroundSound* pxSound = GetSoundByID(pxData->m_uSoundID);


			if(pxSound)
			{
				pxSound->m_uFlags &=~ pxData->m_uFlagsToSet;
				pxSound->m_uFlags |= (pxData->m_uFlags & pxData->m_uFlagsToSet);
			}
			break;
		}

		default :
			Asura_Assert(0,"Unexpected message.");
			break;
	}
}

Asura_StreamingBackgroundSound* Asura_StreamingBackgroundSound_System::GetSoundByID(u_int uID)
{
	for(int i=0;i<s_iNumberOfSounds;i++)
	{
		if(s_pxSounds[i].m_uID == uID) return &s_pxSounds[i];
	}
	return 0;
}

#ifdef ASURA_TOOLS
void Asura_StreamingBackgroundSound_System::SetDisabled(bool bSet)
{
	if(bSet != s_bDisabled)
	{
		s_bDisabled = bSet;
		if(s_bDisabled) StopAll();
	}
}
#endif

int Asura_StreamingBackgroundSound_System::ComparePriorityAndWeight(int iPriority1, float fWeight1, int iPriority2, float fWeight2)
{
	if(iPriority1 > iPriority2) return 1;
	if(iPriority1 < iPriority2) return -1;
	if(fWeight1 > fWeight2) return 1;
	if(fWeight1 < fWeight2) return -1;
	return 0;
}

bool Asura_StreamingBackgroundSound_System::IsMusicPlaying ()
{
	// This is just in a seperate function to avoid the preprocessing being really ugly elsewhere
	#ifdef ASURA_USE_AUDIO_SYSTEM

		#ifdef ASURA_SUPPORT_DYNAMIC_MUSIC_SYSTEM
		
			return Asura_Dynamic_Music_System::IsPlaying();
		
		#else

			return false;

		#endif

	#else // Not using audio system

	return ( Asura_Music_System::GetNumberOfSoundHandlesUsed() != 0 );

	#endif
}


////////////////////////////////////////////////////////////////////////////////////////////////

//(MKR - 29.08.06)
void ActiveBackgroundSound::Pause()
{
	#ifndef ASURA_USE_AUDIO_SYSTEM
	if(m_iSoundHandle != ASURA_INVALID_HANDLE)
	{
		Asura_Sound_System::PauseStreamingSound(m_iSoundHandle);
	}
	#endif
}

//(MKR - 29.08.06)
void ActiveBackgroundSound::Unpause()
{
	#ifndef ASURA_USE_AUDIO_SYSTEM
	if(m_iSoundHandle != ASURA_INVALID_HANDLE)
	{
		Asura_Sound_System::ResumeStreamingSound(m_iSoundHandle);
	}
	#endif
}

void ActiveBackgroundSound::PlaySound(int iIndex,Asura_Hash_ID uFilenameHash, int iPriority, int iSortOrder)
{
	Asura_Assert(m_iIndex == g_iNoSoundIndex,"This sound slot is already in use");
	m_iIndex = iIndex;
	m_fPreMixerVolume = 0.0f;
	m_fOverallVolume = 0.0f;
	m_uFilenameHash = uFilenameHash;
	m_iPriority = iPriority;
	m_iSortOrder = iSortOrder;
	m_bShouldStop = false;
	m_bShouldReleaseHandle = false;
	m_bFailedToPlay = false;

}

void ActiveBackgroundSound::StartSound()
{
	if(m_iSoundHandle != ASURA_INVALID_HANDLE)
	{
		Asura_Assert(0,"Sound already playing");
		return;
	}

	const char* szSoundName = Asura_StreamingBackgroundSound_System::GetSoundFilenameToPlay(m_iIndex);
	if(szSoundName)
	{
		#ifdef ASURA_USE_AUDIO_SYSTEM
			m_iSoundHandle = Asura_Audio_System::CreateStreamingVoice(Asura_GetHashID(szSoundName), false, false);
			Asura_Audio_Voice* const pxVoice = Asura_Audio_System::GetVoice(m_iSoundHandle);

			if(pxVoice)
			{
				pxVoice->SetVolume(m_fOverallVolume);
				pxVoice->Start();
			}
		#else
			m_iSoundHandle = Asura_Sound_System::CreateStreamingSound(szSoundName);
			if(m_iSoundHandle != ASURA_INVALID_HANDLE)
			{
				Asura_Sound_System::PlayStreamingSound(m_iSoundHandle,true);
				Asura_Sound_System::StreamingSoundSetVolume(m_iSoundHandle,m_fOverallVolume);
			}
		#endif
	}

	m_bFailedToPlay = (m_iSoundHandle == ASURA_INVALID_HANDLE);

	Asura_Assertf(!(szSoundName && m_bFailedToPlay), ("Failed to play streaming sound '%s'. Make sure it's checked "
		"in, you have a local copy, and (if using the new audio system) it's registered in the "
		"streaming sounds overlay.", szSoundName));
}

void ActiveBackgroundSound::StopAndFreeSound()
{
	StopSound();

	m_iIndex = g_iNoSoundIndex;
	m_uFilenameHash = ASURA_HASH_ID_UNSET;
}

void ActiveBackgroundSound::StopSound()
{
	if(m_iSoundHandle != ASURA_INVALID_HANDLE)
	{
		#ifdef ASURA_USE_AUDIO_SYSTEM
			Asura_Audio_System::DestroyVoice(m_iSoundHandle);
		#else
			Asura_Sound_System::ReleaseStreamingSound(m_iSoundHandle);
		#endif

		m_iSoundHandle = ASURA_INVALID_HANDLE;		
	}
}


void ActiveBackgroundSound::UpdateVolume(float fTargetVolume)
{
	//fade towards the target volume
	if(fTargetVolume > m_fPreMixerVolume)
	{
		m_fPreMixerVolume += Asura_Timers::GetFrameTime() * g_fFadeSpeed;
		if(m_fPreMixerVolume > fTargetVolume)
		{
			m_fPreMixerVolume = fTargetVolume;
		}
	}
	else if(fTargetVolume < m_fPreMixerVolume)
	{
		m_fPreMixerVolume -= Asura_Timers::GetFrameTime() * g_fFadeSpeed;
		if(m_fPreMixerVolume < fTargetVolume)
		{
			m_fPreMixerVolume = fTargetVolume;
		}
	}

	float fOverallVolume = m_fPreMixerVolume;
	//multiply in the mixer volume;
	fOverallVolume *= Asura_StreamingBackgroundSound_System::GetMixerVolume(m_iIndex);



	//update the volume in the sound system if changed
	if(m_fOverallVolume == 0.0f && (m_bShouldStop || m_bShouldReleaseHandle))
	{
		//volume has reached zero and the sound is supposed to be stopping, so kill it
		if(m_bShouldStop)
		{
			StopAndFreeSound();
		}
		else
		{
			StopSound();
		}
	}
	else if(fOverallVolume != m_fOverallVolume)
	{
		if(m_iSoundHandle != ASURA_INVALID_HANDLE)
		{
			m_fOverallVolume = fOverallVolume;

			#ifdef ASURA_USE_AUDIO_SYSTEM
				Asura_Audio_Voice* const pxVoice = Asura_Audio_System::GetVoice(m_iSoundHandle);
				if(pxVoice)
				{
					pxVoice->SetVolume(m_fOverallVolume);
				}
			#else
				Asura_Sound_System::StreamingSoundSetVolume(m_iSoundHandle,m_fOverallVolume);
			#endif
		}
	}
}



////////////////////////////////////////////////////////////////////////////////////////////////

void Asura_StreamingBackgroundSound::Initialise()
{
	m_pxClipVolume = 0;
}

void Asura_StreamingBackgroundSound::Shutdown()
{
	delete m_pxClipVolume;
	m_pxClipVolume = 0;
}

float Asura_StreamingBackgroundSound::GetVolumeWeighting(const Asura_Vector_3& xPosition) const
{
	if(IsSphere())
	{
		float fDist = (xPosition-m_xPosition).Magnitude();
		if(fDist <= m_fInnerRadius) return 1.0f;
		if(fDist > m_fOuterRadius) return 0.0f;

		float fVolume = (m_fOuterRadius - fDist) / (m_fOuterRadius - m_fInnerRadius);
		fVolume = Asura_Maths::ClampToRange(fVolume, 0.0f, 1.0f);

		return fVolume;
	}
	else if(IsCylinder())
	{
		//get the position relative to the cylinder centre
		Asura_Vector_3 xRelativePos = xPosition - m_xPosition;

		if(m_xOrient.w != 1.0f)
		{
			//rotate into the space of the volume
			Asura_Quat xOrientInverse = m_xOrient;
    	    xOrientInverse.Invert();
			xRelativePos = xOrientInverse.RotateVector(xRelativePos);
		}

		//determine weighting for distance along the axis
		float fLengthWeight;
		const float fTwiceDistAlongAxis = Asura_Maths::Abs(xRelativePos.y)*2.0f;
		if(fTwiceDistAlongAxis <= m_fInnerHeight)
		{
			fLengthWeight = 1.0f;
		}
		else if(fTwiceDistAlongAxis > m_fOuterHeight)
		{
			return 0.0f;	
		}
		else
		{
			fLengthWeight = (m_fOuterHeight-fTwiceDistAlongAxis)/(m_fOuterHeight-m_fInnerHeight);
		}
		//determine weighting based on distance from the axis
		xRelativePos.y = 0.0f;
		float fRadiusWeight;
		const float fDistFromAxis = xRelativePos.Magnitude();

		if(fDistFromAxis <= m_fInnerRadius)
		{
			fRadiusWeight = 1.0f;
		}
		else if(fDistFromAxis > m_fOuterRadius)
		{
			return 0.0f;	
		}
		else
		{
			fRadiusWeight = (m_fOuterRadius-fDistFromAxis)/(m_fOuterRadius-m_fInnerRadius);
		}

		return fLengthWeight*fRadiusWeight;		
		
	}
	else
	{
		Asura_Vector_3 xRotatedPosition = xPosition;

		if(m_xOrient.w != 1.0f)
		{
			//need to get position relative to volume
			Asura_Vector_3 xCentre = m_xOuterBox.CalculateCentre();
			Asura_Vector_3 xRelative = xPosition - xCentre;

			//and rotate into the space of the volume
			Asura_Quat xOrientInverse = m_xOrient;
    	    xOrientInverse.Invert();
			xRelative = xOrientInverse.RotateVector(xRelative);

			xRotatedPosition = xRelative + xCentre;
		}
	
		if(m_xInnerBox.Contains(xRotatedPosition))
		{
			//inside inner box , so use full volume
			return 1.0f;
		}
		if(!m_xOuterBox.Contains(xRotatedPosition))
		{
			//outside outer box , so 0 volume
			return 0.0f;
		}

		float fVolume = 1.0f;

		//check each axis of the position , to see how much the sound should be faded out by
		if(xRotatedPosition.x>m_xInnerBox.MaxX)
		{
			fVolume *= (m_xOuterBox.MaxX-xRotatedPosition.x)/(m_xOuterBox.MaxX-m_xInnerBox.MaxX);
		}
		else if(xRotatedPosition.x<m_xInnerBox.MinX)
		{
			fVolume *= (m_xOuterBox.MinX-xRotatedPosition.x)/(m_xOuterBox.MinX-m_xInnerBox.MinX);
		}
	
		if(xRotatedPosition.y>m_xInnerBox.MaxY)
		{
			fVolume *= (m_xOuterBox.MaxY-xRotatedPosition.y)/(m_xOuterBox.MaxY-m_xInnerBox.MaxY);
		}
		else if(xRotatedPosition.y<m_xInnerBox.MinY)
		{
			fVolume *= (m_xOuterBox.MinY-xRotatedPosition.y)/(m_xOuterBox.MinY-m_xInnerBox.MinY);
		}

		if(xRotatedPosition.z>m_xInnerBox.MaxZ)
		{
			fVolume *= (m_xOuterBox.MaxZ-xRotatedPosition.z)/(m_xOuterBox.MaxZ-m_xInnerBox.MaxZ);
		}
		else if(xRotatedPosition.z<m_xInnerBox.MinZ)
		{
			fVolume *= (m_xOuterBox.MinZ-xRotatedPosition.z)/(m_xOuterBox.MinZ-m_xInnerBox.MinZ);
		}

		return fVolume;
	}

}

#ifdef ASURA_TOOLS
bool Asura_StreamingBackgroundSound::IsMemberOfCurrentLightSet() const
{
	return Tools_LightSets::IsMemberOfCurrentLightSets(m_uLightSets);
}
#endif //ASURA_TOOLS


bool Asura_StreamingBackgroundSound::Contains(const Asura_Vector_3& xPos) const
{
	bool bInMainVolume;
	
	if(IsSphere())
	{
		float fDistSquared = (xPos-m_xPosition).MagnitudeSquared();
		bInMainVolume =  fDistSquared <= m_fOuterRadius*m_fOuterRadius;
	}	
	else if(IsCylinder())
	{
		//get the position relative to the cylinder centre
		Asura_Vector_3 xRelativePos = xPos - m_xPosition;

		if(m_xOrient.w != 1.0f)
		{
			//rotate into the space of the volume
			Asura_Quat xOrientInverse = m_xOrient;
    	    xOrientInverse.Invert();
			xRelativePos = xOrientInverse.RotateVector(xRelativePos);
		}

		//check against height
		const float fTwiceDistAlongAxis = Asura_Maths::Abs(xRelativePos.y)*2.0f;
		bInMainVolume = (fTwiceDistAlongAxis <= m_fOuterHeight);

		if(bInMainVolume)
		{
			//check against radius
			xRelativePos.y = 0.0f;
			const float fDistFromAxisSquared = xRelativePos.MagnitudeSquared();
	
			bInMainVolume =  fDistFromAxisSquared <= m_fOuterRadius*m_fOuterRadius;
		}

	}
	else
	{
		if(m_xOrient.w != 1.0f)
		{
			//need to get position relative to volume
			Asura_Vector_3 xCentre = m_xOuterBox.CalculateCentre();
			Asura_Vector_3 xRelative = xPos - xCentre;

			//and rotate into the space of the volume
			Asura_Quat xOrientInverse = m_xOrient;
    	    xOrientInverse.Invert();
			xRelative = xOrientInverse.RotateVector(xRelative);

			Asura_Vector_3 xRotatedPosition = xRelative + xCentre;

			bInMainVolume = m_xOuterBox.Contains(xRotatedPosition);
		}
		else
		{
			bInMainVolume = m_xOuterBox.Contains(xPos);
		}
	}

	if(!bInMainVolume) return false;

	if(m_pxClipVolume && (m_uFlags & ASURA_STREAMINGBACKGROUNDSOUNDFLAG_USECLIPVOLUME))
	{
		bool bInClipVolume = m_pxClipVolume->Contains(xPos);
		if(m_uFlags & ASURA_STREAMINGBACKGROUNDSOUNDFLAG_INVERTCLIPVOLUME)
		{
			return !bInClipVolume;
		}
		else
		{
			return bInClipVolume;
		}
	}
	return true;
}


#ifdef ASURA_TOOLS
void Asura_StreamingBackgroundSound::ApplyChangesForTemplateInstance(Tools_EntityTemplateInstance* pxInstance)
{
	//The BB's for the sound behave in a similar manner to Asura_OrientedVolume,except that the orientaiton is
	//shared between them.
	//Use a temporary Asura_OrientedVolume to do the transforms , since the default one for a bounding box
	//wouldn't get the correct result.
	Asura_OrientedVolume xTemp;

	xTemp.m_xBox = m_xInnerBox;
	xTemp.m_xOrient = m_xOrient;
	xTemp = pxInstance->TransformOrientedVolume(xTemp);
	m_xInnerBox = xTemp.m_xBox;

	xTemp.m_xBox = m_xOuterBox;
	xTemp.m_xOrient = m_xOrient;
	xTemp = pxInstance->TransformOrientedVolume(xTemp);
	m_xOuterBox = xTemp.m_xBox;
	m_xOrient = xTemp.m_xOrient;

	m_xPosition = pxInstance->TransformPosition(m_xPosition);
	m_uGuid = pxInstance->ConvertGuid(m_uGuid);

	if(m_pxClipVolume)
	{
		m_pxClipVolume->ApplyChangesForTemplateInstance(pxInstance);
	}
}
#endif // ASURA_TOOLS
