diff --git a/Engine/source/T3D/assets/ShapeAsset.h b/Engine/source/T3D/assets/ShapeAsset.h index 1d7b960cd..722f5b4ad 100644 --- a/Engine/source/T3D/assets/ShapeAsset.h +++ b/Engine/source/T3D/assets/ShapeAsset.h @@ -678,6 +678,64 @@ public: static bool _set##name##Data(void* obj, const char* index, const char* data) { static_cast(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]->getShapePath() : ""; } +#define DECLARE_SHAPEASSET_ARRAY_NET_REFACTOR(className, name, max, mask) \ +private: \ + AssetPtr 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 get##name(const U32& index) { if (m##name##Asset[index].notNull()) return m##name##Asset[index]->getShapeResource(); else return NULL; } \ + AssetPtr 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(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]->getShapePath() : ""; } + #define INITPERSISTFIELD_SHAPEASSET_ARRAY_REFACTOR(name, arraySize, consoleClass, docs) \ addProtectedField(assetText(name, Asset), TypeShapeAssetPtrRefactor, 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.)); diff --git a/Engine/source/T3D/fx/groundCover.cpp b/Engine/source/T3D/fx/groundCover.cpp index 9352deabf..bf53746cb 100644 --- a/Engine/source/T3D/fx/groundCover.cpp +++ b/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() ); } } diff --git a/Engine/source/T3D/fx/groundCover.h b/Engine/source/T3D/fx/groundCover.h index af5d02858..af6a74d7e 100644 --- a/Engine/source/T3D/fx/groundCover.h +++ b/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)); diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 8239bd2f3..e6b779eb0 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -297,7 +297,7 @@ PlayerData::PlayerData() imageAnimPrefixFP = StringTable->EmptyString(); for (U32 i=0; iEmptyString()) + 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; iwrite(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; iread(&(mCRCFP[i])); } } + + UNPACKDATA_ASSET_ARRAY_REFACTOR(ShapeFP, ShapeBase::MaxMountedImages) } @@ -1863,9 +1858,9 @@ bool Player::onNewDataBlock( GameBaseData *dptr, bool reload ) { for (U32 i=0; imShapeFP[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(); diff --git a/Engine/source/T3D/player.h b/Engine/source/T3D/player.h index f1f10e29a..a3a426a14 100644 --- a/Engine/source/T3D/player.h +++ b/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(); + } };