Browse Source

Merge pull request #520 from Areloch/EngineAssetify

Engine Asset Update
Areloch 4 years ago
parent
commit
06aef7cff3
100 changed files with 3505 additions and 1764 deletions
  1. 1 1
      Engine/source/T3D/Scene.cpp
  2. 11 16
      Engine/source/T3D/accumulationVolume.cpp
  3. 5 5
      Engine/source/T3D/accumulationVolume.h
  4. 4 4
      Engine/source/T3D/assets/CubemapAsset.cpp
  5. 54 14
      Engine/source/T3D/assets/GUIAsset.cpp
  6. 2 0
      Engine/source/T3D/assets/GUIAsset.h
  7. 3 3
      Engine/source/T3D/assets/GameObjectAsset.cpp
  8. 224 112
      Engine/source/T3D/assets/ImageAsset.cpp
  9. 479 82
      Engine/source/T3D/assets/ImageAsset.h
  10. 31 0
      Engine/source/T3D/assets/ImageAssetInspectors.h
  11. 40 36
      Engine/source/T3D/assets/LevelAsset.cpp
  12. 9 7
      Engine/source/T3D/assets/LevelAsset.h
  13. 137 115
      Engine/source/T3D/assets/MaterialAsset.cpp
  14. 196 132
      Engine/source/T3D/assets/MaterialAsset.h
  15. 2 2
      Engine/source/T3D/assets/ParticleAsset.cpp
  16. 2 2
      Engine/source/T3D/assets/PostEffectAsset.cpp
  17. 3 3
      Engine/source/T3D/assets/ScriptAsset.cpp
  18. 75 74
      Engine/source/T3D/assets/ShapeAsset.cpp
  19. 410 72
      Engine/source/T3D/assets/ShapeAsset.h
  20. 164 26
      Engine/source/T3D/assets/SoundAsset.cpp
  21. 281 2
      Engine/source/T3D/assets/SoundAsset.h
  22. 5 9
      Engine/source/T3D/assets/TerrainAsset.cpp
  23. 38 12
      Engine/source/T3D/assets/TerrainMaterialAsset.cpp
  24. 1 1
      Engine/source/T3D/assets/TerrainMaterialAsset.h
  25. 357 213
      Engine/source/T3D/assets/assetImporter.cpp
  26. 15 2
      Engine/source/T3D/assets/assetImporter.h
  27. 2 2
      Engine/source/T3D/assets/assetImporter_ScriptBinding.h
  28. 2 2
      Engine/source/T3D/assets/stateMachineAsset.cpp
  29. 33 20
      Engine/source/T3D/convexShape.cpp
  30. 9 4
      Engine/source/T3D/convexShape.h
  31. 23 24
      Engine/source/T3D/debris.cpp
  32. 6 2
      Engine/source/T3D/debris.h
  33. 22 27
      Engine/source/T3D/decal/decalData.cpp
  34. 5 6
      Engine/source/T3D/decal/decalData.h
  35. 2 2
      Engine/source/T3D/decal/decalDataFile.cpp
  36. 2 2
      Engine/source/T3D/decal/decalManager.cpp
  37. 4 8
      Engine/source/T3D/examples/renderMeshExample.cpp
  38. 7 7
      Engine/source/T3D/examples/renderMeshExample.h
  39. 5 17
      Engine/source/T3D/examples/renderShapeExample.cpp
  40. 7 4
      Engine/source/T3D/examples/renderShapeExample.h
  41. 14 22
      Engine/source/T3D/fx/explosion.cpp
  42. 7 3
      Engine/source/T3D/fx/explosion.h
  43. 42 63
      Engine/source/T3D/fx/groundCover.cpp
  44. 7 5
      Engine/source/T3D/fx/groundCover.h
  45. 39 49
      Engine/source/T3D/fx/particle.cpp
  46. 13 7
      Engine/source/T3D/fx/particle.h
  47. 3 3
      Engine/source/T3D/fx/particleEmitter.cpp
  48. 41 14
      Engine/source/T3D/fx/precipitation.cpp
  49. 12 2
      Engine/source/T3D/fx/precipitation.h
  50. 10 7
      Engine/source/T3D/fx/splash.cpp
  51. 4 2
      Engine/source/T3D/fx/splash.h
  52. 1 1
      Engine/source/T3D/gameBase/gameConnection.cpp
  53. 1 1
      Engine/source/T3D/gameBase/gameConnection.h
  54. 22 14
      Engine/source/T3D/groundPlane.cpp
  55. 4 2
      Engine/source/T3D/groundPlane.h
  56. 4 4
      Engine/source/T3D/guiObjectView.cpp
  57. 3 1
      Engine/source/T3D/guiObjectView.h
  58. 15 14
      Engine/source/T3D/levelInfo.cpp
  59. 9 6
      Engine/source/T3D/levelInfo.h
  60. 10 11
      Engine/source/T3D/lightFlareData.cpp
  61. 9 3
      Engine/source/T3D/lightFlareData.h
  62. 7 6
      Engine/source/T3D/lighting/reflectionProbe.cpp
  63. 1 1
      Engine/source/T3D/lighting/reflectionProbe.h
  64. 24 22
      Engine/source/T3D/physics/physicsDebris.cpp
  65. 6 2
      Engine/source/T3D/physics/physicsDebris.h
  66. 29 32
      Engine/source/T3D/physics/physicsShape.cpp
  67. 6 5
      Engine/source/T3D/physics/physicsShape.h
  68. 19 16
      Engine/source/T3D/player.cpp
  69. 3 2
      Engine/source/T3D/player.h
  70. 10 12
      Engine/source/T3D/prefab.cpp
  71. 2 2
      Engine/source/T3D/prefab.h
  72. 26 23
      Engine/source/T3D/projectile.cpp
  73. 5 4
      Engine/source/T3D/projectile.h
  74. 1 1
      Engine/source/T3D/rigidShape.cpp
  75. 2 2
      Engine/source/T3D/sfx/sfxEmitter.cpp
  76. 26 73
      Engine/source/T3D/shapeBase.cpp
  77. 13 8
      Engine/source/T3D/shapeBase.h
  78. 41 50
      Engine/source/T3D/shapeImage.cpp
  79. 11 96
      Engine/source/T3D/tsStatic.cpp
  80. 4 14
      Engine/source/T3D/tsStatic.h
  81. 1 1
      Engine/source/T3D/vehicles/vehicle.cpp
  82. 17 19
      Engine/source/T3D/vehicles/wheeledVehicle.cpp
  83. 6 3
      Engine/source/T3D/vehicles/wheeledVehicle.h
  84. 1 1
      Engine/source/afx/ce/afxStaticShape.h
  85. 3 3
      Engine/source/afx/util/afxParticlePool_T3D.cpp
  86. 28 1
      Engine/source/assets/assetBase.cpp
  87. 8 1
      Engine/source/assets/assetBase.h
  88. 21 0
      Engine/source/assets/assetBase_ScriptBinding.h
  89. 1 1
      Engine/source/cinterface/c_simobjectInterface.cpp
  90. 8 0
      Engine/source/console/consoleFunctions.cpp
  91. 41 20
      Engine/source/console/consoleTypes.cpp
  92. 11 6
      Engine/source/console/consoleTypes.h
  93. 36 0
      Engine/source/console/fileSystemFunctions.cpp
  94. 25 19
      Engine/source/console/persistenceManager.cpp
  95. 38 4
      Engine/source/console/simObject.cpp
  96. 28 3
      Engine/source/core/fileObject.cpp
  97. 1 1
      Engine/source/core/fileObject.h
  98. 34 4
      Engine/source/core/stream/bitStream.cpp
  99. 12 0
      Engine/source/core/volume.cpp
  100. 1 0
      Engine/source/core/volume.h

+ 1 - 1
Engine/source/T3D/Scene.cpp

@@ -282,7 +282,7 @@ bool Scene::saveScene(StringTableEntry fileName)
       dSprintf(depSlotName, sizeof(depSlotName), "%s%d", "staticObjectAssetDependency", i);
 
       char depValue[255];
-      dSprintf(depValue, sizeof(depValue), "@Asset=%s", utilizedAssetsList[i]);
+      dSprintf(depValue, sizeof(depValue), "%s=%s", ASSET_ID_SIGNATURE, utilizedAssetsList[i]);
 
       levelAssetDef->setDataField(StringTable->insert(depSlotName), NULL, StringTable->insert(depValue));
 

+ 11 - 16
Engine/source/T3D/accumulationVolume.cpp

@@ -85,19 +85,20 @@ AccumulationVolume::AccumulationVolume()
    mWorldToObj.identity();
 
    // Accumulation Texture.
-   mTextureName = "";
-   mAccuTexture = NULL;
+   INIT_IMAGEASSET(Texture);
 
    resetWorldBox();
 }
 
 AccumulationVolume::~AccumulationVolume()
 {
-   mAccuTexture = NULL;
+   mTexture = nullptr;
 }
 
 void AccumulationVolume::initPersistFields()
 {
+   addProtectedField("textureAsset", TypeImageAssetId, Offset(mTextureAssetId, AccumulationVolume),
+      &_setTexture, &defaultProtectedGetFn, "Accumulation texture.");
    addProtectedField( "texture", TypeStringFilename, Offset( mTextureName, AccumulationVolume ),
          &_setTexture, &defaultProtectedGetFn, "Accumulation texture." );
 
@@ -235,7 +236,7 @@ U32 AccumulationVolume::packUpdate( NetConnection *connection, U32 mask, BitStre
 
    if (stream->writeFlag(mask & InitialUpdateMask))
    {
-      stream->write( mTextureName );
+      PACK_IMAGEASSET(connection, Texture);
    }
 
    return retMask;  
@@ -247,8 +248,8 @@ void AccumulationVolume::unpackUpdate( NetConnection *connection, BitStream *str
 
    if (stream->readFlag())
    {
-      stream->read( &mTextureName );
-      setTexture(mTextureName);
+      UNPACK_IMAGEASSET(connection, Texture);
+      //setTexture(mTextureName);
    }
 }
 
@@ -262,13 +263,7 @@ void AccumulationVolume::inspectPostApply()
 
 void AccumulationVolume::setTexture( const String& name )
 {
-   mTextureName = name;
-   if ( isClientObject() && mTextureName.isNotEmpty() )
-   {
-      mAccuTexture.set(mTextureName, &GFXStaticTextureSRGBProfile, "AccumulationVolume::mAccuTexture");
-      if ( mAccuTexture.isNull() )
-         Con::warnf( "AccumulationVolume::setTexture - Unable to load texture: %s", mTextureName.c_str() );
-   }
+   _setTexture(StringTable->insert(name.c_str()));
    refreshVolumes();
 }
 
@@ -312,7 +307,7 @@ void AccumulationVolume::refreshVolumes()
          if ( object.isNull() ) continue;
 
          if ( volume->containsPoint(object->getPosition()) )
-            object->mAccuTex = volume->mAccuTexture;
+            object->mAccuTex = volume->getTextureResource();
       }
    }
 }
@@ -346,6 +341,6 @@ void AccumulationVolume::updateObject(SceneObject* object)
       if ( volume.isNull() ) continue;
 
       if ( volume->containsPoint(object->getPosition()) )
-         object->mAccuTex = volume->mAccuTexture;
+         object->mAccuTex = volume->getTextureResource();
    }
-}
+}

+ 5 - 5
Engine/source/T3D/accumulationVolume.h

@@ -58,15 +58,15 @@ class AccumulationVolume : public ScenePolyhedralSpace
       
       mutable Vector< SceneObject* > mVolumeQueryList;
 
-      // Name (path) of the accumulation texture.
-      String mTextureName;
-      
       // SceneSpace.
       virtual void _renderObject( ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat );
 
-   public:
+      DECLARE_IMAGEASSET(AccumulationVolume, Texture, onTextureChanged, GFXStaticTextureSRGBProfile);
+      DECLARE_IMAGEASSET_NET_SETGET(AccumulationVolume, Texture, -1);
 
-      GFXTexHandle mAccuTexture;
+      void onTextureChanged() {}
+
+   public:
 
       AccumulationVolume();
       ~AccumulationVolume();

+ 4 - 4
Engine/source/T3D/assets/CubemapAsset.cpp

@@ -136,7 +136,7 @@ void CubemapAsset::initializeAsset()
 {
    mScriptFile = expandAssetFilePath(mScriptFile);
 
-   if(Platform::isFile(mScriptFile))
+   if(Torque::FS::IsScriptFile(mScriptFile))
       Con::executeFile(mScriptFile, false, false);
 }
 
@@ -144,7 +144,7 @@ void CubemapAsset::onAssetRefresh()
 {
    mScriptFile = expandAssetFilePath(mScriptFile);
 
-   if (Platform::isFile(mScriptFile))
+   if (Torque::FS::IsScriptFile(mScriptFile))
       Con::executeFile(mScriptFile, false, false);
 }
 
@@ -207,8 +207,8 @@ GuiControl* GuiInspectorTypeCubemapAssetPtr::constructEditControl()
    dSprintf(szBuffer, sizeof(szBuffer), "CubemapEditor.openCubemapAsset(%d.getText());", retCtrl->getId());
    mShapeEdButton->setField("Command", szBuffer);
 
-   char bitmapName[512] = "tools/worldEditor/images/toolbar/shape-editor";
-   mShapeEdButton->setBitmap(bitmapName);
+   char bitmapName[512] = "ToolsModule:shape_editor_n_image";
+   mShapeEdButton->setBitmap(StringTable->insert(bitmapName));
 
    mShapeEdButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
    mShapeEdButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");

+ 54 - 14
Engine/source/T3D/assets/GUIAsset.cpp

@@ -47,14 +47,14 @@
 
 IMPLEMENT_CONOBJECT(GUIAsset);
 
-ConsoleType(GUIAssetPtr, TypeGUIAssetPtr, String, ASSET_ID_FIELD_PREFIX)
+ConsoleType(GUIAssetPtr, TypeGUIAssetPtr, const char*, ASSET_ID_FIELD_PREFIX)
 
 //-----------------------------------------------------------------------------
 
 ConsoleGetType(TypeGUIAssetPtr)
 {
    // Fetch asset Id.
-   return *((StringTableEntry*)dptr);
+   return *((const char**)(dptr));
 }
 
 //-----------------------------------------------------------------------------
@@ -67,11 +67,7 @@ ConsoleSetType(TypeGUIAssetPtr)
       // Yes, so fetch field value.
       const char* pFieldValue = argv[0];
 
-      // Fetch asset Id.
-      StringTableEntry* assetId = (StringTableEntry*)(dptr);
-
-      // Update asset value.
-      *assetId = StringTable->insert(pFieldValue);
+      *((const char**)dptr) = StringTable->insert(argv[0]);
 
       return;
    }
@@ -123,12 +119,12 @@ void GUIAsset::initializeAsset()
 {
    mGUIPath = expandAssetFilePath(mGUIFile);
 
-   if (Platform::isFile(mGUIPath))
+   if (Torque::FS::IsScriptFile(mGUIPath))
       Con::executeFile(mGUIPath, false, false);
 
    mScriptPath = expandAssetFilePath(mScriptFile);
 
-   if (Platform::isFile(mScriptPath))
+   if (Torque::FS::IsScriptFile(mScriptPath))
       Con::executeFile(mScriptPath, false, false);
 }
 
@@ -136,12 +132,12 @@ void GUIAsset::onAssetRefresh()
 {
    mGUIPath = expandAssetFilePath(mGUIFile);
 
-   if (Platform::isFile(mGUIPath))
+   if (Torque::FS::IsScriptFile(mGUIPath))
       Con::executeFile(mGUIPath, false, false);
 
    mScriptPath = expandAssetFilePath(mScriptFile);
 
-   if (Platform::isFile(mScriptPath))
+   if (Torque::FS::IsScriptFile(mScriptPath))
       Con::executeFile(mScriptPath, false, false);
 }
 
@@ -183,6 +179,50 @@ void GUIAsset::setScriptFile(const char* pScriptFile)
    refreshAsset();
 }
 
+StringTableEntry GUIAsset::getAssetIdByGUIName(StringTableEntry guiName)
+{
+   StringTableEntry assetId = StringTable->EmptyString();
+
+   AssetQuery* query = new AssetQuery();
+   U32 foundCount = AssetDatabase.findAssetType(query, "GUIAsset");
+   if (foundCount == 0)
+   {
+      //Didn't work, so have us fall back to a placeholder asset
+      assetId = StringTable->insert("Core_Rendering:noMaterial");
+   }
+   else
+   {
+      GuiControl* guiObject;
+      if (!Sim::findObject(guiName, guiObject))
+         return "";
+
+      StringTableEntry guiFile = guiObject->getFilename();
+
+      for (U32 i = 0; i < foundCount; i++)
+      {
+         GUIAsset* guiAsset = AssetDatabase.acquireAsset<GUIAsset>(query->mAssetList[i]);
+         if (guiAsset && guiAsset->getGUIPath() == guiFile)
+         {
+            assetId = guiAsset->getAssetId();
+            AssetDatabase.releaseAsset(query->mAssetList[i]);
+            break;
+         }
+         AssetDatabase.releaseAsset(query->mAssetList[i]);
+      }
+   }
+
+   return assetId;
+}
+
+#ifdef TORQUE_TOOLS
+DefineEngineStaticMethod(GUIAsset, getAssetIdByGUIName, const char*, (const char* guiName), (""),
+   "Queries the Asset Database to see if any asset exists that is associated with the provided GUI Name.\n"
+   "@return The AssetId of the associated asset, if any.")
+{
+   return GUIAsset::getAssetIdByGUIName(StringTable->insert(guiName));
+}
+#endif
+
 //-----------------------------------------------------------------------------
 // GuiInspectorTypeAssetId
 //-----------------------------------------------------------------------------
@@ -221,13 +261,13 @@ GuiControl* GuiInspectorTypeGUIAssetPtr::constructEditControl()
    dSprintf(szBuffer, sizeof(szBuffer), "echo(\"Game Object Editor not implemented yet!\");", retCtrl->getId());
    mSMEdButton->setField("Command", szBuffer);
 
-   char bitmapName[512] = "tools/worldEditor/images/toolbar/shape-editor";
-   mSMEdButton->setBitmap(bitmapName);
+   char bitmapName[512] = "ToolsModule:GameTSCtrl_image";
+   mSMEdButton->setBitmap(StringTable->insert(bitmapName));
 
    mSMEdButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
    mSMEdButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");
    mSMEdButton->setDataField(StringTable->insert("hovertime"), NULL, "1000");
-   mSMEdButton->setDataField(StringTable->insert("tooltip"), NULL, "Open this file in the State Machine Editor");
+   mSMEdButton->setDataField(StringTable->insert("tooltip"), NULL, "Open this file in the GUI Editor");
 
    mSMEdButton->registerObject();
    addObject(mSMEdButton);

+ 2 - 0
Engine/source/T3D/assets/GUIAsset.h

@@ -60,6 +60,8 @@ public:
    static void initPersistFields();
    virtual void copyTo(SimObject* object);
 
+   static StringTableEntry getAssetIdByGUIName(StringTableEntry guiName);
+
    /// Declare Console Object.
    DECLARE_CONOBJECT(GUIAsset);
 

+ 3 - 3
Engine/source/T3D/assets/GameObjectAsset.cpp

@@ -133,7 +133,7 @@ void GameObjectAsset::initializeAsset()
    //Ensure we have an expanded filepath
    mScriptPath = getOwned() ? expandAssetFilePath(mScriptFile) : mScriptPath;
 
-   if (Platform::isFile(mScriptPath))
+   if (Torque::FS::IsScriptFile(mScriptPath))
       Con::executeFile(mScriptPath, false, false);
 
    mTAMLPath = getOwned() ? expandAssetFilePath(mTAMLFile) : mTAMLPath;
@@ -144,7 +144,7 @@ void GameObjectAsset::onAssetRefresh()
    //Ensure we have an expanded filepath
    mScriptPath = getOwned() ? expandAssetFilePath(mScriptFile) : mScriptPath;
 
-   if (Platform::isFile(mScriptPath))
+   if (Torque::FS::IsScriptFile(mScriptPath))
       Con::executeFile(mScriptPath, false, false);
 
    mTAMLPath = getOwned() ? expandAssetFilePath(mTAMLFile) : mTAMLPath;
@@ -192,7 +192,7 @@ void GameObjectAsset::setTAMLFile(const char* pTAMLFile)
 
 const char* GameObjectAsset::create()
 {
-   if (!Platform::isFile(mTAMLFile))
+   if (!Torque::FS::IsFile(mTAMLFile))
       return "";
 
    // Set the format mode.

+ 224 - 112
Engine/source/T3D/assets/ImageAsset.cpp

@@ -42,23 +42,30 @@
 
 #include "gfx/gfxStringEnumTranslate.h"
 
+#include "ImageAssetInspectors.h"
+
 // Debug Profiling.
 #include "platform/profiler.h"
 
 #include "T3D/assets/assetImporter.h"
+#include "gfx/gfxDrawUtil.h"
+
+//-----------------------------------------------------------------------------
+
+StringTableEntry ImageAsset::smNoImageAssetFallback(StringTable->insert(Con::getVariable("$Core::NoImageAssetFallback")));
 
 //-----------------------------------------------------------------------------
 
 IMPLEMENT_CONOBJECT(ImageAsset);
 
-ConsoleType(ImageAssetPtr, TypeImageAssetPtr, String, ASSET_ID_FIELD_PREFIX)
+ConsoleType(ImageAssetPtr, TypeImageAssetPtr, const char*, ASSET_ID_FIELD_PREFIX)
 
 //-----------------------------------------------------------------------------
 
 ConsoleGetType(TypeImageAssetPtr)
 {
    // Fetch asset Id.
-   return *((StringTableEntry*)dptr);
+   return *((const char**)(dptr));
 }
 
 //-----------------------------------------------------------------------------
@@ -69,13 +76,7 @@ ConsoleSetType(TypeImageAssetPtr)
    if (argc == 1)
    {
       // Yes, so fetch field value.
-      const char* pFieldValue = argv[0];
-
-      // Fetch asset Id.
-      StringTableEntry* assetId = (StringTableEntry*)(dptr);
-
-      // Update asset value.
-      *assetId = StringTable->insert(pFieldValue);
+      *((const char**)dptr) = StringTable->insert(argv[0]);
 
       return;
    }
@@ -84,7 +85,7 @@ ConsoleSetType(TypeImageAssetPtr)
    Con::warnf("(TypeImageAssetPtr) - Cannot set multiple args to a single asset.");
 }
 
-ConsoleType(assetIdString, TypeImageAssetId, String, ASSET_ID_FIELD_PREFIX)
+ConsoleType(assetIdString, TypeImageAssetId, const char*, ASSET_ID_FIELD_PREFIX)
 
 ConsoleGetType(TypeImageAssetId)
 {
@@ -97,14 +98,7 @@ ConsoleSetType(TypeImageAssetId)
    // Was a single argument specified?
    if (argc == 1)
    {
-      // Yes, so fetch field value.
-      const char* pFieldValue = argv[0];
-
-      // Fetch asset Id.
-      StringTableEntry* assetId = (StringTableEntry*)(dptr);
-
-      // Update asset value.
-      *assetId = StringTable->insert(pFieldValue);
+      *((const char**)dptr) = StringTable->insert(argv[0]);
 
       return;
    }
@@ -117,26 +111,27 @@ ConsoleSetType(TypeImageAssetId)
 ImplementEnumType(ImageAssetType,
    "Type of mesh data available in a shape.\n"
    "@ingroup gameObjects")
-   { ImageAsset::Albedo,      "Albedo",      "" },
-   { ImageAsset::Normal,      "Normal",      "" },
-   { ImageAsset::ORMConfig,   "ORMConfig",   "" },
-   { ImageAsset::GUI,         "GUI",         "" },
-   { ImageAsset::Roughness,   "Roughness",   "" },
-   { ImageAsset::AO,          "AO",          "" },
-   { ImageAsset::Metalness,   "Metalness",   "" },
-   { ImageAsset::Glow,        "Glow",        "" },
-   { ImageAsset::Particle,    "Particle",    "" },
-   { ImageAsset::Decal,       "Decal",       "" },
-   { ImageAsset::Cubemap,     "Cubemap",       "" },
+{ ImageAsset::Albedo, "Albedo", "" },
+{ ImageAsset::Normal,      "Normal",      "" },
+{ ImageAsset::ORMConfig,   "ORMConfig",   "" },
+{ ImageAsset::GUI,         "GUI",         "" },
+{ ImageAsset::Roughness,   "Roughness",   "" },
+{ ImageAsset::AO,          "AO",          "" },
+{ ImageAsset::Metalness,   "Metalness",   "" },
+{ ImageAsset::Glow,        "Glow",        "" },
+{ ImageAsset::Particle,    "Particle",    "" },
+{ ImageAsset::Decal,       "Decal",       "" },
+{ ImageAsset::Cubemap,     "Cubemap",       "" },
 
 EndImplementEnumType;
 
 
 //-----------------------------------------------------------------------------
-ImageAsset::ImageAsset() : AssetBase(), mImage(nullptr), mUseMips(true), mIsHDRImage(false), mIsValidImage(false), mImageType(Albedo)
+ImageAsset::ImageAsset() : AssetBase(), mUseMips(true), mIsHDRImage(false), mIsValidImage(false), mImageType(Albedo)
 {
    mImageFileName = StringTable->EmptyString();
    mImagePath = StringTable->EmptyString();
+   mLoadedState = AssetErrCode::NotLoaded;
 }
 
 //-----------------------------------------------------------------------------
@@ -145,6 +140,15 @@ ImageAsset::~ImageAsset()
 {
 }
 
+
+void ImageAsset::consoleInit()
+{
+   Parent::consoleInit();
+   Con::addVariable("$Core::NoImageAssetFallback", TypeString, &smNoImageAssetFallback,
+      "The assetId of the texture to display when the requested image asset is missing.\n"
+      "@ingroup GFX\n");
+}
+
 //-----------------------------------------------------------------------------
 
 void ImageAsset::initPersistFields()
@@ -163,85 +167,52 @@ void ImageAsset::initPersistFields()
 
 //------------------------------------------------------------------------------
 //Utility function to 'fill out' bindings and resources with a matching asset if one exists
-bool ImageAsset::getAssetByFilename(StringTableEntry fileName, AssetPtr<ImageAsset>* imageAsset)
+U32 ImageAsset::getAssetByFilename(StringTableEntry fileName, AssetPtr<ImageAsset>* imageAsset)
 {
    AssetQuery query;
    S32 foundAssetcount = AssetDatabase.findAssetLooseFile(&query, fileName);
    if (foundAssetcount == 0)
    {
-      //Didn't find any assets
-      //If possible, see if we can run an in-place import and the get the asset from that
-#if TORQUE_DEBUG
-      Con::warnf("ImageAsset::getAssetByFilename - Attempted to in-place import a image file(%s) that had no associated asset", fileName);
-#endif
+      //Didn't work, so have us fall back to a placeholder asset
+      imageAsset->setAssetId(ImageAsset::smNoImageAssetFallback);
 
-      AssetImporter* autoAssetImporter;
-      if (!Sim::findObject("autoAssetImporter", autoAssetImporter))
+      if (imageAsset->isNull())
       {
-         autoAssetImporter = new AssetImporter();
-         autoAssetImporter->registerObject("autoAssetImporter");
+         //Well that's bad, loading the fallback failed.
+         Con::warnf("ImageAsset::getAssetByFilename - Finding of asset associated with file %s failed with no fallback asset", fileName);
+         return AssetErrCode::Failed;
       }
 
-      StringTableEntry resultingAssetId = autoAssetImporter->autoImportFile(fileName);
-
-      if (resultingAssetId != StringTable->EmptyString())
+      //handle noshape not being loaded itself
+      if ((*imageAsset)->mLoadedState == BadFileReference)
       {
-         imageAsset->setAssetId(resultingAssetId);
-
-         if (!imageAsset->isNull())
-            return true;
+         Con::warnf("ImageAsset::getAssetByFilename - Finding of associated with file %s failed, and fallback asset reported error of Bad File Reference.", fileName);
+         return AssetErrCode::BadFileReference;
       }
 
-      //Didn't work, so have us fall back to a placeholder asset
-      imageAsset->setAssetId(StringTable->insert("Core_Rendering:noImage"));
-
-      if (!imageAsset->isNull())
-         return true;
+      Con::warnf("ImageAsset::getAssetByFilename - Finding of associated with file %s failed, utilizing fallback asset", fileName);
 
-      //That didn't work, so fail out
-      return false;
+      (*imageAsset)->mLoadedState = AssetErrCode::UsingFallback;
+      return AssetErrCode::UsingFallback;
    }
    else
    {
       //acquire and bind the asset, and return it out
       imageAsset->setAssetId(query.mAssetList[0]);
-      return true;
+      return (*imageAsset)->mLoadedState;
    }
 }
 
 StringTableEntry ImageAsset::getAssetIdByFilename(StringTableEntry fileName)
 {
-   StringTableEntry imageAssetId = StringTable->EmptyString();
+   if (fileName == StringTable->EmptyString())
+      return StringTable->EmptyString();
+
+   StringTableEntry imageAssetId = ImageAsset::smNoImageAssetFallback;
 
    AssetQuery query;
    S32 foundAssetcount = AssetDatabase.findAssetLooseFile(&query, fileName);
-   if (foundAssetcount == 0)
-   {
-      //Didn't find any assets
-      //If possible, see if we can run an in-place import and the get the asset from that
-#if TORQUE_DEBUG
-      Con::warnf("ImageAsset::getAssetByFilename - Attempted to in-place import a image file(%s) that had no associated asset", fileName);
-#endif
-
-      AssetImporter* autoAssetImporter;
-      if (!Sim::findObject("autoAssetImporter", autoAssetImporter))
-      {
-         autoAssetImporter = new AssetImporter();
-         autoAssetImporter->registerObject("autoAssetImporter");
-      }
-
-      StringTableEntry resultingAssetId = autoAssetImporter->autoImportFile(fileName);
-
-      if (resultingAssetId != StringTable->EmptyString())
-      {
-         imageAssetId = resultingAssetId;
-         return imageAssetId;
-      }
-
-      //Didn't work, so have us fall back to a placeholder asset
-      imageAssetId = StringTable->insert("Core_Rendering:noImage");
-   }
-   else
+   if (foundAssetcount != 0)
    {
       //acquire and bind the asset, and return it out
       imageAssetId = query.mAssetList[0];
@@ -250,22 +221,37 @@ StringTableEntry ImageAsset::getAssetIdByFilename(StringTableEntry fileName)
    return imageAssetId;
 }
 
-bool ImageAsset::getAssetById(StringTableEntry assetId, AssetPtr<ImageAsset>* imageAsset)
+U32 ImageAsset::getAssetById(StringTableEntry assetId, AssetPtr<ImageAsset>* imageAsset)
 {
    (*imageAsset) = assetId;
 
-   if (!imageAsset->isNull())
-      return true;
+   if (imageAsset->notNull())
+   {
+      return (*imageAsset)->mLoadedState;
+   }
+   else
+   {
+      if (imageAsset->isNull())
+      {
+         //Well that's bad, loading the fallback failed.
+         Con::warnf("ImageAsset::getAssetById - Finding of asset with id %s failed with no fallback asset", assetId);
+         return AssetErrCode::Failed;
+      }
 
-   //Didn't work, so have us fall back to a placeholder asset
-   StringTableEntry noImageId = StringTable->insert("Core_Rendering:noMaterial");
-   imageAsset->setAssetId(noImageId);
+      //handle noshape not being loaded itself
+      if ((*imageAsset)->mLoadedState == BadFileReference)
+      {
+         Con::warnf("ImageAsset::getAssetById - Finding of asset with id %s failed, and fallback asset reported error of Bad File Reference.", assetId);
+         return AssetErrCode::BadFileReference;
+      }
 
-   if (!imageAsset->isNull())
-      return true;
+      Con::warnf("ImageAsset::getAssetById - Finding of asset with id %s failed, utilizing fallback asset", assetId);
 
-   return false;
+      (*imageAsset)->mLoadedState = AssetErrCode::UsingFallback;
+      return AssetErrCode::UsingFallback;
+   }
 }
+
 //------------------------------------------------------------------------------
 void ImageAsset::copyTo(SimObject* object)
 {
@@ -275,32 +261,49 @@ void ImageAsset::copyTo(SimObject* object)
 
 void ImageAsset::loadImage()
 {
-   SAFE_DELETE(mImage);
-
    if (mImagePath)
    {
-      if (!Platform::isFile(mImagePath))
+      if (!Torque::FS::IsFile(mImagePath))
       {
          Con::errorf("ImageAsset::initializeAsset: Attempted to load file %s but it was not valid!", mImageFileName);
+         mLoadedState = BadFileReference;
          return;
       }
 
-      mImage.set(mImagePath, &GFXStaticTextureSRGBProfile, avar("%s() - mImage (line %d)", __FUNCTION__, __LINE__));
+      mLoadedState = Ok;
+      mIsValidImage = true;
+      return;
 
-      if (mImage)
+      //GFXTexHandle texture = getTexture(&GFXStaticTextureSRGBProfile);
+
+      //mTexture.set(mImagePath, &GFXStaticTextureSRGBProfile, avar("%s() - mImage (line %d)", __FUNCTION__, __LINE__));
+
+      /*if (texture.isValid())
       {
          mIsValidImage = true;
+
+         //mBitmap = texture.getBitmap();
+
          return;
-      }
+      }*/
+
+      mChangeSignal.trigger();
    }
+   mLoadedState = BadFileReference;
 
    mIsValidImage = false;
 }
 
 void ImageAsset::initializeAsset()
 {
-   mImagePath = expandAssetFilePath(mImageFileName);
+   if (mImageFileName == StringTable->insert("z.png"))
+   {
+      Con::printf("Loaded z");
+   }
+
+   ResourceManager::get().getChangedSignal().notify(this, &ImageAsset::_onResourceChanged);
 
+   mImagePath = expandAssetFilePath(mImageFileName);
    loadImage();
 }
 
@@ -311,6 +314,16 @@ void ImageAsset::onAssetRefresh()
    loadImage();
 }
 
+void ImageAsset::_onResourceChanged(const Torque::Path& path)
+{
+   if (path != Torque::Path(mImagePath))
+      return;
+
+   refreshAsset();
+
+   loadImage();
+}
+
 void ImageAsset::setImageFileName(const char* pScriptFile)
 {
    // Sanity!
@@ -320,24 +333,34 @@ void ImageAsset::setImageFileName(const char* pScriptFile)
    mImageFileName = StringTable->insert(pScriptFile);
 }
 
-GFXTexHandle ImageAsset::getImage(GFXTextureProfile requestedProfile)
+const GBitmap& ImageAsset::getImage()
 {
-   /*if (mResourceMap.contains(requestedProfile))
+   return GBitmap(); //TODO fix this
+}
+
+GFXTexHandle ImageAsset::getTexture(GFXTextureProfile* requestedProfile)
+{
+   if (mResourceMap.contains(requestedProfile))
    {
+      mLoadedState = Ok;
       return mResourceMap.find(requestedProfile)->value;
    }
    else
    {
       //If we don't have an existing map case to the requested format, we'll just create it and insert it in
-      GFXTexHandle newImage;
-      newImage.set(mImageFileName, &requestedProfile, avar("%s() - mImage (line %d)", __FUNCTION__, __LINE__));
-      mResourceMap.insert(requestedProfile, newImage);
-
-      return newImage;
-   }*/
+      GFXTexHandle newTex = TEXMGR->createTexture(mImagePath, requestedProfile);
+      if (newTex)
+      {
+         mResourceMap.insert(requestedProfile, newTex);
+         mLoadedState = Ok;
+         return newTex;
+      }
+      else
+         mLoadedState = BadFileReference;
+   }
 
-   if (mImage.isValid())
-      return mImage;
+   //if (mTexture.isValid())
+   //   return mTexture;
 
    return nullptr;
 }
@@ -348,7 +371,17 @@ const char* ImageAsset::getImageInfo()
    {
       static const U32 bufSize = 2048;
       char* returnBuffer = Con::getReturnBuffer(bufSize);
-      dSprintf(returnBuffer, bufSize, "%s %d %d %d", GFXStringTextureFormat[mImage.getFormat()], mImage.getHeight(), mImage.getWidth(), mImage.getDepth());
+
+      GFXTexHandle newTex = TEXMGR->createTexture(mImagePath, &GFXStaticTextureSRGBProfile);
+      if (newTex)
+      {
+         dSprintf(returnBuffer, bufSize, "%s %d %d %d", GFXStringTextureFormat[newTex->getFormat()], newTex->getHeight(), newTex->getWidth(), newTex->getDepth());
+         newTex = nullptr;
+      }
+      else
+      {
+         dSprintf(returnBuffer, bufSize, "ImageAsset::getImageInfo() - Failed to get image info for %s", getAssetId());
+      }
 
       return returnBuffer;
    }
@@ -384,6 +417,11 @@ const char* ImageAsset::getImageTypeNameFromType(ImageAsset::ImageTypes type)
 
 ImageAsset::ImageTypes ImageAsset::getImageTypeFromName(const char* name)
 {
+   if (dStrIsEmpty(name))
+   {
+      return (ImageTypes)Albedo;
+   }
+
    S32 ret = -1;
    for (S32 i = 0; i < ImageTypeCount; i++)
    {
@@ -414,6 +452,15 @@ DefineEngineMethod(ImageAsset, getImageInfo, const char*, (), ,
    return object->getImageInfo();
 }
 
+#ifdef TORQUE_TOOLS
+DefineEngineStaticMethod(ImageAsset, 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 ImageAsset::getAssetIdByFilename(StringTable->insert(filePath));
+}
+#endif
+
 //-----------------------------------------------------------------------------
 // GuiInspectorTypeAssetId
 //-----------------------------------------------------------------------------
@@ -435,11 +482,16 @@ void GuiInspectorTypeImageAssetPtr::consoleInit()
 
 GuiControl* GuiInspectorTypeImageAssetPtr::constructEditControl()
 {
+   if (mInspector->getInspectObject() == nullptr)
+      return nullptr;
+
    // Create base filename edit controls
    GuiControl* retCtrl = Parent::constructEditControl();
    if (retCtrl == NULL)
       return retCtrl;
 
+   retCtrl->getRenderTooltipDelegate().bind(this, &GuiInspectorTypeImageAssetPtr::renderTooltip);
+
    // Change filespec
    char szBuffer[512];
    dSprintf(szBuffer, sizeof(szBuffer), "AssetBrowser.showDialog(\"ImageAsset\", \"AssetBrowser.changeAsset\", %s, %s);",
@@ -456,8 +508,8 @@ GuiControl* GuiInspectorTypeImageAssetPtr::constructEditControl()
    dSprintf(szBuffer, sizeof(szBuffer), "ShapeEditorPlugin.openShapeAssetId(%d.getText());", retCtrl->getId());
    mImageEdButton->setField("Command", szBuffer);
 
-   char bitmapName[512] = "tools/worldEditor/images/toolbar/shape-editor";
-   mImageEdButton->setBitmap(bitmapName);
+   char bitmapName[512] = "ToolsModule:GameTSCtrl_image";
+   mImageEdButton->setBitmap(StringTable->insert(bitmapName));
 
    mImageEdButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
    mImageEdButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");
@@ -496,6 +548,66 @@ bool GuiInspectorTypeImageAssetPtr::updateRects()
    return resized;
 }
 
+bool GuiInspectorTypeImageAssetPtr::renderTooltip(const Point2I& hoverPos, const Point2I& cursorPos, const char* tipText)
+{
+   if (!mAwake)
+      return false;
+
+   GuiCanvas* root = getRoot();
+   if (!root)
+      return false;
+
+   AssetPtr<ImageAsset> imgAsset;
+   U32 assetState = ImageAsset::getAssetById(getData(), &imgAsset);
+   if (imgAsset == NULL || assetState == ImageAsset::Failed)
+      return false;
+
+   StringTableEntry filename = imgAsset->getImagePath();
+   if (!filename || !filename[0])
+      return false;
+
+   GFXTexHandle texture(filename, &GFXStaticTextureSRGBProfile, avar("%s() - tooltip texture (line %d)", __FUNCTION__, __LINE__));
+   if (texture.isNull())
+      return false;
+
+   // Render image at a reasonable screen size while 
+   // keeping its aspect ratio...
+   Point2I screensize = getRoot()->getWindowSize();
+   Point2I offset = hoverPos;
+   Point2I tipBounds;
+
+   U32 texWidth = texture.getWidth();
+   U32 texHeight = texture.getHeight();
+   F32 aspect = (F32)texHeight / (F32)texWidth;
+
+   const F32 newWidth = 150.0f;
+   F32 newHeight = aspect * newWidth;
+
+   // Offset below cursor image
+   offset.y += 20; // TODO: Attempt to fix?: root->getCursorExtent().y;
+   tipBounds.x = newWidth;
+   tipBounds.y = newHeight;
+
+   // Make sure all of the tooltip will be rendered width the app window,
+   // 5 is given as a buffer against the edge
+   if (screensize.x < offset.x + tipBounds.x + 5)
+      offset.x = screensize.x - tipBounds.x - 5;
+   if (screensize.y < offset.y + tipBounds.y + 5)
+      offset.y = hoverPos.y - tipBounds.y - 5;
+
+   RectI oldClip = GFX->getClipRect();
+   RectI rect(offset, tipBounds);
+   GFX->setClipRect(rect);
+
+   GFXDrawUtil* drawer = GFX->getDrawUtil();
+   drawer->clearBitmapModulation();
+   GFX->getDrawUtil()->drawBitmapStretch(texture, rect);
+
+   GFX->setClipRect(oldClip);
+
+   return true;
+}
+
 IMPLEMENT_CONOBJECT(GuiInspectorTypeImageAssetId);
 
 ConsoleDocClass(GuiInspectorTypeImageAssetId,

+ 479 - 82
Engine/source/T3D/assets/ImageAsset.h

@@ -20,8 +20,7 @@
 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 // IN THE SOFTWARE.
 //-----------------------------------------------------------------------------
-#ifndef IMAGE_ASSET_H
-#define IMAGE_ASSET_H
+#pragma once
 
 #ifndef _ASSET_BASE_H_
 #include "assets/assetBase.h"
@@ -45,7 +44,9 @@
 #include "gfx/bitmap/gBitmap.h"
 #include "gfx/gfxTextureHandle.h"
 
-#include "gui/editor/guiInspectorTypes.h"
+#include "sim/netConnection.h"
+
+#include <string>
 
 //-----------------------------------------------------------------------------
 class ImageAsset : public AssetBase
@@ -70,24 +71,33 @@ public:
       ImageTypeCount = 11
    };
 
+   static StringTableEntry smNoImageAssetFallback;
+
 protected:
    StringTableEntry mImageFileName;
    StringTableEntry mImagePath;
 
-   GFXTexHandle mImage;
-
    bool mIsValidImage;
    bool mUseMips;
    bool mIsHDRImage;
 
    ImageTypes mImageType;
 
-   Map<GFXTextureProfile, GFXTexHandle> mResourceMap;
+   HashMap<GFXTextureProfile*, GFXTexHandle> mResourceMap;
+
+   typedef Signal<void()> ImageAssetChanged;
+   ImageAssetChanged mChangeSignal;
+
+   typedef Signal<void(S32 index)> ImageAssetArrayChanged;
+   ImageAssetArrayChanged mChangeArraySignal;
 
 public:
    ImageAsset();
    virtual ~ImageAsset();
 
+   /// Set up some global script interface stuff.
+   static void consoleInit();
+
    /// Engine.
    static void initPersistFields();
    virtual void copyTo(SimObject* object);
@@ -95,32 +105,40 @@ public:
    /// Declare Console Object.
    DECLARE_CONOBJECT(ImageAsset);
 
-   void                    setImageFileName(const char* pScriptFile);
+   void _onResourceChanged(const Torque::Path& path);
+
+   ImageAssetChanged& getChangedSignal() { return mChangeSignal; }
+   ImageAssetArrayChanged& getChangedArraySignal() { return mChangeArraySignal; }
+
+   void                    setImageFileName(StringTableEntry pScriptFile);
    inline StringTableEntry getImageFileName(void) const { return mImageFileName; };
 
    inline StringTableEntry getImagePath(void) const { return mImagePath; };
 
    bool isValid() { return mIsValidImage; }
 
-   GFXTexHandle getImage(GFXTextureProfile requestedProfile);
+   const GBitmap& getImage();
+   GFXTexHandle getTexture(GFXTextureProfile* requestedProfile);
 
-   const char* getImageInfo();
+   StringTableEntry getImageInfo();
 
-   static const char* getImageTypeNameFromType(ImageTypes type);
-   static ImageTypes getImageTypeFromName(const char* name);
+   static StringTableEntry getImageTypeNameFromType(ImageTypes type);
+   static ImageTypes getImageTypeFromName(StringTableEntry name);
 
    void setImageType(ImageTypes type) { mImageType = type; }
+   ImageTypes getImageType() { return mImageType; }
 
-   static bool getAssetByFilename(StringTableEntry fileName, AssetPtr<ImageAsset>* imageAsset);
+   static U32 getAssetByFilename(StringTableEntry fileName, AssetPtr<ImageAsset>* imageAsset);
    static StringTableEntry getAssetIdByFilename(StringTableEntry fileName);
-   static bool getAssetById(StringTableEntry assetId, AssetPtr<ImageAsset>* imageAsset);
+   static U32 getAssetById(StringTableEntry assetId, AssetPtr<ImageAsset>* imageAsset);
+   static U32 getAssetById(String assetId, AssetPtr<ImageAsset>* imageAsset) { return getAssetById(assetId.c_str(), imageAsset); };
 
 protected:
    virtual void            initializeAsset(void);
    virtual void            onAssetRefresh(void);
 
-   static bool setImageFileName(void* obj, const char* index, const char* data) { static_cast<ImageAsset*>(obj)->setImageFileName(data); return false; }
-   static const char* getImageFileName(void* obj, const char* data) { return static_cast<ImageAsset*>(obj)->getImageFileName(); }
+   static bool setImageFileName(void* obj, StringTableEntry index, StringTableEntry data) { static_cast<ImageAsset*>(obj)->setImageFileName(data); return false; }
+   static StringTableEntry getImageFileName(void* obj, StringTableEntry data) { return static_cast<ImageAsset*>(obj)->getImageFileName(); }
 
    void loadImage();
 };
@@ -131,89 +149,468 @@ DefineConsoleType(TypeImageAssetId, String)
 typedef ImageAsset::ImageTypes ImageAssetType;
 DefineEnumType(ImageAssetType);
 
-class GuiInspectorTypeImageAssetPtr : public GuiInspectorTypeFileName
-{
-   typedef GuiInspectorTypeFileName Parent;
-public:
+#pragma region Singular Asset Macros
+
+//Singular assets
+/// <Summary>
+/// Declares an image asset
+/// This establishes the assetId, asset and legacy filepath fields, along with supplemental getter and setter functions
+/// </Summary>
+#define DECLARE_IMAGEASSET(className, name, changeFunc, profile) public: \
+   GFXTexHandle m##name = NULL;\
+   StringTableEntry m##name##Name; \
+   StringTableEntry m##name##AssetId;\
+   AssetPtr<ImageAsset>  m##name##Asset;\
+   GFXTextureProfile* m##name##Profile = &profile;\
+public: \
+   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<ImageAsset> & get##name##Asset() const { return m##name##Asset; }\
+   void set##name##Asset(const AssetPtr<ImageAsset> &_in) { m##name##Asset = _in;}\
+   \
+   bool _set##name(StringTableEntry _in)\
+   {\
+      if(m##name##AssetId != _in || m##name##Name != _in)\
+      {\
+         if (m##name##Asset.notNull())\
+         {\
+            m##name##Asset->getChangedSignal().remove(this, &className::changeFunc);\
+         }\
+         if (_in == StringTable->EmptyString())\
+         {\
+            m##name##Name = StringTable->EmptyString();\
+            m##name##AssetId = StringTable->EmptyString();\
+            m##name##Asset = NULL;\
+            m##name.free();\
+            m##name = NULL;\
+            return true;\
+         }\
+         else if(_in[0] == '$' || _in[0] == '#')\
+         {\
+            m##name##Name = _in;\
+            m##name##AssetId = StringTable->EmptyString();\
+            m##name##Asset = NULL;\
+            m##name.free();\
+            m##name = NULL;\
+            return true;\
+         }\
+         \
+         if (AssetDatabase.isDeclaredAsset(_in))\
+         {\
+            m##name##AssetId = _in;\
+            \
+            U32 assetState = ImageAsset::getAssetById(m##name##AssetId, &m##name##Asset);\
+            \
+            if (ImageAsset::Ok == assetState)\
+            {\
+               m##name##Name = StringTable->EmptyString();\
+            }\
+         }\
+         else\
+         {\
+            StringTableEntry assetId = ImageAsset::getAssetIdByFilename(_in);\
+            if (assetId != StringTable->EmptyString())\
+            {\
+               m##name##AssetId = assetId;\
+               if (ImageAsset::getAssetById(m##name##AssetId, &m##name##Asset) == ImageAsset::Ok)\
+               {\
+                  m##name##Name = StringTable->EmptyString();\
+               }\
+            }\
+            else\
+            {\
+               m##name##Name = _in;\
+               m##name##AssetId = StringTable->EmptyString();\
+               m##name##Asset = NULL;\
+            }\
+         }\
+      }\
+      if (get##name() != StringTable->EmptyString() && m##name##Name != StringTable->insert("texhandle"))\
+      {\
+         if (m##name##Asset.notNull())\
+         {\
+            m##name##Asset->getChangedSignal().notify(this, &className::changeFunc);\
+         }\
+         \
+         m##name.set(get##name(), m##name##Profile, avar("%s() - mTextureObject (line %d)", __FUNCTION__, __LINE__));\
+      }\
+      else\
+      {\
+         m##name.free();\
+         m##name = NULL;\
+      }\
+      \
+      if(get##name() == StringTable->EmptyString())\
+         return true;\
+      \
+      if (m##name##Asset.notNull() && m##name##Asset->getStatus() != ImageAsset::Ok)\
+      {\
+         Con::errorf("%s(%s)::_set%s() - image asset failure\"%s\" due to [%s]", macroText(className), getName(), macroText(name), _in, ImageAsset::getAssetErrstrn(m##name##Asset->getStatus()).c_str());\
+         return false; \
+      }\
+      else if (bool(m##name) == NULL)\
+      {\
+         Con::errorf("%s(%s)::_set%s() - Couldn't load image \"%s\"", macroText(className), getName(), macroText(name), _in);\
+         return false;\
+      }\
+      return true;\
+   }\
+   \
+   const StringTableEntry get##name() const\
+   {\
+      if (m##name##Asset && (m##name##Asset->getImageFileName() != StringTable->EmptyString()))\
+         return  Platform::makeRelativePathName(m##name##Asset->getImagePath(), Platform::getMainDotCsDir());\
+      else if (m##name##AssetId != StringTable->EmptyString())\
+         return m##name##AssetId;\
+      else if (m##name##Name != StringTable->EmptyString())\
+         return StringTable->insert(Platform::makeRelativePathName(m##name##Name, Platform::getMainDotCsDir()));\
+      else\
+         return StringTable->EmptyString();\
+   }\
+   GFXTexHandle get##name##Resource() \
+   {\
+      return m##name;\
+   }
 
-   GuiBitmapButtonCtrl* mImageEdButton;
+#define DECLARE_IMAGEASSET_SETGET(className, name)\
+   static bool _set##name##Data(void* obj, const char* index, const char* data)\
+   {\
+      bool ret = false;\
+      className* object = static_cast<className*>(obj);\
+      ret = object->_set##name(StringTable->insert(data));\
+      return ret;\
+   }
+
+#define DECLARE_IMAGEASSET_NET_SETGET(className, name, bitmask)\
+   static bool _set##name##Data(void* obj, const char* index, const char* data)\
+   {\
+      bool ret = false;\
+      className* object = static_cast<className*>(obj);\
+      ret = object->_set##name(StringTable->insert(data));\
+      if(ret)\
+         object->setMaskBits(bitmask);\
+      return ret;\
+   }
+
+#define DEF_IMAGEASSET_BINDS(className,name)\
+DefineEngineMethod(className, get##name, const char*, (), , "get name")\
+{\
+   return object->get##name(); \
+}\
+DefineEngineMethod(className, get##name##Asset, const char*, (), , assetText(name, asset reference))\
+{\
+   return object->m##name##AssetId; \
+}\
+DefineEngineMethod(className, set##name, bool, (const char* map), , assetText(name,assignment. first tries asset then flat file.))\
+{\
+    return object->_set##name(StringTable->insert(map));\
+}
 
-   DECLARE_CONOBJECT(GuiInspectorTypeImageAssetPtr);
-   static void consoleInit();
+#define INIT_IMAGEASSET(name) \
+   m##name##Name = StringTable->EmptyString(); \
+   m##name##AssetId = StringTable->EmptyString(); \
+   m##name##Asset = NULL;
 
-   virtual GuiControl* constructEditControl();
-   virtual bool updateRects();
-};
+#ifdef TORQUE_SHOW_LEGACY_FILE_FIELDS
 
-class GuiInspectorTypeImageAssetId : public GuiInspectorTypeImageAssetPtr
-{
-   typedef GuiInspectorTypeImageAssetPtr Parent;
-public:
+#define INITPERSISTFIELD_IMAGEASSET(name, consoleClass, docs) \
+   addProtectedField(#name, TypeImageFilename, Offset(m##name##Name, consoleClass), _set##name##Data, &defaultProtectedGetFn, assetDoc(name, docs)); \
+   addProtectedField(assetText(name, Asset), TypeImageAssetId, Offset(m##name##AssetId, consoleClass), _set##name##Data, &defaultProtectedGetFn, assetDoc(name, asset docs.));
 
-   DECLARE_CONOBJECT(GuiInspectorTypeImageAssetId);
-   static void consoleInit();
-};
+#else
+
+#define INITPERSISTFIELD_IMAGEASSET(name, consoleClass, docs) \
+   addProtectedField(#name, TypeImageFilename, Offset(m##name##Name, consoleClass), _set##name##Data, &defaultProtectedGetFn, assetDoc(name, docs), AbstractClassRep::FIELD_HideInInspectors); \
+   addProtectedField(assetText(name, Asset), TypeImageAssetId, Offset(m##name##AssetId, consoleClass), _set##name##Data, &defaultProtectedGetFn, assetDoc(name, asset docs.));
+
+#endif // SHOW_LEGACY_FILE_FIELDS
+
+#define CLONE_IMAGEASSET(name) \
+   m##name##Name = other.m##name##Name;\
+   m##name##AssetId = other.m##name##AssetId;\
+   m##name##Asset = other.m##name##Asset;
 
-#define assetText(x,suff) std::string(std::string(#x) + std::string(#suff)).c_str()
-
-#define initMapSlot(name) m##name##Filename = String::EmptyString; m##name##AssetId = StringTable->EmptyString(); m##name##Asset = NULL;
-#define bindMapSlot(name) if (m##name##AssetId != String::EmptyString) m##name##Asset = m##name##AssetId;
-
-#define scriptBindMapSlot(name, consoleClass, docs) addField(#name, TypeImageFilename, Offset(m##name##Filename, consoleClass), assetText(name, docs)); \
-                                      addProtectedField(assetText(name, Asset), TypeImageAssetId, Offset(m##name##AssetId, consoleClass), consoleClass::_set##name##Asset, & defaultProtectedGetFn, assetText(name, asset reference.));
-
-#define initMapArraySlot(name,id) m##name##Filename[id] = String::EmptyString; m##name##AssetId[id] = StringTable->EmptyString(); m##name##Asset[id] = NULL;
-#define bindMapArraySlot(name,id) if (m##name##AssetId[id] != String::EmptyString) m##name##Asset[id] = m##name##AssetId[id];
-#define scriptBindMapArraySlot(name, arraySize, consoleClass, docs) addField(#name, TypeImageFilename, Offset(m##name##Filename, consoleClass), arraySize, assetText(name, docs)); \
-                                      addProtectedField(assetText(name,Asset), TypeImageAssetId, Offset(m##name##AssetId, consoleClass), consoleClass::_set##name##AssetSlot, &defaultProtectedGetFn, arraySize, assetText(name,asset reference.));
-
-#define DECLARE_TEXTUREMAP(className,name)      protected: \
-                                      FileName m##name##Filename;\
-                                      StringTableEntry m##name##AssetId;\
-                                      AssetPtr<ImageAsset>  m##name##Asset;\
-                                      public: \
-                                      const String& get##name() const { return m##name##Filename; }\
-                                      void set##name(FileName _in) { m##name##Filename = _in; }\
-                                      const AssetPtr<ImageAsset> & get##name##Asset() const { return m##name##Asset; }\
-                                      void set##name##Asset(AssetPtr<ImageAsset>_in) { m##name##Asset = _in; }\
-static bool _set##name##Asset(void* obj, const char* index, const char* data)\
+#define LOAD_IMAGEASSET(name)\
+if (m##name##AssetId != StringTable->EmptyString())\
 {\
-    className* mat = static_cast<className*>(obj);\
-   mat->m##name##AssetId = StringTable->insert(data);\
-   if (ImageAsset::getAssetById(mat->m##name##AssetId, &mat->m##name##Asset))\
+   S32 assetState = ImageAsset::getAssetById(m##name##AssetId, &m##name##Asset);\
+   if (assetState == ImageAsset::Ok )\
    {\
-      if (mat->m##name##Asset.getAssetId() != StringTable->insert("Core_Rendering:noMaterial"))\
-         mat->m##name##Filename = StringTable->EmptyString();\
-      return true;\
+      m##name##Name = StringTable->EmptyString();\
    }\
-   return true;\
+   else Con::warnf("Warning: %s::LOAD_IMAGEASSET(%s)-%s", mClassName, m##name##AssetId, ImageAsset::getAssetErrstrn(assetState).c_str());\
 }
 
-#define GET_TEXTUREMAP(name)          get##name()
-#define SET_TEXTUREMAP(name,_in)      set##name(_in)
-#define GET_TEXTUREASSET(name)        get##name##Asset()
-#define SET_TEXTUREASSET(name,_in)    set##name##Asset(_in)
+#define PACKDATA_IMAGEASSET(name)\
+   if (stream->writeFlag(m##name##Asset.notNull()))\
+   {\
+      stream->writeString(m##name##Asset.getAssetId());\
+      _set##name(m##name##AssetId);\
+   }\
+   else\
+      stream->writeString(m##name##Name);
 
-#define DECLARE_TEXTUREARRAY(className,name,max) FileName m##name##Filename[max];\
-                                      StringTableEntry m##name##AssetId[max];\
-                                      AssetPtr<ImageAsset>  m##name##Asset[max];\
-static bool _set##name##AssetSlot(void* obj, const char* index, const char* data)\
-{\
-   className* mat = static_cast<className*>(obj);\
-   if (!index) return false;\
-   U32 idx = dAtoi(index);\
-   if (idx >= max)\
-      return false;\
-   mat->m##name##AssetId[idx] = StringTable->insert(data);\
-   if (ImageAsset::getAssetById(mat->m##name##AssetId[idx], &mat->m##name##Asset[idx]))\
-   {\
-      if (mat->m##name##Asset[idx].getAssetId() != StringTable->insert("Core_Rendering:noMaterial"))\
+#define UNPACKDATA_IMAGEASSET(name)\
+   if (stream->readFlag())\
+   {\
+      m##name##AssetId = stream->readSTString();\
+   }\
+   else\
+      m##name##Name = stream->readSTString();
+
+#define PACK_IMAGEASSET(netconn, name)\
+   if (stream->writeFlag(m##name##Asset.notNull()))\
+   {\
+      NetStringHandle assetIdStr = m##name##Asset.getAssetId();\
+      netconn->packNetStringHandleU(stream, assetIdStr);\
+   }\
+   else\
+      stream->writeString(m##name##Name);
+
+#define UNPACK_IMAGEASSET(netconn, name)\
+   if (stream->readFlag())\
+   {\
+      m##name##AssetId = StringTable->insert(netconn->unpackNetStringHandleU(stream).getString());\
+      _set##name(m##name##AssetId);\
+   }\
+   else\
+      m##name##Name = stream->readSTString();
+
+#pragma endregion
+
+#pragma region Arrayed Asset Macros
+
+//Arrayed Assets
+#define DECLARE_IMAGEASSET_ARRAY(className, name, profile, max) public: \
+   static const U32 sm##name##Count = max;\
+   GFXTexHandle m##name[max];\
+   StringTableEntry m##name##Name[max]; \
+   StringTableEntry m##name##AssetId[max];\
+   AssetPtr<ImageAsset>  m##name##Asset[max];\
+   GFXTextureProfile * m##name##Profile = &profile;\
+public: \
+   const StringTableEntry get##name##File(const U32& index) const { return m##name##Name[index]; }\
+   void set##name##File(const FileName &_in, const U32& index) { m##name##Name[index] = StringTable->insert(_in.c_str());}\
+   const AssetPtr<ImageAsset> & get##name##Asset(const U32& index) const { return m##name##Asset[index]; }\
+   void set##name##Asset(const AssetPtr<ImageAsset> &_in, const U32& index) { m##name##Asset[index] = _in;}\
+   \
+   bool _set##name(StringTableEntry _in, const U32& index)\
+   {\
+      if(m##name##AssetId[index] != _in || m##name##Name[index] != _in)\
       {\
-         mat->m##name##Filename[idx] = StringTable->EmptyString();\
+         if(index >= sm##name##Count || index < 0)\
+            return false;\
+         if (_in == StringTable->EmptyString())\
+         {\
+            m##name##Name[index] = StringTable->EmptyString();\
+            m##name##AssetId[index] = StringTable->EmptyString();\
+            m##name##Asset[index] = NULL;\
+            m##name[index].free();\
+            m##name[index] = NULL;\
+            return true;\
+         }\
+         else if(_in[0] == '$' || _in[0] == '#')\
+         {\
+            m##name##Name[index] = _in;\
+            m##name##AssetId[index] = StringTable->EmptyString();\
+            m##name##Asset[index] = NULL;\
+            m##name[index].free();\
+            m##name[index] = NULL;\
+            return true;\
+         }\
+         \
+         if (AssetDatabase.isDeclaredAsset(_in))\
+         {\
+            m##name##AssetId[index] = _in;\
+            \
+            U32 assetState = ImageAsset::getAssetById(m##name##AssetId[index], &m##name##Asset[index]);\
+            \
+            if (ImageAsset::Ok == assetState)\
+            {\
+               m##name##Name[index] = StringTable->EmptyString();\
+            }\
+         }\
+         else\
+         {\
+            StringTableEntry assetId = ImageAsset::getAssetIdByFilename(_in);\
+            if (assetId != StringTable->EmptyString())\
+            {\
+               m##name##AssetId[index] = assetId;\
+               if (ImageAsset::getAssetById(m##name##AssetId[index], &m##name##Asset[index]) == ImageAsset::Ok)\
+               {\
+                  m##name##Name[index] = StringTable->EmptyString();\
+               }\
+            }\
+            else\
+            {\
+               m##name##Name[index] = _in;\
+               m##name##AssetId[index] = StringTable->EmptyString();\
+               m##name##Asset[index] = NULL;\
+            }\
+         }\
+      }\
+      if (get##name(index) != StringTable->EmptyString() && m##name##Name[index] != StringTable->insert("texhandle"))\
+      {\
+         m##name[index].set(get##name(index), m##name##Profile, avar("%s() - mTextureObject (line %d)", __FUNCTION__, __LINE__));\
+      }\
+      else\
+      {\
+         m##name[index].free();\
+         m##name[index] = NULL;\
+      }\
+      \
+      if(get##name(index) == StringTable->EmptyString())\
+         return true;\
+      \
+      if (m##name##Asset[index].notNull() && m##name##Asset[index]->getStatus() != ImageAsset::Ok)\
+      {\
+         Con::errorf("%s(%s)::_set%s(%i) - image asset failure\"%s\" due to [%s]", macroText(className), getName(), macroText(name), index, _in, ImageAsset::getAssetErrstrn(m##name##Asset[index]->getStatus()).c_str());\
+         return false; \
+      }\
+      else if (bool(m##name[index]) == NULL)\
+      {\
+         Con::errorf("%s(%s)::_set%s(%i) - Couldn't load image \"%s\"", macroText(className), getName(), macroText(name), index, _in);\
+         return false; \
       }\
       return true;\
    }\
-   return true;\
+   \
+   const StringTableEntry get##name(const U32& index) const\
+   {\
+      if (m##name##Asset[index] && (m##name##Asset[index]->getImageFileName() != StringTable->EmptyString()))\
+         return  Platform::makeRelativePathName(m##name##Asset[index]->getImagePath(), Platform::getMainDotCsDir());\
+      else if (m##name##AssetId[index] != StringTable->EmptyString())\
+         return m##name##AssetId[index];\
+      else if (m##name##Name[index] != StringTable->EmptyString())\
+         return StringTable->insert(Platform::makeRelativePathName(m##name##Name[index], Platform::getMainDotCsDir()));\
+      else\
+         return StringTable->EmptyString();\
+   }\
+   GFXTexHandle get##name##Resource(const U32& index) \
+   {\
+      if(index >= sm##name##Count || index < 0)\
+         return nullptr;\
+      return m##name[index];\
+   }
+
+#define DECLARE_IMAGEASSET_ARRAY_SETGET(className, name)\
+   static bool _set##name##Data(void* obj, const char* index, const char* data)\
+   {\
+      if (!index) return false;\
+      U32 idx = dAtoi(index);\
+      if (idx >= sm##name##Count)\
+         return false;\
+      bool ret = false;\
+      className* object = static_cast<className*>(obj);\
+      ret = object->_set##name(StringTable->insert(data),idx);\
+      return ret;\
+   }
+
+#define DECLARE_IMAGEASSET_ARRAY_NET_SETGET(className, name, bitmask)\
+   static bool _set##name##Data(void* obj, const char* index, const char* data)\
+   {\
+      if (!index) return false;\
+      U32 idx = dAtoi(index);\
+      if (idx >= sm##name##Count)\
+         return false;\
+      bool ret = false;\
+      className* object = static_cast<className*>(obj);\
+      ret = object->_set##name(StringTable->insert(data),idx);\
+      if(ret)\
+         object->setMaskBits(bitmask);\
+      return ret;\
+   }
+
+#define DEF_IMAGEASSET_ARRAY_BINDS(className,name)\
+DefineEngineMethod(className, get##name, const char*, (S32 index), , "get name")\
+{\
+   return object->get##name(index); \
+}\
+DefineEngineMethod(className, get##name##Asset, const char*, (S32 index), , assetText(name, asset reference))\
+{\
+   if(index >= className::sm##name##Count || index < 0)\
+      return "";\
+   return object->m##name##AssetId[index]; \
+}\
+DefineEngineMethod(className, set##name, bool, (const char* map, S32 index), , assetText(name,assignment. first tries asset then flat file.))\
+{\
+    return object->_set##name(StringTable->insert(map), index);\
+}
+
+#define INIT_IMAGEASSET_ARRAY(name, index) \
+{\
+   m##name##Name[index] = StringTable->EmptyString(); \
+   m##name##AssetId[index] = StringTable->EmptyString(); \
+   m##name##Asset[index] = NULL;\
 }
+
+#ifdef TORQUE_SHOW_LEGACY_FILE_FIELDS
+
+#define INITPERSISTFIELD_IMAGEASSET_ARRAY(name, arraySize, consoleClass, docs) \
+   addProtectedField(#name, TypeImageFilename, Offset(m##name##Name, consoleClass), _set##name##Data, &defaultProtectedGetFn, arraySize, assetDoc(name, docs)); \
+   addProtectedField(assetText(name, Asset), TypeImageAssetId, Offset(m##name##AssetId, consoleClass), _set##name##Data, &defaultProtectedGetFn, arraySize, assetDoc(name, asset docs.));
+
+#else
+
+#define INITPERSISTFIELD_IMAGEASSET_ARRAY(name, arraySize, consoleClass, docs) \
+   addProtectedField(#name, TypeImageFilename, Offset(m##name##Name, consoleClass), _set##name##Data, &defaultProtectedGetFn, arraySize, assetDoc(name, docs), AbstractClassRep::FIELD_HideInInspectors); \
+   addProtectedField(assetText(name, Asset), TypeImageAssetId, Offset(m##name##AssetId, consoleClass), _set##name##Data, &defaultProtectedGetFn, arraySize, assetDoc(name, asset docs.));
+
 #endif
 
+#define CLONE_IMAGEASSET_ARRAY(name, index) \
+{\
+   m##name##Name[index] = other.m##name##Name[index];\
+   m##name##AssetId[index] = other.m##name##AssetId[index];\
+   m##name##Asset[index] = other.m##name##Asset[index];\
+}
+
+#define LOAD_IMAGEASSET_ARRAY(name, index)\
+if (m##name##AssetId[index] != StringTable->EmptyString())\
+{\
+   S32 assetState = ImageAsset::getAssetById(m##name##AssetId[index], &m##name##Asset[index]);\
+   if (assetState == ImageAsset::Ok )\
+   {\
+      m##name##Name[index] = StringTable->EmptyString();\
+   }\
+   else Con::warnf("Warning: %s::LOAD_IMAGEASSET(%s)-%s", mClassName, m##name##AssetId[index], ImageAsset::getAssetErrstrn(assetState).c_str());\
+}
+
+#define PACKDATA_IMAGEASSET_ARRAY(name, index)\
+   if (stream->writeFlag(m##name##Asset[index].notNull()))\
+   {\
+      stream->writeString(m##name##Asset[index].getAssetId());\
+   }\
+   else\
+      stream->writeString(m##name##Name[index]);
+
+#define UNPACKDATA_IMAGEASSET_ARRAY(name, index)\
+   if (stream->readFlag())\
+   {\
+      m##name##AssetId[index] = stream->readSTString();\
+      _set##name(m##name##AssetId[index], index);\
+   }\
+   else\
+      m##name##Name[index] = stream->readSTString();
+
+#define PACK_IMAGEASSET_ARRAY(netconn, name, index)\
+   if (stream->writeFlag(m##name##Asset[index].notNull()))\
+   {\
+      NetStringHandle assetIdStr = m##name##Asset[index].getAssetId();\
+      netconn->packNetStringHandleU(stream, assetIdStr);\
+   }\
+   else\
+      stream->writeString(m##name##Name[index]);
+
+#define UNPACK_IMAGEASSET_ARRAY(netconn, name, index)\
+   if (stream->readFlag())\
+   {\
+      m##name##AssetId[index] = StringTable->insert(netconn->unpackNetStringHandleU(stream).getString());\
+      _set##name(m##name##AssetId[index], index);\
+   }\
+   else\
+      m##name##Name[index] = stream->readSTString();
+
+#pragma endregion
+
+

+ 31 - 0
Engine/source/T3D/assets/ImageAssetInspectors.h

@@ -0,0 +1,31 @@
+#pragma once
+
+#include "ImageAsset.h"
+
+#ifndef _GUI_INSPECTOR_TYPES_H_
+#include "gui/editor/guiInspectorTypes.h"
+#endif
+
+class GuiInspectorTypeImageAssetPtr : public GuiInspectorTypeFileName
+{
+   typedef GuiInspectorTypeFileName Parent;
+public:
+
+   GuiBitmapButtonCtrl* mImageEdButton;
+
+   DECLARE_CONOBJECT(GuiInspectorTypeImageAssetPtr);
+   static void consoleInit();
+
+   virtual GuiControl* constructEditControl();
+   virtual bool updateRects();
+   bool renderTooltip(const Point2I& hoverPos, const Point2I& cursorPos, const char* tipText = NULL);
+};
+
+class GuiInspectorTypeImageAssetId : public GuiInspectorTypeImageAssetPtr
+{
+   typedef GuiInspectorTypeImageAssetPtr Parent;
+public:
+
+   DECLARE_CONOBJECT(GuiInspectorTypeImageAssetId);
+   static void consoleInit();
+};

+ 40 - 36
Engine/source/T3D/assets/LevelAsset.cpp

@@ -47,14 +47,14 @@
 
 IMPLEMENT_CONOBJECT(LevelAsset);
 
-ConsoleType(LevelAssetPtr, TypeLevelAssetPtr, String, ASSET_ID_FIELD_PREFIX)
+ConsoleType(LevelAssetPtr, TypeLevelAssetPtr, const char*, ASSET_ID_FIELD_PREFIX)
 
 //-----------------------------------------------------------------------------
 
 ConsoleGetType(TypeLevelAssetPtr)
 {
    // Fetch asset Id.
-   return *((StringTableEntry*)dptr);
+   return *((const char**)(dptr));
 }
 
 //-----------------------------------------------------------------------------
@@ -65,13 +65,7 @@ ConsoleSetType(TypeLevelAssetPtr)
    if (argc == 1)
    {
       // Yes, so fetch field value.
-      const char* pFieldValue = argv[0];
-
-      // Fetch asset Id.
-      StringTableEntry* assetId = (StringTableEntry*)(dptr);
-
-      // Update asset value.
-      *assetId = StringTable->insert(pFieldValue);
+      *((const char**)dptr) = StringTable->insert(argv[0]);
 
       return;
    }
@@ -86,14 +80,12 @@ LevelAsset::LevelAsset() : AssetBase(), mIsSubLevel(false)
 {
    mLevelName = StringTable->EmptyString();
    mLevelFile = StringTable->EmptyString();
-   mPreviewImage = StringTable->EmptyString();
    mPostFXPresetFile = StringTable->EmptyString();
    mDecalsFile = StringTable->EmptyString();
    mForestFile = StringTable->EmptyString();
    mNavmeshFile = StringTable->EmptyString();
 
    mLevelPath = StringTable->EmptyString();
-   mPreviewImagePath = StringTable->EmptyString();
    mPostFXPresetPath = StringTable->EmptyString();
    mDecalsPath = StringTable->EmptyString();
    mForestPath = StringTable->EmptyString();
@@ -104,6 +96,9 @@ LevelAsset::LevelAsset() : AssetBase(), mIsSubLevel(false)
 
    mEditorFile = StringTable->EmptyString();
    mBakedSceneFile = StringTable->EmptyString();
+
+   mPreviewImageAssetId = StringTable->EmptyString();
+   mPreviewImageAsset = StringTable->EmptyString();
 }
 
 //-----------------------------------------------------------------------------
@@ -122,8 +117,6 @@ void LevelAsset::initPersistFields()
    addProtectedField("LevelFile", TypeAssetLooseFilePath, Offset(mLevelFile, LevelAsset),
       &setLevelFile, &getLevelFile, "Path to the actual level file.");
    addField("LevelName", TypeString, Offset(mLevelName, LevelAsset), "Human-friendly name for the level.");
-   addProtectedField("PreviewImage", TypeAssetLooseFilePath, Offset(mPreviewImage, LevelAsset),
-      &setPreviewImageFile, &getPreviewImageFile, "Path to the image used for selection preview.");
 
    addProtectedField("PostFXPresetFile", TypeAssetLooseFilePath, Offset(mPostFXPresetFile, LevelAsset),
       &setPostFXPresetFile, &getPostFXPresetFile, "Path to the level's postFXPreset.");
@@ -157,24 +150,32 @@ void LevelAsset::initializeAsset()
    // Call parent.
    Parent::initializeAsset();
 
-   // Ensure the image-file is expanded.
-   mPreviewImagePath = expandAssetFilePath(mPreviewImage);
-   mLevelPath = expandAssetFilePath(mLevelFile);
-   mPostFXPresetPath = expandAssetFilePath(mPostFXPresetFile);
-   mDecalsPath = expandAssetFilePath(mDecalsFile);
-   mForestPath = expandAssetFilePath(mForestFile);
-   mNavmeshPath = expandAssetFilePath(mNavmeshFile);
+   loadAsset();
 }
 
 void LevelAsset::onAssetRefresh(void)
+{
+   loadAsset();
+}
+
+void LevelAsset::loadAsset()
 {
    // Ensure the image-file is expanded.
-   mPreviewImagePath = expandAssetFilePath(mPreviewImage);
    mLevelPath = expandAssetFilePath(mLevelFile);
    mPostFXPresetPath = expandAssetFilePath(mPostFXPresetFile);
    mDecalsPath = expandAssetFilePath(mDecalsFile);
    mForestPath = expandAssetFilePath(mForestFile);
    mNavmeshPath = expandAssetFilePath(mNavmeshFile);
+
+   StringTableEntry previewImageAssetId = getAssetDependencyField("previewImageAsset");
+
+   if (previewImageAssetId != StringTable->EmptyString())
+   {
+      mPreviewImageAssetId = previewImageAssetId;
+
+      AssetPtr<ImageAsset> previewImgAsset = mPreviewImageAssetId;
+      mPreviewImageAsset = previewImgAsset;
+   }
 }
 
 //
@@ -197,23 +198,19 @@ void LevelAsset::setLevelFile(const char* pLevelFile)
    refreshAsset();
 }
 
-void LevelAsset::setImageFile(const char* pImageFile)
+StringTableEntry LevelAsset::getPreviewImageAsset() const
 {
-   // Sanity!
-   AssertFatal(pImageFile != NULL, "Cannot use a NULL image file.");
-
-   // Fetch image file.
-   pImageFile = StringTable->insert(pImageFile);
-
-   // Ignore no change,
-   if (pImageFile == mPreviewImage)
-      return;
+   return mPreviewImageAssetId;
+}
 
-   // Update.
-   mPreviewImage = pImageFile;
+StringTableEntry LevelAsset::getPreviewImagePath(void) const
+{
+   if (mPreviewImageAsset.notNull() && mPreviewImageAsset->isAssetValid())
+   {
+      return mPreviewImageAsset->getImagePath();
+   }
 
-   // Refresh the asset.
-   refreshAsset();
+   return StringTable->EmptyString();
 }
 
 void LevelAsset::setEditorFile(const char* pEditorFile)
@@ -368,11 +365,18 @@ DefineEngineMethod(LevelAsset, getLevelPath, const char*, (),,
    return object->getLevelPath();
 }
 
+DefineEngineMethod(LevelAsset, getPreviewImageAsset, const char*, (), ,
+   "Gets the full path of the asset's defined preview image file.\n"
+   "@return The string result of the level preview image path")
+{
+   return object->getPreviewImageAsset();
+}
+
 DefineEngineMethod(LevelAsset, getPreviewImagePath, const char*, (), ,
    "Gets the full path of the asset's defined preview image file.\n"
    "@return The string result of the level preview image path")
 {
-   return object->getImagePath();
+   return object->getPreviewImagePath();
 }
 
 DefineEngineMethod(LevelAsset, getPostFXPresetPath, const char*, (), ,

+ 9 - 7
Engine/source/T3D/assets/LevelAsset.h

@@ -38,6 +38,7 @@
 #ifndef _ASSET_FIELD_TYPES_H_
 #include "assets/assetFieldTypes.h"
 #endif
+#include "T3D/assets/ImageAsset.h"
 
 //-----------------------------------------------------------------------------
 class LevelAsset : public AssetBase
@@ -50,14 +51,12 @@ class LevelAsset : public AssetBase
    StringTableEntry        mDecalsFile;
    StringTableEntry        mForestFile;
    StringTableEntry        mNavmeshFile;
-   StringTableEntry        mPreviewImage;
 
    StringTableEntry        mLevelPath;
    StringTableEntry        mPostFXPresetPath;
    StringTableEntry        mDecalsPath;
    StringTableEntry        mForestPath;
    StringTableEntry        mNavmeshPath;
-   StringTableEntry        mPreviewImagePath;
 
    StringTableEntry        mEditorFile;
    StringTableEntry        mBakedSceneFile;
@@ -69,6 +68,9 @@ class LevelAsset : public AssetBase
 
    Vector<AssetBase*>      mAssetDependencies;
 
+   StringTableEntry mPreviewImageAssetId;
+   AssetPtr<ImageAsset> mPreviewImageAsset;
+
 public:
    LevelAsset();
    virtual ~LevelAsset();
@@ -93,15 +95,16 @@ public:
    inline StringTableEntry getForestFile(void) const { return mForestFile; };
    void                    setNavmeshFile(const char* pNavmeshFile);
    inline StringTableEntry getNavmeshFile(void) const { return mNavmeshFile; };
-   void                    setImageFile(const char* pImageFile);
-   inline StringTableEntry getImageFile(void) const { return mPreviewImage; };
+
+   StringTableEntry        getPreviewImageAsset(void) const;
 
    inline StringTableEntry getLevelPath(void) const { return mLevelPath; };
    inline StringTableEntry getPostFXPresetPath(void) const { return mPostFXPresetPath; };
    inline StringTableEntry getDecalsPath(void) const { return mDecalsPath; };
    inline StringTableEntry getForestPath(void) const { return mForestPath; };
    inline StringTableEntry getNavmeshPath(void) const { return mNavmeshPath; };
-   inline StringTableEntry getImagePath(void) const { return mPreviewImagePath; };
+
+   StringTableEntry        getPreviewImagePath(void) const;
 
    void                    setEditorFile(const char* pEditorFile);
    inline StringTableEntry getEditorFile(void) const { return mEditorFile; };
@@ -113,8 +116,6 @@ public:
 protected:
    static bool setLevelFile(void *obj, const char *index, const char *data) { static_cast<LevelAsset*>(obj)->setLevelFile(data); return false; }
    static const char* getLevelFile(void* obj, const char* data) { return static_cast<LevelAsset*>(obj)->getLevelFile(); }
-   static bool setPreviewImageFile(void *obj, const char *index, const char *data) { static_cast<LevelAsset*>(obj)->setImageFile(data); return false; }
-   static const char* getPreviewImageFile(void* obj, const char* data) { return static_cast<LevelAsset*>(obj)->getImageFile(); }
 
    static bool setEditorFile(void* obj, const char* index, const char* data) { static_cast<LevelAsset*>(obj)->setEditorFile(data); return false; }
    static const char* getEditorFile(void* obj, const char* data) { return static_cast<LevelAsset*>(obj)->getEditorFile(); }
@@ -134,6 +135,7 @@ protected:
 
    virtual void            initializeAsset(void);
    virtual void            onAssetRefresh(void);
+   void                    loadAsset();
 };
 
 DefineConsoleType(TypeLevelAssetPtr, LevelAsset)

+ 137 - 115
Engine/source/T3D/assets/MaterialAsset.cpp

@@ -43,6 +43,8 @@
 
 #include "T3D/assets/assetImporter.h"
 
+StringTableEntry MaterialAsset::smNoMaterialAssetFallback(StringTable->insert(Con::getVariable("$Core::NoMaterialAssetFallback")));
+
 //-----------------------------------------------------------------------------
 
 IMPLEMENT_CONOBJECT(MaterialAsset);
@@ -89,7 +91,7 @@ ConsoleSetType(TypeMaterialAssetPtr)
 }
 
 
-ConsoleType(assetIdString, TypeMaterialAssetId, String, ASSET_ID_FIELD_PREFIX)
+ConsoleType(assetIdString, TypeMaterialAssetId, const char*, ASSET_ID_FIELD_PREFIX)
 
 ConsoleGetType(TypeMaterialAssetId)
 {
@@ -125,16 +127,26 @@ MaterialAsset::MaterialAsset()
    mScriptFile = StringTable->EmptyString();
    mScriptPath = StringTable->EmptyString();
    mMatDefinitionName = StringTable->EmptyString();
+   mMaterialDefinition = nullptr;
 }
 
 //-----------------------------------------------------------------------------
 
 MaterialAsset::~MaterialAsset()
 {
+   //SAFE_DELETE(mMaterialDefinition);
 }
 
 //-----------------------------------------------------------------------------
 
+void MaterialAsset::consoleInit()
+{
+   Parent::consoleInit();
+   Con::addVariable("$Core::NoMaterialAssetFallback", TypeString, &smNoMaterialAssetFallback,
+      "The assetId of the material to display when the requested material asset is missing.\n"
+      "@ingroup GFX\n");
+}
+
 void MaterialAsset::initPersistFields()
 {
    // Call parent.
@@ -152,32 +164,22 @@ void MaterialAsset::initializeAsset()
    // Call parent.
    Parent::initializeAsset();
 
-   compileShader();
-
    mScriptPath = getOwned() ? expandAssetFilePath(mScriptFile) : mScriptPath;
 
-   if (Platform::isFile(mScriptPath))
+   if (Torque::FS::IsScriptFile(mScriptPath))
       Con::executeFile(mScriptPath, false, false);
+
+   loadMaterial();
 }
 
 void MaterialAsset::onAssetRefresh()
 {
    mScriptPath = getOwned() ? expandAssetFilePath(mScriptFile) : mScriptPath;
 
-   if (Platform::isFile(mScriptPath))
+   if (Torque::FS::IsScriptFile(mScriptPath))
       Con::executeFile(mScriptPath, false, false);
 
-   if (mMatDefinitionName != StringTable->EmptyString())
-   {
-      Material* matDef;
-      if (!Sim::findObject(mMatDefinitionName, matDef))
-      {
-         Con::errorf("MaterialAsset: Unable to find the Material %s", mMatDefinitionName);
-         return;
-      }
-
-      matDef->reload();
-   }
+   loadMaterial();
 }
 
 void MaterialAsset::setScriptFile(const char* pScriptFile)
@@ -197,142 +199,162 @@ void MaterialAsset::setScriptFile(const char* pScriptFile)
 
 //------------------------------------------------------------------------------
 
-void MaterialAsset::compileShader()
+void MaterialAsset::loadMaterial()
 {
+   if (mMaterialDefinition)
+      SAFE_DELETE(mMaterialDefinition);
+
+   if (mMatDefinitionName != StringTable->EmptyString())
+   {
+      Material* matDef;
+      if (!Sim::findObject(mMatDefinitionName, matDef))
+      {
+         Con::errorf("MaterialAsset: Unable to find the Material %s", mMatDefinitionName);
+         mLoadedState = BadFileReference;
+         return;
+      }
+
+      mMaterialDefinition = matDef;
+
+      mLoadedState = Ok;
+
+      mMaterialDefinition->reload();
+      return;
+   }
+
+   mLoadedState = Failed;
 }
 
+//------------------------------------------------------------------------------
+
 void MaterialAsset::copyTo(SimObject* object)
 {
    // Call to parent.
    Parent::copyTo(object);
 }
 
-DefineEngineMethod(MaterialAsset, compileShader, void, (), , "Compiles the material's generated shader, if any. Not yet implemented\n")
-{
-   object->compileShader();
-}
-
 //------------------------------------------------------------------------------
-StringTableEntry MaterialAsset::getAssetIdByMaterialName(StringTableEntry matName)
+U32 MaterialAsset::getAssetByMaterialName(StringTableEntry matName, AssetPtr<MaterialAsset>* matAsset)
 {
-   StringTableEntry materialAssetId = StringTable->EmptyString();
-
-   AssetQuery* query = new AssetQuery();
-   U32 foundCount = AssetDatabase.findAssetType(query, "MaterialAsset");
-   if (foundCount == 0)
+   AssetQuery query;
+   U32 foundAssetcount = AssetDatabase.findAssetType(&query, "MaterialAsset");
+   if (foundAssetcount == 0)
    {
       //Didn't work, so have us fall back to a placeholder asset
-      materialAssetId = StringTable->insert("Core_Rendering:noMaterial");
-   }
-   else
-   {
-      for (U32 i = 0; i < foundCount; i++)
+      matAsset->setAssetId(MaterialAsset::smNoMaterialAssetFallback);
+
+      if (matAsset->isNull())
       {
-         MaterialAsset* matAsset = AssetDatabase.acquireAsset<MaterialAsset>(query->mAssetList[i]);
-         if (matAsset && matAsset->getMaterialDefinitionName() == matName)
-         {
-            materialAssetId = matAsset->getAssetId();
-            break;
-         }
-         AssetDatabase.releaseAsset(query->mAssetList[i]); //cleanup if that's not the one we needed
+         //Well that's bad, loading the fallback failed.
+         Con::warnf("MaterialAsset::getAssetByMaterialName - Finding of asset associated with material name %s failed with no fallback asset", matName);
+         return AssetErrCode::Failed;
       }
 
-      if (materialAssetId == StringTable->EmptyString())
+      //handle noshape not being loaded itself
+      if ((*matAsset)->mLoadedState == BadFileReference)
       {
-         //Try auto-importing it if it exists already
-         BaseMaterialDefinition* baseMatDef;
-         if (!Sim::findObject(matName, baseMatDef))
-         {
-            //Not even a real material, apparently?
-            //return back a blank
-            return StringTable->EmptyString();
-         }
-
-         //Ok, a real mat def, we can work with this
-#if TORQUE_DEBUG
-         Con::warnf("MaterialAsset::getAssetIdByMaterialName - Attempted to in-place import a material(%s) that had no associated asset", matName);
-#endif
-
-         AssetImporter* autoAssetImporter;
-         if (!Sim::findObject("autoAssetImporter", autoAssetImporter))
-         {
-            autoAssetImporter = new AssetImporter();
-            autoAssetImporter->registerObject("autoAssetImporter");
-         }
-
-         autoAssetImporter->resetImportSession(true);
-
-         String originalMaterialDefFile = Torque::Path(baseMatDef->getFilename()).getPath();
-
-         autoAssetImporter->setTargetPath(originalMaterialDefFile);
-
-         autoAssetImporter->resetImportConfig();
-
-         AssetImportObject* assetObj = autoAssetImporter->addImportingAsset("MaterialAsset", originalMaterialDefFile, nullptr, matName);
+         Con::warnf("ShapeAsset::getAssetByMaterialName - Finding of associated with aterial name %s failed, and fallback asset reported error of Bad File Reference.", matName);
+         return AssetErrCode::BadFileReference;
+      }
 
-         //Find out if the filepath has an associated module to it. If we're importing in-place, it needs to be within a module's directory
-         ModuleDefinition* targetModuleDef = AssetImporter::getModuleFromPath(originalMaterialDefFile);
+      Con::warnf("ShapeAsset::getAssetByMaterialName - Finding of associated with aterial name %s failed, utilizing fallback asset", matName);
 
-         if (targetModuleDef == nullptr)
-         {
-            return StringTable->EmptyString();
-         }
-         else
+      (*matAsset)->mLoadedState = AssetErrCode::UsingFallback;
+      return AssetErrCode::UsingFallback;
+   }
+   else
+   {
+      for (U32 i = 0; i < foundAssetcount; i++)
+      {
+         MaterialAsset* tMatAsset = AssetDatabase.acquireAsset<MaterialAsset>(query.mAssetList[i]);
+         if (tMatAsset && tMatAsset->getMaterialDefinitionName() == matName)
          {
-            autoAssetImporter->setTargetModuleId(targetModuleDef->getModuleId());
+            matAsset->setAssetId(query.mAssetList[i]);
+            AssetDatabase.releaseAsset(query.mAssetList[i]);
+            return (*matAsset)->mLoadedState;
          }
+         AssetDatabase.releaseAsset(query.mAssetList[i]); //cleanup if that's not the one we needed
+      }
+   }
+}
 
-         autoAssetImporter->processImportAssets();
-
-         bool hasIssues = autoAssetImporter->validateAssets();
-
-         if (hasIssues)
-         {
-            //log it
-            Con::errorf("Error! Import process of Material(%s) has failed due to issues discovered during validation!", matName);
-            return StringTable->EmptyString();
-         }
-         else
-         {
-            autoAssetImporter->importAssets();
-         }
+StringTableEntry MaterialAsset::getAssetIdByMaterialName(StringTableEntry matName)
+{
+   if (matName == StringTable->EmptyString())
+      return StringTable->EmptyString();
 
-#if TORQUE_DEBUG
-         autoAssetImporter->dumpActivityLog();
-#endif
+   StringTableEntry materialAssetId = MaterialAsset::smNoMaterialAssetFallback;
 
-         if (hasIssues)
-         {
-            return StringTable->EmptyString();
-         }
-         else
+   AssetQuery query;
+   U32 foundCount = AssetDatabase.findAssetType(&query, "MaterialAsset");
+   if (foundCount != 0)
+   {
+      for (U32 i = 0; i < foundCount; i++)
+      {
+         MaterialAsset* matAsset = AssetDatabase.acquireAsset<MaterialAsset>(query.mAssetList[i]);
+         if (matAsset && matAsset->getMaterialDefinitionName() == matName)
          {
-            String assetId = autoAssetImporter->getTargetModuleId() + ":" + assetObj->assetName;
-            return StringTable->insert(assetId.c_str());
+            materialAssetId = matAsset->getAssetId();
+            AssetDatabase.releaseAsset(query.mAssetList[i]);
+            break;
          }
+         AssetDatabase.releaseAsset(query.mAssetList[i]);
       }
    }
 
    return materialAssetId;
 }
 
-bool MaterialAsset::getAssetById(StringTableEntry assetId, AssetPtr<MaterialAsset>* materialAsset)
+U32 MaterialAsset::getAssetById(StringTableEntry assetId, AssetPtr<MaterialAsset>* materialAsset)
 {
    (*materialAsset) = assetId;
 
-   if (!materialAsset->isNull())
-      return true;
+   if (materialAsset->notNull())
+   {
+      return (*materialAsset)->mLoadedState;
+   }
+   else
+   {
+      //Didn't work, so have us fall back to a placeholder asset
+      materialAsset->setAssetId(MaterialAsset::smNoMaterialAssetFallback);
+
+      if (materialAsset->isNull())
+      {
+         //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;
+      }
 
-   //Didn't work, so have us fall back to a placeholder asset
-   StringTableEntry noImageId = StringTable->insert("Core_Rendering:noMaterial");
-   materialAsset->setAssetId(noImageId);
+      //handle noshape not being loaded itself
+      if ((*materialAsset)->mLoadedState == BadFileReference)
+      {
+         Con::warnf("MaterialAsset::getAssetById - Finding of asset with id %s failed, and fallback asset reported error of Bad File Reference.", assetId);
+         return AssetErrCode::BadFileReference;
+      }
 
-   if (!materialAsset->isNull())
-      return true;
+      Con::warnf("MaterialAsset::getAssetById - Finding of asset with id %s failed, utilizing fallback asset", assetId);
 
-   return false;
+      (*materialAsset)->mLoadedState = AssetErrCode::UsingFallback;
+      return AssetErrCode::UsingFallback;
+   }
 }
 
+#ifdef TORQUE_TOOLS
+DefineEngineStaticMethod(MaterialAsset, getAssetIdByMaterialName, const char*, (const char* materialName), (""),
+   "Queries the Asset Database to see if any asset exists that is associated with the provided material name.\n"
+   "@return The AssetId of the associated asset, if any.")
+{
+   return MaterialAsset::getAssetIdByMaterialName(StringTable->insert(materialName));
+}
+
+DefineEngineMethod(MaterialAsset, getScriptPath, const char*, (), ,
+   "Queries the Asset Database to see if any asset exists that is associated with the provided material name.\n"
+   "@return The AssetId of the associated asset, if any.")
+{
+   return object->getScriptPath();
+}
+#endif
+
 //-----------------------------------------------------------------------------
 // GuiInspectorTypeAssetId
 //-----------------------------------------------------------------------------
@@ -373,13 +395,13 @@ GuiControl* GuiInspectorTypeMaterialAssetPtr::constructEditControl()
    dSprintf(szBuffer, sizeof(szBuffer), "AssetBrowser.editAsset(%d.getText());", retCtrl->getId());
    mEditButton->setField("Command", szBuffer);
 
-   char bitmapName[512] = "tools/worldEditor/images/toolbar/material-editor";
-   mEditButton->setBitmap(bitmapName);
+   char bitmapName[512] = "ToolsModule:material_editor_n_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, "Open this file in the Material Editor");
+   mEditButton->setDataField(StringTable->insert("tooltip"), NULL, "Open this asset in the Material Editor");
 
    mEditButton->registerObject();
    addObject(mEditButton);

+ 196 - 132
Engine/source/T3D/assets/MaterialAsset.h

@@ -53,6 +53,7 @@
 #include "materials/matTextureTarget.h"
 #include "materials/materialDefinition.h"
 #include "materials/customMaterialDefinition.h"
+#include "materials/materialManager.h"
 
 //-----------------------------------------------------------------------------
 class MaterialAsset : public AssetBase
@@ -64,25 +65,42 @@ class MaterialAsset : public AssetBase
    StringTableEntry        mScriptPath;
    StringTableEntry        mMatDefinitionName;
 
+   SimObjectPtr<Material>  mMaterialDefinition;
+
+public:
+   static StringTableEntry smNoMaterialAssetFallback;
+
 public:
    MaterialAsset();
    virtual ~MaterialAsset();
 
+   /// Set up some global script interface stuff.
+   static void consoleInit();
+
    /// Engine.
    static void initPersistFields();
    virtual void copyTo(SimObject* object);
 
-   void compileShader();
+   void loadMaterial();
 
    StringTableEntry getMaterialDefinitionName() { return mMatDefinitionName; }
+   SimObjectPtr<Material> getMaterialDefinition() { return mMaterialDefinition; }
 
    void                    setScriptFile(const char* pScriptFile);
    inline StringTableEntry getScriptFile(void) const { return mScriptFile; };
 
    inline StringTableEntry getScriptPath(void) const { return mScriptPath; };
 
-   static StringTableEntry getAssetIdByMaterialName(StringTableEntry fileName);
-   static bool getAssetById(StringTableEntry assetId, AssetPtr<MaterialAsset>* materialAsset);
+   /// <summary>
+   /// Looks for any assets that uses the provided Material Definition name.
+   /// If none are found, attempts to auto-import the material definition if the
+   /// material definition exists.
+   /// </summary>
+   /// <param name="matName">Material Definition name to look for</param>
+   /// <returns>AssetId of matching asset.</returns>
+   static StringTableEntry getAssetIdByMaterialName(StringTableEntry matName);
+   static U32 getAssetById(StringTableEntry assetId, AssetPtr<MaterialAsset>* materialAsset);
+   static U32 getAssetByMaterialName(StringTableEntry matName, AssetPtr<MaterialAsset>* matAsset);
 
    /// Declare Console Object.
    DECLARE_CONOBJECT(MaterialAsset);
@@ -124,159 +142,203 @@ public:
    static void consoleInit();
 };
 
-#define assetText(x,suff) std::string(std::string(#x) + std::string(#suff)).c_str()
-
-#define initMaterialAsset(name) m##name##Name = ""; m##name##AssetId = StringTable->EmptyString(); m##name##Asset = NULL;
-#define bindMaterialAsset(name) if (m##name##AssetId != StringTable->EmptyString()) m##name##Asset = m##name##AssetId;
-
-#define scriptBindMaterialAsset(name, consoleClass, docs)\
-   addProtectedField(assetText(name, File), TypeMaterialName, Offset(m##name##Name, consoleClass), consoleClass::_set##name##Name,  & defaultProtectedGetFn, assetText(name, docs), AbstractClassRep::FIELD_HideInInspectors); \
-   addProtectedField(assetText(name, Asset), TypeMaterialAssetId, Offset(m##name##AssetId, consoleClass), consoleClass::_set##name##Asset, & defaultProtectedGetFn, assetText(name, asset reference.));
-
-#define DECLARE_MATERIALASSET(className,name)      protected: \
-                                      String m##name##Name;\
-                                      StringTableEntry m##name##AssetId;\
-                                      AssetPtr<MaterialAsset>  m##name##Asset;\
-                                      public: \
-                                      const String& get##name() const { return m##name##Name; }\
-                                      void set##name(FileName _in) { m##name##Name = _in; }\
-                                      const AssetPtr<MaterialAsset> & get##name##Asset() const { return m##name##Asset; }\
-                                      void set##name##Asset(AssetPtr<MaterialAsset>_in) { m##name##Asset = _in; }\
-static bool _set##name##Name(void* obj, const char* index, const char* data)\
-{\
-   className* shape = static_cast<className*>(obj);\
+#pragma region Singular Asset Macros
+
+//Singular assets
+/// <Summary>
+/// Declares an material asset
+/// This establishes the assetId, asset and legacy filepath fields, along with supplemental getter and setter functions
+/// </Summary>
+#define DECLARE_MATERIALASSET(className, name) public: \
+   StringTableEntry m##name##Name;\
+   StringTableEntry m##name##AssetId;\
+   AssetPtr<MaterialAsset>  m##name##Asset;\
+   SimObjectPtr<Material> m##name;\
+public: \
+   const StringTableEntry get##name##File() const { return m##name##Name; }\
+   void set##name##Name(const FileName &_in) { m##name##Name = StringTable->insert(_in.c_str());}\
+   const AssetPtr<MaterialAsset> & get##name##Asset() const { return m##name##Asset; }\
+   void set##name##Asset(const AssetPtr<MaterialAsset> &_in) { m##name##Asset = _in;}\
    \
-   StringTableEntry assetId = MaterialAsset::getAssetIdByMaterialName(StringTable->insert(data));\
-   if (assetId != StringTable->EmptyString())\
+   bool _set##name(StringTableEntry _in)\
    {\
-      if (shape->_set##name##Asset(obj, index, assetId))\
+      if(m##name##AssetId != _in || m##name##Name != _in)\
       {\
-         if (assetId == StringTable->insert("Core_Rendering:noMaterial"))\
+         if (_in == StringTable->EmptyString())\
          {\
-            shape->m##name##Name = data;\
-            shape->m##name##AssetId = StringTable->EmptyString();\
-            \
+            m##name##Name = StringTable->EmptyString();\
+            m##name##AssetId = StringTable->EmptyString();\
+            m##name##Asset = NULL;\
+            m##name = NULL;\
             return true;\
          }\
-         else\
+         \
+         if (AssetDatabase.isDeclaredAsset(_in))\
          {\
-            shape->m##name##AssetId = assetId;\
-            shape->m##name##Name = StringTable->EmptyString();\
+            m##name##AssetId = _in;\
             \
-            return false;\
+            U32 assetState = MaterialAsset::getAssetById(m##name##AssetId, &m##name##Asset);\
+            \
+            if (MaterialAsset::Ok == assetState)\
+            {\
+               m##name##Name = StringTable->EmptyString();\
+            }\
          }\
+         else\
+         {\
+            StringTableEntry assetId = MaterialAsset::getAssetIdByMaterialName(_in);\
+            if (assetId != StringTable->EmptyString())\
+            {\
+               m##name##AssetId = assetId;\
+               if (MaterialAsset::getAssetById(m##name##AssetId, &m##name##Asset) == MaterialAsset::Ok)\
+               {\
+                  m##name##Name = StringTable->EmptyString();\
+               }\
+            }\
+            else\
+            {\
+               m##name##Name = _in;\
+               m##name##AssetId = StringTable->EmptyString();\
+               m##name##Asset = NULL;\
+            }\
+         }\
+      }\
+      if (get##name() != StringTable->EmptyString() && m##name##Asset.notNull())\
+      {\
+         if (m##name && String(m##name##Asset->getMaterialDefinitionName()).equal(m##name->getName(), String::NoCase))\
+            return false;\
+         \
+         Material* tempMat = nullptr;\
+         \
+         if (!Sim::findObject(m##name##Asset->getMaterialDefinitionName(), tempMat))\
+            Con::errorf("%s::_set%s() - Material %s was not found.", macroText(className), macroText(name), m##name##Asset->getMaterialDefinitionName());\
+         m##name = tempMat;\
+      }\
+      else\
+      {\
+         m##name = NULL;\
+      }\
+      \
+      if(get##name() == StringTable->EmptyString())\
+         return true;\
+      \
+      if (m##name##Asset.notNull() && m##name##Asset->getStatus() != MaterialAsset::Ok)\
+      {\
+         Con::errorf("%s::_set%s() - material asset failure\"%s\" due to [%s]", macroText(className), macroText(name), _in, MaterialAsset::getAssetErrstrn(m##name##Asset->getStatus()).c_str());\
+         return false; \
       }\
+      else if (bool(m##name) == NULL)\
+      {\
+         Con::errorf("%s::_set%s() - Couldn't load material \"%s\"", macroText(className), macroText(name), _in);\
+         return false;\
+      }\
+      return true;\
    }\
-   else\
+   \
+   const StringTableEntry get##name() const\
    {\
-      shape->m##name##Asset = StringTable->EmptyString();\
+      if (m##name##Asset && (m##name##Asset->getMaterialDefinitionName() != StringTable->EmptyString()))\
+         return m##name##Asset->getMaterialDefinitionName();\
+      else if (m##name##AssetId != StringTable->EmptyString())\
+         return m##name##AssetId;\
+      else if (m##name##Name != StringTable->EmptyString())\
+         return m##name##Name;\
+      else\
+         return StringTable->EmptyString();\
    }\
-   \
-   return true;\
+   SimObjectPtr<Material> get##name##Resource() \
+   {\
+      return m##name##;\
+   }
+
+#define DECLARE_MATERIALASSET_SETGET(className, name)\
+   static bool _set##name##Data(void* obj, const char* index, const char* data)\
+   {\
+      bool ret = false;\
+      className* object = static_cast<className*>(obj);\
+      ret = object->_set##name(StringTable->insert(data));\
+      return ret;\
+   }
+
+#define DECLARE_MATERIALASSET_NET_SETGET(className, name, bitmask)\
+   static bool _set##name##Data(void* obj, const char* index, const char* data)\
+   {\
+      bool ret = false;\
+      className* object = static_cast<className*>(obj);\
+      ret = object->_set##name(StringTable->insert(data));\
+      if(ret)\
+         object->setMaskBits(bitmask);\
+      return ret;\
+   }
+
+#define DEF_MATERIALASSET_BINDS(className,name)\
+DefineEngineMethod(className, get##name, const char*, (), , "get name")\
+{\
+   return object->get##name(); \
 }\
-\
-static bool _set##name##Asset(void* obj, const char* index, const char* data)\
+DefineEngineMethod(className, get##name##Asset, const char*, (), , assetText(name, asset reference))\
 {\
-   className* shape = static_cast<className*>(obj);\
-   shape->m##name##AssetId = StringTable->insert(data);\
-   if (MaterialAsset::getAssetById(shape->m##name##AssetId, &shape->m##name##Asset))\
-   {\
-      if (shape->m##name##Asset.getAssetId() != StringTable->insert("Core_Rendering:noMaterial"))\
-         shape->m##name##Name = StringTable->EmptyString();\
-      \
-      return true;\
-   }\
-   return false;\
+   return object->m##name##AssetId; \
 }\
-\
-static bool set##name##Asset(const char* assetId)\
+DefineEngineMethod(className, set##name, bool, (const char* mat), , assetText(name,assignment. first tries asset then material name.))\
 {\
-   m##name##AssetId = StringTable->insert(assetId);\
-   if (m##name##AssetId != StringTable->EmptyString())\
-      m##name##Asset = m##name##AssetId;\
+    return object->_set##name(StringTable->insert(mat));\
 }
 
-/// <summary>
-/// DECLARE_MATERIALASSET is a utility macro for MaterialAssets. It takes in the name of the class using it, the name of the field for the material, and a networking bitmask
-/// The first 2 are for setting up/filling out the fields and class member defines
-/// The bitmask is for when the material is changed, it can automatically kick a network update on the owner object to pass the changed asset to clients
-/// </summary>
-#define DECLARE_NET_MATERIALASSET(className,name,bitmask)      protected: \
-                                      String m##name##Name;\
-                                      StringTableEntry m##name##AssetId;\
-                                      AssetPtr<MaterialAsset>  m##name##Asset;\
-                                      public: \
-                                      const String& get##name() const { return m##name##Name; }\
-                                      void set##name(FileName _in) { m##name##Name = _in; }\
-                                      const AssetPtr<MaterialAsset> & get##name##Asset() const { return m##name##Asset; }\
-                                      void set##name##Asset(AssetPtr<MaterialAsset>_in) { m##name##Asset = _in; }\
-static bool _set##name##Name(void* obj, const char* index, const char* data)\
+#define INIT_MATERIALASSET(name) \
+   m##name##Name = StringTable->EmptyString(); \
+   m##name##AssetId = StringTable->EmptyString(); \
+   m##name##Asset = NULL;\
+   m##name = NULL;
+
+#ifdef TORQUE_SHOW_LEGACY_FILE_FIELDS
+
+#define INITPERSISTFIELD_MATERIALASSET(name, consoleClass, docs) \
+   addProtectedField(#name, TypeMaterialName, Offset(m##name##Name, consoleClass), _set##name##Data, &defaultProtectedGetFn,assetDoc(name, docs)); \
+   addProtectedField(assetText(name, Asset), TypeMaterialAssetId, Offset(m##name##AssetId, consoleClass), _set##name##Data, &defaultProtectedGetFn, assetDoc(name, asset docs.));
+
+#else
+
+#define INITPERSISTFIELD_MATERIALASSET(name, consoleClass, docs) \
+   addProtectedField(#name, TypeMaterialName, Offset(m##name##Name, consoleClass), _set##name##Data, &defaultProtectedGetFn,assetDoc(name, docs), AbstractClassRep::FIELD_HideInInspectors); \
+   addProtectedField(assetText(name, Asset), TypeMaterialAssetId, Offset(m##name##AssetId, consoleClass), _set##name##Data, &defaultProtectedGetFn, assetDoc(name, asset docs.));
+
+#endif // SHOW_LEGACY_FILE_FIELDS
+
+#define CLONE_MATERIALASSET(name) \
+   m##name##Name = other.m##name##Name;\
+   m##name##AssetId = other.m##name##AssetId;\
+   m##name##Asset = other.m##name##Asset;
+
+#define LOAD_MATERIALASSET(name)\
+if (m##name##AssetId != StringTable->EmptyString())\
 {\
-   className* shape = static_cast<className*>(obj);\
-   \
-   StringTableEntry assetId = MaterialAsset::getAssetIdByMaterialName(StringTable->insert(data));\
-   if (assetId != StringTable->EmptyString())\
-   {\
-      if (shape->_set##name##Asset(obj, index, assetId))\
-      {\
-         if (assetId == StringTable->insert("Core_Rendering:noMaterial"))\
-         {\
-            shape->m##name##Name = data;\
-            shape->m##name##AssetId = StringTable->EmptyString();\
-            \
-            return true;\
-         }\
-         else\
-         {\
-            shape->m##name##AssetId = assetId;\
-            shape->m##name##Name = StringTable->EmptyString();\
-            \
-            return false;\
-         }\
-      }\
-   }\
-   else\
+   S32 assetState = MaterialAsset::getAssetById(m##name##AssetId, &m##name##Asset);\
+   if (assetState == MaterialAsset::Ok )\
    {\
-      shape->m##name##Asset = StringTable->EmptyString();\
+      m##name##Name = StringTable->EmptyString();\
    }\
-   \
-   return true;\
-}\
-\
-static bool _set##name##Asset(void* obj, const char* index, const char* data)\
-{\
-   className* shape = static_cast<className*>(obj);\
-   shape->m##name##AssetId = StringTable->insert(data);\
-   if (MaterialAsset::getAssetById(shape->m##name##AssetId, &shape->m##name##Asset))\
+   else Con::warnf("Warning: %s::LOAD_MATERIALASSET(%s)-%s", mClassName, m##name##AssetId, MaterialAsset::getAssetErrstrn(assetState).c_str());\
+}
+
+#define PACKDATA_MATERIALASSET(name)\
+   if (stream->writeFlag(m##name##Asset.notNull()))\
    {\
-      if (shape->m##name##Asset.getAssetId() != StringTable->insert("Core_Rendering:noMaterial"))\
-         shape->m##name##Name = StringTable->EmptyString();\
-      \
-      shape->setMaskBits(bitmask);\
-      shape->inspectPostApply();\
-      return true;\
+      stream->writeString(m##name##Asset.getAssetId());\
    }\
-   shape->inspectPostApply();\
-   return false;\
-}\
-\
-bool set##name##AssetId(const char* _assetId)\
-{\
-   m##name##AssetId = StringTable->insert(_assetId);\
-   if (m##name##AssetId != StringTable->EmptyString())\
+   else\
+      stream->writeString(m##name##Name);
+
+#define UNPACKDATA_MATERIALASSET(name)\
+   if (stream->readFlag())\
    {\
-      m##name##Asset = m##name##AssetId;\
-      \
-      setMaskBits(bitmask);\
-      inspectPostApply();\
-      return true;\
+      m##name##AssetId = stream->readSTString();\
+      _set##name(m##name##AssetId);\
    }\
-   \
-   return false;\
-}
-
+   else\
+      m##name##Name = stream->readSTString();
 
-#define packMaterialAsset(netconn, name)\
+#define PACK_MATERIALASSET(netconn, name)\
    if (stream->writeFlag(m##name##Asset.notNull()))\
    {\
       NetStringHandle assetIdStr = m##name##Asset.getAssetId();\
@@ -285,14 +347,16 @@ bool set##name##AssetId(const char* _assetId)\
    else\
       stream->writeString(m##name##Name);
 
-#define unpackMaterialAsset(netconn, name)\
+#define UNPACK_MATERIALASSET(netconn, name)\
    if (stream->readFlag())\
    {\
       m##name##AssetId = StringTable->insert(netconn->unpackNetStringHandleU(stream).getString());\
-      MaterialAsset::getAssetById(m##name##AssetId, &m##name##Asset);\
+      _set##name(m##name##AssetId);\
    }\
    else\
-      m##name##Name = stream->readSTString();\
+      m##name##Name = stream->readSTString();
+
+#pragma endregion
 
 #endif // _ASSET_BASE_H_
 

+ 2 - 2
Engine/source/T3D/assets/ParticleAsset.cpp

@@ -160,8 +160,8 @@ GuiControl* GuiInspectorTypeParticleAssetPtr::constructEditControl()
    dSprintf(szBuffer, sizeof(szBuffer), "echo(\"Game Object Editor not implemented yet!\");", retCtrl->getId());
    mSMEdButton->setField("Command", szBuffer);
 
-   char bitmapName[512] = "tools/worldEditor/images/toolbar/shape-editor";
-   mSMEdButton->setBitmap(bitmapName);
+   char bitmapName[512] = "ToolsModule:shape_editor_n_image";
+   mSMEdButton->setBitmap(StringTable->insert(bitmapName));
 
    mSMEdButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
    mSMEdButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");

+ 2 - 2
Engine/source/T3D/assets/PostEffectAsset.cpp

@@ -136,7 +136,7 @@ void PostEffectAsset::initializeAsset()
    mHLSLShaderPath = expandAssetFilePath(mHLSLShaderFile);
    mGLSLShaderPath = expandAssetFilePath(mGLSLShaderFile);
 
-   if (Platform::isFile(mScriptPath))
+   if (Torque::FS::IsScriptFile(mScriptPath))
       Con::executeFile(mScriptPath, false, false);
 }
 
@@ -146,7 +146,7 @@ void PostEffectAsset::onAssetRefresh()
    mHLSLShaderPath = expandAssetFilePath(mHLSLShaderFile);
    mGLSLShaderPath = expandAssetFilePath(mGLSLShaderFile);
 
-   if (Platform::isFile(mScriptPath))
+   if (Torque::FS::IsScriptFile(mScriptPath))
       Con::executeFile(mScriptPath, false, false);
 }
 

+ 3 - 3
Engine/source/T3D/assets/ScriptAsset.cpp

@@ -124,7 +124,7 @@ void ScriptAsset::initializeAsset()
 {
    mScriptPath = expandAssetFilePath(mScriptFile);
 
-   if (Platform::isFile(mScriptPath))
+   if (Torque::FS::IsScriptFile(mScriptPath))
    {
       //We're initialized properly, so we'll go ahead and kick along any dependencies we may have as well
       AssetManager::typeAssetDependsOnHash::Iterator assetDependenciesItr = mpOwningAssetManager->getDependedOnAssets()->find(mpAssetDefinition->mAssetId);
@@ -152,7 +152,7 @@ void ScriptAsset::onAssetRefresh()
 {
    mScriptPath = expandAssetFilePath(mScriptFile);
 
-   if (Platform::isFile(mScriptPath))
+   if (Torque::FS::IsScriptFile(mScriptPath))
    {
       //Refresh any dependencies we may have
       for (U32 i = 0; i < mScriptAssets.size(); i++)
@@ -192,7 +192,7 @@ bool ScriptAsset::execScript()
 
    return false;
 
-   if (Platform::isFile(mScriptPath))
+   if (Torque::FS::IsScriptFile(mScriptPath))
    {
       return Con::executeFile(mScriptPath, false, false);
    }

+ 75 - 74
Engine/source/T3D/assets/ShapeAsset.cpp

@@ -50,6 +50,8 @@
 #include "ts/tsLastDetail.h"
 #endif
 
+StringTableEntry ShapeAsset::smNoShapeAssetFallback(StringTable->insert(Con::getVariable("$Core::NoShapeAssetFallback")));
+
 //-----------------------------------------------------------------------------
 
 IMPLEMENT_CONOBJECT(ShapeAsset);
@@ -86,7 +88,7 @@ ConsoleSetType(TypeShapeAssetPtr)
 
 //-----------------------------------------------------------------------------
 
-ConsoleType(assetIdString, TypeShapeAssetId, String, ASSET_ID_FIELD_PREFIX)
+ConsoleType(assetIdString, TypeShapeAssetId, const char*, ASSET_ID_FIELD_PREFIX)
 
 ConsoleGetType(TypeShapeAssetId)
 {
@@ -100,13 +102,7 @@ ConsoleSetType(TypeShapeAssetId)
    if (argc == 1)
    {
       // Yes, so fetch field value.
-      const char* pFieldValue = argv[0];
-
-      // Fetch asset Id.
-      StringTableEntry* assetId = (StringTableEntry*)(dptr);
-
-      // Update asset value.
-      *assetId = StringTable->insert(pFieldValue);
+      *((const char**)dptr) = StringTable->insert(argv[0]);
 
       return;
    }
@@ -143,6 +139,17 @@ ShapeAsset::~ShapeAsset()
 
 //-----------------------------------------------------------------------------
 
+void ShapeAsset::consoleInit()
+{
+   Parent::consoleInit();
+
+   Con::addVariable("$Core::NoShapeAssetFallback", TypeString, &smNoShapeAssetFallback,
+      "The assetId of the shape to display when the requested shape asset is missing.\n"
+      "@ingroup GFX\n");
+}
+
+//-----------------------------------------------------------------------------
+
 void ShapeAsset::initPersistFields()
 {
    // Call parent.
@@ -154,7 +161,7 @@ void ShapeAsset::initPersistFields()
       &setShapeConstructorFile, &getShapeConstructorFile, "Path to the shape file we want to render");
 }
 
-void ShapeAsset::setDataField(StringTableEntry slotName, const char *array, const char *value)
+void ShapeAsset::setDataField(StringTableEntry slotName, StringTableEntry array, StringTableEntry value)
 {
    Parent::setDataField(slotName, array, value);
 
@@ -344,49 +351,39 @@ bool ShapeAsset::loadShape()
 
 //------------------------------------------------------------------------------
 //Utility function to 'fill out' bindings and resources with a matching asset if one exists
-bool ShapeAsset::getAssetByFilename(StringTableEntry fileName, AssetPtr<ShapeAsset>* shapeAsset)
+U32 ShapeAsset::getAssetByFilename(StringTableEntry fileName, AssetPtr<ShapeAsset>* shapeAsset)
 {
    AssetQuery query;
    S32 foundAssetcount = AssetDatabase.findAssetLooseFile(&query, fileName);
    if (foundAssetcount == 0)
    {
-      //Didn't find any assets
-      //If possible, see if we can run an in-place import and the get the asset from that
-#if TORQUE_DEBUG
-      Con::warnf("ShapeAsset::getAssetByFilename - Attempted to in-place import a shapefile(%s) that had no associated asset", fileName);
-#endif
+      //Didn't work, so have us fall back to a placeholder asset
+      shapeAsset->setAssetId(ShapeAsset::smNoShapeAssetFallback);
 
-      AssetImporter* autoAssetImporter;
-      if (!Sim::findObject("autoAssetImporter", autoAssetImporter))
+      if (shapeAsset->isNull())
       {
-         autoAssetImporter = new AssetImporter();
-         autoAssetImporter->registerObject("autoAssetImporter");
+         //Well that's bad, loading the fallback failed.
+         Con::warnf("ShapeAsset::getAssetByFilename - Finding of asset associated with file %s failed with no fallback asset", fileName);
+         return AssetErrCode::Failed;
       }
 
-      StringTableEntry resultingAssetId = autoAssetImporter->autoImportFile(fileName);
-
-      if (resultingAssetId != StringTable->EmptyString())
+      //handle noshape not being loaded itself
+      if ((*shapeAsset)->mLoadedState == BadFileReference)
       {
-         shapeAsset->setAssetId(resultingAssetId);
-
-         if (!shapeAsset->isNull())
-            return true;
+         Con::warnf("ShapeAsset::getAssetByFilename - Finding of associated with file %s failed, and fallback asset reported error of Bad File Reference.", fileName);
+         return AssetErrCode::BadFileReference;
       }
 
-      //Didn't work, so have us fall back to a placeholder asset
-      shapeAsset->setAssetId(StringTable->insert("Core_Rendering:noshape"));
-
-      if (!shapeAsset->isNull())
-         return true;
+      Con::warnf("ShapeAsset::getAssetByFilename - Finding of associated with file %s failed, utilizing fallback asset", fileName);
 
-      //That didn't work, so fail out
-      return false;
+      (*shapeAsset)->mLoadedState = AssetErrCode::UsingFallback;
+      return AssetErrCode::UsingFallback;
    }
    else
    {
       //acquire and bind the asset, and return it out
       shapeAsset->setAssetId(query.mAssetList[0]);
-      return true;
+      return (*shapeAsset)->mLoadedState;
    }
 }
 
@@ -395,37 +392,11 @@ StringTableEntry ShapeAsset::getAssetIdByFilename(StringTableEntry fileName)
    if (fileName == StringTable->EmptyString())
       return StringTable->EmptyString();
 
-   StringTableEntry shapeAssetId = StringTable->EmptyString();
+   StringTableEntry shapeAssetId = ShapeAsset::smNoShapeAssetFallback;
 
    AssetQuery query;
    S32 foundAssetcount = AssetDatabase.findAssetLooseFile(&query, fileName);
-   if (foundAssetcount == 0)
-   {
-      //Didn't find any assets
-      //If possible, see if we can run an in-place import and the get the asset from that
-#if TORQUE_DEBUG
-      Con::warnf("ShapeAsset::getAssetByFilename - Attempted to in-place import a shapefile(%s) that had no associated asset", fileName);
-#endif
-
-      AssetImporter* autoAssetImporter;
-      if (!Sim::findObject("autoAssetImporter", autoAssetImporter))
-      {
-         autoAssetImporter = new AssetImporter();
-         autoAssetImporter->registerObject("autoAssetImporter");
-      }
-
-      StringTableEntry resultingAssetId = autoAssetImporter->autoImportFile(fileName);
-
-      if (resultingAssetId != StringTable->EmptyString())
-      {
-         shapeAssetId = resultingAssetId;
-         return shapeAssetId;
-      }
-
-      //Didn't work, so have us fall back to a placeholder asset
-      shapeAssetId = StringTable->insert("Core_Rendering:noshape");
-   }
-   else
+   if (foundAssetcount != 0)
    {
       //acquire and bind the asset, and return it out
       shapeAssetId = query.mAssetList[0];
@@ -438,24 +409,34 @@ U32 ShapeAsset::getAssetById(StringTableEntry assetId, AssetPtr<ShapeAsset>* sha
 {
    (*shapeAsset) = assetId;
 
-   if ((*shapeAsset))
-      return (*shapeAsset)->mLoadedState;
-
    if (shapeAsset->notNull())
+   {
+      return (*shapeAsset)->mLoadedState;
+   }
+   else
    {
       //Didn't work, so have us fall back to a placeholder asset
-      StringTableEntry noShapeId = StringTable->insert("Core_Rendering:noshape");
-      shapeAsset->setAssetId(noShapeId);
+      shapeAsset->setAssetId(ShapeAsset::smNoShapeAssetFallback);
+
+      if (shapeAsset->isNull())
+      {
+         //Well that's bad, loading the fallback failed.
+         Con::warnf("ShapeAsset::getAssetById - Finding of asset with id %s failed with no fallback asset", assetId);
+         return AssetErrCode::Failed;
+      }
 
       //handle noshape not being loaded itself
       if ((*shapeAsset)->mLoadedState == BadFileReference)
-         return AssetErrCode::Failed;
+      {
+         Con::warnf("ShapeAsset::getAssetById - Finding of asset with id %s failed, and fallback asset reported error of Bad File Reference.", assetId);
+         return AssetErrCode::BadFileReference;
+      }
+
+      Con::warnf("ShapeAsset::getAssetById - Finding of asset with id %s failed, utilizing fallback asset", assetId);
 
       (*shapeAsset)->mLoadedState = AssetErrCode::UsingFallback;
       return AssetErrCode::UsingFallback;
    }
-
-   return AssetErrCode::Failed;
 }
 //------------------------------------------------------------------------------
 
@@ -555,17 +536,37 @@ DefineEngineMethod(ShapeAsset, getAnimation, ShapeAnimationAsset*, (S32 index),
 }
 
 DefineEngineMethod(ShapeAsset, getShapeFile, const char*, (), ,
-   "Creates a new script asset using the targetFilePath.\n"
-   "@return The bool result of calling exec")
+   "Gets the shape's file path\n"
+   "@return The filename of the shape file")
 {
    return object->getShapeFilePath();
 }
 
+DefineEngineMethod(ShapeAsset, getShapeConstructorFilePath, const char*, (), ,
+   "Gets the shape's constructor file.\n"
+   "@return The filename of the shape constructor file")
+{
+   return object->getShapeConstructorFilePath();
+}
+
+DefineEngineMethod(ShapeAsset, getStatusString, String, (), , "get status string")\
+{
+   return ShapeAsset::getAssetErrstrn(object->getStatus());
+}
+
+
 #ifdef TORQUE_TOOLS
 DefineEngineMethod(ShapeAsset, generateCachedPreviewImage, const char*, (S32 resolution), (256), "")
 {
    return object->generateCachedPreviewImage(resolution);
 }
+
+DefineEngineStaticMethod(ShapeAsset, 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 ShapeAsset::getAssetIdByFilename(StringTable->insert(filePath));
+}
 #endif
 
 //-----------------------------------------------------------------------------
@@ -611,8 +612,8 @@ GuiControl* GuiInspectorTypeShapeAssetPtr::constructEditControl()
    dSprintf(szBuffer, sizeof(szBuffer), "ShapeEditorPlugin.openShapeAssetId(%d.getText());", retCtrl->getId());
    mShapeEdButton->setField("Command", szBuffer);
 
-   char bitmapName[512] = "tools/worldEditor/images/toolbar/shape-editor";
-   mShapeEdButton->setBitmap(bitmapName);
+   char bitmapName[512] = "ToolsModule:shape_editor_n_image";
+   mShapeEdButton->setBitmap(StringTable->insert(bitmapName));
 
    mShapeEdButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
    mShapeEdButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");

+ 410 - 72
Engine/source/T3D/assets/ShapeAsset.h

@@ -81,9 +81,11 @@ protected:
    Vector<AssetPtr<ShapeAnimationAsset>> mAnimationAssets;
 
    typedef Signal<void()> ShapeAssetChanged;
-
    ShapeAssetChanged mChangeSignal;
 
+   typedef Signal<void(S32 index)> ShapeAssetArrayChanged;
+   ShapeAssetArrayChanged mChangeArraySignal;
+
 public:
    enum ShapeAssetErrCode
    {
@@ -93,7 +95,12 @@ public:
       Extended
    };
 
+   static StringTableEntry smNoShapeAssetFallback;
+
    static const String mErrCodeStrings[ShapeAssetErrCode::Extended - Parent::Extended + 1];
+
+   static U32 getAssetErrCode(AssetPtr<ShapeAsset> shapeAsset) { if (shapeAsset) return shapeAsset->mLoadedState; else return 0; }
+
    static String getAssetErrstrn(U32 errCode)
    {
       if (errCode < Parent::Extended) return Parent::getAssetErrstrn(errCode);
@@ -104,11 +111,14 @@ public:
    ShapeAsset();
    virtual ~ShapeAsset();
 
+   /// Set up some global script interface stuff.
+   static void consoleInit();
+
    /// Engine.
    static void initPersistFields();
    virtual void copyTo(SimObject* object);
 
-   virtual void setDataField(StringTableEntry slotName, const char *array, const char *value);
+   virtual void setDataField(StringTableEntry slotName, StringTableEntry array, StringTableEntry value);
 
    virtual void initializeAsset();
 
@@ -116,25 +126,25 @@ public:
    DECLARE_CONOBJECT(ShapeAsset);
 
    bool loadShape();
-   U32 mLoadedState;
 
    TSShape* getShape() { return mShape; }
 
    Resource<TSShape> getShapeResource() { return mShape; }
 
    void SplitSequencePathAndName(String& srcPath, String& srcName);
-   StringTableEntry getShapeFilename() { return mFilePath; }
-   
+   StringTableEntry getShapeFileName() { return mFileName; }
+   StringTableEntry getShapePath() { return mFilePath; }
+
    U32 getShapeFilenameHash() { return _StringTable::hashString(mFilePath); }
 
    Vector<AssetPtr<MaterialAsset>> getMaterialAssets() { return mMaterialAssets; }
 
-   inline AssetPtr<MaterialAsset> getMaterialAsset(U32 matId) 
-   { 
-      if(matId >= mMaterialAssets.size()) 
-          return nullptr; 
-      else 
-         return mMaterialAssets[matId]; 
+   inline AssetPtr<MaterialAsset> getMaterialAsset(U32 matId)
+   {
+      if (matId >= mMaterialAssets.size())
+         return nullptr;
+      else
+         return mMaterialAssets[matId];
    }
 
    void clearMaterialAssets() { mMaterialAssets.clear(); }
@@ -145,9 +155,10 @@ public:
    S32 getAnimationCount() { return mAnimationAssets.size(); }
    ShapeAnimationAsset* getAnimation(S32 index);
 
-   void _onResourceChanged(const Torque::Path &path);
+   void _onResourceChanged(const Torque::Path& path);
 
    ShapeAssetChanged& getChangedSignal() { return mChangeSignal; }
+   ShapeAssetArrayChanged& getChangedArraySignal() { return mChangeArraySignal; }
 
    void                    setShapeFile(const char* pScriptFile);
    inline StringTableEntry getShapeFile(void) const { return mFileName; };
@@ -158,13 +169,11 @@ public:
    inline StringTableEntry getShapeFilePath(void) const { return mFilePath; };
    inline StringTableEntry getShapeConstructorFilePath(void) const { return mConstructorFilePath; };
 
-   static bool getAssetByFilename(StringTableEntry fileName, AssetPtr<ShapeAsset>* shapeAsset);
+   static U32 getAssetByFilename(StringTableEntry fileName, AssetPtr<ShapeAsset>* shapeAsset);
 
    static StringTableEntry getAssetIdByFilename(StringTableEntry fileName);
    static U32 getAssetById(StringTableEntry assetId, AssetPtr<ShapeAsset>* shapeAsset);
 
-   static StringTableEntry getNoShapeAssetId() { return StringTable->insert("Core_Rendering:noshape"); }
-
 #ifdef TORQUE_TOOLS
    const char* generateCachedPreviewImage(S32 resolution);
 #endif
@@ -172,7 +181,7 @@ public:
 protected:
    virtual void            onAssetRefresh(void);
 
-   static bool setShapeFile(void *obj, const char *index, const char *data) { static_cast<ShapeAsset*>(obj)->setShapeFile(data); return false; }
+   static bool setShapeFile(void* obj, StringTableEntry index, StringTableEntry data) { static_cast<ShapeAsset*>(obj)->setShapeFile(data); return false; }
    static const char* getShapeFile(void* obj, const char* data) { return static_cast<ShapeAsset*>(obj)->getShapeFile(); }
 
    static bool setShapeConstructorFile(void* obj, const char* index, const char* data) { static_cast<ShapeAsset*>(obj)->setShapeConstructorFile(data); return false; }
@@ -192,7 +201,7 @@ class GuiInspectorTypeShapeAssetPtr : public GuiInspectorTypeFileName
    typedef GuiInspectorTypeFileName Parent;
 public:
 
-   GuiBitmapButtonCtrl  *mShapeEdButton;
+   GuiBitmapButtonCtrl* mShapeEdButton;
 
    DECLARE_CONOBJECT(GuiInspectorTypeShapeAssetPtr);
    static void consoleInit();
@@ -211,86 +220,415 @@ public:
 };
 #endif
 
-#define assetText(x,suff) std::string(std::string(#x) + std::string(#suff)).c_str()
-
-#define initShapeAsset(name) m##name##Name = StringTable->EmptyString(); m##name##AssetId = StringTable->EmptyString(); m##name##Asset = NULL;
-#define cloneShapeAsset(name) m##name##Name = other.m##name##Name; m##name##AssetId = other.m##name##AssetId; m##name##Asset = other.m##name##Asset;
-#define bindShapeAsset(name) if (m##name##AssetId != StringTable->EmptyString()) m##name##Asset = m##name##AssetId;
-
-#define scriptBindShapeAsset(name, consoleClass, docs) addProtectedField(assetText(name, File), TypeShapeFilename, Offset(m##name##Name, consoleClass), consoleClass::_set##name##Filename,  & defaultProtectedGetFn, assetText(name, docs)); \
-                                      addProtectedField(assetText(name, Asset), TypeShapeAssetId, Offset(m##name##AssetId, consoleClass), consoleClass::_set##name##Asset, & defaultProtectedGetFn, assetText(name, asset reference.));
-
-#define DECLARE_SHAPEASSET(className,name)\
-                                      StringTableEntry m##name##Name;\
-                                      StringTableEntry m##name##AssetId;\
-                                      AssetPtr<ShapeAsset>  m##name##Asset;\
-                                      const StringTableEntry& get##name() const { return m##name##Name; }\
-                                      void set##name(FileName _in) { m##name##Name = _in; }\
-                                      const AssetPtr<ShapeAsset> & get##name##Asset() const { return m##name##Asset; }\
-                                      void set##name##Asset(AssetPtr<ShapeAsset>_in) { m##name##Asset = _in; }\
-static bool _set##name##Filename(void* obj, const char* index, const char* data)\
-{\
-   className* shape = static_cast<className*>(obj);\
+#pragma region Singular Asset Macros
+
+#define DECLARE_SHAPEASSET(className,name,changeFunc) public: \
+   Resource<TSShape>m##name;\
+   StringTableEntry m##name##Name; \
+   StringTableEntry m##name##AssetId;\
+   AssetPtr<ShapeAsset>  m##name##Asset;\
+public: \
+   const StringTableEntry get##name##File() const { return StringTable->insert(m##name##Name); }\
+   void set##name##Name(const FileName &_in) { m##name##Name = _in;}\
+   const AssetPtr<ShapeAsset> & get##name##Asset() const { return m##name##Asset; }\
+   void set##name##Asset(const AssetPtr<ShapeAsset> &_in) { m##name##Asset = _in;}\
    \
-   StringTableEntry assetId = ShapeAsset::getAssetIdByFilename(StringTable->insert(data));\
-   if (assetId != StringTable->EmptyString())\
+   bool _set##name(StringTableEntry _in)\
    {\
-      if (shape->_set##name##Asset(obj, index, assetId))\
+      if(m##name##AssetId != _in || m##name##Name != _in)\
       {\
-         if (assetId == StringTable->insert("Core_Rendering:noShape"))\
+         if (m##name##Asset.notNull())\
          {\
-            shape->m##name##Name = data;\
-            shape->m##name##AssetId = StringTable->EmptyString();\
-            \
+            m##name##Asset->getChangedSignal().remove(this, &className::changeFunc);\
+         }\
+         if (_in == StringTable->EmptyString())\
+         {\
+            m##name##Name = StringTable->EmptyString();\
+            m##name##AssetId = StringTable->EmptyString();\
+            m##name##Asset = NULL;\
+            m##name = NULL;\
             return true;\
          }\
-         else\
+         \
+         if (AssetDatabase.isDeclaredAsset(_in))\
          {\
-            shape->m##name##AssetId = assetId;\
-            shape->m##name##Name = StringTable->EmptyString();\
+            m##name##AssetId = _in;\
             \
-            return false;\
+            U32 assetState = ShapeAsset::getAssetById(m##name##AssetId, &m##name##Asset);\
+            \
+            if (ShapeAsset::Ok == assetState)\
+            {\
+               m##name##Name = StringTable->EmptyString();\
+            }\
          }\
+         else\
+         {\
+            StringTableEntry assetId = ShapeAsset::getAssetIdByFilename(_in);\
+            if (assetId != StringTable->EmptyString())\
+            {\
+               m##name##AssetId = assetId;\
+               if (ShapeAsset::getAssetById(m##name##AssetId, &m##name##Asset) == ShapeAsset::Ok)\
+               {\
+                  m##name##Name = StringTable->EmptyString();\
+               }\
+            }\
+            else\
+            {\
+               m##name##Name = _in;\
+               m##name##AssetId = StringTable->EmptyString();\
+               m##name##Asset = NULL;\
+            }\
+         }\
+      }\
+      if (get##name() != StringTable->EmptyString() && m##name##Asset.notNull())\
+      {\
+         m##name = m##name##Asset->getShapeResource();\
+         \
+         m##name##Asset->getChangedSignal().notify(this, &className::changeFunc);\
+      }\
+      else\
+      {\
+         m##name = NULL;\
+      }\
+      \
+      if(get##name() == StringTable->EmptyString())\
+         return true;\
+      \
+      if (m##name##Asset.notNull() && m##name##Asset->getStatus() != ShapeAsset::Ok)\
+      {\
+         Con::errorf("%s(%s)::_set%s() - shape asset failure \"%s\" due to [%s]", macroText(className), getName(), macroText(name), _in, ShapeAsset::getAssetErrstrn(m##name##Asset->getStatus()).c_str());\
+         return false; \
+      }\
+      else if (bool(m##name) == NULL)\
+      {\
+         Con::errorf("%s(%s)::_set%s() - Couldn't load shape \"%s\"", macroText(className), getName(), macroText(name), _in);\
+         return false;\
       }\
+      return true;\
    }\
-   else\
+   \
+   const StringTableEntry get##name() const\
    {\
-      shape->m##name##Asset = StringTable->EmptyString();\
+      if (m##name##Asset && (m##name##Asset->getShapePath() != StringTable->EmptyString()))\
+         return m##name##Asset->getShapePath();\
+      else if (m##name##AssetId != StringTable->EmptyString())\
+         return m##name##AssetId;\
+      else if (m##name##Name != StringTable->EmptyString())\
+         return m##name##Name;\
+      else\
+         return StringTable->EmptyString();\
    }\
-   \
-   return true;\
+   Resource<TSShape> get##name##Resource() \
+   {\
+      return m##name;\
+   }
+
+#define DECLARE_SHAPEASSET_SETGET(className, name)\
+   static bool _set##name##Data(void* obj, const char* index, const char* data)\
+   {\
+      bool ret = false;\
+      className* object = static_cast<className*>(obj);\
+      ret = object->_set##name(StringTable->insert(data));\
+      return ret;\
+   }
+
+#define DECLARE_SHAPEASSET_NET_SETGET(className, name, bitmask)\
+   static bool _set##name##Data(void* obj, const char* index, const char* data)\
+   {\
+      bool ret = false;\
+      className* object = static_cast<className*>(obj);\
+      ret = object->_set##name(StringTable->insert(data));\
+      if(ret)\
+         object->setMaskBits(bitmask);\
+      return ret;\
+   }
+
+#define DEF_SHAPEASSET_BINDS(className,name)\
+DefineEngineMethod(className, get##name, String, (), , "get name")\
+{\
+   return object->get##name(); \
 }\
-\
-static bool _set##name##Asset(void* obj, const char* index, const char* data)\
+DefineEngineMethod(className, get##name##Asset, String, (), , assetText(name, asset reference))\
 {\
-   className* shape = static_cast<className*>(obj);\
-   shape->m##name##AssetId = StringTable->insert(data);\
-   if (ShapeAsset::getAssetById(shape->m##name##AssetId, &shape->m##name##Asset))\
-   {\
-      if (shape->m##name##Asset.getAssetId() != StringTable->insert("Core_Rendering:noShape"))\
-         shape->m##name##Name = StringTable->EmptyString();\
-      return true;\
-   }\
-   return false;\
+   return object->m##name##AssetId; \
 }\
-void pack##name##Asset(BitStream *stream)\
+DefineEngineMethod(className, set##name, bool, (const char*  shape), , assetText(name,assignment. first tries asset then flat file.))\
 {\
+   return object->_set##name(StringTable->insert(shape));\
+}
+
+#define INIT_SHAPEASSET(name) \
+   m##name##Name = StringTable->EmptyString(); \
+   m##name##AssetId = StringTable->EmptyString(); \
+   m##name##Asset = NULL; \
+   m##name = NULL;
+
+#ifdef TORQUE_SHOW_LEGACY_FILE_FIELDS
+
+#define INITPERSISTFIELD_SHAPEASSET(name, consoleClass, docs) \
+   addProtectedField(assetText(name, File), TypeShapeFilename, Offset(m##name##Name, consoleClass), _set##name##Data, & defaultProtectedGetFn, assetText(name, docs)); \
+   addProtectedField(assetText(name, Asset), TypeShapeAssetId, Offset(m##name##AssetId, consoleClass), _set##name##Data, & defaultProtectedGetFn, assetText(name, asset reference.));
+
+#else
+
+#define INITPERSISTFIELD_SHAPEASSET(name, consoleClass, docs) \
+   addProtectedField(assetText(name, File), TypeShapeFilename, Offset(m##name##Name, consoleClass), _set##name##Data, & defaultProtectedGetFn, assetText(name, docs), AbstractClassRep::FIELD_HideInInspectors); \
+   addProtectedField(assetText(name, Asset), TypeShapeAssetId, Offset(m##name##AssetId, consoleClass), _set##name##Data, & defaultProtectedGetFn, assetText(name, asset reference.));
+
+#endif // SHOW_LEGACY_FILE_FIELDS
+
+#define CLONE_SHAPEASSET(name) \
+   m##name##Name = other.m##name##Name;\
+   m##name##AssetId = other.m##name##AssetId;\
+   m##name##Asset = other.m##name##Asset;\
+
+#define PACKDATA_SHAPEASSET(name)\
    if (stream->writeFlag(m##name##Asset.notNull()))\
+   {\
       stream->writeString(m##name##Asset.getAssetId());\
+   }\
    else\
-      stream->writeString(m##name##Name);\
-}\
-void unpack##name##Asset(BitStream *stream)\
-{\
+      stream->writeString(m##name##Name);
+
+#define UNPACKDATA_SHAPEASSET(name)\
    if (stream->readFlag())\
    {\
       m##name##AssetId = stream->readSTString();\
-      ShapeAsset::getAssetById(m##name##AssetId, &m##name##Asset);\
-      m##name##Name = m##name##Asset->getShapeFilename(); \
+      _set##name(m##name##AssetId);\
+   }\
+   else\
+      m##name##Name = stream->readSTString();
+
+#define PACK_SHAPEASSET(netconn, name)\
+   if (stream->writeFlag(m##name##Asset.notNull()))\
+   {\
+      NetStringHandle assetIdStr = m##name##Asset.getAssetId();\
+      netconn->packNetStringHandleU(stream, assetIdStr);\
    }\
    else\
-      m##name##Name = stream->readSTString();\
+      stream->writeString(m##name##Name);
+
+#define UNPACK_SHAPEASSET(netconn, name)\
+   if (stream->readFlag())\
+   {\
+      m##name##AssetId = StringTable->insert(netconn->unpackNetStringHandleU(stream).getString());\
+      _set##name(m##name##AssetId);\
+   }\
+   else\
+      m##name##Name = stream->readSTString();
+
+#pragma endregion
+
+#pragma region Arrayed Asset Macros
+
+#define DECLARE_SHAPEASSET_ARRAY(className,name,max) public: \
+   static const U32 sm##name##Count = max;\
+   Resource<TSShape>m##name[max];\
+   StringTableEntry m##name##Name[max]; \
+   StringTableEntry m##name##AssetId[max];\
+   AssetPtr<ShapeAsset>  m##name##Asset[max];\
+public: \
+   const StringTableEntry get##name##File(const U32& index) const { return m##name##Name[index]; }\
+   void set##name##Name(const FileName &_in, const U32& index) { m##name##Name[index] = _in;}\
+   const AssetPtr<ShapeAsset> & get##name##Asset(const U32& index) const { return m##name##Asset[index]; }\
+   void set##name##Asset(const AssetPtr<ShapeAsset> &_in, const U32& index) { m##name##Asset[index] = _in;}\
+   \
+   bool _set##name(StringTableEntry _in, const U32& index)\
+   {\
+      if(m##name##AssetId[index] != _in || m##name##Name[index] != _in)\
+      {\
+         if(index >= sm##name##Count || index < 0)\
+            return false;\
+         if (_in == StringTable->EmptyString())\
+         {\
+            m##name##Name[index] = StringTable->EmptyString();\
+            m##name##AssetId[index] = StringTable->EmptyString();\
+            m##name##Asset[index] = NULL;\
+            m##name[index] = NULL;\
+            return true;\
+         }\
+         \
+         if (AssetDatabase.isDeclaredAsset(_in))\
+         {\
+            m##name##AssetId[index] = _in;\
+            \
+            U32 assetState = ShapeAsset::getAssetById(m##name##AssetId[index], &m##name##Asset[index]);\
+            \
+            if (ShapeAsset::Ok == assetState)\
+            {\
+               m##name##Name[index] = StringTable->EmptyString();\
+            }\
+         }\
+         else\
+         {\
+            StringTableEntry assetId = ShapeAsset::getAssetIdByFilename(_in);\
+            if (assetId != StringTable->EmptyString())\
+            {\
+               m##name##AssetId[index] = assetId;\
+               if (ShapeAsset::getAssetById(m##name##AssetId[index], &m##name##Asset[index]) == ShapeAsset::Ok)\
+               {\
+                  m##name##Name[index] = StringTable->EmptyString();\
+               }\
+            }\
+            else\
+            {\
+               m##name##Name[index] = _in;\
+               m##name##AssetId[index] = StringTable->EmptyString();\
+               m##name##Asset[index] = NULL;\
+            }\
+         }\
+      }\
+      if (get##name(index) != StringTable->EmptyString() && m##name##Asset[index].notNull())\
+      {\
+         m##name[index] = m##name##Asset[index]->getShapeResource();\
+      }\
+      else\
+      {\
+         m##name[index] = NULL;\
+      }\
+      \
+      if(get##name(index) == StringTable->EmptyString())\
+         return true;\
+      \
+      if (m##name##Asset[index].notNull() && m##name##Asset[index]->getStatus() != ShapeAsset::Ok)\
+      {\
+         Con::errorf("%s(%s)::_set%s(%i) - shape asset failure \"%s\" due to [%s]", macroText(className), getName(), macroText(name), index, _in, ShapeAsset::getAssetErrstrn(m##name##Asset[index]->getStatus()).c_str());\
+         return false; \
+      }\
+      else if (bool(m##name[index]) == NULL)\
+      {\
+         Con::errorf("%s(%s)::_set%s(%i) - Couldn't load shape \"%s\"", macroText(className), getName(), macroText(name), index, _in);\
+         return false; \
+      }\
+      return true;\
+   }\
+   \
+   const StringTableEntry get##name(const U32& index) const\
+   {\
+      if (m##name##Asset[index] && (m##name##Asset[index]->getShapePath() != StringTable->EmptyString()))\
+         return m##name##Asset[index]->getShapePath();\
+      else if (m##name##AssetId[index] != StringTable->EmptyString())\
+         return m##name##AssetId[index];\
+      else if (m##name##Name[index] != StringTable->EmptyString())\
+         return StringTable->insert(m##name##Name[index]);\
+      else\
+         return StringTable->EmptyString();\
+   }\
+   Resource<TSShape> get##name##Resource(const U32& index) \
+   {\
+      if(index >= sm##name##Count || index < 0)\
+         return nullptr;\
+      return m##name[index];\
+   }
+
+#define DECLARE_SHAPEASSET_ARRAY_SETGET(className, name)\
+   static bool _set##name##Data(void* obj, const char* index, const char* data)\
+   {\
+      if (!index) return false;\
+      U32 idx = dAtoi(index);\
+      if (idx >= sm##name##Count)\
+         return false;\
+      bool ret = false;\
+      className* object = static_cast<className*>(obj);\
+      ret = object->_set##name(StringTable->insert(data), idx);\
+      return ret;\
+   }
+
+#define DECLARE_SHAPEASSET_ARRAY_NET_SETGET(className, name, bitmask)\
+   static bool _set##name##Data(void* obj, const char* index, const char* data)\
+   {\
+      if (!index) return false;\
+      U32 idx = dAtoi(index);\
+      if (idx >= sm##name##Count)\
+         return false;\
+      bool ret = false;\
+      className* object = static_cast<className*>(obj);\
+      ret = object->_set##name(StringTable->insert(data), idx);\
+      if(ret)\
+         object->setMaskBits(bitmask);\
+      return ret;\
+   }
+
+#define DEF_SHAPEASSET_ARRAY_BINDS(className,name)\
+DefineEngineMethod(className, get##name, String, (S32 index), , "get name")\
+{\
+   return object->get##name(index); \
+}\
+DefineEngineMethod(className, get##name##Asset, String, (S32 index), , assetText(name, asset reference))\
+{\
+   if(index >= className::sm##name##Count || index < 0)\
+      return "";\
+   return object->m##name##AssetId[index]; \
+}\
+DefineEngineMethod(className, set##name, bool, (const char*  shape, S32 index), , assetText(name,assignment. first tries asset then flat file.))\
+{\
+   return object->_set##name(StringTable->insert(shape), index);\
 }
 
-#endif
+#define INIT_SHAPEASSET_ARRAY(name, index) \
+{\
+   m##name##Name[index] = StringTable->EmptyString(); \
+   m##name##AssetId[index] = StringTable->EmptyString(); \
+   m##name##Asset[index] = NULL; \
+   m##name[index] = NULL;\
+}
+
+#ifdef TORQUE_SHOW_LEGACY_FILE_FIELDS
+
+#define INITPERSISTFIELD_SHAPEASSET_ARRAY(name, arraySize, consoleClass, docs) \
+   addProtectedField(assetText(name, File), TypeShapeFilename, Offset(m##name##Name, consoleClass), _set##name##Data, & defaultProtectedGetFn, arraySize, assetText(name, docs)); \
+   addProtectedField(assetText(name, Asset), TypeShapeAssetId, Offset(m##name##AssetId, consoleClass), _set##name##Data, & defaultProtectedGetFn, arraySize, assetText(name, asset reference.));
+
+#else
+
+#define INITPERSISTFIELD_SHAPEASSET_ARRAY(name, arraySize, consoleClass, docs) \
+   addProtectedField(assetText(name, File), TypeShapeFilename, Offset(m##name##Name, consoleClass), _set##name##Data, & defaultProtectedGetFn, arraySize, assetText(name, docs), AbstractClassRep::FIELD_HideInInspectors); \
+   addProtectedField(assetText(name, Asset), TypeShapeAssetId, Offset(m##name##AssetId, consoleClass), _set##name##Data, & defaultProtectedGetFn, arraySize,assetText(name, asset reference.));
+
+#endif // SHOW_LEGACY_FILE_FIELDS
+
+#define CLONE_SHAPEASSET_ARRAY(name, index) \
+{\
+   m##name##Name[index] = other.m##name##Name[index];\
+   m##name##AssetId[index] = other.m##name##AssetId[index];\
+   m##name##Asset[index] = other.m##name##Asset[index];\
+}
+
+#define PACKDATA_SHAPEASSET_ARRAY(name, index)\
+   if (stream->writeFlag(m##name##Asset[index].notNull()))\
+   {\
+      stream->writeString(m##name##Asset[index].getAssetId());\
+   }\
+   else\
+      stream->writeString(m##name##Name[index]);
+
+#define UNPACKDATA_SHAPEASSET_ARRAY(name, index)\
+   if (stream->readFlag())\
+   {\
+      m##name##AssetId[index] = stream->readSTString();\
+      _set##name(m##name##AssetId[index], index);\
+   }\
+   else\
+      m##name##Name[index] = stream->readSTString();
+
+#define PACK_SHAPEASSET_ARRAY(netconn, name, index)\
+   if (stream->writeFlag(m##name##Asset[index].notNull()))\
+   {\
+      NetStringHandle assetIdStr = m##name##Asset[index].getAssetId();\
+      netconn->packNetStringHandleU(stream, assetIdStr);\
+   }\
+   else\
+      stream->writeString(m##name##Name[index]);
 
+#define UNPACK_SHAPEASSET_ARRAY(netconn, name, index)\
+   if (stream->readFlag())\
+   {\
+      m##name##AssetId[index] = StringTable->insert(netconn->unpackNetStringHandleU(stream).getString());\
+      _set##name(m##name##AssetId[index], index);\
+   }\
+   else\
+      m##name##Name[index] = stream->readSTString();
+
+#pragma endregion
+
+#endif

+ 164 - 26
Engine/source/T3D/assets/SoundAsset.cpp

@@ -42,19 +42,20 @@
 
 // Debug Profiling.
 #include "platform/profiler.h"
+#include "sfx/sfxTypes.h"
 
 //-----------------------------------------------------------------------------
 
 IMPLEMENT_CONOBJECT(SoundAsset);
 
-ConsoleType(SoundAssetPtr, TypeSoundAssetPtr, SoundAsset, ASSET_ID_FIELD_PREFIX)
+ConsoleType(SoundAssetPtr, TypeSoundAssetPtr, const char*, ASSET_ID_FIELD_PREFIX)
 
 //-----------------------------------------------------------------------------
 
 ConsoleGetType(TypeSoundAssetPtr)
 {
    // Fetch asset Id.
-   return (*((AssetPtr<SoundAsset>*)dptr)).getAssetId();
+   return *((const char**)(dptr));
 }
 
 //-----------------------------------------------------------------------------
@@ -65,27 +66,38 @@ ConsoleSetType(TypeSoundAssetPtr)
    if (argc == 1)
    {
       // Yes, so fetch field value.
-      const char* pFieldValue = argv[0];
+      *((const char**)dptr) = StringTable->insert(argv[0]);
 
-      // Fetch asset pointer.
-      AssetPtr<SoundAsset>* pAssetPtr = dynamic_cast<AssetPtr<SoundAsset>*>((AssetPtrBase*)(dptr));
+      return;
+   }
 
-      // Is the asset pointer the correct type?
-      if (pAssetPtr == NULL)
-      {
-         // No, so fail.
-         //Con::warnf("(TypeSoundAssetPtr) - Failed to set asset Id '%d'.", pFieldValue);
-         return;
-      }
+   // Warn.
+   Con::warnf("(TypeSoundAssetPtr) - Cannot set multiple args to a single asset.");
+}
+
+//-----------------------------------------------------------------------------
 
-      // Set asset.
-      pAssetPtr->setAssetId(pFieldValue);
+ConsoleType(assetIdString, TypeSoundAssetId, const char*, ASSET_ID_FIELD_PREFIX)
+
+ConsoleGetType(TypeSoundAssetId)
+{
+   // Fetch asset Id.
+   return *((const char**)(dptr));
+}
+
+ConsoleSetType(TypeSoundAssetId)
+{
+   // Was a single argument specified?
+   if (argc == 1)
+   {
+      // Yes, so fetch field value.
+      *((const char**)dptr) = StringTable->insert(argv[0]);
 
       return;
    }
 
    // Warn.
-   Con::warnf("(TypeSoundAssetPtr) - Cannot set multiple args to a single asset.");
+   Con::warnf("(TypeAssetId) - Cannot set multiple args to a single asset.");
 }
 
 //-----------------------------------------------------------------------------
@@ -94,11 +106,28 @@ SoundAsset::SoundAsset()
 {
    mSoundFile = StringTable->EmptyString();
    mSoundPath = StringTable->EmptyString();
+   mSubtitleString = StringTable->EmptyString();
+
+   mLoadedState = AssetErrCode::NotLoaded;
+   mPreload = false;
+   // SFX description inits
+   // reverb is useless here, reverb is inacted on listener.
+   mProfileDesc.mPitch = 1;
+   mProfileDesc.mVolume = 1;
+   mProfileDesc.mIs3D = false;
+   mProfileDesc.mIsLooping = false;
+   mProfileDesc.mIsStreaming = false;
+   mProfileDesc.mUseHardware = false;
+   mProfileDesc.mMinDistance = 1;
+   mProfileDesc.mMaxDistance = 100;
+   mProfileDesc.mConeInsideAngle = 360;
+   mProfileDesc.mConeOutsideAngle = 360;
+   mProfileDesc.mConeOutsideVolume = 1;
+   mProfileDesc.mRolloffFactor = -1.0f;
+   mProfileDesc.mScatterDistance = Point3F(0.f, 0.f, 0.f);
+   mProfileDesc.mPriority = 1.0f;
+   mProfileDesc.mSourceGroup = NULL;
 
-   mPitchAdjust = 1;
-   mVolumeAdjust = 1;
-
-   //mSound = nullptr;
 }
 
 //-----------------------------------------------------------------------------
@@ -117,8 +146,24 @@ void SoundAsset::initPersistFields()
    addProtectedField("soundFile", TypeAssetLooseFilePath, Offset(mSoundFile, SoundAsset),
       &setSoundFile, &getSoundFile, "Path to the sound file.");
 
-   addField("pitchAdjust", TypeF32, Offset(mPitchAdjust, SoundAsset), "Adjustment of the pitch value");
-   addField("volumeAdjust", TypeF32, Offset(mVolumeAdjust, SoundAsset), "Adjustment to the volume.");
+   addField("pitchAdjust", TypeF32, Offset(mProfileDesc.mPitch, SoundAsset), "Adjustment of the pitch value 1 is default.");
+   addField("volumeAdjust", TypeF32, Offset(mProfileDesc.mVolume, SoundAsset), "Adjustment to the volume.");
+   addField("is3D", TypeBool, Offset(mProfileDesc.mIs3D, SoundAsset), "Set this sound to 3D.");
+   addField("isLooping", TypeBool, Offset(mProfileDesc.mIsLooping, SoundAsset), "Does this sound loop.");
+   // if streaming, a default packet size should be chosen for all sounds.
+   addField("isStreaming", TypeBool, Offset(mProfileDesc.mIsStreaming, SoundAsset), "Use streaming.");
+   //....why?
+   addField("useHardware", TypeBool, Offset(mProfileDesc.mUseHardware, SoundAsset), "Use hardware mixing for this sound.");
+   addField("minDistance", TypeF32, Offset(mProfileDesc.mMinDistance, SoundAsset), "Minimum distance for sound.");
+   // more like it.
+   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("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.");
+
 }
 
 //------------------------------------------------------------------------------
@@ -131,20 +176,73 @@ void SoundAsset::copyTo(SimObject* object)
 
 void SoundAsset::initializeAsset(void)
 {
-   mSoundPath = expandAssetFilePath(mSoundFile);
+   Parent::initializeAsset();
+
+   if (mSoundFile == StringTable->EmptyString())
+      return;
+
+   //ResourceManager::get().getChangedSignal.notify(this, &SoundAsset::_onResourceChanged);
+
+   //Ensure our path is expando'd if it isn't already
+   if (!Platform::isFullPath(mSoundPath))
+      mSoundPath = getOwned() ? expandAssetFilePath(mSoundFile) : mSoundPath;
+
+   mSoundPath = expandAssetFilePath(mSoundPath);
+
+   loadSound();
+}
+
+void SoundAsset::_onResourceChanged(const Torque::Path &path)
+{
+   if (path != Torque::Path(mSoundPath))
+      return;
+
+   refreshAsset();
+
+   loadSound();
 }
 
 void SoundAsset::onAssetRefresh(void)
 {
-   mSoundPath = expandAssetFilePath(mSoundFile);
+   if (mSoundFile == StringTable->EmptyString())
+      return;
+
+   //Update
+   if (!Platform::isFullPath(mSoundFile))
+      mSoundPath = getOwned() ? expandAssetFilePath(mSoundFile) : mSoundPath;
+
+   loadSound();
+}
+
+bool SoundAsset::loadSound()
+{
+   if (mSoundPath)
+   {
+      if (!Torque::FS::IsFile(mSoundPath))
+      {
+         Con::errorf("SoundAsset::initializeAsset: Attempted to load file %s but it was not valid!", mSoundFile);
+         mLoadedState = BadFileReference;
+         return false;
+      }
+      else
+      {// = new SFXProfile(mProfileDesc, mSoundFile, mPreload);
+         mSFXProfile.setDescription(&mProfileDesc);
+         mSFXProfile.setSoundFileName(mSoundFile);
+         mSFXProfile.setPreload(mPreload);
+      }
+
+   }
+   mChangeSignal.trigger();
+   mLoadedState = Ok;
+   return true;
 }
 
 void SoundAsset::setSoundFile(const char* pSoundFile)
 {
    // Sanity!
-   AssertFatal(pSoundFile != NULL, "Cannot use a NULL shape file.");
+   AssertFatal(pSoundFile != NULL, "Cannot use a NULL sound file.");
 
-   // Fetch image file.
+   // Fetch sound file.
    pSoundFile = StringTable->insert(pSoundFile);
 
    // Ignore no change,
@@ -152,7 +250,7 @@ void SoundAsset::setSoundFile(const char* pSoundFile)
       return;
 
    // Update.
-   mSoundFile = StringTable->insert(pSoundFile);
+   mSoundFile = pSoundFile;
 
    // Refresh the asset.
    refreshAsset();
@@ -162,3 +260,43 @@ DefineEngineMethod(SoundAsset, getSoundPath, const char*, (), , "")
 {
    return object->getSoundPath();
 }
+
+IMPLEMENT_CONOBJECT(GuiInspectorTypeSoundAssetPtr);
+
+ConsoleDocClass(GuiInspectorTypeSoundAssetPtr,
+   "@brief Inspector field type for Sounds\n\n"
+   "Editor use only.\n\n"
+   "@internal"
+);
+
+void GuiInspectorTypeSoundAssetPtr::consoleInit()
+{
+   Parent::consoleInit();
+
+   ConsoleBaseType::getType(TypeSoundAssetPtr)->setInspectorFieldType("GuiInspectorTypeSoundAssetPtr");
+}
+
+GuiControl * GuiInspectorTypeSoundAssetPtr::constructEditControl()
+{
+   return nullptr;
+}
+
+bool GuiInspectorTypeSoundAssetPtr::updateRects()
+{
+   return false;
+}
+
+IMPLEMENT_CONOBJECT(GuiInspectorTypeSoundAssetId);
+
+ConsoleDocClass(GuiInspectorTypeSoundAssetId,
+   "@brief Inspector field type for Sounds\n\n"
+   "Editor use only.\n\n"
+   "@internal"
+);
+
+void GuiInspectorTypeSoundAssetId::consoleInit()
+{
+   Parent::consoleInit();
+
+   ConsoleBaseType::getType(TypeSoundAssetId)->setInspectorFieldType("GuiInspectorTypeSoundAssetId");
+}

+ 281 - 2
Engine/source/T3D/assets/SoundAsset.h

@@ -39,7 +39,25 @@
 #include "assets/assetFieldTypes.h"
 #endif
 
-class SFXTrack;
+#include "gui/editor/guiInspectorTypes.h"
+
+#ifndef _BITSTREAM_H_
+#include "core/stream/bitStream.h"
+#endif
+
+#ifndef _SFXRESOURCE_H_
+#include "sfx/sfxResource.h"
+#endif
+
+#ifndef _SFXDESCRIPTION_H_
+#include "sfx/sfxDescription.h"
+#endif // !_SFXDESCRIPTION_H_
+
+#ifndef _SFXPROFILE_H_
+#include "sfx/sfxProfile.h"
+#endif // !_SFXPROFILE_H_
+
+class SFXResource;
 
 //-----------------------------------------------------------------------------
 class SoundAsset : public AssetBase
@@ -49,8 +67,36 @@ class SoundAsset : public AssetBase
 protected:
    StringTableEntry        mSoundFile;
    StringTableEntry        mSoundPath;
+   SFXProfile              mSFXProfile;
+   SFXDescription          mProfileDesc;
+   // subtitles
+   StringTableEntry        mSubtitleString;
+   bool                    mPreload;
+
+   /*These will be needed in the refactor!
+   Resource<SFXResource>   mSoundResource;
+   
+
+   // SFXDesctriptions, some off these will be removed
    F32                     mPitchAdjust;
    F32                     mVolumeAdjust;
+   bool                    mIs3D;
+   bool                    mLoop;
+   bool                    mIsStreaming;
+   bool                    mUseHardware;
+
+   F32                     mMinDistance;
+   F32                     mMaxDistance;
+   U32                     mConeInsideAngle;
+   U32                     mConeOutsideAngle;
+   F32                     mConeOutsideVolume;
+   F32                     mRolloffFactor;
+   Point3F                 mScatterDistance;
+   F32                     mPriority;
+   */
+
+   typedef Signal<void()> SoundAssetChanged;
+   SoundAssetChanged mChangeSignal;
 
 public:
    SoundAsset();
@@ -60,16 +106,26 @@ public:
    static void initPersistFields();
    virtual void copyTo(SimObject* object);
 
+   //SFXResource* getSound() { return mSoundResource; }
+   Resource<SFXResource> getSoundResource() { return mSFXProfile.getResource(); }
+
    /// Declare Console Object.
    DECLARE_CONOBJECT(SoundAsset);
 
    void                    setSoundFile(const char* pSoundFile);
+   bool loadSound();
    inline StringTableEntry getSoundFile(void) const { return mSoundFile; };
-
    inline StringTableEntry getSoundPath(void) const { return mSoundPath; };
+   SFXProfile* getSfxProfile() { return &mSFXProfile; }
+   SFXDescription* getSfxDescription() { return &mProfileDesc; }
+
+   bool isLoop() { return mProfileDesc.mIsLooping; }
+   bool is3D() { return mProfileDesc.mIs3D; }
+
 
 protected:
    virtual void            initializeAsset(void);
+   void _onResourceChanged(const Torque::Path & path);
    virtual void            onAssetRefresh(void);
 
    static bool setSoundFile(void *obj, const char *index, const char *data) { static_cast<SoundAsset*>(obj)->setSoundFile(data); return false; }
@@ -77,6 +133,229 @@ protected:
 };
 
 DefineConsoleType(TypeSoundAssetPtr, SoundAsset)
+DefineConsoleType(TypeSoundAssetId, String)
+
+//-----------------------------------------------------------------------------
+// TypeAssetId GuiInspectorField Class
+//-----------------------------------------------------------------------------
+class GuiInspectorTypeSoundAssetPtr : public GuiInspectorTypeFileName
+{
+   typedef GuiInspectorTypeFileName Parent;
+public:
+
+   GuiBitmapButtonCtrl* mSoundButton;
+
+   DECLARE_CONOBJECT(GuiInspectorTypeSoundAssetPtr);
+   static void consoleInit();
+
+   virtual GuiControl* constructEditControl();
+   virtual bool updateRects();
+};
+
+class GuiInspectorTypeSoundAssetId : public GuiInspectorTypeSoundAssetPtr
+{
+   typedef GuiInspectorTypeSoundAssetPtr Parent;
+public:
+
+   DECLARE_CONOBJECT(GuiInspectorTypeSoundAssetId);
+   static void consoleInit();
+};
+
+#pragma region Singular Asset Macros
+
+//Singular assets
+/// <Summary>
+/// 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: \
+   Resource<SFXResource> m##name;\
+   StringTableEntry m##name##Name; \
+   StringTableEntry m##name##AssetId;\
+   AssetPtr<SoundAsset> m##name##Asset = NULL;\
+   SFXProfile* m##name##Profile = &profile;\
+public: \
+   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;}\
+   \
+   bool _set##name(StringTableEntry _in)\
+   {\
+      if(m##name##AssetId != _in || m##name##Name != _in)\
+      {\
+         if (_in == StringTable->EmptyString())\
+         {\
+            m##name##Name = StringTable->EmptyString();\
+            m##name##AssetId = StringTable->EmptyString();\
+            m##name##Asset = NULL;\
+            m##name = NULL;\
+            return true;\
+         }\
+         \
+         if (AssetDatabase.isDeclaredAsset(_in))\
+         {\
+            m##name##AssetId = _in;\
+            \
+            U32 assetState = SoundAsset::getAssetById(m##name##AssetId, &m##name##Asset);\
+            \
+            if (SoundAsset::Ok == assetState)\
+            {\
+               m##name##Name = StringTable->EmptyString();\
+            }\
+         }\
+         else\
+         {\
+            StringTableEntry assetId = SoundAsset::getAssetIdByFilename(_in);\
+            if (assetId != StringTable->EmptyString())\
+            {\
+               m##name##AssetId = assetId;\
+               if(SoundAsset::getAssetById(m##name##AssetId, &m##name##Asset) == SoundAsset::Ok)\
+               {\
+                  m##name##Name = StringTable->EmptyString();\
+               }\
+            }\
+            else\
+            {\
+               m##name##Name = _in;\
+               m##name##AssetId = StringTable->EmptyString();\
+               m##name##Asset = NULL;\
+            }\
+         }\
+      }\
+      if (get##name() != StringTable->EmptyString() && m##name##Asset.notNull())\
+      {\
+         m##name = m##name##Asset->getSoundResource();\
+      }\
+      else\
+      {\
+         m##name = NULL;\
+      }\
+      \
+      if (m##name##Asset.notNull() && m##name##Asset->getStatus() != ShapeAsset::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());\
+         return false; \
+      }\
+      else if (bool(m##name) == NULL)\
+      {\
+         Con::errorf("%s(%s)::_set%s() - Couldn't load sound \"%s\"", macroText(className), getName(), macroText(name), _in);\
+         return false;\
+      }\
+      return true;\
+   }\
+   \
+   const StringTableEntry get##name() const\
+   {\
+      if (m##name##Asset && (m##name##Asset->getSoundPath() != StringTable->EmptyString()))\
+         return m##name##Asset->getSoundPath();\
+      else if (m##name##AssetId != StringTable->EmptyString())\
+         return m##name##AssetId;\
+      else if (m##name##Name != StringTable->EmptyString())\
+         return StringTable->insert(m##name##Name);\
+      else\
+         return StringTable->EmptyString();\
+   }\
+   Resource<SFXResource> get##name##Resource() \
+   {\
+      return m##name;\
+   }
+
+#define DECLARE_SOUNDASSET_SETGET(className, name)\
+   static bool _set##name##Data(void* obj, const char* index, const char* data)\
+   {\
+      bool ret = false;\
+      className* object = static_cast<className*>(obj);\
+      ret = object->_set##name(StringTable->insert(data));\
+      return ret;\
+   }
+
+#define DECLARE_SOUNDASSET_NET_SETGET(className, name, bitmask)\
+   static bool _set##name##Data(void* obj, const char* index, const char* data)\
+   {\
+      bool ret = false;\
+      className* object = static_cast<className*>(obj);\
+      ret = object->_set##name(StringTable->insert(data));\
+      if(ret)\
+         object->setMaskBits(bitmask);\
+      return ret;\
+   }
+
+#define DEF_SOUNDASSET_BINDS(className,name)\
+DefineEngineMethod(className, get##name, String, (), , "get name")\
+{\
+   return object->get##name(); \
+}\
+DefineEngineMethod(className, get##name##Asset, String, (), , assetText(name, asset reference))\
+{\
+   return object->m##name##AssetId; \
+}\
+DefineEngineMethod(className, set##name, bool, (const char*  shape), , assetText(name,assignment. first tries asset then flat file.))\
+{\
+   return object->_set##name(StringTable->insert(shape));\
+}
+
+#define INIT_SOUNDASSET(name) \
+   m##name##Name = StringTable->EmptyString(); \
+   m##name##AssetId = StringTable->EmptyString(); \
+   m##name##Asset = NULL; \
+   m##name = NULL;\
+
+#ifdef TORQUE_SHOW_LEGACY_FILE_FIELDS
+
+#define INITPERSISTFIELD_SOUNDASSET(name, consoleClass, docs) \
+   addProtectedField(assetText(name, File), TypeSoundFilename, Offset(m##name##Name, consoleClass), _set##name##Data, & defaultProtectedGetFn, assetText(name, docs)); \
+   addProtectedField(assetText(name, Asset), TypeSoundAssetId, Offset(m##name##AssetId, consoleClass), _set##name##Data, & defaultProtectedGetFn, assetText(name, asset reference.));
+
+#else
+
+#define INITPERSISTFIELD_SOUNDASSET(name, consoleClass, docs) \
+   addProtectedField(assetText(name, File), TypeSoundFilename, Offset(m##name##Name, consoleClass), _set##name##Data, & defaultProtectedGetFn, assetText(name, docs), AbstractClassRep::FIELD_HideInInspectors); \
+   addProtectedField(assetText(name, Asset), TypeSoundAssetId, Offset(m##name##AssetId, consoleClass), _set##name##Data, & defaultProtectedGetFn, assetText(name, asset reference.));
+
+#endif // TORQUE_SHOW_LEGACY_FILE_FIELDS
+
+#define CLONE_SOUNDASSET(name) \
+   m##name##Name = other.m##name##Name;\
+   m##name##AssetId = other.m##name##AssetId;\
+   m##name##Asset = other.m##name##Asset;\
+
+#define PACKDATA_SOUNDASSET(name)\
+   if (stream->writeFlag(m##name##Asset.notNull()))\
+   {\
+      stream->writeString(m##name##Asset.getAssetId());\
+   }\
+   else\
+      stream->writeString(m##name##Name);
+
+#define UNPACKDATA_SOUNDASSET(name)\
+   if (stream->readFlag())\
+   {\
+      m##name##AssetId = stream->readSTString();\
+      _set##name(m##name##AssetId);\
+   }\
+   else\
+      m##name##Name = stream->readSTString();
+
+#define PACK_SOUNDASSET(netconn, name)\
+   if (stream->writeFlag(m##name##Asset.notNull()))\
+   {\
+      NetStringHandle assetIdStr = m##name##Asset.getAssetId();\
+      netconn->packNetStringHandleU(stream, assetIdStr);\
+   }\
+   else\
+      stream->writeString(m##name##Name);
+
+#define UNPACK_SOUNDASSET(netconn, name)\
+   if (stream->readFlag())\
+   {\
+      m##name##AssetId = StringTable->insert(netconn->unpackNetStringHandleU(stream).getString());\
+      _set##name(m##name##AssetId);\
+   }\
+   else\
+      m##name##Name = stream->readSTString();
+
+#pragma endregion
 
 #endif // _ASSET_BASE_H_
 

+ 5 - 9
Engine/source/T3D/assets/TerrainAsset.cpp

@@ -91,7 +91,7 @@ ConsoleSetType(TypeTerrainAssetPtr)
 
 
 //-----------------------------------------------------------------------------
-ConsoleType(assetIdString, TypeTerrainAssetId, String, ASSET_ID_FIELD_PREFIX)
+ConsoleType(assetIdString, TypeTerrainAssetId, const char*, ASSET_ID_FIELD_PREFIX)
 
 ConsoleGetType(TypeTerrainAssetId)
 {
@@ -107,11 +107,7 @@ ConsoleSetType(TypeTerrainAssetId)
       // Yes, so fetch field value.
       const char* pFieldValue = argv[0];
 
-      // Fetch asset Id.
-      StringTableEntry* assetId = (StringTableEntry*)(dptr);
-
-      // Update asset value.
-      *assetId = StringTable->insert(pFieldValue);
+      *((const char**)dptr) = StringTable->insert(argv[0]);
 
       return;
    }
@@ -194,7 +190,7 @@ void TerrainAsset::setTerrainFileName(const char* pScriptFile)
 
 bool TerrainAsset::loadTerrain()
 {
-   if (!Platform::isFile(mTerrainFilePath))
+   if (!Torque::FS::IsFile(mTerrainFilePath))
       return false;
 
    mTerrMaterialAssets.clear();
@@ -471,8 +467,8 @@ GuiControl* GuiInspectorTypeTerrainAssetPtr::constructEditControl()
 
    mShapeEdButton->setField("Command", "EditorGui.setEditor(TerrainEditorPlugin);");
 
-   char bitmapName[512] = "tools/worldEditor/images/toolbar/shape-editor";
-   mShapeEdButton->setBitmap(bitmapName);
+   char bitmapName[512] = "ToolsModule:TerrainBlock_image";
+   mShapeEdButton->setBitmap(StringTable->insert(bitmapName));
 
    mShapeEdButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
    mShapeEdButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");

+ 38 - 12
Engine/source/T3D/assets/TerrainMaterialAsset.cpp

@@ -119,11 +119,9 @@ void TerrainMaterialAsset::initializeAsset()
    // Call parent.
    Parent::initializeAsset();
 
-   compileShader();
-
    mScriptPath = expandAssetFilePath(mScriptFile);
 
-   if (Platform::isFile(mScriptPath))
+   if (Torque::FS::IsScriptFile(mScriptPath))
       Con::executeFile(mScriptPath, false, false);
 }
 
@@ -131,7 +129,7 @@ void TerrainMaterialAsset::onAssetRefresh()
 {
    mScriptPath = expandAssetFilePath(mScriptFile);
 
-   if (Platform::isFile(mScriptPath))
+   if (Torque::FS::IsScriptFile(mScriptPath))
       Con::executeFile(mScriptPath, false, false);
 
    if (mMatDefinitionName != StringTable->EmptyString())
@@ -164,21 +162,49 @@ void TerrainMaterialAsset::setScriptFile(const char* pScriptFile)
 
 //------------------------------------------------------------------------------
 
-void TerrainMaterialAsset::compileShader()
-{
-}
-
 void TerrainMaterialAsset::copyTo(SimObject* object)
 {
    // Call to parent.
    Parent::copyTo(object);
 }
 
-DefineEngineMethod(TerrainMaterialAsset, compileShader, void, (), , "Compiles the material's generated shader, if any. Not yet implemented\n")
+StringTableEntry TerrainMaterialAsset::getAssetIdByMaterialName(StringTableEntry matName)
 {
-   object->compileShader();
+   StringTableEntry materialAssetId = StringTable->EmptyString();
+
+   AssetQuery* query = new AssetQuery();
+   U32 foundCount = AssetDatabase.findAssetType(query, "TerrainMaterialAsset");
+   if (foundCount == 0)
+   {
+      //Didn't work, so have us fall back to a placeholder asset
+      materialAssetId = StringTable->insert("Core_Rendering:noMaterial");
+   }
+   else
+   {
+      for (U32 i = 0; i < foundCount; i++)
+      {
+         TerrainMaterialAsset* matAsset = AssetDatabase.acquireAsset<TerrainMaterialAsset>(query->mAssetList[i]);
+         if (matAsset && matAsset->getMaterialDefinitionName() == matName)
+         {
+            materialAssetId = matAsset->getAssetId();
+            AssetDatabase.releaseAsset(query->mAssetList[i]);
+            break;
+         }
+         AssetDatabase.releaseAsset(query->mAssetList[i]);
+      }
+   }
+
+   return materialAssetId;
 }
 
+#ifdef TORQUE_TOOLS
+DefineEngineStaticMethod(TerrainMaterialAsset, getAssetIdByMaterialName, const char*, (const char* materialName), (""),
+   "Queries the Asset Database to see if any asset exists that is associated with the provided material name.\n"
+   "@return The AssetId of the associated asset, if any.")
+{
+   return TerrainMaterialAsset::getAssetIdByMaterialName(StringTable->insert(materialName));
+}
+#endif
 //-----------------------------------------------------------------------------
 // GuiInspectorTypeAssetId
 //-----------------------------------------------------------------------------
@@ -218,7 +244,7 @@ GuiControl* GuiInspectorTypeTerrainMaterialAssetPtr::constructEditControl()
 
    TerrainMaterial* materialDef = nullptr;
 
-   char bitmapName[512] = "tools/worldEditor/images/toolbar/shape-editor";
+   char bitmapName[512] = "ToolsModule:material_editor_n_image";
 
    /*if (!Sim::findObject(matAsset->getMaterialDefinitionName(), materialDef))
    {
@@ -245,7 +271,7 @@ GuiControl* GuiInspectorTypeTerrainMaterialAssetPtr::constructEditControl()
    StringBuilder strbld;
    strbld.append(matAsset->getMaterialDefinitionName());
    strbld.append("\n");
-   strbld.append("Open this file in the Material Editor");
+   strbld.append("Open this asset in the Material Editor");
 
    mMatPreviewButton->setDataField(StringTable->insert("tooltip"), NULL, strbld.data());
 

+ 1 - 1
Engine/source/T3D/assets/TerrainMaterialAsset.h

@@ -66,7 +66,7 @@ public:
    static void initPersistFields();
    virtual void copyTo(SimObject* object);
 
-   void compileShader();
+   static StringTableEntry getAssetIdByMaterialName(StringTableEntry matName);
 
    StringTableEntry getMaterialDefinitionName() { return mMatDefinitionName; }
 

+ 357 - 213
Engine/source/T3D/assets/assetImporter.cpp

@@ -32,10 +32,11 @@ ConsoleDocClass(AssetImportConfig,
 IMPLEMENT_CONOBJECT(AssetImportConfig);
 
 AssetImportConfig::AssetImportConfig() :
-   DuplicatAutoResolution("AutoRename"),
+   DuplicateAutoResolution("AutoRename"),
    WarningsAsErrors(false),
    PreventImportWithErrors(true),
    AutomaticallyPromptMissingFiles(false),
+   AddDirectoryPrefixToAssetName(false),
    ImportMesh(true),
    AlwaysAddShapeSuffix(false),
    AddedShapeSuffix("_shape"),
@@ -89,7 +90,7 @@ AssetImportConfig::AssetImportConfig() :
    ImageType("GUI"),
    DiffuseTypeSuffixes("_ALBEDO,_DIFFUSE,_ALB,_DIF,_COLOR,_COL,_A,_C,-ALBEDO,-DIFFUSE,-ALB,-DIF,-COLOR,-COL,-A,-C"),
    NormalTypeSuffixes("_NORMAL,_NORM,_N,-NORMAL,-NORM,-N"),
-   MetalnessTypeSuffixes("_METAL,_MET,_METALNESS,_METALLIC,_M,-METAL, -MET, -METALNESS, -METALLIC, -M"),
+   MetalnessTypeSuffixes("_METAL,_MET,_METALNESS,_METALLIC,_M,-METAL,-MET,-METALNESS,-METALLIC,-M"),
    RoughnessTypeSuffixes("_ROUGH,_ROUGHNESS,_R,-ROUGH,-ROUGHNESS,-R"),
    SmoothnessTypeSuffixes("_SMOOTH,_SMOOTHNESS,_S,-SMOOTH,-SMOOTHNESS,-S"),
    AOTypeSuffixes("_AO,_AMBIENT,_AMBIENTOCCLUSION,-AO,-AMBIENT,-AMBIENTOCCLUSION"),
@@ -131,10 +132,11 @@ void AssetImportConfig::initPersistFields()
    Parent::initPersistFields();
 
    addGroup("General");
-      addField("DuplicatAutoResolution", TypeRealString, Offset(DuplicatAutoResolution, AssetImportConfig), "Duplicate Asset Auto-Resolution Action. Options are None, AutoPrune, AutoRename");
+      addField("DuplicateAutoResolution", TypeRealString, Offset(DuplicateAutoResolution, AssetImportConfig), "Duplicate Asset Auto-Resolution Action. Options are None, AutoPrune, AutoRename, FolderPrefix");
       addField("WarningsAsErrors", TypeBool, Offset(WarningsAsErrors, AssetImportConfig), "Indicates if warnings should be treated as errors");
       addField("PreventImportWithErrors", TypeBool, Offset(PreventImportWithErrors, AssetImportConfig), "Indicates if importing should be prevented from completing if any errors are detected at all");
       addField("AutomaticallyPromptMissingFiles", TypeBool, Offset(AutomaticallyPromptMissingFiles, AssetImportConfig), "Should the importer automatically prompt to find missing files if they are not detected automatically by the importer");
+      addField("AddDirectoryPrefixToAssetName", TypeBool, Offset(AddDirectoryPrefixToAssetName, AssetImportConfig), "Should the importer add the folder name as a prefix to the assetName. Helps prevent name collisions.");   
    endGroup("General");
 
    addGroup("Meshes");
@@ -228,10 +230,11 @@ void AssetImportConfig::initPersistFields()
 void AssetImportConfig::loadImportConfig(Settings* configSettings, String configName)
 {
    //General
-   DuplicatAutoResolution = configSettings->value(String(configName + "/General/DuplicatAutoResolution").c_str());
+   DuplicateAutoResolution = configSettings->value(String(configName + "/General/DuplicateAutoResolution").c_str());
    WarningsAsErrors = dAtob(configSettings->value(String(configName + "/General/WarningsAsErrors").c_str()));
    PreventImportWithErrors = dAtob(configSettings->value(String(configName + "/General/PreventImportWithErrors").c_str()));
    AutomaticallyPromptMissingFiles = dAtob(configSettings->value(String(configName + "/General/AutomaticallyPromptMissingFiles").c_str()));
+   AddDirectoryPrefixToAssetName = dAtob(configSettings->value(String(configName + "/General/AddDirectoryPrefixToAssetName").c_str()));
 
    //Meshes
    ImportMesh = dAtob(configSettings->value(String(configName + "/Meshes/ImportMesh").c_str()));
@@ -317,10 +320,11 @@ void AssetImportConfig::loadImportConfig(Settings* configSettings, String config
 
 void AssetImportConfig::CopyTo(AssetImportConfig* target) const
 {
-   target->DuplicatAutoResolution = DuplicatAutoResolution;
+   target->DuplicateAutoResolution = DuplicateAutoResolution;
    target->WarningsAsErrors = WarningsAsErrors;
    target->PreventImportWithErrors = PreventImportWithErrors;
    target->AutomaticallyPromptMissingFiles = AutomaticallyPromptMissingFiles;
+   target->AddDirectoryPrefixToAssetName = AddDirectoryPrefixToAssetName;
 
    //Meshes
    target->ImportMesh = ImportMesh;
@@ -1444,8 +1448,8 @@ void AssetImporter::processImportAssets(AssetImportObject* assetItem)
          if (!childItem->processed)
          {
             //Sanitize before modifying our asset name(suffix additions, etc)
-            if (childItem->assetName != childItem->cleanAssetName)
-               childItem->assetName = childItem->cleanAssetName;
+            //if (childItem->assetName != childItem->cleanAssetName)
+            //   childItem->assetName = childItem->cleanAssetName;
 
             //handle special pre-processing here for any types that need it
 
@@ -1579,6 +1583,28 @@ void AssetImporter::processImageAsset(AssetImportObject* assetItem)
 
       }
    }
+   else
+   {
+      //If we're processing an unaffiliated image without generating materials for it, we can check some other bits
+      if (assetItem->parentAssetItem == nullptr)
+      {
+         if (assetItem->typeHint != String::EmptyString)
+         {
+            ImageAssetType type = ImageAsset::getImageTypeFromName(StringTable->insert(assetItem->typeHint.c_str()));
+
+            if (type == ImageAssetType::GUI)
+            {
+
+            }
+         }
+      }
+   }
+
+   if(assetItem->assetName == assetItem->cleanAssetName && activeImportConfig->AlwaysAddImageSuffix)
+   {
+      assetItem->assetName = assetItem->assetName + activeImportConfig->AddedImageSuffix;
+      assetItem->cleanAssetName = assetItem->assetName;
+   }
 
    assetItem->processed = true;
 }
@@ -1612,174 +1638,214 @@ void AssetImporter::processMaterialAsset(AssetImportObject* assetItem)
       }
    }
 
-   if (activeImportConfig->AlwaysAddMaterialSuffix)
+   if (activeImportConfig->UseExistingMaterials)
    {
-      assetItem->assetName += activeImportConfig->AddedMaterialSuffix;
-   }
+      //So if the material already exists, we should just use that. So first, let's find out if it already exists
 
-   if (activeImportConfig->PopulateMaterialMaps)
-   {
-      //If we're trying to populate the rest of our material maps, we need to go looking
-      dSprintf(importLogBuffer, sizeof(importLogBuffer), "Attempting to Auto-Populate Material Maps");
-      activityLog.push_back(importLogBuffer);
+      //check to see if the definition for this already exists
+      StringTableEntry existingMatAsset = MaterialAsset::getAssetIdByMaterialName(StringTable->insert(assetName));
 
-      AssetImportObject* matchedImageTypes[ImageAsset::ImageTypeCount] = { nullptr };
+      if (existingMatAsset != StringTable->EmptyString())
+      {
+         assetItem->skip = true;
+         dSprintf(importLogBuffer, sizeof(importLogBuffer), "Material %s has been skipped because we already found an asset Id that uses that material definition. The found assetId is: %s", assetItem->assetName.c_str(), existingMatAsset);
+         activityLog.push_back(importLogBuffer);
+         return;
+      }
 
-      String materialImageNoSuffix;
+      //If there was no existing assetId, then lets see if it already exists in a legacy file, like a materials.cs or materials.tscript
+      //If it does, we'll just make our asset point to that instead of a new file
+      Material* mat = MATMGR->getMaterialDefinitionByName(assetName);
 
-      for (U32 i = 0; i < assetItem->childAssetItems.size(); i++)
+      if (!mat)
+         mat = MATMGR->getMaterialDefinitionByMapTo(assetName);
+
+      if (!mat && assetItem->assetName != assetItem->cleanAssetName)
       {
-         AssetImportObject* childAssetItem = assetItem->childAssetItems[i];
+         mat = MATMGR->getMaterialDefinitionByName(assetItem->cleanAssetName);
 
-         if (childAssetItem->skip || childAssetItem->assetType != String("ImageAsset"))
-            continue;
+         if (!mat)
+            mat = MATMGR->getMaterialDefinitionByMapTo(assetItem->cleanAssetName);
+      }
 
-         for (S32 t = 0; t < ImageAsset::ImageTypeCount; t++)
+      if(mat)
+      {
+         //We found a match, so just modify our asset item's info to point against it. This will create the asset definition, but otherwise leave the material definition as-is.
+         assetItem->filePath = mat->getFilename();
+      }
+   }
+   else
+   {
+      if (activeImportConfig->AlwaysAddMaterialSuffix) //we only opt to force on the suffix if we're not obligating using the original material defs
+      {
+         assetItem->assetName += activeImportConfig->AddedMaterialSuffix;
+         assetItem->cleanAssetName = assetItem->assetName;
+      }
+
+      if (activeImportConfig->PopulateMaterialMaps)
+      {
+         //If we're trying to populate the rest of our material maps, we need to go looking
+         dSprintf(importLogBuffer, sizeof(importLogBuffer), "Attempting to Auto-Populate Material Maps");
+         activityLog.push_back(importLogBuffer);
+
+         AssetImportObject* matchedImageTypes[ImageAsset::ImageTypeCount] = { nullptr };
+
+         String materialImageNoSuffix;
+
+         for (U32 i = 0; i < assetItem->childAssetItems.size(); i++)
          {
-            //If the imageType name and child asset image type match, check it off our list
-            if (!dStricmp(ImageAsset::getImageTypeNameFromType((ImageAsset::ImageTypes)t), childAssetItem->imageSuffixType.c_str()))
-            {
-               matchedImageTypes[t] = childAssetItem;
+            AssetImportObject* childAssetItem = assetItem->childAssetItems[i];
 
-               if (t == ImageAsset::ImageTypes::Albedo)
+            if (childAssetItem->skip || childAssetItem->assetType != String("ImageAsset"))
+               continue;
+
+            for (S32 t = 0; t < ImageAsset::ImageTypeCount; t++)
+            {
+               //If the imageType name and child asset image type match, check it off our list
+               if (!dStricmp(ImageAsset::getImageTypeNameFromType((ImageAsset::ImageTypes)t), childAssetItem->imageSuffixType.c_str()))
                {
-                  String sufType;
-                  String suffix = parseImageSuffixes(childAssetItem->assetName, &sufType);
+                  matchedImageTypes[t] = childAssetItem;
 
-                  String imageAssetName = childAssetItem->assetName;
+                  if (t == ImageAsset::ImageTypes::Albedo)
+                  {
+                     String sufType;
+                     String suffix = parseImageSuffixes(childAssetItem->assetName, &sufType);
 
-                  if (suffix.isEmpty())
-                     materialImageNoSuffix = imageAssetName;
-                  else
-                     materialImageNoSuffix = imageAssetName.erase(imageAssetName.length() - suffix.length(), suffix.length());//cache this for later as we may need it for file association lookups
+                     String imageAssetName = childAssetItem->assetName;
+
+                     if (suffix.isEmpty())
+                        materialImageNoSuffix = imageAssetName;
+                     else
+                        materialImageNoSuffix = imageAssetName.erase(imageAssetName.length() - suffix.length(), suffix.length());//cache this for later as we may need it for file association lookups
+                  }
                }
             }
          }
-      }
 
-      //Now that we've checked off any existingly matched image types, process through the unmatched to look for files that associate
-      for (S32 t = 0; t < ImageAsset::ImageTypeCount; t++)
-      {
-         //This type wasn't found, so try and find a match based on suffix
-         String suffixList;
-
-         switch (t)
+         //Now that we've checked off any existingly matched image types, process through the unmatched to look for files that associate
+         for (S32 t = 0; t < ImageAsset::ImageTypeCount; t++)
          {
-         case ImageAsset::Albedo:
-            suffixList = activeImportConfig->DiffuseTypeSuffixes;
-            break;
-         case ImageAsset::Normal:
-            suffixList = activeImportConfig->NormalTypeSuffixes;
-            break;
-         case ImageAsset::ORMConfig:
-            suffixList = activeImportConfig->PBRTypeSuffixes;
-            break;
-         case ImageAsset::Metalness:
-            suffixList = activeImportConfig->MetalnessTypeSuffixes;
-            break;
-         case ImageAsset::AO:
-            suffixList = activeImportConfig->AOTypeSuffixes;
-            break;
-         case ImageAsset::Roughness:
-            suffixList = activeImportConfig->RoughnessTypeSuffixes;
-            break;
-            //TODO: Glow map lookup too
-         }
+            //This type wasn't found, so try and find a match based on suffix
+            String suffixList;
 
-         if (!matchedImageTypes[t])
-         {
-            U32 suffixCount = StringUnit::getUnitCount(suffixList.c_str(), ",;\t");
-            for (U32 i = 0; i < suffixCount; i++)
+            switch (t)
             {
-               //First, try checking based on the material's assetName for our patternbase
-               String testPath = assetItem->filePath.getRootAndPath();
-               testPath += "/" + assetItem->cleanAssetName + StringUnit::getUnit(suffixList.c_str(), i, ",;\t");
-
-               String imagePath = AssetImporter::findImagePath(testPath);
+            case ImageAsset::Albedo:
+               suffixList = activeImportConfig->DiffuseTypeSuffixes;
+               break;
+            case ImageAsset::Normal:
+               suffixList = activeImportConfig->NormalTypeSuffixes;
+               break;
+            case ImageAsset::ORMConfig:
+               suffixList = activeImportConfig->PBRTypeSuffixes;
+               break;
+            case ImageAsset::Metalness:
+               suffixList = activeImportConfig->MetalnessTypeSuffixes;
+               break;
+            case ImageAsset::AO:
+               suffixList = activeImportConfig->AOTypeSuffixes;
+               break;
+            case ImageAsset::Roughness:
+               suffixList = activeImportConfig->RoughnessTypeSuffixes;
+               break;
+               //TODO: Glow map lookup too
+            }
 
-               if (imagePath.isNotEmpty())
+            if (!matchedImageTypes[t])
+            {
+               U32 suffixCount = StringUnit::getUnitCount(suffixList.c_str(), ",;\t");
+               for (U32 i = 0; i < suffixCount; i++)
                {
-                  //got a match!
-                  AssetImportObject* newImageAssetObj = addImportingAsset("ImageAsset", imagePath, assetItem, "");
+                  //First, try checking based on the material's assetName for our patternbase
+                  String testPath = assetItem->filePath.getRootAndPath();
+                  testPath += "/" + assetItem->cleanAssetName + StringUnit::getUnit(suffixList.c_str(), i, ",;\t");
 
-                  newImageAssetObj->imageSuffixType = ImageAsset::getImageTypeNameFromType((ImageAsset::ImageTypes)t);
+                  String imagePath = AssetImporter::findImagePath(testPath);
 
-                  matchedImageTypes[t] = newImageAssetObj;
-                  break;
-               }
-               else
-               {
-                  if(materialImageNoSuffix.isNotEmpty())
+                  if (imagePath.isNotEmpty())
                   {
-                     testPath = assetItem->filePath.getRootAndPath();
-                     testPath += "/" + materialImageNoSuffix + StringUnit::getUnit(suffixList.c_str(), i, ",;\t");
+                     //got a match!
+                     AssetImportObject* newImageAssetObj = addImportingAsset("ImageAsset", imagePath, assetItem, "");
 
-                     imagePath = AssetImporter::findImagePath(testPath);
+                     newImageAssetObj->imageSuffixType = ImageAsset::getImageTypeNameFromType((ImageAsset::ImageTypes)t);
 
-                     if (imagePath.isNotEmpty())
+                     matchedImageTypes[t] = newImageAssetObj;
+                     break;
+                  }
+                  else
+                  {
+                     if (materialImageNoSuffix.isNotEmpty())
                      {
-                        //got a match!
-                        AssetImportObject* newImageAssetObj = addImportingAsset("ImageAsset", imagePath, assetItem, "");
+                        testPath = assetItem->filePath.getRootAndPath();
+                        testPath += "/" + materialImageNoSuffix + StringUnit::getUnit(suffixList.c_str(), i, ",;\t");
+
+                        imagePath = AssetImporter::findImagePath(testPath);
 
-                        newImageAssetObj->imageSuffixType = ImageAsset::getImageTypeNameFromType((ImageAsset::ImageTypes)t);
+                        if (imagePath.isNotEmpty())
+                        {
+                           //got a match!
+                           AssetImportObject* newImageAssetObj = addImportingAsset("ImageAsset", imagePath, assetItem, "");
 
-                        matchedImageTypes[t] = newImageAssetObj;
-                        break;
+                           newImageAssetObj->imageSuffixType = ImageAsset::getImageTypeNameFromType((ImageAsset::ImageTypes)t);
+
+                           matchedImageTypes[t] = newImageAssetObj;
+                           break;
+                        }
                      }
                   }
                }
-            }
-
-            //If we're the abledo slot and after all that we didn't find anything, it probably is a suffixless image
-            if (t == ImageAsset::Albedo && matchedImageTypes[t] == nullptr)
-            {
-               String testPath = assetItem->filePath.getRootAndPath() + "/" + assetItem->cleanAssetName;
-               String imagePath = AssetImporter::findImagePath(testPath);
 
-               if (imagePath.isNotEmpty())
+               //If we're the abledo slot and after all that we didn't find anything, it probably is a suffixless image
+               if (t == ImageAsset::Albedo && matchedImageTypes[t] == nullptr)
                {
-                  //got a match!
-                  AssetImportObject* newImageAssetObj = addImportingAsset("ImageAsset", imagePath, assetItem, "");
+                  String testPath = assetItem->filePath.getRootAndPath() + "/" + assetItem->cleanAssetName;
+                  String imagePath = AssetImporter::findImagePath(testPath);
 
-                  //In the event that the names match, we want to avoid duplications, so we'll go ahead and append a suffix onto our new image asset
-                  if (newImageAssetObj->assetName == assetItem->assetName)
+                  if (imagePath.isNotEmpty())
                   {
-                     newImageAssetObj->assetName += StringUnit::getUnit(suffixList.c_str(), 0, ",;\t");
-                     newImageAssetObj->cleanAssetName = newImageAssetObj->assetName;
-                  }
+                     //got a match!
+                     AssetImportObject* newImageAssetObj = addImportingAsset("ImageAsset", imagePath, assetItem, "");
+
+                     //In the event that the names match, we want to avoid duplications, so we'll go ahead and append a suffix onto our new image asset
+                     if (newImageAssetObj->assetName == assetItem->assetName)
+                     {
+                        newImageAssetObj->assetName += StringUnit::getUnit(suffixList.c_str(), 0, ",;\t");
+                        newImageAssetObj->cleanAssetName = newImageAssetObj->assetName;
+                     }
 
-                  newImageAssetObj->imageSuffixType = ImageAsset::getImageTypeNameFromType(ImageAsset::ImageTypes::Albedo);
+                     newImageAssetObj->imageSuffixType = ImageAsset::getImageTypeNameFromType(ImageAsset::ImageTypes::Albedo);
 
-                  matchedImageTypes[t] = newImageAssetObj;
+                     matchedImageTypes[t] = newImageAssetObj;
+                  }
                }
             }
-         }
-         else
-         {
-            //just a bit of cleanup and logical testing for matches
-            //in the event we KNOW what the type is, but we don't have a suffix, such as a found image on a material lookup
-            //that doesn't have a suffix, we assume it to be the albedo, so we'll just append the suffix to avoid collisions if
-            //the name already matches our material name, similar to above logic
-            if (matchedImageTypes[t]->assetName == assetItem->assetName)
+            else
             {
-               matchedImageTypes[t]->assetName += StringUnit::getUnit(suffixList.c_str(), 0, ",;\t");
-               matchedImageTypes[t]->cleanAssetName = matchedImageTypes[t]->assetName;
+               //just a bit of cleanup and logical testing for matches
+               //in the event we KNOW what the type is, but we don't have a suffix, such as a found image on a material lookup
+               //that doesn't have a suffix, we assume it to be the albedo, so we'll just append the suffix to avoid collisions if
+               //the name already matches our material name, similar to above logic
+               if (matchedImageTypes[t]->assetName == assetItem->assetName)
+               {
+                  matchedImageTypes[t]->assetName += StringUnit::getUnit(suffixList.c_str(), 0, ",;\t");
+                  matchedImageTypes[t]->cleanAssetName = matchedImageTypes[t]->assetName;
+               }
             }
          }
-      }
 
-      /*for (U32 i = 0; i < assetItem->childAssetItems.size(); i++)
-      {
-         AssetImportObject* childAssetItem = assetItem->childAssetItems[i];
+         /*for (U32 i = 0; i < assetItem->childAssetItems.size(); i++)
+         {
+            AssetImportObject* childAssetItem = assetItem->childAssetItems[i];
 
-         if (childAssetItem->skip || childAssetItem->processed || childAssetItem->assetType != String("ImageAsset"))
-            continue;
+            if (childAssetItem->skip || childAssetItem->processed || childAssetItem->assetType != String("ImageAsset"))
+               continue;
 
-         if (childAssetItem->imageSuffixType == String("Albedo"))
-         {
-            assetItem->diffuseImageAsset = % childAssetItem;
-         }
-      }*/
+            if (childAssetItem->imageSuffixType == String("Albedo"))
+            {
+               assetItem->diffuseImageAsset = % childAssetItem;
+            }
+         }*/
+      }
    }
    
    assetItem->processed = true;
@@ -1822,6 +1888,7 @@ void AssetImporter::processShapeAsset(AssetImportObject* assetItem)
    if (activeImportConfig->AlwaysAddShapeSuffix)
    {
       assetItem->assetName += activeImportConfig->AddedShapeSuffix;
+      assetItem->cleanAssetName = assetItem->assetName;
    }
 
    S32 meshCount = dAtoi(assetItem->shapeInfo->getDataField(StringTable->insert("_meshCount"), nullptr));
@@ -1888,7 +1955,7 @@ void AssetImporter::processShapeMaterialInfo(AssetImportObject* assetItem, S32 m
    if (matName == assetItem->assetName)
    {
       //So apparently we managed to name the material the same as the shape. So we'll tweak the name
-      matAssetName += activeImportConfig->AlwaysAddMaterialSuffix;
+      matAssetName += activeImportConfig->AddedMaterialSuffix;
    }
 
    //Do a check so we don't import materials that are on our ignore list
@@ -2278,18 +2345,18 @@ void AssetImporter::resolveAssetItemIssues(AssetImportObject* assetItem)
       String humanReadableReason = assetItem->statusType == String("DuplicateImportAsset") ? "Importing asset was duplicate of another importing asset" : "Importing asset was duplicate of an existing asset";
 
       //get the config value for duplicateAutoResolution
-      if (activeImportConfig->DuplicatAutoResolution == String("AutoPrune"))
+      if (activeImportConfig->DuplicateAutoResolution == String("AutoPrune"))
       {
          //delete the item
          deleteImportingAsset(assetItem);
 
          //log it's deletion
-         dSprintf(importLogBuffer, sizeof(importLogBuffer), "Asset %s was autoprined due to %s as part of the Import Configuration", assetItem->assetName.c_str(), humanReadableReason.c_str());
+         dSprintf(importLogBuffer, sizeof(importLogBuffer), "Asset %s was autopruned due to %s as part of the Import Configuration", assetItem->assetName.c_str(), humanReadableReason.c_str());
          activityLog.push_back(importLogBuffer);
 
          importIssues = false;
       }
-      else if (activeImportConfig->DuplicatAutoResolution == String("AutoRename"))
+      else if (activeImportConfig->DuplicateAutoResolution == String("AutoRename"))
       {
          //Set trailing number
          String renamedAssetName = assetItem->assetName;
@@ -2308,10 +2375,31 @@ void AssetImporter::resolveAssetItemIssues(AssetImportObject* assetItem)
          resetAssetValidationStatus(assetItem);
          importIssues = false;
       }
-      else if (activeImportConfig->DuplicatAutoResolution == String("UseExisting"))
+      else if (activeImportConfig->DuplicateAutoResolution == String("UseExisting"))
       {
 
       }
+      else if (activeImportConfig->DuplicateAutoResolution == String("FolderPrefix"))
+      {
+         //Set trailing number
+         String renamedAssetName = assetItem->assetName;
+         String owningFolder = assetItem->filePath.getDirectory(assetItem->filePath.getDirectoryCount() - 1);
+
+         renamedAssetName = owningFolder + "_" + renamedAssetName;
+
+         //Log it's renaming
+         dSprintf(importLogBuffer, sizeof(importLogBuffer), "Asset %s was renamed due to %s as part of the Import Configuration", assetItem->assetName.c_str(), humanReadableReason.c_str());
+         activityLog.push_back(importLogBuffer);
+
+         dSprintf(importLogBuffer, sizeof(importLogBuffer), "Asset %s was renamed to %s", assetItem->assetName.c_str(), renamedAssetName.c_str());
+         activityLog.push_back(importLogBuffer);
+
+         assetItem->assetName = renamedAssetName;
+
+         //Whatever status we had prior is no longer relevent, so reset the status
+         resetAssetValidationStatus(assetItem);
+         importIssues = false;
+      }
    }
    else if (assetItem->statusType == String("MissingFile"))
    {
@@ -2348,7 +2436,7 @@ void AssetImporter::resetImportConfig()
 //
 // Importing
 //
-StringTableEntry AssetImporter::autoImportFile(Torque::Path filePath)
+StringTableEntry AssetImporter::autoImportFile(Torque::Path filePath, String typeHint)
 {
    //Just in case we're reusing the same importer object from another import session, nuke any existing files
    resetImportSession(true);
@@ -2359,6 +2447,8 @@ StringTableEntry AssetImporter::autoImportFile(Torque::Path filePath)
    {
       dSprintf(importLogBuffer, sizeof(importLogBuffer), "Unable to import file %s because it is a folder or zip.", filePath.getFullPath().c_str());
       activityLog.push_back(importLogBuffer);
+
+      dumpActivityLog();
       return StringTable->EmptyString();
    }
 
@@ -2366,6 +2456,8 @@ StringTableEntry AssetImporter::autoImportFile(Torque::Path filePath)
    {
       dSprintf(importLogBuffer, sizeof(importLogBuffer), "Unable to import file %s because it is of an unrecognized/unsupported type.", filePath.getFullPath().c_str());
       activityLog.push_back(importLogBuffer);
+
+      dumpActivityLog();
       return StringTable->EmptyString();
    }
 
@@ -2374,7 +2466,10 @@ StringTableEntry AssetImporter::autoImportFile(Torque::Path filePath)
 
    if (targetModuleDef == nullptr)
    {
-      //log it
+      dSprintf(importLogBuffer, sizeof(importLogBuffer), "Unable to import file %s because it is not in a valid module folder.", filePath.getFullPath().c_str());
+      activityLog.push_back(importLogBuffer);
+
+      dumpActivityLog();
       return StringTable->EmptyString();
    }
    else
@@ -2404,14 +2499,7 @@ StringTableEntry AssetImporter::autoImportFile(Torque::Path filePath)
       importAssets();
    }
 
-#if TORQUE_DEBUG
-   Con::printf("/***************/");
-   for (U32 i = 0; i < activityLog.size(); i++)
-   {
-      Con::printf(activityLog[i].c_str());
-   }
-   Con::printf("/***************/");
-#endif
+   dumpActivityLog();
 
    if (hasIssues)
    {
@@ -2628,8 +2716,15 @@ Torque::Path AssetImporter::importImageAsset(AssetImportObject* assetItem)
       newAsset->setDataField(StringTable->insert("originalFilePath"), nullptr, qualifiedFromFile);
    }
 
-   ImageAsset::ImageTypes imageType = ImageAsset::getImageTypeFromName(assetItem->imageSuffixType.c_str());
-   newAsset->setImageType(imageType);
+   if (assetItem->typeHint != String::EmptyString)
+   {
+      newAsset->setImageType(ImageAsset::getImageTypeFromName(StringTable->insert(assetItem->typeHint.c_str())));
+   }
+   else
+   {
+      ImageAsset::ImageTypes imageType = ImageAsset::getImageTypeFromName(assetItem->imageSuffixType.c_str());
+      newAsset->setImageType(imageType);
+   }
 
    Taml tamlWriter;
    bool importSuccessful = tamlWriter.write(newAsset, tamlPath.c_str());
@@ -2694,7 +2789,7 @@ Torque::Path AssetImporter::importMaterialAsset(AssetImportObject* assetItem)
       dSprintf(dependencyFieldName, 64, "imageMap%i", dependencySlotId);
 
       char dependencyFieldDef[512];
-      dSprintf(dependencyFieldDef, 512, "@Asset=%s:%s", targetModuleId.c_str(), childItem->assetName.c_str());
+      dSprintf(dependencyFieldDef, 512, "%s=%s:%s", ASSET_ID_SIGNATURE, targetModuleId.c_str(), childItem->assetName.c_str());
 
       newAsset->setDataField(StringTable->insert(dependencyFieldName), nullptr, dependencyFieldDef);
 
@@ -2752,74 +2847,96 @@ Torque::Path AssetImporter::importMaterialAsset(AssetImportObject* assetItem)
    FileObject* file = new FileObject();
    file->registerObject();
 
-   //Now write the script file containing our material out
-   //There's 2 ways to do this. If we're in-place importing an existing asset, we can see if the definition existed already, like in an old
-   //materials.tscript file. if it does, we can just find the object by name, and save it out to our new file
-   //If not, we'll just generate one
-   Material* existingMat = MATMGR->getMaterialDefinitionByName(assetName);
-
-   //It's also possible that, for legacy models, the material hooks in via the material's mapTo field, and the material name is something completely different
-   //So we'll check for that as well if we didn't find it by name up above
-   if (existingMat == nullptr)
+   if (activeImportConfig->UseExistingMaterials && Platform::isFile(qualifiedFromFile))
    {
-      existingMat = MATMGR->getMaterialDefinitionByMapTo(assetName);
-   }
+      //Now write the script file containing our material out
+      //There's 2 ways to do this. If we're in-place importing an existing asset, we can see if the definition existed already, like in an old
+      //materials.tscript file. if it does, we can just find the object by name, and save it out to our new file
+      //If not, we'll just generate one
+      Material* existingMat = MATMGR->getMaterialDefinitionByName(assetName);
 
-   if (existingMat)
-   {
-      for (U32 i = 0; i < assetItem->childAssetItems.size(); i++)
+      //It's also possible that, for legacy models, the material hooks in via the material's mapTo field, and the material name is something completely different
+      //So we'll check for that as well if we didn't find it by name up above
+      if (existingMat == nullptr)
+         existingMat = MATMGR->getMaterialDefinitionByMapTo(assetName);
+
+      if (existingMat == nullptr && assetItem->assetName != assetItem->cleanAssetName)
       {
-         AssetImportObject* childItem = assetItem->childAssetItems[i];
+         existingMat = MATMGR->getMaterialDefinitionByName(assetItem->cleanAssetName);
+         if (existingMat == nullptr)
+            existingMat = MATMGR->getMaterialDefinitionByMapTo(assetItem->cleanAssetName);
+      }
 
-         if (childItem->skip || !childItem->processed || childItem->assetType.compare("ImageAsset") != 0)
-            continue;
+      if (existingMat)
+      {
+         PersistenceManager* persistMgr;
+         if (Sim::findObject("ImageAssetValidator", persistMgr))
+         {
+            for (U32 i = 0; i < assetItem->childAssetItems.size(); i++)
+            {
+               AssetImportObject* childItem = assetItem->childAssetItems[i];
 
-         String path = childItem->filePath.getFullFileName();
+               if (childItem->skip || !childItem->processed || childItem->assetType.compare("ImageAsset") != 0)
+                  continue;
 
-         String mapFieldName = "";
-         String assetFieldName = "";
+               String path = childItem->filePath.getFullFileName();
 
-         ImageAsset::ImageTypes imageType = ImageAsset::getImageTypeFromName(childItem->imageSuffixType);
+               String mapFieldName = "";
+               String assetFieldName = "";
 
-         if (imageType == ImageAsset::ImageTypes::Albedo || childItem->imageSuffixType.isEmpty())
-         {
-            mapFieldName = "DiffuseMap";
-         }
-         else if (imageType == ImageAsset::ImageTypes::Normal)
-         {
-            mapFieldName = "NormalMap";
-         }
-         else if (imageType == ImageAsset::ImageTypes::ORMConfig)
-         {
-            mapFieldName = "ORMConfig";
-         }
-         else if (imageType == ImageAsset::ImageTypes::Metalness)
-         {
-            mapFieldName = "MetalnessMap";
-         }
-         else if (imageType == ImageAsset::ImageTypes::AO)
-         {
-            mapFieldName = "AOMap";
-         }
-         else if (imageType == ImageAsset::ImageTypes::Roughness)
-         {
-            mapFieldName = "RoughnessMap";
-         }
+               ImageAsset::ImageTypes imageType = ImageAsset::getImageTypeFromName(childItem->imageSuffixType);
 
-         assetFieldName = mapFieldName + "Asset[0]";
-         mapFieldName += "[0]";
+               if (imageType == ImageAsset::ImageTypes::Albedo || childItem->imageSuffixType.isEmpty())
+               {
+                  mapFieldName = "DiffuseMap";
+               }
+               else if (imageType == ImageAsset::ImageTypes::Normal)
+               {
+                  mapFieldName = "NormalMap";
+               }
+               else if (imageType == ImageAsset::ImageTypes::ORMConfig)
+               {
+                  mapFieldName = "ORMConfig";
+               }
+               else if (imageType == ImageAsset::ImageTypes::Metalness)
+               {
+                  mapFieldName = "MetalnessMap";
+               }
+               else if (imageType == ImageAsset::ImageTypes::AO)
+               {
+                  mapFieldName = "AOMap";
+               }
+               else if (imageType == ImageAsset::ImageTypes::Roughness)
+               {
+                  mapFieldName = "RoughnessMap";
+               }
 
-         //If there's already an existing image map file on the material definition in this slot, don't override it
-         if(!path.isEmpty())
-            existingMat->writeField(mapFieldName.c_str(), path.c_str());
+               assetFieldName = mapFieldName + "Asset[0]";
+               mapFieldName += "[0]";
 
-         String targetAsset = targetModuleId + ":" + childItem->assetName;
+               //If there's already an existing image map file on the material definition in this slot, don't override it
+               if (!path.isEmpty())
+                  existingMat->writeField(mapFieldName.c_str(), path.c_str());
+
+               String targetAsset = targetModuleId + ":" + childItem->assetName;
+
+               existingMat->writeField(assetFieldName.c_str(), targetAsset.c_str());
+            }
 
-         existingMat->writeField(assetFieldName.c_str(), targetAsset.c_str());
+            persistMgr->setDirty(existingMat);
+         }
+         else
+         {
+            Con::errorf("ImageAssetValidator not found!");
+         }
+      }
+      else
+      {
+         dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Failed to find original material definition %s!", assetName);
+         activityLog.push_back(importLogBuffer);
+         return tamlPath;
       }
-      existingMat->save(scriptPath.c_str());
    }
-   //However, if we didn't find any existing material, then we'll want to go ahead and just write out a new one
    else if (file->openForWrite(scriptPath.c_str()))
    {
       file->writeLine((U8*)"//--- OBJECT WRITE BEGIN ---");
@@ -2936,7 +3053,7 @@ Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem)
    newAsset->setShapeFile(shapeFileName.c_str());
    newAsset->setShapeConstructorFile(constructorFileName.c_str());
 
-   AssetImportConfig* cachedConfig = new AssetImportConfig();;
+   AssetImportConfig* cachedConfig = new AssetImportConfig();
    cachedConfig->registerObject();
    activeImportConfig->CopyTo(cachedConfig);
 
@@ -2968,7 +3085,7 @@ Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem)
          dSprintf(dependencyFieldName, 64, "materialSlot%i", dependencySlotId);
 
          char dependencyFieldDef[512];
-         dSprintf(dependencyFieldDef, 512, "@Asset=%s:%s", targetModuleId.c_str(), childItem->assetName.c_str());
+         dSprintf(dependencyFieldDef, 512, "%s=%s:%s", ASSET_ID_SIGNATURE, targetModuleId.c_str(), childItem->assetName.c_str());
   
          newAsset->setDataField(StringTable->insert(dependencyFieldName), nullptr, dependencyFieldDef);
 
@@ -2980,7 +3097,7 @@ Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem)
          dSprintf(dependencyFieldName, 64, "animationSequence%i", dependencySlotId);
 
          char dependencyFieldDef[512];
-         dSprintf(dependencyFieldDef, 512, "@Asset=%s:%s", targetModuleId.c_str(), childItem->assetName.c_str());
+         dSprintf(dependencyFieldDef, 512, "%s=%s:%s", ASSET_ID_SIGNATURE, targetModuleId.c_str(), childItem->assetName.c_str());
 
          newAsset->setDataField(StringTable->insert(dependencyFieldName), nullptr, dependencyFieldDef);
 
@@ -3010,20 +3127,48 @@ Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem)
          return "";
       }
 
-      if (!isInPlace && Platform::isFile(qualifiedFromCSFile))
+      if (!isInPlace)
       {
-         if(!dPathCopy(qualifiedFromCSFile, qualifiedToCSFile, !isReimport))
+         if (Platform::isFile(qualifiedFromCSFile))
          {
-            dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Unable to copy file %s", qualifiedFromCSFile);
-            activityLog.push_back(importLogBuffer);
+            if (!dPathCopy(qualifiedFromCSFile, qualifiedToCSFile, !isReimport))
+            {
+               dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Unable to copy file %s", qualifiedFromCSFile);
+               activityLog.push_back(importLogBuffer);
+            }
+            else
+            {
+               //We successfully copied the original constructor file, so no extra work required
+               makeNewConstructor = false;
+               dSprintf(importLogBuffer, sizeof(importLogBuffer), "Successfully copied original TSShape Constructor file %s", qualifiedFromCSFile);
+               activityLog.push_back(importLogBuffer);
+            }
          }
-         else
+      }
+      else
+      {
+         //We're doing an in-place import, so double check we've already got a constructor file in the expected spot
+         if (Platform::isFile(qualifiedFromCSFile))
          {
-            //We successfully copied the original constructor file, so no extra work required
+            //Yup, found it, we're good to go
             makeNewConstructor = false;
-            dSprintf(importLogBuffer, sizeof(importLogBuffer), "Successfully copied original TSShape Constructor file %s", qualifiedFromCSFile);
+            dSprintf(importLogBuffer, sizeof(importLogBuffer), "Existing TSShape Constructor file %s found", qualifiedFromCSFile);
             activityLog.push_back(importLogBuffer);
          }
+         else
+         {
+            //Didn't work, but it's possible it's using the old .cs extension when our extension variable is set to something else, so check that one as well just to be sure
+            Torque::Path constrFilePath = qualifiedFromCSFile;
+            constrFilePath.setExtension("cs");
+
+            if (Platform::isFile(constrFilePath.getFullPath().c_str()))
+            {
+               //Yup, found it, we're good to go
+               makeNewConstructor = false;
+               dSprintf(importLogBuffer, sizeof(importLogBuffer), "Existing TSShape Constructor file %s found", constrFilePath.getFullPath().c_str());
+               activityLog.push_back(importLogBuffer);
+            }
+         }
       }
    }
 
@@ -3033,10 +3178,10 @@ Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem)
       activityLog.push_back(importLogBuffer);
 
       //find/create shape constructor
-      TSShapeConstructor* constructor = TSShapeConstructor::findShapeConstructor(Torque::Path(qualifiedToFile).getFullPath());
+      TSShapeConstructor* constructor = TSShapeConstructor::findShapeConstructorByFilename(Torque::Path(qualifiedToFile).getFullPath());
       if (constructor == nullptr)
       {
-         constructor = new TSShapeConstructor(qualifiedToFile);
+         constructor = new TSShapeConstructor(StringTable->insert(qualifiedToFile));
 
          String constructorName = assetItem->filePath.getFileName() + assetItem->filePath.getExtension().substr(0, 3);
          constructorName.replace(" ", "_");
@@ -3046,7 +3191,6 @@ Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem)
          constructor->registerObject(constructorName.c_str());
       }
 
-
       //now we write the import config logic into the constructor itself to ensure we load like we wanted it to
       String neverImportMats;
 

+ 15 - 2
Engine/source/T3D/assets/assetImporter.h

@@ -22,7 +22,7 @@ public:
    /// <summary>
    /// Duplicate Asset Auto-Resolution Action. Options are None, AutoPrune, AutoRename
    /// </summary>
-   String DuplicatAutoResolution;
+   String DuplicateAutoResolution;
 
    /// <summary>
    /// Indicates if warnings should be treated as errors.
@@ -40,6 +40,11 @@ public:
    bool AutomaticallyPromptMissingFiles;
    //
 
+   /// <summary>
+   /// Should the importer add the folder name as a prefix to the assetName. Helps prevent name collisions.
+   /// </summary>
+   bool AddDirectoryPrefixToAssetName;
+   //
    //
    //Mesh Settings
    /// <summary>
@@ -531,6 +536,13 @@ public:
    /// </summary>
    GuiTreeViewCtrl* shapeInfo;
 
+   //
+   /// <summary>
+   /// A string that can hold a hint string to help the auto-import ensure the correct asset subtype is assigned.
+   /// e.g. "GUI" would inform an image asset being imported that it should be flagged as a GUI image type
+   /// </summary>
+   String typeHint;
+
 public:
    AssetImportObject();
    virtual ~AssetImportObject();
@@ -820,9 +832,10 @@ public:
    /// <summary>
    /// Runs the import process on a single file in-place. Intended primarily for autoimporting a loose file that's in the game directory.
    /// <para>@param filePath, The filePath of the file to be imported in as an asset</para>
+   /// <para>@param typeHint, Optional. A string that provides a hint of the intended asset type. Such as an image being intended for GUI use.</para>
    /// <para>@return AssetId of the asset that was imported. If import failed, it will be empty.</para>
    /// </summary>
-   StringTableEntry autoImportFile(Torque::Path filePath);
+   StringTableEntry autoImportFile(Torque::Path filePath, String typeHint);
 
    /// <summary>
    /// Runs the import process in the current session

+ 2 - 2
Engine/source/T3D/assets/assetImporter_ScriptBinding.h

@@ -47,11 +47,11 @@ DefineEngineMethod(AssetImporter, getActivityLogLine, String, (S32 i), (0),
    return object->getActivityLogLine(0);
 }
 
-DefineEngineMethod(AssetImporter, autoImportFile, String, (String path), (""),
+DefineEngineMethod(AssetImporter, autoImportFile, String, (String path, String typeHint), ("", ""),
    "Creates a new script asset using the targetFilePath.\n"
    "@return The bool result of calling exec")
 {
-   return object->autoImportFile(path);
+   return object->autoImportFile(path, typeHint);
 }
 
 DefineEngineMethod(AssetImporter, addImportingFile, AssetImportObject*, (String path), (""),

+ 2 - 2
Engine/source/T3D/assets/stateMachineAsset.cpp

@@ -195,8 +195,8 @@ GuiControl* GuiInspectorTypeStateMachineAssetPtr::constructEditControl()
    dSprintf(szBuffer, sizeof(szBuffer), "StateMachineEditor.loadStateMachineAsset(%d.getText()); Canvas.pushDialog(StateMachineEditor);", retCtrl->getId());
    mSMEdButton->setField("Command", szBuffer);
 
-   char bitmapName[512] = "tools/worldEditor/images/toolbar/shape-editor";
-   mSMEdButton->setBitmap(bitmapName);
+   char bitmapName[512] = "ToolsModule:shape_editor_n_image";
+   mSMEdButton->setBitmap(StringTable->insert(bitmapName));
 
    mSMEdButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
    mSMEdButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");

+ 33 - 20
Engine/source/T3D/convexShape.cpp

@@ -264,7 +264,7 @@ bool ConvexShape::protectedSetSurfaceTexture(void *object, const char *index, co
 
    surfaceMaterial surface;
 
-   surface.materialName = data;
+   surface._setMaterial(data);
 
    shape->mSurfaceTextures.push_back(surface);
 
@@ -272,7 +272,7 @@ bool ConvexShape::protectedSetSurfaceTexture(void *object, const char *index, co
 }
 
 ConvexShape::ConvexShape()
- : mMaterialName( "Grid512_OrangeLines_Mat" ),
+   :
    mMaterialInst( NULL ),
    //mVertCount( 0 ),
    //mPrimCount( 0 ),
@@ -289,6 +289,8 @@ ConvexShape::ConvexShape()
    mSurfaceBuffers.clear();
    mSurfaceUVs.clear();
    mSurfaceTextures.clear();
+
+   INIT_MATERIALASSET(Material);
 }
 
 ConvexShape::~ConvexShape()
@@ -310,7 +312,7 @@ void ConvexShape::initPersistFields()
 {
    addGroup( "Rendering" );
 
-      addField( "material", TypeMaterialName, Offset( mMaterialName, ConvexShape ), "Material used to render the ConvexShape surface." );
+      INITPERSISTFIELD_MATERIALASSET(Material, ConvexShape, "Default material used to render the ConvexShape surface.");
 
    endGroup( "Rendering" );
 
@@ -461,9 +463,7 @@ void ConvexShape::writeFields( Stream &stream, U32 tabStop )
       char buffer[1024];
       dMemset(buffer, 0, 1024);
 
-      const char* tex = mSurfaceTextures[i].materialName.c_str();
-
-      dSprintf(buffer, 1024, "surfaceTexture = \"%s\";", mSurfaceTextures[i].materialName.c_str());
+      dSprintf(buffer, 1024, "surfaceTexture = \"%s\";", mSurfaceTextures[i].getMaterial());
 
       stream.writeLine((const U8*)buffer);
    }
@@ -528,7 +528,7 @@ U32 ConvexShape::packUpdate( NetConnection *conn, U32 mask, BitStream *stream )
 
    if ( stream->writeFlag( mask & UpdateMask ) )
    {
-      stream->write( mMaterialName );
+      PACK_MATERIALASSET(conn, Material);
       
       U32 surfCount = mSurfaces.size();
       stream->writeInt( surfCount, 32 );
@@ -556,8 +556,13 @@ U32 ConvexShape::packUpdate( NetConnection *conn, U32 mask, BitStream *stream )
 	  //next check for any texture coord or scale mods
 	  for(U32 i=0; i < surfaceTex; i++)
      {
-        String a = mSurfaceTextures[i].materialName;
-			stream->write( mSurfaceTextures[i].materialName );
+         if (stream->writeFlag(mSurfaceTextures[i].mMaterialAsset.notNull()))
+         {
+            NetStringHandle assetIdStr = mSurfaceTextures[i].mMaterialAsset.getAssetId();
+            conn->packNetStringHandleU(stream, assetIdStr);
+         }
+         else
+            stream->writeString(mSurfaceTextures[i].mMaterialName);
 	  }
    }
 
@@ -579,7 +584,7 @@ void ConvexShape::unpackUpdate( NetConnection *conn, BitStream *stream )
 
    if ( stream->readFlag() ) // UpdateMask
    {
-      stream->read( &mMaterialName );      
+      UNPACK_MATERIALASSET(conn, Material);
 
       mSurfaces.clear();
       mSurfaceUVs.clear();
@@ -619,7 +624,13 @@ void ConvexShape::unpackUpdate( NetConnection *conn, BitStream *stream )
      {
         mSurfaceTextures.increment();
 
-		  stream->read( &mSurfaceTextures[i].materialName );
+        if (stream->readFlag())
+        {
+           mSurfaceTextures[i].mMaterialAssetId = StringTable->insert(conn->unpackNetStringHandleU(stream).getString());
+           mSurfaceTextures[i]._setMaterial(mSurfaceTextures[i].mMaterialAssetId);
+         }
+         else
+           mSurfaceTextures[i].mMaterialName = stream->readSTString();
 	  }
 
      if (isProperlyAdded())
@@ -1207,13 +1218,13 @@ void ConvexShape::_updateMaterial()
    for (U32 i = 0; i<mSurfaceTextures.size(); i++)
    {
       //If we already have the material inst and it hasn't changed, skip
-      if (mSurfaceTextures[i].materialInst && mSurfaceTextures[i].materialName.equal(mSurfaceTextures[i].materialInst->getMaterial()->getName(), String::NoCase))
+      if (mSurfaceTextures[i].materialInst &&
+         mSurfaceTextures[i].getMaterialAsset()->getMaterialDefinitionName() == mSurfaceTextures[i].materialInst->getMaterial()->getName())
          continue;
 
-      Material *material;
+      Material* material = mSurfaceTextures[i].getMaterialResource();
 
-      if (!Sim::findObject(mSurfaceTextures[i].materialName, material))
-         //bail
+      if (material == nullptr)
          continue;
 
       mSurfaceTextures[i].materialInst = material->createMatInstance();
@@ -1229,15 +1240,15 @@ void ConvexShape::_updateMaterial()
    }
 
    // If the material name matches then don't bother updating it.
-   if (mMaterialInst && mMaterialName.equal(mMaterialInst->getMaterial()->getName(), String::NoCase))
+   if (mMaterialInst && getMaterialAsset()->getMaterialDefinitionName() == mMaterialInst->getMaterial()->getName())
       return;
 
    SAFE_DELETE( mMaterialInst );
 
-   Material *material;
-   
-   if ( !Sim::findObject( mMaterialName, material ) )
-      Sim::findObject( "WarningMaterial", material );
+   Material* material = getMaterialResource();
+
+   if (material == nullptr)
+      return;
 
    mMaterialInst = material->createMatInstance();
 
@@ -2159,3 +2170,5 @@ void ConvexShape::Geometry::generate(const Vector< PlaneF > &planes, const Vecto
       faces.push_back( newFace );
    }
 }
+
+DEF_MATERIALASSET_BINDS(ConvexShape, Material);

+ 9 - 4
Engine/source/T3D/convexShape.h

@@ -36,6 +36,8 @@
 #include "collision/convex.h"
 #endif
 
+#include "T3D/assets/MaterialAsset.h"
+
 class ConvexShape;
 
 // Crap name, but whatcha gonna do.
@@ -134,14 +136,17 @@ public:
    struct surfaceMaterial
    {
       // The name of the Material we will use for rendering
-      String            materialName;
+      DECLARE_MATERIALASSET(surfaceMaterial, Material);
+      
+      DECLARE_MATERIALASSET_SETGET(surfaceMaterial, Material);
 
       // The actual Material instance
       BaseMatInstance*  materialInst;
 
       surfaceMaterial()
       {
-         materialName = "";
+         INIT_MATERIALASSET(Material);
+
          materialInst = NULL;
       }
    };
@@ -258,8 +263,8 @@ protected:
   
 protected:
    
-   // The name of the Material we will use for rendering
-   String            mMaterialName;
+   DECLARE_MATERIALASSET(ConvexShape, Material);
+   DECLARE_MATERIALASSET_SETGET(ConvexShape, Material);
 
    // The actual Material instance
    BaseMatInstance*  mMaterialInst;

+ 23 - 24
Engine/source/T3D/debris.cpp

@@ -109,13 +109,14 @@ DebrisData::DebrisData()
    minSpinSpeed = 0.0f;
    maxSpinSpeed = 0.0f;
    textureName = NULL;
-   shapeName = NULL;
    fade = true;
    useRadiusMass = false;
    baseRadius = 1.0f;
    gravModifier = 1.0f;
    terminalVelocity = 0.0f;
    ignoreWater = true;
+
+   INIT_SHAPEASSET(Shape);
 }
 
 //#define TRACK_DEBRIS_DATA_CLONES
@@ -150,8 +151,9 @@ DebrisData::DebrisData(const DebrisData& other, bool temp_clone) : GameBaseData(
    gravModifier = other.gravModifier;
    terminalVelocity = other.terminalVelocity;
    ignoreWater = other.ignoreWater;
-   shapeName = other.shapeName;
-   shape = other.shape; // -- TSShape loaded using shapeName
+
+   CLONE_SHAPEASSET(Shape);
+
    textureName = other.textureName;
    explosionId = other.explosionId; // -- for pack/unpack of explosion ptr
    explosion = other.explosion;
@@ -189,12 +191,7 @@ DebrisData* DebrisData::cloneAndPerformSubstitutions(const SimObject* owner, S32
 
 void DebrisData::onPerformSubstitutions() 
 { 
-   if( shapeName && shapeName[0] != '\0')
-   {
-      shape = ResourceManager::get().load(shapeName);
-      if( bool(shape) == false )
-         Con::errorf("DebrisData::onPerformSubstitutions(): failed to load shape \"%s\"", shapeName);
-   }
+   _setShape(getShape());
 }
 
 bool DebrisData::onAdd()
@@ -277,20 +274,20 @@ bool DebrisData::preload(bool server, String &errorStr)
 
    if( server ) return true;
 
-   if( shapeName && shapeName[0] != '\0' && !bool(shape) )
+   if (mShapeAsset.notNull())
    {
-      shape = ResourceManager::get().load(shapeName);
-      if( bool(shape) == false )
+      if (!mShape)
       {
-         errorStr = String::ToString("DebrisData::load: Couldn't load shape \"%s\"", shapeName);
+         errorStr = String::ToString("DebrisData::load: Couldn't load shape \"%s\"", mShapeAssetId);
          return false;
       }
       else
       {
-         TSShapeInstance* pDummy = new TSShapeInstance(shape, !server);
+         TSShapeInstance* pDummy = new TSShapeInstance(mShape, !server);
          delete pDummy;
+         if (!server && !mShape->preloadMaterialList(mShape.getPath()) && NetConnection::filesWereDownloaded())
+            return false;
       }
-
    }
 
    return true;
@@ -300,9 +297,9 @@ void DebrisData::initPersistFields()
 {
    addGroup("Display");
    addField("texture",              TypeString,                  Offset(textureName,         DebrisData), 
-      "@brief Texture imagemap to use for this debris object.\n\nNot used any more.\n");
-   addField("shapeFile",            TypeShapeFilename,           Offset(shapeName,           DebrisData), 
-      "@brief Object model to use for this debris object.\n\nThis shape is optional.  You could have Debris made up of only particles.\n");
+      "@brief Texture imagemap to use for this debris object.\n\nNot used any more.\n", AbstractClassRep::FIELD_HideInInspectors);
+
+   INITPERSISTFIELD_SHAPEASSET(Shape, DebrisData, "Shape to use for this debris object.");
    endGroup("Display");
 
    addGroup("Datablocks");
@@ -384,7 +381,8 @@ void DebrisData::packData(BitStream* stream)
    stream->write(ignoreWater);
 
    stream->writeString( textureName );
-   stream->writeString( shapeName );
+
+   PACKDATA_SHAPEASSET(Shape);
 
    for( S32 i=0; i<DDC_NUM_EMITTERS; i++ )
    {
@@ -427,7 +425,8 @@ void DebrisData::unpackData(BitStream* stream)
    stream->read(&ignoreWater);
 
    textureName = stream->readSTString();
-   shapeName   = stream->readSTString();
+
+   UNPACKDATA_SHAPEASSET(Shape);
 
    for( S32 i=0; i<DDC_NUM_EMITTERS; i++ )
    {
@@ -669,18 +668,18 @@ bool Debris::onAdd()
    mFriction = mDataBlock->friction;
 
    // Setup our bounding box
-   if( mDataBlock->shape )
+   if( mDataBlock->mShape )
    {
-      mObjBox = mDataBlock->shape->mBounds;
+      mObjBox = mDataBlock->mShape->mBounds;
    }
    else
    {
       mObjBox = Box3F(Point3F(-1, -1, -1), Point3F(1, 1, 1));
    }
 
-   if( mDataBlock->shape )
+   if( mDataBlock->mShape)
    {
-      mShape = new TSShapeInstance( mDataBlock->shape, true);
+      mShape = new TSShapeInstance( mDataBlock->mShape, true);
    }
 
    if( mPart )

+ 6 - 2
Engine/source/T3D/debris.h

@@ -35,6 +35,8 @@
 #include "T3D/gameBase/gameBase.h"
 #endif
 
+#include "T3D/assets/ShapeAsset.h"
+
 class ParticleEmitterData;
 class ParticleEmitter;
 class ExplosionData;
@@ -81,8 +83,8 @@ struct DebrisData : public GameBaseData
    F32      terminalVelocity;    // max velocity magnitude
    bool     ignoreWater;
 
-   const char* shapeName;
-   Resource<TSShape> shape;
+   DECLARE_SHAPEASSET(DebrisData, Shape, onShapeChanged);
+   DECLARE_SHAPEASSET_SETGET(DebrisData, Shape);
 
    StringTableEntry  textureName;
 
@@ -108,6 +110,8 @@ public:
    DebrisData*  cloneAndPerformSubstitutions(const SimObject*, S32 index=0);
    virtual void onPerformSubstitutions();
    virtual bool allowSubstitutions() const { return true; }
+
+   void onShapeChanged() {}
 };
 
 //**************************************************************************

+ 22 - 27
Engine/source/T3D/decal/decalData.cpp

@@ -76,7 +76,7 @@ ConsoleDocClass( DecalData,
 DecalData::DecalData()
 {
    size = 5;
-   materialName = "";
+   INIT_MATERIALASSET(Material);
 
    lifeSpan = 5000;
    fadeTime = 1000;
@@ -89,7 +89,6 @@ DecalData::DecalData()
    fadeStartPixelSize = -1.0f;
    fadeEndPixelSize = 200.0f;
 
-   material = NULL;
    matInst = NULL;
 
    renderPriority = 10;
@@ -144,8 +143,7 @@ void DecalData::initPersistFields()
       addField( "size", TypeF32, Offset( size, DecalData ), 
          "Width and height of the decal in meters before scale is applied." );
 
-      addField( "material", TypeMaterialName, Offset( materialName, DecalData ),
-         "Material to use for this decal." );
+      INITPERSISTFIELD_MATERIALASSET(Material, DecalData, "Material to use for this decal.");
 
       addField( "lifeSpan", TypeS32, Offset( lifeSpan, DecalData ),
          "Time (in milliseconds) before this decal will be automatically deleted." );
@@ -226,7 +224,7 @@ void DecalData::onStaticModified( const char *slotName, const char *newValue )
    // To allow changing materials live.
    if ( dStricmp( slotName, "material" ) == 0 )
    {
-      materialName = newValue;
+      _setMaterial(newValue);
       _updateMaterial();
    }
    // To allow changing name live.
@@ -259,7 +257,9 @@ void DecalData::packData( BitStream *stream )
 
    stream->write( lookupName );
    stream->write( size );
-   stream->write( materialName );
+
+   PACKDATA_MATERIALASSET(Material);
+
    stream->write( lifeSpan );
    stream->write( fadeTime );
 	stream->write( texCoordCount );
@@ -285,8 +285,10 @@ void DecalData::unpackData( BitStream *stream )
 
    stream->read( &lookupName );
    assignName(lookupName);
-   stream->read( &size );  
-   stream->read( &materialName );
+   stream->read( &size );
+
+   UNPACKDATA_MATERIALASSET(Material);
+   
    _updateMaterial();
    stream->read( &lifeSpan );
    stream->read( &fadeTime );
@@ -311,8 +313,10 @@ void DecalData::_initMaterial()
 {
    SAFE_DELETE( matInst );
 
-   if ( material )
-      matInst = material->createMatInstance();
+   if (mMaterialAsset.notNull())
+   {
+      matInst = getMaterialResource()->createMatInstance();
+   }
    else
       matInst = MATMGR->createMatInstance( "WarningMaterial" );
 
@@ -324,7 +328,7 @@ void DecalData::_initMaterial()
    matInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat<DecalVertex>() );
    if( !matInst->isValid() )
    {
-      Con::errorf( "DecalData::_initMaterial - failed to create material instance for '%s'", materialName.c_str() );
+      Con::errorf( "DecalData::_initMaterial - failed to create material instance for '%s'", mMaterialAssetId );
       SAFE_DELETE( matInst );
       matInst = MATMGR->createMatInstance( "WarningMaterial" );
       matInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat< DecalVertex >() );
@@ -333,38 +337,29 @@ void DecalData::_initMaterial()
 
 void DecalData::_updateMaterial()
 {
-   if ( materialName.isEmpty() )
+   if(mMaterialAsset.isNull())
       return;
 
-   Material *pMat = NULL;
-   if ( !Sim::findObject( materialName, pMat ) )
-   {
-      Con::printf( "DecalData::unpackUpdate, failed to find Material of name %s!", materialName.c_str() );
-      return;
-   }
-
-   material = pMat;
-
    // Only update material instance if we have one allocated.
    if ( matInst )
       _initMaterial();
 }
 
-Material* DecalData::getMaterial()
+Material* DecalData::getMaterialDefinition()
 {
-   if ( !material )
+   if ( !getMaterialResource() )
    {
       _updateMaterial();
-      if ( !material )
-         material = static_cast<Material*>( Sim::findObject("WarningMaterial") );
+      if ( !mMaterial )
+         mMaterial = static_cast<Material*>( Sim::findObject("WarningMaterial") );
    }
 
-   return material;
+   return mMaterial;
 }
 
 BaseMatInstance* DecalData::getMaterialInstance()
 {
-   if ( !material || !matInst || matInst->getMaterial() != material )
+   if ( !mMaterial || !matInst || matInst->getMaterial() != mMaterial)
       _initMaterial();
 
    return matInst;

+ 5 - 6
Engine/source/T3D/decal/decalData.h

@@ -36,6 +36,8 @@
 #include "console/dynamicTypes.h"
 #endif
 
+#include "T3D/assets/MaterialAsset.h"
+
 GFXDeclareVertexFormat( DecalVertex )
 {
    // .xyz = coords
@@ -75,11 +77,8 @@ class DecalData : public SimDataBlock
       F32 fadeStartPixelSize;
       F32 fadeEndPixelSize;
 
-      /// Name of material to use.
-      String materialName;
-      
-      /// Render material for decal.
-      SimObjectPtr<Material> material;
+      DECLARE_MATERIALASSET(DecalData, Material);
+      DECLARE_MATERIALASSET_SETGET(DecalData, Material);
       
       /// Material instance for decal.
       BaseMatInstance *matInst;
@@ -113,7 +112,7 @@ class DecalData : public SimDataBlock
       virtual void packData( BitStream* );
       virtual void unpackData( BitStream* );      
       
-      Material* getMaterial();
+      Material* getMaterialDefinition();
       BaseMatInstance* getMaterialInstance();
 
       static SimSet* getSet();

+ 2 - 2
Engine/source/T3D/decal/decalDataFile.cpp

@@ -206,8 +206,8 @@ bool DecalDataFile::read( Stream &stream )
 				data->lookupName = name;
 				data->registerObject(name);
 				Sim::getRootGroup()->addObject( data );
-				data->materialName = "WarningMaterial";
-				data->material = dynamic_cast<Material*>(Sim::findObject("WarningMaterial"));
+				data->mMaterialName = "WarningMaterial";
+				data->mMaterial = dynamic_cast<Material*>(Sim::findObject("WarningMaterial"));
 			
 				Con::errorf( "DecalDataFile::read() - DecalData %s does not exist! Temporarily created %s_missing.", lookupName.c_str(), lookupName.c_str());
 			}

+ 2 - 2
Engine/source/T3D/decal/decalManager.cpp

@@ -186,7 +186,7 @@ S32 QSORT_CALLBACK cmpDecalRenderOrder( const void *p1, const void *p2 )
 
       if ( (*pd2)->mFlags & SaveDecal )
       {
-         S32 id = ( (*pd1)->mDataBlock->getMaterial()->getId() - (*pd2)->mDataBlock->getMaterial()->getId() );      
+         S32 id = ( (*pd1)->mDataBlock->getMaterialDefinition()->getId() - (*pd2)->mDataBlock->getMaterialDefinition()->getId() );
          if ( id != 0 )
             return id;
 
@@ -1225,7 +1225,7 @@ void DecalManager::prepRenderImage( SceneRenderState* state )
    {
       DecalInstance *decal = mDecalQueue[i];      
       DecalData *data = decal->mDataBlock;
-      Material *mat = data->getMaterial();
+      Material *mat = data->getMaterialDefinition();
 
       if ( currentBatch == NULL )
       {

+ 4 - 8
Engine/source/T3D/examples/renderMeshExample.cpp

@@ -59,11 +59,7 @@ RenderMeshExample::RenderMeshExample()
    // Set it as a "static" object that casts shadows
    mTypeMask |= StaticObjectType | StaticShapeObjectType;
 
-   // Make sure we the Material instance to NULL
-   // so we don't try to access it incorrectly
-   mMaterialInst = NULL;
-
-   initMaterialAsset(Material);
+   INIT_MATERIALASSET(Material);
 }
 
 RenderMeshExample::~RenderMeshExample()
@@ -78,7 +74,7 @@ RenderMeshExample::~RenderMeshExample()
 void RenderMeshExample::initPersistFields()
 {
    addGroup( "Rendering" );
-   scriptBindMaterialAsset(Material, RenderMeshExample, "The material used to render the mesh.");
+   INITPERSISTFIELD_MATERIALASSET(Material, RenderMeshExample, "The material used to render the mesh.");
    endGroup( "Rendering" );
 
    // SceneObject already handles exposing the transform
@@ -147,7 +143,7 @@ U32 RenderMeshExample::packUpdate( NetConnection *conn, U32 mask, BitStream *str
    // Write out any of the updated editable properties
    if (stream->writeFlag(mask & UpdateMask))
    {
-      packMaterialAsset(conn, Material);
+      PACK_MATERIALASSET(conn, Material);
    }
 
    return retMask;
@@ -168,7 +164,7 @@ void RenderMeshExample::unpackUpdate(NetConnection *conn, BitStream *stream)
 
    if ( stream->readFlag() )  // UpdateMask
    {
-      unpackMaterialAsset(conn, Material);
+      UNPACK_MATERIALASSET(conn, Material);
 
       if ( isProperlyAdded() )
          updateMaterial();

+ 7 - 7
Engine/source/T3D/examples/renderMeshExample.h

@@ -64,17 +64,17 @@ class RenderMeshExample : public SceneObject
       NextFreeMask  = Parent::NextFreeMask << 2
    };
 
+   // Define our vertex format here so we don't have to
+   // change it in multiple spots later
+   typedef GFXVertexPNT VertexType;
+
    //--------------------------------------------------------------------------
    // Rendering variables
    //--------------------------------------------------------------------------
-   DECLARE_NET_MATERIALASSET(RenderMeshExample, Material, UpdateMask);
+   BaseMatInstance* mMaterialInst;
 
-   // The actual Material instance
-   BaseMatInstance*  mMaterialInst;
-
-   // Define our vertex format here so we don't have to
-   // change it in multiple spots later
-   typedef GFXVertexPNT VertexType;
+   DECLARE_MATERIALASSET(RenderMeshExample, Material);
+   DECLARE_MATERIALASSET_NET_SETGET(RenderMeshExample, Material, UpdateMask);
 
    // The GFX vertex and primitive buffers
    GFXVertexBufferHandle< VertexType > mVertexBuffer;

+ 5 - 17
Engine/source/T3D/examples/renderShapeExample.cpp

@@ -72,8 +72,7 @@ RenderShapeExample::~RenderShapeExample()
 void RenderShapeExample::initPersistFields()
 {
    addGroup( "Rendering" );
-   addField( "shapeFile",      TypeStringFilename, Offset( mShapeFile, RenderShapeExample ),
-      "The path to the DTS shape file." );
+   INITPERSISTFIELD_SHAPEASSET(Shape, RenderShapeExample, "The path to the shape file.")
    endGroup( "Rendering" );
 
    // SceneObject already handles exposing the transform
@@ -146,7 +145,7 @@ U32 RenderShapeExample::packUpdate( NetConnection *conn, U32 mask, BitStream *st
    // Write out any of the updated editable properties
    if ( stream->writeFlag( mask & UpdateMask ) )
    {
-      stream->write( mShapeFile );
+      PACK_SHAPEASSET(conn, Shape);
 
       // Allow the server object a chance to handle a new shape
       createShape();
@@ -170,7 +169,7 @@ void RenderShapeExample::unpackUpdate(NetConnection *conn, BitStream *stream)
 
    if ( stream->readFlag() )  // UpdateMask
    {
-      stream->read( &mShapeFile );
+      UNPACK_SHAPEASSET(conn, Shape);
 
       if ( isProperlyAdded() )
          createShape();
@@ -182,33 +181,22 @@ void RenderShapeExample::unpackUpdate(NetConnection *conn, BitStream *stream)
 //-----------------------------------------------------------------------------
 void RenderShapeExample::createShape()
 {
-   if ( mShapeFile.isEmpty() )
+   if ( getShape() == StringTable->EmptyString() )
       return;
 
    // If this is the same shape then no reason to update it
-   if ( mShapeInstance && mShapeFile.equal( mShape.getPath().getFullPath(), String::NoCase ) )
+   if ( mShapeInstance && getShape() == StringTable->insert(mShape.getPath().getFullPath().c_str()) )
       return;
 
    // Clean up our previous shape
    if ( mShapeInstance )
       SAFE_DELETE( mShapeInstance );
-   mShape = NULL;
-
-   // Attempt to get the resource from the ResourceManager
-   mShape = ResourceManager::get().load( mShapeFile );
-
-   if ( !mShape )
-   {
-      Con::errorf( "RenderShapeExample::createShape() - Unable to load shape: %s", mShapeFile.c_str() );
-      return;
-   }
 
    // Attempt to preload the Materials for this shape
    if ( isClientObject() && 
         !mShape->preloadMaterialList( mShape.getPath() ) && 
         NetConnection::filesWereDownloaded() )
    {
-      mShape = NULL;
       return;
    }
 

+ 7 - 4
Engine/source/T3D/examples/renderShapeExample.h

@@ -30,6 +30,8 @@
 #include "ts/tsShapeInstance.h"
 #endif
 
+#include "T3D/assets/ShapeAsset.h"
+
 //-----------------------------------------------------------------------------
 // This class implements a basic SceneObject that can exist in the world at a
 // 3D position and render itself. There are several valid ways to render an
@@ -59,12 +61,13 @@ class RenderShapeExample : public SceneObject
    //--------------------------------------------------------------------------
    // Rendering variables
    //--------------------------------------------------------------------------
-   // The name of the shape file we will use for rendering
-   String            mShapeFile;
+   DECLARE_SHAPEASSET(RenderShapeExample, Shape, onShapeChanged);
+   DECLARE_SHAPEASSET_SETGET(RenderShapeExample, Shape);
+
    // The actual shape instance
    TSShapeInstance*  mShapeInstance;
-   // Store the resource so we can access the filename later
-   Resource<TSShape> mShape;
+
+   void onShapeChanged() {}
 
 public:
    RenderShapeExample();

+ 14 - 22
Engine/source/T3D/fx/explosion.cpp

@@ -225,7 +225,6 @@ ConsoleDocClass( ExplosionData,
 
 ExplosionData::ExplosionData()
 {
-   dtsFileName  = NULL;
    particleDensity = 10;
    particleRadius = 1.0f;
 
@@ -238,7 +237,8 @@ ExplosionData::ExplosionData()
    explosionScale.set(1.0f, 1.0f, 1.0f);
    playSpeed = 1.0f;
 
-   explosionShape = NULL;
+   INIT_SHAPEASSET(ExplosionShape);
+
    explosionAnimation = -1;
 
    dMemset( emitterList, 0, sizeof( emitterList ) );
@@ -305,7 +305,6 @@ ExplosionData::ExplosionData(const ExplosionData& other, bool temp_clone) : Game
       Con::errorf("ExplosionData -- Clones are on the loose!");
 #endif
 
-   dtsFileName = other.dtsFileName;
    faceViewer = other.faceViewer;
    particleDensity = other.particleDensity;
    particleRadius = other.particleRadius;
@@ -314,7 +313,7 @@ ExplosionData::ExplosionData(const ExplosionData& other, bool temp_clone) : Game
    particleEmitterId = other.particleEmitterId; // -- for pack/unpack of particleEmitter ptr 
    explosionScale = other.explosionScale;
    playSpeed = other.playSpeed;
-   explosionShape = other.explosionShape; // -- TSShape loaded using dtsFileName
+   CLONE_SHAPEASSET(ExplosionShape);
    explosionAnimation = other.explosionAnimation; // -- from explosionShape sequence "ambient"
    dMemcpy( emitterList, other.emitterList, sizeof( emitterList ) );
    dMemcpy( emitterIDList, other.emitterIDList, sizeof( emitterIDList ) ); // -- for pack/unpack of emitterList ptrs
@@ -392,10 +391,9 @@ ExplosionData* ExplosionData::cloneAndPerformSubstitutions(const SimObject* owne
 
 void ExplosionData::initPersistFields()
 {
-   addField( "explosionShape", TypeShapeFilename, Offset(dtsFileName, ExplosionData),
-      "@brief Optional DTS or DAE shape to place at the center of the explosion.\n\n"
-      "The <i>ambient</i> animation of this model will be played automatically at "
-      "the start of the explosion." );
+   INITPERSISTFIELD_SHAPEASSET(ExplosionShape, ExplosionData, "@brief Optional shape asset to place at the center of the explosion.\n\n"
+      "The <i>ambient</i> animation of this model will be played automatically at the start of the explosion.");
+
    addField( "explosionScale", TypePoint3F, Offset(explosionScale, ExplosionData),
       "\"X Y Z\" scale factor applied to the explosionShape model at the start "
       "of the explosion." );
@@ -656,7 +654,7 @@ void ExplosionData::packData(BitStream* stream)
 {
    Parent::packData(stream);
 
-   stream->writeString(dtsFileName);
+   PACKDATA_SHAPEASSET(ExplosionShape);
 
    sfxWrite( stream, soundProfile );
    if (stream->writeFlag(particleEmitter))
@@ -759,7 +757,7 @@ void ExplosionData::unpackData(BitStream* stream)
 {
 	Parent::unpackData(stream);
 
-   dtsFileName = stream->readSTString();
+   UNPACKDATA_SHAPEASSET(ExplosionShape);
 
    sfxRead( stream, &soundProfile );
 
@@ -874,22 +872,16 @@ bool ExplosionData::preload(bool server, String &errorStr)
             Con::errorf(ConsoleLogEntry::General, "Error, unable to load particle emitter for explosion datablock");
    }
 
-   if (dtsFileName && dtsFileName[0]) {
-      explosionShape = ResourceManager::get().load(dtsFileName);
-      if (!bool(explosionShape)) {
-         errorStr = String::ToString("ExplosionData: Couldn't load shape \"%s\"", dtsFileName);
-         return false;
-      }
+   if (mExplosionShapeAsset.notNull()) {
 
       // Resolve animations
-      explosionAnimation = explosionShape->findSequence("ambient");
+      explosionAnimation = mExplosionShape->findSequence("ambient");
 
       // Preload textures with a dummy instance...
-      TSShapeInstance* pDummy = new TSShapeInstance(explosionShape, !server);
+      TSShapeInstance* pDummy = new TSShapeInstance(mExplosionShape, !server);
       delete pDummy;
 
    } else {
-      explosionShape     = NULL;
       explosionAnimation = -1;
    }
 
@@ -1377,8 +1369,8 @@ bool Explosion::explode()
    launchDebris( mInitialNormal );
    spawnSubExplosions();
 
-   if (bool(mDataBlock->explosionShape) && mDataBlock->explosionAnimation != -1) {
-      mExplosionInstance = new TSShapeInstance(mDataBlock->explosionShape, true);
+   if (bool(mDataBlock->mExplosionShape) && mDataBlock->explosionAnimation != -1) {
+      mExplosionInstance = new TSShapeInstance(mDataBlock->mExplosionShape, true);
 
       mExplosionThread   = mExplosionInstance->addThread();
       mExplosionInstance->setSequence(mExplosionThread, mDataBlock->explosionAnimation, 0);
@@ -1388,7 +1380,7 @@ bool Explosion::explode()
       mEndingMS = U32(mExplosionInstance->getScaledDuration(mExplosionThread) * 1000.0f);
 
       mObjScale.convolve(mDataBlock->explosionScale);
-      mObjBox = mDataBlock->explosionShape->mBounds;
+      mObjBox = mDataBlock->mExplosionShape->mBounds;
       resetWorldBox();
    }
 

+ 7 - 3
Engine/source/T3D/fx/explosion.h

@@ -41,6 +41,8 @@
 #include "lighting/lightInfo.h"
 #endif
 
+#include "T3D/assets/ShapeAsset.h"
+
 class ParticleEmitter;
 class ParticleEmitterData;
 class TSThread;
@@ -62,8 +64,6 @@ class ExplosionData : public GameBaseData {
    };
 
   public:
-   StringTableEntry dtsFileName;
-
    bool faceViewer;
 
    S32 particleDensity;
@@ -76,7 +76,9 @@ class ExplosionData : public GameBaseData {
    Point3F              explosionScale;
    F32                  playSpeed;
 
-   Resource<TSShape> explosionShape;
+   DECLARE_SHAPEASSET(ExplosionData, ExplosionShape, onShapeChanged);
+   DECLARE_SHAPEASSET_SETGET(ExplosionData, ExplosionShape);
+
    S32               explosionAnimation;
 
    ParticleEmitterData*    emitterList[EC_NUM_EMITTERS];
@@ -137,6 +139,8 @@ public:
    /*D*/          ~ExplosionData();
    ExplosionData* cloneAndPerformSubstitutions(const SimObject*, S32 index=0);
    virtual bool   allowSubstitutions() const { return true; }
+
+   void onShapeChanged() {}
 };
 
 

+ 42 - 63
Engine/source/T3D/fx/groundCover.cpp

@@ -49,6 +49,7 @@
 #include "materials/matInstance.h"
 #include "renderInstance/renderDeferredMgr.h"
 #include "console/engineAPI.h"
+#include "T3D/assets/MaterialAsset.h"
 
 /// This is used for rendering ground cover billboards.
 GFXImplementVertexFormat( GCVertex )
@@ -458,9 +459,9 @@ GroundCover::GroundCover()
 
    mRandomSeed = 1;
 
-   initMaterialAsset(Material);
+   INIT_MATERIALASSET(Material);
+   mMaterialInst = NULL;
 
-   mMatInst = NULL;
    mMatParams = NULL;
    mTypeRectsParam = NULL;
    mFadeParams = NULL;
@@ -519,7 +520,8 @@ GroundCover::GroundCover()
       mBillboardRects[i].point.set( 0.0f, 0.0f );
       mBillboardRects[i].extent.set( 1.0f, 1.0f );
 
-      mShapeFilenames[i] = NULL;
+      INIT_SHAPEASSET_ARRAY(Shape, i);
+
       mShapeInstances[i] = NULL;
 
       mBillboardAspectScales[i] = 1.0f;
@@ -530,7 +532,7 @@ GroundCover::GroundCover()
 
 GroundCover::~GroundCover()
 {
-   SAFE_DELETE( mMatInst );
+   SAFE_DELETE( mMaterialInst );
 }
 
 IMPLEMENT_CO_NETOBJECT_V1(GroundCover);
@@ -539,7 +541,7 @@ void GroundCover::initPersistFields()
 {
    addGroup( "GroundCover General" );
 
-      scriptBindMaterialAsset(Material, GroundCover, "Material used by all GroundCover segments.");
+      INITPERSISTFIELD_MATERIALASSET(Material, GroundCover, "Material used by all GroundCover segments.");
 
       addField( "radius",        TypeF32,          Offset( mRadius, GroundCover ),              "Outer generation radius from the current camera position." );
       addField( "dissolveRadius",TypeF32,          Offset( mFadeRadius, GroundCover ),          "This is less than or equal to radius and defines when fading of cover elements begins." );
@@ -559,7 +561,8 @@ void GroundCover::initPersistFields()
 
          addField( "billboardUVs",  TypeRectUV,    Offset( mBillboardRects, GroundCover ), MAX_COVERTYPES,  "Subset material UV coordinates for this cover billboard." );
 
-         addField( "shapeFilename", TypeFilename,  Offset( mShapeFilenames, GroundCover ), MAX_COVERTYPES,  "The cover shape filename. [Optional]" );
+         addField("shapeFilename", TypeFilename, Offset(mShapeName, GroundCover), MAX_COVERTYPES, "The cover shape filename. [Optional]", AbstractClassRep::FIELD_HideInInspectors);
+         INITPERSISTFIELD_SHAPEASSET_ARRAY(Shape, MAX_COVERTYPES, GroundCover, "The cover shape. [Optional]");
 
          addField( "layer",         TypeTerrainMaterialName, Offset( mLayer, GroundCover ), MAX_COVERTYPES, "Terrain material name to limit coverage to, or blank to not limit." );
 
@@ -710,7 +713,7 @@ U32 GroundCover::packUpdate( NetConnection *connection, U32 mask, BitStream *str
       // TODO: We could probably optimize a few of these
       // based on reasonable units at some point.
 
-      packMaterialAsset(connection, Material);
+      PACK_MATERIALASSET(connection, Material);
 
       stream->write( mRadius );
       stream->write( mZOffset );
@@ -741,11 +744,11 @@ U32 GroundCover::packUpdate( NetConnection *connection, U32 mask, BitStream *str
          
          stream->write( mMinSlope[i] );
          stream->write( mMaxSlope[i] );
-		 stream->writeFlag(mConformToNormal[i]);
-		 stream->write(mMinRotX[i]);
-		 stream->write(mMaxRotX[i]);
-		 stream->write(mMinRotY[i]);
-		 stream->write(mMaxRotY[i]);
+         stream->writeFlag(mConformToNormal[i]);
+         stream->write(mMinRotX[i]);
+         stream->write(mMaxRotX[i]);
+         stream->write(mMinRotY[i]);
+         stream->write(mMaxRotY[i]);
          
          stream->write( mMinElevation[i] );
          stream->write( mMaxElevation[i] );     
@@ -763,7 +766,7 @@ U32 GroundCover::packUpdate( NetConnection *connection, U32 mask, BitStream *str
          stream->write( mBillboardRects[i].extent.x );
          stream->write( mBillboardRects[i].extent.y );
 
-         stream->writeString( mShapeFilenames[i] );
+         PACK_SHAPEASSET_ARRAY(connection, Shape, i);
       }
 
       stream->writeFlag( mDebugRenderCells );
@@ -781,7 +784,7 @@ void GroundCover::unpackUpdate( NetConnection *connection, BitStream *stream )
 
    if (stream->readFlag())
    {
-      unpackMaterialAsset(connection, Material);
+      UNPACK_MATERIALASSET(connection, Material);
 
       stream->read( &mRadius );
       stream->read( &mZOffset );
@@ -812,11 +815,11 @@ void GroundCover::unpackUpdate( NetConnection *connection, BitStream *stream )
 
          stream->read( &mMinSlope[i] );
          stream->read( &mMaxSlope[i] );
-		 mConformToNormal[i] = stream->readFlag();
-		 stream->read(&mMinRotX[i]);
-		 stream->read(&mMaxRotX[i]);
-		 stream->read(&mMinRotY[i]);
-		 stream->read(&mMaxRotY[i]);
+         mConformToNormal[i] = stream->readFlag();
+         stream->read(&mMinRotX[i]);
+         stream->read(&mMaxRotX[i]);
+         stream->read(&mMinRotY[i]);
+         stream->read(&mMaxRotY[i]);
 
          stream->read( &mMinElevation[i] );
          stream->read( &mMaxElevation[i] );     
@@ -834,7 +837,7 @@ void GroundCover::unpackUpdate( NetConnection *connection, BitStream *stream )
          stream->read( &mBillboardRects[i].extent.x );
          stream->read( &mBillboardRects[i].extent.y );
 
-         mShapeFilenames[i] = stream->readSTString();
+         UNPACK_SHAPEASSET_ARRAY(connection, Shape, i);
       }
 
       mDebugRenderCells    = stream->readFlag();
@@ -854,28 +857,12 @@ void GroundCover::unpackUpdate( NetConnection *connection, BitStream *stream )
 
 void GroundCover::_initMaterial()
 {
-   if (mMaterialAsset.notNull())
-   {
-      if (mMatInst && String(mMaterialAsset->getMaterialDefinitionName()).equal(mMatInst->getMaterial()->getName(), String::NoCase))
-         return;
-
-      SAFE_DELETE(mMatInst);
-
-      if (!Sim::findObject(mMaterialAsset->getMaterialDefinitionName(), mMaterial))
-         Con::errorf("GroundCover::_initMaterial - Material %s was not found.", mMaterialAsset->getMaterialDefinitionName());
+   SAFE_DELETE(mMaterialInst);
 
-      if (mMaterial)
-         mMatInst = mMaterial->createMatInstance();
-      else
-         mMatInst = MATMGR->createMatInstance("WarningMaterial");
-
-      if (!mMatInst)
-         Con::errorf("GroundCover::_initMaterial - no Material called '%s'", mMaterialAsset->getMaterialDefinitionName());
-   }
+   if (mMaterialAsset.notNull() && mMaterialAsset->getStatus() == MaterialAsset::Ok)
+      mMaterialInst = mMaterial->createMatInstance();
    else
-   {
-      return;
-   }
+      mMaterialInst = MATMGR->createMatInstance("WarningMaterial");
    
    // Add our special feature that makes it all work...
    FeatureSet features = MATMGR->getDefaultFeatures();
@@ -883,10 +870,10 @@ void GroundCover::_initMaterial()
    
    // Our feature requires a pointer back to this object
    // to properly setup its shader consts.
-   mMatInst->setUserObject( this );
+   mMaterialInst->setUserObject( this );
 
    // DO IT!
-   mMatInst->init( features, getGFXVertexFormat<GCVertex>() );
+   mMaterialInst->init( features, getGFXVertexFormat<GCVertex>() );
 }
 
 void GroundCover::_initShapes()
@@ -895,25 +882,17 @@ void GroundCover::_initShapes()
 
    for ( S32 i=0; i < MAX_COVERTYPES; i++ )
    {
-      if ( !mShapeFilenames[i] || !mShapeFilenames[i][0] )
+      if ( mShapeAsset[i].isNull() || mShape[i] == nullptr)
          continue;
 
-      // Load the shape.
-      Resource<TSShape> shape = ResourceManager::get().load(mShapeFilenames[i]);
-      if ( !(bool)shape )
-      {
-         Con::warnf( "GroundCover::_initShapes() unable to load shape: %s", mShapeFilenames[i] );
-         continue;
-      }
-
-      if ( isClientObject() && !shape->preloadMaterialList(shape.getPath()) && NetConnection::filesWereDownloaded() )
+      if ( isClientObject() && !mShape[i]->preloadMaterialList(mShape[i].getPath()) && NetConnection::filesWereDownloaded() )
       {
-         Con::warnf( "GroundCover::_initShapes() material preload failed for shape: %s", mShapeFilenames[i] );
+         Con::warnf( "GroundCover::_initShapes() material preload failed for shape: %s", mShapeAssetId[i] );
          continue;
       }
 
       // Create the shape instance.
-      mShapeInstances[i] = new TSShapeInstance( shape, isClientObject() );
+      mShapeInstances[i] = new TSShapeInstance(mShape[i], isClientObject() );
    }
 }
 
@@ -982,16 +961,16 @@ void GroundCover::_initialize( U32 cellCount, U32 cellPlacementCount )
 
    // Rebuild the texture aspect scales for each type.
    F32 textureAspect = 1.0f;
-   if( mMatInst && mMatInst->isValid())
+   if( mMaterialInst && mMaterialInst->isValid())
    {
-      Material* mat = dynamic_cast<Material*>(mMatInst->getMaterial());
+      Material* mat = dynamic_cast<Material*>(mMaterialInst->getMaterial());
       if(mat)
       {
          GFXTexHandle tex;
-         if (!mat->mDiffuseMapFilename[0].isEmpty())
-            tex = GFXTexHandle(mat->mDiffuseMapFilename[0], &GFXStaticTextureSRGBProfile, "GroundCover texture aspect ratio check");
-         else if (!mat->mDiffuseMapAsset[0].isNull())
-            tex = mat->mDiffuseMapAsset[0]->getImage(GFXStaticTextureSRGBProfile);
+         if (mat->getDiffuseMapResource(0))
+            tex = mat->getDiffuseMapResource(0);
+         else if (mat->getDiffuseMap(0) != StringTable->EmptyString())
+            tex = GFXTexHandle(mat->getDiffuseMap(0), &GFXStaticTextureSRGBProfile, "GroundCover texture aspect ratio check");
 
          if(tex.isValid())
          {
@@ -1580,7 +1559,7 @@ void GroundCover::_updateCoverGrid( const Frustum &culler )
 void GroundCover::prepRenderImage( SceneRenderState *state )
 {
    // Reset stats each time we hit the diffuse pass.
-   if (mMatInst == nullptr)
+   if (mMaterialInst == nullptr)
       return;
 
    if( state->isDiffusePass() )
@@ -1617,7 +1596,7 @@ void GroundCover::prepRenderImage( SceneRenderState *state )
 
    // Render billboards but not into shadow passes.
 
-   if ( !state->isShadowPass() && mMatInst->isValid() && !mDebugNoBillboards )
+   if ( !state->isShadowPass() && mMaterialInst->isValid() && !mDebugNoBillboards )
    {
       PROFILE_SCOPE( GroundCover_RenderBillboards );
 
@@ -1692,7 +1671,7 @@ void GroundCover::prepRenderImage( SceneRenderState *state )
          if ( mCuller.isCulled( cell->getRenderBounds() ) )
             continue;
 
-         cell->renderBillboards( state, mMatInst, &mPrimBuffer );
+         cell->renderBillboards( state, mMaterialInst, &mPrimBuffer );
       }     
    }
 

+ 7 - 5
Engine/source/T3D/fx/groundCover.h

@@ -45,7 +45,7 @@
 #include "shaderGen/shaderFeature.h"
 #endif
 
-#include "T3D/assets/MaterialAsset.h"
+#include "T3D/assets/ShapeAsset.h"
 
 class TerrainBlock;
 class GroundCoverCell;
@@ -266,9 +266,10 @@ protected:
    static F32 smDensityScale;   
    static F32 smFadeScale;
 
-   DECLARE_NET_MATERIALASSET(GroundCover, Material, InitialUpdateMask);
-   Material* mMaterial;
-   BaseMatInstance *mMatInst;
+   BaseMatInstance* mMaterialInst;
+
+   DECLARE_MATERIALASSET(GroundCover, Material);
+   DECLARE_MATERIALASSET_NET_SETGET(GroundCover, Material, InitialUpdateMask);
 
    GroundCoverShaderConstData mShaderConstData;
 
@@ -339,7 +340,8 @@ protected:
    RectF mBillboardRects[MAX_COVERTYPES];
 
    /// The cover shape filenames.
-   StringTableEntry mShapeFilenames[MAX_COVERTYPES];
+   DECLARE_SHAPEASSET_ARRAY(GroundCover, Shape, MAX_COVERTYPES);
+   DECLARE_SHAPEASSET_ARRAY_NET_SETGET(GroundCover, Shape, -1);
 
    /// The cover shape instances.
    TSShapeInstance* mShapeInstances[MAX_COVERTYPES];

+ 39 - 49
Engine/source/T3D/fx/particle.cpp

@@ -121,10 +121,10 @@ ParticleData::ParticleData()
    animTexTiling.set(0,0);      // tiling dimensions 
    animTexFramesString = NULL;  // string of animation frame indices
    animTexUVs = NULL;           // array of tile vertex UVs
-   textureName = NULL;          // texture filename
-   textureHandle = NULL;        // loaded texture handle
-   textureExtName = NULL;
-   textureExtHandle = NULL;
+
+   INIT_IMAGEASSET(Texture);
+   INIT_IMAGEASSET(TextureExt);
+
    constrain_pos = false;
    start_angle = 0.0f;
    angle_variance = 0.0f;
@@ -203,11 +203,13 @@ void ParticleData::initPersistFields()
       "animTexFrames = \"0-16 20 19 18 17 31-21\";\n"
       "@endtsexample\n" );
 
-   addField( "textureName", TYPEID< StringTableEntry >(), Offset(textureName, ParticleData),
-      "Texture file to use for this particle." );
-   addField( "animTexName", TYPEID< StringTableEntry >(), Offset(textureName, ParticleData),
+   addProtectedField( "textureName", TYPEID< StringTableEntry >(), Offset(mTextureName, ParticleData), _setTextureData, defaultProtectedGetFn,
+      "Texture file to use for this particle.", AbstractClassRep::FIELD_HideInInspectors );
+   addField( "animTexName", TYPEID< StringTableEntry >(), Offset(mTextureName, ParticleData),
       "@brief Texture file to use for this particle if animateTexture is true.\n\n"
-      "Deprecated. Use textureName instead." );
+      "Deprecated. Use textureName instead.", AbstractClassRep::FIELD_HideInInspectors);
+   INITPERSISTFIELD_IMAGEASSET(Texture, ParticleData, "Texture to use for this particle.");
+   
 
    // Interpolation variables
    addField( "colors", TYPEID< LinearColorF >(), Offset(colors, ParticleData), PDC_NUM_KEYS,
@@ -224,8 +226,9 @@ void ParticleData::initPersistFields()
       "@brief Time keys used with the colors and sizes keyframes.\n\n"
       "Values are from 0.0 (particle creation) to 1.0 (end of lifespace)." );
 
-   addGroup("AFX"); 
-   addField("textureExtName",       TypeFilename, Offset(textureExtName,     ParticleData));
+   addGroup("AFX");
+   addProtectedField("textureExtName", TypeFilename, Offset(mTextureExtName,     ParticleData), _setTextureExtData, &defaultProtectedGetFn, "", AbstractClassRep::FIELD_HideInInspectors);
+   INITPERSISTFIELD_IMAGEASSET(TextureExt, ParticleData, "");
    addField("constrainPos",         TypeBool,     Offset(constrain_pos,      ParticleData));
    addField("angle",                TypeF32,      Offset(start_angle,        ParticleData));
    addField("angleVariance",        TypeF32,      Offset(angle_variance,     ParticleData));
@@ -290,8 +293,8 @@ void ParticleData::packData(BitStream* stream)
       stream->writeFloat( times[i], 8);
    }
 
-   if (stream->writeFlag(textureName && textureName[0]))
-     stream->writeString(textureName);
+   //PACKDATA_IMAGEASSET(Texture);
+
    for (i = 0; i < 4; i++)
       mathWrite(*stream, texCoords[i]);
    if (stream->writeFlag(animateTexture))
@@ -303,8 +306,9 @@ void ParticleData::packData(BitStream* stream)
       mathWrite(*stream, animTexTiling);
       stream->writeInt(framesPerSec, 8);
    }
-   if (stream->writeFlag(textureExtName && textureExtName[0]))
-     stream->writeString(textureExtName);
+
+   //PACKDATA_IMAGEASSET(TextureExt);
+
    stream->writeFlag(constrain_pos);
    stream->writeFloat(start_angle/360.0f, 11);
    stream->writeFloat(angle_variance/180.0f, 10);
@@ -373,7 +377,9 @@ void ParticleData::unpackData(BitStream* stream)
       sizes[i] = stream->readFloat(16) * MaxParticleSize;
       times[i] = stream->readFloat(8);
    }
-   textureName = (stream->readFlag()) ? stream->readSTString() : 0;
+
+   //UNPACKDATA_IMAGEASSET(Texture);
+
    for (i = 0; i < 4; i++)
       mathRead(*stream, &texCoords[i]);
    
@@ -384,7 +390,9 @@ void ParticleData::unpackData(BitStream* stream)
      mathRead(*stream, &animTexTiling);
      framesPerSec = stream->readInt(8);
    }
-   textureExtName = (stream->readFlag()) ? stream->readSTString() : 0;
+
+   //UNPACKDATA_IMAGEASSET(Texture);
+
    constrain_pos = stream->readFlag();
    start_angle = 360.0f*stream->readFloat(11);
    angle_variance = 180.0f*stream->readFloat(10);
@@ -556,27 +564,6 @@ bool ParticleData::preload(bool server, String &errorStr)
    bool error = false;
    if(!server)
    {
-      // Here we attempt to load the particle's texture if specified. An undefined
-      // texture is *not* an error since the emitter may provide one.
-      if (textureName && textureName[0])
-      {
-        textureHandle = GFXTexHandle(textureName, &GFXStaticTextureSRGBProfile, avar("%s() - textureHandle (line %d)", __FUNCTION__, __LINE__));
-        if (!textureHandle)
-        {
-          errorStr = String::ToString("Missing particle texture: %s", textureName);
-          error = true;
-        }
-      }
-      if (textureExtName && textureExtName[0])
-      {
-         textureExtHandle = GFXTexHandle(textureExtName, &GFXStaticTextureSRGBProfile, avar("%s() - textureExtHandle (line %d)", __FUNCTION__, __LINE__));
-         if (!textureExtHandle)
-         {
-            errorStr = String::ToString("Missing particle texture: %s", textureName);
-            error = true;
-         }
-      }
-
       if (animateTexture) 
       {
         // Here we parse animTexFramesString into byte-size frame numbers in animTexFrames.
@@ -698,15 +685,14 @@ void ParticleData::initializeParticle(Particle* init, const Point3F& inheritVelo
 bool ParticleData::reload(char errorBuffer[256])
 {
    bool error = false;
-	if (textureName && textureName[0])
+
+   StringTableEntry particleTex = getTexture();
+
+   if (!_setTexture(particleTex))
    {
-        textureHandle = GFXTexHandle(textureName, &GFXStaticTextureSRGBProfile, avar("%s() - textureHandle (line %d)", __FUNCTION__, __LINE__));
-        if (!textureHandle)
-        {
-				dSprintf(errorBuffer, 256, "Missing particle texture: %s", textureName);
-				error = true;
-		  }
-	}
+      dSprintf(errorBuffer, 256, "Missing particle texture: %s", particleTex);
+   }
+
    /*
    numFrames = 0;
    for( S32 i=0; i<PDC_MAX_TEX; i++ )
@@ -776,12 +762,14 @@ ParticleData::ParticleData(const ParticleData& other, bool temp_clone) : SimData
   animTexTiling = other.animTexTiling;
   animTexFramesString = other.animTexFramesString;
   animTexFrames = other.animTexFrames; // -- parsed from animTexFramesString
-  textureName = other.textureName;
-  textureHandle = other.textureHandle;
+
+  CLONE_IMAGEASSET(Texture);
+  
   spinBias = other.spinBias;
   randomizeSpinDir = other.randomizeSpinDir;
-  textureExtName = other.textureExtName;
-  textureExtHandle = other.textureExtHandle;
+
+  CLONE_IMAGEASSET(TextureExt);
+
   constrain_pos = other.constrain_pos;
   start_angle = other.start_angle;
   angle_variance = other.angle_variance;
@@ -815,3 +803,5 @@ void ParticleData::onPerformSubstitutions()
   char errorBuffer[256];
   reload(errorBuffer);
 }
+
+DEF_IMAGEASSET_BINDS(ParticleData, Texture);

+ 13 - 7
Engine/source/T3D/fx/particle.h

@@ -35,6 +35,8 @@
 #include "gfx/gfxTextureHandle.h"
 #endif
 
+#include "T3D/assets/ImageAsset.h"
+
 #define MaxParticleSize 50.0
 
 struct Particle;
@@ -83,13 +85,16 @@ class ParticleData : public SimDataBlock
    Point2I           animTexTiling;
    StringTableEntry  animTexFramesString;
    Vector<U8>        animTexFrames;
-   StringTableEntry  textureName;
-   GFXTexHandle      textureHandle;
 
-   static bool protectedSetSizes( void *object, const char *index, const char *data );
-   static bool protectedSetTimes( void *object, const char *index, const char *data );
+   DECLARE_IMAGEASSET(ParticleData, Texture, onImageChanged, GFXStaticTextureSRGBProfile);
+   DECLARE_IMAGEASSET_SETGET(ParticleData, Texture);
 
-  public:
+   static bool protectedSetSizes(void* object, const char* index, const char* data);
+   static bool protectedSetTimes(void* object, const char* index, const char* data);
+
+   void onImageChanged() {}
+
+public:
    ParticleData();
    ~ParticleData();
 
@@ -111,9 +116,10 @@ class ParticleData : public SimDataBlock
   protected:
    F32   spinBias;
    bool  randomizeSpinDir;
-   StringTableEntry  textureExtName;
   public:
-   GFXTexHandle      textureExtHandle;
+   DECLARE_IMAGEASSET(ParticleData, TextureExt, onImageChanged, GFXStaticTextureSRGBProfile);
+   DECLARE_IMAGEASSET_SETGET(ParticleData, TextureExt);
+
    bool   constrain_pos;
    F32    start_angle;
    F32    angle_variance;

+ 3 - 3
Engine/source/T3D/fx/particleEmitter.cpp

@@ -741,11 +741,11 @@ bool ParticleEmitterData::preload(bool server, String &errorStr)
      // otherwise, check that all particles refer to the same texture
      else if (particleDataBlocks.size() > 1)
      {
-       StringTableEntry txr_name = particleDataBlocks[0]->textureName;
+       StringTableEntry txr_name = particleDataBlocks[0]->getTexture();
        for (S32 i = 1; i < particleDataBlocks.size(); i++)
        {
          // warn if particle textures are inconsistent
-         if (particleDataBlocks[i]->textureName != txr_name)
+         if (particleDataBlocks[i]->getTexture() != txr_name)
          {
            Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) particles reference different textures.", getName());
            break;
@@ -1225,7 +1225,7 @@ void ParticleEmitter::prepRenderImage(SceneRenderState* state)
    if (mDataBlock->textureHandle)
      ri->diffuseTex = &*(mDataBlock->textureHandle);
    else
-     ri->diffuseTex = &*(part_list_head.next->dataBlock->textureHandle);
+     ri->diffuseTex = &*(part_list_head.next->dataBlock->getTextureResource());
 
    ri->softnessDistance = mDataBlock->softnessDistance; 
 

+ 41 - 14
Engine/source/T3D/fx/precipitation.cpp

@@ -129,9 +129,12 @@ PrecipitationData::PrecipitationData()
 {
    soundProfile      = NULL;
 
-   mDropName         = StringTable->EmptyString();
+   INIT_IMAGEASSET(Drop);
+
    mDropShaderName   = StringTable->EmptyString();
-   mSplashName       = StringTable->EmptyString();
+
+   INIT_IMAGEASSET(Splash);
+
    mSplashShaderName = StringTable->EmptyString();
 
    mDropsPerSide     = 4;
@@ -142,18 +145,32 @@ void PrecipitationData::initPersistFields()
 {
    addField( "soundProfile", TYPEID< SFXTrack >(), Offset(soundProfile, PrecipitationData),
       "Looping SFXProfile effect to play while Precipitation is active." );
-   addField( "dropTexture", TypeFilename, Offset(mDropName, PrecipitationData),
+
+   addProtectedField( "dropTexture", TypeFilename, Offset(mDropName, PrecipitationData), &_setDropData, &defaultProtectedGetFn,
       "@brief Texture filename for drop particles.\n\n"
       "The drop texture can contain several different drop sub-textures "
       "arranged in a grid. There must be the same number of rows as columns. A "
-      "random frame will be chosen for each drop." );
+      "random frame will be chosen for each drop.", AbstractClassRep::FIELD_HideInInspectors );
+      
+   INITPERSISTFIELD_IMAGEASSET(Drop, PrecipitationData, "@brief Texture for drop particles.\n\n"
+      "The drop texture can contain several different drop sub-textures "
+      "arranged in a grid. There must be the same number of rows as columns. A "
+      "random frame will be chosen for each drop.");
+
    addField( "dropShader", TypeString, Offset(mDropShaderName, PrecipitationData),
       "The name of the shader used for raindrops." );
-   addField( "splashTexture", TypeFilename, Offset(mSplashName, PrecipitationData),
+      
+   addProtectedField("splashTexture", TypeFilename, Offset(mSplashName, PrecipitationData), &_setSplashData, &defaultProtectedGetFn,
       "@brief Texture filename for splash particles.\n\n"
       "The splash texture can contain several different splash sub-textures "
       "arranged in a grid. There must be the same number of rows as columns. A "
-      "random frame will be chosen for each splash." );
+      "random frame will be chosen for each splash.", AbstractClassRep::FIELD_HideInInspectors);
+
+   INITPERSISTFIELD_IMAGEASSET(Splash, PrecipitationData, "@brief Texture for splash particles.\n\n"
+      "The splash texture can contain several different splash sub-textures "
+      "arranged in a grid. There must be the same number of rows as columns. A "
+      "random frame will be chosen for each splash.");
+
    addField( "splashShader", TypeString, Offset(mSplashShaderName, PrecipitationData),
       "The name of the shader used for splashes." );
    addField( "dropsPerSide", TypeS32, Offset(mDropsPerSide, PrecipitationData),
@@ -185,9 +202,12 @@ void PrecipitationData::packData(BitStream* stream)
 
    sfxWrite( stream, soundProfile );
 
-   stream->writeString(mDropName);
+   PACKDATA_IMAGEASSET(Drop);
+
    stream->writeString(mDropShaderName);
-   stream->writeString(mSplashName);
+
+   PACKDATA_IMAGEASSET(Splash);
+
    stream->writeString(mSplashShaderName);
    stream->write(mDropsPerSide);
    stream->write(mSplashesPerSide);
@@ -199,9 +219,12 @@ void PrecipitationData::unpackData(BitStream* stream)
 
    sfxRead( stream, &soundProfile );
 
-   mDropName = stream->readSTString();
+   UNPACKDATA_IMAGEASSET(Drop);
+
    mDropShaderName = stream->readSTString();
-   mSplashName = stream->readSTString();
+
+   UNPACKDATA_IMAGEASSET(Splash);
+
    mSplashShaderName = stream->readSTString();
    stream->read(&mDropsPerSide);
    stream->read(&mSplashesPerSide);
@@ -604,8 +627,10 @@ void Precipitation::initMaterials()
    mDropShader = NULL;
    mSplashShader = NULL;
 
-   if( dStrlen(pd->mDropName) > 0 && !mDropHandle.set(pd->mDropName, &GFXStaticTextureSRGBProfile, avar("%s() - mDropHandle (line %d)", __FUNCTION__, __LINE__)) )
-      Con::warnf("Precipitation::initMaterials - failed to locate texture '%s'!", pd->mDropName);
+   if(pd->mDrop.isNull())
+      Con::warnf("Precipitation::initMaterials - failed to locate texture '%s'!", pd->getDrop());
+   else
+      mDropHandle = pd->mDrop;
 
    if ( dStrlen(pd->mDropShaderName) > 0 )
    {
@@ -625,8 +650,10 @@ void Precipitation::initMaterials()
       }
    }
 
-   if( dStrlen(pd->mSplashName) > 0 && !mSplashHandle.set(pd->mSplashName, &GFXStaticTextureSRGBProfile, avar("%s() - mSplashHandle (line %d)", __FUNCTION__, __LINE__)) )
-      Con::warnf("Precipitation::initMaterials - failed to locate texture '%s'!", pd->mSplashName);
+   if (pd->mSplash.isNull())
+      Con::warnf("Precipitation::initMaterials - failed to locate texture '%s'!", pd->getSplash());
+   else
+      mSplashHandle = pd->mSplash;
 
    if ( dStrlen(pd->mSplashShaderName) > 0 )
    {

+ 12 - 2
Engine/source/T3D/fx/precipitation.h

@@ -33,6 +33,8 @@
 #include "renderInstance/renderPassManager.h"
 #endif
 
+#include "T3D/assets/ImageAsset.h"
+
 class SFXTrack;
 class SFXSource;
 
@@ -45,9 +47,14 @@ class PrecipitationData : public GameBaseData
   public:
    SFXTrack*     soundProfile;
 
-   StringTableEntry mDropName;         ///< Texture filename for drop particles
+   DECLARE_IMAGEASSET(PrecipitationData, Drop, onDropChanged, GFXStaticTextureSRGBProfile); ///< Texture for drop particles
+   DECLARE_IMAGEASSET_SETGET(PrecipitationData, Drop);
+
    StringTableEntry mDropShaderName;   ///< The name of the shader used for raindrops
-   StringTableEntry mSplashName;       ///< Texture filename for splash particles
+
+   DECLARE_IMAGEASSET(PrecipitationData, Splash, onSplashChanged, GFXStaticTextureSRGBProfile); ///< Texture for splash particles
+   DECLARE_IMAGEASSET_SETGET(PrecipitationData, Splash);
+
    StringTableEntry mSplashShaderName; ///< The name of the shader used for raindrops
 
    S32  mDropsPerSide;     ///< How many drops are on a side of the raindrop texture.
@@ -59,6 +66,9 @@ class PrecipitationData : public GameBaseData
    static void  initPersistFields();
    virtual void packData(BitStream* stream);
    virtual void unpackData(BitStream* stream);
+
+   void onDropChanged() {}
+   void onSplashChanged() {}
 };
 
 struct Raindrop

+ 10 - 7
Engine/source/T3D/fx/splash.cpp

@@ -93,9 +93,10 @@ SplashData::SplashData()
    explosion = NULL;
    explosionId = 0;
 
-   dMemset( textureName, 0, sizeof( textureName ) );
-
    U32 i;
+   for (i = 0; i < NUM_TEX; i++)
+      INIT_IMAGEASSET_ARRAY(Texture, i);
+
    for( i=0; i<NUM_TIME_KEYS; i++ )
       times[i] = 1.0;
 
@@ -125,7 +126,9 @@ SplashData::SplashData()
    addField("acceleration",      TypeF32,                      Offset(acceleration,       SplashData), "Constant acceleration value to place upon the splash effect.\n");
    addField("times",             TypeF32,                      Offset(times,              SplashData), NUM_TIME_KEYS, "Times to transition through the splash effect. Up to 4 allowed. Values are 0.0 - 1.0, and corrispond to the life of the particle where 0 is first created and 1 is end of lifespace.\n" );
    addField("colors",            TypeColorF,                   Offset(colors,             SplashData), NUM_TIME_KEYS, "Color values to set the splash effect, rgba. Up to 4 allowed. Will transition through colors based on values set in the times value. Example: colors[0] = \"0.6 1.0 1.0 0.5\".\n" );
-   addField("texture",           TypeFilename,                 Offset(textureName,        SplashData), NUM_TEX, "Imagemap file to use as the texture for the splash effect.\n");
+
+   INITPERSISTFIELD_IMAGEASSET_ARRAY(Texture, NUM_TEX, SplashData, "Image to use as the texture for the splash effect.\n");
+
    addField("texWrap",           TypeF32,                      Offset(texWrap,            SplashData), "Amount to wrap the texture around the splash ring, 0.0f - 1.0f.\n");
    addField("texFactor",         TypeF32,                      Offset(texFactor,          SplashData), "Factor in which to apply the texture to the splash ring, 0.0f - 1.0f.\n");
    addField("ejectionFreq",      TypeF32,                      Offset(ejectionFreq,       SplashData), "Frequency in which to emit splash rings.\n");
@@ -198,7 +201,7 @@ void SplashData::packData(BitStream* stream)
 
    for( i=0; i<NUM_TEX; i++ )
    {
-      stream->writeString(textureName[i]);
+      PACKDATA_IMAGEASSET_ARRAY(Texture, i);
    }
 }
 
@@ -252,7 +255,7 @@ void SplashData::unpackData(BitStream* stream)
 
    for( i=0; i<NUM_TEX; i++ )
    {
-      textureName[i] = stream->readSTString();
+      UNPACKDATA_IMAGEASSET_ARRAY(Texture, i);
    }
 }
 
@@ -280,9 +283,9 @@ bool SplashData::preload(bool server, String &errorStr)
 
       for( i=0; i<NUM_TEX; i++ )
       {
-         if (textureName[i] && textureName[i][0])
+         if (mTexture[i].isNull())
          {
-            textureHandle[i] = GFXTexHandle(textureName[i], &GFXStaticTextureSRGBProfile, avar("%s() - textureHandle[%d] (line %d)", __FUNCTION__, i, __LINE__) );
+            _setTexture(getTexture(i), i);
          }
       }
    }

+ 4 - 2
Engine/source/T3D/fx/splash.h

@@ -33,6 +33,8 @@
 
 #include "gfx/gfxTextureHandle.h"
 
+#include "T3D/assets/ImageAsset.h"
+
 class ParticleEmitter;
 class ParticleEmitterData;
 class AudioProfile;
@@ -116,8 +118,8 @@ public:
    F32               times[ NUM_TIME_KEYS ];
    LinearColorF            colors[ NUM_TIME_KEYS ];
 
-   StringTableEntry  textureName[NUM_TEX];
-   GFXTexHandle      textureHandle[NUM_TEX];
+   DECLARE_IMAGEASSET_ARRAY(SplashData, Texture, GFXStaticTextureSRGBProfile, NUM_TEX);
+   DECLARE_IMAGEASSET_ARRAY_SETGET(SplashData, Texture)
 
    ExplosionData*    explosion;
    S32               explosionId;

+ 1 - 1
Engine/source/T3D/gameBase/gameConnection.cpp

@@ -371,7 +371,6 @@ void GameConnection::onConnectionEstablished(bool isInitiator)
       setTranslatesStrings(true);
       Sim::getClientGroup()->addObject(this);
       mMoveList->init();
-
       const char *argv[MaxConnectArgs + 2];
       argv[0] = "onConnect";
       argv[1] = NULL; // Filled in later
@@ -646,6 +645,7 @@ void GameConnection::setCameraObject(GameBase *obj)
          smFovUpdate.trigger(fov);
       }
    }
+
 }
 
 GameBase* GameConnection::getCameraObject()

+ 1 - 1
Engine/source/T3D/gameBase/gameConnection.h

@@ -403,7 +403,7 @@ protected:
 public:   
    void          setRolloverObj(SceneObject*);   
    SceneObject*  getRolloverObj() { return  mRolloverObj; }   
-   void          setSelectedObj(SceneObject*, bool propagate_to_client=false);   
+   void          setSelectedObj(SceneObject*, bool propagate_to_client=false);
    SceneObject*  getSelectedObj() { return  mSelectedObj; }  
    void          setPreSelectedObjFromRollover();
    void          clearPreSelectedObj();

+ 22 - 14
Engine/source/T3D/groundPlane.cpp

@@ -76,6 +76,7 @@ GroundPlane::GroundPlane()
      mScaleU( 1.0f ),
      mScaleV( 1.0f ),
      mMaterial( NULL ),
+     mMaterialInst(NULL),
      mPhysicsRep( NULL ),
      mMin( 0.0f, 0.0f ),
      mMax( 0.0f, 0.0f )
@@ -86,13 +87,15 @@ GroundPlane::GroundPlane()
    mConvexList = new Convex;
    mTypeMask |= TerrainLikeObjectType;
 
-   initMaterialAsset(Material);
+   INIT_MATERIALASSET(Material);
 }
 
 GroundPlane::~GroundPlane()
 {
-   if( mMaterial )
-      SAFE_DELETE( mMaterial );
+   mMaterial = nullptr;
+
+   if(mMaterialInst)
+      SAFE_DELETE(mMaterialInst);
 
    mConvexList->nukeList();
    SAFE_DELETE( mConvexList );
@@ -106,7 +109,7 @@ void GroundPlane::initPersistFields()
       addField( "scaleU",        TypeF32,          Offset( mScaleU, GroundPlane ), "Scale of texture repeat in the U direction." );
       addField( "scaleV",        TypeF32,          Offset( mScaleV, GroundPlane ), "Scale of texture repeat in the V direction." );
 
-      scriptBindMaterialAsset(Material, GroundPlane, "The material used to render the ground plane.");
+      INITPERSISTFIELD_MATERIALASSET(Material, GroundPlane, "The material used to render the ground plane.");
 
    endGroup( "Plane" );
    
@@ -153,6 +156,11 @@ bool GroundPlane::onAdd()
 
 void GroundPlane::onRemove()
 {
+   if (!mMaterialAsset.isNull())
+      AssetDatabase.releaseAsset(mMaterialAsset.getAssetId());
+
+   //SAFE_DELETE(mMaterialInst);
+
    SAFE_DELETE( mPhysicsRep );
 
    removeFromScene();
@@ -191,7 +199,7 @@ U32 GroundPlane::packUpdate( NetConnection* connection, U32 mask, BitStream* str
    stream->write( mScaleU );
    stream->write( mScaleV );
 
-   packMaterialAsset(connection, Material);
+   PACK_MATERIALASSET(connection, Material);
 
    return retMask;
 }
@@ -204,7 +212,7 @@ void GroundPlane::unpackUpdate( NetConnection* connection, BitStream* stream )
    stream->read( &mScaleU );
    stream->read( &mScaleV );
 
-   unpackMaterialAsset(connection, Material);
+   UNPACK_MATERIALASSET(connection, Material);
 
    // If we're added then something possibly changed in 
    // the editor... do an update of the material and the
@@ -220,14 +228,14 @@ void GroundPlane::_updateMaterial()
 {
    if (mMaterialAsset.notNull())
    {
-      if (mMaterial && String(mMaterialAsset->getMaterialDefinitionName()).equal(mMaterial->getMaterial()->getName(), String::NoCase))
+      if (mMaterialInst && String(mMaterialAsset->getMaterialDefinitionName()).equal(mMaterialInst->getMaterial()->getName(), String::NoCase))
          return;
 
-      SAFE_DELETE(mMaterial);
+      SAFE_DELETE(mMaterialInst);
 
-      mMaterial = MATMGR->createMatInstance(mMaterialAsset->getMaterialDefinitionName(), getGFXVertexFormat< VertexType >());
+      mMaterialInst = MATMGR->createMatInstance(mMaterialAsset->getMaterialDefinitionName(), getGFXVertexFormat< VertexType >());
 
-      if (!mMaterial)
+      if (!mMaterialInst)
          Con::errorf("GroundPlane::_updateMaterial - no Material called '%s'", mMaterialAsset->getMaterialDefinitionName());
    }
 }
@@ -242,7 +250,7 @@ bool GroundPlane::castRay( const Point3F& start, const Point3F& end, RayInfo* in
       info->t = t;
       info->setContactPoint( start, end );
       info->normal.set( 0, 0, 1 );
-      info->material = mMaterial;
+      info->material = mMaterialInst;
       info->object = this;
       info->distance = 0;
       info->faceDot = 0;
@@ -336,7 +344,7 @@ bool GroundPlane::buildPolyList( PolyListContext context, AbstractPolyList* poly
    }
 
    Box3F planeBox = getPlaneBox();
-   polyList->addBox( planeBox, mMaterial );
+   polyList->addBox( planeBox, mMaterialInst );
 
    return true;
 }
@@ -353,7 +361,7 @@ void GroundPlane::prepRenderImage( SceneRenderState* state )
 
    // If we don't have a material instance after the override then 
    // we can skip rendering all together.
-   BaseMatInstance *matInst = state->getOverrideMaterial( mMaterial );
+   BaseMatInstance *matInst = state->getOverrideMaterial(mMaterialInst);
    if ( !matInst )
       return;
 
@@ -584,7 +592,7 @@ void GroundPlane::generateGrid( U32 width, U32 height, F32 squareSize,
 
 void GroundPlane::getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList)
 {
-   if (!mMaterialAsset.isNull() && mMaterialAsset->getAssetId() != StringTable->insert("Core_Rendering:noMaterial"))
+   if (!mMaterialAsset.isNull() && mMaterialAsset->getAssetId() != MaterialAsset::smNoMaterialAssetFallback)
       usedAssetsList->push_back_unique(mMaterialAsset->getAssetId());
 
 }

+ 4 - 2
Engine/source/T3D/groundPlane.h

@@ -104,9 +104,11 @@ private:
    F32               mSquareSize;   ///< World units per grid cell edge.
    F32               mScaleU;       ///< Scale factor for U texture coordinates.
    F32               mScaleV;       ///< Scale factor for V texture coordinates.
-   BaseMatInstance*  mMaterial;     ///< Instantiated material based on given material name.
 
-   DECLARE_NET_MATERIALASSET(GroundPlane, Material, -1);
+   BaseMatInstance* mMaterialInst;
+
+   DECLARE_MATERIALASSET(GroundPlane, Material);
+   DECLARE_MATERIALASSET_NET_SETGET(GroundPlane, Material, -1);
 
    PhysicsBody *mPhysicsRep;
 

+ 4 - 4
Engine/source/T3D/guiObjectView.cpp

@@ -634,7 +634,7 @@ void GuiObjectView::_initAnimation()
       {
          Con::errorf( "GuiObjectView::_initAnimation - Cannot find animation sequence '%s' on '%s'",
             mAnimationSeqName.c_str(),
-            mModelName.c_str()
+            mModelName
          );
          
          return;
@@ -649,7 +649,7 @@ void GuiObjectView::_initAnimation()
       {
          Con::errorf( "GuiObjectView::_initAnimation - Sequence '%i' out of range for model '%s'",
             mAnimationSeq,
-            mModelName.c_str()
+            mModelName
          );
          
          mAnimationSeq = -1;
@@ -685,7 +685,7 @@ void GuiObjectView::_initMount()
       {
          Con::errorf( "GuiObjectView::_initMount - No node '%s' on '%s'",
             mMountNodeName.c_str(),
-            mModelName.c_str()
+            mModelName
          );
          
          return;
@@ -698,7 +698,7 @@ void GuiObjectView::_initMount()
    {
       Con::errorf( "GuiObjectView::_initMount - Mount node index '%i' out of range for '%s'",
          mMountNode,
-         mModelName.c_str()
+         mModelName
       );
       
       mMountNode = -1;

+ 3 - 1
Engine/source/T3D/guiObjectView.h

@@ -30,6 +30,8 @@
    #include "ts/tsShapeInstance.h"
 #endif
 
+#include "T3D/assets/ShapeAsset.h"
+
 
 class LightInfo;
 
@@ -68,7 +70,7 @@ class GuiObjectView : public GuiTSCtrl
       /// @{
       
       /// Name of the model loaded for display.
-      String mModelName;
+      StringTableEntry mModelName;
 
       /// Model being displayed in the view.
       TSShapeInstance* mModel;

+ 15 - 14
Engine/source/T3D/levelInfo.cpp

@@ -98,8 +98,8 @@ LevelInfo::LevelInfo()
    mNetFlags.set( ScopeAlways | Ghostable );
 
    mAdvancedLightmapSupport = true;
-   mAccuTextureName = "";
-   mAccuTexture = NULL;
+
+   INIT_IMAGEASSET(AccuTexture);
 
    // Register with the light manager activation signal, and we need to do it first
    // so the advanced light bin manager can be instructed about MRT lightmaps
@@ -166,8 +166,7 @@ void LevelInfo::initPersistFields()
       //addField( "advancedLightmapSupport", TypeBool, Offset( mAdvancedLightmapSupport, LevelInfo ),
       //   "Enable expanded support for mixing static and dynamic lighting (more costly)" );
 
-      addProtectedField("AccuTexture", TypeStringFilename, Offset(mAccuTextureName, LevelInfo),
-         &_setLevelAccuTexture, &defaultProtectedGetFn, "Accumulation texture.");
+      INITPERSISTFIELD_IMAGEASSET(AccuTexture, LevelInfo, "Accumulation texture.");
 
    endGroup( "Lighting" );
    
@@ -216,7 +215,8 @@ U32 LevelInfo::packUpdate(NetConnection *conn, U32 mask, BitStream *stream)
    sfxWrite( stream, mSoundAmbience );
    stream->writeInt( mSoundDistanceModel, 1 );
 
-   stream->write(mAccuTextureName);
+   PACK_IMAGEASSET(conn, AccuTexture);
+
    return retMask;
 }
 
@@ -261,8 +261,9 @@ void LevelInfo::unpackUpdate(NetConnection *conn, BitStream *stream)
 
       SFX->setDistanceModel( mSoundDistanceModel );
    }
-   stream->read(&mAccuTextureName);
-   setLevelAccuTexture(mAccuTextureName);
+
+   UNPACK_IMAGEASSET(conn, AccuTexture);
+   setLevelAccuTexture(getAccuTexture());
 }
 
 //-----------------------------------------------------------------------------
@@ -361,21 +362,21 @@ void LevelInfo::_onLMActivate(const char *lm, bool enable)
 bool LevelInfo::_setLevelAccuTexture(void *object, const char *index, const char *data)
 {
    LevelInfo* volume = reinterpret_cast< LevelInfo* >(object);
-   volume->setLevelAccuTexture(data);
+   volume->setLevelAccuTexture(StringTable->insert(data));
    return false;
 }
 
 
-void LevelInfo::setLevelAccuTexture(const String& name)
+void LevelInfo::setLevelAccuTexture(StringTableEntry name)
 {
-   mAccuTextureName = name;
-   if (isClientObject() && mAccuTextureName.isNotEmpty())
+   _setAccuTexture(name);
+
+   if (isClientObject() && getAccuTexture() != StringTable->EmptyString())
    {
-      mAccuTexture.set(mAccuTextureName, &GFXStaticTextureSRGBProfile, "AccumulationVolume::mAccuTexture");
       if (mAccuTexture.isNull())
-         Con::warnf("AccumulationVolume::setTexture - Unable to load texture: %s", mAccuTextureName.c_str());
+         Con::warnf("AccumulationVolume::setTexture - Unable to load texture: %s", getAccuTexture());
       else
          gLevelAccuMap = mAccuTexture;
    }
    AccumulationVolume::refreshVolumes();
-}
+}

+ 9 - 6
Engine/source/T3D/levelInfo.h

@@ -40,6 +40,8 @@
 #include "gfx/gfxTextureHandle.h"
 #endif
 
+#include "T3D/assets/ImageAsset.h"
+
 class SFXAmbience;
 class SFXSoundscape;
 
@@ -101,8 +103,11 @@ class LevelInfo : public NetObject
 
       void _onLMActivate(const char *lm, bool enable);
    protected:
-      // Name (path) of the accumulation texture.
-      String mAccuTextureName;
+
+      DECLARE_IMAGEASSET(LevelInfo, AccuTexture, onAccuTextureChanged, GFXStaticTextureSRGBProfile);
+      DECLARE_IMAGEASSET_SETGET(LevelInfo, AccuTexture);
+
+      void onAccuTextureChanged() {}
 
    public:
 
@@ -137,13 +142,11 @@ class LevelInfo : public NetObject
          UpdateMask = BIT(0)
       };
 
-      GFXTexHandle mAccuTexture;
-
       virtual U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream );
       virtual void unpackUpdate( NetConnection *conn, BitStream *stream );
       static bool _setLevelAccuTexture(void *object, const char *index, const char *data);
-      void setLevelAccuTexture(const String& name);
+      void setLevelAccuTexture(StringTableEntry name);
       /// @}
 };
 
-#endif // _LEVELINFO_H_
+#endif // _LEVELINFO_H_

+ 10 - 11
Engine/source/T3D/lightFlareData.cpp

@@ -131,7 +131,9 @@ LightFlareData::LightFlareData()
    dMemset( mElementUseLightColor, 0, sizeof( bool ) * MAX_ELEMENTS );   
 
    for ( U32 i = 0; i < MAX_ELEMENTS; i++ )   
-      mElementDist[i] = -1.0f;   
+      mElementDist[i] = -1.0f;
+
+   INIT_IMAGEASSET(FlareTexture);
 }
 
 LightFlareData::~LightFlareData()
@@ -158,8 +160,7 @@ void LightFlareData::initPersistFields()
       addField( "flareEnabled", TypeBool, Offset( mFlareEnabled, LightFlareData ),
          "Allows the user to disable this flare globally for any lights referencing it." );
 
-      addField( "flareTexture", TypeImageFilename, Offset( mFlareTextureName, LightFlareData ),
-         "The texture / sprite sheet for this flare." );
+      INITPERSISTFIELD_IMAGEASSET(FlareTexture, LightFlareData, "The texture / sprite sheet for this flare.");
 
       addArray( "Elements", MAX_ELEMENTS );
 
@@ -217,7 +218,9 @@ void LightFlareData::packData( BitStream *stream )
    Parent::packData( stream );
 
    stream->writeFlag( mFlareEnabled );
-   stream->write( mFlareTextureName );   
+
+   PACKDATA_IMAGEASSET(FlareTexture);
+
    stream->write( mScale );
    stream->write( mOcclusionRadius );
    stream->writeFlag( mRenderReflectPass );
@@ -240,7 +243,9 @@ void LightFlareData::unpackData( BitStream *stream )
    Parent::unpackData( stream );
 
    mFlareEnabled = stream->readFlag();
-   stream->read( &mFlareTextureName );   
+
+   UNPACKDATA_IMAGEASSET(FlareTexture);
+
    stream->read( &mScale );
    stream->read( &mOcclusionRadius );
    mRenderReflectPass = stream->readFlag();
@@ -631,12 +636,6 @@ bool LightFlareData::_preload( bool server, String &errorStr )
    if ( mElementCount > 0 )
       _makePrimBuffer( &mFlarePrimBuffer, mElementCount );
 
-   if ( !server )
-   {
-      if ( mFlareTextureName.isNotEmpty() )      
-         mFlareTexture.set( mFlareTextureName, &GFXStaticTextureSRGBProfile, "FlareTexture" );
-   }
-
    return true;
 }
 

+ 9 - 3
Engine/source/T3D/lightFlareData.h

@@ -45,6 +45,8 @@
 #include "gfx/gfxOcclusionQuery.h"
 #endif
 
+#include "T3D/assets/ImageAsset.h"
+
 class LightInfo;
 struct ObjectRenderInst;
 class SceneRenderState;
@@ -104,6 +106,8 @@ protected:
    void _makePrimBuffer( GFXPrimitiveBufferHandle *pb, U32 count );
    void _renderCorona( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat );
 
+   void onImageChanged() {}
+
 protected:
    
    static const U32 LosMask;
@@ -115,8 +119,10 @@ protected:
 
    F32 mScale;
    bool mFlareEnabled;
-   String mFlareTextureName;
-   GFXTexHandle mFlareTexture;
+
+   DECLARE_IMAGEASSET(LightFlareData, FlareTexture, onImageChanged, GFXStaticTextureSRGBProfile);
+   DECLARE_IMAGEASSET_SETGET(LightFlareData, FlareTexture);
+
    F32 mOcclusionRadius;
    bool mRenderReflectPass;
 
@@ -133,4 +139,4 @@ protected:
    GFXPrimitiveBufferHandle mFlarePrimBuffer;   
 };
 
-#endif // _LIGHTFLAREDATA_H_
+#endif // _LIGHTFLAREDATA_H_

+ 7 - 6
Engine/source/T3D/lighting/reflectionProbe.cpp

@@ -111,6 +111,7 @@ ReflectionProbe::ReflectionProbe()
 
    mUseHDRCaptures = true;
 
+   mCubemapName = StringTable->EmptyString();
    mStaticCubemap = NULL;
    mProbeUniqueID = "";
 
@@ -259,7 +260,7 @@ bool ReflectionProbe::_setReflectionMode(void *object, const char *index, const
    {
       //Clear our cubemap if we changed it to be baked, just for cleanliness
       probe->mReflectionModeType = BakedCubemap;
-      probe->mCubemapName = "";
+      probe->mCubemapName = StringTable->EmptyString();
    }
 
    probe->setMaskBits(StaticDataMask);
@@ -426,7 +427,7 @@ U32 ReflectionProbe::packUpdate(NetConnection *conn, U32 mask, BitStream *stream
       stream->write(mRadius);
       stream->write(mProbeUniqueID);
       stream->write((U32)mReflectionModeType);
-      stream->write(mCubemapName);
+      stream->writeString(mCubemapName);
    }
 
    if (stream->writeFlag(mask & EnabledMask))
@@ -474,8 +475,8 @@ void ReflectionProbe::unpackUpdate(NetConnection *conn, BitStream *stream)
       stream->read(&reflectModeType);
       mReflectionModeType = (ReflectionModeType)reflectModeType;
 
-      String oldCubemapName = mCubemapName;
-      stream->read(&mCubemapName);
+      StringTableEntry oldCubemapName = mCubemapName;
+      mCubemapName = stream->readSTString();
 
       if(oldReflectModeType != mReflectionModeType || oldCubemapName != mCubemapName)
          mCubemapDirty = true;
@@ -630,7 +631,7 @@ void ReflectionProbe::processStaticCubemap()
    String path = Con::getVariable("$pref::ReflectionProbes::CurrentLevelPath", "levels/");
 
    char irradFileName[256];
-   dSprintf(irradFileName, 256, "%s%s_Irradiance.dds", path.c_str(), mCubemapName.c_str());
+   dSprintf(irradFileName, 256, "%s%s_Irradiance.dds", path.c_str(), mCubemapName);
 
    if (Platform::isFile(irradFileName))
    {
@@ -645,7 +646,7 @@ void ReflectionProbe::processStaticCubemap()
    }
 
    char prefilterFileName[256];
-   dSprintf(prefilterFileName, 256, "%s%s_Prefilter.dds", path.c_str(), mCubemapName.c_str());
+   dSprintf(prefilterFileName, 256, "%s%s_Prefilter.dds", path.c_str(), mCubemapName);
 
    if (Platform::isFile(prefilterFileName))
    {

+ 1 - 1
Engine/source/T3D/lighting/reflectionProbe.h

@@ -162,7 +162,7 @@ protected:
    /// <summary>
    /// This is used when a static cubemap is used. The name of the cubemap is looked up and loaded for the IBL calculations
    /// </summary>
-   String mCubemapName;
+   StringTableEntry mCubemapName;
    CubemapData *mStaticCubemap;
    GFXCubemapHandle  mDynamicCubemap;
 

+ 24 - 22
Engine/source/T3D/physics/physicsDebris.cpp

@@ -73,7 +73,8 @@ PhysicsDebrisData::PhysicsDebrisData()
 {
    lifetime = 5.0f;
    lifetimeVariance = 0.0f;
-   shapeName = NULL;
+
+   INIT_SHAPEASSET(Shape);
 }
 
 bool PhysicsDebrisData::onAdd()
@@ -91,21 +92,17 @@ bool PhysicsDebrisData::preload( bool server, String &errorStr )
 
    if ( server ) return true;
 
-   if ( shapeName && shapeName[0] != '\0' && !bool(shape) )
+   if ( mShapeAsset.notNull() )
    {
-      shape = ResourceManager::get().load( shapeName );
-      if ( bool(shape) == false )
-      {
-         errorStr = String::ToString( "PhysicsDebrisData::load: Couldn't load shape \"%s\"", shapeName );
-         return false;
-      }
-      else
-      {
-         // Create a dummy shape to force the generation of shaders and materials
-         // during the level load and not during gameplay.
-         TSShapeInstance *pDummy = new TSShapeInstance( shape, !server );
-         delete pDummy;
-      }
+      // Create a dummy shape to force the generation of shaders and materials
+      // during the level load and not during gameplay.
+      TSShapeInstance *pDummy = new TSShapeInstance( mShape, !server );
+      delete pDummy;
+   }
+   else
+   {
+      errorStr = String::ToString("PhysicsDebrisData::load: Couldn't load shape asset \"%s\"", mShapeAssetId);
+      return false;
    }
 
    return true;
@@ -115,8 +112,11 @@ void PhysicsDebrisData::initPersistFields()
 {
    addGroup( "Display" );
 
-      addField( "shapeFile", TypeShapeFilename, Offset( shapeName, PhysicsDebrisData ),
+      addProtectedField( "shapeFile", TypeShapeFilename, Offset( mShapeName, PhysicsDebrisData ), &_setShapeData, &defaultProtectedGetFn,
          "@brief Path to the .DAE or .DTS file to use for this shape.\n\n"
+         "Compatable with Live-Asset Reloading.", AbstractClassRep::FIELD_HideInInspectors);
+         
+      INITPERSISTFIELD_SHAPEASSET(Shape, PhysicsDebrisData, "@brief Shape to use with this debris.\n\n"
          "Compatable with Live-Asset Reloading.");
 
       addField( "castShadows", TypeBool, Offset( castShadows, PhysicsDebrisData ), 
@@ -214,7 +214,8 @@ void PhysicsDebrisData::packData(BitStream* stream)
    stream->write( angularSleepThreshold );
    stream->write( waterDampingScale );
    stream->write( buoyancyDensity );
-   stream->writeString( shapeName );
+
+   PACKDATA_SHAPEASSET(Shape);
 }
 
 void PhysicsDebrisData::unpackData(BitStream* stream)
@@ -235,7 +236,7 @@ void PhysicsDebrisData::unpackData(BitStream* stream)
    stream->read( &waterDampingScale );
    stream->read( &buoyancyDensity );
 
-   shapeName   = stream->readSTString();
+   UNPACKDATA_SHAPEASSET(Shape);
 }
 
 DefineEngineMethod( PhysicsDebrisData, preload, void, (), , 
@@ -246,7 +247,8 @@ DefineEngineMethod( PhysicsDebrisData, preload, void, (), ,
 {
    String errorStr;
 
-   object->shape = NULL;
+   object->_setShape(object->getShape());
+
    if( !object->preload( false, errorStr ) )
       Con::errorf( "PhsysicsDebrisData::preload - error: %s", errorStr.c_str() );
 }
@@ -358,7 +360,7 @@ bool PhysicsDebris::onAdd()
    }
 
    // Setup our bounding box
-   mObjBox = mDataBlock->shape->mBounds;
+   mObjBox = mDataBlock->mShape->mBounds;
    resetWorldBox();
 
    // Add it to the client scene.
@@ -621,7 +623,7 @@ void PhysicsDebris::_createFragments()
    if ( !mWorld )
       return;
 
-   TSShape *shape = mDataBlock->shape;
+   TSShape *shape = mDataBlock->mShape;
 
    mShapeInstance = new TSShapeInstance( shape, true );
    mShapeInstance->animate();
@@ -695,7 +697,7 @@ void PhysicsDebris::_findNodes( U32 colNode, Vector<U32> &nodeIds )
    // 1. Visible mesh nodes are siblings of the collision node under a common parent dummy node
    // 2. Collision node is a child of its visible mesh node
 
-   TSShape *shape = mDataBlock->shape;
+   TSShape *shape = mDataBlock->mShape;
    S32 itr = shape->nodes[colNode].parentIndex;
    itr = shape->nodes[itr].firstChild;
 

+ 6 - 2
Engine/source/T3D/physics/physicsDebris.h

@@ -33,6 +33,8 @@
 #include "T3D/physics/physicsCommon.h"
 #endif
 
+#include "T3D/assets/ShapeAsset.h"
+
 
 class TSShapeInstance;
 class TSShape;
@@ -84,8 +86,8 @@ public:
    /// Is rendererd during shadow passes.
    bool castShadows;
 
-   const char* shapeName;
-   Resource<TSShape> shape;
+   DECLARE_SHAPEASSET(PhysicsDebrisData, Shape, onShapeChanged);
+   DECLARE_SHAPEASSET_SETGET(PhysicsDebrisData, Shape);
 
    PhysicsDebrisData();
 
@@ -95,6 +97,8 @@ public:
    void        packData( BitStream *stream );
    void        unpackData( BitStream *stream );
 
+   void onShapeChanged() {}
+
    DECLARE_CONOBJECT( PhysicsDebrisData );
 
 };

+ 29 - 32
Engine/source/T3D/physics/physicsShape.cpp

@@ -66,8 +66,7 @@ ConsoleDocClass( PhysicsShapeData,
 );
 
 PhysicsShapeData::PhysicsShapeData()
-   :  shapeName( NULL ),
-      mass( 1.0f ),
+   :  mass( 1.0f ),
       dynamicFriction( 0.0f ),
       staticFriction( 0.0f ),
       restitution( 0.0f ),
@@ -79,6 +78,7 @@ PhysicsShapeData::PhysicsShapeData()
       buoyancyDensity( 0.0f ),
       simType( SimType_ClientServer )      
 {
+   INIT_SHAPEASSET(Shape);
 }
 
 PhysicsShapeData::~PhysicsShapeData()
@@ -91,9 +91,8 @@ void PhysicsShapeData::initPersistFields()
 
    addGroup("Media");
 
-      addField( "shapeName", TypeShapeFilename, Offset( shapeName, PhysicsShapeData ),
-         "@brief Path to the .DAE or .DTS file to use for this shape.\n\n"
-         "Compatable with Live-Asset Reloading. ");
+      INITPERSISTFIELD_SHAPEASSET(Shape, PhysicsShapeData, "@brief Shape asset to be used with this physics object.\n\n"
+         "Compatable with Live-Asset Reloading. ")
 
       addField( "debris", TYPEID< SimObjectRef<PhysicsDebrisData> >(), Offset( debris, PhysicsShapeData ),
          "@brief Name of a PhysicsDebrisData to spawn when this shape is destroyed (optional)." );
@@ -181,7 +180,7 @@ void PhysicsShapeData::packData( BitStream *stream )
 { 
    Parent::packData( stream );
 
-   stream->writeString( shapeName );
+   PACKDATA_SHAPEASSET(Shape);
 
    stream->write( mass );
    stream->write( dynamicFriction );
@@ -205,7 +204,7 @@ void PhysicsShapeData::unpackData( BitStream *stream )
 {
    Parent::unpackData(stream);
 
-   shapeName = stream->readSTString();
+   UNPACKDATA_SHAPEASSET(Shape);
 
    stream->read( &mass );
    stream->read( &dynamicFriction );
@@ -242,28 +241,28 @@ void PhysicsShapeData::onRemove()
 
 void PhysicsShapeData::_onResourceChanged( const Torque::Path &path )
 {
-   if ( path != Path( shapeName ) )
+   if (mShapeAsset.isNull())
       return;
 
+   if ( path != Path(mShapeAsset->getShapeFilePath()) )
+      return;
+
+   _setShape(getShape());
+
    // Reload the changed shape.
-   Resource<TSShape> reloadShape;
    PhysicsCollisionRef reloadcolShape;
 
-   reloadShape = ResourceManager::get().load( shapeName );
-   if ( !bool(reloadShape) )
+   if ( !mShape )
    {
       Con::warnf( ConsoleLogEntry::General, "PhysicsShapeData::_onResourceChanged: Could not reload %s.", path.getFileName().c_str() );
       return;
    }
 
    // Reload the collision shape.
-   reloadcolShape = reloadShape->buildColShape( false, Point3F::One );
+   reloadcolShape = mShape->buildColShape( false, Point3F::One );
 
-   if ( bool(reloadShape) &&  bool(reloadcolShape))
-   {
-      shape = reloadShape;
+   if (  bool(reloadcolShape))
       colShape = reloadcolShape;
-   }
 
    mReloadSignal.trigger();
 }
@@ -283,35 +282,33 @@ bool PhysicsShapeData::preload( bool server, String &errorBuffer )
 
    bool shapeError = false;
 
-   if (shapeName && shapeName[0])
+   if (mShapeAsset.notNull())
    {
-      // Resolve shapename
-      shape = ResourceManager::get().load(shapeName);
-      if (bool(shape) == false)
+      if (bool(mShape) == false)
       {
-         errorBuffer = String::ToString("PhysicsShapeData: Couldn't load shape \"%s\"", shapeName);
+         errorBuffer = String::ToString("PhysicsShapeData: Couldn't load shape \"%s\"", mShapeAssetId);
          return false;
       }
-      if (!server && !shape->preloadMaterialList(shape.getPath()) && NetConnection::filesWereDownloaded())
+      if (!server && !mShape->preloadMaterialList(mShape.getPath()) && NetConnection::filesWereDownloaded())
          shapeError = true;
 
    }
 
    // Prepare the shared physics collision shape.
-   if ( !colShape && shape )
+   if ( !colShape && mShape)
    {
-      colShape = shape->buildColShape( false, Point3F::One );
+      colShape = mShape->buildColShape( false, Point3F::One );
 
       // If we got here and didn't get a collision shape then
       // we need to fail... can't have a shape without collision.
       if ( !colShape )
       {
          //no collision so we create a simple box collision shape from the shapes bounds and alert the user
-         Con::warnf( "PhysicsShapeData::preload - No collision found for shape '%s', auto-creating one", shapeName );
-         Point3F halfWidth = shape->mBounds.getExtents() * 0.5f;
+         Con::warnf( "PhysicsShapeData::preload - No collision found for shape '%s', auto-creating one", mShapeAssetId);
+         Point3F halfWidth = mShape->mBounds.getExtents() * 0.5f;
          colShape = PHYSICSMGR->createCollision();
          MatrixF centerXfm(true);
-         centerXfm.setPosition(shape->mBounds.getCenter());
+         centerXfm.setPosition(mShape->mBounds.getCenter());
          colShape->addBox(halfWidth, centerXfm);
          return true;
       }
@@ -703,11 +700,11 @@ bool PhysicsShape::_createShape()
    mAmbientSeq = -1;
 
    PhysicsShapeData *db = getDataBlock();
-   if ( !db || !db->shape)
+   if ( !db || !db->mShape)
       return false;
 
    // Set the world box.
-   mObjBox = db->shape->mBounds;
+   mObjBox = db->mShape->mBounds;
    resetWorldBox();
 
    // If this is the server and its a client only simulation
@@ -721,11 +718,11 @@ bool PhysicsShape::_createShape()
    }
 
    // Create the shape instance.
-   mShapeInst = new TSShapeInstance( db->shape, isClientObject() );
+   mShapeInst = new TSShapeInstance( db->mShape, isClientObject() );
 
    if ( isClientObject() )
    {
-      mAmbientSeq = db->shape->findSequence( "ambient" );
+      mAmbientSeq = db->mShape->findSequence( "ambient" );
       _initAmbient();   
    }
 
@@ -1207,4 +1204,4 @@ DefineEngineMethod(PhysicsShape, applyForce, void, (Point3F force), ,
    "@note This value is ignored on physics shapes that are not dynamic. Wakes up the dynamic physics shape if it is sleeping.\n")
 {
    object->applyForce( force );
-}
+}

+ 6 - 5
Engine/source/T3D/physics/physicsShape.h

@@ -42,6 +42,8 @@
    #include "console/simObjectRef.h"
 #endif
 
+#include "T3D/assets/ShapeAsset.h"
+
 class TSShapeInstance;
 class PhysicsBody;
 class PhysicsWorld;
@@ -72,11 +74,8 @@ public:
 
 public:
 
-   /// The shape to load.
-   StringTableEntry shapeName;
-
-   /// The shape resource.
-   Resource<TSShape> shape;
+   DECLARE_SHAPEASSET(PhysicsShapeData, Shape, onShapeChanged);
+   DECLARE_SHAPEASSET_SETGET(PhysicsShapeData, Shape);
 
    /// The shared unscaled collision shape.
    PhysicsCollisionRef colShape;
@@ -135,6 +134,8 @@ public:
    SimObjectRef< PhysicsDebrisData > debris;   
    SimObjectRef< ExplosionData > explosion;   
    SimObjectRef< PhysicsShapeData > destroyedShape;
+
+   void onShapeChanged() {}
 };
 
 typedef PhysicsShapeData::SimType PhysicsSimType;

+ 19 - 16
Engine/source/T3D/player.cpp

@@ -272,7 +272,7 @@ PlayerData::PlayerData()
    imageAnimPrefixFP = StringTable->EmptyString();
    for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
    {
-      shapeNameFP[i] = StringTable->EmptyString();
+      INIT_SHAPEASSET_ARRAY(ShapeFP, i);
       mCRCFP[i] = 0;
       mValidShapeFP[i] = false;
    }
@@ -585,35 +585,34 @@ bool PlayerData::preload(bool server, String &errorStr)
    {
       bool shapeError = false;
 
-      if (shapeNameFP[i] && shapeNameFP[i][0])
+      if (mShapeFPAssetId[i] != StringTable->EmptyString())
       {
-         mShapeFP[i] = ResourceManager::get().load(shapeNameFP[i]);
-         if (bool(mShapeFP[i]) == false)
+         if (!mShapeFP[i])
          {
-            errorStr = String::ToString("PlayerData: Couldn't load mounted image %d shape \"%s\"",i,shapeNameFP[i]);
+            errorStr = String::ToString("PlayerData: Couldn't load mounted image %d shape \"%s\"", i, mShapeFPAssetId[i]);
             return false;
          }
 
-         if(!server && !mShapeFP[i]->preloadMaterialList(mShapeFP[i].getPath()) && NetConnection::filesWereDownloaded())
+         if (!server && !mShapeFP[i]->preloadMaterialList(mShapeFP[i].getPath()) && NetConnection::filesWereDownloaded())
             shapeError = true;
 
-         if(computeCRC)
+         if (computeCRC)
          {
-            Con::printf("Validation required for mounted image %d shape: %s", i, shapeNameFP[i]);
+            Con::printf("Validation required for mounted image %d shape: %s", i, mShapeFPAssetId[i]);
 
             Torque::FS::FileNodeRef    fileRef = Torque::FS::GetFileNode(mShapeFP[i].getPath());
 
             if (!fileRef)
             {
-               errorStr = String::ToString("PlayerData: Mounted image %d loading failed, shape \"%s\" is not found.",i,mShapeFP[i].getPath().getFullPath().c_str());
+               errorStr = String::ToString("PlayerData: Mounted image %d loading failed, shape \"%s\" is not found.", i, mShapeFP[i].getPath().getFullPath().c_str());
                return false;
             }
 
-            if(server)
+            if (server)
                mCRCFP[i] = fileRef->getChecksum();
-            else if(mCRCFP[i] != fileRef->getChecksum())
+            else if (mCRCFP[i] != fileRef->getChecksum())
             {
-               errorStr = String::ToString("PlayerData: Mounted image %d shape \"%s\" does not match version on server.",i,shapeNameFP[i]);
+               errorStr = String::ToString("PlayerData: Mounted image %d shape \"%s\" does not match version on server.", i, mShapeFPAssetId[i]);
                return false;
             }
          }
@@ -1165,9 +1164,13 @@ void PlayerData::initPersistFields()
 
       // Mounted images arrays
       addArray( "Mounted Images", ShapeBase::MaxMountedImages );
-
-         addField( "shapeNameFP", TypeShapeFilename, Offset(shapeNameFP, PlayerData), ShapeBase::MaxMountedImages,
+         addProtectedField("shapeNameFP", TypeShapeFilename, Offset(mShapeFPName, PlayerData), &_setShapeFPData, &defaultProtectedGetFn, ShapeBase::MaxMountedImages,
             "@brief File name of this player's shape that will be used in conjunction with the corresponding mounted image.\n\n"
+            "These optional parameters correspond to each mounted image slot to indicate a shape that is rendered "
+            "in addition to the mounted image shape.  Typically these are a player's arms (or arm) that is "
+            "animated along with the mounted image's state animation sequences.\n", AbstractClassRep::FIELD_HideInInspectors);
+
+         INITPERSISTFIELD_SHAPEASSET_ARRAY(ShapeFP, ShapeBase::MaxMountedImages, PlayerData, "@brief File name of this player's shape that will be used in conjunction with the corresponding mounted image.\n\n"
             "These optional parameters correspond to each mounted image slot to indicate a shape that is rendered "
             "in addition to the mounted image shape.  Typically these are a player's arms (or arm) that is "
             "animated along with the mounted image's state animation sequences.\n");
@@ -1369,7 +1372,7 @@ void PlayerData::packData(BitStream* stream)
    stream->writeString(imageAnimPrefixFP);
    for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
    {
-      stream->writeString(shapeNameFP[i]);
+      PACKDATA_SHAPEASSET_ARRAY(ShapeFP, i);
 
       // computeCRC is handled in ShapeBaseData
       if (computeCRC)
@@ -1549,7 +1552,7 @@ void PlayerData::unpackData(BitStream* stream)
    imageAnimPrefixFP = stream->readSTString();
    for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
    {
-      shapeNameFP[i] = stream->readSTString();
+      UNPACKDATA_SHAPEASSET_ARRAY(ShapeFP, i);
 
       // computeCRC is handled in ShapeBaseData
       if (computeCRC)

+ 3 - 2
Engine/source/T3D/player.h

@@ -75,10 +75,11 @@ struct PlayerData: public ShapeBaseData {
                                                                   ///  that we don't create a TSThread on the player if we don't
                                                                   ///  need to.
 
-   StringTableEntry  shapeNameFP[ShapeBase::MaxMountedImages];    ///< Used to render with mounted images in first person [optional]
+   DECLARE_SHAPEASSET_ARRAY(PlayerData, ShapeFP, ShapeBase::MaxMountedImages); ///< Used to render with mounted images in first person [optional]
+   DECLARE_SHAPEASSET_ARRAY_SETGET(PlayerData, ShapeFP);
+
    StringTableEntry  imageAnimPrefixFP;                           ///< Passed along to mounted images to modify
                                                                   ///  animation sequences played in first person. [optional]
-   Resource<TSShape> mShapeFP[ShapeBase::MaxMountedImages];       ///< First person mounted image shape resources [optional]
    U32               mCRCFP[ShapeBase::MaxMountedImages];         ///< Computed CRC values for the first person mounted image shapes
                                                                   ///  Depends on the ShapeBaseData computeCRC field.
    bool              mValidShapeFP[ShapeBase::MaxMountedImages];  ///< Indicates that there is a valid first person mounted image shape

+ 10 - 12
Engine/source/T3D/prefab.cpp

@@ -196,7 +196,7 @@ U32 Prefab::packUpdate( NetConnection *conn, U32 mask, BitStream *stream )
 
    if ( stream->writeFlag( mask & FileMask ) )
    {
-      stream->write( mFilename );
+      stream->writeString( mFilename );
    }
 
    if ( stream->writeFlag( mask & TransformMask ) )
@@ -218,7 +218,7 @@ void Prefab::unpackUpdate(NetConnection *conn, BitStream *stream)
    // FileMask
    if ( stream->readFlag() ) 
    {
-      stream->read( &mFilename );
+      mFilename = stream->readSTString();
    }
 
    // TransformMask
@@ -235,9 +235,7 @@ bool Prefab::protectedSetFile( void *object, const char *index, const char *data
 {
    Prefab *prefab = static_cast<Prefab*>(object);
    
-   String file = String( Platform::makeRelativePathName(data, Platform::getMainDotCsDir()) );
-
-   prefab->setFile( file );
+   prefab->setFile( StringTable->insert(Platform::makeRelativePathName(data, Platform::getMainDotCsDir())));
 
    return false;
 }
@@ -336,12 +334,12 @@ void Prefab::_loadFile( bool addFileNotify )
 {
    AssertFatal( isServerObject(), "Prefab-bad" );
 
-   if ( mFilename.isEmpty() )
+   if ( mFilename == StringTable->EmptyString())
       return;
 
-   if ( !Platform::isFile( mFilename ) )
+   if ( !Torque::FS::IsScriptFile( mFilename ) )
    {
-      Con::errorf( "Prefab::_loadFile() - file %s was not found.", mFilename.c_str() );
+      Con::errorf( "Prefab::_loadFile() - file %s was not found.", mFilename );
       return;
    }
 
@@ -349,19 +347,19 @@ void Prefab::_loadFile( bool addFileNotify )
    {
       Con::errorf( 
          "Prefab::_loadFile - failed loading prefab file (%s). \n"
-         "File was referenced recursively by both a Parent and Child prefab.", mFilename.c_str() );
+         "File was referenced recursively by both a Parent and Child prefab.", mFilename );
       return;
    }
 
    sPrefabFileStack.push_back(mFilename);
 
-   String command = String::ToString( "exec( \"%s\" );", mFilename.c_str() );
+   String command = String::ToString( "exec( \"%s\" );", mFilename );
    Con::evaluate( command );
 
    SimGroup *group;
    if ( !Sim::findObject( Con::getVariable( "$ThisPrefab" ), group ) )
    {
-      Con::errorf( "Prefab::_loadFile() - file %s did not create $ThisPrefab.", mFilename.c_str() );
+      Con::errorf( "Prefab::_loadFile() - file %s did not create $ThisPrefab.", mFilename );
       return;
    }
 
@@ -614,4 +612,4 @@ void ExplodePrefabUndoAction::redo()
    name += "_exploded";
    name = Sim::getUniqueName( name );
    mGroup->assignName( name );   
-}
+}

+ 2 - 2
Engine/source/T3D/prefab.h

@@ -122,7 +122,7 @@ protected:
 protected:
 
    /// Prefab file which defines our children objects.
-   String mFilename;
+   StringTableEntry mFilename;
 
    /// Group which holds all children objects.
    SimObjectPtr<SimGroup> mChildGroup;
@@ -168,4 +168,4 @@ protected:
 };
 
 
-#endif // _PREFAB_H_
+#endif // _PREFAB_H_

+ 26 - 23
Engine/source/T3D/projectile.cpp

@@ -144,7 +144,7 @@ U32 Projectile::smProjectileWarpTicks = 5;
 //
 ProjectileData::ProjectileData()
 {
-   projectileShapeName = NULL;
+   INIT_SHAPEASSET(ProjectileShape);
 
    sound = NULL;
 
@@ -197,7 +197,6 @@ ProjectileData::ProjectileData()
 
 ProjectileData::ProjectileData(const ProjectileData& other, bool temp_clone) : GameBaseData(other, temp_clone)
 {
-   projectileShapeName = other.projectileShapeName;
    faceViewer = other.faceViewer; // -- always set to false
    scale = other.scale;
    velInheritFactor = other.velInheritFactor;
@@ -221,7 +220,7 @@ ProjectileData::ProjectileData(const ProjectileData& other, bool temp_clone) : G
    sound = other.sound;
    lightDesc = other.lightDesc;
    lightDescId = other.lightDescId; // -- for pack/unpack of lightDesc ptr
-   projectileShape = other.projectileShape; // -- TSShape loads using projectileShapeName
+   CLONE_SHAPEASSET(ProjectileShape);// -- TSShape loads using mProjectileShapeName
    activateSeq = other.activateSeq; // -- from projectileShape sequence "activate"
    maintainSeq = other.maintainSeq; // -- from projectileShape sequence "maintain"
    particleEmitter = other.particleEmitter;
@@ -244,8 +243,11 @@ void ProjectileData::initPersistFields()
       "as the projectile enters or leaves water.\n\n"
       "@see particleEmitter\n");
 
-   addField("projectileShapeName", TypeShapeFilename, Offset(projectileShapeName, ProjectileData),
-      "@brief File path to the model of the projectile.\n\n");
+   addProtectedField("projectileShapeName", TypeShapeFilename, Offset(mProjectileShapeName, ProjectileData), &_setProjectileShapeData, &defaultProtectedGetFn,
+      "@brief File path to the model of the projectile.\n\n", AbstractClassRep::FIELD_HideInInspectors);
+      
+   INITPERSISTFIELD_SHAPEASSET(ProjectileShape, ProjectileData, "@brief The model of the projectile.\n\n");
+
    addField("scale", TypePoint3F, Offset(scale, ProjectileData),
       "@brief Scale to apply to the projectile's size.\n\n"
       "@note This is applied after SceneObject::scale\n");
@@ -375,22 +377,22 @@ bool ProjectileData::preload(bool server, String &errorStr)
             Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockid(lightDesc): %d", lightDescId);   
    }
 
-   if (projectileShapeName && projectileShapeName[0] != '\0')
+   if (mProjectileShapeAssetId != StringTable->EmptyString())
    {
-      projectileShape = ResourceManager::get().load(projectileShapeName);
-      if (bool(projectileShape) == false)
+      //If we've got a shapeAsset assigned for our projectile, but we failed to load the shape data itself, report the error
+      if (!mProjectileShape)
       {
-         errorStr = String::ToString("ProjectileData::load: Couldn't load shape \"%s\"", projectileShapeName);
+         errorStr = String::ToString("ProjectileData::load: Couldn't load shape \"%s\"", mProjectileShapeAssetId);
          return false;
       }
-      activateSeq = projectileShape->findSequence("activate");
-      maintainSeq = projectileShape->findSequence("maintain");
-   }
+      else
+      {
+         activateSeq = mProjectileShape->findSequence("activate");
+         maintainSeq = mProjectileShape->findSequence("maintain");
 
-   if (bool(projectileShape)) // create an instance to preload shape data
-   {
-      TSShapeInstance* pDummy = new TSShapeInstance(projectileShape, !server);
-      delete pDummy;
+         TSShapeInstance* pDummy = new TSShapeInstance(mProjectileShape, !server);
+         delete pDummy;
+      }
    }
 
    return true;
@@ -401,7 +403,8 @@ void ProjectileData::packData(BitStream* stream)
 {
    Parent::packData(stream);
 
-   stream->writeString(projectileShapeName);
+   PACKDATA_SHAPEASSET(ProjectileShape);
+
    stream->writeFlag(faceViewer);
    if(stream->writeFlag(scale.x != 1 || scale.y != 1 || scale.z != 1))
    {
@@ -465,7 +468,7 @@ void ProjectileData::unpackData(BitStream* stream)
 {
    Parent::unpackData(stream);
 
-   projectileShapeName = stream->readSTString();
+   UNPACKDATA_SHAPEASSET(ProjectileShape);
 
    faceViewer = stream->readFlag();
    if(stream->readFlag())
@@ -786,9 +789,9 @@ bool Projectile::onAdd()
    }
    else
    {
-      if (bool(mDataBlock->projectileShape))
+      if (bool(mDataBlock->mProjectileShape))
       {
-         mProjectileShape = new TSShapeInstance(mDataBlock->projectileShape, isClientObject());
+         mProjectileShape = new TSShapeInstance(mDataBlock->mProjectileShape, isClientObject());
 
          if (mDataBlock->activateSeq != -1)
          {
@@ -827,8 +830,8 @@ bool Projectile::onAdd()
       processAfter(mSourceObject);
 
    // Setup our bounding box
-   if (bool(mDataBlock->projectileShape) == true)
-      mObjBox = mDataBlock->projectileShape->mBounds;
+   if (bool(mDataBlock->mProjectileShape) == true)
+      mObjBox = mDataBlock->mProjectileShape->mBounds;
    else
       mObjBox = Box3F(Point3F(0, 0, 0), Point3F(0, 0, 0));
 
@@ -1509,4 +1512,4 @@ DefineEngineMethod(Projectile, presimulate, void, (F32 seconds), (1.0f),
                                        "@note This function is not called if the SimObject::hidden is true.")
 {
 	object->simulate( seconds );
-}
+}

+ 5 - 4
Engine/source/T3D/projectile.h

@@ -44,6 +44,7 @@
 #include "lighting/lightInfo.h"
 #endif
 
+#include "T3D/assets/ShapeAsset.h"
 
 class ExplosionData;
 class SplashData;
@@ -69,9 +70,8 @@ protected:
    bool onAdd();
 
 public:
-   // variables set in datablock definition:
-   // Shape related
-   const char* projectileShapeName;
+   DECLARE_SHAPEASSET(ProjectileData, ProjectileShape, onShapeChanged);
+   DECLARE_SHAPEASSET_SETGET(ProjectileData, ProjectileShape);
 
    /// Set to true if it is a billboard and want it to always face the viewer, false otherwise
    bool faceViewer;
@@ -121,7 +121,6 @@ public:
    S32 lightDescId;   
 
    // variables set on preload:
-   Resource<TSShape> projectileShape;
    S32 activateSeq;
    S32 maintainSeq;
 
@@ -152,6 +151,8 @@ public:
 public:
    ProjectileData(const ProjectileData&, bool = false);
    virtual bool allowSubstitutions() const { return true; }
+
+   void onShapeChanged() {}
 };
 
 

+ 1 - 1
Engine/source/T3D/rigidShape.cpp

@@ -291,7 +291,7 @@ bool RigidShapeData::preload(bool server, String &errorStr)
    if (!collisionDetails.size() || collisionDetails[0] == -1)
    {
       Con::errorf("RigidShapeData::preload failed: Rigid shapes must define a collision-1 detail");
-      errorStr = String::ToString("RigidShapeData: Couldn't load shape \"%s\"", mShapeName);
+      errorStr = String::ToString("RigidShapeData: Couldn't load shape asset \"%s\"", mShapeAsset.getAssetId());
       return false;
    }
 

+ 2 - 2
Engine/source/T3D/sfx/sfxEmitter.cpp

@@ -721,12 +721,12 @@ void SFXEmitter::_update()
          mLocalProfile.mResource = NULL;
          mLocalProfile.mBuffer = NULL;
 
-         if( !mLocalProfile.mFilename.isEmpty() )
+         if( mLocalProfile.mFilename != StringTable->EmptyString() )
          {
             mSource = SFX->createSource( &mLocalProfile, &transform, &velocity );
             if( !mSource )
                Con::errorf( "SFXEmitter::_update() - failed to create sound for: %s",
-                  mLocalProfile.mFilename.c_str() );
+                  mLocalProfile.mFilename );
             
             prevState = mPlayOnAdd ? SFXStatusPlaying : prevState;
          }

+ 26 - 73
Engine/source/T3D/shapeBase.cpp

@@ -163,7 +163,6 @@ ShapeBaseData::ShapeBaseData()
    reflectorDesc( NULL ),
    debris( NULL ),
    debrisID( 0 ),
-   debrisShapeName( StringTable->EmptyString() ),
    explosion( NULL ),
    explosionID( 0 ),
    underwaterExplosion( NULL ),
@@ -198,7 +197,9 @@ ShapeBaseData::ShapeBaseData()
    renderWhenDestroyed( true ),
    inheritEnergyFromMount( false )
 {
-   initShapeAsset(Shape);
+   INIT_SHAPEASSET(Shape);
+   INIT_SHAPEASSET(DebrisShape);
+
    dMemset( mountPointNode, -1, sizeof( S32 ) * SceneObject::NumMountPoints );
    remap_txr_tags = NULL;
    remap_buffer = NULL;
@@ -213,14 +214,13 @@ ShapeBaseData::ShapeBaseData(const ShapeBaseData& other, bool temp_clone) : Game
    shadowProjectionDistance = other.shadowProjectionDistance;
    shadowSphereAdjust = other.shadowSphereAdjust;
    cloakTexName = other.cloakTexName;
-   cloneShapeAsset(Shape);
+   CLONE_SHAPEASSET(Shape);
    cubeDescName = other.cubeDescName;
    cubeDescId = other.cubeDescId;
    reflectorDesc = other.reflectorDesc;
    debris = other.debris;
    debrisID = other.debrisID; // -- for pack/unpack of debris ptr
-   debrisShapeName = other.debrisShapeName;
-   debrisShape = other.debrisShape; // -- TSShape loaded using debrisShapeName
+   CLONE_SHAPEASSET(DebrisShape);
    explosion = other.explosion;
    explosionID = other.explosionID; // -- for pack/unpack of explosion ptr
    underwaterExplosion = other.underwaterExplosion;
@@ -337,67 +337,28 @@ bool ShapeBaseData::preload(bool server, String &errorStr)
             "ShapeBaseData::preload: invalid debris data");
       }
 
-
-      if( debrisShapeName && debrisShapeName[0] != '\0' && !bool(debrisShape) )
+      if( bool(mDebrisShape))
       {
-         debrisShape = ResourceManager::get().load(debrisShapeName);
-         if( bool(debrisShape) == false )
-         {
-            errorStr = String::ToString("ShapeBaseData::load: Couldn't load shape \"%s\"", debrisShapeName);
-            return false;
-         }
-         else
-         {
-            if(!server && !debrisShape->preloadMaterialList(debrisShape.getPath()) && NetConnection::filesWereDownloaded())
-               shapeError = true;
-
-            TSShapeInstance* pDummy = new TSShapeInstance(debrisShape, !server);
-            delete pDummy;
-         }
+         TSShapeInstance* pDummy = new TSShapeInstance(mDebrisShape, !server);
+         delete pDummy;
       }
    }
-   PersistenceManager *persistMgr;
-   if (!Sim::findObject("ServerAssetValidator", persistMgr)) Con::errorf("ServerAssetValidator not found!");
-   if (server && persistMgr && mShapeAssetId == StringTable->EmptyString())
-   {
-      persistMgr->setDirty(this);
-   }
 
-   //Legacy catch
-   if (mShapeName != StringTable->EmptyString())
-   {
-      mShapeAssetId = ShapeAsset::getAssetIdByFilename(mShapeName);
-   }
-   U32 assetState = ShapeAsset::getAssetById(mShapeAssetId, &mShapeAsset);
-   if (ShapeAsset::Failed != assetState)
+   S32 i;
+   if (ShapeAsset::getAssetErrCode(mShapeAsset) != ShapeAsset::Failed && ShapeAsset::getAssetErrCode(mShapeAsset) != ShapeAsset::BadFileReference)
    {
-      //only clear the legacy direct file reference if everything checks out fully
-      if (assetState == ShapeAsset::Ok)
-      {
-         mShapeName = StringTable->EmptyString();
-      }
-      else Con::warnf("Warning: ShapeBaseData::preload-%s", ShapeAsset::getAssetErrstrn(assetState).c_str());
-      S32 i;
-
-      // Resolve shapename
-      mShape = mShapeAsset->getShapeResource();
-      if (bool(mShape) == false)
-      {
-         errorStr = String::ToString("ShapeBaseData: Couldn't load shape \"%s\"",mShapeName);
-         return false;
-      }
-      if(!server && !mShape->preloadMaterialList(mShape.getPath()) && NetConnection::filesWereDownloaded())
+      if (!server && !mShape->preloadMaterialList(mShape.getPath()) && NetConnection::filesWereDownloaded())
          shapeError = true;
 
       if(computeCRC)
       {
-         Con::printf("Validation required for shape: %s", mShapeName);
+         Con::printf("Validation required for shape asset: %s", mShapeAsset.getAssetId());
 
-         Torque::FS::FileNodeRef    fileRef = Torque::FS::GetFileNode(mShape.getPath());
+         Torque::FS::FileNodeRef    fileRef = Torque::FS::GetFileNode(mShapeAsset->getShapePath());
 
          if (!fileRef)
          {
-            errorStr = String::ToString("ShapeBaseData: Couldn't load shape \"%s\"", mShapeName);
+            errorStr = String::ToString("ShapeBaseData: Couldn't load shape asset \"%s\"", mShapeAsset.getAssetId());
             return false;
          }
 
@@ -405,7 +366,7 @@ bool ShapeBaseData::preload(bool server, String &errorStr)
             mCRC = fileRef->getChecksum();
          else if(mCRC != fileRef->getChecksum())
          {
-            errorStr = String::ToString("Shape \"%s\" does not match version on server.", mShapeName);
+            errorStr = String::ToString("Shape asset \"%s\" does not match version on server.", mShapeAsset.getAssetId());
             return false;
          }
       }
@@ -427,13 +388,13 @@ bool ShapeBaseData::preload(bool server, String &errorStr)
             if (!mShape->mBounds.isContained(collisionBounds.last()))
             {
                if (!silent_bbox_check)
-               Con::warnf("Warning: shape %s collision detail %d (Collision-%d) bounds exceed that of shape.", mShapeName, collisionDetails.size() - 1, collisionDetails.last());
+               Con::warnf("Warning: shape asset %s collision detail %d (Collision-%d) bounds exceed that of shape.", mShapeAsset.getAssetId(), collisionDetails.size() - 1, collisionDetails.last());
                collisionBounds.last() = mShape->mBounds;
             }
             else if (collisionBounds.last().isValidBox() == false)
             {
                if (!silent_bbox_check)
-               Con::errorf("Error: shape %s-collision detail %d (Collision-%d) bounds box invalid!", mShapeName, collisionDetails.size() - 1, collisionDetails.last());
+               Con::errorf("Error: shape asset %s-collision detail %d (Collision-%d) bounds box invalid!", mShapeAsset.getAssetId(), collisionDetails.size() - 1, collisionDetails.last());
                collisionBounds.last() = mShape->mBounds;
             }
 
@@ -593,11 +554,7 @@ void ShapeBaseData::initPersistFields()
 
    addGroup( "Render" );
 
-      addField("shapeAsset", TypeShapeAssetId, Offset(mShapeAssetId, ShapeBaseData),
-         "The source shape asset.");
-
-      addField( "shapeFile", TypeShapeFilename, Offset(mShapeName, ShapeBaseData),
-         "The DTS or DAE model to use for this object." );
+      INITPERSISTFIELD_SHAPEASSET(Shape, ShapeBaseData, "The source shape asset.");
 
    endGroup( "Render" );
 
@@ -611,8 +568,8 @@ void ShapeBaseData::initPersistFields()
          "%Debris to generate when this shape is blown up." );
       addField( "renderWhenDestroyed", TypeBool, Offset(renderWhenDestroyed, ShapeBaseData),
          "Whether to render the shape when it is in the \"Destroyed\" damage state." );
-      addField( "debrisShapeName", TypeShapeFilename, Offset(debrisShapeName, ShapeBaseData),
-         "The DTS or DAE model to use for auto-generated breakups. @note may not be functional." );
+
+      INITPERSISTFIELD_SHAPEASSET(DebrisShape, ShapeBaseData, "The shape asset to use for auto-generated breakups. @note may not be functional.");
 
    endGroup( "Destruction" );
 
@@ -800,8 +757,8 @@ void ShapeBaseData::packData(BitStream* stream)
    stream->write(shadowProjectionDistance);
    stream->write(shadowSphereAdjust);
 
-
-   packShapeAsset(stream);
+   PACKDATA_SHAPEASSET(Shape);
+   PACKDATA_SHAPEASSET(DebrisShape);
 
    stream->writeString(cloakTexName);
    if(stream->writeFlag(mass != gShapeBaseDataProto.mass))
@@ -825,7 +782,6 @@ void ShapeBaseData::packData(BitStream* stream)
       stream->write(cameraMaxFov);
    stream->writeFlag(cameraCanBank);
    stream->writeFlag(mountedImagesBank);
-   stream->writeString( debrisShapeName );
 
    stream->writeFlag(observeThroughObject);
 
@@ -879,8 +835,8 @@ void ShapeBaseData::unpackData(BitStream* stream)
    stream->read(&shadowProjectionDistance);
    stream->read(&shadowSphereAdjust);
 
-
-   unpackShapeAsset(stream);
+   UNPACKDATA_SHAPEASSET(Shape);
+   UNPACKDATA_SHAPEASSET(DebrisShape);
 
    cloakTexName = stream->readSTString();
    if(stream->readFlag())
@@ -930,9 +886,6 @@ void ShapeBaseData::unpackData(BitStream* stream)
 
    cameraCanBank = stream->readFlag();
    mountedImagesBank = stream->readFlag();
-
-   debrisShapeName = stream->readSTString();
-
    observeThroughObject = stream->readFlag();
 
    if( stream->readFlag() )
@@ -2011,13 +1964,13 @@ void ShapeBase::blowUp()
 
    TSShapeInstance *debShape = NULL;
 
-   if( mDataBlock->debrisShape == NULL )
+   if( mDataBlock->mDebrisShape == NULL )
    {
       return;
    }
    else
    {
-      debShape = new TSShapeInstance( mDataBlock->debrisShape, true);
+      debShape = new TSShapeInstance( mDataBlock->mDebrisShape, true);
    }
 
 

+ 13 - 8
Engine/source/T3D/shapeBase.h

@@ -373,8 +373,11 @@ struct ShapeBaseImageData: public GameBaseData {
    F32 scriptAnimTransitionTime;    ///< The amount of time to transition between the previous sequence and new sequence
                                     ///< when the script prefix has changed.
 
-   StringTableEntry  shapeName;     ///< Name of shape to render.
-   StringTableEntry  shapeNameFP;   ///< Name of shape to render in first person (optional).
+   DECLARE_SHAPEASSET_ARRAY(ShapeBaseImageData, Shape, MaxShapes);  ///< Name of shape to render.
+   DECLARE_SHAPEASSET_ARRAY_SETGET(ShapeBaseImageData, Shape);
+
+   //DECLARE_SHAPEASSET(ShapeBaseImageData, ShapeFP);  ///< Name of shape to render in first person (optional).
+   //DECLARE_SHAPEASSET_SETGET(ShapeBaseImageData, ShapeFP);
 
    StringTableEntry  imageAnimPrefix;     ///< Passed along to the mounting shape to modify
                                           ///  animation sequences played in 3rd person. [optional]
@@ -407,7 +410,6 @@ struct ShapeBaseImageData: public GameBaseData {
 
    /// @name Shape Data
    /// @{
-   Resource<TSShape> shape[MaxShapes]; ///< Shape handle
    bool shapeIsValid[MaxShapes];       ///< Indicates that the shape has been loaded and is valid
 
    U32 mCRC[MaxShapes];                ///< Checksum of shape.
@@ -538,7 +540,8 @@ public:
    F32 shadowProjectionDistance;
    F32 shadowSphereAdjust;
 
-   DECLARE_SHAPEASSET(ShapeBaseData, Shape);
+   DECLARE_SHAPEASSET(ShapeBaseData, Shape, onShapeChanged);
+   DECLARE_SHAPEASSET_SETGET(ShapeBaseData, Shape);
 
    StringTableEntry  cloakTexName;
 
@@ -552,8 +555,9 @@ public:
    /// @{
    DebrisData *      debris;
    S32               debrisID;
-   StringTableEntry  debrisShapeName;
-   Resource<TSShape> debrisShape;
+
+   DECLARE_SHAPEASSET(ShapeBaseData, DebrisShape, onDebrisChanged);
+   DECLARE_SHAPEASSET_SETGET(ShapeBaseData, DebrisShape);
 
    ExplosionData*    explosion;
    S32               explosionID;
@@ -598,8 +602,6 @@ public:
 
    /// @name Data initialized on preload
    /// @{
-
-   Resource<TSShape> mShape;         ///< Shape handle
    U32 mCRC;
    bool computeCRC;
 
@@ -672,6 +674,9 @@ public:
    char* remap_buffer;
    Vector<TextureTagRemapping> txr_tag_remappings;
    bool silent_bbox_check;
+
+   void onShapeChanged() {}
+   void onDebrisChanged() {}
 public:
    ShapeBaseData(const ShapeBaseData&, bool = false);
 };

+ 41 - 50
Engine/source/T3D/shapeImage.cpp

@@ -191,8 +191,6 @@ ShapeBaseImageData::ShapeBaseImageData()
    lightRadius = 10.f;
    lightBrightness = 1.0f;
 
-   shapeName = "core/rendering/shapes/noshape.dts";
-   shapeNameFP = "";
    imageAnimPrefix = "";
    imageAnimPrefixFP = "";
    fireState = -1;
@@ -295,6 +293,8 @@ ShapeBaseImageData::ShapeBaseImageData()
       isAnimated[i] = false;
       hasFlash[i] = false;
       shapeIsValid[i] = false;
+
+      INIT_SHAPEASSET_ARRAY(Shape, i);
    }
 
    shakeCamera = false;
@@ -407,6 +407,7 @@ bool ShapeBaseImageData::preload(bool server, String &errorStr)
 {
    if (!Parent::preload(server, errorStr))
       return false;
+   bool shapeError = false;
 
    // Resolve objects transmitted from server
    if (!server) {
@@ -434,14 +435,12 @@ bool ShapeBaseImageData::preload(bool server, String &errorStr)
       // Shape 0: Standard image shape
       // Shape 1: Optional first person image shape
 
-      StringTableEntry name;
       if (i == FirstPersonImageShape)
       {
-         if ((useEyeOffset || useEyeNode) && shapeNameFP && shapeNameFP[0])
+         if ((useEyeOffset || useEyeNode) && !mShapeAsset[i].isNull())
          {
             // Make use of the first person shape
             useFirstPersonShape = true;
-            name = shapeNameFP;
          }
          else
          {
@@ -449,27 +448,25 @@ bool ShapeBaseImageData::preload(bool server, String &errorStr)
             continue;
          }
       }
-      else
-      {
-         name = shapeName;
-      }
 
-      if (name && name[0]) {
+      if (!mShapeAsset[i].isNull())
+      {
          // Resolve shapename
-         shape[i] = ResourceManager::get().load(name);
-         if (!bool(shape[i])) {
-            errorStr = String::ToString("Unable to load shape: %s", name);
+         mShape[i] = mShapeAsset[i]->getShapeResource();
+
+         if (!bool(mShape[i])) {
+            errorStr = String::ToString("Unable to load shape asset: %s", mShapeAsset[i]->getAssetId());
             return false;
          }
          if(computeCRC)
          {
-            Con::printf("Validation required for shape: %s", name);
+            Con::printf("Validation required for shape asset: %s", mShapeAsset[i]->getAssetId());
 
-            Torque::FS::FileNodeRef    fileRef = Torque::FS::GetFileNode(shape[i].getPath());
+            Torque::FS::FileNodeRef    fileRef = Torque::FS::GetFileNode(mShape[i].getPath());
 
             if (!fileRef)
             {
-               errorStr = String::ToString("ShapeBaseImageData: Couldn't load shape \"%s\"",name);
+               errorStr = String::ToString("ShapeBaseImageData: Couldn't load shape asset\"%s\"", mShapeAsset[i]->getAssetId());
                return false;
             }
 
@@ -479,29 +476,29 @@ bool ShapeBaseImageData::preload(bool server, String &errorStr)
             }
             else if(mCRC[i] != fileRef->getChecksum())
             {
-               errorStr = String::ToString("Shape \"%s\" does not match version on server.",name);
+               errorStr = String::ToString("Shape asset\"%s\" does not match version on server.", mShapeAsset[i]->getAssetId());
                return false;
             }
          }
 
          // Resolve nodes & build mount transform
-         eyeMountNode[i] = shape[i]->findNode("eyeMount");
-         eyeNode[i] = shape[i]->findNode("eye");
+         eyeMountNode[i] = mShape[i]->findNode("eyeMount");
+         eyeNode[i] = mShape[i]->findNode("eye");
          if (eyeNode[i] == -1)
             eyeNode[i] = eyeMountNode[i];
-         ejectNode[i] = shape[i]->findNode("ejectPoint");
-         muzzleNode[i] = shape[i]->findNode("muzzlePoint");
-         retractNode[i] = shape[i]->findNode("retractionPoint");
+         ejectNode[i] = mShape[i]->findNode("ejectPoint");
+         muzzleNode[i] = mShape[i]->findNode("muzzlePoint");
+         retractNode[i] = mShape[i]->findNode("retractionPoint");
          mountTransform[i] = mountOffset;
-         S32 node = shape[i]->findNode("mountPoint");
+         S32 node = mShape[i]->findNode("mountPoint");
          if (node != -1) {
             MatrixF total(1);
             do {
                MatrixF nmat;
                QuatF q;
-               TSTransform::setMatrix(shape[i]->defaultRotations[node].getQuatF(&q),shape[i]->defaultTranslations[node],&nmat);
+               TSTransform::setMatrix(mShape[i]->defaultRotations[node].getQuatF(&q), mShape[i]->defaultTranslations[node],&nmat);
                total.mul(nmat);
-               node = shape[i]->nodes[node].parentIndex;
+               node = mShape[i]->nodes[node].parentIndex;
             }
             while(node != -1);
             total.inverse();
@@ -514,7 +511,7 @@ bool ShapeBaseImageData::preload(bool server, String &errorStr)
          for (U32 j = 0; j < MaxStates; j++) {
             StateData& s = state[j];
             if (stateSequence[j] && stateSequence[j][0])
-               s.sequence[i] = shape[i]->findSequence(stateSequence[j]);
+               s.sequence[i] = mShape[i]->findSequence(stateSequence[j]);
             if (s.sequence[i] != -1)
             {
                // This state has an animation sequence
@@ -525,7 +522,7 @@ bool ShapeBaseImageData::preload(bool server, String &errorStr)
                char bufferVis[128];
                dStrncpy(bufferVis, stateSequence[j], 100);
                dStrcat(bufferVis, "_vis", 128);
-               s.sequenceVis[i] = shape[i]->findSequence(bufferVis);
+               s.sequenceVis[i] = mShape[i]->findSequence(bufferVis);
             }
             if (s.sequenceVis[i] != -1)
             {
@@ -537,13 +534,13 @@ bool ShapeBaseImageData::preload(bool server, String &errorStr)
             s.ignoreLoadedForReady = stateIgnoreLoadedForReady[j];
 
             if (stateEmitterNode[j] && stateEmitterNode[j][0])
-               s.emitterNode[i] = shape[i]->findNode(stateEmitterNode[j]);
+               s.emitterNode[i] = mShape[i]->findNode(stateEmitterNode[j]);
             if (s.emitterNode[i] == -1)
                s.emitterNode[i] = muzzleNode[i];
          }
 
-         ambientSequence[i] = shape[i]->findSequence("ambient");
-         spinSequence[i] = shape[i]->findSequence("spin");
+         ambientSequence[i] = mShape[i]->findSequence("ambient");
+         spinSequence[i] = mShape[i]->findSequence("spin");
 
          shapeIsValid[i] = true;
       }
@@ -567,7 +564,7 @@ bool ShapeBaseImageData::preload(bool server, String &errorStr)
    {
       if( shapeIsValid[i] )
       {
-         TSShapeInstance* pDummy = new TSShapeInstance(shape[i], !server);
+         TSShapeInstance* pDummy = new TSShapeInstance(mShape[i], !server);
          delete pDummy;
       }
    }
@@ -590,19 +587,9 @@ void ShapeBaseImageData::initPersistFields()
    addField( "emap", TypeBool, Offset(emap, ShapeBaseImageData),
       "@brief Whether to enable environment mapping on this Image.\n\n" );
 
-   addField( "shapeFile", TypeShapeFilename, Offset(shapeName, ShapeBaseImageData),
-      "@brief The DTS or DAE model to use for this Image.\n\n" );
-
-   addField( "shapeFileFP", TypeShapeFilename, Offset(shapeNameFP, ShapeBaseImageData),
-      "@brief The DTS or DAE model to use for this Image when in first person.\n\n"
-      "This is an optional parameter that also requires either eyeOffset or useEyeNode "
-      "to be set.  If none of these conditions is met then shapeFile will be used "
-      "for all cases.\n\n"
-      "Typically you set a first person image for a weapon that "
-      "includes the player's arms attached to it for animating while firing, "
-      "reloading, etc.  This is typical of many FPS games."
-      "@see eyeOffset\n"
-      "@see useEyeNode\n");
+   INITPERSISTFIELD_SHAPEASSET_ARRAY(Shape, MaxShapes, ShapeBaseImageData, "The shape asset to use for this image in the third person")
+
+   //addProtectedField("shapeFileFP", TypeShapeFilename, Offset(mShapeName[1], ShapeBaseImageData), _setShapeData, defaultProtectedGetFn, "deprecated alias for ShapeFPFile/Asset", AbstractClassRep::FIELD_HideInInspectors);
 
    addField( "imageAnimPrefix", TypeCaseString, Offset(imageAnimPrefix, ShapeBaseImageData),
       "@brief Passed along to the mounting shape to modify animation sequences played in third person. [optional]\n\n" );
@@ -987,8 +974,10 @@ void ShapeBaseImageData::packData(BitStream* stream)
       }
    }
 
-   stream->writeString(shapeName);        // shape 0 for normal use
-   stream->writeString(shapeNameFP);      // shape 1 for first person use (optional)
+   for (U32 j = 0; j < MaxShapes; ++j)
+   {
+      PACKDATA_SHAPEASSET_ARRAY(Shape, j);        // shape 0 for normal use, shape 1 for first person use (optional)
+   }
 
    stream->writeString(imageAnimPrefix);
    stream->writeString(imageAnimPrefixFP);
@@ -1169,8 +1158,10 @@ void ShapeBaseImageData::unpackData(BitStream* stream)
       }
    }
 
-   shapeName = stream->readSTString();       // shape 0 for normal use
-   shapeNameFP = stream->readSTString();     // shape 1 for first person use (optional)
+   for (U32 j = 0; j < MaxShapes; ++j)
+   {
+      UNPACKDATA_SHAPEASSET_ARRAY(Shape, j);        // shape 0 for normal use, shape 1 for first person use (optional)
+   }
 
    imageAnimPrefix = stream->readSTString();
    imageAnimPrefixFP = stream->readSTString();
@@ -2128,7 +2119,7 @@ S32 ShapeBase::getNodeIndex(U32 imageSlot,StringTableEntry nodeName)
 {
    MountedImage& image = mMountedImageList[imageSlot];
    if (image.dataBlock)
-      return image.dataBlock->shape[getImageShapeIndex(image)]->findNode(nodeName);
+      return image.dataBlock->mShape[getImageShapeIndex(image)]->findNode(nodeName);
    else
       return -1;
 }
@@ -2318,7 +2309,7 @@ void ShapeBase::setImage(  U32 imageSlot,
    for (U32 i=0; i<ShapeBaseImageData::MaxShapes; ++i)
    {
       if (image.dataBlock->shapeIsValid[i])
-         image.shapeInstance[i] = new TSShapeInstance(image.dataBlock->shape[i], isClientObject());
+         image.shapeInstance[i] = new TSShapeInstance(image.dataBlock->mShape[i], isClientObject());
    }
 
    if (isClientObject())

+ 11 - 96
Engine/source/T3D/tsStatic.cpp

@@ -114,7 +114,6 @@ TSStatic::TSStatic()
 
    mTypeMask |= StaticObjectType | StaticShapeObjectType;
 
-   mShapeName = "";
    mShapeInstance = NULL;
 
    mPlayAmbient = true;
@@ -150,8 +149,7 @@ TSStatic::TSStatic()
    mAnimOffset = 0.0f;
    mAnimSpeed = 1.0f;
 
-   mShapeAsset = StringTable->EmptyString();
-   mShapeAssetId = StringTable->EmptyString();
+   INIT_SHAPEASSET(Shape);
 }
 
 TSStatic::~TSStatic()
@@ -184,13 +182,11 @@ void TSStatic::initPersistFields()
       "Percent Animation Speed.");
    addGroup("Shape");
 
-   addProtectedField("shapeAsset", TypeShapeAssetId, Offset(mShapeAssetId, TSStatic),
-      &TSStatic::_setShapeAsset, &defaultProtectedGetFn,
-      "The source shape asset.");
+   INITPERSISTFIELD_SHAPEASSET(Shape, TSStatic, "Model to use for this TSStatic");
 
    addProtectedField("shapeName", TypeShapeFilename, Offset(mShapeName, TSStatic),
-      &TSStatic::_setShapeName, &defaultProtectedGetFn,
-      "%Path and filename of the model file (.DTS, .DAE) to use for this TSStatic. Legacy field. Any loose files assigned here will attempt to be auto-imported in as an asset.");
+      &TSStatic::_setShapeData, &defaultProtectedGetFn,
+      "%Path and filename of the model file (.DTS, .DAE) to use for this TSStatic. Legacy field. Any loose files assigned here will attempt to be auto-imported in as an asset.", AbstractClassRep::FIELD_HideInInspectors);
 
    endGroup("Shape");
 
@@ -287,50 +283,6 @@ void TSStatic::consoleInit()
    Con::addVariable("$pref::staticObjectUnfadeableSize", TypeF32, &TSStatic::smStaticObjectUnfadeableSize, "Size of object where if the bounds is at or bigger than this, it will be ignored in the $pref::useStaticObjectFade logic. Useful for very large, distance-important objects.\n");
 }
 
-bool TSStatic::_setShapeAsset(void* obj, const char* index, const char* data)
-{
-   TSStatic* ts = static_cast<TSStatic*>(obj);// ->setFile(FileName(data));
-
-   ts->mShapeAssetId = StringTable->insert(data);
-
-   return ts->setShapeAsset(ts->mShapeAssetId);
-}
-
-bool TSStatic::_setShapeName(void* obj, const char* index, const char* data)
-{
-   TSStatic* ts = static_cast<TSStatic*>(obj);// ->setFile(FileName(data));
-
-   StringTableEntry assetId = ShapeAsset::getAssetIdByFilename(StringTable->insert(data));
-   if (assetId != StringTable->EmptyString())
-   {
-      //Special exception case. If we've defaulted to the 'no shape' mesh, don't save it out, we'll retain the original ids/paths so it doesn't break
-      //the TSStatic
-      if (ts->setShapeAsset(assetId))
-      {
-         if (assetId == StringTable->insert("Core_Rendering:noShape"))
-         {
-            ts->mShapeName = data;
-            ts->mShapeAssetId = StringTable->EmptyString();
-
-            return true;
-         }
-         else
-         {
-            ts->mShapeAssetId = assetId;
-            ts->mShapeName = StringTable->EmptyString();
-
-            return false;
-         }
-      }
-    }
-   else
-   {
-      ts->mShapeAsset = StringTable->EmptyString();
-   }
-
-   return true;
-}
-
 bool TSStatic::_setFieldSkin(void* object, const char* index, const char* data)
 {
    TSStatic* ts = static_cast<TSStatic*>(object);
@@ -425,34 +377,6 @@ bool TSStatic::onAdd()
    return true;
 }
 
-bool TSStatic::setShapeAsset(const StringTableEntry shapeAssetId)
-{
-   if (!mShapeAsset.isNull())
-   {
-      mShapeAsset->getChangedSignal().remove(this, &TSStatic::_onAssetChanged);
-   }
-
-   if (ShapeAsset::getAssetById(shapeAssetId, &mShapeAsset))
-   {
-      //Special exception case. If we've defaulted to the 'no shape' mesh, don't save it out, we'll retain the original ids/paths so it doesn't break
-      //the TSStatic
-      if (mShapeAsset.getAssetId() != StringTable->insert("Core_Rendering:noshape"))
-      {
-         mShapeName = StringTable->EmptyString();
-
-         mShapeAsset->getChangedSignal().notify(this, &TSStatic::_onAssetChanged);
-      }
-
-      _createShape();
-
-      setMaskBits(-1);
-
-      return true;
-   }
-
-   return false;
-}
-
 bool TSStatic::_createShape()
 {
    // Cleanup before we create.
@@ -674,9 +598,6 @@ void TSStatic::onRemove()
    if (isClientObject())
       mCubeReflector.unregisterReflector();
 
-   if(!mShapeAsset.isNull())
-      mShapeAsset->getChangedSignal().remove(this, &TSStatic::_onAssetChanged);
-
    Parent::onRemove();
 }
 
@@ -689,7 +610,7 @@ void TSStatic::_onResourceChanged(const Torque::Path& path)
    _updateShouldTick();
 }
 
-void TSStatic::_onAssetChanged()
+void TSStatic::onShapeChanged()
 {
    _createShape();
    _updateShouldTick();
@@ -1037,8 +958,7 @@ U32 TSStatic::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
 
    if (stream->writeFlag(mask & AdvancedStaticOptionsMask))
    {
-      stream->writeString(mShapeAsset.getAssetId());
-      stream->writeString(mShapeName);
+      PACK_SHAPEASSET(con, Shape);
 
       stream->write((U32)mDecalType);
 
@@ -1153,11 +1073,7 @@ void TSStatic::unpackUpdate(NetConnection* con, BitStream* stream)
 
    if (stream->readFlag()) // AdvancedStaticOptionsMask
    {
-      char buffer[256];
-      stream->readString(buffer);
-      setShapeAsset(StringTable->insert(buffer));
-
-      mShapeName = stream->readSTString();
+      UNPACK_SHAPEASSET(con, Shape);
 
       stream->read((U32*)&mDecalType);
 
@@ -1676,7 +1592,7 @@ void TSStatic::updateMaterials()
 
    String path;
    if (mShapeAsset->isAssetValid())
-      path = mShapeAsset->getShapeFilename();
+      path = mShapeAsset->getShapeFileName();
    else
       path = mShapeName;
 
@@ -1709,9 +1625,8 @@ void TSStatic::updateMaterials()
 
 void TSStatic::getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList)
 {
-   if(!mShapeAsset.isNull() && mShapeAsset->getAssetId() != StringTable->insert("Core_Rendering:noShape"))
+   if(!mShapeAsset.isNull() && mShapeAsset->getAssetId() != ShapeAsset::smNoShapeAssetFallback)
       usedAssetsList->push_back_unique(mShapeAsset->getAssetId());
-
 }
 
 //------------------------------------------------------------------------
@@ -1874,7 +1789,7 @@ DefineEngineMethod(TSStatic, changeMaterial, void, (const char* mapTo, Material*
       return;
    }
 
-   TSMaterialList* shapeMaterialList = object->getShape()->materialList;
+   TSMaterialList* shapeMaterialList = object->getShapeResource()->materialList;
 
    // Check the mapTo name exists for this shape
    S32 matIndex = shapeMaterialList->getMaterialNameList().find_next(String(mapTo));
@@ -1914,7 +1829,7 @@ DefineEngineMethod(TSStatic, getModelFile, const char*, (), ,
    "@endtsexample\n"
 )
 {
-   return object->getShapeFileName();
+   return object->getShape();
 }
 
 void TSStatic::set_special_typing()

+ 4 - 14
Engine/source/T3D/tsStatic.h

@@ -163,8 +163,6 @@ protected:
    bool buildExportPolyList(ColladaUtils::ExportData* exportData, const Box3F& box, const SphereF&);
    void buildConvex(const Box3F& box, Convex* convex);
 
-   bool setShapeAsset(const StringTableEntry shapeAssetId);
-
    bool _createShape();
 
    void _updatePhysics();
@@ -172,7 +170,7 @@ protected:
    void _renderNormals(ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat);
 
    void _onResourceChanged(const Torque::Path& path);
-   void _onAssetChanged();
+   void onShapeChanged();
 
    // ProcessObject
    virtual void processTick(const Move* move);
@@ -193,16 +191,14 @@ protected:
 
    Convex* mConvexList;
 
-   StringTableEntry  mShapeName;
+   DECLARE_SHAPEASSET(TSStatic, Shape, onShapeChanged);
+   DECLARE_SHAPEASSET_NET_SETGET(TSStatic, Shape, AdvancedStaticOptionsMask);
+
    U32               mShapeHash;
-   Resource<TSShape> mShape;
    Vector<S32> mCollisionDetails;
    Vector<S32> mLOSDetails;
    TSShapeInstance* mShapeInstance;
 
-   AssetPtr<ShapeAsset> mShapeAsset;
-   StringTableEntry mShapeAssetId;
-
    NetStringHandle   mSkinNameHandle;
    String            mAppliedSkinName;
 
@@ -242,8 +238,6 @@ public:
    DECLARE_CONOBJECT(TSStatic);
    static void initPersistFields();
    static void consoleInit();
-   static bool _setShapeAsset(void* obj, const char* index, const char* data);
-   static bool _setShapeName(void* obj, const char* index, const char* data);
    static bool _setFieldSkin(void* object, const char* index, const char* data);
    static const char* _getFieldSkin(void* object, const char* data);
 
@@ -268,10 +262,6 @@ public:
 
    bool allowPlayerStep() const { return mAllowPlayerStep; }
 
-   Resource<TSShape> getShape() const { return mShape; }
-   StringTableEntry getShapeFileName() { return mShapeName; }
-   void setShapeFileName(StringTableEntry shapeName) { mShapeName = shapeName; }
-
    TSShapeInstance* getShapeInstance() const { return mShapeInstance; }
 
    U32 getNumDetails();

+ 1 - 1
Engine/source/T3D/vehicles/vehicle.cpp

@@ -208,7 +208,7 @@ bool VehicleData::preload(bool server, String &errorStr)
    if (!collisionDetails.size() || collisionDetails[0] == -1)
    {
       Con::errorf("VehicleData::preload failed: Vehicle models must define a collision-1 detail");
-      errorStr = String::ToString("VehicleData: Couldn't load shape \"%s\"", mShapeName);
+      errorStr = String::ToString("VehicleData: Couldn't load shape asset \"%s\"", mShapeAsset.getAssetId());
       return false;
    }
 

+ 17 - 19
Engine/source/T3D/vehicles/wheeledVehicle.cpp

@@ -75,8 +75,8 @@ ConsoleDocClass( WheeledVehicleTire,
 
 WheeledVehicleTire::WheeledVehicleTire()
 {
-   shape = 0;
-   shapeName = "";
+   INIT_SHAPEASSET(Shape);
+
    staticFriction = 1;
    kineticFriction = 0.5f;
    restitution = 1;
@@ -94,21 +94,17 @@ bool WheeledVehicleTire::preload(bool server, String &errorStr)
 {
    // Load up the tire shape.  ShapeBase has an option to force a
    // CRC check, this is left out here, but could be easily added.
-   if (shapeName && shapeName[0]) 
+   if (!mShape)
+   {
+      errorStr = String::ToString("WheeledVehicleTire: Couldn't load shape \"%s\"", mShapeAssetId);
+      return false;
+   }
+   else
    {
-
-      // Load up the shape resource
-      shape = ResourceManager::get().load(shapeName);
-      if (!bool(shape)) 
-      {
-         errorStr = String::ToString("WheeledVehicleTire: Couldn't load shape \"%s\"",shapeName);
-         return false;
-      }
-
       // Determinw wheel radius from the shape's bounding box.
       // The tire should be built with it's hub axis along the
       // object's Y axis.
-      radius = shape->mBounds.len_z() / 2;
+      radius = mShape->mBounds.len_z() / 2;
    }
 
    return true;
@@ -116,8 +112,8 @@ bool WheeledVehicleTire::preload(bool server, String &errorStr)
 
 void WheeledVehicleTire::initPersistFields()
 {
-   addField( "shapeFile",TypeShapeFilename,Offset(shapeName,WheeledVehicleTire),
-      "The path to the shape to use for the wheel." );
+   INITPERSISTFIELD_SHAPEASSET(Shape, WheeledVehicleTire, "The shape to use for the wheel.");
+
    addField( "mass", TypeF32, Offset(mass, WheeledVehicleTire),
       "The mass of the wheel.\nCurrently unused." );
    addField( "radius", TypeF32, Offset(radius, WheeledVehicleTire),
@@ -181,7 +177,8 @@ void WheeledVehicleTire::packData(BitStream* stream)
 {
    Parent::packData(stream);
 
-   stream->writeString(shapeName);
+   PACKDATA_SHAPEASSET(Shape);
+
    stream->write(mass);
    stream->write(staticFriction);
    stream->write(kineticFriction);
@@ -199,7 +196,8 @@ void WheeledVehicleTire::unpackData(BitStream* stream)
 {
    Parent::unpackData(stream);
 
-   shapeName = stream->readSTString();
+   UNPACKDATA_SHAPEASSET(Shape);
+
    stream->read(&mass);
    stream->read(&staticFriction);
    stream->read(&kineticFriction);
@@ -1542,8 +1540,8 @@ void WheeledVehicle::unpackUpdate(NetConnection *con, BitStream *stream)
 
             // Create an instance of the tire for rendering
             delete wheel->shapeInstance;
-            wheel->shapeInstance = (wheel->tire->shape == NULL) ? 0:
-               new TSShapeInstance(wheel->tire->shape);
+            wheel->shapeInstance = (wheel->tire->mShape == NULL) ? 0:
+               new TSShapeInstance(wheel->tire->mShape);
          }
       }
    }

+ 6 - 3
Engine/source/T3D/vehicles/wheeledVehicle.h

@@ -31,6 +31,8 @@
 #include "collision/clippedPolyList.h"
 #endif
 
+#include "T3D/assets/ShapeAsset.h"
+
 class ParticleEmitter;
 class ParticleEmitterData;
 
@@ -41,8 +43,8 @@ struct WheeledVehicleTire: public SimDataBlock
 {
    typedef SimDataBlock Parent;
 
-   //
-   StringTableEntry shapeName;// Max shape to render
+   DECLARE_SHAPEASSET(WheeledVehicleTire, Shape, onShapeChanged);
+   DECLARE_SHAPEASSET_SETGET(WheeledVehicleTire, Shape);
 
    // Physical properties
    F32 mass;                  // Mass of the whole wheel
@@ -62,7 +64,6 @@ struct WheeledVehicleTire: public SimDataBlock
    F32 longitudinalRelaxation;
 
    // Shape information initialized in the preload
-   Resource<TSShape> shape;   // The loaded shape
    F32 radius;                // Tire radius
 
    //
@@ -72,6 +73,8 @@ struct WheeledVehicleTire: public SimDataBlock
    bool preload(bool, String &errorStr);
    virtual void packData(BitStream* stream);
    virtual void unpackData(BitStream* stream);
+
+   void onShapeChanged() {}
 };
 
 

+ 1 - 1
Engine/source/afx/ce/afxStaticShape.h

@@ -86,7 +86,7 @@ public:
   virtual U32           packUpdate(NetConnection*, U32, BitStream*);
   virtual void          unpackUpdate(NetConnection*, BitStream*);
 
-  const char*           getShapeFileName() const { return mDataBlock->mShapeName; }
+  const char*           getShapeFileName() const { return mDataBlock->mShapeAsset->getShapeFileName(); }
   void                  setVisibility(bool flag) { mIs_visible = flag; }
 
   DECLARE_CONOBJECT(afxStaticShape);

+ 3 - 3
Engine/source/afx/util/afxParticlePool_T3D.cpp

@@ -146,7 +146,7 @@ void afxParticlePool::pool_renderObject_Normal(RenderPassManager *renderManager,
   if (main_emitter_data->textureHandle)
     ri->diffuseTex = &*(main_emitter_data->textureHandle);
   else
-    ri->diffuseTex = &*(main_emitter_data->particleDataBlocks[0]->textureHandle);
+    ri->diffuseTex = &*(main_emitter_data->particleDataBlocks[0]->getTextureResource());
 
   ri->softnessDistance = main_emitter_data->softnessDistance;
 
@@ -277,7 +277,7 @@ void afxParticlePool::pool_renderObject_TwoPass(RenderPassManager *renderManager
   //if (main_emitter_data->textureHandle)
   //  ri->diffuseTex = &*(main_emitter_data->textureHandle);
   //else
-    ri->diffuseTex = &*(main_emitter_data->particleDataBlocks[0]->textureExtHandle);
+    ri->diffuseTex = &*(main_emitter_data->particleDataBlocks[0]->getTextureExtResource());
 
   F32 save_sort_dist = ri->sortDistSq;
 
@@ -481,7 +481,7 @@ void afxParticlePool::pool_renderObject_TwoPass(RenderPassManager *renderManager
   if (main_emitter_data->textureHandle)
     ri->diffuseTex = &*(main_emitter_data->textureHandle);
   else
-    ri->diffuseTex = &*(main_emitter_data->particleDataBlocks[0]->textureHandle);
+    ri->diffuseTex = &*(main_emitter_data->particleDataBlocks[0]->getTextureResource());
 
   ri->softnessDistance = main_emitter_data->softnessDistance;
 

+ 28 - 1
Engine/source/assets/assetBase.cpp

@@ -76,6 +76,7 @@ mAssetInitialized(false)
    mInternalName = StringTable->EmptyString();
    mClassName = StringTable->EmptyString();
    mSuperClassName = StringTable->EmptyString();
+   mLoadedState = AssetErrCode::NotLoaded;
 }
 
 //-----------------------------------------------------------------------------
@@ -315,6 +316,32 @@ S32 AssetBase::getAssetDependencyFieldCount(const char* pFieldName)
 
 //-----------------------------------------------------------------------------
 
+StringTableEntry AssetBase::getAssetDependencyField(const char* pFieldName, S32 index)
+{
+   SimFieldDictionary* fieldDictionary = getFieldDictionary();
+   for (SimFieldDictionaryIterator itr(fieldDictionary); *itr; ++itr)
+   {
+      SimFieldDictionary::Entry* entry = *itr;
+
+      String slotName = String(entry->slotName);
+
+      if (slotName.startsWith(pFieldName))
+      {
+         S32 trailingNum;
+         String::GetTrailingNumber(slotName.c_str(), trailingNum);
+
+         if (trailingNum == index)
+         {
+            return StringTable->insert(String(entry->value).replace(ASSET_ID_FIELD_PREFIX, "").c_str());
+         }
+      }
+   }
+
+   return StringTable->EmptyString();
+}
+
+//-----------------------------------------------------------------------------
+
 void AssetBase::clearAssetDependencyFields(const char* pFieldName)
 {
    SimFieldDictionary* fieldDictionary = getFieldDictionary();
@@ -340,7 +367,7 @@ void AssetBase::addAssetDependencyField(const char* pFieldName, const char* pAss
    dSprintf(depSlotName, sizeof(depSlotName), "%s%d", pFieldName, existingFieldCount);
 
    char depValue[255];
-   dSprintf(depValue, sizeof(depValue), "@Asset=%s", pAssetId);
+   dSprintf(depValue, sizeof(depValue), "%s=%s", ASSET_ID_SIGNATURE, pAssetId);
 
    setDataField(StringTable->insert(depSlotName), NULL, StringTable->insert(depValue));
 }

+ 8 - 1
Engine/source/assets/assetBase.h

@@ -66,6 +66,7 @@ protected:
    bool                    mAssetInitialized;
    AssetDefinition*        mpAssetDefinition;
    U32                     mAcquireReferenceCount;
+   U32                     mLoadedState;
 
 public:
    enum AssetErrCode
@@ -87,7 +88,7 @@ public:
       if (errCode > AssetErrCode::Extended) return "undefined error";
       return mErrCodeStrings[errCode];
    };
-
+   U32 getStatus() { return mLoadedState; };
    AssetBase();
    virtual ~AssetBase();
 
@@ -124,6 +125,7 @@ public:
    void                    refreshAsset(void);
 
    S32 getAssetDependencyFieldCount(const char* pFieldName);
+   StringTableEntry getAssetDependencyField(const char* pFieldName, S32 index = 0);
    void clearAssetDependencyFields(const char* pFieldName);
    void addAssetDependencyField(const char* pFieldName, const char* pAssetId);
 
@@ -167,5 +169,10 @@ private:
    void                    setOwned(AssetManager* pAssetManager, AssetDefinition* pAssetDefinition);
 };
 
+//helper macro for stitching string and non string values togeather sans quotes
+#define assetText(x,suff) #x#suff
+#define macroText(x) #x
+#define assetDoc(x,suff) "@brief "#x" "#suff
+
 #endif // _ASSET_BASE_H_
 

+ 21 - 0
Engine/source/assets/assetBase_ScriptBinding.h

@@ -50,6 +50,15 @@ DefineEngineMethod(AssetBase, getAssetDependencyFieldCount, S32, (const char* pF
    return object->getAssetDependencyFieldCount(pFieldName);
 }
 
+DefineEngineMethod(AssetBase, getAssetDependencyField, const char*, (const char* pFieldName, S32 index), ("", 0),
+   "Gets an asset dependency field to the asset definition at a given index.\n"
+   "@param fieldName The name of the field.\n"
+   "@param index The index of the field to look up in the event there are multiple dependency fields. Defaults to 0"
+   "@return The assetID assigned to the given dependency field.\n")
+{
+   return object->getAssetDependencyField(pFieldName, index);
+}
+
 DefineEngineMethod(AssetBase, clearAssetDependencyFields, void, (const char* pFieldName), (""),
    "Clears any asset dependency fields matching the name provided.\n"
    "@param fieldName The name of the fields to be cleared")
@@ -71,3 +80,15 @@ DefineEngineMethod(AssetBase, saveAsset, bool, (), ,
 {
    return object->saveAsset();
 }
+
+DefineEngineMethod(AssetBase, getStatus, S32, (), , "get status")\
+{
+   return object->getStatus();
+}
+
+DefineEngineMethod(AssetBase, getStatusString, const char*, (), ,
+   "Returns the load status of the asset.\n"
+   "@return What status code the asset had after being loaded.\n")
+{
+   return object->getAssetErrstrn(object->getStatus());
+}

+ 1 - 1
Engine/source/cinterface/c_simobjectInterface.cpp

@@ -53,7 +53,7 @@ DefineNewEngineMethod(SimObject, InspectPreApply, void, (), , "")
    object->inspectPreApply();
 }
 
-DefineNewEngineMethod(SimObject, InspectPostApply, void, (), , "")
+DefineEngineMethod(SimObject, InspectPostApply, void, (), , "")
 {
    object->inspectPostApply();
 }

+ 8 - 0
Engine/source/console/consoleFunctions.cpp

@@ -2801,3 +2801,11 @@ DefineEngineFunction( getMaxDynamicVerts, S32, (),,
 {
    return GFX_MAX_DYNAMIC_VERTS / 2;
 }
+
+DefineEngineFunction( getStringHash, S32, (const char* _inString, bool _sensitive), ("", true), "generate a hash from a string. foramt is (string, casesensitive). defaults to true")
+{
+   if (_sensitive)
+      return S32(String::String(_inString).getHashCaseSensitive());
+   else
+      return S32(String::String(_inString).getHashCaseInsensitive());
+}

+ 41 - 20
Engine/source/console/consoleTypes.cpp

@@ -159,7 +159,7 @@ ConsoleProcessData( TypeFilename )
 //-----------------------------------------------------------------------------
 // TypeStringFilename
 //-----------------------------------------------------------------------------
-ConsolePrepType( filename, TypeStringFilename, String )
+ConsolePrepType( filename, TypeStringFilename, const char* )
 
 ConsoleSetType( TypeStringFilename )
 {
@@ -177,7 +177,7 @@ ConsoleSetType( TypeStringFilename )
          return;
       }
 
-      *((String*)dptr) = String(buffer);
+      *((const char**)dptr) = StringTable->insert(buffer);
    }
    else
       Con::printf("(TypeStringFilename) Cannot set multiple args to a single filename.");
@@ -185,7 +185,7 @@ ConsoleSetType( TypeStringFilename )
 
 ConsoleGetType( TypeStringFilename )
 {
-   return *((String*)dptr);
+   return *((const char**)(dptr));
 }
 
 ConsoleProcessData( TypeStringFilename )
@@ -204,7 +204,7 @@ ConsoleProcessData( TypeStringFilename )
 //-----------------------------------------------------------------------------
 // TypePrefabFilename
 //-----------------------------------------------------------------------------
-ConsolePrepType( filename, TypePrefabFilename, String )
+ConsolePrepType( filename, TypePrefabFilename, const char* )
 
 ConsoleSetType( TypePrefabFilename )
 {
@@ -213,7 +213,7 @@ ConsoleSetType( TypePrefabFilename )
 
 ConsoleGetType( TypePrefabFilename )
 {
-   return *((String*)dptr);
+   return *((const char**)(dptr));
 }
 
 ConsoleProcessData( TypePrefabFilename )
@@ -232,16 +232,16 @@ ConsoleProcessData( TypePrefabFilename )
 //-----------------------------------------------------------------------------
 // TypeImageFilename
 //-----------------------------------------------------------------------------
-ConsolePrepType( filename, TypeImageFilename, String )
+ConsolePrepType( filename, TypeImageFilename, const char* )
 
 ConsoleSetType( TypeImageFilename )
 {
-   Con::setData(TypeStringFilename, dptr, 0, argc, argv, tbl, flag);
+   Con::setData(TypeFilename, dptr, 0, argc, argv, tbl, flag);
 }
 
 ConsoleGetType( TypeImageFilename )
 {
-   return *((String*)dptr);
+   return *((const char**)(dptr));
 }
 
 ConsoleProcessData( TypeImageFilename )
@@ -281,6 +281,33 @@ ConsoleProcessData( TypeShapeFilename )
    }
 }
 
+//-----------------------------------------------------------------------------
+// TypeSoundFilename
+//-----------------------------------------------------------------------------
+ConsolePrepType(filename, TypeSoundFilename, const char*)
+
+ConsoleSetType(TypeSoundFilename)
+{
+   Con::setData(TypeFilename, dptr, 0, argc, argv, tbl, flag);
+}
+
+ConsoleGetType(TypeSoundFilename)
+{
+   return *((const char **)(dptr));
+}
+
+ConsoleProcessData(TypeSoundFilename)
+{
+   if (Con::expandScriptFilename(buffer, bufferSz, data))
+      return buffer;
+   else
+   {
+      Con::warnf("(TypeSoundFilename) illegal filename detected: %s", data);
+      return data;
+   }
+}
+
+
 //-----------------------------------------------------------------------------
 // TypeS8
 //-----------------------------------------------------------------------------
@@ -797,20 +824,17 @@ ConsoleSetType( TypeParticleParameterString )
 // TypeMaterialName
 //-----------------------------------------------------------------------------
 
-ConsoleType(string, TypeMaterialName, String, "")
+ConsoleType(string, TypeMaterialName, const char*, "")
 
 ConsoleGetType( TypeMaterialName )
 {
-   const String *theString = static_cast<const String*>(dptr);
-   return theString->c_str();
+   return* ((const char**)(dptr));
 }
 
 ConsoleSetType( TypeMaterialName )
 {
-   String *theString = static_cast<String*>(dptr);
-
    if(argc == 1)
-      *theString = argv[0];
+      *((const char**)dptr) = StringTable->insert(argv[0]);
    else
       Con::printf("(TypeMaterialName) Cannot set multiple args to a single string.");
 }
@@ -860,20 +884,17 @@ ConsoleSetType( TypeTerrainMaterialName )
 // TypeCubemapName
 //-----------------------------------------------------------------------------
 
-ConsoleType(string, TypeCubemapName, String, "")
+ConsoleType(string, TypeCubemapName, const char*, "")
 
 ConsoleGetType( TypeCubemapName )
 {
-   const String *theString = static_cast<const String*>(dptr);
-   return theString->c_str();
+   return*((const char**)(dptr));
 }
 
 ConsoleSetType( TypeCubemapName )
 {
-   String *theString = static_cast<String*>(dptr);
-
    if(argc == 1)
-      *theString = argv[0];
+      *((const char**)dptr) = StringTable->insert(argv[0]);
    else
       Con::printf("(TypeCubemapName) Cannot set multiple args to a single string.");
 }

+ 11 - 6
Engine/source/console/consoleTypes.h

@@ -73,7 +73,7 @@ DefineConsoleType( TypeCaseString, const char * )
 DefineConsoleType( TypeRealString, String )
 DefineConsoleType( TypeCommand, String )
 DefineConsoleType( TypeFilename, const char * )
-DefineConsoleType( TypeStringFilename, String )
+DefineConsoleType( TypeStringFilename, const char*)
 
 DefineConsoleType(TypeRotationF, RotationF)
 
@@ -87,22 +87,27 @@ DefineUnmappedConsoleType( TypePID, SimPersistID* );
 /// TypeImageFilename is equivalent to TypeStringFilename in its usage,
 /// it exists for the benefit of GuiInspector, which will provide a custom
 /// InspectorField for this type that can display a texture preview.
-DefineConsoleType( TypeImageFilename, String )
+DefineConsoleType( TypeImageFilename, const char* )
 
 /// TypePrefabFilename is equivalent to TypeStringFilename in its usage,
 /// it exists for the benefit of GuiInspector, which will provide a 
 /// custom InspectorField for this type.
-DefineConsoleType( TypePrefabFilename, String )
+DefineConsoleType( TypePrefabFilename, const char*)
 
 /// TypeShapeFilename is equivalent to TypeStringFilename in its usage,
 /// it exists for the benefit of GuiInspector, which will provide a 
 /// custom InspectorField for this type.
-DefineConsoleType( TypeShapeFilename, String )
+DefineConsoleType( TypeShapeFilename, const char* )
+
+/// TypeSoundFilename is exactly the same as TypeShapeFilename 
+/// it exists for the benefit of GuiInspector, which will provide a 
+/// custom InspectorField for this type.
+DefineConsoleType(TypeSoundFilename, const char*)
 
 /// TypeMaterialName is equivalent to TypeRealString in its usage,
 /// it exists for the benefit of GuiInspector, which will provide a 
 /// custom InspectorField for this type.
-DefineConsoleType( TypeMaterialName, String )
+DefineConsoleType( TypeMaterialName, const char*)
 
 /// TypeTerrainMaterialIndex is equivalent to TypeS32 in its usage,
 /// it exists for the benefit of GuiInspector, which will provide a 
@@ -116,7 +121,7 @@ DefineConsoleType( TypeTerrainMaterialName, const char * )
 
 /// TypeCubemapName is equivalent to TypeRealString in its usage,
 /// but the Inspector will provide a drop-down list of CubemapData objects.
-DefineConsoleType( TypeCubemapName, String )
+DefineConsoleType( TypeCubemapName, const char*)
 
 DefineConsoleType( TypeParticleParameterString, const char * )
 

+ 36 - 0
Engine/source/console/fileSystemFunctions.cpp

@@ -398,6 +398,21 @@ DefineEngineFunction(isFile, bool, ( const char* fileName ),,
    return Torque::FS::IsFile(givenPath);
 }
 
+DefineEngineFunction(isScriptFile, bool, (const char* fileName), ,
+   "@brief Determines if the specified file exists or not\n\n"
+
+   "@param fileName The path to the file.\n"
+   "@return Returns true if the file was found.\n"
+
+   "@ingroup FileSystem")
+{
+   String cleanfilename(Torque::Path::CleanSeparators(fileName));
+   Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), cleanfilename.c_str());
+
+   Torque::Path givenPath(Torque::Path::CompressPath(sgScriptFilenameBuffer));
+   return Torque::FS::IsScriptFile(givenPath.getFullPath());
+}
+
 DefineEngineFunction( IsDirectory, bool, ( const char* directory ),,
    "@brief Determines if a specified directory exists or not\n\n"
 
@@ -565,6 +580,27 @@ DefineEngineFunction( fileCreatedTime, String, ( const char* fileName ),,
    return buffer;
 }
 
+DefineEngineFunction(compareFileTimes, S32, (const char* fileA, const char* fileB), ("", ""),
+   "@brief Compares 2 files' modified file times."
+
+   "@param fileName Name and path of first file to compare\n"
+   "@param fileName Name and path of second file to compare\n"
+   "@return S32. If value is 1, then fileA is newer. If value is -1, then fileB is newer. If value is 0, they are equal.\n"
+   "@ingroup FileSystem")
+{
+   Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), fileA);
+
+   FileTime fileATime = { 0 };
+   Platform::getFileTimes(sgScriptFilenameBuffer, NULL, &fileATime);
+
+   Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), fileB);
+
+   FileTime fileBTime = { 0 };
+   Platform::getFileTimes(sgScriptFilenameBuffer, NULL, &fileBTime);
+
+   return Platform::compareFileTimes(fileATime, fileBTime);
+}
+
 DefineEngineFunction(fileDelete, bool, ( const char* path ),,
    "@brief Delete a file from the hard drive\n\n"
 

+ 25 - 19
Engine/source/console/persistenceManager.cpp

@@ -1254,17 +1254,7 @@ PersistenceManager::ParsedObject* PersistenceManager::writeNewObject(SimObject*
        dynamic_cast<TSShapeConstructor*>(object))
       dclToken = "singleton";
    else if( dynamic_cast< SimDataBlock* >( object ) )
-   {
-      SimDataBlock* db = static_cast<SimDataBlock*>(object);
-
-      if( db->isClientOnly() )
-      {
-         if( db->getName() && db->getName()[ 0 ] )
-            dclToken = "singleton";
-      }
-      else
-         dclToken = "datablock";
-   }
+      dclToken = "datablock";
 
    char newLine[ 4096 ];
    dMemset(newLine, 0, sizeof( newLine));
@@ -1416,17 +1406,25 @@ void PersistenceManager::updateObject(SimObject* object, ParsedObject* parentObj
                {
                   // TODO: This should be wrapped in a helper method... probably.
                   // Detect and collapse relative path information
-                  if (f->type == TypeFilename ||
-                     f->type == TypeStringFilename ||
-                     f->type == TypeImageFilename ||
-                     f->type == TypePrefabFilename ||
-                     f->type == TypeShapeFilename)
+                  if (f->type == TypeFilename       ||
+                      f->type == TypeStringFilename ||
+                      f->type == TypeImageFilename  ||
+                      f->type == TypePrefabFilename ||
+                      f->type == TypeShapeFilename  ||
+                      f->type == TypeSoundFilename )
                   {
                      char fnBuf[1024];
                      Con::collapseScriptFilename(fnBuf, 1024, value);
 
                      updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, fnBuf, true);
                   }
+                  else if (f->type == TypeCommand || f->type == TypeString || f->type == TypeRealString)
+                  {
+                     char cmdBuf[1024];
+                     expandEscape(cmdBuf, value);
+
+                     updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, cmdBuf, true);
+                  }
                   else
                      updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, value, true);
                }
@@ -1495,17 +1493,25 @@ void PersistenceManager::updateObject(SimObject* object, ParsedObject* parentObj
             {
                // TODO: This should be wrapped in a helper method... probably.
                // Detect and collapse relative path information
-               if (f->type == TypeFilename ||
+               if (f->type == TypeFilename       ||
                    f->type == TypeStringFilename ||
-                   f->type == TypeImageFilename ||
+                   f->type == TypeImageFilename  ||
                    f->type == TypePrefabFilename ||
-                   f->type == TypeShapeFilename)
+                   f->type == TypeShapeFilename  ||
+                   f->type == TypeSoundFilename )
                {
                   char fnBuf[1024];
                   Con::collapseScriptFilename(fnBuf, 1024, value);
 
                   newLines.push_back(createNewProperty(f->pFieldname, fnBuf, f->elementCount > 1, j));
                }
+               else if (f->type == TypeCommand)
+               {
+                  char cmdBuf[1024];
+                  expandEscape(cmdBuf, value);
+
+                  newLines.push_back(createNewProperty(f->pFieldname, cmdBuf, f->elementCount > 1, j));
+               }
                else
                   newLines.push_back(createNewProperty(f->pFieldname, value, f->elementCount > 1, j));              
             }

+ 38 - 4
Engine/source/console/simObject.cpp

@@ -339,11 +339,12 @@ void SimObject::writeFields(Stream &stream, U32 tabStop)
 
          // detect and collapse relative path information
          char fnBuf[1024];
-         if (f->type == TypeFilename ||
+         if (f->type == TypeFilename       ||
              f->type == TypeStringFilename ||
-             f->type == TypeImageFilename ||
+             f->type == TypeImageFilename  ||
              f->type == TypePrefabFilename ||
-             f->type == TypeShapeFilename)
+             f->type == TypeShapeFilename  ||
+             f->type == TypeSoundFilename )
          {
             Con::collapseScriptFilename(fnBuf, 1024, val);
             val = fnBuf;
@@ -919,7 +920,15 @@ void SimObject::assignFieldsFrom(SimObject *parent)
             dMemset( bufferSecure, 0, 2048 );
             dMemcpy( bufferSecure, szBuffer, dStrlen( szBuffer ) );
 
-            if((*f->setDataFn)( this, NULL, bufferSecure ) )
+            //If we have an index worth mentioning, process it for pass-along as well to ensure we set stuff correctly
+            char* elementIdxBuffer = nullptr;
+            if (f->elementCount > 1)
+            {
+               elementIdxBuffer = Con::getArgBuffer(256);
+               dSprintf(elementIdxBuffer, 256, "%i", j);
+            }
+
+            if((*f->setDataFn)( this, elementIdxBuffer, bufferSecure ) )
                Con::setData(f->type, (void *) (((const char *)this) + f->offset), j, 1, &fieldVal, f->table);
 
             if (f->networkMask != 0)
@@ -3176,6 +3185,31 @@ DefineEngineMethod( SimObject, getField, const char*, ( S32 index ),,
    return "";
 }
 
+DefineEngineFunction(getClassHierarchy, const char*, (const char* name), ,
+   "Returns the inheritance hierarchy for a given class.")
+{
+   AbstractClassRep* pRep = AbstractClassRep::findClassRep(name);
+   if (!pRep)
+   {
+      //Con::errorf("%s does not exist", name);
+      return StringTable->EmptyString();
+   }
+
+   StringBuilder buffer;
+
+   while (pRep != NULL)
+   {
+      StringTableEntry className = pRep->getClassName();
+      buffer.append(className);
+      buffer.append(" ");
+      pRep = pRep->getParentClass();
+   }
+
+   String result = buffer.end().trim();
+   //Con::printf("getClassHierarchy for %s=%s", name, result.c_str());
+   return Con::getReturnBuffer(result.c_str());
+
+}
 //-----------------------------------------------------------------------------
 
 #ifdef TORQUE_DEBUG

+ 28 - 3
Engine/source/core/fileObject.cpp

@@ -177,7 +177,7 @@ const U8 *FileObject::readLine()
    return mFileBuffer + tokPos;
 }
 
-void FileObject::peekLine( U8* line, S32 length )
+void FileObject::peekLine( S32 peekLineOffset, U8* line, S32 length )
 {
    if(!mFileBuffer)
    {
@@ -189,6 +189,31 @@ void FileObject::peekLine( U8* line, S32 length )
    // we can't modify the file buffer.
    S32 i = 0;
    U32 tokPos = mCurPos;
+   S32 lineOffset = 0;
+
+   //Lets push our tokPos up until we've offset the requested number of lines
+   while (lineOffset < peekLineOffset && tokPos <= mBufferSize)
+   {
+      if (mFileBuffer[tokPos] == '\r')
+      {
+         tokPos++;
+         if (mFileBuffer[tokPos] == '\n')
+            tokPos++;
+         lineOffset++;
+         continue;
+      }
+
+      if (mFileBuffer[tokPos] == '\n')
+      {
+         tokPos++;
+         lineOffset++;
+         continue;
+      }
+
+      tokPos++;
+   }
+
+   //now peek that line, then return the results
    while( ( tokPos != mBufferSize ) && ( mFileBuffer[tokPos] != '\r' ) && ( mFileBuffer[tokPos] != '\n' ) && ( i < ( length - 1 ) ) )
       line[i++] = mFileBuffer[tokPos++];
 
@@ -317,7 +342,7 @@ DefineEngineMethod( FileObject, readLine, const char*, (),,
 	return (const char *) object->readLine();
 }
 
-DefineEngineMethod( FileObject, peekLine, const char*, (),,
+DefineEngineMethod( FileObject, peekLine, const char*, (S32 peekOffset), (0),
    "@brief Read a line from the file without moving the stream position.\n\n"
    
    "Emphasis on *line*, as in you cannot parse individual characters or chunks of data.  "
@@ -345,7 +370,7 @@ DefineEngineMethod( FileObject, peekLine, const char*, (),,
 {
 	static const U32 bufSize = 512;
 	char *line = Con::getReturnBuffer( bufSize );
-	object->peekLine( (U8*)line, bufSize );
+	object->peekLine(peekOffset, (U8*)line, bufSize );
 	return line;
 }
 

+ 1 - 1
Engine/source/core/fileObject.h

@@ -46,7 +46,7 @@ public:
    bool readMemory(const char *fileName);
    const U8 *buffer() { return mFileBuffer; }
    const U8 *readLine();
-   void peekLine(U8 *line, S32 length);
+   void peekLine(S32 peekLineOffset, U8 *line, S32 length);
    bool isEOF();
    void writeLine(const U8 *line);
    void close();

+ 34 - 4
Engine/source/core/stream/bitStream.cpp

@@ -344,22 +344,52 @@ void BitStream::writeInt(S32 val, S32 bitCount)
 
 void BitStream::writeFloat(F32 f, S32 bitCount)
 {
-   writeInt((S32)(f * ((1 << bitCount) - 1)), bitCount);
+   auto maxInt = (1U << bitCount) - 1;
+   U32 i;
+   if (f < POINT_EPSILON)
+   {
+      // Special case: <= 0 serializes to 0
+      i = 0.0f;
+   }
+   else if (f == 0.5)
+   {
+      // Special case: 0.5 serializes to maxInt / 2 + 1
+      i = maxInt / 2 + 1;
+   }
+   else if (f > (1.0f- POINT_EPSILON))
+   {
+      // Special case: >= 1 serializes to maxInt
+      i = maxInt;
+   }
+   else
+   {
+      // Serialize normally but round the number
+      i = static_cast<U32>(roundf(f * maxInt));
+   }
+   writeInt(i, bitCount);
 }
 
 F32 BitStream::readFloat(S32 bitCount)
 {
-   return readInt(bitCount) / F32((1 << bitCount) - 1);
+   auto maxInt = (1U << bitCount) - 1;
+   auto i = static_cast<U32>(readInt(bitCount));
+   if (i == 0)
+      return 0;
+   if (i == maxInt / 2 + 1)
+      return 0.5;
+   if (i == maxInt)
+      return 1;
+   return i / static_cast<F32>(maxInt);
 }
 
 void BitStream::writeSignedFloat(F32 f, S32 bitCount)
 {
-   writeInt((S32)(((f + 1) * .5) * ((1 << bitCount) - 1)), bitCount);
+   writeFloat((f + 1) / 2, bitCount);
 }
 
 F32 BitStream::readSignedFloat(S32 bitCount)
 {
-   return readInt(bitCount) * 2 / F32((1 << bitCount) - 1) - 1.0f;
+   return readFloat(bitCount) * 2 - 1;
 }
 
 void BitStream::writeSignedInt(S32 value, S32 bitCount)

+ 12 - 0
Engine/source/core/volume.cpp

@@ -1074,6 +1074,18 @@ bool IsFile(const Path &path)
    return sgMountSystem.isFile(path);
 }
 
+bool IsScriptFile(const char* pFilePath)
+{
+   return (sgMountSystem.isFile(pFilePath)
+      || sgMountSystem.isFile(pFilePath + String(".dso"))
+      || sgMountSystem.isFile(pFilePath + String(".mis"))
+      || sgMountSystem.isFile(pFilePath + String(".mis.dso"))
+      || sgMountSystem.isFile(pFilePath + String(".gui"))
+      || sgMountSystem.isFile(pFilePath + String(".gui.dso"))
+      || sgMountSystem.isFile(pFilePath + String("." TORQUE_SCRIPT_EXTENSION))
+      || sgMountSystem.isFile(pFilePath + String("." TORQUE_SCRIPT_EXTENSION) + String(".dso")));
+}
+
 bool IsDirectory(const Path &path)
 {
    return sgMountSystem.isDirectory(path);

+ 1 - 0
Engine/source/core/volume.h

@@ -550,6 +550,7 @@ bool CreatePath(const Path &path);
 bool IsReadOnly(const Path &path);
 bool IsDirectory(const Path &path);
 bool IsFile(const Path &path);
+bool IsScriptFile(const char* pFilePath);
 bool VerifyWriteAccess(const Path &path);
 
 /// This returns a unique file path from the components 

Some files were not shown because too many files changed in this diff