initial commit

change the macro to use the refactor (exact same structure as the imageasset macro)
This commit is contained in:
marauder2k7 2025-06-19 13:34:07 +01:00
parent 61a75ada1e
commit ca1604170d
29 changed files with 700 additions and 457 deletions

View file

@ -117,6 +117,54 @@ ConsoleSetType(TypeShapeAssetId)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// REFACTOR
//-----------------------------------------------------------------------------
IMPLEMENT_STRUCT(AssetPtr<ShapeAsset>, AssetPtrShapeAsset, , "")
END_IMPLEMENT_STRUCT
ConsoleType(ShapeAssetPtrRefactor, TypeShapeAssetPtrRefactor, AssetPtr<ShapeAsset>, ASSET_ID_FIELD_PREFIX)
ConsoleGetType(TypeShapeAssetPtrRefactor)
{
// Fetch asset Id.
return (*((AssetPtr<ShapeAsset>*)dptr)).getAssetId();
}
ConsoleSetType(TypeShapeAssetPtrRefactor)
{
// Was a single argument specified?
if (argc == 1)
{
// Yes, so fetch field value.
const char* pFieldValue = argv[0];
// Fetch asset pointer.
AssetPtr<ShapeAsset>* pAssetPtr = dynamic_cast<AssetPtr<ShapeAsset>*>((AssetPtrBase*)(dptr));
// Is the asset pointer the correct type?
if (pAssetPtr == NULL)
{
Con::warnf("(TypeShapeAssetPtrRefactor) - Failed to set asset Id '%d'.", pFieldValue);
return;
}
// Set asset.
pAssetPtr->setAssetId(pFieldValue);
return;
}
// Warn.
Con::warnf("(TypeShapeAssetPtrRefactor) - Cannot set multiple args to a single asset.");
}
//-----------------------------------------------------------------------------
// REFACTOR END
//-----------------------------------------------------------------------------
const String ShapeAsset::mErrCodeStrings[] = const String ShapeAsset::mErrCodeStrings[] =
{ {
"TooManyVerts", "TooManyVerts",
@ -478,7 +526,38 @@ StringTableEntry ShapeAsset::getAssetIdByFilename(StringTableEntry fileName)
} }
else 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->getShapePath() == fileName)
{
return shapeAsset;
}
else
{
Torque::Path temp1 = temp->getShapePath();
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; return shapeAssetId;

View file

@ -212,6 +212,9 @@ protected:
DefineConsoleType(TypeShapeAssetPtr, S32) DefineConsoleType(TypeShapeAssetPtr, S32)
DefineConsoleType(TypeShapeAssetId, String) DefineConsoleType(TypeShapeAssetId, String)
DECLARE_STRUCT(AssetPtr<ShapeAsset>)
DefineConsoleType(TypeShapeAssetPtrRefactor, AssetPtr<ShapeAsset>)
#ifdef TORQUE_TOOLS #ifdef TORQUE_TOOLS
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// TypeAssetId GuiInspectorField Class // TypeAssetId GuiInspectorField Class
@ -493,4 +496,196 @@ public: \
#pragma endregion #pragma endregion
//-----------------------------------------------------------------------------
// REFACTOR
//-----------------------------------------------------------------------------
#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()) \
{ \
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 NULL; } \
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->getShapePath() : ""; }
#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()) \
{ \
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 NULL; } \
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->getShapePath() : ""; }
#define INITPERSISTFIELD_SHAPEASSET_REFACTOR(name, consoleClass, docs) \
addProtectedField(assetText(name, Asset), TypeShapeAssetPtrRefactor, 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()) \
{ \
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 NULL; } \
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]->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.));
#pragma endregion
//-----------------------------------------------------------------------------
// REFACTOR END
//-----------------------------------------------------------------------------
#endif #endif

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -502,10 +502,10 @@ bool PlayerData::preload(bool server, String &errorStr)
// If we don't have a shape don't crash out trying to // If we don't have a shape don't crash out trying to
// setup animations and sequences. // setup animations and sequences.
if ( mShape ) if (getShape())
{ {
// Go ahead a pre-load the player shape // Go ahead a pre-load the player shape
TSShapeInstance* si = new TSShapeInstance(mShape, false); TSShapeInstance* si = new TSShapeInstance(getShape(), false);
TSThread* thread = si->addThread(); TSThread* thread = si->addThread();
// Extract ground transform velocity from animations // Extract ground transform velocity from animations
@ -516,7 +516,7 @@ bool PlayerData::preload(bool server, String &errorStr)
ActionAnimationDef *sp = &ActionAnimationList[i]; ActionAnimationDef *sp = &ActionAnimationList[i];
dp->name = sp->name; dp->name = sp->name;
dp->dir.set(sp->dir.x,sp->dir.y,sp->dir.z); 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 // If this is a sprint action and is missing a sequence, attempt to use
// the standard run ones. // the standard run ones.
@ -524,7 +524,7 @@ bool PlayerData::preload(bool server, String &errorStr)
{ {
S32 offset = i-SprintRootAnim; S32 offset = i-SprintRootAnim;
ActionAnimationDef *standDef = &ActionAnimationList[RootAnim+offset]; ActionAnimationDef *standDef = &ActionAnimationList[RootAnim+offset];
dp->sequence = mShape->findSequence(standDef->name); dp->sequence = getShape()->findSequence(standDef->name);
} }
dp->velocityScale = true; dp->velocityScale = true;
@ -532,12 +532,12 @@ bool PlayerData::preload(bool server, String &errorStr)
if (dp->sequence != -1) if (dp->sequence != -1)
getGroundInfo(si,thread,dp); 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)) if (!isTableSequence(b))
{ {
dp->sequence = b; dp->sequence = b;
dp->name = mShape->getName(mShape->sequences[b].nameIndex); dp->name = getShape()->getName(getShape()->sequences[b].nameIndex);
dp->velocityScale = false; dp->velocityScale = false;
getGroundInfo(si,thread,dp++); getGroundInfo(si,thread,dp++);
} }
@ -554,17 +554,17 @@ bool PlayerData::preload(bool server, String &errorStr)
lookAction = c; lookAction = c;
// Resolve spine // Resolve spine
spineNode[0] = mShape->findNode("Bip01 Pelvis"); spineNode[0] = getShape()->findNode("Bip01 Pelvis");
spineNode[1] = mShape->findNode("Bip01 Spine"); spineNode[1] = getShape()->findNode("Bip01 Spine");
spineNode[2] = mShape->findNode("Bip01 Spine1"); spineNode[2] = getShape()->findNode("Bip01 Spine1");
spineNode[3] = mShape->findNode("Bip01 Spine2"); spineNode[3] = getShape()->findNode("Bip01 Spine2");
spineNode[4] = mShape->findNode("Bip01 Neck"); spineNode[4] = getShape()->findNode("Bip01 Neck");
spineNode[5] = mShape->findNode("Bip01 Head"); spineNode[5] = getShape()->findNode("Bip01 Head");
// Recoil animations // Recoil animations
recoilSequence[0] = mShape->findSequence("light_recoil"); recoilSequence[0] = getShape()->findSequence("light_recoil");
recoilSequence[1] = mShape->findSequence("medium_recoil"); recoilSequence[1] = getShape()->findSequence("medium_recoil");
recoilSequence[2] = mShape->findSequence("heavy_recoil"); recoilSequence[2] = getShape()->findSequence("heavy_recoil");
} }
// Convert pickupRadius to a delta of boundingBox // Convert pickupRadius to a delta of boundingBox
@ -7511,8 +7511,8 @@ F32 Player::getAnimationDurationByID(U32 anim_id)
if (anim_id == BAD_ANIM_ID) if (anim_id == BAD_ANIM_ID)
return 0.0f; return 0.0f;
S32 seq_id = mDataBlock->actionList[anim_id].sequence; S32 seq_id = mDataBlock->actionList[anim_id].sequence;
if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size()) if (seq_id >= 0 && seq_id < mDataBlock->getShape()->sequences.size())
return mDataBlock->mShape->sequences[seq_id].duration; return mDataBlock->getShape()->sequences[seq_id].duration;
return 0.0f; return 0.0f;
} }
@ -7524,8 +7524,8 @@ bool Player::isBlendAnimation(const char* name)
return false; return false;
S32 seq_id = mDataBlock->actionList[anim_id].sequence; S32 seq_id = mDataBlock->actionList[anim_id].sequence;
if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size()) if (seq_id >= 0 && seq_id < mDataBlock->getShape()->sequences.size())
return mDataBlock->mShape->sequences[seq_id].isBlend(); return mDataBlock->getShape()->sequences[seq_id].isBlend();
return false; return false;
} }

View file

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

View file

@ -310,7 +310,7 @@ bool RigidShapeData::preload(bool server, String &errorStr)
if (!collisionDetails.size() || collisionDetails[0] == -1) if (!collisionDetails.size() || collisionDetails[0] == -1)
{ {
Con::errorf("RigidShapeData::preload failed: Rigid shapes must define a collision-1 detail"); 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; return false;
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -163,7 +163,7 @@ bool VehicleData::preload(bool server, String &errorStr)
if (!collisionDetails.size() || collisionDetails[0] == -1) if (!collisionDetails.size() || collisionDetails[0] == -1)
{ {
Con::errorf("VehicleData::preload failed: Vehicle models must define a collision-1 detail"); 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; return false;
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -85,7 +85,7 @@ public:
U32 packUpdate(NetConnection*, U32, BitStream*) override; U32 packUpdate(NetConnection*, U32, BitStream*) override;
void unpackUpdate(NetConnection*, 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; } void setVisibility(bool flag) { mIs_visible = flag; }
DECLARE_CONOBJECT(afxStaticShape); DECLARE_CONOBJECT(afxStaticShape);

View file

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

View file

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