2018-01-28 21:17:14 +00:00
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
# include "T3D/components/audio/SoundComponent.h"
# include "core/stream/bitStream.h"
# include "sim/netConnection.h"
# include "sfx/sfxSystem.h"
# include "sfx/sfxSource.h"
# include "sfx/sfxTrack.h"
# include "sfx/sfxDescription.h"
# include "T3D/sfx/sfx3DWorld.h"
# include "sfx/sfxTrack.h"
# include "sfx/sfxTypes.h"
# include "renderInstance/renderPassManager.h"
# include "gfx/gfxDrawUtil.h"
// Timeout for non-looping sounds on a channel
static SimTime sAudioTimeout = 500 ;
extern bool gEditingMission ;
//////////////////////////////////////////////////////////////////////////
// Constructor/Destructor
//////////////////////////////////////////////////////////////////////////
SoundComponent : : SoundComponent ( ) : Component ( )
{
//These flags inform that, in this particular component, we network down to the client, which enables the pack/unpackData functions to operate
mNetworked = true ;
mFriendlyName = " Sound(Component) " ;
mComponentType = " Sound " ;
mDescription = getDescriptionText ( " Stores up to 4 sounds for playback. " ) ;
for ( U32 slotNum = 0 ; slotNum < MaxSoundThreads ; slotNum + + ) {
mSoundThread [ slotNum ] . play = false ;
mSoundThread [ slotNum ] . profile = 0 ;
mSoundThread [ slotNum ] . sound = 0 ;
mSoundFile [ slotNum ] = NULL ;
mPreviewSound [ slotNum ] = false ;
mPlay [ slotNum ] = false ;
}
}
SoundComponent : : ~ SoundComponent ( )
{
}
IMPLEMENT_CO_NETOBJECT_V1 ( SoundComponent ) ;
//Standard onAdd function, for when the component is created
bool SoundComponent : : onAdd ( )
{
if ( ! Parent : : onAdd ( ) )
return false ;
for ( U32 slotNum = 0 ; slotNum < MaxSoundThreads ; slotNum + + )
mPreviewSound [ slotNum ] = false ;
return true ;
}
//Standard onRemove function, when the component object is deleted
void SoundComponent : : onRemove ( )
{
Parent : : onRemove ( ) ;
}
//This is called when the component has been added to an entity
void SoundComponent : : onComponentAdd ( )
{
Parent : : onComponentAdd ( ) ;
Con : : printf ( " We were added to an entity! SoundComponent reporting in for owner entity %i " , mOwner - > getId ( ) ) ;
}
//This is called when the component has been removed from an entity
void SoundComponent : : onComponentRemove ( )
{
Con : : printf ( " We were removed from our entity! SoundComponent signing off for owner entity %i " , mOwner - > getId ( ) ) ;
Parent : : onComponentRemove ( ) ;
}
//This is called any time a component is added to an entity. Every component currently owned by the entity is informed of the event.
//This allows you to do dependency behavior, like collisions being aware of a mesh component, etc
void SoundComponent : : componentAddedToOwner ( Component * comp )
{
for ( S32 slotNum = 0 ; slotNum < MaxSoundThreads ; slotNum + + )
{
if ( mPlay [ slotNum ] )
{
playAudio ( slotNum , mSoundFile [ slotNum ] ) ;
}
}
Con : : printf ( " Our owner entity has a new component being added! SoundComponent welcomes component %i of type %s " , comp - > getId ( ) , comp - > getClassRep ( ) - > getNameSpace ( ) ) ;
}
//This is called any time a component is removed from an entity. Every component current owned by the entity is informed of the event.
//This allows cleanup and dependency management.
void SoundComponent : : componentRemovedFromOwner ( Component * comp )
{
Con : : printf ( " Our owner entity has a removed a component! SoundComponent waves farewell to component %i of type %s " , comp - > getId ( ) , comp - > getClassRep ( ) - > getNameSpace ( ) ) ;
}
//Regular init persist fields function to set up static fields.
void SoundComponent : : initPersistFields ( )
{
//addArray("Sounds", MaxSoundThreads);
addField ( " mSoundFile " , TypeSFXTrackName , Offset ( mSoundFile , SoundComponent ) , MaxSoundThreads , " If the text will not fit in the control, the deniedSound is played. " ) ;
addProtectedField ( " mPreviewSound " , TypeBool , Offset ( mPreviewSound , SoundComponent ) ,
& _previewSound , & defaultProtectedGetFn , MaxSoundThreads , " Preview Sound " , AbstractClassRep : : FieldFlags : : FIELD_ComponentInspectors ) ;
addProtectedField ( " play " , TypeBool , Offset ( mPlay , SoundComponent ) ,
& _autoplay , & defaultProtectedGetFn , MaxSoundThreads , " Whether playback of the emitter's sound should start as soon as the emitter object is added to the level. \n "
" If this is true, the emitter will immediately start to play when the level is loaded. " ) ;
//endArray("Sounds");
Parent : : initPersistFields ( ) ;
}
bool SoundComponent : : _previewSound ( void * object , const char * index , const char * data )
{
U32 slotNum = ( index ! = NULL ) ? dAtoui ( index ) : 0 ;
SoundComponent * component = reinterpret_cast < SoundComponent * > ( object ) ;
if ( ! component - > mPreviewSound [ slotNum ] )
component - > playAudio ( slotNum , component - > mSoundFile [ slotNum ] ) ;
else
component - > stopAudio ( slotNum ) ;
component - > mPreviewSound [ slotNum ] = ! component - > mPreviewSound [ slotNum ] ;
return false ;
}
bool SoundComponent : : _autoplay ( void * object , const char * index , const char * data )
{
U32 slotNum = ( index ! = NULL ) ? dAtoui ( index ) : 0 ;
SoundComponent * component = reinterpret_cast < SoundComponent * > ( object ) ;
component - > mPlay [ slotNum ] = dAtoui ( data ) ;
if ( component - > mPlay [ slotNum ] )
component - > playAudio ( slotNum , component - > mSoundFile [ slotNum ] ) ;
else
component - > stopAudio ( slotNum ) ;
return false ;
}
U32 SoundComponent : : packUpdate ( NetConnection * con , U32 mask , BitStream * stream )
{
U32 retMask = Parent : : packUpdate ( con , mask , stream ) ;
if ( mask & InitialUpdateMask )
{
// mask off sounds that aren't playing
S32 slotNum ;
for ( slotNum = 0 ; slotNum < MaxSoundThreads ; slotNum + + )
if ( ! mSoundThread [ slotNum ] . play )
mask & = ~ ( SoundMaskN < < slotNum ) ;
}
for ( S32 slotNum = 0 ; slotNum < MaxSoundThreads ; slotNum + + )
stream - > writeFlag ( mPreviewSound [ slotNum ] ) ;
if ( stream - > writeFlag ( mask & SoundMask ) )
{
for ( S32 slotNum = 0 ; slotNum < MaxSoundThreads ; slotNum + + )
{
Sound & st = mSoundThread [ slotNum ] ;
if ( stream - > writeFlag ( mask & ( SoundMaskN < < slotNum ) ) )
{
if ( stream - > writeFlag ( st . play ) )
//stream->writeRangedU32(st.profile->getId(), DataBlockObjectIdFirst,
// DataBlockObjectIdLast);
stream - > writeString ( st . profile - > getName ( ) ) ;
}
}
}
return retMask ;
}
void SoundComponent : : unpackUpdate ( NetConnection * con , BitStream * stream )
{
Parent : : unpackUpdate ( con , stream ) ;
for ( S32 slotNum = 0 ; slotNum < MaxSoundThreads ; slotNum + + )
mPreviewSound [ slotNum ] = stream - > readFlag ( ) ;
if ( stream - > readFlag ( ) )
{
for ( S32 slotNum = 0 ; slotNum < MaxSoundThreads ; slotNum + + )
{
if ( stream - > readFlag ( ) )
{
Sound & st = mSoundThread [ slotNum ] ;
st . play = stream - > readFlag ( ) ;
if ( st . play )
{
//st.profile = (SFXTrack*)stream->readRangedU32(DataBlockObjectIdFirst,
// DataBlockObjectIdLast);
char profileName [ 255 ] ;
stream - > readString ( profileName ) ;
if ( ! Sim : : findObject ( profileName , st . profile ) )
Con : : errorf ( " Could not find SFXTrack " ) ;
}
//if (isProperlyAdded())
updateAudioState ( st ) ;
}
}
}
}
//This allows custom behavior in the event the owner is being edited
void SoundComponent : : onInspect ( )
{
}
//This allows cleanup of the custom editor behavior if our owner stopped being edited
void SoundComponent : : onEndInspect ( )
{
}
//Process tick update function, natch
void SoundComponent : : processTick ( )
{
Parent : : processTick ( ) ;
}
//Client-side advance function
void SoundComponent : : advanceTime ( F32 dt )
{
}
//Client-side interpolation function
void SoundComponent : : interpolateTick ( F32 delta )
{
}
void SoundComponent : : prepRenderImage ( SceneRenderState * state )
{
if ( ! mEnabled | | ! mOwner | | ! gEditingMission )
return ;
ObjectRenderInst * ri = state - > getRenderPass ( ) - > allocInst < ObjectRenderInst > ( ) ;
ri - > renderDelegate . bind ( this , & SoundComponent : : _renderObject ) ;
ri - > type = RenderPassManager : : RIT_Editor ;
ri - > defaultKey = 0 ;
ri - > defaultKey2 = 0 ;
state - > getRenderPass ( ) - > addInst ( ri ) ;
}
void SoundComponent : : _renderObject ( ObjectRenderInst * ri ,
SceneRenderState * state ,
BaseMatInstance * overrideMat )
{
if ( overrideMat )
return ;
GFXStateBlockDesc desc ;
desc . setBlend ( true ) ;
MatrixF camera = GFX - > getWorldMatrix ( ) ;
camera . inverse ( ) ;
Point3F pos = mOwner - > getPosition ( ) ;
for ( S32 slotNum = 0 ; slotNum < MaxSoundThreads ; slotNum + + )
{
if ( mPreviewSound [ slotNum ] )
{
Sound & st = mSoundThread [ slotNum ] ;
if ( st . sound & & st . sound - > getDescription ( ) )
{
F32 minRad = st . sound - > getDescription ( ) - > mMinDistance ;
F32 falloffRad = st . sound - > getDescription ( ) - > mMaxDistance ;
SphereF sphere ( pos , falloffRad ) ;
if ( sphere . isContained ( camera . getPosition ( ) ) )
desc . setCullMode ( GFXCullNone ) ;
GFX - > getDrawUtil ( ) - > drawSphere ( desc , minRad , pos , ColorI ( 255 , 0 , 255 , 64 ) ) ;
GFX - > getDrawUtil ( ) - > drawSphere ( desc , falloffRad , pos , ColorI ( 128 , 0 , 128 , 64 ) ) ;
}
}
}
}
void SoundComponent : : playAudio ( U32 slotNum , SFXTrack * _profile )
{
AssertFatal ( slotNum < MaxSoundThreads , " ShapeBase::playAudio() bad slot index " ) ;
SFXTrack * profile = ( _profile ! = NULL ) ? _profile : mSoundFile [ slotNum ] ;
Sound & st = mSoundThread [ slotNum ] ;
if ( profile & & ( ! st . play | | st . profile ! = profile ) )
{
setMaskBits ( SoundMaskN < < slotNum ) ;
st . play = true ;
st . profile = profile ;
updateAudioState ( st ) ;
}
}
void SoundComponent : : stopAudio ( U32 slotNum )
{
AssertFatal ( slotNum < MaxSoundThreads , " ShapeBase::stopAudio() bad slot index " ) ;
Sound & st = mSoundThread [ slotNum ] ;
if ( st . play )
{
st . play = false ;
setMaskBits ( SoundMaskN < < slotNum ) ;
updateAudioState ( st ) ;
}
}
void SoundComponent : : updateServerAudio ( )
{
// Timeout non-looping sounds
for ( S32 slotNum = 0 ; slotNum < MaxSoundThreads ; slotNum + + )
{
Sound & st = mSoundThread [ slotNum ] ;
if ( st . play & & st . timeout & & st . timeout < Sim : : getCurrentTime ( ) )
{
//clearMaskBits(SoundMaskN << slotNum);
st . play = false ;
}
}
}
void SoundComponent : : updateAudioState ( Sound & st )
{
SFX_DELETE ( st . sound ) ;
if ( st . play & & st . profile )
{
if ( isClientObject ( ) )
{
//if (Sim::findObject(SimObjectId((uintptr_t)st.profile), st.profile))
// {
2018-02-03 23:32:39 +00:00
MatrixF transform = mOwner - > getTransform ( ) ;
st . sound = SFX - > createSource ( st . profile , & transform ) ;
2018-01-28 21:17:14 +00:00
if ( st . sound )
st . sound - > play ( ) ;
//}
else
st . play = false ;
}
else
{
// Non-looping sounds timeout on the server
st . timeout = 0 ;
if ( ! st . profile - > getDescription ( ) - > mIsLooping )
st . timeout = Sim : : getCurrentTime ( ) + sAudioTimeout ;
}
}
else
st . play = false ;
}
void SoundComponent : : updateAudioPos ( )
{
for ( S32 slotNum = 0 ; slotNum < MaxSoundThreads ; slotNum + + )
{
SFXSource * source = mSoundThread [ slotNum ] . sound ;
if ( source )
source - > setTransform ( mOwner - > getTransform ( ) ) ;
}
}
//----------------------------------------------------------------------------
DefineEngineMethod ( SoundComponent , playAudio , bool , ( S32 slot , SFXTrack * track ) , ( 0 , nullAsType < SFXTrack * > ( ) ) ,
" @brief Attach a sound to this shape and start playing it. \n \n "
" @param slot Audio slot index for the sound (valid range is 0 - 3) \n " // 3 = ShapeBase::MaxSoundThreads-1
" @param track SFXTrack to play \n "
" @return true if the sound was attached successfully, false if failed \n \n "
" @see stopAudio() \n " )
{
if ( track & & slot > = 0 & & slot < SoundComponent : : MaxSoundThreads ) {
object - > playAudio ( slot , track ) ;
return true ;
}
return false ;
}
DefineEngineMethod ( SoundComponent , stopAudio , bool , ( S32 slot ) , ,
" @brief Stop a sound started with playAudio. \n \n "
" @param slot audio slot index (started with playAudio) \n "
" @return true if the sound was stopped successfully, false if failed \n \n "
" @see playAudio() \n " )
{
if ( slot > = 0 & & slot < SoundComponent : : MaxSoundThreads ) {
object - > stopAudio ( slot ) ;
return true ;
}
return false ;
}