diff --git a/Engine/source/T3D/assets/SoundAsset.cpp b/Engine/source/T3D/assets/SoundAsset.cpp index 14cb00744..d00c0dbe2 100644 --- a/Engine/source/T3D/assets/SoundAsset.cpp +++ b/Engine/source/T3D/assets/SoundAsset.cpp @@ -40,6 +40,10 @@ #include "assets/assetPtr.h" #endif +#ifndef _SFXSOURCE_H_ +#include "sfx/sfxSource.h" +#endif + // Debug Profiling. #include "platform/profiler.h" #include "sfx/sfxTypes.h" @@ -159,7 +163,7 @@ void SoundAsset::initPersistFields() addField("maxDistance", TypeF32, Offset(mProfileDesc.mMaxDistance, SoundAsset), "Max distance for sound."); addField("coneInsideAngle", TypeS32, Offset(mProfileDesc.mConeInsideAngle, SoundAsset), "Cone inside angle."); addField("coneOutsideAngle", TypeS32, Offset(mProfileDesc.mConeOutsideAngle, SoundAsset), "Cone outside angle."); - addField("coneOutsideVolume", TypeS32, Offset(mProfileDesc.mConeOutsideVolume, SoundAsset), "Cone outside volume."); + addField("coneOutsideVolume", TypeF32, Offset(mProfileDesc.mConeOutsideVolume, SoundAsset), "Cone outside volume."); addField("rolloffFactor", TypeF32, Offset(mProfileDesc.mRolloffFactor, SoundAsset), "Rolloff factor."); addField("scatterDistance", TypePoint3F, Offset(mProfileDesc.mScatterDistance, SoundAsset), "Randomization to the spacial position of the sound."); addField("sourceGroup", TypeSFXSourceName, Offset(mProfileDesc.mSourceGroup, SoundAsset), "Group that sources playing with this description should be put into."); @@ -181,13 +185,7 @@ void SoundAsset::initializeAsset(void) if (mSoundFile == StringTable->EmptyString()) return; - //ResourceManager::get().getChangedSignal.notify(this, &SoundAsset::_onResourceChanged); - - //Ensure our path is expando'd if it isn't already mSoundPath = getOwned() ? expandAssetFilePath(mSoundFile) : mSoundPath; - - mSoundPath = expandAssetFilePath(mSoundPath); - loadSound(); } @@ -208,7 +206,6 @@ void SoundAsset::onAssetRefresh(void) //Update mSoundPath = getOwned() ? expandAssetFilePath(mSoundFile) : mSoundPath; - loadSound(); } @@ -225,7 +222,7 @@ bool SoundAsset::loadSound() else {// = new SFXProfile(mProfileDesc, mSoundFile, mPreload); mSFXProfile.setDescription(&mProfileDesc); - mSFXProfile.setSoundFileName(mSoundFile); + mSFXProfile.setSoundFileName(mSoundPath); mSFXProfile.setPreload(mPreload); } @@ -254,11 +251,106 @@ void SoundAsset::setSoundFile(const char* pSoundFile) refreshAsset(); } +StringTableEntry SoundAsset::getAssetIdByFileName(StringTableEntry fileName) +{ + if (fileName == StringTable->EmptyString()) + return StringTable->EmptyString(); + + StringTableEntry materialAssetId = ""; + + AssetQuery query; + U32 foundCount = AssetDatabase.findAssetType(&query, "SoundAsset"); + if (foundCount != 0) + { + for (U32 i = 0; i < foundCount; i++) + { + SoundAsset* soundAsset = AssetDatabase.acquireAsset(query.mAssetList[i]); + if (soundAsset && soundAsset->getSoundPath() == fileName) + { + materialAssetId = soundAsset->getAssetId(); + AssetDatabase.releaseAsset(query.mAssetList[i]); + break; + } + AssetDatabase.releaseAsset(query.mAssetList[i]); + } + } + + return materialAssetId; +} + +U32 SoundAsset::getAssetById(StringTableEntry assetId, AssetPtr* materialAsset) +{ + (*materialAsset) = assetId; + + if (materialAsset->notNull()) + { + return (*materialAsset)->mLoadedState; + } + else + { + //Well that's bad, loading the fallback failed. + Con::warnf("MaterialAsset::getAssetById - Finding of asset with id %s failed with no fallback asset", assetId); + return AssetErrCode::Failed; + } +} + +U32 SoundAsset::getAssetByFileName(StringTableEntry fileName, AssetPtr* soundAsset) +{ + AssetQuery query; + U32 foundAssetcount = AssetDatabase.findAssetType(&query, "SoundAsset"); + if (foundAssetcount == 0) + { + //Well that's bad, loading the fallback failed. + Con::warnf("MaterialAsset::getAssetByMaterialName - Finding of asset associated with filename %s failed with no fallback asset", fileName); + return AssetErrCode::Failed; + } + else + { + for (U32 i = 0; i < foundAssetcount; i++) + { + SoundAsset* tSoundAsset = AssetDatabase.acquireAsset(query.mAssetList[i]); + if (tSoundAsset && tSoundAsset->getSoundPath() == fileName) + { + soundAsset->setAssetId(query.mAssetList[i]); + AssetDatabase.releaseAsset(query.mAssetList[i]); + return (*soundAsset)->mLoadedState; + } + AssetDatabase.releaseAsset(query.mAssetList[i]); //cleanup if that's not the one we needed + } + } + + //No good match + return AssetErrCode::Failed; +} + DefineEngineMethod(SoundAsset, getSoundPath, const char*, (), , "") { return object->getSoundPath(); } +DefineEngineMethod(SoundAsset, playSound, S32, (Point3F position), (Point3F::Zero), + "Gets the number of materials for this shape asset.\n" + "@return Material count.\n") +{ + if (object->getSfxProfile()) + { + MatrixF transform; + transform.setPosition(position); + SFXSource* source = SFX->playOnce(object->getSfxProfile(), &transform, NULL, -1); + return source->getId(); + } + else + return 0; +} + +#ifdef TORQUE_TOOLS +DefineEngineStaticMethod(SoundAsset, getAssetIdByFilename, const char*, (const char* filePath), (""), + "Queries the Asset Database to see if any asset exists that is associated with the provided file path.\n" + "@return The AssetId of the associated asset, if any.") +{ + return SoundAsset::getAssetIdByFileName(StringTable->insert(filePath)); +} +#endif IMPLEMENT_CONOBJECT(GuiInspectorTypeSoundAssetPtr); ConsoleDocClass(GuiInspectorTypeSoundAssetPtr, @@ -276,12 +368,63 @@ void GuiInspectorTypeSoundAssetPtr::consoleInit() GuiControl * GuiInspectorTypeSoundAssetPtr::constructEditControl() { - return nullptr; + // Create base filename edit controls + GuiControl* retCtrl = Parent::constructEditControl(); + if (retCtrl == NULL) + return retCtrl; + + // Change filespec + char szBuffer[512]; + dSprintf(szBuffer, sizeof(szBuffer), "AssetBrowser.showDialog(\"SoundAsset\", \"AssetBrowser.changeAsset\", %s, \"\");", + getIdString()); + mBrowseButton->setField("Command", szBuffer); + + setDataField(StringTable->insert("targetObject"), NULL, mInspector->getInspectObject()->getIdString()); + + // Create "Open in Editor" button + mEditButton = new GuiBitmapButtonCtrl(); + + dSprintf(szBuffer, sizeof(szBuffer), "AssetBrowser.editAsset(%d.getText());", retCtrl->getId()); + mEditButton->setField("Command", szBuffer); + + char bitmapName[512] = "ToolsModule:SFXEmitter_image"; + mEditButton->setBitmap(StringTable->insert(bitmapName)); + + mEditButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile"); + mEditButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile"); + mEditButton->setDataField(StringTable->insert("hovertime"), NULL, "1000"); + mEditButton->setDataField(StringTable->insert("tooltip"), NULL, "Test play this sound"); + + mEditButton->registerObject(); + addObject(mEditButton); + + return retCtrl; } bool GuiInspectorTypeSoundAssetPtr::updateRects() { - return false; + S32 dividerPos, dividerMargin; + mInspector->getDivider(dividerPos, dividerMargin); + Point2I fieldExtent = getExtent(); + Point2I fieldPos = getPosition(); + + mCaptionRect.set(0, 0, fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y); + mEditCtrlRect.set(fieldExtent.x - dividerPos + dividerMargin, 1, dividerPos - dividerMargin - 34, fieldExtent.y); + + bool resized = mEdit->resize(mEditCtrlRect.point, mEditCtrlRect.extent); + if (mBrowseButton != NULL) + { + mBrowseRect.set(fieldExtent.x - 32, 2, 14, fieldExtent.y - 4); + resized |= mBrowseButton->resize(mBrowseRect.point, mBrowseRect.extent); + } + + if (mEditButton != NULL) + { + RectI shapeEdRect(fieldExtent.x - 16, 2, 14, fieldExtent.y - 4); + resized |= mEditButton->resize(shapeEdRect.point, shapeEdRect.extent); + } + + return resized; } IMPLEMENT_CONOBJECT(GuiInspectorTypeSoundAssetId); diff --git a/Engine/source/T3D/assets/SoundAsset.h b/Engine/source/T3D/assets/SoundAsset.h index bbaeaef9d..321c461b4 100644 --- a/Engine/source/T3D/assets/SoundAsset.h +++ b/Engine/source/T3D/assets/SoundAsset.h @@ -122,6 +122,9 @@ public: bool isLoop() { return mProfileDesc.mIsLooping; } bool is3D() { return mProfileDesc.mIs3D; } + static StringTableEntry getAssetIdByFileName(StringTableEntry fileName); + static U32 getAssetById(StringTableEntry assetId, AssetPtr* materialAsset); + static U32 getAssetByFileName(StringTableEntry fileName, AssetPtr* matAsset); protected: virtual void initializeAsset(void); @@ -143,7 +146,7 @@ class GuiInspectorTypeSoundAssetPtr : public GuiInspectorTypeFileName typedef GuiInspectorTypeFileName Parent; public: - GuiBitmapButtonCtrl* mSoundButton; + GuiBitmapButtonCtrl* mEditButton; DECLARE_CONOBJECT(GuiInspectorTypeSoundAssetPtr); static void consoleInit(); @@ -168,14 +171,14 @@ public: /// Declares a sound asset /// This establishes the assetId, asset and legacy filepath fields, along with supplemental getter and setter functions /// -#define DECLARE_SOUNDASSET(className, name, profile) public: \ +#define DECLARE_SOUNDASSET(className, name) public: \ Resource m##name;\ StringTableEntry m##name##Name; \ StringTableEntry m##name##AssetId;\ AssetPtr m##name##Asset = NULL;\ - SFXProfile* m##name##Profile = &profile;\ + SFXProfile* m##name##Profile = NULL;\ public: \ - const StringTableEntry get##name##File() const { return m##name##Name); }\ + const StringTableEntry get##name##File() const { return m##name##Name; }\ void set##name##File(const FileName &_in) { m##name##Name = StringTable->insert(_in.c_str());}\ const AssetPtr & get##name##Asset() const { return m##name##Asset; }\ void set##name##Asset(const AssetPtr &_in) { m##name##Asset = _in;}\ @@ -206,7 +209,7 @@ public: \ }\ else\ {\ - StringTableEntry assetId = SoundAsset::getAssetIdByFilename(_in);\ + StringTableEntry assetId = SoundAsset::getAssetIdByFileName(_in);\ if (assetId != StringTable->EmptyString())\ {\ m##name##AssetId = assetId;\ @@ -232,9 +235,9 @@ public: \ m##name = NULL;\ }\ \ - if (m##name##Asset.notNull() && m##name##Asset->getStatus() != ShapeAsset::Ok)\ + if (m##name##Asset.notNull() && m##name##Asset->getStatus() != SoundAsset::Ok)\ {\ - Con::errorf("%s(%s)::_set%s() - sound asset failure\"%s\" due to [%s]", macroText(className), getName(), macroText(name), _in, ShapeAsset::getAssetErrstrn(m##name##Asset->getStatus()).c_str());\ + Con::errorf("%s(%s)::_set%s() - sound asset failure\"%s\" due to [%s]", macroText(className), getName(), macroText(name), _in, SoundAsset::getAssetErrstrn(m##name##Asset->getStatus()).c_str());\ return false; \ }\ else if (m##name)\ diff --git a/Engine/source/T3D/assets/assetImporter.cpp b/Engine/source/T3D/assets/assetImporter.cpp index b0497631e..c6095726e 100644 --- a/Engine/source/T3D/assets/assetImporter.cpp +++ b/Engine/source/T3D/assets/assetImporter.cpp @@ -1412,8 +1412,10 @@ void AssetImporter::processImportAssets(AssetImportObject* assetItem) { processShapeAsset(item); } - /*else if (item->assetType == String("SoundAsset")) - SoundAsset::prepareAssetForImport(this, item);*/ + else if (item->assetType == String("SoundAsset")) + { + processSoundAsset(item); + } else if (item->assetType == String("MaterialAsset")) { processMaterialAsset(item); @@ -1462,8 +1464,10 @@ void AssetImporter::processImportAssets(AssetImportObject* assetItem) { processShapeAsset(childItem); } - /*else if (item->assetType == String("SoundAsset")) - SoundAsset::prepareAssetForImport(this, item);*/ + else if (childItem->assetType == String("SoundAsset")) + { + processSoundAsset(childItem); + } else if (childItem->assetType == String("MaterialAsset")) { processMaterialAsset(childItem); @@ -2046,93 +2050,12 @@ void AssetImporter::processShapeMaterialInfo(AssetImportObject* assetItem, S32 m void AssetImporter::processSoundAsset(AssetImportObject* assetItem) { - dSprintf(importLogBuffer, sizeof(importLogBuffer), "Preparing Image for Import: %s", assetItem->assetName.c_str()); + dSprintf(importLogBuffer, sizeof(importLogBuffer), "Preparing Sound for Import: %s", assetItem->assetName.c_str()); activityLog.push_back(importLogBuffer); - if ((activeImportConfig->GenerateMaterialOnImport && assetItem->parentAssetItem == nullptr)/* || assetItem->parentAssetItem != nullptr*/) - { - //find our suffix match, if any - String noSuffixName = assetItem->assetName; - String suffixType; - String suffix = parseImageSuffixes(assetItem->assetName, &suffixType); - if (suffix.isNotEmpty()) - { - assetItem->imageSuffixType = suffixType; - S32 suffixPos = assetItem->assetName.find(suffix, 0, String::NoCase | String::Left); - noSuffixName = assetItem->assetName.substr(0, suffixPos); - } - - //We try to automatically populate materials under the naming convention: materialName: Rock, image maps: Rock_Albedo, Rock_Normal, etc - - AssetImportObject* materialAsset = findImportingAssetByName(noSuffixName); - if (materialAsset != nullptr && materialAsset->assetType != String("MaterialAsset")) - { - //We may have a situation where an asset matches the no-suffix name, but it's not a material asset. Ignore this - //asset item for now - - materialAsset = nullptr; - } - - //If we didn't find a matching material asset in our current items, we'll make one now - if (materialAsset == nullptr) - { - if (!assetItem->filePath.isEmpty()) - { - materialAsset = addImportingAsset("MaterialAsset", assetItem->filePath, nullptr, noSuffixName); - } - } - - //Not that, one way or another, we have the generated material asset, lets move on to associating our image with it - if (materialAsset != nullptr && materialAsset != assetItem->parentAssetItem) - { - if (assetItem->parentAssetItem != nullptr) - { - //If the image had an existing parent, it gets removed from that parent's child item list - assetItem->parentAssetItem->childAssetItems.remove(assetItem); - } - else - { - //If it didn't have one, we're going to pull it from the importingAssets list - importingAssets.remove(assetItem); - } - - //Now we can add it to the correct material asset - materialAsset->childAssetItems.push_back(assetItem); - assetItem->parentAssetItem = materialAsset; - - assetHeirarchyChanged = true; - } - - //Now to do some cleverness. If we're generating a material, we can parse like assets being imported(similar filenames) but different suffixes - //If we find these, we'll just populate into the original's material - - //if we need to append the diffuse suffix and indeed didn't find a suffix on the name, do that here - if (suffixType.isEmpty()) - { - if (activeImportConfig->UseDiffuseSuffixOnOriginImage) - { - String diffuseToken = StringUnit::getUnit(activeImportConfig->DiffuseTypeSuffixes, 0, ",;\t"); - assetItem->assetName = assetItem->assetName + diffuseToken; - assetItem->cleanAssetName = assetItem->assetName; - } - else - { - //We need to ensure that our image asset doesn't match the same name as the material asset, so if we're not trying to force the diffuse suffix - //we'll give it a generic one - if ((materialAsset && materialAsset->assetName.compare(assetItem->assetName) == 0) || activeImportConfig->AlwaysAddImageSuffix) - { - assetItem->assetName = assetItem->assetName + activeImportConfig->AddedImageSuffix; - assetItem->cleanAssetName = assetItem->assetName; - } - } - - //Assume for abledo if it has no suffix matches - assetItem->imageSuffixType = "Albedo"; - } - } - assetItem->processed = true; } + // // Validation // diff --git a/Engine/source/T3D/sfx/sfxEmitter.cpp b/Engine/source/T3D/sfx/sfxEmitter.cpp index e48ca8fee..655e0071a 100644 --- a/Engine/source/T3D/sfx/sfxEmitter.cpp +++ b/Engine/source/T3D/sfx/sfxEmitter.cpp @@ -94,22 +94,23 @@ ColorI SFXEmitter::smRenderColorRangeSphere( 200, 0, 0, 90 ); SFXEmitter::SFXEmitter() : SceneObject(), mSource( NULL ), - mTrack( NULL ), mUseTrackDescriptionOnly( false ), - mLocalProfile( &mDescription ), mPlayOnAdd( true ) { mTypeMask |= MarkerObjectType; mNetFlags.set( Ghostable | ScopeAlways ); - + mDescription.mIs3D = true; mDescription.mIsLooping = true; mDescription.mIsStreaming = false; mDescription.mFadeInTime = -1.f; mDescription.mFadeOutTime = -1.f; - + + mLocalProfile.mFilename = StringTable->EmptyString(); mLocalProfile._registerSignals(); + INIT_SOUNDASSET(Sound); + mObjBox.minExtents.set( -1.f, -1.f, -1.f ); mObjBox.maxExtents.set( 1.f, 1.f, 1.f ); } @@ -174,15 +175,17 @@ void SFXEmitter::consoleInit() void SFXEmitter::initPersistFields() { addGroup( "Media" ); - - addField( "track", TypeSFXTrackName, Offset( mTrack, SFXEmitter), + + INITPERSISTFIELD_SOUNDASSET(Sound, SFXEmitter, ""); + + /*addField("track", TypeSFXTrackName, Offset(mTrack, SFXEmitter), "The track which the emitter should play.\n" "@note If assigned, this field will take precedence over a #fileName that may also be assigned to the " "emitter." ); addField( "fileName", TypeStringFilename, Offset( mLocalProfile.mFilename, SFXEmitter), "The sound file to play.\n" "Use @b either this property @b or #track. If both are assigned, #track takes precendence. The primary purpose of this " - "field is to avoid the need for the user to define SFXTrack datablocks for all sounds used in a level." ); + "field is to avoid the need for the user to define SFXTrack datablocks for all sounds used in a level." );*/ endGroup( "Media"); @@ -287,12 +290,13 @@ U32 SFXEmitter::packUpdate( NetConnection *con, U32 mask, BitStream *stream ) stream->writeAffineTransform( mObjToWorld ); // track - if( stream->writeFlag( mDirty.test( Track ) ) ) - sfxWrite( stream, mTrack ); + PACK_SOUNDASSET(con, Sound); + //if (stream->writeFlag(mDirty.test(Track))) + // sfxWrite( stream, mTrack ); // filename - if( stream->writeFlag( mDirty.test( Filename ) ) ) - stream->writeString( mLocalProfile.mFilename ); + //if( stream->writeFlag( mDirty.test( Filename ) ) ) + // stream->writeString( mLocalProfile.mFilename ); // volume if( stream->writeFlag( mDirty.test( Volume ) ) ) @@ -397,7 +401,8 @@ void SFXEmitter::unpackUpdate( NetConnection *conn, BitStream *stream ) } // track - if ( _readDirtyFlag( stream, Track ) ) + UNPACK_SOUNDASSET(conn, Sound); + /*if (_readDirtyFlag(stream, Track)) { String errorStr; if( !sfxReadAndResolve( stream, &mTrack, errorStr ) ) @@ -406,7 +411,7 @@ void SFXEmitter::unpackUpdate( NetConnection *conn, BitStream *stream ) // filename if ( _readDirtyFlag( stream, Filename ) ) - mLocalProfile.mFilename = stream->readSTString(); + mLocalProfile.mFilename = stream->readSTString();*/ // volume if ( _readDirtyFlag( stream, Volume ) ) @@ -586,8 +591,8 @@ void SFXEmitter::inspectPostApply() // Parent will call setScale so sync up scale with distance. F32 maxDistance = mDescription.mMaxDistance; - if( mUseTrackDescriptionOnly && mTrack ) - maxDistance = mTrack->getDescription()->mMaxDistance; + if( mUseTrackDescriptionOnly && mSoundAsset ) + maxDistance = mSoundAsset->getSfxDescription()->mMaxDistance; mObjScale.set( maxDistance, maxDistance, maxDistance ); @@ -608,8 +613,8 @@ bool SFXEmitter::onAdd() mDescription.validate(); // Read an old 'profile' field for backwards-compatibility. - - if( !mTrack ) + /* + if(mSoundAsset.isNull() || !mSoundAsset->getSfxProfile()) { static const char* sProfile = StringTable->insert( "profile" ); const char* profileName = getDataField( sProfile, NULL ); @@ -643,7 +648,7 @@ bool SFXEmitter::onAdd() // Remove the old 'channel' field. setDataField( sChannel, NULL, "" ); } - } + }*/ } else { @@ -683,6 +688,12 @@ void SFXEmitter::_update() // we can restore it. SFXStatus prevState = mSource ? mSource->getStatus() : SFXStatusNull; + if (mSoundAsset.notNull() ) + { + mLocalProfile = *mSoundAsset->getSfxProfile(); + mDescription = *mSoundAsset->getSfxDescription(); + } + // Make sure all the settings are valid. mDescription.validate(); @@ -695,12 +706,12 @@ void SFXEmitter::_update() SFX_DELETE( mSource ); // Do we have a track? - if( mTrack ) + if( mSoundAsset && mSoundAsset->getSfxProfile() ) { - mSource = SFX->createSource( mTrack, &transform, &velocity ); + mSource = SFX->createSource(mSoundAsset->getSfxProfile(), &transform, &velocity ); if( !mSource ) Con::errorf( "SFXEmitter::_update() - failed to create sound for track %i (%s)", - mTrack->getId(), mTrack->getName() ); + mSoundAsset->getSfxProfile()->getId(), mSoundAsset->getSfxProfile()->getName() ); // If we're supposed to play when the emitter is // added to the scene then also restart playback @@ -739,12 +750,12 @@ void SFXEmitter::_update() // is toggled on a local profile sound. It makes the // editor feel responsive and that things are working. if( gEditingMission && - !mTrack && + (mSoundAsset.isNull() || !mSoundAsset->getSfxProfile()) && mPlayOnAdd && mDirty.test( IsLooping ) ) prevState = SFXStatusPlaying; - bool useTrackDescriptionOnly = ( mUseTrackDescriptionOnly && mTrack ); + bool useTrackDescriptionOnly = ( mUseTrackDescriptionOnly && mSoundAsset.notNull() && mSoundAsset->getSfxProfile()); // The rest only applies if we have a source. if( mSource ) @@ -1087,8 +1098,8 @@ SFXStatus SFXEmitter::_getPlaybackStatus() const bool SFXEmitter::is3D() const { - if( mTrack != NULL ) - return mTrack->getDescription()->mIs3D; + if( mSoundAsset.notNull() && mSoundAsset->getSfxProfile() != NULL ) + return mSoundAsset->getSfxProfile()->getDescription()->mIs3D; else return mDescription.mIs3D; } @@ -1124,8 +1135,8 @@ void SFXEmitter::setScale( const VectorF &scale ) { F32 maxDistance; - if( mUseTrackDescriptionOnly && mTrack ) - maxDistance = mTrack->getDescription()->mMaxDistance; + if( mUseTrackDescriptionOnly && mSoundAsset.notNull() && mSoundAsset->getSfxProfile()) + maxDistance = mSoundAsset->getSfxProfile()->getDescription()->mMaxDistance; else { // Use the average of the three coords. diff --git a/Engine/source/T3D/sfx/sfxEmitter.h b/Engine/source/T3D/sfx/sfxEmitter.h index 226cf5ce5..aeecd13bf 100644 --- a/Engine/source/T3D/sfx/sfxEmitter.h +++ b/Engine/source/T3D/sfx/sfxEmitter.h @@ -36,6 +36,7 @@ #include "gfx/gfxStateBlock.h" #endif +#include "T3D/assets/SoundAsset.h" class SFXSource; class SFXTrack; @@ -103,13 +104,11 @@ class SFXEmitter : public SceneObject /// The current dirty flags. BitSet32 mDirty; + DECLARE_SOUNDASSET(SFXEmitter, Sound); + DECLARE_SOUNDASSET_NET_SETGET(SFXEmitter, Sound, DirtyUpdateMask); + /// The sound source for the emitter. SFXSource *mSource; - - /// The selected track or null if the local - /// profile should be used. - SFXTrack *mTrack; - /// Whether to leave sound setup exclusively to the assigned mTrack and not /// override part of the track's description with emitter properties. bool mUseTrackDescriptionOnly; diff --git a/Engine/source/sfx/sfxSource.cpp b/Engine/source/sfx/sfxSource.cpp index abc5b35e1..9b776af94 100644 --- a/Engine/source/sfx/sfxSource.cpp +++ b/Engine/source/sfx/sfxSource.cpp @@ -791,6 +791,9 @@ void SFXSource::_setStatus( SFXStatus status ) void SFXSource::_updateVolume( const MatrixF& listener ) { + if (!mDescription) + return; + // Handle fades (compute mFadedVolume). mFadedVolume = mPreFadeVolume; @@ -919,13 +922,19 @@ void SFXSource::_updateVolume( const MatrixF& listener ) void SFXSource::_updatePitch() { + if (!mDescription) + return; + mEffectivePitch = mModulativePitch * mPitch; } //----------------------------------------------------------------------------- void SFXSource::_updatePriority() -{ +{ + if (!mDescription) + return; + mEffectivePriority = mPriority * mModulativePriority; SFXSource* group = getSourceGroup(); diff --git a/Templates/BaseGame/game/data/UI/sounds/buttonClick.asset.taml b/Templates/BaseGame/game/data/UI/sounds/buttonClick.asset.taml new file mode 100644 index 000000000..99773fd2d --- /dev/null +++ b/Templates/BaseGame/game/data/UI/sounds/buttonClick.asset.taml @@ -0,0 +1 @@ + diff --git a/Templates/BaseGame/game/data/UI/sounds/buttonHover.asset.taml b/Templates/BaseGame/game/data/UI/sounds/buttonHover.asset.taml new file mode 100644 index 000000000..00b1af405 --- /dev/null +++ b/Templates/BaseGame/game/data/UI/sounds/buttonHover.asset.taml @@ -0,0 +1 @@ + diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetBrowser.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetBrowser.tscript index e95ec0572..bf21750ff 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetBrowser.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetBrowser.tscript @@ -1178,7 +1178,7 @@ function AssetBrowserPreviewButton::onRightClick(%this) EditAssetPopup.enableItem(7, true); //Is it an editable type? - if(%assetType $= "ImageAsset" /*|| %assetType $= "GameObjectAsset"*/ || %assetType $= "CppAsset" || %assetType $= "SoundAsset") + if(%assetType $= "ImageAsset" /*|| %assetType $= "GameObjectAsset"*/ || %assetType $= "CppAsset") { EditAssetPopup.enableItem(0, false); } diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/shape.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/shape.tscript index 4bf17453a..8ca2a21ed 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/shape.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/shape.tscript @@ -42,6 +42,10 @@ function AssetBrowser::editShapeAsset(%this, %assetDef) ShapeEditorPlugin.openShapeAsset(%assetDef); } +function AssetBrowser::onShapeAssetChanged(%this, %assetDef) +{ +} + function AssetBrowser::deleteShapeAsset(%this, %assetDef) { diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/sound.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/sound.tscript index c32222568..3322964bc 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/sound.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/sound.tscript @@ -1,13 +1,36 @@ +function AssetBrowser::editSoundAsset(%this, %assetDef) +{ + if (isObject($PreviewSoundSource)) + sfxStop($PreviewSoundSource); + $PreviewSoundSource = %assetDef.playSound(); +} + +function AssetBrowser::onSoundAssetChanged(%this, %assetDef) +{ + if (isObject($PreviewSoundSource)) + sfxStop($PreviewSoundSource); +} + function AssetBrowser::buildSoundAssetPreview(%this, %assetDef, %previewData) { %previewData.assetName = %assetDef.assetName; %previewData.assetPath = %assetDef.soundFilePath; - //%previewData.doubleClickCommand = "EditorOpenFileInTorsion( "@%previewData.assetPath@", 0 );"; if(%this.selectMode) + { %previewData.doubleClickCommand = "AssetBrowser.selectAsset( AssetBrowser.selectedAsset );"; + } else + { + if(EditorSettings.value("Assets/Browser/doubleClickAction", "Edit Asset") $= "Edit Asset") + { %previewData.doubleClickCommand = "AssetBrowser.editAsset( "@%assetDef@" );"; + } + else + { + %previewData.doubleClickCommand = "AssetBrowser.onSoundAssetEditorDropped( "@%assetDef@" );"; + } + } %previewData.previewImage = "ToolsModule:soundIcon_image"; @@ -38,7 +61,7 @@ function AssetBrowser::onSoundAssetEditorDropped(%this, %assetDef, %position) %newSFXEmitter = new SFXEmitter() { position = %pos; - fileName = %assetDef.getSoundPath(); + soundAsset = %assetDef.getAssetId(); pitch = %assetDef.pitchAdjust; volume = %assetDef.volumeAdjust; }; @@ -50,4 +73,28 @@ function AssetBrowser::onSoundAssetEditorDropped(%this, %assetDef, %position) EWorldEditor.isDirty = true; +} + +function GuiInspectorTypeShapeAssetPtr::onControlDropped( %this, %payload, %position ) +{ + Canvas.popDialog(EditorDragAndDropLayer); + + // Make sure this is a color swatch drag operation. + if( !%payload.parentGroup.isInNamespaceHierarchy( "AssetPreviewControlType_AssetDrop" ) ) + return; + + %assetType = %payload.assetType; + + if(%assetType $= "SoundAsset") +{ + %module = %payload.moduleName; + %asset = %payload.assetName; + + %targetComponent = %this.targetObject; + %targetComponent.soundAsset = %module @ ":" @ %asset; + + //Inspector.refresh(); + } + + EWorldEditor.isDirty = true; } \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/editAsset.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/editAsset.tscript index 856ba7d11..13269cb25 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/editAsset.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/editAsset.tscript @@ -3,7 +3,14 @@ function AssetBrowser_editAsset::saveAsset(%this) %file = AssetDatabase.getAssetFilePath(%this.editedAssetId); %success = TamlWrite(AssetBrowser_editAsset.editedAsset, %file); - AssetBrowser.loadFilters(); + AssetBrowser.reloadAsset(%this.editedAssetId); + + AssetBrowser.refresh(); + + %assetType = AssetDatabase.getAssetType(%this.editedAssetId); + %assetDef = AssetDatabase.acquireAsset(%this.editedAssetId); + AssetBrowser.call("on" @ %assetType @ "Changed", %assetDef); + AssetDatabase.releaseAsset(%this.editedAssetId); Canvas.popDialog(AssetBrowser_editAsset); } @@ -79,6 +86,17 @@ function AssetBrowser::editAssetInfo(%this) } //------------------------------------------------------------ +function AssetBrowser::reloadAsset(%this, %assetId) +{ + %moduleName = getToken(%assetId, ":", 0); + %moduleDef = ModuleDatabase.findModule(%moduleName); + + %assetName = getToken(%assetId, ":", 1); + %assetFilePath = AssetDatabase.getAssetFilePath(%assetId); + + AssetDatabase.removeDeclaredAsset(%assetId); + AssetDatabase.addDeclaredAsset(%moduleDef, %assetFilePath); +} function AssetBrowser::refreshAsset(%this, %assetId) { diff --git a/Templates/BaseGame/game/tools/projectImporter/scripts/pre40/T3Dpre4ProjectImporter.tscript b/Templates/BaseGame/game/tools/projectImporter/scripts/pre40/T3Dpre4ProjectImporter.tscript index 735e8dd66..99099085e 100644 --- a/Templates/BaseGame/game/tools/projectImporter/scripts/pre40/T3Dpre4ProjectImporter.tscript +++ b/Templates/BaseGame/game/tools/projectImporter/scripts/pre40/T3Dpre4ProjectImporter.tscript @@ -838,6 +838,7 @@ T3Dpre4ProjectImporter::genProcessor("afxBillboardData", "texture textureAsset") T3Dpre4ProjectImporter::genProcessor("afxModelData", "shapeName shapeAsset shapeFile shapeAsset"); T3Dpre4ProjectImporter::genProcessor("afxZodiacData", "texture textureAsset"); T3Dpre4ProjectImporter::genProcessor("afxZodiacPlaneData", "texture textureAsset"); +T3Dpre4ProjectImporter::genProcessor("sfxEmitter", "track soundAsset filename soundAsset"); //============================================================================== // Levels //============================================================================== @@ -1064,6 +1065,169 @@ function T3Dpre4ProjectImporter::processTerrainMaterialObject(%this, %file, %obj //============================================================================== T3Dpre4ProjectImporter::genProcessor("PostEffect", "texture textureAsset"); +//============================================================================== +// Sounds +// Sounds are a little weird because there's so much data tied up in a given sound +// source. So our approach is find old SFXProfiles and process those into sound assets +// by cross-referencing the filename for existing asset definitions. +// Using existing SFXProfiles allows us to also injest the descriptions, giving us +// our meta-properties on the sound asset itself. +//============================================================================== +function T3Dpre4ProjectImporter::processSFXProfileLine(%this, %line) +{ + return %line; +} + +function T3Dpre4ProjectImporter::processSFXProfileObject(%this, %file, %objectName) +{ + %soundFilename = findObjectField("filename"); + + %soundFilename = sanitizeFilename(%soundFilename); + + %soundAsset = SoundAsset::getAssetIdByFilename(%soundFilename); + + //Throw a warn that this file's already been claimed and move on + if(%soundAsset !$= "") + { + warn("T3Dpre4ProjectImporter::processSFXProfileObject() - attempting to process SFXProfile " @ %objectName + @ " but its filename is already associated to another sound asset. Continuing, but be aware."); + } + + %assetName = %objectName; + + %moduleName = AssetBrowser.dirHandler.getModuleFromAddress(%soundFilename).ModuleId; + + %assetPath = filePath(%soundFilename) @ "/"; + + %tamlpath = %assetPath @ %assetName @ ".asset.taml"; + + if(isFile(%tamlpath)) + { + error("T3Dpre4ProjectImporter::processSFXProfileObject() - Failed to create as taml file already exists: " @ %soundFilename); + return false; + } + + %asset = new SoundAsset() + { + AssetName = %assetName; + versionId = 1; + shaderData = ""; + soundFile = fileBase(%soundFilename) @ fileExt(%soundFilename); + }; + + %descriptionName = findObjectField("description"); + + if(%descriptionName !$= "") + { + //Optimization, see if we already have this description by happenstance + if(isObject(%descriptionName)) + { + %asset.sourceGroup = %descriptionName.sourceGroup; + %asset.volume = %descriptionName.volume; + %asset.pitch = %descriptionName.pitch; + %asset.isLooping = %descriptionName.isLooping; + %asset.priority = %descriptionName.priority; + %asset.useHardware = %descriptionName.useHardware; + %asset.is3D = %descriptionName.is3D; + %asset.minDistance = %descriptionName.minDistance; + %asset.maxDistance = %descriptionName.maxDistance; + %asset.scatterDistance = %descriptionName.scatterDistance; + %asset.coneInsideAngle = %descriptionName.coneInsideAngle; + %asset.coneOutsideAngle = %descriptionName.coneOutsideAngle; + %asset.coneOutsideVolume = %descriptionName.coneOutsideVolume; + %asset.rolloffFactor = %descriptionName.rolloffFactor; + %asset.isStreaming = %descriptionName.isStreaming; + } + else + { + %objFileFinder = ""; + //first check our cache + if(isObject($ProjectImporter::SFXDescriptionCache) && + $ProjectImporter::SFXDescriptionCache.getIndexFromKey(%descriptionName) !$= "") + { + %key = $ProjectImporter::SFXDescriptionCache.getIndexFromKey(%descriptionName); + %objFileFinder = $ProjectImporter::SFXDescriptionCache.getValue(%key); + } + else + { + %objFileFinder = findObjectInFiles(%descriptionName); + } + + if(%objFileFinder !$= "") + { + %valueArray = new ArrayObject(); + + %fileObj = getField(%objFileFinder, 0); + + %valueArray.add("sourceGroup" SPC findObjectField("sourceGroup", %fileObj)); + %valueArray.add("volume" SPC findObjectField("volume", %fileObj)); + %valueArray.add("pitch" SPC findObjectField("pitch", %fileObj)); + %valueArray.add("isLooping" SPC findObjectField("isLooping", %fileObj)); + %valueArray.add("priority" SPC findObjectField("priority", %fileObj)); + %valueArray.add("useHardware" SPC findObjectField("useHardware", %fileObj)); + %valueArray.add("is3D" SPC findObjectField("is3D", %fileObj)); + %valueArray.add("minDistance" SPC findObjectField("minDistance", %fileObj)); + %valueArray.add("maxDistance" SPC findObjectField("maxDistance", %fileObj)); + %valueArray.add("scatterDistance" SPC findObjectField("scatterDistance", %fileObj)); + %valueArray.add("coneInsideAngle" SPC findObjectField("coneInsideAngle", %fileObj)); + %valueArray.add("coneOutsideAngle" SPC findObjectField("coneOutsideAngle", %fileObj)); + %valueArray.add("coneOutsideVolume" SPC findObjectField("coneOutsideVolume", %fileObj)); + %valueArray.add("rolloffFactor" SPC findObjectField("rolloffFactor", %fileObj)); + %valueArray.add("isStreaming" SPC findObjectField("isStreaming", %fileObj)); + + if(isObject($ProjectImporter::SFXDescriptionCache)) + { + $ProjectImporter::SFXDescriptionCache.add(%descriptionName, %objFileFinder); + } + + for(%v=0; %v < %valueArray.Count(); %v++) + { + %varSet = %valueArray.getKey(%v); + %var = getWord(%varSet, 0); + %varVal = getWord(%varSet, 1); + + if(%varVal !$= "") + %asset.setFieldValue(%var, %varVal); + } + + %valueArray.delete(); + } + } + } + + TamlWrite(%asset, %tamlpath); + + %moduleDef = ModuleDatabase.findModule(%moduleName, 1); + %success = AssetDatabase.addDeclaredAsset(%moduleDef, %tamlpath); + + if(!%success) + return false; + + //Now mark the original SFXProfile for removal from the file as it's redundant + //now that we have the asset def + $ProjectImporter::ToRemoveObjectList.add(%objectName, %file TAB 0); + + return true; +} + +function T3Dpre4ProjectImporter::processAudioProfileObject(%this, %file, %objectName) + { + return %this.processSFXProfileObject(%file, %objectName); + } + +function T3Dpre4ProjectImporter::processSFXDescriptionObject(%this, %file, %objectName) + { + $ProjectImporter::ToRemoveObjectList.add(%objectName, %file TAB 0); + + return true; +} + +function T3Dpre4ProjectImporter::processAudioDescriptionObject(%this, %file, %objectName) +{ + $ProjectImporter::ToRemoveObjectList.add(%objectName, %file TAB 0); + + return true; +} //============================================================================== // Misc Utility functions diff --git a/Templates/BaseGame/game/tools/projectImporter/scripts/projectImporter.tscript b/Templates/BaseGame/game/tools/projectImporter/scripts/projectImporter.tscript index 05a0847f4..a28233596 100644 --- a/Templates/BaseGame/game/tools/projectImporter/scripts/projectImporter.tscript +++ b/Templates/BaseGame/game/tools/projectImporter/scripts/projectImporter.tscript @@ -166,6 +166,16 @@ function ProjectImportWizardPage2::openPage(%this) $ProjectImporter::importTool.delete(); $ProjectImporter::importTool = new ScriptObject($ProjectImporter::versionMode @ "Importer"); + + if(isObject($ProjectImporter::SFXDescriptionCache)) + $ProjectImporter::SFXDescriptionCache.delete(); + + $ProjectImporter::SFXDescriptionCache = new ArrayObject(); + + if(isObject($ProjectImporter::ToRemoveObjectList)) + $ProjectImporter::ToRemoveObjectList.delete(); + + $ProjectImporter::ToRemoveObjectList = new ArrayObject(); } function ProjectImportWizardPage2::processPage(%this) @@ -312,6 +322,28 @@ function ProjectImportWizardPage6::openPage(%this) { $ProjectImporter::importTool.processScriptExtensions(); } + + //All good? now go through and wipe any objects flagged in our files for deletion + %objRemovePM = new PersistenceManager(); + + for(%o = 0; %o < $ProjectImporter::ToRemoveObjectList.count(); %o++) + { + %name = $ProjectImporter::ToRemoveObjectList.getKey(%o); + %file = getField($ProjectImporter::ToRemoveObjectList.getValue(%o),0); + + if(%name !$= "" && isFile(%file)) + { + if(!isObject(%name)) + { + //spoof it + %tmpObj = new SimObject(%name); + %objRemovePM.setDirty(%name, %file); + } + %objRemovePM.removeObjectFromFile(%name, %file); + + %tmpObj.delete(); + } + } } function ProjectImportWizardPage6::processPage(%this) @@ -416,7 +448,7 @@ function sanitizeFilename(%file) %sanitizedName = sanitizeString(%targetName); if(startsWith(%sanitizedName, "_")) { - %sanitizedName = substr(%sanitizedName, 1, -1); + %sanitizedName = getSubStr(%sanitizedName, 1, -1); } if(%sanitizedName !$= %targetName) { @@ -472,6 +504,12 @@ function testFilenameExtensions(%filename) return %filename @ ".dae"; else if(isFile(%filename @ ".dds")) return %filename @ ".dds"; + else if(isFile(%filename @ ".ogg")) + return %filename @ ".ogg"; + else if(isFile(%filename @ ".wav")) + return %filename @ ".wav"; + else if(isFile(%filename @ ".mp3")) + return %filename @ ".dds"; return %filename; } @@ -659,11 +697,14 @@ function findObjectName(%line, %createWord) return %objectName; } -function findObjectField(%fieldName) +function findObjectField(%fieldName, %fileObj) { + if(%fileObj $= "") + %fileObj = $ProjectImporter::fileObject; + %value = ""; %peekLineOffset = 0; - %peekLine = $ProjectImporter::fileObject.peekLine(%peekLineOffset); + %peekLine = %fileObj.peekLine(%peekLineOffset); while(!strIsMatchExpr("*};*", %peekLine) && !strIsMatchExpr("*singleton*(*)*", %peekLine) && !strIsMatchExpr("*new*(*)*", %peekLine) && @@ -671,7 +712,7 @@ function findObjectField(%fieldName) !strIsMatchExpr("\n", %peekLine) && !strIsMatchExpr("\r", %peekLine)) { - if(strpos(%peekLine, %fieldName) != -1) + if(strpos(strlwr(%peekLine), strlwr(%fieldName)) != -1) { %value = ""; %pos = strpos(%peekLine, "= \""); @@ -682,8 +723,7 @@ function findObjectField(%fieldName) %value = getSubStr(%peekLine, %pos+3, %endPos-%pos-3); break; } - else - { + %pos = strpos(%peekLine, "=\""); if(%pos != -1) { @@ -692,16 +732,109 @@ function findObjectField(%fieldName) %value = getSubStr(%peekLine, %pos+2, %endPos-%pos-2); break; } + + %pos = strpos(%peekLine, "= "); + if(%pos != -1) + { + %endPos = strpos(%peekLine, ";", %pos); + + %value = getSubStr(%peekLine, %pos+2, %endPos-%pos-2); + break; + } + + %pos = strpos(%peekLine, "="); + if(%pos != -1) + { + %endPos = strpos(%peekLine, ";", %pos); + + %value = getSubStr(%peekLine, %pos+2, %endPos-%pos-2); + break; } } %peekLineOffset++; - %peekLine = $ProjectImporter::fileObject.peekLine(%peekLineOffset); + %peekLine = %fileObj.peekLine(%peekLineOffset); } return %value; } +function findObjectInFiles(%objectName) +{ + //First, wipe out any files inside the folder first + %file = findFirstFileMultiExpr( "*.*", true); + + %fileObj = new FileObject(); + + while( %file !$= "" ) + { + %filename = fileName(%file); + %fileBase = fileBase(%file); + %fileExt = fileExt(%file); + %filePath = filePath(%file); + + if ( %fileObj.openForRead( %file ) ) + { + %lineNum = 0; + while ( !%fileObj.isEOF() ) + { + %line = %fileObj.readLine(); + %trimmedLine = trim(%line); + + if(strIsMatchExpr("*new*(*)*", %line) && strpos(%line, "::") == -1) + { + %className = findObjectClass(%line, "new"); + if (%className $= "") continue; + + %objName = findObjectName(%line, "new"); + + if(%objectName $= %objName) + { + return %fileObj TAB %file TAB %lineNum; + } + } + else if(strIsMatchExpr("*datablock*(*)*", %line) && strpos(%line, "::") == -1) + { + %className = findObjectClass(%line, "datablock"); + if (%className $= "") continue; + + %objName = findObjectName(%line, "datablock"); + + if(%objectName $= %objName) + { + return %fileObj TAB %file TAB %lineNum; + } + } + else if(strIsMatchExpr("*singleton*(*)*", %line) && strpos(%line, "::") == -1) + { + %className = findObjectClass(%line, "singleton"); + if (%className $= "") continue; + + %objName = findObjectName(%line, "singleton"); + + if(%objectName $= %objName) + { + return %fileObj TAB %file TAB %lineNum; + } + } + + %lineNum++; + } + + %fileObj.close(); + } + else + { + error("findObjectInFiles() - File not able to be opened: " @ %file); + } + + %file = findNextFileMultiExpr( "*.*" ); + } + + %fileObj.delete(); + + return ""; +} //============================================================================== //Shape Importing //==============================================================================