浏览代码

Merge pull request #561 from Areloch/SoundAssetInitRollin

Sound asset initial rollin
Brian Roberts 4 年之前
父节点
当前提交
753b6c7189

+ 154 - 11
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<SoundAsset>(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<SoundAsset>* 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>* 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<SoundAsset>(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);

+ 10 - 7
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<SoundAsset>* materialAsset);
+   static U32 getAssetByFileName(StringTableEntry fileName, AssetPtr<SoundAsset>* 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
 /// </Summary>
-#define DECLARE_SOUNDASSET(className, name, profile) public: \
+#define DECLARE_SOUNDASSET(className, name) public: \
    Resource<SFXResource> m##name;\
    StringTableEntry m##name##Name; \
    StringTableEntry m##name##AssetId;\
    AssetPtr<SoundAsset> 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<SoundAsset> & get##name##Asset() const { return m##name##Asset; }\
    void set##name##Asset(const AssetPtr<SoundAsset> &_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)\

+ 10 - 87
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
 //

+ 38 - 27
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.

+ 4 - 5
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;

+ 10 - 1
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();

+ 1 - 0
Templates/BaseGame/game/data/UI/sounds/buttonClick.asset.taml

@@ -0,0 +1 @@
+<SoundAsset canSave="true" canSaveDynamicFields="true" AssetName="buttonClick" soundFile="@assetFile=buttonClick.wav" PitchAdjust="1" VolumeAdjust="1" is3D="false" isLooping="false" isStreaming="false" useHardware="false" minDistance="1" maxDistance="100" coneInsideAngle="360" coneOutsideAngle="360" coneOutsideVolume="1" rolloffFactor="-1" scatterDistance="0 0 0"/>

+ 1 - 0
Templates/BaseGame/game/data/UI/sounds/buttonHover.asset.taml

@@ -0,0 +1 @@
+<SoundAsset canSave="true" canSaveDynamicFields="true" AssetName="buttonHover" soundFile="@assetFile=buttonHover.wav" PitchAdjust="1" VolumeAdjust="1" is3D="false" isLooping="false" isStreaming="false" useHardware="true" minDistance="1" maxDistance="120" coneInsideAngle="360" coneOutsideAngle="360" coneOutsideVolume="1" rolloffFactor="-1" scatterDistance="0 0 0"/>

+ 1 - 1
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);
    }

+ 4 - 0
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)
 {
    

+ 49 - 2
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;
 }

+ 19 - 1
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)
 {

+ 164 - 0
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

+ 140 - 7
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
 //==============================================================================