diff --git a/Engine/source/T3D/assets/SoundAsset.cpp b/Engine/source/T3D/assets/SoundAsset.cpp index 45f78bc48..06a30b7e4 100644 --- a/Engine/source/T3D/assets/SoundAsset.cpp +++ b/Engine/source/T3D/assets/SoundAsset.cpp @@ -218,6 +218,9 @@ bool SoundAsset::loadSound() { Con::errorf("SoundAsset::initializeAsset: Attempted to load file %s but it was not valid!", mSoundFile); mLoadedState = BadFileReference; + mSFXProfile.setDescription(NULL); + mSFXProfile.setSoundFileName(StringTable->insert(StringTable->EmptyString())); + mSFXProfile.setPreload(false); return false; } else @@ -257,7 +260,7 @@ StringTableEntry SoundAsset::getAssetIdByFileName(StringTableEntry fileName) if (fileName == StringTable->EmptyString()) return StringTable->EmptyString(); - StringTableEntry materialAssetId = ""; + StringTableEntry soundAssetId = StringTable->EmptyString(); AssetQuery query; U32 foundCount = AssetDatabase.findAssetType(&query, "SoundAsset"); @@ -268,7 +271,7 @@ StringTableEntry SoundAsset::getAssetIdByFileName(StringTableEntry fileName) SoundAsset* soundAsset = AssetDatabase.acquireAsset(query.mAssetList[i]); if (soundAsset && soundAsset->getSoundPath() == fileName) { - materialAssetId = soundAsset->getAssetId(); + soundAssetId = soundAsset->getAssetId(); AssetDatabase.releaseAsset(query.mAssetList[i]); break; } @@ -276,7 +279,7 @@ StringTableEntry SoundAsset::getAssetIdByFileName(StringTableEntry fileName) } } - return materialAssetId; + return soundAssetId; } U32 SoundAsset::getAssetById(StringTableEntry assetId, AssetPtr* soundAsset) @@ -330,8 +333,8 @@ DefineEngineMethod(SoundAsset, getSoundPath, const char*, (), , "") } DefineEngineMethod(SoundAsset, playSound, S32, (Point3F position), (Point3F::Zero), - "Gets the number of materials for this shape asset.\n" - "@return Material count.\n") + "Plays the sound for this asset.\n" + "@return (sound plays).\n") { if (object->getSfxProfile()) { diff --git a/Engine/source/T3D/fx/explosion.cpp b/Engine/source/T3D/fx/explosion.cpp index 88a5a9432..d73d1bc01 100644 --- a/Engine/source/T3D/fx/explosion.cpp +++ b/Engine/source/T3D/fx/explosion.cpp @@ -859,9 +859,6 @@ bool ExplosionData::preload(bool server, String &errorStr) if (Parent::preload(server, errorStr) == false) return false; - if (!server && !getSoundProfile()) - return false; - if( !server ) { @@ -870,12 +867,18 @@ bool ExplosionData::preload(bool server, String &errorStr) _setSound(getSound()); if (!getSoundProfile()) + { Con::errorf(ConsoleLogEntry::General, "SplashData::preload: Cant get an sfxProfile for splash."); + return false; + } } if (!particleEmitter && particleEmitterId != 0) if (Sim::findObject(particleEmitterId, particleEmitter) == false) + { Con::errorf(ConsoleLogEntry::General, "Error, unable to load particle emitter for explosion datablock"); + return false; + } } if (mExplosionShapeAsset.notNull()) { diff --git a/Engine/source/T3D/gameBase/gameConnection.cpp b/Engine/source/T3D/gameBase/gameConnection.cpp index f759a03da..0331e3764 100644 --- a/Engine/source/T3D/gameBase/gameConnection.cpp +++ b/Engine/source/T3D/gameBase/gameConnection.cpp @@ -1562,33 +1562,50 @@ void GameConnection::packetDropped(PacketNotify *note) //---------------------------------------------------------------------------- -void GameConnection::play2D(SFXProfile* profile) +void GameConnection::play2D(StringTableEntry assetId) { - postNetEvent(new Sim2DAudioEvent(profile)); + if (AssetDatabase.isDeclaredAsset(assetId)) + { + + AssetPtr tempSoundAsset; + tempSoundAsset = assetId; + + postNetEvent(new SimSoundAssetEvent(tempSoundAsset)); + + } } -void GameConnection::play3D(SFXProfile* profile, const MatrixF *transform) +void GameConnection::play3D(StringTableEntry assetId, const MatrixF *transform) { if ( !transform ) - play2D(profile); + play2D(assetId); - else if ( !mControlObject ) - postNetEvent(new Sim3DAudioEvent(profile,transform)); - - else + if (AssetDatabase.isDeclaredAsset(assetId)) { - // TODO: Maybe improve this to account for the duration - // of the sound effect and if the control object can get - // into hearing range within time? - // Only post the event if it's within audible range - // of the control object. - Point3F ear,pos; - transform->getColumn(3,&pos); - mControlObject->getTransform().getColumn(3,&ear); - if ((ear - pos).len() < profile->getDescription()->mMaxDistance) - postNetEvent(new Sim3DAudioEvent(profile,transform)); - } + AssetPtr tempSoundAsset; + tempSoundAsset = assetId; + + if (!mControlObject) + postNetEvent(new SimSoundAssetEvent(tempSoundAsset, transform)); + else + { + // TODO: Maybe improve this to account for the duration + // of the sound effect and if the control object can get + // into hearing range within time? + + // Only post the event if it's within audible range + // of the control object. + tempSoundAsset->getSfxDescription(); + Point3F ear, pos; + transform->getColumn(3, &pos); + mControlObject->getTransform().getColumn(3, &ear); + if ((ear - pos).len() < tempSoundAsset->getSfxDescription()->mMaxDistance) + postNetEvent(new SimSoundAssetEvent(tempSoundAsset, transform)); + } + + } + } void GameConnection::doneScopingScene() @@ -2010,49 +2027,49 @@ DefineEngineMethod( GameConnection, isControlObjectRotDampedCamera, bool, (),, return object->isControlObjectRotDampedCamera(); } -DefineEngineMethod( GameConnection, play2D, bool, (SFXProfile* profile),, +DefineEngineMethod( GameConnection, play2D, bool, (StringTableEntry assetId),, "@brief Used on the server to play a 2D sound that is not attached to any object.\n\n" - "@param profile The SFXProfile that defines the sound to play.\n\n" + "@param assetID The SoundAsset ID that defines the sound to play.\n" "@tsexample\n" - "function ServerPlay2D(%profile)\n" + "function ServerPlay2D(%assetId)\n" "{\n" - " // Play the given sound profile on every client.\n" + " // Play the given sound asset on every client.\n" " // The sounds will be transmitted as an event, not attached to any object.\n" " for(%idx = 0; %idx < ClientGroup.getCount(); %idx++)\n" - " ClientGroup.getObject(%idx).play2D(%profile);\n" + " ClientGroup.getObject(%idx).play2D(%assetId);\n" "}\n" "@endtsexample\n\n") { - if(!profile) + if(assetId == StringTable->EmptyString()) return false; - object->play2D(profile); + object->play2D(assetId); return true; } -DefineEngineMethod( GameConnection, play3D, bool, (SFXProfile* profile, TransformF location),, +DefineEngineMethod( GameConnection, play3D, bool, (StringTableEntry assetId, TransformF location),, "@brief Used on the server to play a 3D sound that is not attached to any object.\n\n" - "@param profile The SFXProfile that defines the sound to play.\n" + "@param assetID The SoundAsset ID that defines the sound to play.\n" "@param location The position and orientation of the 3D sound given in the form of \"x y z ax ay az aa\".\n\n" "@tsexample\n" - "function ServerPlay3D(%profile,%transform)\n" + "function ServerPlay3D(%assetId,%transform)\n" "{\n" - " // Play the given sound profile at the given position on every client\n" + " // Play the given sound asset at the given position on every client\n" " // The sound will be transmitted as an event, not attached to any object.\n" " for(%idx = 0; %idx < ClientGroup.getCount(); %idx++)\n" - " ClientGroup.getObject(%idx).play3D(%profile,%transform);\n" + " ClientGroup.getObject(%idx).play3D(%assetID,%transform);\n" "}\n" "@endtsexample\n\n") { - if(!profile) + if(assetId == StringTable->EmptyString()) return false; MatrixF mat = location.getMatrix(); - object->play3D(profile,&mat); + object->play3D(assetId,&mat); return true; } diff --git a/Engine/source/T3D/gameBase/gameConnection.h b/Engine/source/T3D/gameBase/gameConnection.h index c735fb62c..64cdb3cbd 100644 --- a/Engine/source/T3D/gameBase/gameConnection.h +++ b/Engine/source/T3D/gameBase/gameConnection.h @@ -352,8 +352,8 @@ public: /// @name Sound /// @{ - void play2D(SFXProfile *profile); - void play3D(SFXProfile *profile, const MatrixF *transform); + void play2D(StringTableEntry assetId); + void play3D(StringTableEntry assetId, const MatrixF *transform); /// @} /// @name Misc. diff --git a/Engine/source/T3D/gameBase/gameConnectionEvents.cpp b/Engine/source/T3D/gameBase/gameConnectionEvents.cpp index 4aff2ce64..d2c3badce 100644 --- a/Engine/source/T3D/gameBase/gameConnectionEvents.cpp +++ b/Engine/source/T3D/gameBase/gameConnectionEvents.cpp @@ -47,6 +47,7 @@ //-------------------------------------------------------------------------- IMPLEMENT_CO_CLIENTEVENT_V1(SimDataBlockEvent); +IMPLEMENT_CO_CLIENTEVENT_V1(SimSoundAssetEvent); IMPLEMENT_CO_CLIENTEVENT_V1(Sim2DAudioEvent); IMPLEMENT_CO_CLIENTEVENT_V1(Sim3DAudioEvent); IMPLEMENT_CO_CLIENTEVENT_V1(SetMissionCRCEvent); @@ -293,6 +294,104 @@ void SimDataBlockEvent::process(NetConnection *cptr) //---------------------------------------------------------------------------- +static F32 SoundPosAccuracy = 0.5; +static S32 SoundRotBits = 8; + +SimSoundAssetEvent::SimSoundAssetEvent(AssetPtr asset, const MatrixF* mat) +{ + // cant get here unless the asset is declared. + mAsset = asset; + + if (mat) + mTransform = *mat; +} + +void SimSoundAssetEvent::pack(NetConnection* con, BitStream* stream) +{ + NetStringHandle assetIdStr = mAsset->getAssetId(); + con->packNetStringHandleU(stream, assetIdStr); + + // only stream if this is a 3d sound asset. + if (mAsset->is3D()) + { + SFXDescription* ad = mAsset->getSfxDescription(); + if (stream->writeFlag(ad->mConeInsideAngle || ad->mConeOutsideAngle)) + { + QuatF q(mTransform); + q.normalize(); + + // LH - we can get a valid quat that's very slightly over 1 in and so + // this fails (barely) check against zero. So use some error- + AssertFatal((1.0 - ((q.x * q.x) + (q.y * q.y) + (q.z * q.z))) >= (0.0 - 0.001), + "QuatF::normalize() is broken in Sim3DAudioEvent"); + + stream->writeSignedFloat(q.x, SoundRotBits); + stream->writeSignedFloat(q.y, SoundRotBits); + stream->writeSignedFloat(q.z, SoundRotBits); + stream->writeFlag(q.w < 0.0); + } + + Point3F pos; + mTransform.getColumn(3, &pos); + stream->writeCompressedPoint(pos, SoundPosAccuracy); + } + +} + +void SimSoundAssetEvent::write(NetConnection* con, BitStream* stream) +{ + // Just do the normal pack... + pack(con, stream); +} + +void SimSoundAssetEvent::unpack(NetConnection* con, BitStream* stream) +{ + + StringTableEntry temp = StringTable->insert(con->unpackNetStringHandleU(stream).getString()); + if (AssetDatabase.isDeclaredAsset(temp)) + { + AssetPtr tempSoundAsset; + tempSoundAsset = temp; + + mAsset = temp; + } + + if (mAsset->is3D()) + { + if (stream->readFlag()) { + QuatF q; + q.x = stream->readSignedFloat(SoundRotBits); + q.y = stream->readSignedFloat(SoundRotBits); + q.z = stream->readSignedFloat(SoundRotBits); + F32 value = ((q.x * q.x) + (q.y * q.y) + (q.z * q.z)); + // #ifdef __linux + // Hmm, this should never happen, but it does... + if (value > 1.f) + value = 1.f; + // #endif + q.w = mSqrt(1.f - value); + if (stream->readFlag()) + q.w = -q.w; + q.setMatrix(&mTransform); + } + else + mTransform.identity(); + + Point3F pos; + stream->readCompressedPoint(&pos, SoundPosAccuracy); + mTransform.setColumn(3, pos); + } +} + +void SimSoundAssetEvent::process(NetConnection* con) +{ + + if (mAsset->is3D()) + SFX->playOnce(mAsset->getSfxProfile(), &mTransform); + else + SFX->playOnce(mAsset->getSfxProfile()); + +} Sim2DAudioEvent::Sim2DAudioEvent(SFXProfile *profile) { @@ -321,11 +420,6 @@ void Sim2DAudioEvent::process(NetConnection *) SFX->playOnce( mProfile ); } -//---------------------------------------------------------------------------- - -static F32 SoundPosAccuracy = 0.5; -static S32 SoundRotBits = 8; - Sim3DAudioEvent::Sim3DAudioEvent(SFXProfile *profile,const MatrixF* mat) { mProfile = profile; diff --git a/Engine/source/T3D/gameBase/gameConnectionEvents.h b/Engine/source/T3D/gameBase/gameConnectionEvents.h index b9652a940..ede198645 100644 --- a/Engine/source/T3D/gameBase/gameConnectionEvents.h +++ b/Engine/source/T3D/gameBase/gameConnectionEvents.h @@ -39,6 +39,9 @@ #include "core/stream/bitStream.h" #endif +#include "T3D/assets/SoundAsset.h" + + class QuitEvent : public SimEvent { @@ -102,6 +105,23 @@ class SimDataBlockEvent : public NetEvent DECLARE_CATEGORY( "Game Networking" ); }; +class SimSoundAssetEvent : public NetEvent +{ +private: + AssetPtr mAsset; + MatrixF mTransform; + +public: + typedef NetEvent Parent; + + SimSoundAssetEvent(AssetPtr asset = NULL, const MatrixF* mat = NULL); + void pack(NetConnection*, BitStream* bstream); + void write(NetConnection*, BitStream* bstream); + void unpack(NetConnection*, BitStream* bstream); + void process(NetConnection*); + DECLARE_CONOBJECT(SimSoundAssetEvent); +}; + class Sim2DAudioEvent: public NetEvent { private: diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index 4730ee6b7..dab20b693 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -1009,7 +1009,7 @@ ShapeBase::ShapeBase() for (i = 0; i < MaxSoundThreads; i++) { mSoundThread[i].play = false; - mSoundThread[i].profile = 0; + mSoundThread[i].asset = 0; mSoundThread[i].sound = 0; } @@ -2233,24 +2233,30 @@ void ShapeBase::applyImpulse(const Point3F&,const VectorF&) //---------------------------------------------------------------------------- -void ShapeBase::playAudio(U32 slot,SFXTrack* profile) +void ShapeBase::playAudio(U32 slot, StringTableEntry assetId) { AssertFatal( slot < MaxSoundThreads, "ShapeBase::playAudio() bad slot index" ); - Sound& st = mSoundThread[slot]; - if( profile && ( !st.play || st.profile != profile ) ) + if (AssetDatabase.isDeclaredAsset(assetId)) + { + AssetPtr tempSoundAsset; + tempSoundAsset = assetId; + + SoundThread& st = mSoundThread[slot]; + if (tempSoundAsset && (!st.play || st.asset != tempSoundAsset)) { setMaskBits(SoundMaskN << slot); st.play = true; - st.profile = profile; + st.asset = tempSoundAsset; updateAudioState(st); } + } } void ShapeBase::stopAudio(U32 slot) { AssertFatal( slot < MaxSoundThreads, "ShapeBase::stopAudio() bad slot index" ); - Sound& st = mSoundThread[slot]; + SoundThread& st = mSoundThread[slot]; if ( st.play ) { st.play = false; @@ -2263,7 +2269,7 @@ void ShapeBase::updateServerAudio() { // Timeout non-looping sounds for (S32 i = 0; i < MaxSoundThreads; i++) { - Sound& st = mSoundThread[i]; + SoundThread& st = mSoundThread[i]; if (st.play && st.timeout && st.timeout < Sim::getCurrentTime()) { clearMaskBits(SoundMaskN << i); st.play = false; @@ -2271,17 +2277,18 @@ void ShapeBase::updateServerAudio() } } -void ShapeBase::updateAudioState(Sound& st) +void ShapeBase::updateAudioState(SoundThread& st) { SFX_DELETE( st.sound ); - if ( st.play && st.profile ) + if ( st.play && st.asset ) { if ( isGhost() ) { - if ( Sim::findObject( SimObjectId((uintptr_t)st.profile), st.profile ) ) + // if asset is valid, play + if (st.asset->isAssetValid() ) { - st.sound = SFX->createSource( st.profile, &getTransform() ); + st.sound = SFX->createSource( st.asset->getSfxProfile() , &getTransform() ); if ( st.sound ) st.sound->play(); } @@ -2292,12 +2299,17 @@ void ShapeBase::updateAudioState(Sound& st) { // Non-looping sounds timeout on the server st.timeout = 0; - if ( !st.profile->getDescription()->mIsLooping ) + if ( !st.asset->getSfxDescription()->mIsLooping ) st.timeout = Sim::getCurrentTime() + sAudioTimeout; } } else + { + // st.sound was not stopped before. If this causes issues remove. st.play = false; + if (st.sound) + st.sound->stop(); + } } void ShapeBase::updateAudioPos() @@ -3122,13 +3134,15 @@ U32 ShapeBase::packUpdate(NetConnection *con, U32 mask, BitStream *stream) if (stream->writeFlag(mask & SoundMask)) { for (S32 i = 0; i < MaxSoundThreads; i++) { - Sound& st = mSoundThread[i]; + SoundThread& st = mSoundThread[i]; if (stream->writeFlag(mask & (SoundMaskN << i))) if (stream->writeFlag(st.play)) - stream->writeRangedU32(st.profile->getId(),DataBlockObjectIdFirst, - DataBlockObjectIdLast); + { + NetStringHandle assetIdStr = st.asset->getAssetId(); + con->packNetStringHandleU(stream, assetIdStr); } } + } if (stream->writeFlag(mask & ImageMask)) { for (S32 i = 0; i < MaxMountedImages; i++) @@ -3242,12 +3256,18 @@ void ShapeBase::unpackUpdate(NetConnection *con, BitStream *stream) { if ( stream->readFlag() ) { - Sound& st = mSoundThread[i]; + SoundThread& st = mSoundThread[i]; st.play = stream->readFlag(); if ( st.play ) { - st.profile = (SFXTrack*)(uintptr_t)stream->readRangedU32( DataBlockObjectIdFirst, - DataBlockObjectIdLast ); + StringTableEntry temp = StringTable->insert(con->unpackNetStringHandleU(stream).getString()); + if (AssetDatabase.isDeclaredAsset(temp)) + { + AssetPtr tempSoundAsset; + tempSoundAsset = temp; + + st.asset = temp; + } } if ( isProperlyAdded() ) @@ -3777,7 +3797,7 @@ DefineEngineMethod( ShapeBase, isHidden, bool, (),, } //---------------------------------------------------------------------------- -DefineEngineMethod( ShapeBase, playAudio, bool, ( S32 slot, SFXTrack* track ),, +DefineEngineMethod( ShapeBase, playAudio, bool, ( S32 slot, StringTableEntry assetId),, "@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 @@ -3786,8 +3806,8 @@ DefineEngineMethod( ShapeBase, playAudio, bool, ( S32 slot, SFXTrack* track ),, "@see stopAudio()\n") { - if (track && slot >= 0 && slot < ShapeBase::MaxSoundThreads) { - object->playAudio(slot,track); + if (assetId && slot >= 0 && slot < ShapeBase::MaxSoundThreads) { + object->playAudio(slot, assetId); return true; } return false; diff --git a/Engine/source/T3D/shapeBase.h b/Engine/source/T3D/shapeBase.h index 920289385..6ef0fd985 100644 --- a/Engine/source/T3D/shapeBase.h +++ b/Engine/source/T3D/shapeBase.h @@ -265,6 +265,7 @@ struct ShapeBaseImageData: public GameBaseData { F32 emitterTime; ///< S32 emitterNode[MaxShapes]; ///< Node ID on the shape to emit from SoundAsset* sound; + SFXTrack* soundTrack; /// asset; ///< Asset on server SFXSource* sound; ///< Sound on client }; - Sound mSoundThread[MaxSoundThreads]; + SoundThread mSoundThread[MaxSoundThreads]; /// @} /// @name Scripted Animation Threads @@ -1114,7 +1115,7 @@ protected: /// Updates the audio state of the supplied sound /// @param st Sound - void updateAudioState(Sound& st); + void updateAudioState(SoundThread& st); /// Recalculates the spacial sound based on the current position of the object /// emitting the sound. @@ -1328,9 +1329,7 @@ public: /// Plays an audio sound from a mounted object /// @param slot Mount slot ID - /// @param track Audio track to play - void playAudio(U32 slot,SFXTrack* track); - void playAudio( U32 slot, SFXProfile* profile ) { playAudio( slot, ( SFXTrack* ) profile ); } + void playAudio(U32 slot, StringTableEntry assetId); /// Stops audio from a mounted object /// @param slot Mount slot ID diff --git a/Engine/source/T3D/shapeImage.cpp b/Engine/source/T3D/shapeImage.cpp index ee5d62476..88d5f837c 100644 --- a/Engine/source/T3D/shapeImage.cpp +++ b/Engine/source/T3D/shapeImage.cpp @@ -133,6 +133,7 @@ ShapeBaseImageData::StateData::StateData() spin = IgnoreSpin; recoil = NoRecoil; sound = NULL; + soundTrack = NULL; emitter = NULL; shapeSequence = NULL; shapeSequenceScale = true; @@ -372,6 +373,19 @@ bool ShapeBaseImageData::onAdd() //_setstateSound(getstateSound(i),i); s.sound = getstateSoundAsset(i); + if (s.sound == NULL && mstateSoundName[i] != StringTable->EmptyString()) + { + //ok, so we've got some sort of special-case here like a fallback or SFXPlaylist. So do the hook-up now + SFXTrack* sndTrack; + if (!Sim::findObject(mstateSoundName[i], sndTrack)) + { + Con::errorf("ShapeBaseImageData::onAdd() - attempted to find sound %s but failed!", mstateSoundName[i]); + } + else + { + s.soundTrack = sndTrack; + } + } s.script = stateScript[i]; s.emitter = stateEmitter[i]; s.emitterTime = stateEmitterTime[i]; @@ -2580,7 +2594,7 @@ bool ShapeBase::hasImageState(U32 imageSlot, const char* state) return false; } -void ShapeBase::setImageState(U32 imageSlot, U32 newState,bool force) +void ShapeBase::setImageState(U32 imageSlot, U32 newState, bool force) { if (!mMountedImageList[imageSlot].dataBlock) return; @@ -2611,12 +2625,12 @@ void ShapeBase::setImageState(U32 imageSlot, U32 newState,bool force) // Eject shell casing on every state change (client side only) ShapeBaseImageData::StateData& nextStateData = image.dataBlock->state[newState]; if (isGhost() && nextStateData.ejectShell) { - ejectShellCasing( imageSlot ); + ejectShellCasing(imageSlot); } // Shake camera on client. if (isGhost() && nextStateData.fire && image.dataBlock->shakeCamera) { - shakeCamera( imageSlot ); + shakeCamera(imageSlot); } // Server must animate the shape if it is a firestate... @@ -2632,12 +2646,12 @@ void ShapeBase::setImageState(U32 imageSlot, U32 newState,bool force) if (!force && image.state == &image.dataBlock->state[newState]) { image.delayTime = image.state->timeoutValue; if (image.state->script && !isGhost()) - scriptCallback(imageSlot,image.state->script); + scriptCallback(imageSlot, image.state->script); // If this is a flash sequence, we need to select a new position for the // animation if we're returning to that state... F32 randomPos = Platform::getRandom(); - for (U32 i=0; ishapeIsValid[i] || (i != imageShapeIndex && !image.doAnimateAllShapes)) continue; @@ -2665,7 +2679,7 @@ void ShapeBase::setImageState(U32 imageSlot, U32 newState,bool force) // Mount pending images if (image.nextImage != InvalidImagePtr && stateData.allowImageChange) { - setImage(imageSlot,image.nextImage,image.nextSkinNameHandle,image.nextLoaded); + setImage(imageSlot, image.nextImage, image.nextSkinNameHandle, image.nextLoaded); return; } @@ -2673,16 +2687,16 @@ void ShapeBase::setImageState(U32 imageSlot, U32 newState,bool force) // (the first key frame should be it's off state). // We need to do this across all image shapes to make sure we have no hold overs when switching // rendering shapes while in the middle of a state change. - for (U32 i=0; igetSequence()->isCyclic() && (stateData.sequenceNeverTransition || !(stateData.sequenceTransitionIn || lastState->sequenceTransitionOut))) { - image.shapeInstance[i]->setPos(image.animThread[i],0); - image.shapeInstance[i]->setTimeScale(image.animThread[i],0); + image.shapeInstance[i]->setPos(image.animThread[i], 0); + image.shapeInstance[i]->setTimeScale(image.animThread[i], 0); } if (image.flashThread[i]) { - image.shapeInstance[i]->setPos(image.flashThread[i],0); - image.shapeInstance[i]->setTimeScale(image.flashThread[i],0); + image.shapeInstance[i]->setPos(image.flashThread[i], 0); + image.shapeInstance[i]->setTimeScale(image.flashThread[i], 0); } } @@ -2695,10 +2709,10 @@ void ShapeBase::setImageState(U32 imageSlot, U32 newState,bool force) if (image.delayTime <= 0 || !stateData.waitForTimeout) { if ((ns = stateData.transition.loaded[image.loaded]) != -1) { - setImageState(imageSlot,ns); + setImageState(imageSlot, ns); return; } - for (U32 i=0; isound && lastState->sound->getSfxProfile()->getDescription()->mIsLooping) { - for(Vector::iterator i = image.mSoundSources.begin(); i != image.mSoundSources.end(); i++) + for (Vector::iterator i = image.mSoundSources.begin(); i != image.mSoundSources.end(); i++) SFX_DELETE((*i)); image.mSoundSources.clear(); } // Play sound - if( stateData.sound && isGhost() ) + if (isGhost()) + { + if (stateData.sound) { const Point3F& velocity = getVelocity(); - image.addSoundSource(SFX->createSource(stateData.sound->getSfxProfile(), &getRenderTransform(), &velocity )); + image.addSoundSource(SFX->createSource(stateData.sound->getSfxProfile(), &getRenderTransform(), &velocity)); + } + if (stateData.soundTrack) + { + const Point3F& velocity = getVelocity(); + image.addSoundSource(SFX->createSource(stateData.soundTrack, &getRenderTransform(), &velocity)); + } } // Play animation diff --git a/Engine/source/sfx/sfxPlayList.cpp b/Engine/source/sfx/sfxPlayList.cpp index 0e56d4607..ea186c125 100644 --- a/Engine/source/sfx/sfxPlayList.cpp +++ b/Engine/source/sfx/sfxPlayList.cpp @@ -352,15 +352,19 @@ bool SFXPlayList::preload( bool server, String& errorStr ) { for( U32 i = 0; i < NUM_SLOTS; ++ i ) { - _setTrack(getTrack(i),i); + StringTableEntry track = getTrack(i); + if (track != StringTable->EmptyString()) + { + _setTrack(getTrack(i), i); if (!getTrackProfile(i)) { Con::errorf("SFXPlayList::Preload() - unable to find sfxProfile for asset %s", mTrackAssetId[i]); return false; } - if( !sfxResolve( &mSlots.mState[ i ], errorStr ) ) + if (!sfxResolve(&mSlots.mState[i], errorStr)) return false; + } } } diff --git a/Engine/source/sfx/sfxProfile.cpp b/Engine/source/sfx/sfxProfile.cpp index 4cd6a8cdb..ab3bc44ab 100644 --- a/Engine/source/sfx/sfxProfile.cpp +++ b/Engine/source/sfx/sfxProfile.cpp @@ -35,6 +35,7 @@ #include "core/stream/bitStream.h" #include "core/resourceManager.h" #include "console/engineAPI.h" +#include "core/stream/fileStream.h" using namespace Torque; @@ -283,8 +284,13 @@ bool SFXProfile::_preloadBuffer() Resource& SFXProfile::getResource() { - if( !mResource && mFilename != StringTable->EmptyString()) - mResource = SFXResource::load( mFilename ); + char buf[1024]; + FileName fullFilename = String(Platform::makeFullPathName(mFilename, buf, sizeof(buf))); + + if (!mResource && SFXResource::exists(fullFilename)) + mResource = SFXResource::load(mFilename); + else + mResource = NULL; return mResource; } diff --git a/Engine/source/sfx/sfxSystem.cpp b/Engine/source/sfx/sfxSystem.cpp index 3d793995e..d23880bfd 100644 --- a/Engine/source/sfx/sfxSystem.cpp +++ b/Engine/source/sfx/sfxSystem.cpp @@ -1581,133 +1581,70 @@ DefineEngineFunction( sfxPlay, S32, ( const char * trackName, const char * point //----------------------------------------------------------------------------- static ConsoleDocFragment _sPlayOnce1( - "@brief Create a play-once source for the given @a track.\n\n" + "@brief Create a play-once source for the given @a asset.\n\n" "Once playback has finished, the source will be automatically deleted in the next sound system update.\n" "@param track The sound datablock.\n" "@return A newly created temporary source in \"Playing\" state or 0 if the operation failed.\n\n" "@ref SFXSource_playonce\n\n" "@ingroup SFX", NULL, - "SFXSource sfxPlayOnce( SFXTrack track );" + "SFXSource sfxPlayOnce( StringTableEntry assetID );" ); static ConsoleDocFragment _sPlayOnce2( - "@brief Create a play-once source for the given given @a track and position the source's 3D sound at the given coordinates " - "only if the track's description is set up for 3D sound).\n\n" + "@brief Create a play-once source for the given given @a asset and position the source's 3D sound at the given coordinates " + "only if the asset is set up for 3D sound).\n\n" "Once playback has finished, the source will be automatically deleted in the next sound system update.\n" - "@param track The sound datablock.\n" + "@param assetId The sound asset.\n" "@param x The X coordinate of the 3D sound position.\n" "@param y The Y coordinate of the 3D sound position.\n" "@param z The Z coordinate of the 3D sound position.\n" - "@param fadeInTime If >=0, this overrides the SFXDescription::fadeInTime value on the track's description.\n" + "@param fadeInTime If >=0, this overrides the SFXDescription::fadeInTime value on the asset's definition.\n" "@return A newly created temporary source in \"Playing\" state or 0 if the operation failed.\n\n" "@tsexample\n" - "// Immediately start playing the given track. Fade it in to full volume over 5 seconds.\n" - "sfxPlayOnce( MusicTrack, 0, 0, 0, 5.f );\n" + "// Immediately start playing the given asset. Fade it in to full volume over 5 seconds.\n" + "sfxPlayOnce( ExampleModule:MusicTrack, 0, 0, 0, 5.f );\n" "@endtsexample\n\n" "@ref SFXSource_playonce\n\n" "@ingroup SFX", NULL, - "SFXSource sfxPlayOnce( SFXTrack track, float x, float y, float z, float fadeInTime=-1 );" -); -static ConsoleDocFragment _sPlayOnce3( - "@brief Create a new temporary SFXProfile from the given @a description and @a filename, then create a play-once source " - "for it and start playback.\n\n" - "Once playback has finished, the source will be automatically deleted in the next sound system update. If not referenced " - "otherwise by then, the temporary SFXProfile will also be deleted.\n" - "@param description The description to use for playback.\n" - "@param filename Path to the sound file to play.\n" - "@return A newly created temporary source in \"Playing\" state or 0 if the operation failed.\n\n" - "@tsexample\n" - "// Play a sound effect file once.\n" - "sfxPlayOnce( AudioEffects, \"art/sound/weapons/Weapon_pickup\" );\n" - "@endtsexample\n\n" - "@ref SFXSource_playonce\n\n" - "@ingroup SFX", - NULL, - "SFXSource sfxPlayOnce( SFXDescription description, string filename );" -); -static ConsoleDocFragment _sPlayOnce4( - "@brief Create a new temporary SFXProfile from the given @a description and @a filename, then create a play-once source " - "for it and start playback. Position the source's 3D sound at the given coordinates (only if the description " - "is set up for 3D sound).\n\n" - "Once playback has finished, the source will be automatically deleted in the next sound system update. If not referenced " - "otherwise by then, the temporary SFXProfile will also be deleted.\n" - "@param description The description to use for playback.\n" - "@param filename Path to the sound file to play.\n" - "@param x The X coordinate of the 3D sound position.\n" - "@param y The Y coordinate of the 3D sound position.\n" - "@param z The Z coordinate of the 3D sound position.\n" - "@param fadeInTime If >=0, this overrides the SFXDescription::fadeInTime value on the track's description.\n" - "@return A newly created temporary source in \"Playing\" state or 0 if the operation failed.\n\n" - "@tsexample\n" - "// Play a sound effect file once using a 3D sound with a default falloff placed at the origin.\n" - "sfxPlayOnce( AudioDefault3D, \"art/sound/weapons/Weapon_pickup\", 0, 0, 0 );\n" - "@endtsexample\n\n" - "@ref SFXSource_playonce\n\n" - "@ingroup SFX", - NULL, - "SFXSource sfxPlayOnce( SFXDescription description, string filename, float x, float y, float z, float fadeInTime=-1 );" + "SFXSource sfxPlayOnce( StringTableEntry assetID, float x, float y, float z, float fadeInTime=-1 );" ); -DefineEngineFunction( sfxPlayOnce, S32, ( const char * sfxType, const char * arg0, const char * arg1, const char * arg2, const char * arg3, const char* arg4 ), ("", "", "", "", "-1.0f"), +DefineEngineFunction( sfxPlayOnce, S32, (StringTableEntry assetId, const char* arg0, const char * arg1, const char * arg2, const char * arg3 ), (StringTable->EmptyString(), "", "", "", "-1.0f"), "SFXSource sfxPlayOnce( ( SFXTrack track | SFXDescription description, string filename ) [, float x, float y, float z, float fadeInTime=-1 ] ) " "Create a new play-once source for the given profile or description+filename and start playback of the source.\n" "@hide" ) { SFXDescription* description = NULL; - SFXTrack* track = dynamic_cast< SFXTrack* >( Sim::findObject( sfxType ) ); - if( !track ) - { - description = dynamic_cast< SFXDescription* >( Sim::findObject( sfxType ) ); - if( !description ) + if (assetId == StringTable->EmptyString()) { - Con::errorf( "sfxPlayOnce - Unable to locate sound track/description '%s'", sfxType ); + Con::errorf( "sfxPlayOnce - Must Define a sound asset"); return 0; } - } SFXSource* source = NULL; - if( track ) + + if (AssetDatabase.isDeclaredAsset(assetId)) { - // In this overloaded use, arg0..arg2 are x, y, z, and arg3 is the fadeInTime. - if (String::isEmpty(arg0)) + + AssetPtr tempSoundAsset; + tempSoundAsset = assetId; + + if (String::isEmpty(arg0) || !tempSoundAsset->is3D()) { - source = SFX->playOnce( track ); + source = SFX->playOnce(tempSoundAsset->getSfxProfile()); } else { MatrixF transform; - transform.set( EulerF( 0, 0, 0 ), Point3F( dAtof( arg0 ), dAtof( arg1 ),dAtof( arg2 ) ) ); - source = SFX->playOnce( track, &transform, NULL, dAtof( arg3 ) ); - } + transform.set(EulerF(0, 0, 0), Point3F(dAtof(arg0), dAtof(arg1), dAtof(arg2))); + source = SFX->playOnce(tempSoundAsset->getSfxProfile(), &transform, NULL, dAtof(arg3)); } - else if( description ) - { - // In this overload, arg0 is the filename, arg1..arg3 are x, y, z, and arg4 is fadeInTime. - SFXProfile* tempProfile = new SFXProfile( description, StringTable->insert( arg0 ), true ); - if( !tempProfile->registerObject() ) - { - Con::errorf( "sfxPlayOnce - unable to create profile" ); - delete tempProfile; } else { - if (String::isEmpty(arg1)) - source = SFX->playOnce( tempProfile ); - else - { - MatrixF transform; - transform.set( EulerF( 0, 0, 0 ), Point3F( dAtof( arg1 ), dAtof( arg2 ),dAtof( arg3 ) ) ); - source = SFX->playOnce( tempProfile, &transform, NULL, dAtof( arg4 ) ); - } - - // Set profile to auto-delete when SFXSource releases its reference. - // Also add to root group so the profile will get deleted when the - // Sim system is shut down before the SFXSource has played out. - - tempProfile->setAutoDelete( true ); - Sim::getRootGroup()->addObject( tempProfile ); - } + Con::errorf("sfxPlayOnce - Could not locate assetId '%s'", assetId); + return 0; } if( !source )