瀏覽代碼

Merge pull request #1506 from marauder2k9-torque/SoundAsset_Refactor

Shape Asset Refactor and Image Asset fixes and tweaks
Brian Roberts 2 月之前
父節點
當前提交
f1cf4147a8
共有 78 個文件被更改,包括 1471 次插入1181 次删除
  1. 54 40
      Engine/source/T3D/assets/ImageAsset.cpp
  2. 36 16
      Engine/source/T3D/assets/ImageAsset.h
  3. 113 43
      Engine/source/T3D/assets/ShapeAsset.cpp
  4. 278 284
      Engine/source/T3D/assets/ShapeAsset.h
  5. 24 92
      Engine/source/T3D/assets/assetImporter.cpp
  6. 14 14
      Engine/source/T3D/debris.cpp
  7. 4 4
      Engine/source/T3D/debris.h
  8. 7 12
      Engine/source/T3D/examples/renderShapeExample.cpp
  9. 2 5
      Engine/source/T3D/examples/renderShapeExample.h
  10. 64 2
      Engine/source/T3D/fps/guiCrossHairHud.cpp
  11. 12 10
      Engine/source/T3D/fx/explosion.cpp
  12. 4 4
      Engine/source/T3D/fx/explosion.h
  13. 10 11
      Engine/source/T3D/fx/groundCover.cpp
  14. 4 4
      Engine/source/T3D/fx/groundCover.h
  15. 37 41
      Engine/source/T3D/guiObjectView.cpp
  16. 15 20
      Engine/source/T3D/guiObjectView.h
  17. 4 4
      Engine/source/T3D/missionMarker.cpp
  18. 16 14
      Engine/source/T3D/physics/physicsDebris.cpp
  19. 7 6
      Engine/source/T3D/physics/physicsDebris.h
  20. 21 20
      Engine/source/T3D/physics/physicsShape.cpp
  21. 4 4
      Engine/source/T3D/physics/physicsShape.h
  22. 35 40
      Engine/source/T3D/player.cpp
  23. 7 3
      Engine/source/T3D/player.h
  24. 15 17
      Engine/source/T3D/projectile.cpp
  25. 4 4
      Engine/source/T3D/projectile.h
  26. 3 3
      Engine/source/T3D/proximityMine.cpp
  27. 1 1
      Engine/source/T3D/rigidShape.cpp
  28. 152 159
      Engine/source/T3D/shapeBase.cpp
  29. 18 13
      Engine/source/T3D/shapeBase.h
  30. 23 33
      Engine/source/T3D/shapeImage.cpp
  31. 54 61
      Engine/source/T3D/tsStatic.cpp
  32. 9 4
      Engine/source/T3D/tsStatic.h
  33. 3 3
      Engine/source/T3D/turret/aiTurretShape.cpp
  34. 10 10
      Engine/source/T3D/turret/turretShape.cpp
  35. 2 2
      Engine/source/T3D/vehicles/flyingVehicle.cpp
  36. 1 1
      Engine/source/T3D/vehicles/hoverVehicle.cpp
  37. 1 1
      Engine/source/T3D/vehicles/vehicle.cpp
  38. 16 17
      Engine/source/T3D/vehicles/wheeledVehicle.cpp
  39. 4 4
      Engine/source/T3D/vehicles/wheeledVehicle.h
  40. 10 7
      Engine/source/afx/afxMagicMissile.cpp
  41. 8 8
      Engine/source/afx/afxMagicMissile.h
  42. 26 34
      Engine/source/afx/ce/afxModel.cpp
  43. 10 9
      Engine/source/afx/ce/afxModel.h
  44. 1 1
      Engine/source/afx/ce/afxStaticShape.h
  45. 19 3
      Engine/source/assets/assetBase.cpp
  46. 17 0
      Engine/source/console/console.cpp
  47. 15 17
      Engine/source/environment/VolumetricFog.cpp
  48. 2 4
      Engine/source/environment/VolumetricFog.h
  49. 6 7
      Engine/source/forest/forestItem.cpp
  50. 6 4
      Engine/source/forest/forestItem.h
  51. 7 7
      Engine/source/forest/ts/tsForestItemData.cpp
  52. 6 0
      Engine/source/gui/buttons/guiBitmapButtonCtrl.h
  53. 10 7
      Engine/source/gui/controls/guiBitmapCtrl.cpp
  54. 9 0
      Engine/source/gui/core/guiCanvas.cpp
  55. 3 1
      Engine/source/gui/core/guiOffscreenCanvas.cpp
  56. 5 0
      Engine/source/gui/core/guiOffscreenCanvas.h
  57. 4 0
      Engine/source/gui/core/guiTypes.cpp
  58. 1 1
      Engine/source/gui/editor/guiShapeEdPreview.cpp
  59. 1 1
      Engine/source/gui/editor/inspector/group.cpp
  60. 1 1
      Engine/source/gui/editor/inspector/variableInspector.cpp
  61. 17 0
      Engine/source/ts/assimp/assimpShapeLoader.cpp
  62. 17 0
      Engine/source/ts/collada/colladaShapeLoader.cpp
  63. 1 1
      Engine/source/ts/tsShapeConstruct.cpp
  64. 1 1
      Engine/source/ts/tsShapeConstruct.h
  65. 15 0
      Templates/BaseGame/game/data/Prototyping/Prototyping.tscript
  66. 11 0
      Templates/BaseGame/game/data/Prototyping/gui_offscreen_test/ScreenTarget.asset.taml
  67. 14 0
      Templates/BaseGame/game/data/Prototyping/gui_offscreen_test/monitor_base_mat.asset.taml
  68. 16 0
      Templates/BaseGame/game/data/Prototyping/gui_offscreen_test/monitor_screen_mat.asset.taml
  69. 6 0
      Templates/BaseGame/game/data/Prototyping/gui_offscreen_test/monitor_shape.asset.taml
  70. 二進制
      Templates/BaseGame/game/data/Prototyping/gui_offscreen_test/monitor_shape.fbx
  71. 20 0
      Templates/BaseGame/game/data/Prototyping/gui_offscreen_test/monitor_shape.tscript
  72. 2 2
      Templates/BaseGame/game/data/UI/scripts/cursors.tscript
  73. 2 2
      Templates/BaseGame/game/tools/assetBrowser/scripts/assetBrowser.tscript
  74. 28 4
      Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/image.tscript
  75. 28 7
      Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/material.tscript
  76. 24 12
      Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/shape.tscript
  77. 6 6
      Templates/BaseGame/game/tools/gui/cursors.ed.tscript
  78. 8 8
      Templates/BaseGame/game/tools/worldEditor/scripts/cursors.ed.tscript

+ 54 - 40
Engine/source/T3D/assets/ImageAsset.cpp

@@ -213,6 +213,8 @@ bool ImageAsset::onAdd()
 
 void ImageAsset::onRemove()
 {
+   Torque::FS::RemoveChangeNotification(mImageFile, this, &ImageAsset::_onResourceChanged);
+
    // Call Parent.
    Parent::onRemove();
 }
@@ -345,6 +347,11 @@ void ImageAsset::initializeAsset(void)
       return;
 
    mImageFile = expandAssetFilePath(mImageFile);
+
+   if (getOwned())
+      Torque::FS::AddChangeNotification(mImageFile, this, &ImageAsset::_onResourceChanged);
+
+   populateImage();
 }
 
 void ImageAsset::onAssetRefresh(void)
@@ -356,6 +363,8 @@ void ImageAsset::onAssetRefresh(void)
    // Call parent.
    Parent::onAssetRefresh();
 
+   populateImage();
+
 }
 
 //------------------------------------------------------------------------------
@@ -385,6 +394,8 @@ void ImageAsset::setImageFile(StringTableEntry pImageFile)
    if (pImageFile == mImageFile)
       return;
 
+   Torque::FS::RemoveChangeNotification(mImageFile, this, &ImageAsset::_onResourceChanged);
+
    if (String(pImageFile).startsWith("#") || String(pImageFile).startsWith("$"))
    {
       mImageFile = StringTable->insert(pImageFile);
@@ -395,46 +406,6 @@ void ImageAsset::setImageFile(StringTableEntry pImageFile)
 
    mImageFile = getOwned() ? expandAssetFilePath(pImageFile) : StringTable->insert(pImageFile);
 
-   if (Torque::FS::IsFile(mImageFile))
-   {
-      if (dStrEndsWith(mImageFile, ".dds"))
-      {
-         DDSFile* tempFile = new DDSFile();
-         FileStream* ddsFs;
-         if ((ddsFs = FileStream::createAndOpen(mImageFile, Torque::FS::File::Read)) == NULL)
-         {
-            Con::errorf("ImageAsset::setImageFile Failed to open ddsfile: %s", mImageFile);
-         }
-
-         if (!tempFile->readHeader(*ddsFs))
-         {
-            Con::errorf("ImageAsset::setImageFile Failed to read header of ddsfile: %s", mImageFile);
-         }
-         else
-         {
-            mImageWidth = tempFile->mWidth;
-            mImageHeight = tempFile->mHeight;
-         }
-
-         ddsFs->close();
-         delete tempFile;
-      }
-      else
-      {
-         if (!stbi_info(mImageFile, &mImageWidth, &mImageHeight, &mImageChannels))
-         {
-            StringTableEntry stbErr = stbi_failure_reason();
-            if (stbErr == StringTable->EmptyString())
-               stbErr = "ImageAsset::Unkown Error!";
-
-            Con::errorf("ImageAsset::setImageFile STB Get file info failed: %s", stbErr);
-         }
-      }
-
-      // we only support 2d textures..... for no ;)
-      mImageDepth = 1;
-   }
-
    refreshAsset();
 }
 
@@ -675,6 +646,49 @@ void ImageAsset::onTamlCustomRead(const TamlCustomNodes& customNodes)
    }
 }
 
+void ImageAsset::populateImage(void)
+{
+   if (Torque::FS::IsFile(mImageFile))
+   {
+      if (dStrEndsWith(mImageFile, ".dds"))
+      {
+         DDSFile* tempFile = new DDSFile();
+         FileStream* ddsFs;
+         if ((ddsFs = FileStream::createAndOpen(mImageFile, Torque::FS::File::Read)) == NULL)
+         {
+            Con::errorf("ImageAsset::setImageFile Failed to open ddsfile: %s", mImageFile);
+         }
+
+         if (!tempFile->readHeader(*ddsFs))
+         {
+            Con::errorf("ImageAsset::setImageFile Failed to read header of ddsfile: %s", mImageFile);
+         }
+         else
+         {
+            mImageWidth = tempFile->mWidth;
+            mImageHeight = tempFile->mHeight;
+         }
+
+         ddsFs->close();
+         delete tempFile;
+      }
+      else
+      {
+         if (!stbi_info(mImageFile, &mImageWidth, &mImageHeight, &mImageChannels))
+         {
+            StringTableEntry stbErr = stbi_failure_reason();
+            if (stbErr == StringTable->EmptyString())
+               stbErr = "ImageAsset::Unkown Error!";
+
+            Con::errorf("ImageAsset::setImageFile STB Get file info failed: %s", stbErr);
+         }
+      }
+
+      // we only support 2d textures..... for now ;)
+      mImageDepth = 1; 
+   }
+}
+
 const char* ImageAsset::getImageInfo()
 {
    if (isAssetValid())

+ 36 - 16
Engine/source/T3D/assets/ImageAsset.h

@@ -123,7 +123,7 @@ public:
    };
 
    static const String mErrCodeStrings[U32(ImageAssetErrCode::Extended) - U32(Parent::Extended) + 1];
-   static U32 getAssetErrCode(ConcreteAssetPtr checkAsset) { if (checkAsset) return checkAsset->mLoadedState; else return 0; }
+   static U32 getAssetErrCode(ConcreteAssetPtr checkAsset) { if (checkAsset.notNull()) return checkAsset->mLoadedState; else return 0; }
 
    static String getAssetErrstrn(U32 errCode)
    {
@@ -196,7 +196,7 @@ public:
    static U32 getAssetById(StringTableEntry assetId, AssetPtr<ImageAsset>* imageAsset);
    static U32 getAssetById(String assetId, AssetPtr<ImageAsset>* imageAsset) { return getAssetById(assetId.c_str(), imageAsset); };
 
-
+   void populateImage(void);
    const char* getImageInfo();
 
 protected:
@@ -233,17 +233,20 @@ DefineEnumType(ImageAssetType);
 
 #pragma region Refactor Asset Macros
 
-#define DECLARE_IMAGEASSET(className, name, profile)                                                                                                                 \
+#define DECLARE_IMAGEASSET(className, name, profile)                                                                                                                          \
 private:                                                                                                                                                                      \
-   AssetPtr<ImageAsset> m##name##Asset;\
-   String               m##name##File;\
+   AssetPtr<ImageAsset> m##name##Asset;                                                                                                                                       \
+   StringTableEntry     m##name##File  = StringTable->EmptyString();                                                                                                          \
 public:                                                                                                                                                                       \
    void _set##name(StringTableEntry _in){                                                                                                                                     \
       if(m##name##Asset.getAssetId() == _in)                                                                                                                                  \
          return;                                                                                                                                                              \
-      if(_in == NULL || _in == StringTable->EmptyString())                                                                                                                    \
+      if(get##name##File() == _in)                                                                                                                                            \
+         return;                                                                                                                                                              \
+      if(_in == NULL || _in == StringTable->EmptyString() || _in == "")                                                                                                       \
       {                                                                                                                                                                       \
          m##name##Asset = NULL;                                                                                                                                               \
+         m##name##File = "";                                                                                                                                                  \
          return;                                                                                                                                                              \
       }                                                                                                                                                                       \
       if(!AssetDatabase.isDeclaredAsset(_in))                                                                                                                                 \
@@ -271,10 +274,12 @@ public:
             imageAssetId = ImageAsset::smNoImageAssetFallback;                                                                                                                \
          }                                                                                                                                                                    \
          m##name##Asset = imageAssetId;                                                                                                                                       \
+         m##name##File = _in;                                                                                                                                                 \
       }                                                                                                                                                                       \
       else                                                                                                                                                                    \
       {                                                                                                                                                                       \
          m##name##Asset = _in;                                                                                                                                                \
+         m##name##File = get##name##File();                                                                                                                                   \
       }                                                                                                                                                                       \
    };                                                                                                                                                                         \
                                                                                                                                                                               \
@@ -285,17 +290,20 @@ public:
    StringTableEntry get##name##File(){ return m##name##Asset.notNull() ? m##name##Asset->getImageFile() : ""; }
 
 
-#define DECLARE_IMAGEASSET_NET(className, name, profile, mask)                                                                                                       \
+#define DECLARE_IMAGEASSET_NET(className, name, profile, mask)                                                                                                                \
 private:                                                                                                                                                                      \
    AssetPtr<ImageAsset> m##name##Asset;                                                                                                                                       \
-   String               m##name##File;\
+   StringTableEntry     m##name##File  = StringTable->EmptyString();                                                                                                          \
 public:                                                                                                                                                                       \
    void _set##name(StringTableEntry _in){                                                                                                                                     \
       if(m##name##Asset.getAssetId() == _in)                                                                                                                                  \
          return;                                                                                                                                                              \
-      if(_in == NULL || _in == StringTable->EmptyString())                                                                                                                    \
+      if(get##name##File() == _in)                                                                                                                                            \
+         return;                                                                                                                                                              \
+      if(_in == NULL || _in == StringTable->EmptyString() || _in == "")                                                                                                       \
       {                                                                                                                                                                       \
          m##name##Asset = NULL;                                                                                                                                               \
+         m##name##File = "";                                                                                                                                                  \
          setMaskBits(mask);                                                                                                                                                   \
          return;                                                                                                                                                              \
       }                                                                                                                                                                       \
@@ -324,10 +332,12 @@ public:
             imageAssetId = ImageAsset::smNoImageAssetFallback;                                                                                                                \
          }                                                                                                                                                                    \
          m##name##Asset = imageAssetId;                                                                                                                                       \
+         m##name##File = _in;                                                                                                                                                 \
       }                                                                                                                                                                       \
       else                                                                                                                                                                    \
       {                                                                                                                                                                       \
          m##name##Asset = _in;                                                                                                                                                \
+         m##name##File = get##name##File();                                                                                                                                   \
       }                                                                                                                                                                       \
       setMaskBits(mask);                                                                                                                                                      \
    };                                                                                                                                                                         \
@@ -339,22 +349,25 @@ public:
    StringTableEntry get##name##File(){ return m##name##Asset.notNull() ? m##name##Asset->getImageFile() : ""; }
 
 
-#define INITPERSISTFIELD_IMAGEASSET(name, consoleClass, docs)                                                                                                        \
+#define INITPERSISTFIELD_IMAGEASSET(name, consoleClass, docs)                                                                                                                 \
    addProtectedField(assetText(name, Asset), TypeImageAssetPtr, Offset(m##name##Asset, consoleClass), _set##name##Data, &defaultProtectedGetFn, assetDoc(name, asset docs.)); \
    addProtectedField(assetText(name, File), TypeFilename, Offset(m##name##File, consoleClass), _set##name##Data, &defaultProtectedGetFn, assetDoc(name, file docs.));
 
 
-#define DECLARE_IMAGEASSET_ARRAY(className, name, profile, max)                                                                                                      \
+#define DECLARE_IMAGEASSET_ARRAY(className, name, profile, max)                                                                                                               \
 private:                                                                                                                                                                      \
    AssetPtr<ImageAsset> m##name##Asset[max];                                                                                                                                  \
-   String               m##name##File[max];\
+   StringTableEntry     m##name##File[max] = {StringTable->EmptyString() };                                                                                                   \
 public:                                                                                                                                                                       \
    void _set##name(StringTableEntry _in, const U32& index){                                                                                                                   \
       if(m##name##Asset[index].getAssetId() == _in)                                                                                                                           \
          return;                                                                                                                                                              \
-      if(_in == NULL || _in == StringTable->EmptyString())                                                                                                                    \
+      if(get##name##File(index) == _in)                                                                                                                                       \
+         return;                                                                                                                                                              \
+      if(_in == NULL || _in == StringTable->EmptyString() || _in == "")                                                                                                       \
       {                                                                                                                                                                       \
          m##name##Asset[index] = NULL;                                                                                                                                        \
+         m##name##File[index] = "";                                                                                                                                           \
          return;                                                                                                                                                              \
       }                                                                                                                                                                       \
       if(!AssetDatabase.isDeclaredAsset(_in))                                                                                                                                 \
@@ -382,10 +395,12 @@ public:
             imageAssetId = ImageAsset::smNoImageAssetFallback;                                                                                                                \
          }                                                                                                                                                                    \
          m##name##Asset[index] = imageAssetId;                                                                                                                                \
+         m##name##File[index] = _in;                                                                                                                                          \
       }                                                                                                                                                                       \
       else                                                                                                                                                                    \
       {                                                                                                                                                                       \
          m##name##Asset[index] = _in;                                                                                                                                         \
+         m##name##File[index] = get##name##File(index);                                                                                                                       \
       }                                                                                                                                                                       \
    };                                                                                                                                                                         \
                                                                                                                                                                               \
@@ -397,17 +412,20 @@ public:
    StringTableEntry get##name##File(const U32& idx){ return m##name##Asset[idx].notNull() ? m##name##Asset[idx]->getImageFile() : ""; }
 
 
-#define DECLARE_IMAGEASSET_ARRAY_NET(className, name, profile, max, mask)                                                                                            \
+#define DECLARE_IMAGEASSET_ARRAY_NET(className, name, profile, max, mask)                                                                                                     \
 private:                                                                                                                                                                      \
    AssetPtr<ImageAsset> m##name##Asset[max];                                                                                                                                  \
-   String               m##name##File[max];\
+   StringTableEntry     m##name##File[max] = {StringTable->EmptyString() };                                                                                                   \
 public:                                                                                                                                                                       \
    void _set##name(StringTableEntry _in, const U32& index){                                                                                                                   \
       if(m##name##Asset[index].getAssetId() == _in)                                                                                                                           \
          return;                                                                                                                                                              \
-      if(_in == NULL || _in == StringTable->EmptyString())                                                                                                                    \
+      if(get##name##File(index) == _in)                                                                                                                                       \
+         return;                                                                                                                                                              \
+      if(_in == NULL || _in == StringTable->EmptyString() || _in == "")                                                                                                       \
       {                                                                                                                                                                       \
          m##name##Asset[index] = NULL;                                                                                                                                        \
+         m##name##File[index] = "";                                                                                                                                           \
          setMaskBits(mask);                                                                                                                                                   \
          return;                                                                                                                                                              \
       }                                                                                                                                                                       \
@@ -436,10 +454,12 @@ public:
             imageAssetId = ImageAsset::smNoImageAssetFallback;                                                                                                                \
          }                                                                                                                                                                    \
          m##name##Asset[index] = imageAssetId;                                                                                                                                \
+         m##name##File[index] = _in;                                                                                                                                          \
       }                                                                                                                                                                       \
       else                                                                                                                                                                    \
       {                                                                                                                                                                       \
          m##name##Asset[index] = _in;                                                                                                                                         \
+         m##name##File[index] = get##name##File(index);                                                                                                                       \
       }                                                                                                                                                                       \
       setMaskBits(mask);                                                                                                                                                      \
    };                                                                                                                                                                         \

+ 113 - 43
Engine/source/T3D/assets/ShapeAsset.cpp

@@ -60,12 +60,20 @@ StringTableEntry ShapeAsset::smNoShapeAssetFallback = NULL;
 
 IMPLEMENT_CONOBJECT(ShapeAsset);
 
-ConsoleType(assetIdString, TypeShapeAssetPtr, String, ASSET_ID_FIELD_PREFIX)
+
+//-----------------------------------------------------------------------------
+// REFACTOR
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_STRUCT(AssetPtr<ShapeAsset>, AssetPtrShapeAsset, , "")
+END_IMPLEMENT_STRUCT
+
+ConsoleType(ShapeAssetPtr, TypeShapeAssetPtr, AssetPtr<ShapeAsset>, ASSET_ID_FIELD_PREFIX)
+
 
 ConsoleGetType(TypeShapeAssetPtr)
 {
    // Fetch asset Id.
-   //return *((StringTableEntry*)dptr);
    return (*((AssetPtr<ShapeAsset>*)dptr)).getAssetId();
 }
 
@@ -77,17 +85,24 @@ ConsoleSetType(TypeShapeAssetPtr)
       // Yes, so fetch field value.
       const char* pFieldValue = argv[0];
 
-      // Fetch asset Id.
-      StringTableEntry* assetId = (StringTableEntry*)(dptr);
+      // Fetch asset pointer.
+      AssetPtr<ShapeAsset>* pAssetPtr = dynamic_cast<AssetPtr<ShapeAsset>*>((AssetPtrBase*)(dptr));
 
-      // Update asset value.
-      *assetId = StringTable->insert(pFieldValue);
+      // Is the asset pointer the correct type?
+      if (pAssetPtr == NULL)
+      {
+         Con::warnf("(TypeShapeAssetPtr) - Failed to set asset Id '%d'.", pFieldValue);
+         return;
+      }
+
+      // Set asset.
+      pAssetPtr->setAssetId(pFieldValue);
 
       return;
    }
 
    // Warn.
-   Con::warnf("(TypeAssetId) - Cannot set multiple args to a single asset.");
+   Con::warnf("(TypeShapeAssetPtr) - Cannot set multiple args to a single asset.");
 }
 
 //-----------------------------------------------------------------------------
@@ -115,6 +130,8 @@ ConsoleSetType(TypeShapeAssetId)
    Con::warnf("(TypeAssetId) - Cannot set multiple args to a single asset.");
 }
 
+//-----------------------------------------------------------------------------
+// REFACTOR END
 //-----------------------------------------------------------------------------
 
 const String ShapeAsset::mErrCodeStrings[] =
@@ -128,15 +145,11 @@ const String ShapeAsset::mErrCodeStrings[] =
 
 ShapeAsset::ShapeAsset()
 {
-   mFileName = StringTable->EmptyString();
+   mShapeFile = StringTable->EmptyString();
    mConstructorFileName = StringTable->EmptyString();
-   mFilePath = StringTable->EmptyString();
-   mConstructorFilePath = StringTable->EmptyString();
 
    mDiffuseImposterFileName = StringTable->EmptyString();
-   mDiffuseImposterPath = StringTable->EmptyString();
    mNormalImposterFileName = StringTable->EmptyString();
-   mNormalImposterPath = StringTable->EmptyString();
 
 
    mLoadedState = AssetErrCode::NotLoaded;
@@ -169,7 +182,7 @@ void ShapeAsset::initPersistFields()
    // Call parent.
    Parent::initPersistFields();
 
-   addProtectedField("fileName", TypeAssetLooseFilePath, Offset(mFileName, ShapeAsset),
+   addProtectedField("fileName", TypeAssetLooseFilePath, Offset(mShapeFile, ShapeAsset),
       &setShapeFile, &getShapeFile, "Path to the shape file we want to render");
    addProtectedField("constuctorFileName", TypeAssetLooseFilePath, Offset(mConstructorFileName, ShapeAsset),
       &setShapeConstructorFile, &getShapeConstructorFile, "Path to the shape file we want to render");
@@ -200,29 +213,31 @@ void ShapeAsset::initializeAsset()
    // Call parent.
    Parent::initializeAsset();
 
-   if (mFileName == StringTable->EmptyString())
+   if (mShapeFile == StringTable->EmptyString())
       return;
 
    ResourceManager::get().getChangedSignal().notify(this, &ShapeAsset::_onResourceChanged);
 
    //Ensure our path is expando'd if it isn't already
-   mFilePath = getOwned() ? expandAssetFilePath(mFileName) : mFilePath;
+   mShapeFile = getOwned() ? expandAssetFilePath(mShapeFile) : mShapeFile;
 
-   mConstructorFilePath = getOwned() ? expandAssetFilePath(mConstructorFileName) : mConstructorFilePath;
-   if (!Torque::FS::IsFile(mConstructorFilePath))
-      Con::errorf("ShapeAsset::initializeAsset (%s) could not find %s!", getAssetName(), mConstructorFilePath);
-   mDiffuseImposterPath = getOwned() ? expandAssetFilePath(mDiffuseImposterFileName) : mDiffuseImposterFileName;
-   if (mDiffuseImposterPath == StringTable->EmptyString())
+   mConstructorFileName = getOwned() ? expandAssetFilePath(mConstructorFileName) : mConstructorFileName;
+   if (!Torque::FS::IsFile(mConstructorFileName))
+      Con::errorf("ShapeAsset::initializeAsset (%s) could not find %s!", getAssetName(), mConstructorFileName);
+
+
+   mDiffuseImposterFileName = getOwned() ? expandAssetFilePath(mDiffuseImposterFileName) : mDiffuseImposterFileName;
+   if (mDiffuseImposterFileName == StringTable->EmptyString())
    {
-      String diffusePath = String(mFilePath) + "_imposter.dds";
-      mDiffuseImposterPath = StringTable->insert(diffusePath.c_str());
+      String diffusePath = String(mShapeFile) + "_imposter.dds";
+      mDiffuseImposterFileName = StringTable->insert(diffusePath.c_str());
    }
 
-   mNormalImposterPath = getOwned() ? expandAssetFilePath(mNormalImposterFileName) : mNormalImposterFileName;
-   if (mNormalImposterPath == StringTable->EmptyString())
+   mNormalImposterFileName = getOwned() ? expandAssetFilePath(mNormalImposterFileName) : mNormalImposterFileName;
+   if (mNormalImposterFileName == StringTable->EmptyString())
    {
-      String normalPath = String(mFilePath) + "_imposter_normals.dds";
-      mNormalImposterPath = StringTable->insert(normalPath.c_str());
+      String normalPath = String(mShapeFile) + "_imposter_normals.dds";
+      mNormalImposterFileName = StringTable->insert(normalPath.c_str());
    }
 }
 
@@ -235,10 +250,10 @@ void ShapeAsset::setShapeFile(const char* pShapeFile)
    pShapeFile = StringTable->insert(pShapeFile, true);
 
    // Ignore no change,
-   if (pShapeFile == mFileName)
+   if (pShapeFile == mShapeFile)
       return;
 
-   mFileName = getOwned() ? expandAssetFilePath(pShapeFile) : pShapeFile;
+   mShapeFile = getOwned() ? expandAssetFilePath(pShapeFile) : pShapeFile;
 
    // Refresh the asset.
    refreshAsset();
@@ -300,7 +315,7 @@ void ShapeAsset::setNormalImposterFile(const char* pImageFile)
 
 void ShapeAsset::_onResourceChanged(const Torque::Path &path)
 {
-   if (path != Torque::Path(mFilePath) )
+   if (path != Torque::Path(mShapeFile) )
       return;
 
    refreshAsset();
@@ -349,17 +364,17 @@ U32 ShapeAsset::load()
       }
    }
 
-   mShape = ResourceManager::get().load(mFilePath);
+   mShape = ResourceManager::get().load(mShapeFile);
 
    if (!mShape)
    {
-      Con::errorf("ShapeAsset::loadShape : failed to load shape file %s (%s)!", getAssetName(), mFilePath);
+      Con::errorf("ShapeAsset::loadShape : failed to load shape file %s (%s)!", getAssetName(), mShapeFile);
       mLoadedState = BadFileReference;
       return mLoadedState; //if it failed to load, bail out
    }
    // Construct billboards if not done already
    if (GFXDevice::devicePresent())
-      mShape->setupBillboardDetails(mFilePath, mDiffuseImposterPath, mNormalImposterPath);
+      mShape->setupBillboardDetails(mShapeFile, mDiffuseImposterFileName, mNormalImposterFileName);
 
    //If they exist, grab our imposters here and bind them to our shapeAsset
 
@@ -419,8 +434,6 @@ U32 ShapeAsset::load()
 
    mLoadedState = Ok;
 
-   mChangeSignal.trigger();
-
    return mLoadedState;
 }
 
@@ -478,7 +491,38 @@ StringTableEntry ShapeAsset::getAssetIdByFilename(StringTableEntry fileName)
    }
    else
    {
-      AssetPtr<ShapeAsset> shapeAsset = shapeAssetId; //ensures the fallback is loaded
+      foundAssetcount = AssetDatabase.findAssetType(&query, "ShapeAsset");
+      if (foundAssetcount != 0)
+      {
+         // loop all image assets and see if we can find one
+         // using the same image file/named target.
+         for (auto shapeAsset : query.mAssetList)
+         {
+            AssetPtr<ShapeAsset> temp = shapeAsset;
+            if (temp.notNull())
+            {
+               if (temp->getShapeFile() == fileName)
+               {
+                  return shapeAsset;
+               }
+               else
+               {
+                  Torque::Path temp1 = temp->getShapeFile();
+                  Torque::Path temp2 = fileName;
+
+                  if (temp1.getPath() == temp2.getPath() && temp1.getFileName() == temp2.getFileName())
+                  {
+                     return shapeAsset;
+                  }
+               }
+
+            }
+         }
+      }
+      else
+      {
+         AssetPtr<ShapeAsset> shapeAsset = shapeAssetId; //ensures the fallback is loaded
+      }
    }
 
    return shapeAssetId;
@@ -527,16 +571,43 @@ void ShapeAsset::copyTo(SimObject* object)
 
 void ShapeAsset::onAssetRefresh(void)
 {
-   if (mFileName == StringTable->EmptyString())
+   // Ignore if not yet added to the sim.
+   if (!isProperlyAdded())
       return;
 
-   // Update.
-   if(!Platform::isFullPath(mFileName))
-      mFilePath = getOwned() ? expandAssetFilePath(mFileName) : mFilePath;
+   if (mShapeFile == StringTable->EmptyString())
+      return;
+
+   // Call parent.
+   Parent::onAssetRefresh();
 
    load();
 }
 
+void ShapeAsset::onTamlPreWrite(void)
+{
+   // Call parent.
+   Parent::onTamlPreWrite();
+
+   // ensure paths are collapsed.
+   mShapeFile                 = collapseAssetFilePath(mShapeFile);
+   mConstructorFileName       = collapseAssetFilePath(mConstructorFileName);
+   mDiffuseImposterFileName   = collapseAssetFilePath(mDiffuseImposterFileName);
+   mNormalImposterFileName    = collapseAssetFilePath(mNormalImposterFileName);
+}
+
+void ShapeAsset::onTamlPostWrite(void)
+{
+   // Call parent.
+   Parent::onTamlPostWrite();
+
+   // ensure paths are expanded.
+   mShapeFile                 = expandAssetFilePath(mShapeFile);
+   mConstructorFileName       = expandAssetFilePath(mConstructorFileName);
+   mDiffuseImposterFileName   = expandAssetFilePath(mDiffuseImposterFileName);
+   mNormalImposterFileName    = expandAssetFilePath(mNormalImposterFileName);
+}
+
 void ShapeAsset::SplitSequencePathAndName(String& srcPath, String& srcName)
 {
    srcName = "";
@@ -626,7 +697,7 @@ const char* ShapeAsset::generateCachedPreviewImage(S32 resolution, String overri
    delete imposterCap;
    delete shape;
 
-   String dumpPath = String(mFilePath) + ".png";
+   String dumpPath = String(mShapeFile) + ".png";
 
    char* returnBuffer = Con::getReturnBuffer(128);
    dSprintf(returnBuffer, 128, "%s", dumpPath.c_str());
@@ -670,14 +741,14 @@ DefineEngineMethod(ShapeAsset, getShapePath, const char*, (), ,
    "Gets the shape's file path\n"
    "@return The filename of the shape file")
 {
-   return object->getShapeFilePath();
+   return object->getShapeFile();
 }
 
 DefineEngineMethod(ShapeAsset, getShapeConstructorFilePath, const char*, (), ,
    "Gets the shape's constructor file.\n"
    "@return The filename of the shape constructor file")
 {
-   return object->getShapeConstructorFilePath();
+   return object->getShapeConstructorFile();
 }
 
 DefineEngineMethod(ShapeAsset, getStatusString, String, (), , "get status string")\
@@ -913,5 +984,4 @@ void GuiInspectorTypeShapeAssetId::consoleInit()
 
    ConsoleBaseType::getType(TypeShapeAssetId)->setInspectorFieldType("GuiInspectorTypeShapeAssetId");
 }
-
 #endif

+ 278 - 284
Engine/source/T3D/assets/ShapeAsset.h

@@ -67,33 +67,6 @@ class ShapeAsset : public AssetBase
    typedef AssetBase Parent;
    typedef AssetPtr<ShapeAsset> ConcreteAssetPtr;
 
-protected:
-   StringTableEntry   mFileName;
-   StringTableEntry   mConstructorFileName;
-   StringTableEntry   mFilePath;
-   StringTableEntry   mConstructorFilePath;
-   Resource<TSShape>	 mShape;
-
-   StringTableEntry   mDiffuseImposterFileName;
-   StringTableEntry   mDiffuseImposterPath;
-
-   StringTableEntry   mNormalImposterFileName;
-   StringTableEntry   mNormalImposterPath;
-
-   //Material assets we're dependent on and use
-   Vector<StringTableEntry> mMaterialAssetIds;
-   Vector<AssetPtr<MaterialAsset>> mMaterialAssets;
-
-   //Animation assets we're dependent on and use
-   Vector<StringTableEntry> mAnimationAssetIds;
-   Vector<AssetPtr<ShapeAnimationAsset>> mAnimationAssets;
-
-   typedef Signal<void()> ShapeAssetChanged;
-   ShapeAssetChanged mChangeSignal;
-
-   typedef Signal<void(S32 index)> ShapeAssetArrayChanged;
-   ShapeAssetArrayChanged mChangeArraySignal;
-
 public:
    enum ShapeAssetErrCode
    {
@@ -115,6 +88,23 @@ public:
       return mErrCodeStrings[errCode - Parent::Extended];
    };
 
+private:
+   StringTableEntry   mShapeFile;
+   StringTableEntry   mConstructorFileName;
+   StringTableEntry   mDiffuseImposterFileName;
+   StringTableEntry   mNormalImposterFileName;
+
+   //Material assets we're dependent on and use
+   Vector<StringTableEntry> mMaterialAssetIds;
+   Vector<AssetPtr<MaterialAsset>> mMaterialAssets;
+
+   //Animation assets we're dependent on and use
+   Vector<StringTableEntry> mAnimationAssetIds;
+   Vector<AssetPtr<ShapeAnimationAsset>> mAnimationAssets;
+
+   Resource<TSShape>	 mShape;
+public:
+
    ShapeAsset();
    virtual ~ShapeAsset();
 
@@ -127,8 +117,6 @@ public:
 
    virtual void setDataField(StringTableEntry slotName, StringTableEntry array, StringTableEntry value);
 
-   void initializeAsset() override;
-
    /// Declare Console Object.
    DECLARE_CONOBJECT(ShapeAsset);
 
@@ -139,10 +127,8 @@ public:
    Resource<TSShape> getShapeResource() { load(); return mShape; }
 
    void SplitSequencePathAndName(String& srcPath, String& srcName);
-   StringTableEntry getShapeFileName() { return mFileName; }
-   StringTableEntry getShapePath() { return mFilePath; }
 
-   U32 getShapeFilenameHash() { return _StringTable::hashString(mFilePath); }
+   U32 getShapeFilenameHash() { return _StringTable::hashString(mShapeFile); }
 
    Vector<AssetPtr<MaterialAsset>> getMaterialAssets() { return mMaterialAssets; }
 
@@ -164,26 +150,18 @@ public:
 
    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; };
+   inline StringTableEntry getShapeFile(void) const { return mShapeFile; };
 
    void                    setShapeConstructorFile(const char* pScriptFile);
    inline StringTableEntry getShapeConstructorFile(void) const { return mConstructorFileName; };
 
-   inline StringTableEntry getShapeFilePath(void) const { return mFilePath; };
-   inline StringTableEntry getShapeConstructorFilePath(void) const { return mConstructorFilePath; };
-
    //Imposter images
    void                    setDiffuseImposterFile(const char* pImageFile);
    inline StringTableEntry getDiffuseImposterFile(void) const { return mDiffuseImposterFileName; };
-   inline StringTableEntry getDiffuseImposterFilePath(void) const { return mDiffuseImposterPath; };
 
    void                    setNormalImposterFile(const char* pImageFile);
    inline StringTableEntry getNormalImposterFile(void) const { return mNormalImposterFileName; };
-   inline StringTableEntry getNormalImposterFilePath(void) const { return mNormalImposterPath; };
 
    static U32 getAssetByFilename(StringTableEntry fileName, AssetPtr<ShapeAsset>* shapeAsset);
 
@@ -195,23 +173,34 @@ public:
 #endif
 
 protected:
-   void            onAssetRefresh(void) override;
+   // Asset Base callback
+   void initializeAsset(void) override;
+   void onAssetRefresh(void) override;
 
+   /// Taml callbacks.
+   void onTamlPreWrite(void) override;
+   void onTamlPostWrite(void) override;
+
+protected:
    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 writeShapeFile(void* obj, StringTableEntry pFieldName) { return static_cast<ShapeAsset*>(obj)->getShapeFile() != StringTable->EmptyString(); }
 
    static bool setShapeConstructorFile(void* obj, const char* index, const char* data) { static_cast<ShapeAsset*>(obj)->setShapeConstructorFile(data); return false; }
    static const char* getShapeConstructorFile(void* obj, const char* data) { return static_cast<ShapeAsset*>(obj)->getShapeConstructorFile(); }
 
    static bool setDiffuseImposterFile(void* obj, StringTableEntry index, StringTableEntry data) { static_cast<ShapeAsset*>(obj)->setDiffuseImposterFile(data); return false; }
    static const char* getDiffuseImposterFile(void* obj, const char* data) { return static_cast<ShapeAsset*>(obj)->getDiffuseImposterFile(); }
+
    static bool setNormalImposterFile(void* obj, StringTableEntry index, StringTableEntry data) { static_cast<ShapeAsset*>(obj)->setNormalImposterFile(data); return false; }
    static const char* getNormalImposterFile(void* obj, const char* data) { return static_cast<ShapeAsset*>(obj)->getNormalImposterFile(); }
 };
 
-DefineConsoleType(TypeShapeAssetPtr, S32)
 DefineConsoleType(TypeShapeAssetId, String)
 
+DECLARE_STRUCT(AssetPtr<ShapeAsset>)
+DefineConsoleType(TypeShapeAssetPtr, AssetPtr<ShapeAsset>)
+
 #ifdef TORQUE_TOOLS
 //-----------------------------------------------------------------------------
 // TypeAssetId GuiInspectorField Class
@@ -246,251 +235,256 @@ public:
    DECLARE_CONOBJECT(GuiInspectorTypeShapeAssetId);
    static void consoleInit();
 };
-#endif
 
-#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;}\
-   \
-   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 == NULL || _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 = 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 (!m##name)\
-      {\
-         Con::errorf("%s(%s)::_set%s() - Couldn't load shape \"%s\"", macroText(className), getName(), macroText(name), _in);\
-         return false;\
-      }\
-      return true;\
-   }\
-   \
-   const StringTableEntry get##name() const\
-   {\
-      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();\
-   }\
-   Resource<TSShape> get##name##Resource() \
-   {\
-      return m##name;\
-   }\
-   bool is##name##Valid() {return (get##name() != StringTable->EmptyString() && m##name##Asset->getStatus() == AssetBase::Ok); }
-
-#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
+#endif
 
-#pragma endregion
+//-----------------------------------------------------------------------------
+// REFACTOR
+//-----------------------------------------------------------------------------
 
-#pragma region Arrayed Asset Macros
-
-#define DECLARE_SHAPEASSET_ARRAY(className,name,max,changeFunc) 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##Asset[index].notNull())\
-      {\
-            m##name##Asset[index]->getChangedSignal().remove(this, &className::changeFunc);\
-      }\
-      if(m##name##AssetId[index] != _in || m##name##Name[index] != _in)\
-      {\
-         if(index >= sm##name##Count || index < 0)\
-            return false;\
-         if (_in == NULL || _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();\
-         \
-         m##name##Asset[index]->getChangedSignal().notify(this, &className::changeFunc);\
-      }\
-      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 (!m##name[index])\
-      {\
-         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 ResourceManager::get().load( "" );\
-      return m##name[index];\
-   }\
-   bool is##name##Valid(const U32& id) {return (get##name(id) != StringTable->EmptyString() && m##name##Asset[id]->getStatus() == AssetBase::Ok); }
-
-#ifdef TORQUE_SHOW_LEGACY_FILE_FIELDS
-
-#define INITPERSISTFIELD_SHAPEASSET_ARRAY(name, arraySize, consoleClass, docs) \
-   addProtectedField(assetText(name, File), TypeShapeFilename, Offset(m##name##Name, consoleClass), _set##name##Data, & defaultProtectedGetFn, arraySize, assetText(name, docs)); \
-   addProtectedField(assetText(name, Asset), TypeShapeAssetId, Offset(m##name##AssetId, consoleClass), _set##name##Data, & defaultProtectedGetFn, arraySize, assetText(name, asset reference.));
-
-#else
-
-#define INITPERSISTFIELD_SHAPEASSET_ARRAY(name, arraySize, consoleClass, docs) \
-   addProtectedField(assetText(name, File), TypeShapeFilename, Offset(m##name##Name, consoleClass), _set##name##Data, & defaultProtectedGetFn, arraySize, assetText(name, docs), AbstractClassRep::FIELD_HideInInspectors); \
-   addProtectedField(assetText(name, Asset), TypeShapeAssetId, Offset(m##name##AssetId, consoleClass), _set##name##Data, & defaultProtectedGetFn, arraySize,assetText(name, asset reference.));
-
-#endif // SHOW_LEGACY_FILE_FIELDS
+#pragma region Refactor Asset Macros
+
+#define DECLARE_SHAPEASSET_REFACTOR(className, name)                                                                                                                          \
+private:                                                                                                                                                                      \
+   AssetPtr<ShapeAsset> m##name##Asset;                                                                                                                                       \
+   StringTableEntry     m##name##File = StringTable->EmptyString();                                                                                                           \
+public:                                                                                                                                                                       \
+   void _set##name(StringTableEntry _in) {                                                                                                                                    \
+      if (m##name##Asset.getAssetId() == _in)                                                                                                                                 \
+         return;                                                                                                                                                              \
+      if(get##name##File() == _in)                                                                                                                                            \
+         return;                                                                                                                                                              \
+      if(_in == NULL || _in == StringTable->EmptyString() || _in == "")                                                                                                       \
+      {                                                                                                                                                                       \
+         m##name##Asset = NULL;                                                                                                                                               \
+         m##name##File = "";                                                                                                                                                  \
+         return;                                                                                                                                                              \
+      }                                                                                                                                                                       \
+      if (!AssetDatabase.isDeclaredAsset(_in))                                                                                                                                \
+      {                                                                                                                                                                       \
+         StringTableEntry shapeAssetId = StringTable->EmptyString();                                                                                                          \
+         AssetQuery query;                                                                                                                                                    \
+         S32 foundAssetcount = AssetDatabase.findAssetLooseFile(&query, _in);                                                                                                 \
+         if (foundAssetcount != 0)                                                                                                                                            \
+         {                                                                                                                                                                    \
+            shapeAssetId = query.mAssetList[0];                                                                                                                               \
+         }                                                                                                                                                                    \
+         else if (Torque::FS::IsFile(_in) || (_in[0] == '$' || _in[0] == '#'))                                                                                                \
+         {                                                                                                                                                                    \
+            shapeAssetId = ShapeAsset::getAssetIdByFilename(_in);                                                                                                             \
+            if (shapeAssetId == ShapeAsset::smNoShapeAssetFallback)                                                                                                           \
+            {                                                                                                                                                                 \
+               ShapeAsset* privateShape = new ShapeAsset();                                                                                                                   \
+               privateShape->setShapeFile(_in);                                                                                                                               \
+               shapeAssetId = AssetDatabase.addPrivateAsset(privateShape);                                                                                                    \
+            }                                                                                                                                                                 \
+         }                                                                                                                                                                    \
+         else                                                                                                                                                                 \
+         {                                                                                                                                                                    \
+            Con::warnf("%s::%s: Could not find asset for: %s using fallback", #className, #name, _in);                                                                        \
+            shapeAssetId = ShapeAsset::smNoShapeAssetFallback;                                                                                                                \
+         }                                                                                                                                                                    \
+         m##name##Asset = shapeAssetId;                                                                                                                                       \
+         m##name##File = _in;                                                                                                                                                 \
+      }                                                                                                                                                                       \
+      else                                                                                                                                                                    \
+      {                                                                                                                                                                       \
+         m##name##Asset = _in;                                                                                                                                                \
+         m##name##File = get##name##File();                                                                                                                                   \
+      }                                                                                                                                                                       \
+   };                                                                                                                                                                         \
+                                                                                                                                                                              \
+   inline StringTableEntry _get##name##AssetId(void) const { return m##name##Asset.getAssetId(); }                                                                            \
+   Resource<TSShape> get##name() { if (m##name##Asset.notNull()) return m##name##Asset->getShapeResource(); else return ResourceManager::get().load( "" ); }                  \
+   AssetPtr<ShapeAsset> get##name##Asset(void) { return m##name##Asset; }                                                                                                     \
+   static bool _set##name##Data(void* obj, const char* index, const char* data) { static_cast<className*>(obj)->_set##name(_getStringTable()->insert(data)); return false; }  \
+   StringTableEntry get##name##File() { return m##name##Asset.notNull() ? m##name##Asset->getShapeFile() : ""; }
+
+#define DECLARE_SHAPEASSET_NET_REFACTOR(className, name, mask)                                                                                                                \
+private:                                                                                                                                                                      \
+   AssetPtr<ShapeAsset> m##name##Asset;                                                                                                                                       \
+   StringTableEntry     m##name##File = StringTable->EmptyString();                                                                                                           \
+public:                                                                                                                                                                       \
+   void _set##name(StringTableEntry _in) {                                                                                                                                    \
+      if (m##name##Asset.getAssetId() == _in)                                                                                                                                 \
+         return;                                                                                                                                                              \
+      if(get##name##File() == _in)                                                                                                                                            \
+         return;                                                                                                                                                              \
+      if(_in == NULL || _in == StringTable->EmptyString() || _in == "")                                                                                                       \
+      {                                                                                                                                                                       \
+         m##name##Asset = NULL;                                                                                                                                               \
+         m##name##File = "";                                                                                                                                                  \
+         setMaskBits(mask);                                                                                                                                                   \
+         return;                                                                                                                                                              \
+      }                                                                                                                                                                       \
+      if (!AssetDatabase.isDeclaredAsset(_in))                                                                                                                                \
+      {                                                                                                                                                                       \
+         StringTableEntry shapeAssetId = StringTable->EmptyString();                                                                                                          \
+         AssetQuery query;                                                                                                                                                    \
+         S32 foundAssetcount = AssetDatabase.findAssetLooseFile(&query, _in);                                                                                                 \
+         if (foundAssetcount != 0)                                                                                                                                            \
+         {                                                                                                                                                                    \
+            shapeAssetId = query.mAssetList[0];                                                                                                                               \
+         }                                                                                                                                                                    \
+         else if (Torque::FS::IsFile(_in) || (_in[0] == '$' || _in[0] == '#'))                                                                                                \
+         {                                                                                                                                                                    \
+            shapeAssetId = ShapeAsset::getAssetIdByFilename(_in);                                                                                                             \
+            if (shapeAssetId == ShapeAsset::smNoShapeAssetFallback)                                                                                                           \
+            {                                                                                                                                                                 \
+               ShapeAsset* privateShape = new ShapeAsset();                                                                                                                   \
+               privateShape->setShapeFile(_in);                                                                                                                               \
+               shapeAssetId = AssetDatabase.addPrivateAsset(privateShape);                                                                                                    \
+            }                                                                                                                                                                 \
+         }                                                                                                                                                                    \
+         else                                                                                                                                                                 \
+         {                                                                                                                                                                    \
+            Con::warnf("%s::%s: Could not find asset for: %s using fallback", #className, #name, _in);                                                                        \
+            shapeAssetId = ShapeAsset::smNoShapeAssetFallback;                                                                                                                \
+         }                                                                                                                                                                    \
+         m##name##Asset = shapeAssetId;                                                                                                                                       \
+         m##name##File = _in;                                                                                                                                                 \
+      }                                                                                                                                                                       \
+      else                                                                                                                                                                    \
+      {                                                                                                                                                                       \
+         m##name##Asset = _in;                                                                                                                                                \
+         m##name##File = get##name##File();                                                                                                                                   \
+      }                                                                                                                                                                       \
+      setMaskBits(mask);                                                                                                                                                      \
+   };                                                                                                                                                                         \
+                                                                                                                                                                              \
+   inline StringTableEntry _get##name##AssetId(void) const { return m##name##Asset.getAssetId(); }                                                                            \
+   Resource<TSShape> get##name() { if (m##name##Asset.notNull()) return m##name##Asset->getShapeResource(); else return ResourceManager::get().load( "" ); }                  \
+   AssetPtr<ShapeAsset> get##name##Asset(void) { return m##name##Asset; }                                                                                                     \
+   static bool _set##name##Data(void* obj, const char* index, const char* data) { static_cast<className*>(obj)->_set##name(_getStringTable()->insert(data)); return false; }  \
+   StringTableEntry get##name##File() { return m##name##Asset.notNull() ? m##name##Asset->getShapeFile() : ""; }  
+
+#define INITPERSISTFIELD_SHAPEASSET_REFACTOR(name, consoleClass, docs)                                                                                                                \
+   addProtectedField(assetText(name, Asset), TypeShapeAssetPtr, Offset(m##name##Asset, consoleClass), _set##name##Data, &defaultProtectedGetFn, assetDoc(name, asset docs.)); \
+   addProtectedField(assetText(name, File), TypeFilename, Offset(m##name##File, consoleClass), _set##name##Data, &defaultProtectedGetFn, assetDoc(name, file docs.));
+
+
+#define DECLARE_SHAPEASSET_ARRAY_REFACTOR(className, name, max)                                                                                                               \
+private:                                                                                                                                                                      \
+   AssetPtr<ShapeAsset> m##name##Asset[max];                                                                                                                                  \
+   StringTableEntry     m##name##File[max] = {StringTable->EmptyString() };                                                                                                   \
+public:                                                                                                                                                                       \
+   void _set##name(StringTableEntry _in, const U32& index){                                                                                                                   \
+      if (m##name##Asset[index].getAssetId() == _in)                                                                                                                          \
+         return;                                                                                                                                                              \
+      if(get##name##File(index) == _in)                                                                                                                                       \
+         return;                                                                                                                                                              \
+      if(_in == NULL || _in == StringTable->EmptyString() || _in == "")                                                                                                       \
+      {                                                                                                                                                                       \
+         m##name##Asset[index] = NULL;                                                                                                                                        \
+         m##name##File[index] = "";                                                                                                                                           \
+         return;                                                                                                                                                              \
+      }                                                                                                                                                                       \
+      if (!AssetDatabase.isDeclaredAsset(_in))                                                                                                                                \
+      {                                                                                                                                                                       \
+         StringTableEntry shapeAssetId = StringTable->EmptyString();                                                                                                          \
+         AssetQuery query;                                                                                                                                                    \
+         S32 foundAssetcount = AssetDatabase.findAssetLooseFile(&query, _in);                                                                                                 \
+         if (foundAssetcount != 0)                                                                                                                                            \
+         {                                                                                                                                                                    \
+            shapeAssetId = query.mAssetList[0];                                                                                                                               \
+         }                                                                                                                                                                    \
+         else if (Torque::FS::IsFile(_in) || (_in[0] == '$' || _in[0] == '#'))                                                                                                \
+         {                                                                                                                                                                    \
+            shapeAssetId = ShapeAsset::getAssetIdByFilename(_in);                                                                                                             \
+            if (shapeAssetId == ShapeAsset::smNoShapeAssetFallback)                                                                                                           \
+            {                                                                                                                                                                 \
+               ShapeAsset* privateShape = new ShapeAsset();                                                                                                                   \
+               privateShape->setShapeFile(_in);                                                                                                                               \
+               shapeAssetId = AssetDatabase.addPrivateAsset(privateShape);                                                                                                    \
+            }                                                                                                                                                                 \
+         }                                                                                                                                                                    \
+         else                                                                                                                                                                 \
+         {                                                                                                                                                                    \
+            Con::warnf("%s::%s: Could not find asset for: %s using fallback", #className, #name, _in);                                                                        \
+            shapeAssetId = ShapeAsset::smNoShapeAssetFallback;                                                                                                                \
+         }                                                                                                                                                                    \
+         m##name##Asset[index] = shapeAssetId;                                                                                                                                \
+         m##name##File[index] = _in;                                                                                                                                          \
+      }                                                                                                                                                                       \
+      else                                                                                                                                                                    \
+      {                                                                                                                                                                       \
+         m##name##Asset[index] = _in;                                                                                                                                         \
+         m##name##File[index] = get##name##File(index);                                                                                                                       \
+      }                                                                                                                                                                       \
+   };                                                                                                                                                                         \
+                                                                                                                                                                              \
+   inline StringTableEntry _get##name##AssetId(const U32& index) const { return m##name##Asset[index].getAssetId(); }                                                         \
+   Resource<TSShape> get##name(const U32& index) { if (m##name##Asset[index].notNull()) return m##name##Asset[index]->getShapeResource(); else return ResourceManager::get().load( "" ); } \
+   AssetPtr<ShapeAsset> get##name##Asset(const U32& index) { return m##name##Asset[index]; }                                                                                  \
+   static bool _set##name##Data(void* obj, const char* index, const char* data) { static_cast<className*>(obj)->_set##name(_getStringTable()->insert(data), dAtoi(index)); return false;}\
+   StringTableEntry get##name##File(const U32& idx) { return m##name##Asset[idx].notNull() ? m##name##Asset[idx]->getShapeFile() : ""; }
+
+#define DECLARE_SHAPEASSET_ARRAY_NET_REFACTOR(className, name, max, mask)                                                                                                     \
+private:                                                                                                                                                                      \
+   AssetPtr<ShapeAsset> m##name##Asset[max];                                                                                                                                  \
+   StringTableEntry     m##name##File[max] = {StringTable->EmptyString() };                                                                                                   \
+public:                                                                                                                                                                       \
+   void _set##name(StringTableEntry _in, const U32& index){                                                                                                                   \
+      if (m##name##Asset[index].getAssetId() == _in)                                                                                                                          \
+         return;                                                                                                                                                              \
+      if(get##name##File(index) == _in)                                                                                                                                       \
+         return;                                                                                                                                                              \
+      if (_in == NULL || _in == StringTable->EmptyString())                                                                                                                   \
+      {                                                                                                                                                                       \
+         m##name##Asset[index] = NULL;                                                                                                                                        \
+         m##name##File[index] = "";                                                                                                                                           \
+         setMaskBits(mask);                                                                                                                                                   \
+         return;                                                                                                                                                              \
+      }                                                                                                                                                                       \
+      if (!AssetDatabase.isDeclaredAsset(_in))                                                                                                                                \
+      {                                                                                                                                                                       \
+         StringTableEntry shapeAssetId = StringTable->EmptyString();                                                                                                          \
+         AssetQuery query;                                                                                                                                                    \
+         S32 foundAssetcount = AssetDatabase.findAssetLooseFile(&query, _in);                                                                                                 \
+         if (foundAssetcount != 0)                                                                                                                                            \
+         {                                                                                                                                                                    \
+            shapeAssetId = query.mAssetList[0];                                                                                                                               \
+         }                                                                                                                                                                    \
+         else if (Torque::FS::IsFile(_in) || (_in[0] == '$' || _in[0] == '#'))                                                                                                \
+         {                                                                                                                                                                    \
+            shapeAssetId = ShapeAsset::getAssetIdByFilename(_in);                                                                                                             \
+            if (shapeAssetId == ShapeAsset::smNoShapeAssetFallback)                                                                                                           \
+            {                                                                                                                                                                 \
+               ShapeAsset* privateShape = new ShapeAsset();                                                                                                                   \
+               privateShape->setShapeFile(_in);                                                                                                                               \
+               shapeAssetId = AssetDatabase.addPrivateAsset(privateShape);                                                                                                    \
+            }                                                                                                                                                                 \
+         }                                                                                                                                                                    \
+         else                                                                                                                                                                 \
+         {                                                                                                                                                                    \
+            Con::warnf("%s::%s: Could not find asset for: %s using fallback", #className, #name, _in);                                                                        \
+            shapeAssetId = ShapeAsset::smNoShapeAssetFallback;                                                                                                                \
+         }                                                                                                                                                                    \
+         m##name##Asset[index] = shapeAssetId;                                                                                                                                \
+         m##name##File[index] = _in;                                                                                                                                          \
+      }                                                                                                                                                                       \
+      else                                                                                                                                                                    \
+      {                                                                                                                                                                       \
+         m##name##Asset[index] = _in;                                                                                                                                         \
+         m##name##File[index] = get##name##File(index);                                                                                                                       \
+      }                                                                                                                                                                       \
+      setMaskBits(mask);                                                                                                                                                      \
+   };                                                                                                                                                                         \
+                                                                                                                                                                              \
+   inline StringTableEntry _get##name##AssetId(const U32& index) const { return m##name##Asset[index].getAssetId(); }                                                         \
+   Resource<TSShape> get##name(const U32& index) { if (m##name##Asset[index].notNull()) return m##name##Asset[index]->getShapeResource(); else return ResourceManager::get().load( "" ); } \
+   AssetPtr<ShapeAsset> get##name##Asset(const U32& index) { return m##name##Asset[index]; }                                                                                  \
+   static bool _set##name##Data(void* obj, const char* index, const char* data) { static_cast<className*>(obj)->_set##name(_getStringTable()->insert(data), dAtoi(index)); return false;}\
+   StringTableEntry get##name##File(const U32& idx) { return m##name##Asset[idx].notNull() ? m##name##Asset[idx]->getShapeFile() : ""; }
+
+#define INITPERSISTFIELD_SHAPEASSET_ARRAY_REFACTOR(name, arraySize, consoleClass, docs)                                                                                       \
+   addProtectedField(assetText(name, Asset), TypeShapeAssetPtr, Offset(m##name##Asset, consoleClass), _set##name##Data, &defaultProtectedGetFn, arraySize, assetDoc(name, asset docs.));\
+   addProtectedField(assetText(name, File), TypeFilename, Offset(m##name##File, consoleClass), _set##name##Data, &defaultProtectedGetFn, arraySize, assetDoc(name, asset docs.));
 
 #pragma endregion
 
+//-----------------------------------------------------------------------------
+// REFACTOR END
+//-----------------------------------------------------------------------------
+
 #endif

+ 24 - 92
Engine/source/T3D/assets/assetImporter.cpp

@@ -2805,6 +2805,7 @@ void AssetImporter::acquireAssets(AssetImportObject* assetItem)
       if (AssetDatabase.isDeclaredAsset(assetId))
       {
          AssetDatabase.acquireAsset<AssetBase>(assetId);
+         AssetDatabase.refreshAsset(assetId);
          AssetDatabase.releaseAsset(assetId);
       }
    }
@@ -2825,29 +2826,18 @@ Torque::Path AssetImporter::importImageAsset(AssetImportObject* assetItem)
    StringTableEntry assetName = StringTable->insert(assetItem->assetName.c_str());
 
    String imageFileName = assetItem->filePath.getFullFileName();
-   String assetPath = targetPath + "/" + imageFileName;
+   String assetPath = "@" + imageFileName;
    String tamlPath = targetPath + "/" + assetName + ".asset.taml";
    String originalPath = assetItem->filePath.getFullPath().c_str();
 
-   char qualifiedFromFile[2048];
-   char qualifiedToFile[2048];
-
-#ifndef TORQUE_SECURE_VFS
-   Platform::makeFullPathName(originalPath.c_str(), qualifiedFromFile, sizeof(qualifiedFromFile));
-   Platform::makeFullPathName(assetPath.c_str(), qualifiedToFile, sizeof(qualifiedToFile));
-#else
-   dStrcpy(qualifiedFromFile, originalPath.c_str(), sizeof(qualifiedFromFile));
-   dStrcpy(qualifiedToFile, assetPath.c_str(), sizeof(qualifiedToFile));
-#endif
-   
    newAsset->setAssetName(assetName);
    newAsset->setImageFile(assetPath.c_str());
 
    //If it's not a re-import, check that the file isn't being in-place imported. If it isn't, store off the original
    //file path for reimporting support later
-   if (!isReimport && String::compare(qualifiedFromFile, qualifiedToFile) && Torque::FS::IsFile(qualifiedFromFile))
+   if (!isReimport)
    {
-      newAsset->setDataField(StringTable->insert("originalFilePath"), nullptr, qualifiedFromFile);
+      newAsset->setDataField(StringTable->insert("originalFilePath"), nullptr, originalPath.c_str());
    }
 
    if (assetItem->typeHint != String::EmptyString)
@@ -2870,18 +2860,6 @@ Torque::Path AssetImporter::importImageAsset(AssetImportObject* assetItem)
       return "";
    }
 
-   if (!isReimport)
-   {
-      bool isInPlace = !String::compare(qualifiedFromFile, qualifiedToFile);
-
-      if (!isInPlace && !Torque::FS::CopyFile(qualifiedFromFile, qualifiedToFile, !isReimport))
-      {
-         dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Unable to copy file %s", assetItem->filePath.getFullPath().c_str());
-         activityLog.push_back(importLogBuffer);
-         return "";
-      }
-   }
-
    return tamlPath;
 }
 
@@ -3057,31 +3035,15 @@ Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem)
 
    StringTableEntry assetName = StringTable->insert(assetItem->assetName.c_str());
 
-   String shapeFileName = assetItem->filePath.getFileName() + "." + assetItem->filePath.getExtension();
+   String shapeFileName = "@" + assetItem->filePath.getFileName() + "." + assetItem->filePath.getExtension();
    String constructorFileName = assetItem->filePath.getFileName() + "." TORQUE_SCRIPT_EXTENSION;
    String assetPath = targetPath + "/" + shapeFileName;
    String constructorPath = targetPath + "/" + constructorFileName;
+   constructorFileName = "@" + constructorFileName;
    String tamlPath = targetPath + "/" + assetName + ".asset.taml";
    String originalPath = assetItem->filePath.getFullPath().c_str();
    String originalConstructorPath = assetItem->filePath.getPath() + "/" + constructorFileName;
 
-   char qualifiedFromFile[2048];
-   char qualifiedToFile[2048];
-   char qualifiedFromCSFile[2048];
-   char qualifiedToCSFile[2048];
-
-#ifndef TORQUE_SECURE_VFS
-   Platform::makeFullPathName(originalPath.c_str(), qualifiedFromFile, sizeof(qualifiedFromFile));
-   Platform::makeFullPathName(assetPath.c_str(), qualifiedToFile, sizeof(qualifiedToFile));
-   Platform::makeFullPathName(originalConstructorPath.c_str(), qualifiedFromCSFile, sizeof(qualifiedFromCSFile));
-   Platform::makeFullPathName(constructorPath.c_str(), qualifiedToCSFile, sizeof(qualifiedToCSFile));
-#else
-   dStrcpy(qualifiedFromFile, originalPath.c_str(), sizeof(qualifiedFromFile));
-   dStrcpy(qualifiedToFile, assetPath.c_str(), sizeof(qualifiedToFile));
-   dStrcpy(qualifiedFromCSFile, originalConstructorPath.c_str(), sizeof(qualifiedFromCSFile));
-   dStrcpy(qualifiedToCSFile, constructorPath.c_str(), sizeof(qualifiedToCSFile));
-#endif
-
    newAsset->setAssetName(assetName);
    newAsset->setShapeFile(shapeFileName.c_str());
    newAsset->setShapeConstructorFile(constructorFileName.c_str());
@@ -3098,9 +3060,9 @@ Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem)
 
    //If it's not a re-import, check that the file isn't being in-place imported. If it isn't, store off the original
    //file path for reimporting support later
-   if (!isReimport && String::compare(qualifiedFromFile, qualifiedToFile) && Torque::FS::IsFile(qualifiedFromFile))
+   if (!isReimport && Torque::FS::IsFile(originalPath))
    {
-      newAsset->setDataField(StringTable->insert("originalFilePath"), nullptr, qualifiedFromFile);
+      newAsset->setDataField(StringTable->insert("originalFilePath"), nullptr, originalPath.c_str());
    }
 
    //iterate through and write out the material maps dependencies
@@ -3140,8 +3102,8 @@ Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem)
 
    if (Con::getBoolVariable("$TSLastDetail::dumpImposters", false))
    {
-      String imposterPath = assetItem->assetName + "_imposter.png";
-      String normalsPath = assetItem->assetName + "_imposter_normals.png";
+      String imposterPath = "@" + assetItem->assetName + "_imposter.png";
+      String normalsPath = "@" + assetItem->assetName + "_imposter_normals.png";
 
       newAsset->setDiffuseImposterFile(imposterPath.c_str());
       newAsset->setNormalImposterFile(normalsPath.c_str());
@@ -3160,67 +3122,37 @@ Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem)
    bool makeNewConstructor = true;
    if (!isReimport)
    {
-      bool isInPlace = !String::compare(qualifiedFromFile, qualifiedToFile);
-
-      if (!isInPlace && !Torque::FS::CopyFile(qualifiedFromFile, qualifiedToFile, !isReimport))
+      //We're doing an in-place import, so double check we've already got a constructor file in the expected spot
+      if (Torque::FS::IsFile(constructorPath))
       {
-         dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Unable to copy file %s", qualifiedFromFile);
+         //Yup, found it, we're good to go
+         makeNewConstructor = false;
+         dSprintf(importLogBuffer, sizeof(importLogBuffer), "Existing TSShape Constructor file %s found", constructorPath.c_str());
          activityLog.push_back(importLogBuffer);
-         return "";
-      }
-
-      if (!isInPlace)
-      {
-         if (Torque::FS::IsFile(qualifiedFromCSFile))
-         {
-            if (!Torque::FS::CopyFile(qualifiedFromCSFile, qualifiedToCSFile, !isReimport))
-            {
-               dSprintf(importLogBuffer, sizeof(importLogBuffer), "Error! Unable to copy file %s", qualifiedFromCSFile);
-               activityLog.push_back(importLogBuffer);
-            }
-            else
-            {
-               //We successfully copied the original constructor file, so no extra work required
-               makeNewConstructor = false;
-               dSprintf(importLogBuffer, sizeof(importLogBuffer), "Successfully copied original TSShape Constructor file %s", qualifiedFromCSFile);
-               activityLog.push_back(importLogBuffer);
-            }
-         }
       }
       else
       {
-         //We're doing an in-place import, so double check we've already got a constructor file in the expected spot
-         if (Torque::FS::IsFile(qualifiedFromCSFile))
+         //Didn't work, but it's possible it's using the old .cs extension when our extension variable is set to something else, so check that one as well just to be sure
+         Torque::Path constrFilePath = constructorPath;
+         constrFilePath.setExtension("cs");
+
+         if (Torque::FS::IsFile(constrFilePath.getFullPath().c_str()))
          {
             //Yup, found it, we're good to go
             makeNewConstructor = false;
-            dSprintf(importLogBuffer, sizeof(importLogBuffer), "Existing TSShape Constructor file %s found", qualifiedFromCSFile);
+            dSprintf(importLogBuffer, sizeof(importLogBuffer), "Existing TSShape Constructor file %s found", constrFilePath.getFullPath().c_str());
             activityLog.push_back(importLogBuffer);
          }
-         else
-         {
-            //Didn't work, but it's possible it's using the old .cs extension when our extension variable is set to something else, so check that one as well just to be sure
-            Torque::Path constrFilePath = qualifiedFromCSFile;
-            constrFilePath.setExtension("cs");
-
-            if (Torque::FS::IsFile(constrFilePath.getFullPath().c_str()))
-            {
-               //Yup, found it, we're good to go
-               makeNewConstructor = false;
-               dSprintf(importLogBuffer, sizeof(importLogBuffer), "Existing TSShape Constructor file %s found", constrFilePath.getFullPath().c_str());
-               activityLog.push_back(importLogBuffer);
-            }
-         }
       }
    }
 
    if (makeNewConstructor)
    {
-      dSprintf(importLogBuffer, sizeof(importLogBuffer), "Beginning creation of new TSShapeConstructor file: %s", qualifiedToCSFile);
+      dSprintf(importLogBuffer, sizeof(importLogBuffer), "Beginning creation of new TSShapeConstructor file: %s", constructorPath.c_str());
       activityLog.push_back(importLogBuffer);
 
       //find/create shape constructor
-      TSShapeConstructor* constructor = TSShapeConstructor::findShapeConstructorByFilename(Torque::Path(qualifiedToFile).getFullPath());
+      TSShapeConstructor* constructor = TSShapeConstructor::findShapeConstructorByFilename(Torque::Path(constructorPath).getFullPath());
       if (constructor == nullptr)
       {
          String fullAssetName = assetItem->moduleName + ":" + assetItem->assetName;
@@ -3324,7 +3256,7 @@ Torque::Path AssetImporter::importShapeAsset(AssetImportObject* assetItem)
 
       PersistenceManager* constructorPersist = new PersistenceManager();
       constructorPersist->registerObject();
-      constructorPersist->setDirty(constructor, qualifiedToCSFile);
+      constructorPersist->setDirty(constructor, constructorPath);
 
       if (!constructorPersist->saveDirtyObject(constructor))
       {

+ 14 - 14
Engine/source/T3D/debris.cpp

@@ -116,7 +116,7 @@ DebrisData::DebrisData()
    terminalVelocity = 0.0f;
    ignoreWater = true;
 
-   INIT_ASSET(Shape);
+   mShapeAsset.registerRefreshNotify(this);
 }
 
 //#define TRACK_DEBRIS_DATA_CLONES
@@ -152,7 +152,7 @@ DebrisData::DebrisData(const DebrisData& other, bool temp_clone) : GameBaseData(
    terminalVelocity = other.terminalVelocity;
    ignoreWater = other.ignoreWater;
 
-   CLONE_ASSET(Shape);
+   mShapeAsset = other.mShapeAsset;
 
    textureName = other.textureName;
    explosionId = other.explosionId; // -- for pack/unpack of explosion ptr
@@ -191,7 +191,7 @@ DebrisData* DebrisData::cloneAndPerformSubstitutions(const SimObject* owner, S32
 
 void DebrisData::onPerformSubstitutions() 
 { 
-   _setShape(getShape());
+   _setShape(_getShapeAssetId());
 }
 
 bool DebrisData::onAdd()
@@ -276,16 +276,16 @@ bool DebrisData::preload(bool server, String &errorStr)
 
    if (mShapeAsset.notNull())
    {
-      if (!mShape)
+      if (!getShape())
       {
-         errorStr = String::ToString("DebrisData::load: Couldn't load shape \"%s\"", mShapeAssetId);
+         errorStr = String::ToString("DebrisData::load: Couldn't load shape \"%s\"", _getShapeAssetId());
          return false;
       }
       else
       {
-         TSShapeInstance* pDummy = new TSShapeInstance(mShape, !server);
+         TSShapeInstance* pDummy = new TSShapeInstance(getShape(), !server);
          delete pDummy;
-         if (!server && !mShape->preloadMaterialList(mShape.getPath()) && NetConnection::filesWereDownloaded())
+         if (!server && !getShape()->preloadMaterialList(getShape().getPath()) && NetConnection::filesWereDownloaded())
             return false;
       }
    }
@@ -304,7 +304,7 @@ void DebrisData::initPersistFields()
    addGroup("Shapes");
       addField("texture",              TypeString,                  Offset(textureName,         DebrisData), 
          "@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.");
+      INITPERSISTFIELD_SHAPEASSET_REFACTOR(Shape, DebrisData, "Shape to use for this debris object.");
    endGroup("Shapes");
 
    addGroup("Particle Effects");
@@ -389,7 +389,7 @@ void DebrisData::packData(BitStream* stream)
 
    stream->writeString( textureName );
 
-   PACKDATA_ASSET(Shape);
+   PACKDATA_ASSET_REFACTOR(Shape);
 
    for( S32 i=0; i<DDC_NUM_EMITTERS; i++ )
    {
@@ -433,7 +433,7 @@ void DebrisData::unpackData(BitStream* stream)
 
    textureName = stream->readSTString();
 
-   UNPACKDATA_ASSET(Shape);
+   UNPACKDATA_ASSET_REFACTOR(Shape);
 
    for( S32 i=0; i<DDC_NUM_EMITTERS; i++ )
    {
@@ -676,18 +676,18 @@ bool Debris::onAdd()
    mFriction = mDataBlock->friction;
 
    // Setup our bounding box
-   if( mDataBlock->mShape )
+   if( mDataBlock->getShape())
    {
-      mObjBox = mDataBlock->mShape->mBounds;
+      mObjBox = mDataBlock->getShape()->mBounds;
    }
    else
    {
       mObjBox = Box3F(Point3F(-1, -1, -1), Point3F(1, 1, 1));
    }
 
-   if( mDataBlock->mShape)
+   if( mDataBlock->getShape())
    {
-      mShape = new TSShapeInstance( mDataBlock->mShape, true);
+      mShape = new TSShapeInstance( mDataBlock->getShape(), true);
    }
 
    if( mPart )

+ 4 - 4
Engine/source/T3D/debris.h

@@ -47,7 +47,7 @@ class TSShape;
 //**************************************************************************
 // Debris Data
 //**************************************************************************
-struct DebrisData : public GameBaseData
+struct DebrisData : public GameBaseData, protected AssetPtrCallback
 {
    typedef GameBaseData Parent;
 
@@ -83,8 +83,7 @@ struct DebrisData : public GameBaseData
    F32      terminalVelocity;    // max velocity magnitude
    bool     ignoreWater;
 
-   DECLARE_SHAPEASSET(DebrisData, Shape, onShapeChanged);
-   DECLARE_ASSET_SETGET(DebrisData, Shape);
+   DECLARE_SHAPEASSET_REFACTOR(DebrisData, Shape)
 
    StringTableEntry  textureName;
 
@@ -111,7 +110,8 @@ public:
    void onPerformSubstitutions() override;
    bool allowSubstitutions() const override { return true; }
 
-   void onShapeChanged()
+protected:
+   void onAssetRefreshed(AssetPtrBase* pAssetPtrBase) override
    {
       reloadOnLocalClient();
    }

+ 7 - 12
Engine/source/T3D/examples/renderShapeExample.cpp

@@ -59,7 +59,6 @@ RenderShapeExample::RenderShapeExample()
    mTypeMask |= StaticObjectType | StaticShapeObjectType;
 
    // Make sure to initialize our TSShapeInstance to NULL
-   INIT_ASSET(Shape);
    mShapeInstance = NULL;
 }
 
@@ -75,7 +74,7 @@ void RenderShapeExample::initPersistFields()
    docsURL;
    Parent::initPersistFields();
    addGroup( "Shapes" );
-   INITPERSISTFIELD_SHAPEASSET(Shape, RenderShapeExample, "The path to the shape file.")
+   INITPERSISTFIELD_SHAPEASSET_REFACTOR(Shape, RenderShapeExample, "The path to the shape file.")
    endGroup( "Shapes" );
 
    // SceneObject already handles exposing the transform
@@ -147,7 +146,7 @@ U32 RenderShapeExample::packUpdate( NetConnection *conn, U32 mask, BitStream *st
    // Write out any of the updated editable properties
    if ( stream->writeFlag( mask & UpdateMask ) )
    {
-      PACK_ASSET(conn, Shape);
+      PACK_ASSET_REFACTOR(conn, Shape);
 
       // Allow the server object a chance to handle a new shape
       createShape();
@@ -171,7 +170,7 @@ void RenderShapeExample::unpackUpdate(NetConnection *conn, BitStream *stream)
 
    if ( stream->readFlag() )  // UpdateMask
    {
-      UNPACK_ASSET(conn, Shape);
+      UNPACK_ASSET_REFACTOR(conn, Shape);
 
       if ( isProperlyAdded() )
          createShape();
@@ -183,11 +182,7 @@ void RenderShapeExample::unpackUpdate(NetConnection *conn, BitStream *stream)
 //-----------------------------------------------------------------------------
 void RenderShapeExample::createShape()
 {
-   if ( getShape() == StringTable->EmptyString() )
-      return;
-
-   // If this is the same shape then no reason to update it
-   if ( mShapeInstance && getShape() == StringTable->insert(mShape.getPath().getFullPath().c_str()) )
+   if ( mShapeAsset.isNull() )
       return;
 
    // Clean up our previous shape
@@ -196,19 +191,19 @@ void RenderShapeExample::createShape()
 
    // Attempt to preload the Materials for this shape
    if ( isClientObject() && 
-        !mShape->preloadMaterialList( mShape.getPath() ) && 
+        !getShape()->preloadMaterialList(getShape().getPath() ) &&
         NetConnection::filesWereDownloaded() )
    {
       return;
    }
 
    // Update the bounding box
-   mObjBox = mShape->mBounds;
+   mObjBox = getShape()->mBounds;
    resetWorldBox();
    setRenderTransform(mObjToWorld);
 
    // Create the TSShapeInstance
-   mShapeInstance = new TSShapeInstance( mShape, isClientObject() );
+   mShapeInstance = new TSShapeInstance(getShape(), isClientObject() );
 }
 
 void RenderShapeExample::prepRenderImage( SceneRenderState *state )

+ 2 - 5
Engine/source/T3D/examples/renderShapeExample.h

@@ -61,14 +61,11 @@ class RenderShapeExample : public SceneObject
    //--------------------------------------------------------------------------
    // Rendering variables
    //--------------------------------------------------------------------------
-   DECLARE_SHAPEASSET(RenderShapeExample, Shape, onShapeChanged);
-   DECLARE_ASSET_SETGET(RenderShapeExample, Shape);
+   DECLARE_SHAPEASSET_REFACTOR(RenderShapeExample, Shape)
 
    // The actual shape instance
    TSShapeInstance*  mShapeInstance;
 
-   void onShapeChanged() {}
-
 public:
    RenderShapeExample();
    virtual ~RenderShapeExample();
@@ -119,4 +116,4 @@ public:
    void prepRenderImage( SceneRenderState *state ) override;
 };
 
-#endif // _RENDERSHAPEEXAMPLE_H_
+#endif // _RENDERSHAPEEXAMPLE_H_

+ 64 - 2
Engine/source/T3D/fps/guiCrossHairHud.cpp

@@ -30,7 +30,11 @@
 #include "T3D/shapeBase.h"
 #include "gfx/gfxDrawUtil.h"
 #include "console/engineAPI.h"
-
+#include "gui/core/guiOffscreenCanvas.h"
+#include "T3D/tsStatic.h"
+#include "materials/baseMatInstance.h"
+#include "materials/matInstance.h"
+#include "materials/materialDefinition.h"
 
 //-----------------------------------------------------------------------------
 /// Vary basic cross hair hud.
@@ -46,12 +50,14 @@ class GuiCrossHairHud : public GuiBitmapCtrl
    LinearColorF   mDamageFrameColor;
    Point2I  mDamageRectSize;
    Point2I  mDamageOffset;
+   PlatformTimer* mFrameTime;
 
 protected:
    void drawDamage(Point2I offset, F32 damage, F32 opacity);
 
 public:
    GuiCrossHairHud();
+   ~GuiCrossHairHud();
 
    void onRender( Point2I, const RectI &) override;
    static void initPersistFields();
@@ -95,6 +101,12 @@ GuiCrossHairHud::GuiCrossHairHud()
    mDamageFrameColor.set( 1.0f, 0.6f, 0.0f, 1.0f );
    mDamageRectSize.set(50, 4);
    mDamageOffset.set(0,32);
+   mFrameTime = PlatformTimer::create();
+}
+
+GuiCrossHairHud::~GuiCrossHairHud()
+{
+   SAFE_DELETE(mFrameTime);
 }
 
 void GuiCrossHairHud::initPersistFields()
@@ -139,11 +151,61 @@ void GuiCrossHairHud::onRender(Point2I offset, const RectI &updateRect)
 
    // Collision info. We're going to be running LOS tests and we
    // don't want to collide with the control object.
-   static U32 losMask = TerrainObjectType | ShapeBaseObjectType;
+   static U32 losMask = TerrainObjectType | ShapeBaseObjectType | StaticShapeObjectType;
    control->disableCollision();
 
    RayInfo info;
    if (gClientContainer.castRay(camPos, endPos, losMask, &info)) {
+      // is this a tsstatic? then it could be a offscreen canvas, check the list.
+      if (TSStatic* ts = dynamic_cast<TSStatic*>(info.object))
+      {
+         if (mFrameTime->getElapsedMs() > 32)
+         {
+            GuiOffscreenCanvas::sActiveOffscreenCanvas = NULL;
+            mFrameTime->reset();
+
+            Point3F newStart, newEnd;
+            ts->getWorldTransform().mulP(camPos, &newStart);
+            ts->getWorldTransform().mulP(endPos, &newEnd);
+
+            newStart.convolveInverse(ts->getScale());
+            newEnd.convolveInverse(ts->getScale());
+
+            info.generateTexCoord = true;
+            if (ts->getShapeInstance()->castRayOpcode(0, newStart, newEnd, &info))
+            {
+               MatInstance* matInst = dynamic_cast<MatInstance*>(info.material);
+               if (matInst)
+               {
+                  Material* mat = matInst->getMaterial();
+                  if (mat && mat->getDiffuseMapAsset(0).notNull() && mat->getDiffuseMapAsset(0)->isNamedTarget())
+                  {
+                     String canvasName = String(mat->getDiffuseMapAsset(0)->getImageFile()).substr(1, (U32)strlen(mat->getDiffuseMapAsset(0)->getImageFile()) - 1);
+                     for (GuiOffscreenCanvas* canvas : GuiOffscreenCanvas::sList)
+                     {
+                        if (canvas->getTarget()->getName() == canvasName)
+                        {
+                           if (!canvas->canInteract() || canvas->getMaxInteractDistance() < info.distance)
+                           {
+                              break;
+                           }
+
+                           Point2I canvasSize = canvas->getWindowSize();
+                           Point2I newCursorPos(mRound(mClampF((info.texCoord.x * canvasSize.x), 0.0f, (F32)canvasSize.x)),
+                              mRound(mClampF((info.texCoord.y * canvasSize.y), 0.0f, (F32)canvasSize.y)));
+
+                           canvas->setCursorPos(newCursorPos);
+                           canvas->markDirty();
+                           GuiOffscreenCanvas::sActiveOffscreenCanvas = canvas;
+                           break;
+                        }
+                     }
+                  }
+               }
+            }
+         }
+      }
+
       // Hit something... but we'll only display health for named
       // ShapeBase objects.  Could mask against the object type here
       // and do a static cast if it's a ShapeBaseObjectType, but this

+ 12 - 10
Engine/source/T3D/fx/explosion.cpp

@@ -239,7 +239,7 @@ ExplosionData::ExplosionData()
    explosionScale.set(1.0f, 1.0f, 1.0f);
    playSpeed = 1.0f;
 
-   INIT_ASSET(ExplosionShape);
+   mExplosionShapeAsset.registerRefreshNotify(this);
 
    explosionAnimation = -1;
 
@@ -315,7 +315,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;
-   CLONE_ASSET(ExplosionShape);
+   mExplosionShapeAsset = other.mExplosionShapeAsset;
    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
@@ -360,6 +360,8 @@ ExplosionData::~ExplosionData()
    if (!isTempClone())
       return;
 
+   mExplosionShapeAsset.unregisterRefreshNotify();
+
    // particleEmitter, emitterList[*], debrisList[*], explosionList[*] will delete themselves
 
 #ifdef TRACK_EXPLOSION_DATA_CLONES
@@ -393,7 +395,7 @@ void ExplosionData::initPersistFields()
 {
    docsURL;
    addGroup("Shapes");
-      INITPERSISTFIELD_SHAPEASSET(ExplosionShape, ExplosionData, "@brief Optional shape asset to place at the center of the explosion.\n\n"
+      INITPERSISTFIELD_SHAPEASSET_REFACTOR(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.");
    endGroup("Shapes");
 
@@ -668,7 +670,7 @@ void ExplosionData::packData(BitStream* stream)
 {
    Parent::packData(stream);
 
-   PACKDATA_ASSET(ExplosionShape);
+   PACKDATA_ASSET_REFACTOR(ExplosionShape);
 
    //PACKDATA_SOUNDASSET(Sound);
    PACKDATA_ASSET(Sound);
@@ -773,7 +775,7 @@ void ExplosionData::unpackData(BitStream* stream)
 {
 	Parent::unpackData(stream);
 
-   UNPACKDATA_ASSET(ExplosionShape);
+   UNPACKDATA_ASSET_REFACTOR(ExplosionShape);
 
    UNPACKDATA_ASSET(Sound);
 
@@ -897,10 +899,10 @@ bool ExplosionData::preload(bool server, String &errorStr)
    if (mExplosionShapeAsset.notNull()) {
 
       // Resolve animations
-      explosionAnimation = mExplosionShape->findSequence("ambient");
+      explosionAnimation = getExplosionShape()->findSequence("ambient");
 
       // Preload textures with a dummy instance...
-      TSShapeInstance* pDummy = new TSShapeInstance(mExplosionShape, !server);
+      TSShapeInstance* pDummy = new TSShapeInstance(getExplosionShape(), !server);
       delete pDummy;
 
    } else {
@@ -1392,8 +1394,8 @@ bool Explosion::explode()
    launchDebris( mInitialNormal );
    spawnSubExplosions();
 
-   if (bool(mDataBlock->mExplosionShape) && mDataBlock->explosionAnimation != -1) {
-      mExplosionInstance = new TSShapeInstance(mDataBlock->mExplosionShape, true);
+   if (bool(mDataBlock->getExplosionShape()) && mDataBlock->explosionAnimation != -1) {
+      mExplosionInstance = new TSShapeInstance(mDataBlock->getExplosionShape(), true);
 
       mExplosionThread   = mExplosionInstance->addThread();
       mExplosionInstance->setSequence(mExplosionThread, mDataBlock->explosionAnimation, 0);
@@ -1403,7 +1405,7 @@ bool Explosion::explode()
       mEndingMS = U32(mExplosionInstance->getScaledDuration(mExplosionThread) * 1000.0f);
 
       mObjScale.convolve(mDataBlock->explosionScale);
-      mObjBox = mDataBlock->mExplosionShape->mBounds;
+      mObjBox = mDataBlock->getExplosionShape()->mBounds;
       resetWorldBox();
    }
 

+ 4 - 4
Engine/source/T3D/fx/explosion.h

@@ -52,7 +52,7 @@ struct DebrisData;
 
 class SFXProfile;
 //--------------------------------------------------------------------------
-class ExplosionData : public GameBaseData {
+class ExplosionData : public GameBaseData, protected AssetPtrCallback {
   public:
    typedef GameBaseData Parent;
 
@@ -79,8 +79,7 @@ class ExplosionData : public GameBaseData {
    Point3F              explosionScale;
    F32                  playSpeed;
 
-   DECLARE_SHAPEASSET(ExplosionData, ExplosionShape, onShapeChanged);
-   DECLARE_ASSET_SETGET(ExplosionData, ExplosionShape);
+   DECLARE_SHAPEASSET_REFACTOR(ExplosionData, ExplosionShape)
 
    S32               explosionAnimation;
 
@@ -143,7 +142,8 @@ public:
    ExplosionData* cloneAndPerformSubstitutions(const SimObject*, S32 index=0);
    bool   allowSubstitutions() const override { return true; }
 
-   void onShapeChanged()
+protected:
+   void onAssetRefreshed(AssetPtrBase* pAssetPtrBase) override
    {
       reloadOnLocalClient();
    }

+ 10 - 11
Engine/source/T3D/fx/groundCover.cpp

@@ -521,7 +521,7 @@ GroundCover::GroundCover()
       mBillboardRects[i].point.set( 0.0f, 0.0f );
       mBillboardRects[i].extent.set( 1.0f, 1.0f );
 
-      INIT_ASSET_ARRAY(Shape, i);
+      mShapeAsset[i].registerRefreshNotify(this);
 
       mShapeInstances[i] = NULL;
 
@@ -563,8 +563,7 @@ void GroundCover::initPersistFields()
 
          addField( "billboardUVs",  TypeRectUV,    Offset( mBillboardRects, GroundCover ), MAX_COVERTYPES,  "Subset material UV coordinates for this cover billboard." );
 
-         addField("shapeFilename", TypeFilename, Offset(mShapeName, GroundCover), MAX_COVERTYPES, "The cover shape filename. [Optional]", AbstractClassRep::FIELD_HideInInspectors);
-         INITPERSISTFIELD_SHAPEASSET_ARRAY(Shape, MAX_COVERTYPES, GroundCover, "The cover shape. [Optional]");
+         INITPERSISTFIELD_SHAPEASSET_ARRAY_REFACTOR(Shape, MAX_COVERTYPES, GroundCover, "The cover shape. [Optional]");
 
          addField( "layer",         TypeTerrainMaterialAssetId, Offset( mLayer, GroundCover ), MAX_COVERTYPES,      "Terrain material assetId to limit coverage to, or blank to not limit." );
 
@@ -767,10 +766,10 @@ U32 GroundCover::packUpdate( NetConnection *connection, U32 mask, BitStream *str
          stream->write( mBillboardRects[i].point.y );
          stream->write( mBillboardRects[i].extent.x );
          stream->write( mBillboardRects[i].extent.y );
-
-         PACK_ASSET_ARRAY(connection, Shape, i);
       }
 
+      PACK_ASSET_ARRAY_REFACTOR(connection, Shape, MAX_COVERTYPES)
+
       stream->writeFlag( mDebugRenderCells );
       stream->writeFlag( mDebugNoBillboards );
       stream->writeFlag( mDebugNoShapes );
@@ -838,10 +837,10 @@ void GroundCover::unpackUpdate( NetConnection *connection, BitStream *stream )
          stream->read( &mBillboardRects[i].point.y );
          stream->read( &mBillboardRects[i].extent.x );
          stream->read( &mBillboardRects[i].extent.y );
-
-         UNPACK_ASSET_ARRAY(connection, Shape, i);
       }
 
+      UNPACK_ASSET_ARRAY_REFACTOR(connection, Shape, MAX_COVERTYPES)
+
       mDebugRenderCells    = stream->readFlag();
       mDebugNoBillboards   = stream->readFlag();
       mDebugNoShapes       = stream->readFlag();
@@ -887,17 +886,17 @@ void GroundCover::_initShapes()
 
    for ( S32 i=0; i < MAX_COVERTYPES; i++ )
    {
-      if ( mShapeAsset[i].isNull() || mShape[i] == nullptr)
+      if ( mShapeAsset[i].isNull() || getShape(i) == nullptr)
          continue;
 
-      if ( isClientObject() && !mShape[i]->preloadMaterialList(mShape[i].getPath()) && NetConnection::filesWereDownloaded() )
+      if ( isClientObject() && !getShape(i)->preloadMaterialList(getShape(i).getPath()) && NetConnection::filesWereDownloaded() )
       {
-         Con::warnf( "GroundCover::_initShapes() material preload failed for shape: %s", mShapeAssetId[i] );
+         Con::warnf( "GroundCover::_initShapes() material preload failed for shape: %s", _getShapeAssetId(i));
          continue;
       }
 
       // Create the shape instance.
-      mShapeInstances[i] = new TSShapeInstance(mShape[i], isClientObject() );
+      mShapeInstances[i] = new TSShapeInstance(getShape(i), isClientObject() );
    }
 }
 

+ 4 - 4
Engine/source/T3D/fx/groundCover.h

@@ -111,7 +111,7 @@ public:
 };
 
 
-class GroundCover : public SceneObject
+class GroundCover : public SceneObject, protected AssetPtrCallback
 {
    friend class GroundCoverShaderConstHandles;
    friend class GroundCoverCell;
@@ -341,8 +341,7 @@ protected:
    RectF mBillboardRects[MAX_COVERTYPES];
 
    /// The cover shape filenames.
-   DECLARE_SHAPEASSET_ARRAY(GroundCover, Shape, MAX_COVERTYPES, onShapeChanged);
-   DECLARE_ASSET_ARRAY_NET_SETGET(GroundCover, Shape, -1);
+   DECLARE_SHAPEASSET_ARRAY_NET_REFACTOR(GroundCover, Shape, MAX_COVERTYPES, -1)
 
    /// The cover shape instances.
    TSShapeInstance* mShapeInstances[MAX_COVERTYPES];
@@ -410,7 +409,8 @@ protected:
 
    void _debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat );
 
-   void onShapeChanged()
+protected:
+   void onAssetRefreshed(AssetPtrBase* pAssetPtrBase) override
    {
       _initShapes();
       setMaskBits(U32(-1));

+ 37 - 41
Engine/source/T3D/guiObjectView.cpp

@@ -119,8 +119,6 @@ GuiObjectView::GuiObjectView()
    // By default don't do dynamic reflection
    // updates for this viewport.
    mReflectPriority = 0.0f;
-   INIT_ASSET(Model);
-   INIT_ASSET(MountedModel);
 }
 
 //------------------------------------------------------------------------------
@@ -137,7 +135,7 @@ void GuiObjectView::initPersistFields()
 {
    docsURL;
    addGroup( "Model" );   
-      INITPERSISTFIELD_SHAPEASSET(Model, GuiObjectView, "The source shape asset.");
+      INITPERSISTFIELD_SHAPEASSET_REFACTOR(Model, GuiObjectView, "The source shape asset.");
       addField( "skin", TypeRealString, Offset( mSkinName, GuiObjectView ),
          "The skin to use on the object model." );   
    endGroup( "Model" );
@@ -150,7 +148,7 @@ void GuiObjectView::initPersistFields()
    endGroup( "Animation" );
    
    addGroup( "Mounting" );   
-      INITPERSISTFIELD_SHAPEASSET(MountedModel, GuiObjectView, "The mounted shape asset.");
+   INITPERSISTFIELD_SHAPEASSET_REFACTOR(MountedModel, GuiObjectView, "The mounted shape asset.");
       addField( "mountedSkin", TypeRealString, Offset( mMountSkinName, GuiObjectView ),
          "Skin name used on mounted shape file." );
       addField( "mountedNode", TypeRealString, Offset( mMountNodeName, GuiObjectView ),
@@ -335,19 +333,23 @@ bool GuiObjectView::setObjectModel( const String& modelName )
 {
    mRunThread = 0;
 
-   // Load the shape.
-   _setModel(modelName);
-   if( !getModelResource())
+   // Load the shape if its not the one already set.
+   if (modelName.c_str() != _getModelAssetId())
+      _setModel(modelName.c_str());
+   else
+      return true;
+
+   if( !getModel())
    {
       Con::warnf( "GuiObjectView::setObjectModel - Failed to load model '%s'", modelName.c_str() );
       return false;
    }
 
-   if (!getModelResource()->preloadMaterialList(getModelResource().getPath())) return false;
+   if (!getModel()->preloadMaterialList(getModel().getPath())) return false;
 
    // Instantiate it.
 
-   mModelInstance = new TSShapeInstance(getModelResource(), true );
+   mModelInstance = new TSShapeInstance(getModel(), true );
    mModelInstance->resetMaterialList();
    mModelInstance->cloneMaterialList();
    
@@ -355,12 +357,12 @@ bool GuiObjectView::setObjectModel( const String& modelName )
       mModelInstance->reSkin( mSkinName );
 
    TSMaterialList* pMatList = mModelInstance->getMaterialList();
-   pMatList->setTextureLookupPath(mModelAsset->getShapeFileName());
+   pMatList->setTextureLookupPath(mModelAsset->getShapeFile());
    mModelInstance->initMaterialList();
    // Initialize camera values.
    
-   mOrbitPos = getModelResource()->center;
-   mMinOrbitDist = getModelResource()->mRadius;
+   mOrbitPos = getModel()->center;
+   mMinOrbitDist = getModel()->mRadius;
 
    // Initialize animation.
    
@@ -369,11 +371,6 @@ bool GuiObjectView::setObjectModel( const String& modelName )
    return true;
 }
 
-void GuiObjectView::onModelChanged()
-{
-
-}
-
 //------------------------------------------------------------------------------
 
 void GuiObjectView::setSkin( const String& name )
@@ -389,17 +386,21 @@ void GuiObjectView::setSkin( const String& name )
 
 bool GuiObjectView::setMountedObject( const String& modelName )
 {
-   // Load the model.   
-   _setMountedModel(modelName);
-   if (!getMountedModelResource())
+   // Load the model if it is not already the asset then set it..
+   if (modelName.c_str() != _getMountedModelAssetId())
+      _setMountedModel(modelName.c_str());
+   else
+      return true;
+
+   if (!getMountedModel())
    {
       Con::warnf("GuiObjectView::setMountedObject - Failed to load model '%s'", modelName.c_str());
       return false;
    }
 
-   if (!getMountedModelResource()->preloadMaterialList(getMountedModelResource().getPath())) return false;
+   if (!getMountedModel()->preloadMaterialList(getMountedModel().getPath())) return false;
 
-   mMountedModelInstance = new TSShapeInstance(getMountedModelResource(), true);
+   mMountedModelInstance = new TSShapeInstance(getMountedModel(), true);
    mMountedModelInstance->resetMaterialList();
    mMountedModelInstance->cloneMaterialList();
 
@@ -413,11 +414,6 @@ bool GuiObjectView::setMountedObject( const String& modelName )
    return true;
 }
 
-void GuiObjectView::onMountedModelChanged()
-{
-
-}
-
 //------------------------------------------------------------------------------
 
 void GuiObjectView::setMountSkin(const String& name)
@@ -632,7 +628,7 @@ void GuiObjectView::setLightDirection( const Point3F& direction )
 
 void GuiObjectView::_initAnimation()
 {
-   AssertFatal(getModelResource(), "GuiObjectView::_initAnimation - No model loaded!" );
+   AssertFatal(getModel(), "GuiObjectView::_initAnimation - No model loaded!" );
    
    if( mAnimationSeqName.isEmpty() && mAnimationSeq == -1 )
       return;
@@ -641,13 +637,13 @@ void GuiObjectView::_initAnimation()
             
    if( !mAnimationSeqName.isEmpty() )
    {
-      mAnimationSeq = getModelResource()->findSequence( mAnimationSeqName );
+      mAnimationSeq = getModel()->findSequence( mAnimationSeqName );
       
       if( mAnimationSeq == -1 )
       {
          Con::errorf( "GuiObjectView::_initAnimation - Cannot find animation sequence '%s' on '%s'",
             mAnimationSeqName.c_str(),
-            mModelName
+            _getModelAssetId()
          );
          
          return;
@@ -658,11 +654,11 @@ void GuiObjectView::_initAnimation()
       
    if( mAnimationSeq != -1 )
    {
-      if( mAnimationSeq >= getModelResource()->sequences.size() )
+      if( mAnimationSeq >= getModel()->sequences.size() )
       {
          Con::errorf( "GuiObjectView::_initAnimation - Sequence '%i' out of range for model '%s'",
             mAnimationSeq,
-            mModelName
+            _getModelAssetId()
          );
          
          mAnimationSeq = -1;
@@ -693,12 +689,12 @@ void GuiObjectView::_initMount()
    
    if( !mMountNodeName.isEmpty() )
    {
-      mMountNode = getModelResource()->findNode( mMountNodeName );
+      mMountNode = getModel()->findNode( mMountNodeName );
       if( mMountNode == -1 )
       {
          Con::errorf( "GuiObjectView::_initMount - No node '%s' on '%s'",
             mMountNodeName.c_str(),
-            mModelName
+            _getModelAssetId()
          );
          
          return;
@@ -707,11 +703,11 @@ void GuiObjectView::_initMount()
    
    // Make sure mount node is valid.
    
-   if( mMountNode != -1 && mMountNode >= getModelResource()->nodes.size() )
+   if( mMountNode != -1 && mMountNode >= getModel()->nodes.size() )
    {
       Con::errorf( "GuiObjectView::_initMount - Mount node index '%i' out of range for '%s'",
          mMountNode,
-         mModelName
+         _getModelAssetId()
       );
       
       mMountNode = -1;
@@ -720,11 +716,11 @@ void GuiObjectView::_initMount()
    
    // Look up node on the mounted model from
    // which to mount to the primary model's node.
-   if (!getMountedModelResource()) return;
-   S32 mountPoint = getMountedModelResource()->findNode( "mountPoint" );
+   if (!getMountedModel()) return;
+   S32 mountPoint = getMountedModel()->findNode( "mountPoint" );
    if( mountPoint != -1 )
    {
-      getMountedModelResource()->getNodeWorldTransform(mountPoint, &mMountTransform),
+      getMountedModel()->getNodeWorldTransform(mountPoint, &mMountTransform),
       mMountTransform.inverse();
    }
 }
@@ -745,7 +741,7 @@ DefineEngineMethod( GuiObjectView, getModel, const char*, (),,
    "@return Name of the displayed model.\n\n"
    "@see GuiControl")
 {
-   return Con::getReturnBuffer( object->getModel() );
+   return Con::getReturnBuffer( object->_getModelAssetId() );
 }
 
 //-----------------------------------------------------------------------------
@@ -775,7 +771,7 @@ DefineEngineMethod( GuiObjectView, getMountedModel, const char*, (),,
    "@return Name of the mounted model.\n\n"
    "@see GuiControl")
 {
-   return Con::getReturnBuffer( object->getMountedModel() );
+   return Con::getReturnBuffer( object->_getMountedModelAssetId() );
 }
 
 //-----------------------------------------------------------------------------

+ 15 - 20
Engine/source/T3D/guiObjectView.h

@@ -37,7 +37,7 @@ class LightInfo;
 
 
 /// A control that displays a TSShape in its view.
-class GuiObjectView : public GuiTSCtrl
+class GuiObjectView : public GuiTSCtrl, protected AssetPtrCallback
 {
    public:
    
@@ -70,15 +70,8 @@ class GuiObjectView : public GuiTSCtrl
       /// @{
       
       ///Model loaded for display.
-      DECLARE_SHAPEASSET(GuiObjectView, Model, onModelChanged);
-      static bool _setModelData(void* obj, const char* index, const char* data)\
-      {
-         bool ret = false;
-         GuiObjectView* object = static_cast<GuiObjectView*>(obj);
-         ret = object->setObjectModel(StringTable->insert(data));
-         return ret;
-      }
-      void onModelChanged();
+      DECLARE_SHAPEASSET_REFACTOR(GuiObjectView, Model)
+
       TSShapeInstance* mModelInstance;
       /// Name of skin to use on model.
       String mSkinName;
@@ -109,15 +102,7 @@ class GuiObjectView : public GuiTSCtrl
       /// @{
       
       ///Model to mount to the primary model.
-      DECLARE_SHAPEASSET(GuiObjectView, MountedModel, onMountedModelChanged);
-      static bool _setMountedModelData(void* obj, const char* index, const char* data)\
-      {
-         bool ret = false;
-         GuiObjectView* object = static_cast<GuiObjectView*>(obj);
-         ret = object->setMountedObject(StringTable->insert(data));
-         return ret;
-      }
-      void onMountedModelChanged();
+      DECLARE_SHAPEASSET_REFACTOR(GuiObjectView, MountedModel)
       TSShapeInstance* mMountedModelInstance;
       
       ///
@@ -284,7 +269,17 @@ class GuiObjectView : public GuiTSCtrl
       static void initPersistFields();
 
       DECLARE_CONOBJECT( GuiObjectView );
-      DECLARE_DESCRIPTION( "A control that shows a TSShape model." );   
+      DECLARE_DESCRIPTION( "A control that shows a TSShape model." );
+
+protected:
+   void onAssetRefreshed(AssetPtrBase* pAssetPtrBase) override
+   {
+      if (mModelAsset.notNull())
+         setObjectModel(_getModelAssetId());
+
+      if (mMountedModelAsset.notNull())
+         setMountedObject(_getMountedModelAssetId());
+   }
 };
 
 #endif // !_GUIOBJECTVIEW_H_

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

@@ -444,14 +444,14 @@ void SpawnSphere::unpackUpdate(NetConnection * con, BitStream * stream)
       {
          delete mShapeInstance;
          ShapeBaseData *spawnedDatablock = dynamic_cast<ShapeBaseData *>(Sim::findObject(mSpawnDataBlock.c_str()));
-         if (spawnedDatablock && spawnedDatablock->mShape)
+         if (spawnedDatablock && spawnedDatablock->getShape())
          {
-               mShapeInstance = new TSShapeInstance(spawnedDatablock->mShape);
+               mShapeInstance = new TSShapeInstance(spawnedDatablock->getShape());
          }
          else if (mDataBlock)
          {
-            if (mDataBlock->mShape)
-               mShapeInstance = new TSShapeInstance(mDataBlock->mShape);
+            if (mDataBlock->getShape())
+               mShapeInstance = new TSShapeInstance(mDataBlock->getShape());
          }
       }
       stream->read(&mSpawnName);

+ 16 - 14
Engine/source/T3D/physics/physicsDebris.cpp

@@ -74,7 +74,12 @@ PhysicsDebrisData::PhysicsDebrisData()
    lifetime = 5.0f;
    lifetimeVariance = 0.0f;
 
-   INIT_ASSET(Shape);
+   mShapeAsset.registerRefreshNotify(this);
+}
+
+PhysicsDebrisData::~PhysicsDebrisData()
+{
+   mShapeAsset.unregisterRefreshNotify();
 }
 
 bool PhysicsDebrisData::onAdd()
@@ -96,12 +101,12 @@ bool PhysicsDebrisData::preload( bool server, String &errorStr )
    {
       // 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 );
+      TSShapeInstance *pDummy = new TSShapeInstance( getShape(), !server);
       delete pDummy;
    }
    else
    {
-      errorStr = String::ToString("PhysicsDebrisData::load: Couldn't load shape asset \"%s\"", mShapeAssetId);
+      errorStr = String::ToString("PhysicsDebrisData::load: Couldn't load shape asset \"%s\"", _getShapeAssetId());
       return false;
    }
 
@@ -113,12 +118,9 @@ void PhysicsDebrisData::initPersistFields()
    docsURL;
    addGroup( "Shapes" );
 
-      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_REFACTOR(Shape, PhysicsDebrisData, "@brief Shape to use with this debris.\n\n"
+      "Compatable with Live-Asset Reloading."); 
 
-      INITPERSISTFIELD_SHAPEASSET(Shape, PhysicsDebrisData, "@brief Shape to use with this debris.\n\n"
-         "Compatable with Live-Asset Reloading.");
    endGroup( "Shapes" );
 
    addGroup("Rendering");
@@ -216,7 +218,7 @@ void PhysicsDebrisData::packData(BitStream* stream)
    stream->write( waterDampingScale );
    stream->write( buoyancyDensity );
 
-   PACKDATA_ASSET(Shape);
+   PACKDATA_ASSET_REFACTOR(Shape);
 }
 
 void PhysicsDebrisData::unpackData(BitStream* stream)
@@ -237,7 +239,7 @@ void PhysicsDebrisData::unpackData(BitStream* stream)
    stream->read( &waterDampingScale );
    stream->read( &buoyancyDensity );
 
-   UNPACKDATA_ASSET(Shape);
+   UNPACKDATA_ASSET_REFACTOR(Shape);
 }
 
 DefineEngineMethod( PhysicsDebrisData, preload, void, (), , 
@@ -248,7 +250,7 @@ DefineEngineMethod( PhysicsDebrisData, preload, void, (), ,
 {
    String errorStr;
 
-   object->_setShape(object->getShape());
+   object->_setShape(object->_getShapeAssetId());
 
    if( !object->preload( false, errorStr ) )
       Con::errorf( "PhsysicsDebrisData::preload - error: %s", errorStr.c_str() );
@@ -362,7 +364,7 @@ bool PhysicsDebris::onAdd()
    }
 
    // Setup our bounding box
-   mObjBox = mDataBlock->mShape->mBounds;
+   mObjBox = mDataBlock->getShape()->mBounds;
    resetWorldBox();
 
    // Add it to the client scene.
@@ -625,7 +627,7 @@ void PhysicsDebris::_createFragments()
    if ( !mWorld )
       return;
 
-   TSShape *shape = mDataBlock->mShape;
+   TSShape *shape = mDataBlock->getShape();
 
    mShapeInstance = new TSShapeInstance( shape, true );
    mShapeInstance->animate();
@@ -699,7 +701,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->mShape;
+   TSShape *shape = mDataBlock->getShape();
    S32 itr = shape->nodes[colNode].parentIndex;
    itr = shape->nodes[itr].firstChild;
 

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

@@ -42,7 +42,7 @@ class TSShape;
 //**************************************************************************
 // Debris Data
 //**************************************************************************
-class PhysicsDebrisData : public GameBaseData
+class PhysicsDebrisData : public GameBaseData, protected AssetPtrCallback
 {
    typedef GameBaseData Parent;
 
@@ -86,10 +86,10 @@ public:
    /// Is rendererd during shadow passes.
    bool castShadows;
 
-   DECLARE_SHAPEASSET(PhysicsDebrisData, Shape, onShapeChanged);
-   DECLARE_ASSET_SETGET(PhysicsDebrisData, Shape);
+   DECLARE_SHAPEASSET_REFACTOR(PhysicsDebrisData, Shape)
 
    PhysicsDebrisData();
+   virtual ~PhysicsDebrisData();
 
    bool        onAdd() override;
    bool        preload( bool server, String &errorStr ) override;
@@ -97,13 +97,14 @@ public:
    void        packData( BitStream *stream ) override;
    void        unpackData( BitStream *stream ) override;
 
-   void onShapeChanged()
+   DECLARE_CONOBJECT( PhysicsDebrisData );
+
+protected:
+   void onAssetRefreshed(AssetPtrBase* pAssetPtrBase) override
    {
       reloadOnLocalClient();
    }
 
-   DECLARE_CONOBJECT( PhysicsDebrisData );
-
 };
 
 

+ 21 - 20
Engine/source/T3D/physics/physicsShape.cpp

@@ -78,11 +78,12 @@ PhysicsShapeData::PhysicsShapeData()
       buoyancyDensity( 0.0f ),
       simType( SimType_ClientServer )      
 {
-   INIT_ASSET(Shape);
+   mShapeAsset.registerRefreshNotify(this);
 }
 
 PhysicsShapeData::~PhysicsShapeData()
 {
+   mShapeAsset.unregisterRefreshNotify();
 }
 
 void PhysicsShapeData::initPersistFields()
@@ -90,7 +91,7 @@ void PhysicsShapeData::initPersistFields()
    docsURL;
    addGroup("Shapes");
 
-      INITPERSISTFIELD_SHAPEASSET(Shape, PhysicsShapeData, "@brief Shape asset to be used with this physics object.\n\n"
+      INITPERSISTFIELD_SHAPEASSET_REFACTOR(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 ),
@@ -180,7 +181,7 @@ void PhysicsShapeData::packData( BitStream *stream )
 { 
    Parent::packData( stream );
 
-   PACKDATA_ASSET(Shape);
+   PACKDATA_ASSET_REFACTOR(Shape);
 
    stream->write( mass );
    stream->write( dynamicFriction );
@@ -204,7 +205,7 @@ void PhysicsShapeData::unpackData( BitStream *stream )
 {
    Parent::unpackData(stream);
 
-   UNPACKDATA_ASSET(Shape);
+   UNPACKDATA_ASSET_REFACTOR(Shape);
 
    stream->read( &mass );
    stream->read( &dynamicFriction );
@@ -246,22 +247,22 @@ void PhysicsShapeData::_onResourceChanged( const Torque::Path &path )
    {
       return;
    }
-   if ( path != Path(mShapeAsset->getShapeFilePath()) )
+   if ( path != Path(mShapeAsset->getShapeFile()) )
       return;
 
-   _setShape(getShape());
+   _setShape(_getShapeAssetId());
 
    // Reload the changed shape.
    PhysicsCollisionRef reloadcolShape;
 
-   if ( !mShape )
+   if ( !getShape())
    {
       Con::warnf( ConsoleLogEntry::General, "PhysicsShapeData::_onResourceChanged: Could not reload %s.", path.getFileName().c_str() );
       return;
    }
 
    // Reload the collision shape.
-   reloadcolShape = mShape->buildColShape( false, Point3F::One );
+   reloadcolShape = getShape()->buildColShape( false, Point3F::One );
 
    if (  bool(reloadcolShape))
       colShape = reloadcolShape;
@@ -286,31 +287,31 @@ bool PhysicsShapeData::preload( bool server, String &errorBuffer )
 
    if (mShapeAsset.notNull())
    {
-      if (bool(mShape) == false)
+      if (bool(getShape()) == false)
       {
-         errorBuffer = String::ToString("PhysicsShapeData: Couldn't load shape \"%s\"", mShapeAssetId);
+         errorBuffer = String::ToString("PhysicsShapeData: Couldn't load shape \"%s\"", _getShapeAssetId());
          return false;
       }
-      if (!server && !mShape->preloadMaterialList(mShape.getPath()) && NetConnection::filesWereDownloaded())
+      if (!server && !getShape()->preloadMaterialList(getShape().getPath()) && NetConnection::filesWereDownloaded())
          shapeError = true;
 
    }
 
    // Prepare the shared physics collision shape.
-   if ( !colShape && mShape)
+   if ( !colShape && getShape())
    {
-      colShape = mShape->buildColShape( false, Point3F::One );
+      colShape = getShape()->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", mShapeAssetId);
-         Point3F halfWidth = mShape->mBounds.getExtents() * 0.5f;
+         Con::warnf( "PhysicsShapeData::preload - No collision found for shape '%s', auto-creating one", _getShapeAssetId());
+         Point3F halfWidth = getShape()->mBounds.getExtents() * 0.5f;
          colShape = PHYSICSMGR->createCollision();
          MatrixF centerXfm(true);
-         centerXfm.setPosition(mShape->mBounds.getCenter());
+         centerXfm.setPosition(getShape()->mBounds.getCenter());
          colShape->addBox(halfWidth, centerXfm);
          return true;
       }
@@ -703,11 +704,11 @@ bool PhysicsShape::_createShape()
    mAmbientSeq = -1;
 
    PhysicsShapeData *db = getDataBlock();
-   if ( !db || !db->mShape)
+   if ( !db || !db->getShape())
       return false;
 
    // Set the world box.
-   mObjBox = db->mShape->mBounds;
+   mObjBox = db->getShape()->mBounds;
    resetWorldBox();
 
    // If this is the server and its a client only simulation
@@ -721,11 +722,11 @@ bool PhysicsShape::_createShape()
    }
 
    // Create the shape instance.
-   mShapeInst = new TSShapeInstance( db->mShape, isClientObject() );
+   mShapeInst = new TSShapeInstance( db->getShape(), isClientObject() );
 
    if ( isClientObject() )
    {
-      mAmbientSeq = db->mShape->findSequence( "ambient" );
+      mAmbientSeq = db->getShape()->findSequence( "ambient" );
       _initAmbient();   
    }
 

+ 4 - 4
Engine/source/T3D/physics/physicsShape.h

@@ -51,7 +51,7 @@ class PhysicsDebrisData;
 class ExplosionData;
 
 
-class PhysicsShapeData : public GameBaseData
+class PhysicsShapeData : public GameBaseData, protected AssetPtrCallback
 {
    typedef GameBaseData Parent;
 
@@ -74,8 +74,7 @@ public:
 
 public:
 
-   DECLARE_SHAPEASSET(PhysicsShapeData, Shape, onShapeChanged);
-   DECLARE_ASSET_SETGET(PhysicsShapeData, Shape);
+   DECLARE_SHAPEASSET_REFACTOR(PhysicsShapeData, Shape)
 
    /// The shared unscaled collision shape.
    PhysicsCollisionRef colShape;
@@ -135,7 +134,8 @@ public:
    SimObjectRef< ExplosionData > explosion;   
    SimObjectRef< PhysicsShapeData > destroyedShape;
 
-   void onShapeChanged()
+protected:
+   void onAssetRefreshed(AssetPtrBase* pAssetPtrBase) override
    {
       reloadOnLocalClient();
    }

+ 35 - 40
Engine/source/T3D/player.cpp

@@ -297,7 +297,7 @@ PlayerData::PlayerData()
    imageAnimPrefixFP = StringTable->EmptyString();
    for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
    {
-      INIT_ASSET_ARRAY(ShapeFP, i);
+      mShapeFPAsset[i].registerRefreshNotify(this);
       mCRCFP[i] = 0;
       mValidShapeFP[i] = false;
    }
@@ -502,10 +502,10 @@ bool PlayerData::preload(bool server, String &errorStr)
 
    // If we don't have a shape don't crash out trying to
    // setup animations and sequences.
-   if ( mShape )
+   if (getShape())
    {
       // Go ahead a pre-load the player shape
-      TSShapeInstance* si = new TSShapeInstance(mShape, false);
+      TSShapeInstance* si = new TSShapeInstance(getShape(), false);
       TSThread* thread = si->addThread();
 
       // Extract ground transform velocity from animations
@@ -516,7 +516,7 @@ bool PlayerData::preload(bool server, String &errorStr)
          ActionAnimationDef *sp = &ActionAnimationList[i];
          dp->name          = sp->name;
          dp->dir.set(sp->dir.x,sp->dir.y,sp->dir.z);
-         dp->sequence      = mShape->findSequence(sp->name);
+         dp->sequence      = getShape()->findSequence(sp->name);
 
          // If this is a sprint action and is missing a sequence, attempt to use
          // the standard run ones.
@@ -524,7 +524,7 @@ bool PlayerData::preload(bool server, String &errorStr)
          {
             S32 offset = i-SprintRootAnim;
             ActionAnimationDef *standDef = &ActionAnimationList[RootAnim+offset];
-            dp->sequence = mShape->findSequence(standDef->name);
+            dp->sequence = getShape()->findSequence(standDef->name);
          }
 
          dp->velocityScale = true;
@@ -532,12 +532,12 @@ bool PlayerData::preload(bool server, String &errorStr)
          if (dp->sequence != -1)
             getGroundInfo(si,thread,dp);
       }
-      for (S32 b = 0; b < mShape->sequences.size(); b++)
+      for (S32 b = 0; b < getShape()->sequences.size(); b++)
       {
          if (!isTableSequence(b))
          {
             dp->sequence      = b;
-            dp->name          = mShape->getName(mShape->sequences[b].nameIndex);
+            dp->name          = getShape()->getName(getShape()->sequences[b].nameIndex);
             dp->velocityScale = false;
             getGroundInfo(si,thread,dp++);
          }
@@ -554,17 +554,17 @@ bool PlayerData::preload(bool server, String &errorStr)
             lookAction = c;
 
       // Resolve spine
-      spineNode[0] = mShape->findNode("Bip01 Pelvis");
-      spineNode[1] = mShape->findNode("Bip01 Spine");
-      spineNode[2] = mShape->findNode("Bip01 Spine1");
-      spineNode[3] = mShape->findNode("Bip01 Spine2");
-      spineNode[4] = mShape->findNode("Bip01 Neck");
-      spineNode[5] = mShape->findNode("Bip01 Head");
+      spineNode[0] = getShape()->findNode("Bip01 Pelvis");
+      spineNode[1] = getShape()->findNode("Bip01 Spine");
+      spineNode[2] = getShape()->findNode("Bip01 Spine1");
+      spineNode[3] = getShape()->findNode("Bip01 Spine2");
+      spineNode[4] = getShape()->findNode("Bip01 Neck");
+      spineNode[5] = getShape()->findNode("Bip01 Head");
 
       // Recoil animations
-      recoilSequence[0] = mShape->findSequence("light_recoil");
-      recoilSequence[1] = mShape->findSequence("medium_recoil");
-      recoilSequence[2] = mShape->findSequence("heavy_recoil");
+      recoilSequence[0] = getShape()->findSequence("light_recoil");
+      recoilSequence[1] = getShape()->findSequence("medium_recoil");
+      recoilSequence[2] = getShape()->findSequence("heavy_recoil");
    }
 
    // Convert pickupRadius to a delta of boundingBox
@@ -607,26 +607,26 @@ bool PlayerData::preload(bool server, String &errorStr)
    {
       bool shapeError = false;
 
-      if (mShapeFPAssetId[i] != StringTable->EmptyString())
+      if (mShapeFPAsset[i].notNull())
       {
-         if (!mShapeFP[i])
+         if (!getShapeFP(i))
          {
-            errorStr = String::ToString("PlayerData: Couldn't load mounted image %d shape \"%s\"", i, mShapeFPAssetId[i]);
+            errorStr = String::ToString("PlayerData: Couldn't load mounted image %d shape \"%s\"", i, _getShapeFPAssetId(i));
             return false;
          }
 
-         if (!server && !mShapeFP[i]->preloadMaterialList(mShapeFP[i].getPath()) && NetConnection::filesWereDownloaded())
+         if (!server && !getShapeFP(i)->preloadMaterialList(getShapeFP(i).getPath()) && NetConnection::filesWereDownloaded())
             shapeError = true;
 
          if (computeCRC)
          {
-            Con::printf("Validation required for mounted image %d shape: %s", i, mShapeFPAssetId[i]);
+            Con::printf("Validation required for mounted image %d shape: %s", i, _getShapeFPAssetId(i));
 
-            Torque::FS::FileNodeRef    fileRef = Torque::FS::GetFileNode(mShapeFP[i].getPath());
+            Torque::FS::FileNodeRef    fileRef = Torque::FS::GetFileNode(getShapeFP(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, getShapeFP(i).getPath().getFullPath().c_str());
                return false;
             }
 
@@ -634,7 +634,7 @@ bool PlayerData::preload(bool server, String &errorStr)
                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, mShapeFPAssetId[i]);
+               errorStr = String::ToString("PlayerData: Mounted image %d shape \"%s\" does not match version on server.", i, _getShapeFPAssetId(i));
                return false;
             }
          }
@@ -1134,13 +1134,8 @@ void PlayerData::initPersistFields()
 
       // Mounted images arrays
       addArray( "Mounted Images", ShapeBase::MaxMountedImages );
-         addProtectedField("shapeNameFP", TypeShapeFilename, Offset(mShapeFPName, PlayerData), &_setShapeFPData, &defaultProtectedGetFn, ShapeBase::MaxMountedImages,
-            "@brief File name of this player's shape that will be used in conjunction with the corresponding mounted image.\n\n"
-            "These optional parameters correspond to each mounted image slot to indicate a shape that is rendered "
-            "in addition to the mounted image shape.  Typically these are a player's arms (or arm) that is "
-            "animated along with the mounted image's state animation sequences.\n", AbstractClassRep::FIELD_HideInInspectors);
 
-         INITPERSISTFIELD_SHAPEASSET_ARRAY(ShapeFP, ShapeBase::MaxMountedImages, PlayerData, "@brief File name of this player's shape that will be used in conjunction with the corresponding mounted image.\n\n"
+         INITPERSISTFIELD_SHAPEASSET_ARRAY_REFACTOR(ShapeFP, ShapeBase::MaxMountedImages, PlayerData, "@brief File name of this player's shape that will be used in conjunction with the corresponding mounted image.\n\n"
             "These optional parameters correspond to each mounted image slot to indicate a shape that is rendered "
             "in addition to the mounted image shape.  Typically these are a player's arms (or arm) that is "
             "animated along with the mounted image's state animation sequences.\n");
@@ -1340,14 +1335,14 @@ void PlayerData::packData(BitStream* stream)
    stream->writeString(imageAnimPrefixFP);
    for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
    {
-      PACKDATA_ASSET_ARRAY(ShapeFP, i);
-
       // computeCRC is handled in ShapeBaseData
       if (computeCRC)
       {
          stream->write(mCRCFP[i]);
       }
    }
+
+   PACKDATA_ASSET_ARRAY_REFACTOR(ShapeFP, ShapeBase::MaxMountedImages)
 }
 
 void PlayerData::unpackData(BitStream* stream)
@@ -1520,14 +1515,14 @@ void PlayerData::unpackData(BitStream* stream)
    imageAnimPrefixFP = stream->readSTString();
    for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
    {
-      UNPACKDATA_ASSET_ARRAY(ShapeFP, i);
-
       // computeCRC is handled in ShapeBaseData
       if (computeCRC)
       {
          stream->read(&(mCRCFP[i]));
       }
    }
+
+   UNPACKDATA_ASSET_ARRAY_REFACTOR(ShapeFP, ShapeBase::MaxMountedImages)
 }
 
 
@@ -1863,9 +1858,9 @@ bool Player::onNewDataBlock( GameBaseData *dptr, bool reload )
    {
       for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
       {
-         if (bool(mDataBlock->mShapeFP[i]))
+         if (bool(mDataBlock->getShapeFP(i)))
          {
-            mShapeFPInstance[i] = new TSShapeInstance(mDataBlock->mShapeFP[i], isClientObject());
+            mShapeFPInstance[i] = new TSShapeInstance(mDataBlock->getShapeFP(i), isClientObject());
 
             mShapeFPInstance[i]->cloneMaterialList();
 
@@ -7511,8 +7506,8 @@ F32 Player::getAnimationDurationByID(U32 anim_id)
    if (anim_id == BAD_ANIM_ID)
       return 0.0f;
    S32 seq_id = mDataBlock->actionList[anim_id].sequence;
-   if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size())
-      return mDataBlock->mShape->sequences[seq_id].duration;
+   if (seq_id >= 0 && seq_id < mDataBlock->getShape()->sequences.size())
+      return mDataBlock->getShape()->sequences[seq_id].duration;
 
    return 0.0f;
 }
@@ -7524,8 +7519,8 @@ bool Player::isBlendAnimation(const char* name)
       return false;
 
    S32 seq_id = mDataBlock->actionList[anim_id].sequence;
-   if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size())
-      return mDataBlock->mShape->sequences[seq_id].isBlend();
+   if (seq_id >= 0 && seq_id < mDataBlock->getShape()->sequences.size())
+      return mDataBlock->getShape()->sequences[seq_id].isBlend();
 
    return false;
 }

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

@@ -58,7 +58,7 @@ class OpenVRTrackedObject;
 
 //----------------------------------------------------------------------------
 
-struct PlayerData: public ShapeBaseData {
+struct PlayerData: public ShapeBaseData /*protected AssetPtrCallback < already in shapebasedata. */ {
    typedef ShapeBaseData Parent;
    enum Constants {
       RecoverDelayBits = 7,
@@ -82,8 +82,7 @@ struct PlayerData: public ShapeBaseData {
                                                                   ///  that we don't create a TSThread on the player if we don't
                                                                   ///  need to.
 
-   DECLARE_SHAPEASSET_ARRAY(PlayerData, ShapeFP, ShapeBase::MaxMountedImages, onShapeChanged); ///< Used to render with mounted images in first person [optional]
-   DECLARE_ASSET_ARRAY_SETGET(PlayerData, ShapeFP);
+   DECLARE_SHAPEASSET_ARRAY_REFACTOR(PlayerData, ShapeFP, ShapeBase::MaxMountedImages)
 
    StringTableEntry  imageAnimPrefixFP;                           ///< Passed along to mounted images to modify
                                                                   ///  animation sequences played in first person. [optional]
@@ -391,6 +390,11 @@ struct PlayerData: public ShapeBaseData {
    DECLARE_CALLBACK( void, onEnterMissionArea, ( Player* obj ) );
    DECLARE_CALLBACK( void, onLeaveMissionArea, ( Player* obj ) );
    /// @}
+protected:
+   void onAssetRefreshed(AssetPtrBase* pAssetPtrBase) override
+   {
+      reloadOnLocalClient();
+   }
 };
 
 

+ 15 - 17
Engine/source/T3D/projectile.cpp

@@ -145,7 +145,7 @@ U32 Projectile::smProjectileWarpTicks = 5;
 //
 ProjectileData::ProjectileData()
 {
-   INIT_ASSET(ProjectileShape);
+   mProjectileShapeAsset.registerRefreshNotify(this);
 
    INIT_ASSET(ProjectileSound);
 
@@ -223,7 +223,7 @@ ProjectileData::ProjectileData(const ProjectileData& other, bool temp_clone) : G
    CLONE_ASSET(ProjectileSound);
    lightDesc = other.lightDesc;
    lightDescId = other.lightDescId; // -- for pack/unpack of lightDesc ptr
-   CLONE_ASSET(ProjectileShape);// -- TSShape loads using mProjectileShapeName
+   mProjectileShapeAsset = other.mProjectileShapeAsset;// -- TSShape loads using mProjectileShapeName
    activateSeq = other.activateSeq; // -- from projectileShape sequence "activate"
    maintainSeq = other.maintainSeq; // -- from projectileShape sequence "maintain"
    particleEmitter = other.particleEmitter;
@@ -237,9 +237,7 @@ void ProjectileData::initPersistFields()
 {
    docsURL;
    addGroup("Shapes");
-      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");
+      INITPERSISTFIELD_SHAPEASSET_REFACTOR(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");
@@ -383,20 +381,20 @@ bool ProjectileData::preload(bool server, String &errorStr)
             Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockid(lightDesc): %d", lightDescId);   
    }
 
-   if (mProjectileShapeAssetId != StringTable->EmptyString())
+   if (mProjectileShapeAsset.notNull())
    {
       //If we've got a shapeAsset assigned for our projectile, but we failed to load the shape data itself, report the error
-      if (!mProjectileShape)
+      if (!getProjectileShape())
       {
-         errorStr = String::ToString("ProjectileData::load: Couldn't load shape \"%s\"", mProjectileShapeAssetId);
+         errorStr = String::ToString("ProjectileData::load: Couldn't load shape \"%s\"", _getProjectileShapeAssetId());
          return false;
       }
       else
       {
-         activateSeq = mProjectileShape->findSequence("activate");
-         maintainSeq = mProjectileShape->findSequence("maintain");
+         activateSeq = getProjectileShape()->findSequence("activate");
+         maintainSeq = getProjectileShape()->findSequence("maintain");
 
-         TSShapeInstance* pDummy = new TSShapeInstance(mProjectileShape, !server);
+         TSShapeInstance* pDummy = new TSShapeInstance(getProjectileShape(), !server);
          delete pDummy;
       }
    }
@@ -409,7 +407,7 @@ void ProjectileData::packData(BitStream* stream)
 {
    Parent::packData(stream);
 
-   PACKDATA_ASSET(ProjectileShape);
+   PACKDATA_ASSET_REFACTOR(ProjectileShape);
 
    stream->writeFlag(faceViewer);
    if(stream->writeFlag(scale.x != 1 || scale.y != 1 || scale.z != 1))
@@ -474,7 +472,7 @@ void ProjectileData::unpackData(BitStream* stream)
 {
    Parent::unpackData(stream);
 
-   UNPACKDATA_ASSET(ProjectileShape);
+   UNPACKDATA_ASSET_REFACTOR(ProjectileShape);
 
    faceViewer = stream->readFlag();
    if(stream->readFlag())
@@ -800,9 +798,9 @@ bool Projectile::onAdd()
    }
    else
    {
-      if (bool(mDataBlock->mProjectileShape))
+      if (bool(mDataBlock->getProjectileShape()))
       {
-         mProjectileShape = new TSShapeInstance(mDataBlock->mProjectileShape, isClientObject());
+         mProjectileShape = new TSShapeInstance(mDataBlock->getProjectileShape(), isClientObject());
 
          if (mDataBlock->activateSeq != -1)
          {
@@ -841,8 +839,8 @@ bool Projectile::onAdd()
       processAfter(mSourceObject);
 
    // Setup our bounding box
-   if (bool(mDataBlock->mProjectileShape) == true)
-      mObjBox = mDataBlock->mProjectileShape->mBounds;
+   if (bool(mDataBlock->getProjectileShape()) == true)
+      mObjBox = mDataBlock->getProjectileShape()->mBounds;
    else
       mObjBox = Box3F(Point3F(0, 0, 0), Point3F(0, 0, 0));
 

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

@@ -63,7 +63,7 @@ class Projectile;
 
 //--------------------------------------------------------------------------
 /// Datablock for projectiles.  This class is the base class for all other projectiles.
-class ProjectileData : public GameBaseData
+class ProjectileData : public GameBaseData, protected AssetPtrCallback
 {
    typedef GameBaseData Parent;
 
@@ -71,8 +71,7 @@ protected:
    bool onAdd() override;
 
 public:
-   DECLARE_SHAPEASSET(ProjectileData, ProjectileShape, onShapeChanged);
-   DECLARE_ASSET_SETGET(ProjectileData, ProjectileShape);
+   DECLARE_SHAPEASSET_REFACTOR(ProjectileData, ProjectileShape)
 
    /// Set to true if it is a billboard and want it to always face the viewer, false otherwise
    bool faceViewer;
@@ -154,7 +153,8 @@ public:
    ProjectileData(const ProjectileData&, bool = false);
    bool allowSubstitutions() const override { return true; }
 
-   void onShapeChanged()
+protected:
+   void onAssetRefreshed(AssetPtrBase* pAssetPtrBase) override
    {
       reloadOnLocalClient();
    }

+ 3 - 3
Engine/source/T3D/proximityMine.cpp

@@ -144,11 +144,11 @@ bool ProximityMineData::preload( bool server, String& errorStr )
       }
    }
 
-   if ( mShape )
+   if ( getShape() )
    {
       // Lookup animation sequences
-      armingSequence = mShape->findSequence( "armed" );
-      triggerSequence = mShape->findSequence( "triggered" );
+      armingSequence = getShape()->findSequence( "armed" );
+      triggerSequence = getShape()->findSequence( "triggered" );
    }
 
    return true;

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

@@ -310,7 +310,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 asset \"%s\"", mShapeAsset.getAssetId());
+      errorStr = String::ToString("RigidShapeData: Couldn't load shape asset \"%s\"", getShapeAsset().getAssetId());
       return false;
    }
 

+ 152 - 159
Engine/source/T3D/shapeBase.cpp

@@ -201,13 +201,12 @@ ShapeBaseData::ShapeBaseData()
    inheritEnergyFromMount( false ),
    mAIControllData(NULL)
 {
-   INIT_ASSET(Shape);
-   INIT_ASSET(DebrisShape);
-
    dMemset( mountPointNode, -1, sizeof( S32 ) * SceneObject::NumMountPoints );
    remap_txr_tags = NULL;
    remap_buffer = NULL;
    silent_bbox_check = false;
+   mShapeAsset.registerRefreshNotify(this);
+   mDebrisShapeAsset.registerRefreshNotify(this);
 }
 
 ShapeBaseData::ShapeBaseData(const ShapeBaseData& other, bool temp_clone) : GameBaseData(other, temp_clone)
@@ -217,13 +216,13 @@ ShapeBaseData::ShapeBaseData(const ShapeBaseData& other, bool temp_clone) : Game
    shadowProjectionDistance = other.shadowProjectionDistance;
    shadowSphereAdjust = other.shadowSphereAdjust;
    cloakTexName = other.cloakTexName;
-   CLONE_ASSET(Shape);
+   mShapeAsset = other.mShapeAsset;
    cubeDescName = other.cubeDescName;
    cubeDescId = other.cubeDescId;
    reflectorDesc = other.reflectorDesc;
    debris = other.debris;
    debrisID = other.debrisID; // -- for pack/unpack of debris ptr
-   CLONE_ASSET(DebrisShape);
+   mDebrisShapeAsset = other.mDebrisShapeAsset;
    explosion = other.explosion;
    explosionID = other.explosionID; // -- for pack/unpack of explosion ptr
    underwaterExplosion = other.underwaterExplosion;
@@ -245,7 +244,6 @@ ShapeBaseData::ShapeBaseData(const ShapeBaseData& other, bool temp_clone) : Game
    cameraMaxFov = other.cameraMaxFov;
    cameraCanBank = other.cameraCanBank;
    mountedImagesBank = other.mountedImagesBank;
-   mShape = other.mShape; // -- TSShape loaded using shapeName
    mCRC = other.mCRC; // -- from shape, used to verify client shape 
    computeCRC = other.computeCRC;
    eyeNode = other.eyeNode; // -- from shape node "eye"
@@ -304,6 +302,9 @@ ShapeBaseData::~ShapeBaseData()
 
    if (remap_buffer && !isTempClone())
       dFree(remap_buffer);
+
+   mShapeAsset.unregisterRefreshNotify();
+   mDebrisShapeAsset.unregisterRefreshNotify();
 }
 
 bool ShapeBaseData::preload(bool server, String &errorStr)
@@ -342,156 +343,159 @@ bool ShapeBaseData::preload(bool server, String &errorStr)
             "ShapeBaseData::preload: invalid debris data");
       }
 
-      if( bool(mDebrisShape))
+      if(mDebrisShapeAsset.notNull())
       {
-         TSShapeInstance* pDummy = new TSShapeInstance(mDebrisShape, !server);
+         TSShapeInstance* pDummy = new TSShapeInstance(getDebrisShape(), !server);
          delete pDummy;
       }
    }
 
    S32 i;
-   U32 assetStatus = ShapeAsset::getAssetErrCode(mShapeAsset);
-   if (assetStatus == AssetBase::Ok || assetStatus == AssetBase::UsingFallback)
+   if (mShapeAsset.notNull())
    {
-      if (!server && !mShape->preloadMaterialList(mShape.getPath()) && NetConnection::filesWereDownloaded())
-         shapeError = true;
-
-      if(computeCRC)
-      {
-         Con::printf("Validation required for shape asset: %s", mShapeAsset.getAssetId());
-
-         Torque::FS::FileNodeRef    fileRef = Torque::FS::GetFileNode(mShapeAsset->getShapePath());
-
-         if (!fileRef)
-         {
-            errorStr = String::ToString("ShapeBaseData: Couldn't load shape asset \"%s\"", mShapeAsset.getAssetId());
-            return false;
-         }
-
-         if(server)
-            mCRC = fileRef->getChecksum();
-         else if(mCRC != fileRef->getChecksum())
-         {
-            errorStr = String::ToString("Shape asset \"%s\" does not match version on server.", mShapeAsset.getAssetId());
-            return false;
-         }
-      }
-      // Resolve details and camera node indexes.
-      static const String sCollisionStr( "collision-" );
-
-      for (i = 0; i < mShape->details.size(); i++)
+      U32 assetStatus = ShapeAsset::getAssetErrCode(mShapeAsset);
+      if (assetStatus == AssetBase::Ok || assetStatus == AssetBase::UsingFallback)
       {
-         const String &name = mShape->names[mShape->details[i].nameIndex];
+         if (!server && !getShape()->preloadMaterialList(getShape().getPath()) && NetConnection::filesWereDownloaded())
+            shapeError = true;
 
-         if (name.compare( sCollisionStr, sCollisionStr.length(), String::NoCase ) == 0)
+         if (computeCRC)
          {
-            collisionDetails.push_back(i);
-            collisionBounds.increment();
+            Con::printf("Validation required for shape asset: %s", mShapeAsset.getAssetId());
 
-            mShape->computeBounds(collisionDetails.last(), collisionBounds.last());
-            mShape->getAccelerator(collisionDetails.last());
+            Torque::FS::FileNodeRef    fileRef = Torque::FS::GetFileNode(mShapeAsset->getShapeFile());
 
-            if (!mShape->mBounds.isContained(collisionBounds.last()))
+            if (!fileRef)
             {
-               if (!silent_bbox_check)
-               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;
+               errorStr = String::ToString("ShapeBaseData: Couldn't load shape asset \"%s\"", mShapeAsset.getAssetId());
+               return false;
             }
-            else if (collisionBounds.last().isValidBox() == false)
+
+            if (server)
+               mCRC = fileRef->getChecksum();
+            else if (mCRC != fileRef->getChecksum())
             {
-               if (!silent_bbox_check)
-               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;
+               errorStr = String::ToString("Shape asset \"%s\" does not match version on server.", mShapeAsset.getAssetId());
+               return false;
             }
+         }
+         // Resolve details and camera node indexes.
+         static const String sCollisionStr("collision-");
 
-            // The way LOS works is that it will check to see if there is a LOS detail that matches
-            // the the collision detail + 1 + MaxCollisionShapes (this variable name should change in
-            // the future). If it can't find a matching LOS it will simply use the collision instead.
-            // We check for any "unmatched" LOS's further down
-            LOSDetails.increment();
+         for (i = 0; i < getShape()->details.size(); i++)
+         {
+            const String& name = getShape()->names[getShape()->details[i].nameIndex];
 
-            String   buff = String::ToString("LOS-%d", i + 1 + MaxCollisionShapes);
-            U32 los = mShape->findDetail(buff);
-            if (los == -1)
-               LOSDetails.last() = i;
-            else
-               LOSDetails.last() = los;
-         }
-      }
+            if (name.compare(sCollisionStr, sCollisionStr.length(), String::NoCase) == 0)
+            {
+               collisionDetails.push_back(i);
+               collisionBounds.increment();
 
-      // Snag any "unmatched" LOS details
-      static const String sLOSStr( "LOS-" );
+               getShape()->computeBounds(collisionDetails.last(), collisionBounds.last());
+               getShape()->getAccelerator(collisionDetails.last());
 
-      for (i = 0; i < mShape->details.size(); i++)
-      {
-         const String &name = mShape->names[mShape->details[i].nameIndex];
+               if (!getShape()->mBounds.isContained(collisionBounds.last()))
+               {
+                  if (!silent_bbox_check)
+                     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() = getShape()->mBounds;
+               }
+               else if (collisionBounds.last().isValidBox() == false)
+               {
+                  if (!silent_bbox_check)
+                     Con::errorf("Error: shape asset %s-collision detail %d (Collision-%d) bounds box invalid!", mShapeAsset.getAssetId(), collisionDetails.size() - 1, collisionDetails.last());
+                  collisionBounds.last() = getShape()->mBounds;
+               }
 
-         if (name.compare( sLOSStr, sLOSStr.length(), String::NoCase ) == 0)
+               // The way LOS works is that it will check to see if there is a LOS detail that matches
+               // the the collision detail + 1 + MaxCollisionShapes (this variable name should change in
+               // the future). If it can't find a matching LOS it will simply use the collision instead.
+               // We check for any "unmatched" LOS's further down
+               LOSDetails.increment();
+
+               String   buff = String::ToString("LOS-%d", i + 1 + MaxCollisionShapes);
+               U32 los = getShape()->findDetail(buff);
+               if (los == -1)
+                  LOSDetails.last() = i;
+               else
+                  LOSDetails.last() = los;
+            }
+         }
+
+         // Snag any "unmatched" LOS details
+         static const String sLOSStr("LOS-");
+
+         for (i = 0; i < getShape()->details.size(); i++)
          {
-            // See if we already have this LOS
-            bool found = false;
-            for (U32 j = 0; j < LOSDetails.size(); j++)
+            const String& name = getShape()->names[getShape()->details[i].nameIndex];
+
+            if (name.compare(sLOSStr, sLOSStr.length(), String::NoCase) == 0)
             {
-               if (LOSDetails[j] == i)
+               // See if we already have this LOS
+               bool found = false;
+               for (U32 j = 0; j < LOSDetails.size(); j++)
                {
+                  if (LOSDetails[j] == i)
+                  {
                      found = true;
                      break;
+                  }
                }
-            }
 
-            if (!found)
-               LOSDetails.push_back(i);
+               if (!found)
+                  LOSDetails.push_back(i);
+            }
          }
-      }
 
-      debrisDetail = mShape->findDetail("Debris-17");
-      eyeNode = mShape->findNode("eye");
-      earNode = mShape->findNode( "ear" );
-      if( earNode == -1 )
-         earNode = eyeNode;
-      cameraNode = mShape->findNode("cam");
-      if (cameraNode == -1)
-         cameraNode = eyeNode;
-
-      // Resolve mount point node indexes
-      for (i = 0; i < SceneObject::NumMountPoints; i++) {
-         char fullName[256];
-         dSprintf(fullName,sizeof(fullName),"mount%d",i);
-         mountPointNode[i] = mShape->findNode(fullName);
-      }
+         debrisDetail = getShape()->findDetail("Debris-17");
+         eyeNode = getShape()->findNode("eye");
+         earNode = getShape()->findNode("ear");
+         if (earNode == -1)
+            earNode = eyeNode;
+         cameraNode = getShape()->findNode("cam");
+         if (cameraNode == -1)
+            cameraNode = eyeNode;
+
+         // Resolve mount point node indexes
+         for (i = 0; i < SceneObject::NumMountPoints; i++) {
+            char fullName[256];
+            dSprintf(fullName, sizeof(fullName), "mount%d", i);
+            mountPointNode[i] = getShape()->findNode(fullName);
+         }
 
-        // find the AIRepairNode - hardcoded to be the last node in the array...
-      mountPointNode[AIRepairNode] = mShape->findNode("AIRepairNode");
+         // find the AIRepairNode - hardcoded to be the last node in the array...
+         mountPointNode[AIRepairNode] = getShape()->findNode("AIRepairNode");
 
-      //
-      hulkSequence = mShape->findSequence("Visibility");
-      damageSequence = mShape->findSequence("Damage");
+         //
+         hulkSequence = getShape()->findSequence("Visibility");
+         damageSequence = getShape()->findSequence("Damage");
 
-      //
-      F32 w = mShape->mBounds.len_y() / 2;
-      if (cameraMaxDist < w)
-         cameraMaxDist = w;
-      // just parse up the string and collect the remappings in txr_tag_remappings.
-      if (!server && remap_txr_tags != NULL && remap_txr_tags != StringTable->insert(""))
-      {
-         txr_tag_remappings.clear();
-         if (remap_buffer)
-            dFree(remap_buffer);
+         //
+         F32 w = getShape()->mBounds.len_y() / 2;
+         if (cameraMaxDist < w)
+            cameraMaxDist = w;
+         // just parse up the string and collect the remappings in txr_tag_remappings.
+         if (!server && remap_txr_tags != NULL && remap_txr_tags != StringTable->insert(""))
+         {
+            txr_tag_remappings.clear();
+            if (remap_buffer)
+               dFree(remap_buffer);
 
-         remap_buffer = dStrdup(remap_txr_tags);
+            remap_buffer = dStrdup(remap_txr_tags);
 
-         char* remap_token = dStrtok(remap_buffer, " \t");
-         while (remap_token != NULL)
-         {
-            char* colon = dStrchr(remap_token, ':');
-            if (colon)
+            char* remap_token = dStrtok(remap_buffer, " \t");
+            while (remap_token != NULL)
             {
-               *colon = '\0';
-               txr_tag_remappings.increment();
-               txr_tag_remappings.last().old_tag = remap_token;
-               txr_tag_remappings.last().new_tag = colon+1;
+               char* colon = dStrchr(remap_token, ':');
+               if (colon)
+               {
+                  *colon = '\0';
+                  txr_tag_remappings.increment();
+                  txr_tag_remappings.last().old_tag = remap_token;
+                  txr_tag_remappings.last().new_tag = colon + 1;
+               }
+               remap_token = dStrtok(NULL, " \t");
             }
-            remap_token = dStrtok(NULL, " \t");
          }
       }
    }
@@ -543,12 +547,12 @@ void ShapeBaseData::initPersistFields()
 {
    docsURL;
    addGroup( "Shapes" );
-      INITPERSISTFIELD_SHAPEASSET(Shape, ShapeBaseData, "The source shape asset.");
+   INITPERSISTFIELD_SHAPEASSET_REFACTOR(Shape, ShapeBaseData, "The source shape asset.");
       addField("computeCRC", TypeBool, Offset(computeCRC, ShapeBaseData),
          "If true, verify that the CRC of the client's shape model matches the "
          "server's CRC for the shape model when loaded by the client.");
       addField("silentBBoxValidation", TypeBool, Offset(silent_bbox_check, ShapeBaseData));
-      INITPERSISTFIELD_SHAPEASSET(DebrisShape, ShapeBaseData, "The shape asset to use for auto-generated breakups via blowup(). @note may not be functional.");
+      INITPERSISTFIELD_SHAPEASSET_REFACTOR(DebrisShape, ShapeBaseData, "The shape asset to use for auto-generated breakups via blowup(). @note may not be functional.");
    endGroup( "Shapes" );
    addGroup("Movement");
       addField("aiControllerData", TYPEID< AIControllerData >(), Offset(mAIControllData, ShapeBaseData),
@@ -677,12 +681,12 @@ DefineEngineMethod( ShapeBaseData, checkDeployPos, bool, ( TransformF txfm ),,
 
    "@note This is a server side only check, and is not actually limited to spawning.\n")
 {
-   if (bool(object->mShape) == false)
+   if (bool(object->getShape()) == false)
       return false;
 
    MatrixF mat = txfm.getMatrix();
 
-   Box3F objBox = object->mShape->mBounds;
+   Box3F objBox = object->getShape()->mBounds;
    Point3F boxCenter = (objBox.minExtents + objBox.maxExtents) * 0.5f;
    objBox.minExtents = boxCenter + (objBox.minExtents - boxCenter) * 0.9f;
    objBox.maxExtents = boxCenter + (objBox.maxExtents - boxCenter) * 0.9f;
@@ -752,8 +756,8 @@ void ShapeBaseData::packData(BitStream* stream)
    stream->write(shadowProjectionDistance);
    stream->write(shadowSphereAdjust);
 
-   PACKDATA_ASSET(Shape);
-   PACKDATA_ASSET(DebrisShape);
+   PACKDATA_ASSET_REFACTOR(Shape);
+   PACKDATA_ASSET_REFACTOR(DebrisShape);
 
    stream->writeString(cloakTexName);
    if(stream->writeFlag(mass != gShapeBaseDataProto.mass))
@@ -829,8 +833,8 @@ void ShapeBaseData::unpackData(BitStream* stream)
    stream->read(&shadowProjectionDistance);
    stream->read(&shadowSphereAdjust);
 
-   UNPACKDATA_ASSET(Shape);
-   UNPACKDATA_ASSET(DebrisShape);
+   UNPACKDATA_ASSET_REFACTOR(Shape);
+   UNPACKDATA_ASSET_REFACTOR(DebrisShape);
 
    cloakTexName = stream->readSTString();
    if(stream->readFlag())
@@ -918,17 +922,6 @@ void ShapeBaseData::unpackData(BitStream* stream)
    silent_bbox_check = stream->readFlag();
 }
 
-//
-//
-void ShapeBaseData::onShapeChanged()
-{
-   reloadOnLocalClient();
-}
-
-void ShapeBaseData::onDebrisChanged()
-{
-   reloadOnLocalClient();
-}
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 
@@ -1210,12 +1203,12 @@ bool ShapeBase::onNewDataBlock( GameBaseData *dptr, bool reload )
 
    // Even if loadShape succeeds, there may not actually be
    // a shape assigned to this object.
-   if (bool(mDataBlock->mShape)) {
+   if (bool(mDataBlock->getShape())) {
       delete mShapeInstance;
       if (isClientObject() && mDataBlock->txr_tag_remappings.size() > 0)
       {
          // temporarily substitute material tags with alternates
-         TSMaterialList* mat_list = mDataBlock->mShape->materialList;
+         TSMaterialList* mat_list = mDataBlock->getShape()->materialList;
          if (mat_list)
          {
             for (S32 i = 0; i < mDataBlock->txr_tag_remappings.size(); i++)
@@ -1235,7 +1228,7 @@ bool ShapeBase::onNewDataBlock( GameBaseData *dptr, bool reload )
             }
          }
       }
-      mShapeInstance = new TSShapeInstance(mDataBlock->mShape, isClientObject());
+      mShapeInstance = new TSShapeInstance(mDataBlock->getShape(), isClientObject());
       if (isClientObject())
       {
          mShapeInstance->cloneMaterialList();
@@ -1243,7 +1236,7 @@ bool ShapeBase::onNewDataBlock( GameBaseData *dptr, bool reload )
          // restore the material tags to original form
          if (mDataBlock->txr_tag_remappings.size() > 0)
          {
-            TSMaterialList* mat_list = mDataBlock->mShape->materialList;
+            TSMaterialList* mat_list = mDataBlock->getShape()->materialList;
             if (mat_list)
             {
                for (S32 i = 0; i < mDataBlock->txr_tag_remappings.size(); i++)
@@ -1269,11 +1262,11 @@ bool ShapeBase::onNewDataBlock( GameBaseData *dptr, bool reload )
          }
       }
 
-      mObjBox = mDataBlock->mShape->mBounds;
+      mObjBox = mDataBlock->getShape()->mBounds;
       resetWorldBox();
 
       // Set the initial mesh hidden state.
-      mMeshHidden.setSize(mDataBlock->mShape->objects.size());
+      mMeshHidden.setSize(mDataBlock->getShape()->objects.size());
       mMeshHidden.clear();
 
       // Initialize the threads
@@ -1297,11 +1290,11 @@ bool ShapeBase::onNewDataBlock( GameBaseData *dptr, bool reload )
 
             AssertFatal(prevDB != NULL, "ShapeBase::onNewDataBlock - how did you have a sequence playing without a prior datablock?");
 
-            const TSShape* prevShape = prevDB->mShape;
+            const TSShape* prevShape = prevDB->getShape();
             const TSShape::Sequence& prevSeq = prevShape->sequences[st.sequence];
             const String& prevSeqName = prevShape->names[prevSeq.nameIndex];
 
-            st.sequence = mDataBlock->mShape->findSequence(prevSeqName);
+            st.sequence = mDataBlock->getShape()->findSequence(prevSeqName);
 
             if (st.sequence != -1)
             {
@@ -1971,13 +1964,13 @@ void ShapeBase::blowUp()
 
    TSShapeInstance *debShape = NULL;
 
-   if( mDataBlock->mDebrisShape == NULL )
+   if( mDataBlock->getDebrisShape() == NULL)
    {
       return;
    }
    else
    {
-      debShape = new TSShapeInstance( mDataBlock->mDebrisShape, true);
+      debShape = new TSShapeInstance( mDataBlock->getDebrisShape(), true);
    }
 
 
@@ -2049,7 +2042,7 @@ Point3F ShapeBase::getAIRepairPoint()
 //----------------------------------------------------------------------------
 void ShapeBase::getNodeTransform(const char* nodeName, MatrixF* outMat)
 {
-   S32 nodeIDx = mDataBlock->getShapeResource()->findNode(nodeName);
+   S32 nodeIDx = mDataBlock->getShape()->findNode(nodeName);
    const MatrixF& xfm = isMounted() ? mMount.xfm : MatrixF::Identity;
 
    MatrixF nodeTransform(xfm);
@@ -2216,7 +2209,7 @@ void ShapeBase::getNodeTransform(const char* nodeName, const MatrixF& xfm, Matri
    if (!mShapeInstance)
       return;
 
-   S32 nodeIDx = mDataBlock->getShapeResource()->findNode(nodeName);
+   S32 nodeIDx = mDataBlock->getShape()->findNode(nodeName);
 
    MatrixF nodeTransform(xfm);
    const Point3F& scale = getScale();
@@ -5027,7 +5020,7 @@ void ShapeBase::_updateHiddenMeshes()
 
 void ShapeBase::setMeshHidden( const char *meshName, bool forceHidden )
 {
-   setMeshHidden( mDataBlock->mShape->findObject( meshName ), forceHidden );
+   setMeshHidden( mDataBlock->getShape()->findObject(meshName), forceHidden);
 }
 
 void ShapeBase::setMeshHidden( S32 meshIndex, bool forceHidden )
@@ -5096,7 +5089,7 @@ void ShapeBase::dumpMeshVisibility()
    {
       const TSShapeInstance::MeshObjectInstance &mesh = meshes[i];
 
-      const String &meshName = mDataBlock->mShape->getMeshName( i );
+      const String &meshName = mDataBlock->getShape()->getMeshName( i );
 
       Con::printf( "%d - %s - forceHidden = %s, visibility = %f", 
          i,
@@ -5378,8 +5371,8 @@ F32 ShapeBase::getAnimationDurationByID(U32 anim_id)
       return 0.0f;
 
    S32 seq_id = (S32) anim_id;
-   if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size())
-      return mDataBlock->mShape->sequences[seq_id].duration;
+   if (seq_id >= 0 && seq_id < mDataBlock->getShape()->sequences.size())
+      return mDataBlock->getShape()->sequences[seq_id].duration;
 
    return 0.0f;
 }
@@ -5391,8 +5384,8 @@ bool ShapeBase::isBlendAnimation(const char* name)
       return false;
 
    S32 seq_id = (S32) anim_id;
-   if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size())
-      return mDataBlock->mShape->sequences[seq_id].isBlend();
+   if (seq_id >= 0 && seq_id < mDataBlock->getShape()->sequences.size())
+      return mDataBlock->getShape()->sequences[seq_id].isBlend();
 
    return false;
 }
@@ -5404,11 +5397,11 @@ const char* ShapeBase::getLastClipName(U32 clip_tag)
 
    S32 seq_id = (S32) last_anim_id;
 
-   S32 idx = mDataBlock->mShape->sequences[seq_id].nameIndex;
-   if (idx < 0 || idx >= mDataBlock->mShape->names.size())
+   S32 idx = mDataBlock->getShape()->sequences[seq_id].nameIndex;
+   if (idx < 0 || idx >= mDataBlock->getShape()->names.size())
       return 0;
 
-   return mDataBlock->mShape->names[idx];
+   return mDataBlock->getShape()->names[idx];
 }
 
 //

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

@@ -140,7 +140,8 @@ class ShapeBaseConvex : public Convex
 
 //--------------------------------------------------------------------------
 
-struct ShapeBaseImageData: public GameBaseData {
+struct ShapeBaseImageData: public GameBaseData, protected AssetPtrCallback
+{
   private:
    typedef GameBaseData Parent;
 
@@ -380,11 +381,7 @@ 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.
 
-   DECLARE_SHAPEASSET_ARRAY(ShapeBaseImageData, Shape, MaxShapes, onShapeChanged);  ///< Name of shape to render.
-   DECLARE_ASSET_ARRAY_SETGET(ShapeBaseImageData, Shape);
-
-   //DECLARE_SHAPEASSET(ShapeBaseImageData, ShapeFP);  ///< Name of shape to render in first person (optional).
-   //DECLARE_ASSET_SETGET(ShapeBaseImageData, ShapeFP);
+   DECLARE_SHAPEASSET_ARRAY_REFACTOR(ShapeBaseImageData, Shape, MaxShapes)  ///< Name of shape to render.
 
    StringTableEntry  imageAnimPrefix;     ///< Passed along to the mounting shape to modify
                                           ///  animation sequences played in 3rd person. [optional]
@@ -519,6 +516,12 @@ struct ShapeBaseImageData: public GameBaseData {
    DECLARE_CALLBACK( void, onMount, ( SceneObject* obj, S32 slot, F32 dt ) );
    DECLARE_CALLBACK( void, onUnmount, ( SceneObject* obj, S32 slot, F32 dt ) );
    /// @}
+
+protected:
+   void onAssetRefreshed(AssetPtrBase* pAssetPtrBase) override
+   {
+      reloadOnLocalClient();
+   }
 };
 
 typedef ShapeBaseImageData::LightType ShapeBaseImageLightType;
@@ -533,7 +536,7 @@ DefineEnumType( ShapeBaseImageRecoilState );
 
 //--------------------------------------------------------------------------
 /// @nosubgrouping
-struct ShapeBaseData : public GameBaseData {
+struct ShapeBaseData : public GameBaseData, protected AssetPtrCallback {
   private:
    typedef GameBaseData Parent;
    
@@ -553,8 +556,7 @@ public:
    F32 shadowProjectionDistance;
    F32 shadowSphereAdjust;
 
-   DECLARE_SHAPEASSET(ShapeBaseData, Shape, onShapeChanged);
-   DECLARE_ASSET_SETGET(ShapeBaseData, Shape);
+   DECLARE_SHAPEASSET_REFACTOR(ShapeBaseData, Shape)
 
    StringTableEntry  cloakTexName;
 
@@ -570,8 +572,7 @@ public:
    DebrisData *      debris;
    S32               debrisID;
 
-   DECLARE_SHAPEASSET(ShapeBaseData, DebrisShape, onDebrisChanged);
-   DECLARE_ASSET_SETGET(ShapeBaseData, DebrisShape);
+   DECLARE_SHAPEASSET_REFACTOR(ShapeBaseData, DebrisShape)
 
    ExplosionData*    explosion;
    S32               explosionID;
@@ -691,10 +692,14 @@ public:
    Vector<TextureTagRemapping> txr_tag_remappings;
    bool silent_bbox_check;
 
-   void onShapeChanged();
-   void onDebrisChanged();
 public:
    ShapeBaseData(const ShapeBaseData&, bool = false);
+
+protected:
+   void onAssetRefreshed(AssetPtrBase* pAssetPtrBase) override
+   {
+      reloadOnLocalClient();
+   }
 };
 
 

+ 23 - 33
Engine/source/T3D/shapeImage.cpp

@@ -293,8 +293,7 @@ ShapeBaseImageData::ShapeBaseImageData()
       isAnimated[i] = false;
       hasFlash[i] = false;
       shapeIsValid[i] = false;
-
-      INIT_ASSET_ARRAY(Shape, i);
+      mShapeAsset[i].registerRefreshNotify(this);
    }
 
    shakeCamera = false;
@@ -454,10 +453,7 @@ bool ShapeBaseImageData::preload(bool server, String &errorStr)
 
       if (!mShapeAsset[i].isNull())
       {
-         // Resolve shapename
-         mShape[i] = mShapeAsset[i]->getShapeResource();
-
-         if (!bool(mShape[i])) {
+         if (!bool(getShape(i))) {
             errorStr = String::ToString("Unable to load shape asset: %s", mShapeAsset[i]->getAssetId());
             return false;
          }
@@ -465,7 +461,7 @@ bool ShapeBaseImageData::preload(bool server, String &errorStr)
          {
             Con::printf("Validation required for shape asset: %s", mShapeAsset[i]->getAssetId());
 
-            Torque::FS::FileNodeRef    fileRef = Torque::FS::GetFileNode(mShape[i].getPath());
+            Torque::FS::FileNodeRef    fileRef = Torque::FS::GetFileNode(getShape(i).getPath());
 
             if (!fileRef)
             {
@@ -485,23 +481,23 @@ bool ShapeBaseImageData::preload(bool server, String &errorStr)
          }
 
          // Resolve nodes & build mount transform
-         eyeMountNode[i] = mShape[i]->findNode("eyeMount");
-         eyeNode[i] = mShape[i]->findNode("eye");
+         eyeMountNode[i]   = getShape(i)->findNode("eyeMount");
+         eyeNode[i]        = getShape(i)->findNode("eye");
          if (eyeNode[i] == -1)
             eyeNode[i] = eyeMountNode[i];
-         ejectNode[i] = mShape[i]->findNode("ejectPoint");
-         muzzleNode[i] = mShape[i]->findNode("muzzlePoint");
-         retractNode[i] = mShape[i]->findNode("retractionPoint");
+         ejectNode[i]      = getShape(i)->findNode("ejectPoint");
+         muzzleNode[i]     = getShape(i)->findNode("muzzlePoint");
+         retractNode[i]    = getShape(i)->findNode("retractionPoint");
          mountTransform[i] = mountOffset;
-         S32 node = mShape[i]->findNode("mountPoint");
+         S32 node          = getShape(i)->findNode("mountPoint");
          if (node != -1) {
             MatrixF total(1);
             do {
                MatrixF nmat;
                QuatF q;
-               TSTransform::setMatrix(mShape[i]->defaultRotations[node].getQuatF(&q), mShape[i]->defaultTranslations[node],&nmat);
+               TSTransform::setMatrix(getShape(i)->defaultRotations[node].getQuatF(&q), getShape(i)->defaultTranslations[node],&nmat);
                total.mul(nmat);
-               node = mShape[i]->nodes[node].parentIndex;
+               node = getShape(i)->nodes[node].parentIndex;
             }
             while(node != -1);
             total.inverse();
@@ -514,7 +510,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] = mShape[i]->findSequence(stateSequence[j]);
+               s.sequence[i] = getShape(i)->findSequence(stateSequence[j]);
             if (s.sequence[i] != -1)
             {
                // This state has an animation sequence
@@ -525,7 +521,7 @@ bool ShapeBaseImageData::preload(bool server, String &errorStr)
                char bufferVis[128];
                dStrncpy(bufferVis, stateSequence[j], 100);
                dStrcat(bufferVis, "_vis", 128);
-               s.sequenceVis[i] = mShape[i]->findSequence(bufferVis);
+               s.sequenceVis[i] = getShape(i)->findSequence(bufferVis);
             }
             if (s.sequenceVis[i] != -1)
             {
@@ -537,13 +533,13 @@ bool ShapeBaseImageData::preload(bool server, String &errorStr)
             s.ignoreLoadedForReady = stateIgnoreLoadedForReady[j];
 
             if (stateEmitterNode[j] && stateEmitterNode[j][0])
-               s.emitterNode[i] = mShape[i]->findNode(stateEmitterNode[j]);
+               s.emitterNode[i] = getShape(i)->findNode(stateEmitterNode[j]);
             if (s.emitterNode[i] == -1)
                s.emitterNode[i] = muzzleNode[i];
          }
 
-         ambientSequence[i] = mShape[i]->findSequence("ambient");
-         spinSequence[i] = mShape[i]->findSequence("spin");
+         ambientSequence[i]   = getShape(i)->findSequence("ambient");
+         spinSequence[i]      = getShape(i)->findSequence("spin");
 
          shapeIsValid[i] = true;
       }
@@ -567,7 +563,7 @@ bool ShapeBaseImageData::preload(bool server, String &errorStr)
    {
       if( shapeIsValid[i] )
       {
-         TSShapeInstance* pDummy = new TSShapeInstance(mShape[i], !server);
+         TSShapeInstance* pDummy = new TSShapeInstance(getShape(i), !server);
          delete pDummy;
       }
    }
@@ -628,8 +624,8 @@ void ShapeBaseImageData::initPersistFields()
 {
    docsURL;
    addGroup("Shapes");
-      INITPERSISTFIELD_SHAPEASSET_ARRAY(Shape, MaxShapes, ShapeBaseImageData, "The shape asset to use for this image in the third person")
-   //addProtectedField("shapeFileFP", TypeShapeFilename, Offset(mShapeName[1], ShapeBaseImageData), _setShapeData, defaultProtectedGetFn, "deprecated alias for ShapeFPFile/Asset", AbstractClassRep::FIELD_HideInInspectors);
+   INITPERSISTFIELD_SHAPEASSET_ARRAY_REFACTOR(Shape, MaxShapes, ShapeBaseImageData, "The shape assets for this shape image")
+
       addField("casing", TYPEID< DebrisData >(), Offset(casing, ShapeBaseImageData),
             "@brief DebrisData datablock to use for ejected casings.\n\n"
             "@see stateEjectShell");
@@ -1002,10 +998,7 @@ void ShapeBaseImageData::packData(BitStream* stream)
       }
    }
 
-   for (U32 j = 0; j < MaxShapes; ++j)
-   {
-      PACKDATA_ASSET_ARRAY(Shape, j);        // shape 0 for normal use, shape 1 for first person use (optional)
-   }
+   PACKDATA_ASSET_ARRAY_REFACTOR(Shape, MaxShapes);        // shape 0 for normal use, shape 1 for first person use (optional)
 
    stream->writeString(imageAnimPrefix);
    stream->writeString(imageAnimPrefixFP);
@@ -1186,10 +1179,7 @@ void ShapeBaseImageData::unpackData(BitStream* stream)
       }
    }
 
-   for (U32 j = 0; j < MaxShapes; ++j)
-   {
-      UNPACKDATA_ASSET_ARRAY(Shape, j);        // shape 0 for normal use, shape 1 for first person use (optional)
-   }
+   UNPACKDATA_ASSET_ARRAY_REFACTOR(Shape, MaxShapes);        // shape 0 for normal use, shape 1 for first person use (optional)
 
    imageAnimPrefix = stream->readSTString();
    imageAnimPrefixFP = stream->readSTString();
@@ -2148,7 +2138,7 @@ S32 ShapeBase::getNodeIndex(U32 imageSlot,StringTableEntry nodeName)
 {
    MountedImage& image = mMountedImageList[imageSlot];
    if (image.dataBlock)
-      return image.dataBlock->mShape[getImageShapeIndex(image)]->findNode(nodeName);
+      return image.dataBlock->getShape(getImageShapeIndex(image))->findNode(nodeName);
    else
       return -1;
 }
@@ -2338,7 +2328,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->mShape[i], isClientObject());
+         image.shapeInstance[i] = new TSShapeInstance(image.dataBlock->getShape(i), isClientObject());
    }
 
    if (isClientObject())

+ 54 - 61
Engine/source/T3D/tsStatic.cpp

@@ -151,13 +151,14 @@ TSStatic::TSStatic()
    mAnimOffset = 0.0f;
    mAnimSpeed = 1.0f;
 
-   INIT_ASSET(Shape);
+   mShapeAsset.registerRefreshNotify(this);
 }
 
 TSStatic::~TSStatic()
 {
    delete mConvexList;
    mConvexList = NULL;
+   mShapeAsset.unregisterRefreshNotify();
 }
 
 ImplementEnumType(TSMeshType,
@@ -180,11 +181,7 @@ void TSStatic::initPersistFields()
    docsURL;
    addGroup("Shape");
 
-   INITPERSISTFIELD_SHAPEASSET(Shape, TSStatic, "Model to use for this TSStatic");
-
-   addProtectedField("shapeName", TypeShapeFilename, Offset(mShapeName, TSStatic),
-      &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);
+   INITPERSISTFIELD_SHAPEASSET_REFACTOR(Shape, TSStatic, "Model to use for this TSStatic");
 
    endGroup("Shape");
 
@@ -393,59 +390,55 @@ bool TSStatic::_createShape()
    mAmbientThread = NULL;
    //mShape = NULL;
 
-   U32 assetStatus = ShapeAsset::getAssetErrCode(mShapeAsset);
-   if (assetStatus == AssetBase::Ok || assetStatus == AssetBase::UsingFallback)
+   if (mShapeAsset.notNull())
    {
-      //Special-case handling, usually because we set noShape
-      mShape = mShapeAsset->getShapeResource();
-   }
-
-   if (!mShape)
-   {
-      Con::errorf("TSStatic::_createShape() - Shape Asset %s had no valid shape!", mShapeAsset.getAssetId());
-      return false;
-   }
+      if (!getShape())
+      {
+         Con::errorf("TSStatic::_createShape() - Shape Asset %s had no valid shape!", mShapeAsset.getAssetId());
+         return false;
+      }
 
-   if (isClientObject() &&
-      !mShape->preloadMaterialList(mShape.getPath()) &&
-      NetConnection::filesWereDownloaded())
-      return false;
+      if (isClientObject() &&
+         !getShape()->preloadMaterialList(getShape().getPath()) &&
+         NetConnection::filesWereDownloaded())
+         return false;
 
-   mObjBox = mShape->mBounds;
-   resetWorldBox();
+      mObjBox = getShape()->mBounds;
+      resetWorldBox();
 
-   mShapeInstance = new TSShapeInstance(mShape, isClientObject());
-   mShapeInstance->resetMaterialList();
-   mShapeInstance->cloneMaterialList();
+      mShapeInstance = new TSShapeInstance(getShape(), isClientObject());
+      mShapeInstance->resetMaterialList();
+      mShapeInstance->cloneMaterialList();
 
-   if (isGhost())
-   {
-      // Reapply the current skin
-      mAppliedSkinName = "";
-      reSkin();
+      if (isGhost())
+      {
+         // Reapply the current skin
+         mAppliedSkinName = "";
+         reSkin();
 
-      updateMaterials();
-   }
+         updateMaterials();
+      }
 
-   prepCollision();
+      prepCollision();
 
-   // Find the "ambient" animation if it exists
-   S32 ambientSeq = mShape->findSequence("ambient");
+      // Find the "ambient" animation if it exists
+      S32 ambientSeq = getShape()->findSequence("ambient");
 
-   if (ambientSeq > -1 && !mAmbientThread)
-      mAmbientThread = mShapeInstance->addThread();
+      if (ambientSeq > -1 && !mAmbientThread)
+         mAmbientThread = mShapeInstance->addThread();
 
-   if ( mAmbientThread )
-      mShapeInstance->setSequence(mAmbientThread, ambientSeq, mAnimOffset);
+      if ( mAmbientThread )
+         mShapeInstance->setSequence(mAmbientThread, ambientSeq, mAnimOffset);
 
-   // Resolve CubeReflectorDesc.
-   if (cubeDescName.isNotEmpty())
-   {
-      Sim::findObject(cubeDescName, reflectorDesc);
-   }
-   else if (cubeDescId > 0)
-   {
-      Sim::findObject(cubeDescId, reflectorDesc);
+      // Resolve CubeReflectorDesc.
+      if (cubeDescName.isNotEmpty())
+      {
+         Sim::findObject(cubeDescName, reflectorDesc);
+      }
+      else if (cubeDescId > 0)
+      {
+         Sim::findObject(cubeDescId, reflectorDesc);
+      }
    }
 
    //Set up the material slot vars for easy manipulation
@@ -533,20 +526,20 @@ void TSStatic::prepCollision()
 
    if (mCollisionType == CollisionMesh || mCollisionType == VisibleMesh)
    {
-      mShape->findColDetails(mCollisionType == VisibleMesh, &mCollisionDetails, &mLOSDetails, mCollisionLOD);
+      getShape()->findColDetails(mCollisionType == VisibleMesh, &mCollisionDetails, &mLOSDetails, mCollisionLOD);
       if (mDecalType == mCollisionType)
       {
          mDecalDetailsPtr = &mCollisionDetails;
       }
       else if (mDecalType == CollisionMesh || mDecalType == VisibleMesh)
       {
-         mShape->findColDetails(mDecalType == VisibleMesh, &mDecalDetails, 0, mCollisionLOD);
+         getShape()->findColDetails(mDecalType == VisibleMesh, &mDecalDetails, 0, mCollisionLOD);
          mDecalDetailsPtr = &mDecalDetails;
       }
    }
    else if (mDecalType == CollisionMesh || mDecalType == VisibleMesh)
    {
-      mShape->findColDetails(mDecalType == VisibleMesh, &mDecalDetails, 0, mCollisionLOD);
+      getShape()->findColDetails(mDecalType == VisibleMesh, &mDecalDetails, 0, mCollisionLOD);
       mDecalDetailsPtr = &mDecalDetails;
    }
 
@@ -564,12 +557,12 @@ void TSStatic::_updatePhysics()
    if (mCollisionType == Bounds)
    {
       MatrixF offset(true);
-      offset.setPosition(mShape->center);
+      offset.setPosition(getShape()->center);
       colShape = PHYSICSMGR->createCollision();
       colShape->addBox(getObjBox().getExtents() * 0.5f * mObjScale, offset);
    }
    else
-      colShape = mShape->buildColShape(mCollisionType == VisibleMesh, getScale());
+      colShape = getShape()->buildColShape(mCollisionType == VisibleMesh, getScale());
 
    if (colShape)
    {
@@ -958,7 +951,7 @@ U32 TSStatic::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
 
    if (stream->writeFlag(mask & AdvancedStaticOptionsMask))
    {
-      PACK_ASSET(con, Shape);
+      PACK_ASSET_REFACTOR(con, Shape);
 
       stream->write((U32)mDecalType);
 
@@ -1075,7 +1068,7 @@ void TSStatic::unpackUpdate(NetConnection* con, BitStream* stream)
 
    if (stream->readFlag()) // AdvancedStaticOptionsMask
    {
-      UNPACK_ASSET(con, Shape);
+      UNPACK_ASSET_REFACTOR(con, Shape);
 
       stream->read((U32*)&mDecalType);
 
@@ -1597,9 +1590,9 @@ void TSStatic::updateMaterials()
 
    String path;
    if (mShapeAsset->isAssetValid())
-      path = mShapeAsset->getShapeFileName();
+      path = mShapeAsset->getShapeFile();
    else
-      path = mShapeName;
+      path = mShapeFile;
 
    pMatList->setTextureLookupPath(path);
 
@@ -1781,7 +1774,7 @@ DefineEngineMethod(TSStatic, changeMaterial, void, (const char* mapTo, Material*
       return;
    }
 
-   TSMaterialList* shapeMaterialList = object->getShapeResource()->materialList;
+   TSMaterialList* shapeMaterialList = object->getShape()->materialList;
 
    // Check the mapTo name exists for this shape
    S32 matIndex = shapeMaterialList->getMaterialNameList().find_next(String(mapTo));
@@ -1821,7 +1814,7 @@ DefineEngineMethod(TSStatic, getModelFile, const char*, (), ,
    "@endtsexample\n"
 )
 {
-   return object->getShape();
+   return object->getShapeFile();
 }
 
 void TSStatic::set_special_typing()
@@ -1866,14 +1859,14 @@ void TSStatic::setSelectionFlags(U8 flags)
 bool TSStatic::hasNode(const char* nodeName)
 {
 
-   S32 nodeIDx = getShapeResource()->findNode(nodeName);
+   S32 nodeIDx = getShape()->findNode(nodeName);
    return nodeIDx >= 0;
 }
 
 void TSStatic::getNodeTransform(const char *nodeName, const MatrixF &xfm, MatrixF *outMat)
 {
 
-    S32 nodeIDx = getShapeResource()->findNode(nodeName);
+    S32 nodeIDx = getShape()->findNode(nodeName);
 
     MatrixF nodeTransform(xfm);
     const Point3F& scale = getScale();

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

@@ -101,7 +101,7 @@ public:
 
 
 /// A simple mesh shape with optional ambient animation.
-class TSStatic : public SceneObject
+class TSStatic : public SceneObject, protected AssetPtrCallback
 {
    typedef SceneObject Parent;
 
@@ -186,12 +186,17 @@ protected:
    ReflectorDesc* reflectorDesc;
    CubeReflector mCubeReflector;
 
+   void onAssetRefreshed(AssetPtrBase* pAssetPtrBase) override
+   {
+      _createShape();
+      _updateShouldTick();
+   }
+
 protected:
 
    Convex* mConvexList;
 
-   DECLARE_SHAPEASSET(TSStatic, Shape, onShapeChanged);
-   DECLARE_ASSET_NET_SETGET(TSStatic, Shape, AdvancedStaticOptionsMask);
+   DECLARE_SHAPEASSET_NET_REFACTOR(TSStatic, Shape, AdvancedStaticOptionsMask)
 
    U32               mShapeHash;
    Vector<S32> mCollisionDetails;
@@ -239,7 +244,7 @@ public:
    DECLARE_CATEGORY("Object \t Simple");
    static void initPersistFields();
    /// returns the shape asset used for this object
-   StringTableEntry getTypeHint() const override { return (getShapeAsset()) ? getShapeAsset()->getAssetName(): StringTable->EmptyString(); }
+   StringTableEntry getTypeHint() const override { return (mShapeAsset.notNull()) ? mShapeAsset->getAssetName(): StringTable->EmptyString(); }
    static void consoleInit();
    static bool _setFieldSkin(void* object, const char* index, const char* data);
    static const char* _getFieldSkin(void* object, const char* data);

+ 3 - 3
Engine/source/T3D/turret/aiTurretShape.cpp

@@ -246,8 +246,8 @@ bool AITurretShapeData::preload(bool server, String &errorStr)
       return false;
 
    // We have mShape at this point.  Resolve nodes.
-   scanNode = mShape->findNode("scanPoint");
-   aimNode = mShape->findNode("aimPoint");
+   scanNode = getShape()->findNode("scanPoint");
+   aimNode = getShape()->findNode("aimPoint");
 
    if (scanNode == -1) scanNode = pitchNode;
    if (scanNode == -1) scanNode = headingNode;
@@ -259,7 +259,7 @@ bool AITurretShapeData::preload(bool server, String &errorStr)
    for (U32 j = 0; j < MaxStates; j++) {
       StateData& s = state[j];
       if (stateSequence[j] && stateSequence[j][0])
-         s.sequence = mShape->findSequence(stateSequence[j]);
+         s.sequence = getShape()->findSequence(stateSequence[j]);
       if (s.sequence != -1)
       {
          // This state has an animation sequence

+ 10 - 10
Engine/source/T3D/turret/turretShape.cpp

@@ -217,35 +217,35 @@ bool TurretShapeData::preload(bool server, String &errorStr)
       return false;
 
    // We have mShape at this point.  Resolve nodes.
-   headingNode = mShape->findNode("heading");
-   pitchNode = mShape->findNode("pitch");
+   headingNode = getShape()->findNode("heading");
+   pitchNode = getShape()->findNode("pitch");
 
    // Find any mirror pitch nodes
    for (U32 i = 0; i < NumMirrorDirectionNodes; ++i)
    {
       char name[32];
       dSprintf(name, 31, "pitch%d", i+1);
-      pitchNodes[i] = mShape->findNode(name);
+      pitchNodes[i] = getShape()->findNode(name);
 
       dSprintf(name, 31, "heading%d", i+1);
-      headingNodes[i] = mShape->findNode(name);
+      headingNodes[i] = getShape()->findNode(name);
    }
 
    // Resolve weapon mount point node indexes
    for (U32 i = 0; i < ShapeBase::MaxMountedImages; i++) {
       char fullName[256];
       dSprintf(fullName,sizeof(fullName),"weaponMount%d",i);
-      weaponMountNode[i] = mShape->findNode(fullName);
+      weaponMountNode[i] = getShape()->findNode(fullName);
    }
 
    // Recoil animations
-   recoilSequence[0] = mShape->findSequence("light_recoil");
-   recoilSequence[1] = mShape->findSequence("medium_recoil");
-   recoilSequence[2] = mShape->findSequence("heavy_recoil");
+   recoilSequence[0] = getShape()->findSequence("light_recoil");
+   recoilSequence[1] = getShape()->findSequence("medium_recoil");
+   recoilSequence[2] = getShape()->findSequence("heavy_recoil");
 
    // Optional sequences used when the turret rotates
-   pitchSequence = mShape->findSequence("pitch");
-   headingSequence = mShape->findSequence("heading");
+   pitchSequence = getShape()->findSequence("pitch");
+   headingSequence = getShape()->findSequence("heading");
 
    return true;
 }

+ 2 - 2
Engine/source/T3D/vehicles/flyingVehicle.cpp

@@ -135,7 +135,7 @@ bool FlyingVehicleData::preload(bool server, String &errorStr)
    if (!Parent::preload(server, errorStr))
       return false;
 
-   TSShapeInstance* si = new TSShapeInstance(mShape, false);
+   TSShapeInstance* si = new TSShapeInstance(getShape(), false);
 
    // Resolve objects transmitted from server
    if (!server) {
@@ -164,7 +164,7 @@ bool FlyingVehicleData::preload(bool server, String &errorStr)
 
    // Resolve jet nodes
    for (S32 j = 0; j < MaxJetNodes; j++)
-      jetNode[j] = mShape->findNode(sJetNode[j]);
+      jetNode[j] = getShape()->findNode(sJetNode[j]);
 
    //
    maxSpeed = maneuveringForce / minDrag;

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

@@ -332,7 +332,7 @@ bool HoverVehicleData::preload(bool server, String &errorStr)
    }
    // Resolve jet nodes
    for (S32 j = 0; j < MaxJetNodes; j++)
-      jetNode[j] = mShape->findNode(sJetNode[j]);
+      jetNode[j] = getShape()->findNode(sJetNode[j]);
 
    return true;
 }

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

@@ -163,7 +163,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 asset \"%s\"", mShapeAsset.getAssetId());
+      errorStr = String::ToString("VehicleData: Couldn't load shape asset \"%s\"", getShapeAsset().getAssetId());
       return false;
    }
 

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

@@ -75,8 +75,6 @@ ConsoleDocClass( WheeledVehicleTire,
 
 WheeledVehicleTire::WheeledVehicleTire()
 {
-   INIT_ASSET(Shape);
-
    staticFriction = 1;
    kineticFriction = 0.5f;
    restitution = 1;
@@ -88,15 +86,16 @@ WheeledVehicleTire::WheeledVehicleTire()
    longitudinalDamping = 1;
    longitudinalRelaxation = 1;
    mass = 1.f;
+   mShapeAsset.registerRefreshNotify(this);
 }
 
 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 (!mShape)
+   if (!getShape())
    {
-      errorStr = String::ToString("WheeledVehicleTire: Couldn't load shape \"%s\"", mShapeAssetId);
+      errorStr = String::ToString("WheeledVehicleTire: Couldn't load shape \"%s\"", _getShapeAssetId());
       return false;
    }
    else
@@ -104,7 +103,7 @@ bool WheeledVehicleTire::preload(bool server, String &errorStr)
       // 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 = mShape->mBounds.len_z() / 2;
+      radius = getShape()->mBounds.len_z() / 2;
    }
 
    return true;
@@ -113,7 +112,7 @@ bool WheeledVehicleTire::preload(bool server, String &errorStr)
 void WheeledVehicleTire::initPersistFields()
 {
    docsURL;
-   INITPERSISTFIELD_SHAPEASSET(Shape, WheeledVehicleTire, "The shape to use for the wheel.");
+   INITPERSISTFIELD_SHAPEASSET_REFACTOR(Shape, WheeledVehicleTire, "The shape to use for the wheel.");
 
    addFieldV( "mass", TypeRangedF32, Offset(mass, WheeledVehicleTire), &CommonValidators::PositiveFloat,
       "The mass of the wheel.\nCurrently unused." );
@@ -178,7 +177,7 @@ void WheeledVehicleTire::packData(BitStream* stream)
 {
    Parent::packData(stream);
 
-   PACKDATA_ASSET(Shape);
+   PACKDATA_ASSET_REFACTOR(Shape);
 
    stream->write(mass);
    stream->write(staticFriction);
@@ -197,7 +196,7 @@ void WheeledVehicleTire::unpackData(BitStream* stream)
 {
    Parent::unpackData(stream);
 
-   UNPACKDATA_ASSET(Shape);
+   UNPACKDATA_ASSET_REFACTOR(Shape);
 
    stream->read(&mass);
    stream->read(&staticFriction);
@@ -343,7 +342,7 @@ bool WheeledVehicleData::preload(bool server, String &errorStr)
 
    // A temporary shape instance is created so that we can
    // animate the shape and extract wheel information.
-   TSShapeInstance* si = new TSShapeInstance(mShape, false);
+   TSShapeInstance* si = new TSShapeInstance(getShape(), false);
 
    // Resolve objects transmitted from server
    if (!server) {
@@ -367,14 +366,14 @@ bool WheeledVehicleData::preload(bool server, String &errorStr)
 
       // The wheel must have a hub node to operate at all.
       dSprintf(buff,sizeof(buff),"hub%d",i);
-      wp->springNode = mShape->findNode(buff);
+      wp->springNode = getShape()->findNode(buff);
       if (wp->springNode != -1) {
 
          // Check for spring animation.. If there is none we just grab
          // the current position of the hub. Otherwise we'll animate
          // and get the position at time 0.
          dSprintf(buff,sizeof(buff),"spring%d",i);
-         wp->springSequence = mShape->findSequence(buff);
+         wp->springSequence = getShape()->findSequence(buff);
          if (wp->springSequence == -1)
             si->mNodeTransforms[wp->springNode].getColumn(3, &wp->pos);
          else {
@@ -403,17 +402,17 @@ bool WheeledVehicleData::preload(bool server, String &errorStr)
    // Check for steering. Should think about normalizing the
    // steering animation the way the suspension is, but I don't
    // think it's as critical.
-   steeringSequence = mShape->findSequence("steering");
+   steeringSequence = getShape()->findSequence("steering");
 
    // Brakes
-   brakeLightSequence = mShape->findSequence("brakelight");
+   brakeLightSequence = getShape()->findSequence("brakelight");
 
    // Extract collision planes from shape collision detail level
    if (collisionDetails[0] != -1) {
       MatrixF imat(1);
       SphereF sphere;
-      sphere.center = mShape->center;
-      sphere.radius = mShape->mRadius;
+      sphere.center = getShape()->center;
+      sphere.radius = getShape()->mRadius;
       PlaneExtractorPolyList polyList;
       polyList.mPlaneList = &rigidBody.mPlaneList;
       polyList.setTransform(&imat, Point3F(1,1,1));
@@ -1579,8 +1578,8 @@ void WheeledVehicle::unpackUpdate(NetConnection *con, BitStream *stream)
 
             // Create an instance of the tire for rendering
             delete wheel->shapeInstance;
-            wheel->shapeInstance = (wheel->tire->mShape == NULL) ? 0:
-               new TSShapeInstance(wheel->tire->mShape);
+            wheel->shapeInstance = (wheel->tire->getShape() == NULL) ? 0:
+               new TSShapeInstance(wheel->tire->getShape());
          }
       }
    }

+ 4 - 4
Engine/source/T3D/vehicles/wheeledVehicle.h

@@ -39,12 +39,11 @@ class ParticleEmitterData;
 
 //----------------------------------------------------------------------------
 
-struct WheeledVehicleTire: public SimDataBlock 
+struct WheeledVehicleTire: public SimDataBlock, protected AssetPtrCallback
 {
    typedef SimDataBlock Parent;
 
-   DECLARE_SHAPEASSET(WheeledVehicleTire, Shape, onShapeChanged);
-   DECLARE_ASSET_SETGET(WheeledVehicleTire, Shape);
+   DECLARE_SHAPEASSET_REFACTOR(WheeledVehicleTire, Shape)
 
    // Physical properties
    F32 mass;                  // Mass of the whole wheel
@@ -74,7 +73,8 @@ struct WheeledVehicleTire: public SimDataBlock
    void packData(BitStream* stream) override;
    void unpackData(BitStream* stream) override;
 
-   void onShapeChanged()
+protected:
+   void onAssetRefreshed(AssetPtrBase* pAssetPtrBase) override
    {
       reloadOnLocalClient();
    }

+ 10 - 7
Engine/source/afx/afxMagicMissile.cpp

@@ -141,7 +141,6 @@ U32 Projectile::smProjectileWarpTicks = 5;
 //
 afxMagicMissileData::afxMagicMissileData()
 {
-   INIT_ASSET(ProjectileShape);
    INIT_ASSET(ProjectileSound);
 
    /* From stock Projectile code...
@@ -241,11 +240,13 @@ afxMagicMissileData::afxMagicMissileData()
   reverse_targeting = false;
 
   caster_safety_time = U32_MAX;
+
+  mProjectileShapeAsset.registerRefreshNotify(this);
 }
 
 afxMagicMissileData::afxMagicMissileData(const afxMagicMissileData& other, bool temp_clone) : GameBaseData(other, temp_clone)
 {
-   CLONE_ASSET(ProjectileShape);
+   mProjectileShapeAsset = other.mProjectileShapeAsset;
   projectileShape = other.projectileShape; // -- TSShape loads using projectileShapeName
   CLONE_ASSET(ProjectileSound);
   splash = other.splash;
@@ -305,6 +306,8 @@ afxMagicMissileData::~afxMagicMissileData()
 {
   if (wiggle_axis)
     delete [] wiggle_axis;
+
+  mProjectileShapeAsset.unregisterRefreshNotify();
 }
 
 afxMagicMissileData* afxMagicMissileData::cloneAndPerformSubstitutions(const SimObject* owner, S32 index)
@@ -334,7 +337,7 @@ void afxMagicMissileData::initPersistFields()
    static IRangeValidatorScaled ticksFromMS(TickMs, 0, MaxLifetimeTicks);
 
    addGroup("Shapes");
-      INITPERSISTFIELD_SHAPEASSET(ProjectileShape, afxMagicMissileData, "Shape for the projectile");
+      INITPERSISTFIELD_SHAPEASSET_REFACTOR(ProjectileShape, afxMagicMissileData, "Shape for the projectile");
       addField("scale", TypePoint3F, Offset(scale, afxMagicMissileData));
       addField("missileShapeScale",   TypePoint3F,  myOffset(scale));
    endGroup("Shapes");
@@ -531,10 +534,10 @@ bool afxMagicMissileData::preload(bool server, String &errorStr)
    U32 assetStatus = ShapeAsset::getAssetErrCode(mProjectileShapeAsset);
    if (assetStatus == AssetBase::Ok || assetStatus == AssetBase::UsingFallback)
    {
-      projectileShape = mProjectileShapeAsset->getShapeResource();
+      projectileShape = getProjectileShape();
       if (bool(projectileShape) == false)
       {
-         errorStr = String::ToString("afxMagicMissileData::preload: Couldn't load shape \"%s\"", mProjectileShapeAssetId);
+         errorStr = String::ToString("afxMagicMissileData::preload: Couldn't load shape \"%s\"", _getProjectileShapeAssetId());
          return false;
       }
       /* From stock Projectile code...
@@ -586,7 +589,7 @@ void afxMagicMissileData::packData(BitStream* stream)
 {
    Parent::packData(stream);
 
-   PACKDATA_ASSET(ProjectileShape);
+   PACKDATA_ASSET_REFACTOR(ProjectileShape);
 
    /* From stock Projectile code...
    stream->writeFlag(faceViewer);
@@ -697,7 +700,7 @@ void afxMagicMissileData::unpackData(BitStream* stream)
 {
    Parent::unpackData(stream);
 
-   UNPACKDATA_ASSET(ProjectileShape);
+   UNPACKDATA_ASSET_REFACTOR(ProjectileShape);
    /* From stock Projectile code...
    faceViewer = stream->readFlag();
    */

+ 8 - 8
Engine/source/afx/afxMagicMissile.h

@@ -56,7 +56,7 @@ class SFXSource;
 //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 // afxMagicMissileData
 
-class afxMagicMissileData : public GameBaseData
+class afxMagicMissileData : public GameBaseData, protected AssetPtrCallback
 {
   typedef GameBaseData Parent;
   
@@ -66,16 +66,10 @@ protected:
 public:
   enum { MaxLifetimeTicks = 4095 };
 
-  void onShapeChanged()
-  {
-     reloadOnLocalClient();
-  }
-  
 public:
    // variables set in datablock definition:
    // Shape related
-   DECLARE_SHAPEASSET(afxMagicMissileData, ProjectileShape, onShapeChanged);
-   DECLARE_ASSET_SETGET(afxMagicMissileData, ProjectileShape);
+   DECLARE_SHAPEASSET_REFACTOR(afxMagicMissileData, ProjectileShape)
   //StringTableEntry      projectileShapeName;
 
   //bool                  hasLight;
@@ -228,6 +222,12 @@ public:
   afxMagicMissileData*  cloneAndPerformSubstitutions(const SimObject*, S32 index=0);
   bool          allowSubstitutions() const override { return true; }
   void                  gather_cons_defs(Vector<afxConstraintDef>& defs);
+
+protected:
+   void onAssetRefreshed(AssetPtrBase* pAssetPtrBase) override
+   {
+      reloadOnLocalClient();
+   }
 };
 
 //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//

+ 26 - 34
Engine/source/afx/ce/afxModel.cpp

@@ -54,7 +54,6 @@ ConsoleDocClass( afxModelData,
 
 afxModelData::afxModelData()
 {
-   INIT_ASSET(Shape);
   sequence = ST_NULLSTRING;
   seq_rate = 1.0f;
   seq_offset = 0.0f;
@@ -79,11 +78,13 @@ afxModelData::afxModelData()
   shadowMaxVisibleDistance = 80.0f;
   shadowProjectionDistance = 10.0f;
   shadowSphereAdjust = 1.0;
+
+  mShapeAsset.registerRefreshNotify(this);
 }
 
 afxModelData::afxModelData(const afxModelData& other, bool temp_clone) : GameBaseData(other, temp_clone)
 {
-   CLONE_ASSET(Shape);
+   mShapeAsset = other.mShapeAsset;
   sequence = other.sequence;
   seq_rate = other.seq_rate;
   seq_offset = other.seq_offset;
@@ -113,6 +114,8 @@ afxModelData::~afxModelData()
 {
    if (remap_buffer)
       dFree(remap_buffer);
+
+   mShapeAsset.unregisterRefreshNotify();
 }
 
 bool afxModelData::preload(bool server, String &errorStr)
@@ -126,9 +129,9 @@ bool afxModelData::preload(bool server, String &errorStr)
   
   if (mShapeAsset.notNull())
   {
-    if (!mShape)
+    if (!getShape())
     {
-      errorStr = String::ToString("afxModelData::load: Failed to load shape \"%s\"", mShapeAssetId);
+      errorStr = String::ToString("afxModelData::load: Failed to load shape \"%s\"", _getShapeAssetId());
       return false;
     }
 
@@ -160,7 +163,7 @@ bool afxModelData::preload(bool server, String &errorStr)
     if (txr_tag_remappings.size() == 0)
     {
       // this little hack forces the textures to preload
-      TSShapeInstance* pDummy = new TSShapeInstance(mShape);
+      TSShapeInstance* pDummy = new TSShapeInstance(getShape());
       delete pDummy;
     }
   }
@@ -174,7 +177,7 @@ void afxModelData::initPersistFields()
 {
    docsURL;
    addGroup("Shapes");
-      INITPERSISTFIELD_SHAPEASSET(Shape, afxModelData, "The name of a .dts format file to use for the model.");
+      INITPERSISTFIELD_SHAPEASSET_REFACTOR(Shape, afxModelData, "The name of a .dts format file to use for the model.");
    endGroup("Shapes");
 
    addGroup("Animation");
@@ -258,7 +261,7 @@ void afxModelData::packData(BitStream* stream)
 {
   Parent::packData(stream);
 
-  PACKDATA_ASSET(Shape);
+  PACKDATA_ASSET_REFACTOR(Shape);
   stream->writeString(sequence);
   stream->write(seq_rate);  
   stream->write(seq_offset);
@@ -289,7 +292,7 @@ void afxModelData::unpackData(BitStream* stream)
 {
   Parent::unpackData(stream);
 
-  UNPACKDATA_ASSET(Shape);
+  UNPACKDATA_ASSET_REFACTOR(Shape);
   sequence = stream->readSTString();
   stream->read(&seq_rate);
   stream->read(&seq_offset);
@@ -318,21 +321,10 @@ void afxModelData::unpackData(BitStream* stream)
 
 void afxModelData::onPerformSubstitutions()
 {
-   if (mShapeAssetId != StringTable->EmptyString())
+   if (!getShape())
    {
-      mShapeAsset = mShapeAssetId;
-      if (mShapeAsset.notNull())
-      {
-         mShape = mShapeAsset->getShapeResource();
-      }
-
-      if (!mShape)
-      {
-         Con::errorf("afxModelData::onPerformSubstitutions: Failed to load shape \"%s\"", mShapeAssetId);
-         return;
-      }
-
-      // REMAP-TEXTURE-TAGS ISSUES?
+      Con::errorf("afxModelData::onPerformSubstitutions: Failed to load shape \"%s\"", _getShapeAssetId());
+      return;
    }
 }
 
@@ -406,18 +398,18 @@ bool afxModel::onAdd()
     return false;
 
   // setup our bounding box
-  if (mDataBlock->mShape)
-    mObjBox = mDataBlock->mShape->mBounds;
+  if (mDataBlock->getShape())
+    mObjBox = mDataBlock->getShape()->mBounds;
   else
     mObjBox = Box3F(Point3F(-1, -1, -1), Point3F(1, 1, 1));
 
   // setup the shape instance and sequence
-  if (mDataBlock->mShape)
+  if (mDataBlock->getShape())
   {
      if (/*isClientObject() && */mDataBlock->txr_tag_remappings.size() > 0)
      {
         // temporarily substitute material tags with alternates
-        TSMaterialList* mat_list = mDataBlock->mShape->materialList;
+        TSMaterialList* mat_list = mDataBlock->getShape()->materialList;
         if (mat_list)
         {
            for (S32 i = 0; i < mDataBlock->txr_tag_remappings.size(); i++)
@@ -438,7 +430,7 @@ bool afxModel::onAdd()
         }
      }
 
-    shape_inst = new TSShapeInstance(mDataBlock->mShape);
+    shape_inst = new TSShapeInstance(mDataBlock->getShape());
 
     if (true) // isClientObject())
     {
@@ -447,7 +439,7 @@ bool afxModel::onAdd()
        // restore the material tags to original form
        if (mDataBlock->txr_tag_remappings.size() > 0)
        {
-          TSMaterialList* mat_list = mDataBlock->mShape->materialList;
+          TSMaterialList* mat_list = mDataBlock->getShape()->materialList;
           if (mat_list)
           {
              for (S32 i = 0; i < mDataBlock->txr_tag_remappings.size(); i++)
@@ -513,14 +505,14 @@ bool afxModel::onAdd()
 
   resetWorldBox();
 
-  if (mDataBlock->mShape)
+  if (mDataBlock->getShape())
   {
     // Scan out the collision hulls...
     static const String sCollisionStr( "collision-" );
 
-    for (U32 i = 0; i < mDataBlock->mShape->details.size(); i++)
+    for (U32 i = 0; i < mDataBlock->getShape()->details.size(); i++)
     {
-      const String &name = mDataBlock->mShape->names[mDataBlock->mShape->details[i].nameIndex];
+      const String &name = mDataBlock->getShape()->names[mDataBlock->getShape()->details[i].nameIndex];
 
       if (name.compare( sCollisionStr, sCollisionStr.length(), String::NoCase ) == 0)
       {
@@ -534,7 +526,7 @@ bool afxModel::onAdd()
 
         char buff[128];
         dSprintf(buff, sizeof(buff), "LOS-%d", i + 1 + 8/*MaxCollisionShapes*/);
-        U32 los = mDataBlock->mShape->findDetail(buff);
+        U32 los = mDataBlock->getShape()->findDetail(buff);
         if (los == -1)
           mLOSDetails.last() = i;
         else
@@ -545,9 +537,9 @@ bool afxModel::onAdd()
     // Snag any "unmatched" LOS details
     static const String sLOSStr( "LOS-" );
 
-    for (U32 i = 0; i < mDataBlock->mShape->details.size(); i++)
+    for (U32 i = 0; i < mDataBlock->getShape()->details.size(); i++)
     {
-      const String &name = mDataBlock->mShape->names[mDataBlock->mShape->details[i].nameIndex];
+      const String &name = mDataBlock->getShape()->names[mDataBlock->getShape()->details[i].nameIndex];
 
       if (name.compare( sLOSStr, sLOSStr.length(), String::NoCase ) == 0)
       {

+ 10 - 9
Engine/source/afx/ce/afxModel.h

@@ -39,12 +39,11 @@ class TSShape;
 //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 // afxModel Data
 
-struct afxModelData : public GameBaseData
+struct afxModelData : public GameBaseData, protected AssetPtrCallback
 {
   typedef GameBaseData Parent;
 
-  DECLARE_SHAPEASSET(afxModelData, Shape, onShapeChanged);
-  DECLARE_ASSET_SETGET(afxModelData, Shape);
+  DECLARE_SHAPEASSET_REFACTOR(afxModelData, Shape)
 
   StringTableEntry      sequence;
 
@@ -94,13 +93,15 @@ public:
 
   static void           initPersistFields();
 
-  void onShapeChanged()
-  {
-     reloadOnLocalClient();
-  }
   void onSequenceChanged() {}
 
   DECLARE_CONOBJECT(afxModelData);
+
+protected:
+   void onAssetRefreshed(AssetPtrBase* pAssetPtrBase) override
+   {
+      reloadOnLocalClient();
+   }
 };
 
 //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
@@ -154,9 +155,9 @@ public:
   void                  setSequenceRateFactor(F32 factor);
   void                  setSortPriority(S8 priority) { sort_priority = priority; }
 
-  const char*           getShapeFileName() const { return mDataBlock->getShape(); }
+  const char*           getShapeFileName() const { return mDataBlock->getShapeFile(); }
   void                  setVisibility(bool flag) { is_visible = flag; }
-  TSShape*              getTSShape() { return mDataBlock->getShapeResource(); }
+  TSShape*              getTSShape() { return mDataBlock->getShape(); }
   TSShapeInstance*      getTSShapeInstance() { return shape_inst; }
 
   U32                   setAnimClip(const char* clip, F32 pos, F32 rate, F32 trans);

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

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

+ 19 - 3
Engine/source/assets/assetBase.cpp

@@ -226,9 +226,20 @@ StringTableEntry AssetBase::expandAssetFilePath(const char* pAssetFilePath) cons
       assetBasePathHint = NULL;
    }
 
-   // Expand the path with the asset base-path hint.
    char assetFilePathBuffer[1024];
-   Con::expandPath(assetFilePathBuffer, sizeof(assetFilePathBuffer), pAssetFilePath, assetBasePathHint);
+
+   if (*pAssetFilePath != '@')
+   {
+      // Expand the path with the asset base-path hint.
+      Con::expandPath(assetFilePathBuffer, sizeof(assetFilePathBuffer), pAssetFilePath, assetBasePathHint);
+      return StringTable->insert(assetFilePathBuffer);
+   }
+
+   if(!getOwned())
+      return StringTable->insert(pAssetFilePath);
+
+   // Format expanded path taking into account any missing slash.
+   dSprintf(assetFilePathBuffer, sizeof(assetFilePathBuffer), "%s/%s", mpOwningAssetManager->getAssetPath(getAssetId()), pAssetFilePath + (pAssetFilePath[1] == '/' ? 2 : 1));
    return StringTable->insert(assetFilePathBuffer);
 }
 
@@ -254,6 +265,11 @@ StringTableEntry AssetBase::collapseAssetFilePath(const char* pAssetFilePath) co
 
    char assetFilePathBuffer[1024];
 
+   if (*pAssetFilePath == '@')
+   {
+      return StringTable->insert(pAssetFilePath);
+   }
+
    // Is the asset not owned or private?
    if (!getOwned() || getAssetPrivate())
    {
@@ -272,7 +288,7 @@ StringTableEntry AssetBase::collapseAssetFilePath(const char* pAssetFilePath) co
       StringTableEntry relativePath = Platform::makeRelativePathName(pAssetFilePath, assetBasePath);
 
       // Format the collapsed path.
-      dSprintf(assetFilePathBuffer, sizeof(assetFilePathBuffer), "%s", relativePath);
+      dSprintf(assetFilePathBuffer, sizeof(assetFilePathBuffer), "@%s", relativePath);
    }
    else
    {

+ 17 - 0
Engine/source/console/console.cpp

@@ -2226,6 +2226,23 @@ bool stripRepeatSlashes(char* pDstPath, const char* pSrcPath, S32 dstSize)
 
 //-----------------------------------------------------------------------------
 
+DefineEngineFunction(expandPath, const char*, (const char* path),, "(string path) - Expands an expando or relative path into a full path.")
+{
+   char* ret = Con::getReturnBuffer(1024);
+   Con::expandPath(ret, 1024, path);
+   return ret;
+}
+
+//-----------------------------------------------------------------------------
+
+DefineEngineFunction(collapsePath, const char*, (const char* path), , "(string path) - Collapses a path into either an expando path or a relative path.")
+{
+   char* ret = Con::getReturnBuffer(1024);
+   Con::collapsePath(ret, 1024, path);
+   return ret;
+}
+
+
 DefineEngineFunction( log, void, ( const char* message ),,
    "@brief Logs a message to the console.\n\n"
    "@param message The message text.\n"

+ 15 - 17
Engine/source/environment/VolumetricFog.cpp

@@ -135,8 +135,6 @@ VolumetricFog::VolumetricFog()
    mTexTiles = 1.0f;
    mSpeed1.set(0.5f, 0.0f);
    mSpeed2.set(0.1f, 0.1f);
-
-   INIT_ASSET(Shape);
 }
 
 VolumetricFog::~VolumetricFog()
@@ -164,7 +162,7 @@ void VolumetricFog::initPersistFields()
    docsURL;
    Parent::initPersistFields();
    addGroup("Shapes");
-      INITPERSISTFIELD_SHAPEASSET(Shape, VolumetricFog, "The source shape asset.");
+      INITPERSISTFIELD_SHAPEASSET_REFACTOR(Shape, VolumetricFog, "The source shape asset.");
    endGroup("Shapes");
 
    addGroup("VolumetricFogData");
@@ -342,7 +340,7 @@ void VolumetricFog::handleResize(VolumetricFogRTManager *RTM, bool resize)
 
 bool VolumetricFog::setShapeAsset(const StringTableEntry shapeAssetId)
 {
-   mShapeAssetId = shapeAssetId;
+   _setShape(shapeAssetId);
 
    LoadShape();
    return true;
@@ -358,20 +356,20 @@ bool VolumetricFog::LoadShape()
       return false;
    }
 
-   if (!mShape)
+   if (!getShape())
    {
       Con::errorf("VolumetricFog::_createShape() - Shape Asset had no valid shape!");
       return false;
    }
 
-   mObjBox = mShape->mBounds;
-   mRadius = mShape->mRadius;
+   mObjBox = getShape()->mBounds;
+   mRadius = getShape()->mRadius;
    resetWorldBox();
 
    if (!isClientObject())
       return false;
 
-   TSShapeInstance *mShapeInstance = new TSShapeInstance(mShape, false);
+   TSShapeInstance *mShapeInstance = new TSShapeInstance(getShape(), false);
    meshes mesh_detail;
 
    for (S32 i = 0; i < det_size.size(); i++)
@@ -387,9 +385,9 @@ bool VolumetricFog::LoadShape()
 
    // browsing model for detail levels
 
-   for (U32 i = 0; i < mShape->details.size(); i++)
+   for (U32 i = 0; i < getShape()->details.size(); i++)
    {
-      const TSDetail *detail = &mShape->details[i];
+      const TSDetail *detail = &getShape()->details[i];
       mesh_detail.det_size = detail->size;
       mesh_detail.sub_shape = detail->subShapeNum;
       mesh_detail.obj_det = detail->objectDetailNum;
@@ -405,8 +403,8 @@ bool VolumetricFog::LoadShape()
       const S32 ss = det_size[i].sub_shape;
       if (ss >= 0)
       {
-         const S32 start = mShape->subShapeFirstObject[ss];
-         const S32 end = start + mShape->subShapeNumObjects[ss];
+         const S32 start = getShape()->subShapeFirstObject[ss];
+         const S32 end = start + getShape()->subShapeNumObjects[ss];
          for (S32 j = start; j < end; j++)
          {
             // Loading shape, only the first mesh for each detail will be used!
@@ -568,7 +566,7 @@ U32 VolumetricFog::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
    }
    if (stream->writeFlag(mask & FogShapeMask))
    {
-      PACK_ASSET(con, Shape);
+      PACK_ASSET_REFACTOR(con, Shape);
       mathWrite(*stream, getTransform());
       mathWrite(*stream, getScale());
 
@@ -597,8 +595,8 @@ void VolumetricFog::unpackUpdate(NetConnection *con, BitStream *stream)
    VectorF scale;
    VectorF mOldScale = getScale();
    StringTableEntry oldTextureName = mTextureAsset.getAssetId();
-   StringTableEntry oldShapeAsset = mShapeAssetId;
-   StringTableEntry oldShape = mShapeName;
+   StringTableEntry oldShapeAsset = _getShapeAssetId();
+   StringTableEntry oldShape = getShapeFile();
 
    if (stream->readFlag())// Fog color
       stream->read(&mFogColor);
@@ -667,11 +665,11 @@ void VolumetricFog::unpackUpdate(NetConnection *con, BitStream *stream)
    }
    if (stream->readFlag())//Fog shape
    {
-      UNPACK_ASSET(con, Shape);
+      UNPACK_ASSET_REFACTOR(con, Shape);
 
       mathRead(*stream, &mat);
       mathRead(*stream, &scale);
-      if (strcmp(oldShapeAsset, mShapeAssetId) != 0 || strcmp(oldShape, mShapeName) != 0)
+      if (strcmp(oldShapeAsset, _getShapeAssetId()) != 0 || strcmp(oldShape, getShapeFile()) != 0)
       {
          mIsVBDirty = true;
          mShapeLoaded = LoadShape();

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

@@ -84,8 +84,7 @@ class VolumetricFog : public SceneObject
       Vector <U32> *indices;
    };
 
-   DECLARE_SHAPEASSET(VolumetricFog, Shape, onShapeChanged);
-   DECLARE_ASSET_NET_SETGET(VolumetricFog, Shape, FogShapeMask);
+   DECLARE_SHAPEASSET_REFACTOR(VolumetricFog, Shape)
    
    protected:
       // Rendertargets;
@@ -203,6 +202,7 @@ class VolumetricFog : public SceneObject
       void ResizeRT(PlatformWindow *win, bool resize);
    
    protected:
+
       // Protected methods
       bool onAdd() override;
       void onRemove() override;
@@ -246,8 +246,6 @@ class VolumetricFog : public SceneObject
       bool isInsideFog();
 
       bool setShapeAsset(const StringTableEntry shapeAssetId);
-
-      void onShapeChanged() {}
    
       DECLARE_CONOBJECT(VolumetricFog);
       DECLARE_CATEGORY("Environment \t Weather");

+ 6 - 7
Engine/source/forest/forestItem.cpp

@@ -53,7 +53,8 @@ ForestItemData::ForestItemData()
       mTightnessCoefficient( 0.4f ),
       mDampingCoefficient( 0.7f )      
 {
-   INIT_ASSET(Shape);
+   mShape = NULL;
+   mShapeAsset.registerRefreshNotify(this);
 }
 
 void ForestItemData::initPersistFields()
@@ -61,10 +62,8 @@ void ForestItemData::initPersistFields()
    docsURL;
    addGroup( "Shapes" );
 
-      INITPERSISTFIELD_SHAPEASSET(Shape, ForestItemData, "Shape asset for this item type");
-      
-      addProtectedField( "shapeFile",  TypeShapeFilename, Offset( mShapeName, ForestItemData ), &_setShapeData, &defaultProtectedGetFn,
-         "Shape file for this item type", AbstractClassRep::FIELD_HideInInspectors );
+      INITPERSISTFIELD_SHAPEASSET_REFACTOR(Shape, ForestItemData, "Shape asset for this item type");
+
    endGroup( "Shapes" );
 
    addGroup("Physics");
@@ -164,7 +163,7 @@ void ForestItemData::packData(BitStream* stream)
 
    stream->write( localName );
 
-   PACKDATA_ASSET(Shape);
+   PACKDATA_ASSET_REFACTOR(Shape);
    
    stream->writeFlag( mCollidable );
 
@@ -190,7 +189,7 @@ void ForestItemData::unpackData(BitStream* stream)
    stream->read( &localName );
    setInternalName( localName );
 
-   UNPACKDATA_ASSET(Shape);
+   UNPACKDATA_ASSET_REFACTOR(Shape);
    
    mCollidable = stream->readFlag();
 

+ 6 - 4
Engine/source/forest/forestItem.h

@@ -48,7 +48,7 @@ struct RayInfo;
 class AbstractPolyList;
 
 
-class ForestItemData : public SimDataBlock
+class ForestItemData : public SimDataBlock, protected AssetPtrCallback
 {
 protected:
 
@@ -62,8 +62,7 @@ protected:
 
 public:
    
-   DECLARE_SHAPEASSET(ForestItemData, Shape, onShapeChanged);
-   DECLARE_ASSET_SETGET(ForestItemData, Shape);
+   DECLARE_SHAPEASSET_REFACTOR(ForestItemData, Shape)
 
    /// This is the radius used during placement to ensure
    /// the element isn't crowded up against other trees.
@@ -144,7 +143,10 @@ public:
       return theSignal;
    }
 
-   void onShapeChanged()
+   Resource<TSShape> mShape;
+
+protected:
+   void onAssetRefreshed(AssetPtrBase* pAssetPtrBase) override
    {
       reloadOnLocalClient();
    }

+ 7 - 7
Engine/source/forest/ts/tsForestItemData.cpp

@@ -99,13 +99,13 @@ void TSForestItemData::inspectPostApply()
 
 void TSForestItemData::_onResourceChanged( const Torque::Path &path )
 {
-   U32 assetStatus = ShapeAsset::getAssetErrCode(mShapeAsset);
+   U32 assetStatus = ShapeAsset::getAssetErrCode(_getShapeAssetId());
    if (assetStatus != AssetBase::Ok && assetStatus != AssetBase::UsingFallback)
    {
       return;
    }
 
-   if ( path != Path(mShapeAsset->getShapeFilePath()) )
+   if ( path != Path(getShapeFile()) )
       return;
    
    SAFE_DELETE( mShapeInstance );
@@ -116,18 +116,18 @@ void TSForestItemData::_onResourceChanged( const Torque::Path &path )
 
 void TSForestItemData::_loadShape()
 {
-   U32 assetStatus = ShapeAsset::getAssetErrCode(mShapeAsset);
+   mShape = getShape();
+   U32 assetStatus = ShapeAsset::getAssetErrCode(_getShapeAssetId());
    if (assetStatus != AssetBase::Ok && assetStatus != AssetBase::UsingFallback)
    {
       return;
    }
-   _setShape(mShapeAssetId);
 
    if ( !(bool)mShape )
       return;
 
    if ( mIsClientObject && 
-       !mShape->preloadMaterialList(mShapeAsset->getShapeFilePath()) )
+       !mShape->preloadMaterialList(mShape.getPath()) )
       return;
    
    // Lets add an autobillboard detail if don't have one.
@@ -165,7 +165,7 @@ TSShapeInstance* TSForestItemData::_getShapeInstance() const
 
 void TSForestItemData::_checkLastDetail()
 {
-   U32 assetStatus = ShapeAsset::getAssetErrCode(mShapeAsset);
+   U32 assetStatus = ShapeAsset::getAssetErrCode(_getShapeAssetId());
    if (assetStatus != AssetBase::Ok && assetStatus != AssetBase::UsingFallback)
    {
       return;
@@ -177,7 +177,7 @@ void TSForestItemData::_checkLastDetail()
    // TODO: Expose some real parameters to the datablock maybe?
    if ( detail->subShapeNum != -1 )
    {
-      mShape->addImposter(mShapeAsset->getShapeFilePath(), 10, 4, 0, 0, 256, 0, 0 );
+      mShape->addImposter(mShape.getPath(), 10, 4, 0, 0, 256, 0, 0);
 
       // HACK: If i don't do this it crashes!
       while ( mShape->detailCollisionAccelerators.size() < mShape->details.size() )

+ 6 - 0
Engine/source/gui/buttons/guiBitmapButtonCtrl.h

@@ -123,6 +123,12 @@ private:
    String               mBitmapFile; 
 public:
    void _setBitmap(StringTableEntry _in) {
+      if (_in == NULL || _in == StringTable->EmptyString() || _in == "")
+      {
+         mBitmapAsset = NULL;
+         mBitmapFile = "";
+         return;
+      }
    if (mBitmapAsset.getAssetId() == _in) return; if (!AssetDatabase.isDeclaredAsset(_in)) {
       StringTableEntry imageAssetId = ImageAsset::smNoImageAssetFallback; AssetQuery query; S32 foundAssetcount = AssetDatabase.findAssetLooseFile(&query, _in); if (foundAssetcount != 0) {
          imageAssetId = query.mAssetList[0];

+ 10 - 7
Engine/source/gui/controls/guiBitmapCtrl.cpp

@@ -121,16 +121,19 @@ void GuiBitmapCtrl::setBitmap(const char* name, bool resize)
       if (assetId != StringTable->EmptyString())
          _setBitmap(assetId);
       else
-         _setBitmap(name);
+         _setBitmap(StringTable->EmptyString());
    }
 
-   mBitmap = mBitmapAsset->getTexture(&GFXDefaultGUIProfile);
-
-   if (getBitmap() && resize)
+   if (mBitmapAsset.notNull())
    {
-     
-      setExtent(mBitmap->getWidth(), mBitmap->getHeight());
-      updateSizing();
+      mBitmap = mBitmapAsset->getTexture(&GFXDefaultGUIProfile);
+
+      if (getBitmap() && resize)
+      {
+
+         setExtent(mBitmap->getWidth(), mBitmap->getHeight());
+         updateSizing();
+      }
    }
 
    setUpdate();

+ 9 - 0
Engine/source/gui/core/guiCanvas.cpp

@@ -690,6 +690,15 @@ bool GuiCanvas::processInputEvent(InputEventInfo &inputEvent)
    mConsumeLastInputEvent = true;
    mLastInputDeviceType = inputEvent.deviceType;
 
+   // If we have an active offscreen canvas, give it the input
+   if (GuiOffscreenCanvas::sActiveOffscreenCanvas &&
+      (GuiOffscreenCanvas::sActiveOffscreenCanvas != this) &&
+      GuiOffscreenCanvas::sActiveOffscreenCanvas->processInputEvent(inputEvent))
+   {
+      GuiOffscreenCanvas::sActiveOffscreenCanvas = NULL;
+      return mConsumeLastInputEvent;
+   }
+
    // First call the general input handler (on the extremely off-chance that it will be handled):
    if (mFirstResponder &&  mFirstResponder->onInputEvent(inputEvent))
    {

+ 3 - 1
Engine/source/gui/core/guiOffscreenCanvas.cpp

@@ -9,6 +9,7 @@
 
 IMPLEMENT_CONOBJECT(GuiOffscreenCanvas);
 
+GuiOffscreenCanvas* GuiOffscreenCanvas::sActiveOffscreenCanvas = NULL;
 Vector<GuiOffscreenCanvas*> GuiOffscreenCanvas::sList;
 
 GuiOffscreenCanvas::GuiOffscreenCanvas()
@@ -33,7 +34,8 @@ void GuiOffscreenCanvas::initPersistFields()
    addField( "targetName", TypeRealString, Offset( mTargetName, GuiOffscreenCanvas ), "");
    addField( "dynamicTarget", TypeBool, Offset( mDynamicTarget, GuiOffscreenCanvas ), "");
    addField( "useDepth", TypeBool, Offset( mUseDepth, GuiOffscreenCanvas ), "");
-
+   addField("canInteract", TypeBool, Offset(mCanInteract, GuiOffscreenCanvas), "");
+   addField("maxInteractDistance", TypeF32, Offset(mMaxInteractDistance, GuiOffscreenCanvas), "");
    Parent::initPersistFields();
 }
 

+ 5 - 0
Engine/source/gui/core/guiOffscreenCanvas.h

@@ -38,6 +38,8 @@ public:
    void _teardownTargets();
 
    NamedTexTargetRef getTarget() { return &mNamedTarget; }
+   bool canInteract() { return mCanInteract; }
+   F32 getMaxInteractDistance() { return mMaxInteractDistance; }
 
    void markDirty() { mTargetDirty = true; }
 
@@ -59,9 +61,12 @@ protected:
    
    bool mUseDepth;
    GFXTexHandle mTargetDepth;
+   bool mCanInteract;
+   F32 mMaxInteractDistance;
 
 public:
    static Vector<GuiOffscreenCanvas*> sList;
+   static GuiOffscreenCanvas* sActiveOffscreenCanvas;
 };
 
 #endif

+ 4 - 0
Engine/source/gui/core/guiTypes.cpp

@@ -116,6 +116,10 @@ void GuiCursor::render(const Point2I &pos)
    {
       mExtent.set(getBitmap()->getWidth(), getBitmap()->getHeight());
    }
+   else
+   {
+      return;
+   }
 
    // Render the cursor centered according to dimensions of texture
    S32 texWidth = getBitmap()->getWidth();

+ 1 - 1
Engine/source/gui/editor/guiShapeEdPreview.cpp

@@ -420,7 +420,7 @@ bool GuiShapeEdPreview::setObjectShapeAsset(const char* assetId)
       if (assetType == StringTable->insert("ShapeAsset"))
       {
          ShapeAsset* asset = AssetDatabase.acquireAsset<ShapeAsset>(id);
-         modelName = asset->getShapeFilePath();
+         modelName = asset->getShapeFile();
          AssetDatabase.releaseAsset(id);
       }
       else if (assetType == StringTable->insert("ShapeAnimationAsset"))

+ 1 - 1
Engine/source/gui/editor/inspector/group.cpp

@@ -665,7 +665,7 @@ void GuiInspectorGroup::addInspectorField(StringTableEntry name, StringTableEntr
       else if (typeName == StringTable->insert("image"))
          fieldType = TypeImageAssetPtr;
       else if (typeName == StringTable->insert("shape"))
-         fieldType = TypeShapeAssetId;
+         fieldType = TypeShapeAssetPtr;
       else if (typeName == StringTable->insert("sound"))
          fieldType = TypeSoundAssetId;
       else if (typeName == StringTable->insert("bool"))

+ 1 - 1
Engine/source/gui/editor/inspector/variableInspector.cpp

@@ -205,7 +205,7 @@ void GuiVariableInspector::addField(const char* name, const char* label, const c
       else if (newField->mFieldTypeName == StringTable->insert("image"))
          fieldTypeMask = TypeImageAssetPtr;
       else if (newField->mFieldTypeName == StringTable->insert("shape"))
-         fieldTypeMask = TypeShapeAssetId;
+         fieldTypeMask = TypeShapeAssetPtr;
       else if (newField->mFieldTypeName == StringTable->insert("bool"))
          fieldTypeMask = TypeBool;
       else if (newField->mFieldTypeName == StringTable->insert("object"))

+ 17 - 0
Engine/source/ts/assimp/assimpShapeLoader.cpp

@@ -993,6 +993,23 @@ TSShape* assimpLoadShape(const Torque::Path &path)
          tss->write(&dtsStream);
       }
 
+      Torque::Path dsqPath(cachedPath);
+      dsqPath.setExtension("dsq");
+      FileStream animOutStream;
+      for (S32 i = 0; i < tss->sequences.size(); i++)
+      {
+         const String& seqName = tss->getName(tss->sequences[i].nameIndex);
+         Con::printf("Writing DSQ Animation File for sequence '%s'", seqName.c_str());
+
+         dsqPath.setFileName(cachedPath.getFileName() + "_" + seqName);
+         if (animOutStream.open(dsqPath.getFullPath(), Torque::FS::File::Write))
+         {
+            tss->exportSequence(&animOutStream, tss->sequences[i], false);
+            animOutStream.close();
+         }
+
+      }
+
       loader.updateMaterialsScript(path);
    }
    loader.releaseImport();

+ 17 - 0
Engine/source/ts/collada/colladaShapeLoader.cpp

@@ -737,6 +737,23 @@ TSShape* loadColladaShape(const Torque::Path &path)
             tss->write(&dtsStream);
          }
 
+         Torque::Path dsqPath(cachedPath);
+         dsqPath.setExtension("dsq");
+         FileStream animOutStream;
+         for (S32 i = 0; i < tss->sequences.size(); i++)
+         {
+            const String& seqName = tss->getName(tss->sequences[i].nameIndex);
+            Con::printf("Writing DSQ Animation File for sequence '%s'", seqName.c_str());
+
+            dsqPath.setFileName(cachedPath.getFileName() + "_" + seqName);
+            if (animOutStream.open(dsqPath.getFullPath(), Torque::FS::File::Write))
+            {
+               tss->exportSequence(&animOutStream, tss->sequences[i], false);
+               animOutStream.close();
+            }
+
+         }
+
 #endif // DAE2DTS_TOOL
 
          // Add collada materials to materials.tscript

+ 1 - 1
Engine/source/ts/tsShapeConstruct.cpp

@@ -2138,7 +2138,7 @@ DefineTSShapeConstructorMethod(addSequence, bool,
       if (assetType == StringTable->insert("ShapeAsset"))
       {
          ShapeAsset* asset = AssetDatabase.acquireAsset<ShapeAsset>(assetId);
-         srcPath = asset->getShapeFilePath();
+         srcPath = asset->getShapeFile();
          AssetDatabase.releaseAsset(assetId);
       }
       else if (assetType == StringTable->insert("ShapeAnimationAsset"))

+ 1 - 1
Engine/source/ts/tsShapeConstruct.h

@@ -236,7 +236,7 @@ public:
    StringTableEntry getShapePath() const
    {
       if (mShapeAsset.notNull())
-         return mShapeAsset->getShapeFilePath();
+         return mShapeAsset->getShapeFile();
       else
          return StringTable->EmptyString();
    }

+ 15 - 0
Templates/BaseGame/game/data/Prototyping/Prototyping.tscript

@@ -42,6 +42,21 @@ function Prototyping::initClient(%this)
 //This is called when a client connects to a server
 function Prototyping::onCreateClientConnection(%this)
 {
+   if (!isObject(screen_Canvas))
+   {
+      new GuiOffscreenCanvas(screen_Canvas) {
+         targetName = "screen_Canvas";
+         targetSize = "1280 720";
+         dynamicTarget = false;
+         canInteract = true;
+         maxInteractDistance = "3";
+      };
+   }
+
+   if(isObject(OptionsMenu))
+   {
+      screen_Canvas.setContent(OptionsMenu);
+   }
 }
 
 //This is called when a client disconnects from a server

+ 11 - 0
Templates/BaseGame/game/data/Prototyping/gui_offscreen_test/ScreenTarget.asset.taml

@@ -0,0 +1,11 @@
+<ImageAsset
+    AssetName="ScreenTarget"
+    imageFile="@assetFile=#screen_Canvas"
+    VersionId="1">
+    <ImageAsset.ImageMetadata>
+        <ImageInfo
+            ImageWidth="-1"
+            ImageHeight="-1"
+            ImageDepth="-1"/>
+    </ImageAsset.ImageMetadata>
+</ImageAsset>

+ 14 - 0
Templates/BaseGame/game/data/Prototyping/gui_offscreen_test/monitor_base_mat.asset.taml

@@ -0,0 +1,14 @@
+<MaterialAsset
+    AssetName="monitor_base_mat"
+    materialDefinitionName="monitor_base_mat">
+    <Material
+        Name="monitor_base_mat"
+        mapTo="monitor_base_mat"
+        doubleSided="true"
+        originalAssetName="monitor_base_mat">
+        <Material.Stages>
+            <Stages_beginarray
+                DiffuseColor="0 0 0 1"/>
+        </Material.Stages>
+    </Material>
+</MaterialAsset>

+ 16 - 0
Templates/BaseGame/game/data/Prototyping/gui_offscreen_test/monitor_screen_mat.asset.taml

@@ -0,0 +1,16 @@
+<MaterialAsset
+    AssetName="monitor_screen_mat"
+    materialDefinitionName="monitor_screen_mat">
+    <Material
+        Name="monitor_screen_mat"
+        mapTo="monitor_screen_mat"
+        originalAssetName="monitor_base_mat">
+        <Material.Stages>
+            <Stages_beginarray
+                DiffuseMapAsset="@asset=Prototyping:ScreenTarget"
+                IgnoreLighting="true"/>
+            <Stages_beginarray
+                DiffuseColor="White"/>
+        </Material.Stages>
+    </Material>
+</MaterialAsset>

+ 6 - 0
Templates/BaseGame/game/data/Prototyping/gui_offscreen_test/monitor_shape.asset.taml

@@ -0,0 +1,6 @@
+<ShapeAsset
+    AssetName="monitor_shape"
+    fileName="@assetFile=monitor_shape.fbx"
+    constuctorFileName="@assetFile=monitor_shape.tscript"
+    materialSlot0="@asset=Prototyping:monitor_base_mat"
+    materialSlot1="@asset=Prototyping:monitor_screen_mat"/>

二進制
Templates/BaseGame/game/data/Prototyping/gui_offscreen_test/monitor_shape.fbx


+ 20 - 0
Templates/BaseGame/game/data/Prototyping/gui_offscreen_test/monitor_shape.tscript

@@ -0,0 +1,20 @@
+
+singleton TSShapeConstructor(monitor_shapefbx)
+{
+   baseShapeAsset = "Prototyping:monitor_shape";
+   singleDetailSize = "0";
+   neverImportMat = "DefaultMaterial	ColorEffect*";
+   flipUVCoords = "0";
+   joinIdenticalVerts = "0";
+   reverseWindingOrder = "0";
+   removeRedundantMats = "0";
+   animFPS = "2";
+};
+
+function monitor_shapefbx::onLoad(%this)
+{
+   %this.addNode("Col-1", "", "0 0 0 0 0 1 0", "0", "");
+   %this.addNode("ColBox-1", "Col-1", "0 0 0 1 0 0 0", "0", "Bounds");
+   %this.addCollisionDetail("-1", "Box", "Bounds", "4", "10", "30", "32", "30", "30", "30", "Flood fill");
+   %this.setBounds("-0.8 -0.244957 -0.0409516 0.8 0.244957 1.10231");
+}

+ 2 - 2
Templates/BaseGame/game/data/UI/scripts/cursors.tscript

@@ -26,7 +26,7 @@ if($platform $= "macos")
    {
       hotSpot = "4 4";
       renderOffset = "0 0";
-      bitmapName = "data/ui/images/macCursor";
+      bitmapAsset = "UI:macCursor_image";
    };
 } 
 else 
@@ -35,6 +35,6 @@ else
    {
       hotSpot = "1 1";
       renderOffset = "0 0";
-      bitmapName = "data/ui/images/defaultCursor";
+      bitmapAsset = "UI:defaultCursor_image";
    };
 }

+ 2 - 2
Templates/BaseGame/game/tools/assetBrowser/scripts/assetBrowser.tscript

@@ -278,7 +278,7 @@ function AssetBrowser::initialize(%this)
       
    if(!isObject(%this.dirHandler))
    {
-      %this.dirHandler = makedirectoryHandler(%this-->filterTree, "cache,shaderCache", ""); 
+      %this.dirHandler = makedirectoryHandler(%this-->filterTree, "cache,shaderCache,previewCache", ""); 
       %this.dirHandler.currentAddress = "data/";
    }
       
@@ -1633,7 +1633,7 @@ function AssetBrowser::doRebuildAssetArray(%this)
             else
             {
                //got it.	
-               if(%folderName $= "shaderCache" || %folderName $= "cache" || %folderName $= ".git")
+               if(%folderName $= "shaderCache" || %folderName $= "cache" || %folderName $= ".git" || %folderName $= "previewCache")
                   continue;
                   
                if(!%this.coreModulesFilter && %folderName $= "core" && %breadcrumbPath $= "")

+ 28 - 4
Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/image.tscript

@@ -42,29 +42,53 @@ function ImageAsset::generatePreviewImage(%this, %previewButton, %forceRegenerat
    if(%forceRegenerate $= "")
       %forceRegenerate = false;
       
-   %previewPath = "tools/resources/previewCache/" @ %previewButton.moduleName @ "/";
+   %previewPath =  "tools/resources/previewCache/" @ %previewButton.moduleName @ "/";
    
    if(!IsDirectory(%previewPath))
    {
       $CurrentAssetBrowser.dirHandler.createFolder(%previewPath);
    }
    
-   %previewFilePath = %previewPath @ %this.assetName @ ".png";
+   %previewFilePath = %previewPath @ %this.assetName @ "_Preview.png";
    
    if(!isFile(%previewFilePath) || (compareFileTimes(%this.getImagePath(), %previewFilePath) == 1))
    {
       %generatePreview = true;
    }
 
+   %previewAssetName = "ToolsModule:" @ %this.assetName @ "_Preview";
+
    if(%generatePreview || %forceRegenerate)
    {
       %success = saveScaledImage(%this.getImagePath(), %previewFilePath, EditorSettings.value("Assets/Browser/PreviewImageSize"));
       
       if(%success)
-         %previewButton.setBitmap(%previewFilePath);
-      
+      {
+         
+         if(!AssetDatabase.isDeclaredAsset(%previewAssetName))
+         {
+            %preview_Asset = new ImageAsset()
+            {
+               assetName = %this.assetName @ "_Preview";
+               versionId = 1;
+               imageFile = "@" @ %this.assetName @ "_Preview.png";
+            };
+
+            TamlWrite(%preview_Asset, expandPath("^ToolsModule/resources/previewCache/" @ %previewButton.moduleName @ "/" @ %preview_Asset.AssetName @ ".asset.taml"));
+            %toolsModuleDef = ModuleDatabase.findModule("ToolsModule",1);
+            AssetDatabase.addDeclaredAsset(%toolsModuleDef, expandPath("^ToolsModule/resources/previewCache/" @ %previewButton.moduleName @ "/" @ %preview_Asset.AssetName @ ".asset.taml"));
+         }
+
+         %previewButton.bitmapAsset = %previewAssetName;
+      }
+
       return %success;
    }
+   else
+   {
+      %previewButton.bitmapAsset = %previewAssetName;
+      return true;
+   }
    
    return false;
 }

+ 28 - 7
Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/material.tscript

@@ -101,8 +101,7 @@ function MaterialAsset::generatePreviewImage(%this, %previewButton, %forceRegene
    if(%forceRegenerate $= "")
       %forceRegenerate = false;
       
-   %module = $CurrentAssetBrowser.dirHandler.getModuleFromAddress(makeRelativePath(filePath(AssetDatabase.getAssetFilePath(%this.getAssetId()))));
-   %previewPath = "tools/resources/previewCache/" @ %module.moduleId @ "/";
+   %previewPath =  "tools/resources/previewCache/" @ %previewButton.moduleName @ "/";
 
    if(!IsDirectory(%previewPath))
    {
@@ -111,7 +110,8 @@ function MaterialAsset::generatePreviewImage(%this, %previewButton, %forceRegene
 
    %generatePreview = false;
 
-   %previewFilePath = %previewPath @ %this.assetName @ ".png";
+   %previewFilePath = %previewPath @ %this.assetName @ "_Preview.png";
+   
    if(!isFile(%previewFilePath))
    {
       %generatePreview = true;
@@ -126,6 +126,8 @@ function MaterialAsset::generatePreviewImage(%this, %previewButton, %forceRegene
       }
    }
 
+   %previewAssetName = "ToolsModule:" @ %this.assetName @ "_Preview";
+
    if(%generatePreview || %forceRegenerate)
    {
       if(isObject(%this.materialDefinitionName))
@@ -137,22 +139,41 @@ function MaterialAsset::generatePreviewImage(%this, %previewButton, %forceRegene
             %diffuseMapAsset = AssetDatabase.acquireAsset(%diffuseMapAssetId);
             AssetDatabase.releaseAsset(%diffuseMapAssetId);
          }
+
          %previewShapeDef = AssetDatabase.acquireAsset("ToolsModule:previewSphereShape");
          %generatedFilePath = %previewShapeDef.generateCachedPreviewImage(256, %this.materialDefinitionName);
       
          pathCopy(%generatedFilePath, %previewFilePath, false);
          fileDelete(%generatedFilePath);
 
-         if(isFile(%previewFilePath))
+         if(!AssetDatabase.isDeclaredAsset(%previewAssetName))
          {
-            %previewButton.setBitmap(%previewFilePath);
-            return true;
+            %preview_Asset = new ImageAsset()
+            {
+               assetName = %this.assetName @ "_Preview";
+               versionId = 1;
+               imageFile = "@" @ %this.assetName @ "_Preview.png";
+            };
+
+            TamlWrite(%preview_Asset, expandPath("^ToolsModule/resources/previewCache/" @ %previewButton.moduleName @ "/" @ %preview_Asset.AssetName @ ".asset.taml"));
+            %toolsModuleDef = ModuleDatabase.findModule("ToolsModule",1);
+            AssetDatabase.addDeclaredAsset(%toolsModuleDef, expandPath("^ToolsModule/resources/previewCache/" @ %previewButton.moduleName @ "/" @ %preview_Asset.AssetName @ ".asset.taml"));
          }
 
+         %previewButton.bitmapAsset = %previewAssetName;
+         return true;
+      }
+      else
+      {
          return false;
       }
    }
-      
+   else
+   {
+      %previewButton.bitmapAsset = %previewAssetName;
+      return true;
+   }
+   
    return false;
 }
 

+ 24 - 12
Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/shape.tscript

@@ -111,23 +111,21 @@ function ShapeAsset::generatePreviewImage(%this, %previewButton, %forceRegenerat
    if(%forceRegenerate $= "")
       %forceRegenerate = false;
       
-   %assetId = %this.getAssetId();
-
-   %module = %previewButton.assetBrowser.dirHandler.getModuleFromAddress(makeRelativePath(filePath(%this.getShapePath())));
-   %previewPath = "tools/resources/previewCache/" @ %module.moduleId @ "/";
+   %previewPath =  "tools/resources/previewCache/" @ %previewButton.moduleName @ "/";
    
    if(!IsDirectory(%previewPath))
    {
-      %previewButton.assetBrowser.dirHandler.createFolder(%previewPath);
+      $CurrentAssetBrowser.dirHandler.createFolder(%previewPath);
    }
    
-   %generatePreview = false;
+   %previewFilePath = %previewPath @ %this.assetName @ "_Preview.png";
    
-   %previewFilePath = %previewPath @ %this.assetName @ ".png";
    if(!isFile(%previewFilePath) || (compareFileTimes(%this.getShapePath(), %previewFilePath) == 1))
    {
       %generatePreview = true;
    }
+
+   %previewAssetName = "ToolsModule:" @ %this.assetName @ "_Preview";
    
    if(%generatePreview || %forceRegenerate)
    {
@@ -146,14 +144,28 @@ function ShapeAsset::generatePreviewImage(%this, %previewButton, %forceRegenerat
       
       pathCopy(%filePath, %previewFilePath, false);
       fileDelete(%filePath); //cleanup
-      
-      if(isFile(%previewFilePath))
+
+      if(!AssetDatabase.isDeclaredAsset(%previewAssetName))
       {
-         %previewButton.setBitmap(%previewFilePath);
-         return true;
+         %preview_Asset = new ImageAsset()
+         {
+            assetName = %this.assetName @ "_Preview";
+            versionId = 1;
+            imageFile = "@" @ %this.assetName @ "_Preview.png";
+         };
+
+         TamlWrite(%preview_Asset, expandPath("^ToolsModule/resources/previewCache/" @ %previewButton.moduleName @ "/" @ %preview_Asset.AssetName @ ".asset.taml"));
+         %toolsModuleDef = ModuleDatabase.findModule("ToolsModule",1);
+         AssetDatabase.addDeclaredAsset(%toolsModuleDef, expandPath("^ToolsModule/resources/previewCache/" @ %previewButton.moduleName @ "/" @ %preview_Asset.AssetName @ ".asset.taml"));
       }
 
-      return false;
+      %previewButton.bitmapAsset = %previewAssetName;
+      return true;
+   }
+   else
+   {
+      %previewButton.bitmapAsset = %previewAssetName;
+      return true;
    }
    
    return false;

+ 6 - 6
Templates/BaseGame/game/tools/gui/cursors.ed.tscript

@@ -24,40 +24,40 @@ new GuiCursor(LeftRightCursor)
 {
    hotSpot = "0.5 0";
    renderOffset = "0.5 0";
-   bitmapName = "./Images/leftRight";
+   bitmapAsset = "ToolsModule:leftRight_image";
 };
 
 new GuiCursor(UpDownCursor)
 {
    hotSpot = "1 1";
    renderOffset = "0 1";
-   bitmapName = "./Images/upDown";
+   bitmapAsset = "ToolsModule:upDown_image";
 };
 
 new GuiCursor(NWSECursor)
 {
    hotSpot = "1 1";
    renderOffset = "0.5 0.5";
-   bitmapName = "./Images/NWSE";
+   bitmapAsset = "ToolsModule:NWSE_image";
 };
 
 new GuiCursor(NESWCursor)
 {
    hotSpot = "1 1";
    renderOffset = "0.5 0.5";
-   bitmapName = "./Images/NESW";
+   bitmapAsset = "ToolsModule:NESW_image";
 };
 
 new GuiCursor(MoveCursor)
 {
    hotSpot = "1 1";
    renderOffset = "0.5 0.5";
-   bitmapName = "./Images/move";
+   bitmapAsset = "ToolsModule:move_image";
 };
 
 new GuiCursor(TextEditCursor)
 {
    hotSpot = "1 1";
    renderOffset = "0.5 0.5";
-   bitmapName = "./Images/textEdit";
+   bitmapAsset = "ToolsModule:textEdit_image";
 };

+ 8 - 8
Templates/BaseGame/game/tools/worldEditor/scripts/cursors.ed.tscript

@@ -27,48 +27,48 @@
 new GuiCursor(EditorHandCursor)
 {
    hotSpot = "7 0";
-   bitmapName = "~/worldEditor/images/CUR_hand.png";
+   bitmapAsset = "ToolsModule:CUR_hand_image";
 };
 
 new GuiCursor(EditorRotateCursor)
 {
    hotSpot = "11 18";
-   bitmapName = "~/worldEditor/images/CUR_rotate.png";
+   bitmapAsset = "ToolsModule:CUR_rotate_image";
 };
 
 new GuiCursor(EditorMoveCursor)
 {
    hotSpot = "9 13";
-   bitmapName = "~/worldEditor/images/CUR_grab.png";
+   bitmapAsset = "ToolsModule:CUR_grab_image";
 };
 
 new GuiCursor(EditorArrowCursor)
 {
    hotSpot = "0 0";
-   bitmapName = "~/worldEditor/images/CUR_3darrow.png";
+   bitmapAsset = "ToolsModule:CUR_3darrow_image";
 };
 
 new GuiCursor(EditorUpDownCursor)
 {
    hotSpot = "5 10";
-   bitmapName = "~/worldEditor/images/CUR_3dupdown";
+   bitmapAsset = "ToolsModule:CUR_3dupdown_image";
 };
 new GuiCursor(EditorLeftRightCursor)
 {
    hotSpot = "9 5";
-   bitmapName = "~/worldEditor/images/CUR_3dleftright";
+   bitmapAsset = "ToolsModule:CUR_3dleftright_image";
 };
 
 new GuiCursor(EditorDiagRightCursor)
 {
    hotSpot = "8 8";
-   bitmapName = "~/worldEditor/images/CUR_3ddiagright";
+   bitmapAsset = "ToolsModule:CUR_3ddiagright_image";
 };
 
 new GuiCursor(EditorDiagLeftCursor)
 {
    hotSpot = "8 8";
-   bitmapName = "~/worldEditor/images/CUR_3ddiagleft";
+   bitmapAsset = "ToolsModule:CUR_3ddiagleft_image";
 };
 
 new GuiControl(EmptyControl)