Explorar el Código

Converts all game, gui editor, and system classes to utilize assets
Processed core, tools and default modules to utilize assets
Converted all console types that were string based, such as TypeImageFilename to utilize const char*/the string table, which avoids a lot of type swapping shenanigans and avoids string corruption
Removed unneeded MainEditor mockup module
Removed some unused/duplicate image assets from the tools

Areloch hace 4 años
padre
commit
5525f8ecdd
Se han modificado 100 ficheros con 3335 adiciones y 1855 borrados
  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. 1 1
      Engine/source/T3D/assets/CubemapAsset.cpp
  5. 48 8
      Engine/source/T3D/assets/GUIAsset.cpp
  6. 2 0
      Engine/source/T3D/assets/GUIAsset.h
  7. 222 110
      Engine/source/T3D/assets/ImageAsset.cpp
  8. 475 82
      Engine/source/T3D/assets/ImageAsset.h
  9. 31 0
      Engine/source/T3D/assets/ImageAssetInspectors.h
  10. 40 36
      Engine/source/T3D/assets/LevelAsset.cpp
  11. 9 7
      Engine/source/T3D/assets/LevelAsset.h
  12. 126 111
      Engine/source/T3D/assets/MaterialAsset.cpp
  13. 196 132
      Engine/source/T3D/assets/MaterialAsset.h
  14. 1 1
      Engine/source/T3D/assets/ParticleAsset.cpp
  15. 65 71
      Engine/source/T3D/assets/ShapeAsset.cpp
  16. 408 72
      Engine/source/T3D/assets/ShapeAsset.h
  17. 164 26
      Engine/source/T3D/assets/SoundAsset.cpp
  18. 281 2
      Engine/source/T3D/assets/SoundAsset.h
  19. 3 7
      Engine/source/T3D/assets/TerrainAsset.cpp
  20. 34 8
      Engine/source/T3D/assets/TerrainMaterialAsset.cpp
  21. 1 1
      Engine/source/T3D/assets/TerrainMaterialAsset.h
  22. 293 198
      Engine/source/T3D/assets/assetImporter.cpp
  23. 14 1
      Engine/source/T3D/assets/assetImporter.h
  24. 2 2
      Engine/source/T3D/assets/assetImporter_ScriptBinding.h
  25. 1 1
      Engine/source/T3D/assets/stateMachineAsset.cpp
  26. 31 20
      Engine/source/T3D/convexShape.cpp
  27. 9 4
      Engine/source/T3D/convexShape.h
  28. 21 24
      Engine/source/T3D/debris.cpp
  29. 6 2
      Engine/source/T3D/debris.h
  30. 22 27
      Engine/source/T3D/decal/decalData.cpp
  31. 5 6
      Engine/source/T3D/decal/decalData.h
  32. 2 2
      Engine/source/T3D/decal/decalDataFile.cpp
  33. 2 2
      Engine/source/T3D/decal/decalManager.cpp
  34. 4 8
      Engine/source/T3D/examples/renderMeshExample.cpp
  35. 7 7
      Engine/source/T3D/examples/renderMeshExample.h
  36. 5 17
      Engine/source/T3D/examples/renderShapeExample.cpp
  37. 7 4
      Engine/source/T3D/examples/renderShapeExample.h
  38. 14 22
      Engine/source/T3D/fx/explosion.cpp
  39. 7 3
      Engine/source/T3D/fx/explosion.h
  40. 37 63
      Engine/source/T3D/fx/groundCover.cpp
  41. 7 5
      Engine/source/T3D/fx/groundCover.h
  42. 39 49
      Engine/source/T3D/fx/particle.cpp
  43. 13 7
      Engine/source/T3D/fx/particle.h
  44. 3 3
      Engine/source/T3D/fx/particleEmitter.cpp
  45. 37 14
      Engine/source/T3D/fx/precipitation.cpp
  46. 12 2
      Engine/source/T3D/fx/precipitation.h
  47. 10 7
      Engine/source/T3D/fx/splash.cpp
  48. 4 2
      Engine/source/T3D/fx/splash.h
  49. 1 1
      Engine/source/T3D/gameBase/gameConnection.cpp
  50. 1 1
      Engine/source/T3D/gameBase/gameConnection.h
  51. 22 14
      Engine/source/T3D/groundPlane.cpp
  52. 4 2
      Engine/source/T3D/groundPlane.h
  53. 4 4
      Engine/source/T3D/guiObjectView.cpp
  54. 3 1
      Engine/source/T3D/guiObjectView.h
  55. 15 14
      Engine/source/T3D/levelInfo.cpp
  56. 9 6
      Engine/source/T3D/levelInfo.h
  57. 10 11
      Engine/source/T3D/lightFlareData.cpp
  58. 9 3
      Engine/source/T3D/lightFlareData.h
  59. 5 5
      Engine/source/T3D/lighting/reflectionProbe.cpp
  60. 1 1
      Engine/source/T3D/lighting/reflectionProbe.h
  61. 24 22
      Engine/source/T3D/physics/physicsDebris.cpp
  62. 6 2
      Engine/source/T3D/physics/physicsDebris.h
  63. 29 32
      Engine/source/T3D/physics/physicsShape.cpp
  64. 6 5
      Engine/source/T3D/physics/physicsShape.h
  65. 19 15
      Engine/source/T3D/player.cpp
  66. 3 2
      Engine/source/T3D/player.h
  67. 9 11
      Engine/source/T3D/prefab.cpp
  68. 2 2
      Engine/source/T3D/prefab.h
  69. 23 24
      Engine/source/T3D/projectile.cpp
  70. 5 4
      Engine/source/T3D/projectile.h
  71. 1 1
      Engine/source/T3D/rigidShape.cpp
  72. 2 2
      Engine/source/T3D/sfx/sfxEmitter.cpp
  73. 25 75
      Engine/source/T3D/shapeBase.cpp
  74. 13 8
      Engine/source/T3D/shapeBase.h
  75. 41 50
      Engine/source/T3D/shapeImage.cpp
  76. 11 96
      Engine/source/T3D/tsStatic.cpp
  77. 4 14
      Engine/source/T3D/tsStatic.h
  78. 1 1
      Engine/source/T3D/vehicles/vehicle.cpp
  79. 17 19
      Engine/source/T3D/vehicles/wheeledVehicle.cpp
  80. 6 3
      Engine/source/T3D/vehicles/wheeledVehicle.h
  81. 1 1
      Engine/source/afx/ce/afxStaticShape.h
  82. 3 3
      Engine/source/afx/util/afxParticlePool_T3D.cpp
  83. 27 1
      Engine/source/assets/assetBase.cpp
  84. 8 1
      Engine/source/assets/assetBase.h
  85. 21 0
      Engine/source/assets/assetBase_ScriptBinding.h
  86. 8 0
      Engine/source/console/consoleFunctions.cpp
  87. 41 20
      Engine/source/console/consoleTypes.cpp
  88. 11 6
      Engine/source/console/consoleTypes.h
  89. 25 19
      Engine/source/console/persistenceManager.cpp
  90. 13 4
      Engine/source/console/simObject.cpp
  91. 24 64
      Engine/source/environment/VolumetricFog.cpp
  92. 11 8
      Engine/source/environment/VolumetricFog.h
  93. 4 9
      Engine/source/environment/basicClouds.cpp
  94. 4 2
      Engine/source/environment/basicClouds.h
  95. 18 26
      Engine/source/environment/cloudLayer.cpp
  96. 6 3
      Engine/source/environment/cloudLayer.h
  97. 19 18
      Engine/source/environment/decalRoad.cpp
  98. 5 4
      Engine/source/environment/decalRoad.h
  99. 10 10
      Engine/source/environment/editors/guiMeshRoadEditorCtrl.cpp
  100. 7 6
      Engine/source/environment/editors/guiMeshRoadEditorCtrl.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();

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

@@ -208,7 +208,7 @@ GuiControl* GuiInspectorTypeCubemapAssetPtr::constructEditControl()
    mShapeEdButton->setField("Command", szBuffer);
 
    char bitmapName[512] = "tools/worldEditor/images/toolbar/shape-editor";
-   mShapeEdButton->setBitmap(bitmapName);
+   mShapeEdButton->setBitmap(StringTable->insert(bitmapName));
 
    mShapeEdButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
    mShapeEdButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");

+ 48 - 8
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;
    }
@@ -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
 //-----------------------------------------------------------------------------
@@ -222,7 +262,7 @@ GuiControl* GuiInspectorTypeGUIAssetPtr::constructEditControl()
    mSMEdButton->setField("Command", szBuffer);
 
    char bitmapName[512] = "tools/worldEditor/images/toolbar/shape-editor";
-   mSMEdButton->setBitmap(bitmapName);
+   mSMEdButton->setBitmap(StringTable->insert(bitmapName));
 
    mSMEdButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
    mSMEdButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");

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

+ 222 - 110
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))
       {
          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);",
@@ -457,7 +509,7 @@ GuiControl* GuiInspectorTypeImageAssetPtr::constructEditControl()
    mImageEdButton->setField("Command", szBuffer);
 
    char bitmapName[512] = "tools/worldEditor/images/toolbar/shape-editor";
-   mImageEdButton->setBitmap(bitmapName);
+   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,

+ 475 - 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,464 @@ 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.));
 
-#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)\
+#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 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)

+ 126 - 111
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,12 +164,12 @@ void MaterialAsset::initializeAsset()
    // Call parent.
    Parent::initializeAsset();
 
-   compileShader();
-
    mScriptPath = getOwned() ? expandAssetFilePath(mScriptFile) : mScriptPath;
 
    if (Platform::isFile(mScriptPath))
       Con::executeFile(mScriptPath, false, false);
+
+   loadMaterial();
 }
 
 void MaterialAsset::onAssetRefresh()
@@ -167,17 +179,7 @@ void MaterialAsset::onAssetRefresh()
    if (Platform::isFile(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,141 +199,154 @@ 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;
+      }
+
+      //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;
+      }
 
-   //Didn't work, so have us fall back to a placeholder asset
-   StringTableEntry noImageId = StringTable->insert("Core_Rendering:noMaterial");
-   materialAsset->setAssetId(noImageId);
+      Con::warnf("MaterialAsset::getAssetById - Finding of asset with id %s failed, utilizing fallback asset", assetId);
 
-   if (!materialAsset->isNull())
-      return true;
+      (*materialAsset)->mLoadedState = AssetErrCode::UsingFallback;
+      return AssetErrCode::UsingFallback;
+   }
+}
 
-   return false;
+#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));
 }
+#endif
 
 //-----------------------------------------------------------------------------
 // GuiInspectorTypeAssetId
@@ -374,7 +389,7 @@ GuiControl* GuiInspectorTypeMaterialAssetPtr::constructEditControl()
    mEditButton->setField("Command", szBuffer);
 
    char bitmapName[512] = "tools/worldEditor/images/toolbar/material-editor";
-   mEditButton->setBitmap(bitmapName);
+   mEditButton->setBitmap(StringTable->insert(bitmapName));
 
    mEditButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
    mEditButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");

+ 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("classname::_set##name() - Material %s was not found.", 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(map));\
 }
 
-/// <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_
 

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

@@ -161,7 +161,7 @@ GuiControl* GuiInspectorTypeParticleAssetPtr::constructEditControl()
    mSMEdButton->setField("Command", szBuffer);
 
    char bitmapName[512] = "tools/worldEditor/images/toolbar/shape-editor";
-   mSMEdButton->setBitmap(bitmapName);
+   mSMEdButton->setBitmap(StringTable->insert(bitmapName));
 
    mSMEdButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
    mSMEdButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");

+ 65 - 71
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;
 }
 //------------------------------------------------------------------------------
 
@@ -561,11 +542,24 @@ DefineEngineMethod(ShapeAsset, getShapeFile, const char*, (), ,
    return object->getShapeFilePath();
 }
 
+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
 
 //-----------------------------------------------------------------------------
@@ -612,7 +606,7 @@ GuiControl* GuiInspectorTypeShapeAssetPtr::constructEditControl()
    mShapeEdButton->setField("Command", szBuffer);
 
    char bitmapName[512] = "tools/worldEditor/images/toolbar/shape-editor";
-   mShapeEdButton->setBitmap(bitmapName);
+   mShapeEdButton->setBitmap(StringTable->insert(bitmapName));
 
    mShapeEdButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
    mShapeEdButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");

+ 408 - 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,413 @@ 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;\
+   _set##name(StringTable->insert(ShapeAsset::smNoShapeAssetFallback));
+
+#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();\
+      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\
+      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;\
+   _set##name(StringTable->insert(ShapeAsset::smNoShapeAssetFallback), index);
 
+#ifdef TORQUE_SHOW_LEGACY_FILE_FIELDS
+
+#define INITPERSISTFIELD_SHAPEASSET_ARRAY(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_ARRAY(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_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 (!Platform::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_
 

+ 3 - 7
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;
    }
@@ -472,7 +468,7 @@ GuiControl* GuiInspectorTypeTerrainAssetPtr::constructEditControl()
    mShapeEdButton->setField("Command", "EditorGui.setEditor(TerrainEditorPlugin);");
 
    char bitmapName[512] = "tools/worldEditor/images/toolbar/shape-editor";
-   mShapeEdButton->setBitmap(bitmapName);
+   mShapeEdButton->setBitmap(StringTable->insert(bitmapName));
 
    mShapeEdButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
    mShapeEdButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");

+ 34 - 8
Engine/source/T3D/assets/TerrainMaterialAsset.cpp

@@ -119,8 +119,6 @@ void TerrainMaterialAsset::initializeAsset()
    // Call parent.
    Parent::initializeAsset();
 
-   compileShader();
-
    mScriptPath = expandAssetFilePath(mScriptFile);
 
    if (Platform::isFile(mScriptPath))
@@ -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
 //-----------------------------------------------------------------------------

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

+ 293 - 198
Engine/source/T3D/assets/assetImporter.cpp

@@ -36,6 +36,7 @@ AssetImportConfig::AssetImportConfig() :
    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"),
@@ -135,6 +136,7 @@ void AssetImportConfig::initPersistFields()
       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");
@@ -232,6 +234,7 @@ void AssetImportConfig::loadImportConfig(Settings* configSettings, String config
    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()));
@@ -321,6 +324,7 @@ void AssetImportConfig::CopyTo(AssetImportConfig* target) const
    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 (childAssetItem->skip || childAssetItem->assetType != String("ImageAsset"))
+               continue;
 
-               if (t == ImageAsset::ImageTypes::Albedo)
+            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");
 
-                        newImageAssetObj->imageSuffixType = ImageAsset::getImageTypeNameFromType((ImageAsset::ImageTypes)t);
+                        imagePath = AssetImporter::findImagePath(testPath);
 
-                        matchedImageTypes[t] = newImageAssetObj;
-                        break;
+                        if (imagePath.isNotEmpty())
+                        {
+                           //got a match!
+                           AssetImportObject* newImageAssetObj = addImportingAsset("ImageAsset", imagePath, assetItem, "");
+
+                           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
@@ -2284,7 +2351,7 @@ void AssetImporter::resolveAssetItemIssues(AssetImportObject* assetItem)
          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;
@@ -2348,7 +2415,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 +2426,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 +2435,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 +2445,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 +2478,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 +2695,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 +2768,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 +2826,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";
+               }
+
+               assetFieldName = mapFieldName + "Asset[0]";
+               mapFieldName += "[0]";
 
-         //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());
+               //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;
+               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 +3032,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 +3064,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 +3076,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);
 
@@ -3036,7 +3132,7 @@ Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem)
       TSShapeConstructor* constructor = TSShapeConstructor::findShapeConstructor(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 +3142,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;
 

+ 14 - 1
Engine/source/T3D/assets/assetImporter.h

@@ -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), (""),

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

@@ -196,7 +196,7 @@ GuiControl* GuiInspectorTypeStateMachineAssetPtr::constructEditControl()
    mSMEdButton->setField("Command", szBuffer);
 
    char bitmapName[512] = "tools/worldEditor/images/toolbar/shape-editor";
-   mSMEdButton->setBitmap(bitmapName);
+   mSMEdButton->setBitmap(StringTable->insert(bitmapName));
 
    mSMEdButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
    mSMEdButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");

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

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

+ 21 - 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,18 @@ 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;
       }
-
    }
 
    return true;
@@ -300,9 +295,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 +379,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 +423,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 +666,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() {}
 };
 
 

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

@@ -458,9 +458,9 @@ GroundCover::GroundCover()
 
    mRandomSeed = 1;
 
-   initMaterialAsset(Material);
+   INIT_MATERIALASSET(Material);
+   mMaterialInst = NULL;
 
-   mMatInst = NULL;
    mMatParams = NULL;
    mTypeRectsParam = NULL;
    mFadeParams = NULL;
@@ -519,7 +519,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 +531,7 @@ GroundCover::GroundCover()
 
 GroundCover::~GroundCover()
 {
-   SAFE_DELETE( mMatInst );
+   SAFE_DELETE( mMaterialInst );
 }
 
 IMPLEMENT_CO_NETOBJECT_V1(GroundCover);
@@ -539,7 +540,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 +560,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]" );
+         INITPERSISTFIELD_SHAPEASSET_ARRAY(Shape, GroundCover, "The cover shape. [Optional]");
+         addField( "shapeFilename", TypeFilename,  Offset( mShapeName, GroundCover ), MAX_COVERTYPES,  "The cover shape filename. [Optional]", AbstractClassRep::FIELD_HideInInspectors );
 
          addField( "layer",         TypeTerrainMaterialName, Offset( mLayer, GroundCover ), MAX_COVERTYPES, "Terrain material name to limit coverage to, or blank to not limit." );
 
@@ -710,7 +712,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 +743,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 +765,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 +783,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 +814,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 +836,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 +856,8 @@ 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());
-
-      if (mMaterial)
-         mMatInst = mMaterial->createMatInstance();
-      else
-         mMatInst = MATMGR->createMatInstance("WarningMaterial");
-
-      if (!mMatInst)
-         Con::errorf("GroundCover::_initMaterial - no Material called '%s'", mMaterialAsset->getMaterialDefinitionName());
-   }
-   else
-   {
+   if (!mMaterialInst)
       return;
-   }
    
    // Add our special feature that makes it all work...
    FeatureSet features = MATMGR->getDefaultFeatures();
@@ -883,10 +865,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 +877,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 +956,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");
+         if (mat->mDiffuseMapName[0] != StringTable->EmptyString())
+            tex = GFXTexHandle(mat->mDiffuseMapName[0], &GFXStaticTextureSRGBProfile, "GroundCover texture aspect ratio check");
          else if (!mat->mDiffuseMapAsset[0].isNull())
-            tex = mat->mDiffuseMapAsset[0]->getImage(GFXStaticTextureSRGBProfile);
+            tex = mat->mDiffuseMapAsset[0]->getTexture(&GFXStaticTextureSRGBProfile);
 
          if(tex.isValid())
          {
@@ -1580,7 +1554,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 +1591,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 +1666,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; 
 

+ 37 - 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,8 @@ 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());
 
    if ( dStrlen(pd->mDropShaderName) > 0 )
    {
@@ -625,8 +648,8 @@ 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());
 
    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_

+ 5 - 5
Engine/source/T3D/lighting/reflectionProbe.cpp

@@ -426,7 +426,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 +474,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 +630,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 +645,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 - 15
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;
             }
          }
@@ -1166,8 +1165,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, 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 +1373,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 +1553,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

+ 9 - 11
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 ) )
    {
-      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_

+ 23 - 24
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,21 +377,17 @@ 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 (!mProjectileShape)
    {
-      projectileShape = ResourceManager::get().load(projectileShapeName);
-      if (bool(projectileShape) == false)
-      {
-         errorStr = String::ToString("ProjectileData::load: Couldn't load shape \"%s\"", projectileShapeName);
-         return false;
-      }
-      activateSeq = projectileShape->findSequence("activate");
-      maintainSeq = projectileShape->findSequence("maintain");
+      errorStr = String::ToString("ProjectileData::load: Couldn't load shape \"%s\"", mProjectileShapeAssetId);
+      return false;
    }
-
-   if (bool(projectileShape)) // create an instance to preload shape data
+   else
    {
-      TSShapeInstance* pDummy = new TSShapeInstance(projectileShape, !server);
+      activateSeq = mProjectileShape->findSequence("activate");
+      maintainSeq = mProjectileShape->findSequence("maintain");
+
+      TSShapeInstance* pDummy = new TSShapeInstance(mProjectileShape, !server);
       delete pDummy;
    }
 
@@ -401,7 +399,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 +464,7 @@ void ProjectileData::unpackData(BitStream* stream)
 {
    Parent::unpackData(stream);
 
-   projectileShapeName = stream->readSTString();
+   UNPACKDATA_SHAPEASSET(ProjectileShape);
 
    faceViewer = stream->readFlag();
    if(stream->readFlag())
@@ -786,9 +785,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 +826,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 +1508,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;
          }

+ 25 - 75
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,25 @@ 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())
-         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 +363,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 +385,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 +551,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 +565,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 +754,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 +779,6 @@ void ShapeBaseData::packData(BitStream* stream)
       stream->write(cameraMaxFov);
    stream->writeFlag(cameraCanBank);
    stream->writeFlag(mountedImagesBank);
-   stream->writeString( debrisShapeName );
 
    stream->writeFlag(observeThroughObject);
 
@@ -879,8 +832,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 +883,6 @@ void ShapeBaseData::unpackData(BitStream* stream)
 
    cameraCanBank = stream->readFlag();
    mountedImagesBank = stream->readFlag();
-
-   debrisShapeName = stream->readSTString();
-
    observeThroughObject = stream->readFlag();
 
    if( stream->readFlag() )
@@ -2011,13 +1961,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, 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;
 

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

@@ -315,6 +315,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 +366,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) std::string(std::string(#x) + std::string(#suff)).c_str()
+#define macroText(x) std::string(std::string(#x)).c_str()
+#define assetDoc(x,suff) std::string(std::string("@brief") + std::string(#x) + std::string(#suff)).c_str()
+
 #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());
+}

+ 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 * )
 

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

+ 13 - 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)

+ 24 - 64
Engine/source/environment/VolumetricFog.cpp

@@ -124,7 +124,6 @@ VolumetricFog::VolumetricFog()
    mLightRayMod = 1.0f;
    mOldLightRayStrength = 0.1f;
 
-   mShapeName = "";
    mShapeLoaded = false;
    mMinDisplaySize = 10.0f;
    mFadeSize = 0.0f;
@@ -132,15 +131,14 @@ VolumetricFog::VolumetricFog()
    mNumDetailLevels = 0;
    det_size.clear();
 
-   mTextureName = "";
    mIsTextured = false;
    mStrength = 0.5f;
    mTexTiles = 1.0f;
    mSpeed1.set(0.5f, 0.0f);
    mSpeed2.set(0.1f, 0.1f);
 
-   mShapeAsset = StringTable->EmptyString();
-   mShapeAssetId = StringTable->EmptyString();
+   INIT_SHAPEASSET(Shape);
+   INIT_IMAGEASSET(Texture);
 }
 
 VolumetricFog::~VolumetricFog()
@@ -168,11 +166,8 @@ VolumetricFog::~VolumetricFog()
 void VolumetricFog::initPersistFields()
 {
    addGroup("VolumetricFogData");
-   addProtectedField("shapeAsset", TypeShapeAssetPtr, Offset(mShapeAsset, VolumetricFog),
-      &VolumetricFog::_setShapeAsset, &defaultProtectedGetFn, "The source shape asset.");
 
-   addField("shapeName", TypeShapeFilename, Offset(mShapeName, VolumetricFog),
-      "Path and filename of the model file (.DTS, .DAE) to use for this Volume.", AbstractClassRep::FieldFlags::FIELD_HideInInspectors );
+   INITPERSISTFIELD_SHAPEASSET(Shape, VolumetricFog, "The source shape asset.");
 
    addField("FogColor", TypeColorI, Offset(mFogColor, VolumetricFog),
       "Fog color RGBA (Alpha is ignored)");
@@ -187,8 +182,8 @@ void VolumetricFog::initPersistFields()
    endGroup("VolumetricFogData");
 
    addGroup("VolumetricFogModulation");
-   addField("texture", TypeImageFilename, Offset(mTextureName, VolumetricFog),
-      "A texture which contains Fogdensity modulator in the red channel and color with 1-green channel. No texture disables modulation.");
+   INITPERSISTFIELD_IMAGEASSET(Texture, VolumetricFog, "A texture which contains Fogdensity modulator in the red channel and color with 1-green channel. No texture disables modulation.");
+
    addField("tiles", TypeF32, Offset(mTexTiles, VolumetricFog), 
       "How many times the texture is mapped to the object.");
    addField("modStrength", TypeF32, Offset(mStrength, VolumetricFog),
@@ -356,40 +351,15 @@ bool VolumetricFog::LoadShape()
 {
    GFXPrimitiveType GFXdrawTypes[] = { GFXTriangleList, GFXTriangleStrip };
 
-   Resource<TSShape> mShape;
-   if (mShapeAssetId != StringTable->EmptyString())
-   {
-      mShapeAsset = mShapeAssetId;
-
-      if (mShapeAsset.isNull())
-      {
-         Con::errorf("[TSStatic] Failed to load shape asset.");
-         return false;
-      }
-
-      mShape = mShapeAsset->getShapeResource();
-
-      if (!mShape)
-      {
-         Con::errorf("TSStatic::_createShape() - Shape Asset had no valid shape!");
-         return false;
-      }
-   }
-   else
+   if (mShapeAsset.isNull())
    {
-      if (!mShapeName || mShapeName[0] == '\0')
-      {
-         Con::errorf("VolumetricFog::LoadShape() - No shape name! Volumetric Fog will not be rendered!");
-         return false;
-      }
-
-      // Load shape, server side only reads bounds and radius
-      mShape = ResourceManager::get().load(mShapeName);
+      Con::errorf("[VolumetricFog] Failed to load shape asset.");
+      return false;
    }
 
-   if (bool(mShape) == false)
+   if (!mShape)
    {
-      Con::errorf("VolumetricFog::LoadShape() - Unable to load shape: %s", mShapeName);
+      Con::errorf("VolumetricFog::_createShape() - Shape Asset had no valid shape!");
       return false;
    }
 
@@ -573,7 +543,7 @@ U32 VolumetricFog::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
       stream->write(mFogDensity);
    if (stream->writeFlag(mask & FogModulationMask))
    {
-      stream->write(mTextureName);
+      PACK_IMAGEASSET(con, Texture);
       mTexTiles = mFabs(mTexTiles);
       stream->write(mTexTiles);
       stream->write(mStrength);
@@ -597,27 +567,20 @@ U32 VolumetricFog::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
    }
    if (stream->writeFlag(mask & FogShapeMask))
    {
-      stream->writeString(mShapeAssetId);
-      stream->writeString(mShapeName);
+      PACK_SHAPEASSET(con, Shape);
       mathWrite(*stream, getTransform());
       mathWrite(*stream, getScale());
 
-      Resource<TSShape> mShape;
-
-      if (mShapeAssetId != StringTable->EmptyString())
+      if (mShapeAsset.notNull())
       {
-         mShape = mShapeAsset->getShapeResource();
+         mObjBox = mShapeAsset->getShapeResource()->mBounds;
+         mRadius = mShapeAsset->getShapeResource()->mRadius;
       }
-      else if (mShapeName && mShapeName[0] != '\0')
+      else
       {
-         mShape = ResourceManager::get().load(mShapeName);
-      }
-
-      if (bool(mShape) == false)
          return retMask;
+      }
 
-      mObjBox = mShape->mBounds;
-      mRadius = mShape->mRadius;
       resetWorldBox();
       mObjSize = mWorldBox.getGreatestDiagonalLength();
       mObjScale = getScale();
@@ -632,7 +595,7 @@ void VolumetricFog::unpackUpdate(NetConnection *con, BitStream *stream)
    MatrixF mat;
    VectorF scale;
    VectorF mOldScale = getScale();
-   String oldTextureName = mTextureName;
+   StringTableEntry oldTextureName = mTextureAssetId;
    StringTableEntry oldShapeAsset = mShapeAssetId;
    StringTableEntry oldShape = mShapeName;
 
@@ -650,7 +613,7 @@ void VolumetricFog::unpackUpdate(NetConnection *con, BitStream *stream)
    }
    if (stream->readFlag())// Fog Modulation
    {
-      stream->read(&mTextureName);
+      UNPACK_IMAGEASSET(con, Texture);
       stream->read(&mTexTiles);
       mTexTiles = mFabs(mTexTiles);
       stream->read(&mStrength);
@@ -660,9 +623,9 @@ void VolumetricFog::unpackUpdate(NetConnection *con, BitStream *stream)
 
       if (isProperlyAdded())
       {
-         if (oldTextureName != mTextureName)
+         if (oldTextureName != mTextureAssetId)
             InitTexture();
-         if (oldTextureName.isNotEmpty() && mTextureName.isEmpty())
+         if (oldTextureName != StringTable->EmptyString() && mTextureAssetId == StringTable->EmptyString())
          {
             mIsTextured = false;
             mTexture.free();
@@ -704,11 +667,8 @@ void VolumetricFog::unpackUpdate(NetConnection *con, BitStream *stream)
    }
    if (stream->readFlag())//Fog shape
    {
-      char buffer[256];
-      stream->readString(buffer);
-      mShapeAssetId = StringTable->insert(buffer);
+      UNPACK_SHAPEASSET(con, Shape);
 
-      mShapeName = stream->readSTString();
       mathRead(*stream, &mat);
       mathRead(*stream, &scale);
       if (strcmp(oldShapeAsset, mShapeAssetId) != 0 || strcmp(oldShape, mShapeName) != 0)
@@ -1255,8 +1215,8 @@ void VolumetricFog::InitTexture()
 {
    mIsTextured = false;
 
-   if (mTextureName.isNotEmpty())
-      mTexture.set(mTextureName, &GFXStaticTextureSRGBProfile, "VolumetricFogMod");
+   if (mTextureAsset.isNull())
+      return;
 
    if (!mTexture.isNull())
    {

+ 11 - 8
Engine/source/environment/VolumetricFog.h

@@ -83,6 +83,9 @@ class VolumetricFog : public SceneObject
       Vector <GFXPrimitive> *piArray;
       Vector <U32> *indices;
    };
+
+   DECLARE_SHAPEASSET(VolumetricFog, Shape, onShapeChanged);
+   DECLARE_SHAPEASSET_NET_SETGET(VolumetricFog, Shape, FogShapeMask);
    
    protected:
       // Rendertargets;
@@ -91,9 +94,6 @@ class VolumetricFog : public SceneObject
       NamedTexTargetRef mDepthBufferTarget;
       NamedTexTargetRef mFrontBufferTarget;
    
-      // Fog Modulation texture
-      GFXTexHandle mTexture;
-   
       // Shaders
       GFXShaderRef mShader;
       GFXShaderRef mDeferredShader;
@@ -143,10 +143,7 @@ class VolumetricFog : public SceneObject
       GFXPrimitiveBufferHandle mPB;
    
       // Fog volume data;
-      AssetPtr<ShapeAsset> mShapeAsset;
-      StringTableEntry mShapeAssetId;
-
-      StringTableEntry mShapeName;
+      
       ColorI mFogColor;
       F32 mFogDensity;
       bool mIgnoreWater;
@@ -165,7 +162,9 @@ class VolumetricFog : public SceneObject
       F32 mInvScale;
    
       // Fog Modulation data
-      String mTextureName;
+      DECLARE_IMAGEASSET(VolumetricFog, Texture, onImageChanged, GFXStaticTextureSRGBProfile);
+      DECLARE_IMAGEASSET_NET_SETGET(VolumetricFog, Texture, FogModulationMask);
+
       bool mIsTextured;
       F32 mTexTiles;
       F32 mStrength;
@@ -221,6 +220,8 @@ class VolumetricFog : public SceneObject
       void _leaveFog(ShapeBase *control);
 
       static bool _setShapeAsset(void* obj, const char* index, const char* data);
+
+      void onImageChanged() {}
    
    public:
       // Public methods
@@ -248,6 +249,8 @@ class VolumetricFog : public SceneObject
       bool isInsideFog();
 
       bool setShapeAsset(const StringTableEntry shapeAssetId);
+
+      void onShapeChanged() {}
    
       DECLARE_CONOBJECT(VolumetricFog);
    

+ 4 - 9
Engine/source/environment/basicClouds.cpp

@@ -173,8 +173,7 @@ void BasicClouds::initPersistFields()
          addField( "layerEnabled", TypeBool, Offset( mLayerEnabled, BasicClouds ), TEX_COUNT,
             "Enable or disable rendering of this layer." );
 
-         addField( "texture", TypeImageFilename, Offset( mTexName, BasicClouds ), TEX_COUNT,
-            "Texture for this layer." );
+         INITPERSISTFIELD_IMAGEASSET_ARRAY(Texture, TEX_COUNT, BasicClouds, "Texture for this layer.");
 
          addField( "texScale", TypeF32, Offset( mTexScale, BasicClouds ), TEX_COUNT,
             "Texture repeat for this layer." );
@@ -216,7 +215,7 @@ U32 BasicClouds::packUpdate( NetConnection *conn, U32 mask, BitStream *stream )
    {
       stream->writeFlag( mLayerEnabled[i] );
 
-      stream->write( mTexName[i] );
+      PACK_IMAGEASSET_ARRAY(conn, Texture, i);
 
       stream->write( mTexScale[i] );
       mathWrite( *stream, mTexDirection[i] );
@@ -237,7 +236,7 @@ void BasicClouds::unpackUpdate( NetConnection *conn, BitStream *stream )
    {
       mLayerEnabled[i] = stream->readFlag();
 
-      stream->read( &mTexName[i] );
+      UNPACK_IMAGEASSET_ARRAY(conn, Texture, i);
       
       stream->read( &mTexScale[i] );      
       mathRead( *stream, &mTexDirection[i] );
@@ -340,11 +339,7 @@ void BasicClouds::_initTexture()
          continue;
       }
 
-      if ( mTexName[i].isNotEmpty() )
-      mTexture[i].set( mTexName[i], &GFXStaticTextureSRGBProfile, "BasicClouds" );
-
-      if ( mTexture[i].isNull() )
-         mTexture[i].set( GFXTextureManager::getWarningTexturePath(), &GFXStaticTextureSRGBProfile, "BasicClouds" );
+      _setTexture(getTexture(i), i);
    }
 }
 

+ 4 - 2
Engine/source/environment/basicClouds.h

@@ -42,6 +42,8 @@
 #include "gfx/gfxShader.h"
 #endif
 
+#include "T3D/assets/ImageAsset.h"
+
 class BaseMatInstance;
 
 
@@ -91,7 +93,8 @@ protected:
    static U32 smVertCount;
    static U32 smTriangleCount;
 
-   GFXTexHandle mTexture[TEX_COUNT];
+   DECLARE_IMAGEASSET_ARRAY(BasicClouds, Texture, GFXStaticTextureSRGBProfile, TEX_COUNT);
+   DECLARE_IMAGEASSET_ARRAY_NET_SETGET(BasicClouds, Texture, -1);
 
    GFXStateBlockRef mStateblock;
 
@@ -111,7 +114,6 @@ protected:
    // Fields...
 
    bool mLayerEnabled[TEX_COUNT];
-   String mTexName[TEX_COUNT];
    F32 mTexScale[TEX_COUNT];
    Point2F mTexDirection[TEX_COUNT];
    F32 mTexSpeed[TEX_COUNT];   

+ 18 - 26
Engine/source/environment/cloudLayer.cpp

@@ -38,6 +38,8 @@
 #include "lighting/lightInfo.h"
 #include "math/mathIO.h"
 
+#include "sim/netConnection.h"
+
 ConsoleDocClass( CloudLayer,
    "@brief A layer of clouds which change shape over time and are affected by scene lighting.\n\n"
 
@@ -110,6 +112,8 @@ CloudLayer::CloudLayer()
    mTexOffset[0] = mTexOffset[1] = mTexOffset[2] = Point2F::Zero;
 
    mHeight = 4.0f;
+
+   INIT_IMAGEASSET(Texture);
 }
 
 IMPLEMENT_CO_NETOBJECT_V1( CloudLayer );
@@ -127,9 +131,10 @@ bool CloudLayer::onAdd()
 
    addToScene();
 
+   LOAD_IMAGEASSET(Texture);
+
    if ( isClientObject() )
    {
-      _initTexture();
       _initBuffers();
 
       // Find ShaderData
@@ -186,11 +191,10 @@ void CloudLayer::onRemove()
 
 void CloudLayer::initPersistFields()
 {
-   addGroup( "CloudLayer" );	   
-      
-      addField( "texture", TypeImageFilename, Offset( mTextureName, CloudLayer ),
-         "An RGBA texture which should contain normals and opacity (density)." );
+   addGroup( "CloudLayer" );
 
+      INITPERSISTFIELD_IMAGEASSET(Texture, CloudLayer, "An RGBA texture which should contain normals and opacity (density).");
+      
       addArray( "Textures", TEX_COUNT );
 
          addField( "texScale", TypeF32, Offset( mTexScale, CloudLayer ), TEX_COUNT,
@@ -238,7 +242,7 @@ U32 CloudLayer::packUpdate( NetConnection *conn, U32 mask, BitStream *stream )
 {
    U32 retMask = Parent::packUpdate( conn, mask, stream );
 
-   stream->write( mTextureName );
+   PACK_IMAGEASSET(conn, Texture);
    
    for ( U32 i = 0; i < TEX_COUNT; i++ )
    {
@@ -260,8 +264,10 @@ void CloudLayer::unpackUpdate( NetConnection *conn, BitStream *stream )
 {
    Parent::unpackUpdate( conn, stream );
 
-   String oldTextureName = mTextureName;
-   stream->read( &mTextureName );
+   UNPACK_IMAGEASSET(conn, Texture);
+
+   if(mTextureAssetId != StringTable->EmptyString())
+      mTextureAsset = mTextureAssetId;
 
    for ( U32 i = 0; i < TEX_COUNT; i++ )
    {
@@ -283,8 +289,6 @@ void CloudLayer::unpackUpdate( NetConnection *conn, BitStream *stream )
 
    if ( isProperlyAdded() )
    {
-      if ( ( oldTextureName != mTextureName ) || ( ( oldCoverage == 0.0f ) != ( mCoverage == 0.0f ) ) )
-         _initTexture();
       if ( oldHeight != mHeight )
          _initBuffers();
    }
@@ -330,6 +334,9 @@ void CloudLayer::renderObject( ObjectRenderInst *ri, SceneRenderState *state, Ba
 {
    GFXTransformSaver saver;
 
+   if (!mTextureAsset || !mTextureAsset->isAssetValid())
+      return;
+
    const Point3F &camPos = state->getCameraPosition();
    MatrixF xfm(true);
    xfm.setPosition(camPos);
@@ -378,7 +385,7 @@ void CloudLayer::renderObject( ObjectRenderInst *ri, SceneRenderState *state, Ba
 
    mShaderConsts->setSafe( mExposureSC, mExposure );
 
-   GFX->setTexture( mNormalHeightMapSC->getSamplerRegister(), mTexture );                            
+   GFX->setTexture( mNormalHeightMapSC->getSamplerRegister(), getTextureResource());
    GFX->setVertexBuffer( mVB );            
    GFX->setPrimitiveBuffer( mPB );
 
@@ -389,21 +396,6 @@ void CloudLayer::renderObject( ObjectRenderInst *ri, SceneRenderState *state, Ba
 // CloudLayer Internal Methods....
 
 
-void CloudLayer::_initTexture()
-{
-   if ( mCoverage <= 0.0f )
-   {
-      mTexture = NULL;
-      return;
-   }
-
-   if ( mTextureName.isNotEmpty() )
-      mTexture.set( mTextureName, &GFXNormalMapProfile, "CloudLayer" );
-
-   if ( mTexture.isNull() )
-      mTexture.set( GFXTextureManager::getWarningTexturePath(), &GFXNormalMapProfile, "CloudLayer" );
-}
-
 void CloudLayer::_initBuffers()
 {      
    // Vertex Buffer...

+ 6 - 3
Engine/source/environment/cloudLayer.h

@@ -39,6 +39,8 @@
 #include "materials/matInstance.h"
 #endif
 
+#include "T3D/assets/ImageAsset.h"
+
 GFXDeclareVertexFormat( GFXCloudVertex )
 {
    Point3F point;
@@ -81,9 +83,10 @@ public:
    void prepRenderImage( SceneRenderState *state );
    void renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *mi );
 
+   void onImageChanged() {}
+
 protected:
 
-   void _initTexture();
    void _initBuffers();
 
 protected:
@@ -93,7 +96,8 @@ protected:
    static U32 smVertCount;
    static U32 smTriangleCount;
 
-   GFXTexHandle mTexture;
+   DECLARE_IMAGEASSET(CloudLayer, Texture, onImageChanged, GFXStaticTextureSRGBProfile);
+   DECLARE_IMAGEASSET_NET_SETGET(CloudLayer, Texture, CloudLayerMask);
 
    GFXShaderRef mShader;
 
@@ -120,7 +124,6 @@ protected:
 
    // Fields...
 
-   String mTextureName;
    F32 mTexScale[TEX_COUNT];
    Point2F mTexDirection[TEX_COUNT];
    F32 mTexSpeed[TEX_COUNT];   

+ 19 - 18
Engine/source/environment/decalRoad.cpp

@@ -277,8 +277,6 @@ DecalRoad::DecalRoad()
    mTextureLength( 5.0f ),
    mRenderPriority( 10 ),
    mLoadRenderData( true ),
-   mMaterial( NULL ),
-   mMatInst( NULL ),
    mTriangleCount(0),
    mVertCount(0),
    mUpdateEventId( -1 ),
@@ -289,7 +287,9 @@ DecalRoad::DecalRoad()
    mTypeMask |= StaticObjectType | StaticShapeObjectType;
    mNetFlags.set(Ghostable);
 
-   initMaterialAsset(Material);
+   INIT_MATERIALASSET(Material);
+
+   mMaterialInst = nullptr;
 }
 
 DecalRoad::~DecalRoad()
@@ -305,8 +305,7 @@ void DecalRoad::initPersistFields()
 {
    addGroup( "DecalRoad" );
 
-      addProtectedField("materialAsset", TypeMaterialAssetId, Offset(mMaterialAssetId, DecalRoad), &DecalRoad::_setMaterialAsset, &defaultProtectedGetFn, "Material Asset used for rendering.");
-      addProtectedField( "material", TypeMaterialName, Offset( mMaterialName, DecalRoad ), &DecalRoad::_setMaterialName, &defaultProtectedGetFn, "Material used for rendering." );
+      INITPERSISTFIELD_MATERIALASSET(Material, DecalRoad, "Material used for rendering.");
 
       addProtectedField( "textureLength", TypeF32, Offset( mTextureLength, DecalRoad ), &DecalRoad::ptSetTextureLength, &defaultProtectedGetFn, 
          "The length in meters of textures mapped to the DecalRoad" );      
@@ -398,7 +397,7 @@ bool DecalRoad::onAdd()
 
 void DecalRoad::onRemove()
 {
-   SAFE_DELETE( mMatInst );
+   SAFE_DELETE( mMaterialInst );
 
    TerrainBlock::smUpdateSignal.remove( this, &DecalRoad::_onTerrainChanged );
 
@@ -492,7 +491,7 @@ U32 DecalRoad::packUpdate(NetConnection * con, U32 mask, BitStream * stream)
    if ( stream->writeFlag( mask & DecalRoadMask ) )
    {
       // Write Texture Name.
-      packMaterialAsset(con, Material);
+      PACK_MATERIALASSET(con, Material);
 
       stream->write( mBreakAngle );      
 
@@ -581,7 +580,7 @@ void DecalRoad::unpackUpdate( NetConnection *con, BitStream *stream )
    // DecalRoadMask
    if ( stream->readFlag() )
    {
-      unpackMaterialAsset(con, Material);
+      UNPACK_MATERIALASSET(con, Material);
 
       if (isProperlyAdded())
          _initMaterial();
@@ -685,13 +684,13 @@ void DecalRoad::prepRenderImage( SceneRenderState* state )
 
    if (  mNodes.size() <= 1 || 
          mBatches.size() == 0 ||
-         !mMatInst ||
+         !mMaterialInst ||
          state->isShadowPass() )
       return;
 
    // If we don't have a material instance after the override then 
    // we can skip rendering all together.
-   BaseMatInstance *matInst = state->getOverrideMaterial( mMatInst );
+   BaseMatInstance *matInst = state->getOverrideMaterial(mMaterialInst);
    if ( !matInst )
       return;
       
@@ -1045,12 +1044,14 @@ bool DecalRoad::addNodeFromField( void *object, const char *index, const char *d
 
 void DecalRoad::_initMaterial()
 {
+   _setMaterial(getMaterial());
+
    if (mMaterialAsset.notNull())
    {
-      if (mMatInst && String(mMaterialAsset->getMaterialDefinitionName()).equal(mMatInst->getMaterial()->getName(), String::NoCase))
+      if (mMaterialInst && String(mMaterialAsset->getMaterialDefinitionName()).equal(mMaterialInst->getMaterial()->getName(), String::NoCase))
          return;
 
-      SAFE_DELETE(mMatInst);
+      SAFE_DELETE(mMaterialInst);
 
       Material* tMat = nullptr;
 
@@ -1060,22 +1061,22 @@ void DecalRoad::_initMaterial()
       mMaterial = tMat;
 
       if (mMaterial)
-         mMatInst = mMaterial->createMatInstance();
+         mMaterialInst = mMaterial->createMatInstance();
       else
-         mMatInst = MATMGR->createMatInstance("WarningMaterial");
+         mMaterialInst = MATMGR->createMatInstance("WarningMaterial");
 
-      if (!mMatInst)
+      if (!mMaterialInst)
          Con::errorf("DecalRoad::_initMaterial - no Material called '%s'", mMaterialAsset->getMaterialDefinitionName());
    }
 
-   if (!mMatInst)
+   if (!mMaterialInst)
       return;
 
    GFXStateBlockDesc desc;
    desc.setZReadWrite( true, false );
-   mMatInst->addStateBlockDesc( desc );
+   mMaterialInst->addStateBlockDesc( desc );
 
-   mMatInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVertexPNTBT>() );
+   mMaterialInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVertexPNTBT>() );
 }
 
 void DecalRoad::_debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* )

+ 5 - 4
Engine/source/environment/decalRoad.h

@@ -240,7 +240,11 @@ protected:
    U32 mSegmentsPerBatch;
    F32 mTextureLength;
 
-   DECLARE_NET_MATERIALASSET(DecalRoad, Material, DecalRoadMask);
+   BaseMatInstance* mMaterialInst;
+
+   DECLARE_MATERIALASSET(DecalRoad, Material);
+   DECLARE_MATERIALASSET_NET_SETGET(DecalRoad, Material, DecalRoadMask);
+
    U32 mRenderPriority;
 
    // Static ConsoleVars for editor
@@ -261,9 +265,6 @@ protected:
    RoadBatchVector mBatches;
    
    bool mLoadRenderData;
-   
-   SimObjectPtr<Material> mMaterial;
-   BaseMatInstance *mMatInst;
 
    GFXVertexBufferHandle<GFXVertexPNTBT> mVB;
    GFXPrimitiveBufferHandle mPB;

+ 10 - 10
Engine/source/environment/editors/guiMeshRoadEditorCtrl.cpp

@@ -97,6 +97,10 @@ GuiMeshRoadEditorCtrl::GuiMeshRoadEditorCtrl()
     mHoverNodeColor( 255,255,255,255 ),
 	 mHasCopied( false )
 {
+   INIT_MATERIALASSET(TopMaterial);
+   INIT_MATERIALASSET(BottomMaterial);
+   INIT_MATERIALASSET(SideMaterial);
+
    mTopMaterialAssetId = Con::getVariable("$MeshRoadEditor::defaultTopMaterialAsset");
    mBottomMaterialAssetId = Con::getVariable("$MeshRoadEditor::defaultBottomMaterialAsset");
    mSideMaterialAssetId = Con::getVariable("$MeshRoadEditor::defaultSideMaterialAsset");
@@ -205,10 +209,6 @@ bool GuiMeshRoadEditorCtrl::onAdd()
    desc.zEnable = true;
    mZEnableSB = GFX->createStateBlock(desc);
 
-   bindMaterialAsset(TopMaterial);
-   bindMaterialAsset(BottomMaterial);
-   bindMaterialAsset(SideMaterial);
-
    return true;
 }
 
@@ -222,9 +222,9 @@ void GuiMeshRoadEditorCtrl::initPersistFields()
    addField( "HoverNodeColor",      TypeColorI, Offset( mHoverNodeColor, GuiMeshRoadEditorCtrl ) );
    addField( "isDirty",             TypeBool,   Offset( mIsDirty, GuiMeshRoadEditorCtrl ) );
 
-   addField("topMaterial", TypeMaterialAssetId, Offset(mTopMaterialAssetId, GuiMeshRoadEditorCtrl), "Default Material used by the Mesh Road Editor on upper surface road creation.");
-   addField("bottomMaterial", TypeMaterialAssetId, Offset(mBottomMaterialAssetId, GuiMeshRoadEditorCtrl), "Default Material used by the Mesh Road Editor on bottom surface road creation.");
-   addField("sideMaterial", TypeMaterialAssetId, Offset(mSideMaterialAssetId, GuiMeshRoadEditorCtrl), "Default Material used by the Mesh Road Editor on side surface road creation.");
+   INITPERSISTFIELD_MATERIALASSET(TopMaterial, GuiMeshRoadEditorCtrl, "Default Material used by the Mesh Road Editor on upper surface road creation.");
+   INITPERSISTFIELD_MATERIALASSET(BottomMaterial, GuiMeshRoadEditorCtrl, "Default Material used by the Mesh Road Editor on bottom surface road creation.");
+   INITPERSISTFIELD_MATERIALASSET(SideMaterial, GuiMeshRoadEditorCtrl, "Default Material used by the Mesh Road Editor on side surface road creation.");
 
    //addField( "MoveNodeCursor", TYPEID< SimObject >(), Offset( mMoveNodeCursor, GuiMeshRoadEditorCtrl) );
    //addField( "AddNodeCursor", TYPEID< SimObject >(), Offset( mAddNodeCursor, GuiMeshRoadEditorCtrl) );
@@ -627,11 +627,11 @@ void GuiMeshRoadEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event)
 		MeshRoad *newRoad = new MeshRoad;  
 
       if(mTopMaterialAsset.notNull())
-		   newRoad->setTopMaterialAssetId(mTopMaterialAssetId);
+		   newRoad->_setTopMaterial(mTopMaterialAssetId);
       if (mBottomMaterialAsset.notNull())
-		   newRoad->setBottomMaterialAssetId(mBottomMaterialAssetId);
+		   newRoad->_setBottomMaterial(mBottomMaterialAssetId);
       if (mSideMaterialAsset.notNull())
-		   newRoad->setSideMaterialAssetId(mSideMaterialAssetId);
+		   newRoad->_setSideMaterial(mSideMaterialAssetId);
 			
       newRoad->registerObject();
 

+ 7 - 6
Engine/source/environment/editors/guiMeshRoadEditorCtrl.h

@@ -159,13 +159,14 @@ class GuiMeshRoadEditorCtrl : public EditTSCtrl
       bool mHasCopied;
 	public:
 
-      StringTableEntry mTopMaterialAssetId;
-      StringTableEntry mBottomMaterialAssetId;
-      StringTableEntry mSideMaterialAssetId;
+      DECLARE_MATERIALASSET(GuiMeshRoadEditorCtrl, TopMaterial);
+      DECLARE_MATERIALASSET_SETGET(GuiMeshRoadEditorCtrl, TopMaterial);
 
-      AssetPtr<MaterialAsset> mTopMaterialAsset;
-      AssetPtr<MaterialAsset> mBottomMaterialAsset;
-      AssetPtr<MaterialAsset> mSideMaterialAsset;
+      DECLARE_MATERIALASSET(GuiMeshRoadEditorCtrl, BottomMaterial);
+      DECLARE_MATERIALASSET_SETGET(GuiMeshRoadEditorCtrl, BottomMaterial);
+
+      DECLARE_MATERIALASSET(GuiMeshRoadEditorCtrl, SideMaterial);
+      DECLARE_MATERIALASSET_SETGET(GuiMeshRoadEditorCtrl, SideMaterial);
 };
 
 class GuiMeshRoadEditorUndoAction : public UndoAction

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio