Cleans up ShapeAsset of some unnecessary/redundant elements like extra material and animations tracking

Removed the old SHAPE_ASSET macros
Implements AssetRef struct that acts as a universal wrapper for an templated AssetPtr and AssetId pair
Adds Type handling for AssetRef for ShapeAsset to unify handling in classes that utilize a shapeAsset, so assigning an assetPtr or an assetId will keep a record of the assignment in the event the assetPtr is invalid.
Update all classes that utilized the old SHAPE_ASSET macros to utilize the AssetRef struct and updated the class code to utilize it to provide much more clean and concise code that isn't blocked behind macro definitions
Added a new example class: shapeDatablockExample which allows render of a simple shape object utilizing a simple example datablock.
This commit is contained in:
JeffR 2026-05-31 01:19:26 -05:00
parent c2c5674fe9
commit b44158cb89
52 changed files with 1860 additions and 1086 deletions

View file

@ -241,13 +241,13 @@ afxMagicMissileData::afxMagicMissileData()
caster_safety_time = U32_MAX;
mProjectileShapeAsset.registerRefreshNotify(this);
projectileShapeAssetRef.assetPtr.registerRefreshNotify(this);
projectileShape = NULL;
}
afxMagicMissileData::afxMagicMissileData(const afxMagicMissileData& other, bool temp_clone) : GameBaseData(other, temp_clone)
{
mProjectileShapeAsset = other.mProjectileShapeAsset;
projectileShapeAssetRef = other.projectileShapeAssetRef;
projectileShape = other.projectileShape; // -- TSShape loads using projectileShapeName
CLONE_ASSET(ProjectileSound);
splash = other.splash;
@ -307,8 +307,6 @@ afxMagicMissileData::~afxMagicMissileData()
{
if (wiggle_axis)
delete [] wiggle_axis;
mProjectileShapeAsset.unregisterRefreshNotify();
}
afxMagicMissileData* afxMagicMissileData::cloneAndPerformSubstitutions(const SimObject* owner, S32 index)
@ -338,7 +336,8 @@ void afxMagicMissileData::initPersistFields()
static IRangeValidatorScaled ticksFromMS(TickMs, 0, MaxLifetimeTicks);
addGroup("Shapes");
INITPERSISTFIELD_SHAPEASSET_REFACTOR(ProjectileShape, afxMagicMissileData, "Shape for the projectile");
ADD_FIELD("projectileShapeAsset", TypeShapeAssetRef, Offset(projectileShapeAssetRef, afxMagicMissileData))
.doc("Shape for the projectile");
addField("scale", TypePoint3F, Offset(scale, afxMagicMissileData));
addField("missileShapeScale", TypePoint3F, myOffset(scale));
endGroup("Shapes");
@ -531,16 +530,20 @@ bool afxMagicMissileData::preload(bool server, String &errorStr)
if (Sim::findObject(lightDescId, lightDesc) == false)
Con::errorf(ConsoleLogEntry::General, "afxMagicMissileData::preload: Invalid packet, bad datablockid(lightDesc): %d", lightDescId);
}
if (getProjectileShape())
if (projectileShapeAssetRef.notNull())
{
TSShapeInstance* pDummy = new TSShapeInstance(getProjectileShape(), !server);
delete pDummy;
}
else if (mProjectileShapeAsset.notNull())
{
errorStr = String::ToString("afxMagicMissileData::preload: Couldn't load shape \"%s\"", _getProjectileShapeAssetId());
return false;
Resource<TSShape> shape = projectileShapeAssetRef.assetPtr->getShapeResource();
if (shape)
{
TSShapeInstance* pDummy = new TSShapeInstance(shape, !server);
delete pDummy;
}
else
{
errorStr = String::ToString("afxMagicMissileData(%s)::preload: Couldn't load shape \"%s\"", getName(), projectileShapeAssetRef.assetId);
return false;
}
}
return true;
@ -580,7 +583,7 @@ void afxMagicMissileData::packData(BitStream* stream)
{
Parent::packData(stream);
PACKDATA_ASSET_REFACTOR(ProjectileShape);
AssetDatabase.packDataAsset(stream, projectileShapeAssetRef.assetId);
/* From stock Projectile code...
stream->writeFlag(faceViewer);
@ -691,7 +694,7 @@ void afxMagicMissileData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
UNPACKDATA_ASSET_REFACTOR(ProjectileShape);
projectileShapeAssetRef = AssetDatabase.unpackDataAsset(stream);
/* From stock Projectile code...
faceViewer = stream->readFlag();
*/

View file

@ -69,7 +69,7 @@ public:
public:
// variables set in datablock definition:
// Shape related
DECLARE_SHAPEASSET_REFACTOR(afxMagicMissileData, ProjectileShape)
AssetRef<ShapeAsset> projectileShapeAssetRef;
//StringTableEntry projectileShapeName;
//bool hasLight;

View file

@ -79,12 +79,12 @@ afxModelData::afxModelData()
shadowProjectionDistance = 10.0f;
shadowSphereAdjust = 1.0;
mShapeAsset.registerRefreshNotify(this);
shapeAssetRef.assetPtr.registerRefreshNotify(this);
}
afxModelData::afxModelData(const afxModelData& other, bool temp_clone) : GameBaseData(other, temp_clone)
{
mShapeAsset = other.mShapeAsset;
shapeAssetRef = other.shapeAssetRef;
sequence = other.sequence;
seq_rate = other.seq_rate;
seq_offset = other.seq_offset;
@ -114,8 +114,6 @@ afxModelData::~afxModelData()
{
if (remap_buffer)
dFree(remap_buffer);
mShapeAsset.unregisterRefreshNotify();
}
bool afxModelData::preload(bool server, String &errorStr)
@ -126,8 +124,12 @@ bool afxModelData::preload(bool server, String &errorStr)
// don't need to do this stuff on the server
if (server)
return true;
if (getShape())
if (shapeAssetRef.isNull())
return false;
Resource<TSShape> shape = shapeAssetRef.assetPtr->getShapeResource();
if (shape)
{
// just parse up the string and collect the remappings in txr_tag_remappings.
if (remap_txr_tags != ST_NULLSTRING)
@ -157,13 +159,13 @@ bool afxModelData::preload(bool server, String &errorStr)
if (txr_tag_remappings.size() == 0)
{
// this little hack forces the textures to preload
TSShapeInstance* pDummy = new TSShapeInstance(getShape());
TSShapeInstance* pDummy = new TSShapeInstance(shape);
delete pDummy;
}
}
else
{
errorStr = String::ToString("afxModelData::load: Failed to load shape \"%s\"", _getShapeAssetId());
errorStr = String::ToString("afxModelData::load: Failed to load shape \"%s\"", shapeAssetRef.assetId);
return false;
}
return true;
@ -175,7 +177,8 @@ void afxModelData::initPersistFields()
{
docsURL;
addGroup("Shapes");
INITPERSISTFIELD_SHAPEASSET_REFACTOR(Shape, afxModelData, "The name of a .dts format file to use for the model.");
ADD_FIELD("shapeAsset", TypeShapeAssetRef, Offset(shapeAssetRef, afxModelData))
.doc("The id of a shapeAsset to use for the shape.");
endGroup("Shapes");
addGroup("Animation");
@ -259,7 +262,8 @@ void afxModelData::packData(BitStream* stream)
{
Parent::packData(stream);
PACKDATA_ASSET_REFACTOR(Shape);
AssetDatabase.packDataAsset(stream, shapeAssetRef.assetId);
stream->writeString(sequence);
stream->write(seq_rate);
stream->write(seq_offset);
@ -290,7 +294,8 @@ void afxModelData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
UNPACKDATA_ASSET_REFACTOR(Shape);
shapeAssetRef = AssetDatabase.unpackDataAsset(stream);
sequence = stream->readSTString();
stream->read(&seq_rate);
stream->read(&seq_offset);
@ -319,9 +324,15 @@ void afxModelData::unpackData(BitStream* stream)
void afxModelData::onPerformSubstitutions()
{
if (!getShape())
if (!shapeAssetRef.isNull())
{
Con::errorf("afxModelData::onPerformSubstitutions: Failed to load shape \"%s\"", _getShapeAssetId());
Con::errorf("afxModelData::onPerformSubstitutions: Failed to load shape asset \"%s\"", shapeAssetRef.assetId);
}
Resource<TSShape> shape = shapeAssetRef.assetPtr->getShapeResource();
if (!shape)
{
Con::errorf("afxModelData::onPerformSubstitutions: Failed to load shape \"%s\"", shapeAssetRef.assetId);
return;
}
}
@ -395,19 +406,22 @@ bool afxModel::onAdd()
if (!conn || !Parent::onAdd())
return false;
// setup our bounding box
if (mDataBlock->getShape())
mObjBox = mDataBlock->getShape()->mBounds;
else
mObjBox = Box3F(Point3F(-1, -1, -1), Point3F(1, 1, 1));
Resource<TSShape> shape;
mObjBox = Box3F(Point3F(-1, -1, -1), Point3F(1, 1, 1));
if (!mDataBlock->shapeAssetRef.isNull())
{
shape = mDataBlock->shapeAssetRef.assetPtr->getShapeResource();
if (shape)
mObjBox = shape->mBounds;
}
// setup the shape instance and sequence
if (mDataBlock->getShape())
if (shape)
{
if (/*isClientObject() && */mDataBlock->txr_tag_remappings.size() > 0)
{
// temporarily substitute material tags with alternates
TSMaterialList* mat_list = mDataBlock->getShape()->materialList;
TSMaterialList* mat_list = shape->materialList;
if (mat_list)
{
for (S32 i = 0; i < mDataBlock->txr_tag_remappings.size(); i++)
@ -428,7 +442,7 @@ bool afxModel::onAdd()
}
}
shape_inst = new TSShapeInstance(mDataBlock->getShape());
shape_inst = new TSShapeInstance(shape);
if (true) // isClientObject())
{
@ -437,7 +451,7 @@ bool afxModel::onAdd()
// restore the material tags to original form
if (mDataBlock->txr_tag_remappings.size() > 0)
{
TSMaterialList* mat_list = mDataBlock->getShape()->materialList;
TSMaterialList* mat_list = shape->materialList;
if (mat_list)
{
for (S32 i = 0; i < mDataBlock->txr_tag_remappings.size(); i++)
@ -466,7 +480,6 @@ bool afxModel::onAdd()
else
{
// here we start the default animation sequence
TSShape* shape = shape_inst->getShape();
main_seq_id = shape->findSequence(mDataBlock->sequence);
if (main_seq_id != -1)
{
@ -503,14 +516,14 @@ bool afxModel::onAdd()
resetWorldBox();
if (mDataBlock->getShape())
if (shape)
{
// Scan out the collision hulls...
static const String sCollisionStr( "collision-" );
for (U32 i = 0; i < mDataBlock->getShape()->details.size(); i++)
for (U32 i = 0; i < shape->details.size(); i++)
{
const String &name = mDataBlock->getShape()->names[mDataBlock->getShape()->details[i].nameIndex];
const String &name = shape->names[shape->details[i].nameIndex];
if (name.compare( sCollisionStr, sCollisionStr.length(), String::NoCase ) == 0)
{
@ -524,7 +537,7 @@ bool afxModel::onAdd()
char buff[128];
dSprintf(buff, sizeof(buff), "LOS-%d", i + 1 + 8/*MaxCollisionShapes*/);
U32 los = mDataBlock->getShape()->findDetail(buff);
U32 los = shape->findDetail(buff);
if (los == -1)
mLOSDetails.last() = i;
else
@ -535,9 +548,9 @@ bool afxModel::onAdd()
// Snag any "unmatched" LOS details
static const String sLOSStr( "LOS-" );
for (U32 i = 0; i < mDataBlock->getShape()->details.size(); i++)
for (U32 i = 0; i < shape->details.size(); i++)
{
const String &name = mDataBlock->getShape()->names[mDataBlock->getShape()->details[i].nameIndex];
const String &name = shape->names[shape->details[i].nameIndex];
if (name.compare( sLOSStr, sLOSStr.length(), String::NoCase ) == 0)
{

View file

@ -43,7 +43,7 @@ struct afxModelData : public GameBaseData, protected AssetPtrCallback
{
typedef GameBaseData Parent;
DECLARE_SHAPEASSET_REFACTOR(afxModelData, Shape)
AssetRef<ShapeAsset> shapeAssetRef;
StringTableEntry sequence;
@ -155,9 +155,21 @@ public:
void setSequenceRateFactor(F32 factor);
void setSortPriority(S8 priority) { sort_priority = priority; }
const char* getShapeFileName() const { return mDataBlock->getShapeFile(); }
const char* getShapeFileName() const
{
if (mDataBlock->shapeAssetRef.isNull())
return "";
return mDataBlock->shapeAssetRef.assetPtr->getShapeFile();
}
void setVisibility(bool flag) { is_visible = flag; }
TSShape* getTSShape() { return mDataBlock->getShape(); }
TSShape* getTSShape()
{
if (mDataBlock->shapeAssetRef.isNull())
return nullptr;
return mDataBlock->shapeAssetRef.assetPtr->getShape();
}
TSShapeInstance* getTSShapeInstance() { return shape_inst; }
U32 setAnimClip(const char* clip, F32 pos, F32 rate, F32 trans);

View file

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