Game Development Community

dev|Pro Game Development Curriculum

Mountimage Replacement for Equipment

by Peter Simard · 07/03/2006 (11:34 am) · 42 comments

Here are the code modifications:

shapebase.h changes

Insert these lines into the ShapeBaseData public block:
MatrixF           mountOffset;
   Point3F	     mountScale;

Add this new class above your ShapeBase definition:
class equipmentMesh
{
        // Contains the data for a mounted equipment
	public:
		ShapeBaseData*		shapeData;
		TSShapeInstance*	shapeInstance;
		U32				  nodeNumber;
		bool				   pendingRemoval;
};

Add a new mask to your ShapeBaseMasks. Make sure you assign its number to the next available one in the list.
EquipmentMask   = Parent::NextFreeMask << 5,

Add this inside your ShapeBase public block
void renderEquipment(SceneState* state);
        bool mountEquipment(ShapeBaseData* imageData, const char* slotName);
	void setEquipmentSlot(U32 shapeDataID, U32 nodeNumber);
	void processEquipment();
	void setEquipmentDeleteFlag();
	void removeOldEquipment();
	void getRenderEquipmentTransform(U32 mountPoint,MatrixF* mat);
        
        Vector<equipmentMesh*> equipment;


shapebase.cc changes

Add this to the top of ShapeBaseData::ShapeBaseData()
mountOffset.identity();
     mountScale.set(1, 1, 1);

Add these inside of ShapeBaseData::initPersistantFields()
addGroup("Mount");
   addField("offset", TypeMatrixPosition, Offset(mountOffset,ShapeBaseData));
   addField("rotation", TypeMatrixRotation, Offset(mountOffset,ShapeBaseData));
   addField("scale", TypePoint3F, Offset(mountScale,ShapeBaseData));
   endGroup("Mount");

Insert these lines into ShapeBaseData::packData()
void ShapeBaseData::packData(BitStream* stream)
{
   Parent::packData(stream);

	[b]stream->write(mountScale.x);
	stream->write(mountScale.y);
	stream->write(mountScale.z);
        if (!stream->writeFlag(mountOffset.isIdentity()))
           stream->writeAffineTransform(mountOffset);

[/b]

Now add these lines to ShapeBaseData::unPackData()
void ShapeBaseData::unpackData(BitStream* stream)
{
   Parent::unpackData(stream);

	 [b]stream->read(&mountScale.x);
	 stream->read(&mountScale.y);
	 stream->read(&mountScale.z);
         if (stream->readFlag())
            mountOffset.identity();
         else
            stream->readAffineTransform(&mountOffset);
[/b]

At the top of ShapeBase::onRemove()
equipment.clear();

Inside of ShapeBase::renderObject() add the following line:
if (shiri != NULL)
   {
      renderMountedImage(state, shiri);
   }
   else
   {
      renderImage(state, image);
   }
   [b]renderEquipment(state);[/b]

In ShapeBase::packUpdate() make the following changes
[b]Modify the mask list to add the equipment mask[/b]
   if(!stream->writeFlag(mask & (NameMask | DamageMask | SoundMask |
         ThreadMask | ImageMask | CloakMask | MountedMask | [b]EquipmentMask[/b]
         )))

[b]Add this after it packs the sound data[/b]
   if (stream->writeFlag(mask & EquipmentMask)) {
	   stream->writeInt(equipment.size(), 8);
	   //Con::printf("Writing %i items", equipment.size());
	   for(int i = 0; i < equipment.size(); i++)
	   {
		   equipmentMesh* eqp = equipment[i];
		   if(stream->writeFlag(eqp))
		   {
			   stream->writeInt(eqp->shapeData->getId(), DataBlockObjectIdBitSize);
			   stream->writeInt(eqp->nodeNumber, 8);
		   }
	   }
   }

In ShapeBase::unpackUpdate() make the following changes
[b]After unpacking the sound data[/b]
   if (stream->readFlag()) {
	   setEquipmentDeleteFlag();
	   U32 eqpSize = stream->readInt(8);
	   for(int i = 0; i < eqpSize; i++)
	   {
		   if(stream->readFlag())
		   {
			   U32 shapeDataID = stream->readInt(DataBlockObjectIdBitSize);
			   U32 nodeNumber = stream->readInt(8);
			   setEquipmentSlot(shapeDataID, nodeNumber);
		   }
	   }
		removeOldEquipment();
   }

Add this code to the end of the file
// Script hook
ConsoleMethod( ShapeBase, mountEquipment, bool, 4, 4, "(ShapeBaseData image, const char* slot)")
{
   ShapeBaseData* imageData;
   if (Sim::findObject(argv[2],imageData)) {
         object->mountEquipment(imageData,argv[3]);
		 return true;
   }
   else
   {
		return false;
   }
}

// Render the mounted equipment
void ShapeBase::renderEquipment(SceneState* state)
{
	PROFILE_START(RenderEquipment);
	for(int x=0; x < equipment.size(); x++)
	{
		Point3F cameraOffset;
		getRenderTransform().getColumn(3,&cameraOffset);
		cameraOffset -= state->getCameraPosition();
		F32 dist = cameraOffset.len();
		F32 fogAmount = state->getHazeAndFog(dist,cameraOffset.z);

		TSShapeInstance* pShapeInstance = equipment[x]->shapeInstance;
		if (pShapeInstance) {
			MatrixF mat;
			getRenderEquipmentTransform(equipment[x]->nodeNumber, &mat);
			mat.mul(equipment[x]->shapeData->mountOffset);
			glPushMatrix();
			dglMultMatrix(&mat);

			if (mCloakLevel != 0.0)
				pShapeInstance->setAlphaAlways(0.15 + (1 - mCloakLevel) * 0.85);
			else
				pShapeInstance->setAlphaAlways(1.0);

			mShapeInstance->setEnvironmentMap(state->getEnvironmentMap());
			mShapeInstance->setEnvironmentMapOn(true, 1.0);

			glScalef(equipment[x]->shapeData->mountScale.x, equipment[x]->shapeData->mountScale.y, equipment[x]->shapeData->mountScale.z);

			pShapeInstance->setupFog(fogAmount,state->getFogColor());
			pShapeInstance->animate();
			pShapeInstance->render();

			mShapeInstance->setEnvironmentMapOn(false, 1.0);
			glPopMatrix();
		}
	}
	PROFILE_END();
}

// Returns mount point to world space transform
void ShapeBase::getRenderEquipmentTransform(U32 mountPoint,MatrixF* mat)
{
    MatrixF mountTransform = mShapeInstance->mNodeTransforms[mountPoint];
    const Point3F& scale = getScale();

    // The position of the mount point needs to be scaled.
    Point3F position = mountTransform.getPosition();

    position.convolve( scale );
    mountTransform.setPosition( position );

    // Also we would like the object to be scaled to the model.
    mountTransform.scale( scale );
    mat->mul(getRenderTransform(), mountTransform);
    return;
}
// Called from the script to add/change an equipment slot
// Any changes will set the network mask and will send
// the changes out to all clients in their next update
bool ShapeBase::mountEquipment(ShapeBaseData* imageData, const char* slotName)
{
	if(isGhost())
		return false;

	U32 mountNode = mDataBlock->shape->findNode(slotName);
	if(mountNode < 0)
	{
		// If no mountNode avalible
		Con::errorf("Unable to locate node: \"%s\" while attempting to mount to %s", slotName, imageData->getName());
		return false;
	}

	// Search the equipment Vector for the same node/shapeData
	for(int x=0; x < equipment.size(); x++)
	{
		equipmentMesh* eqp = equipment[x];
		if(!eqp)
			continue;

		if(eqp->nodeNumber == mountNode)
		{
			if(eqp->shapeData == imageData)
			{
				// The same shapeData is already equiped in that slot
				eqp->pendingRemoval = false;
				return false;
			}
			else
			{
				// A new shapeData is going into the slot.
				// Delete the current one. We dont need to delete the mesh
				// because the mesh is only created on the client
				delete eqp;
				equipment.erase(x);
			}
		}

	}
	
	setMaskBits(EquipmentMask);

	// Doesnt add a new equipment item because its blank
	if(!imageData)
		return false;

	// No mounted item in that slot, add one to the vector
	equipmentMesh *eqp = new equipmentMesh();
	eqp->nodeNumber = mountNode;
	eqp->shapeData = imageData;
	eqp->shapeInstance = NULL;
	eqp->pendingRemoval = false;
	equipment.push_back(eqp);
	return true;
}

// Called anytime there is an equipment update.
// By default the eqipment will be deleted this frame
// unless an update is sent with the same mesh/slot
void ShapeBase::setEquipmentDeleteFlag()
{
	for(int x=0; x < equipment.size(); x++)
	{
		equipmentMesh* eqp = equipment[x];
		eqp->pendingRemoval = true;
	}
}

void ShapeBase::removeOldEquipment()
{
	for(int x = equipment.size()-1; x > -1 ; x--)
	{
		equipmentMesh* eqp = equipment[x];
		if(eqp->pendingRemoval)
		{
			delete eqp;
			equipment.erase(x);
		}
	}

	if(!isGhost())
	{
		setMaskBits(EquipmentMask);
	}
}


// Set the node to certain equipment.
// Will create the clientside mesh if
// it is the first time its equiped.
void ShapeBase::setEquipmentSlot(U32 shapeDataID, U32 nodeNumber)
{
	// This method will only be called on a ghosts unpackUpdate
	if(!isGhost())
		return;

	for(int x=0; x < equipment.size(); x++)
	{
		equipmentMesh* eqp = equipment[x];
		if(!eqp)
			continue;

		if(eqp->nodeNumber == nodeNumber && eqp->shapeData->getId() == shapeDataID)
		{
			// You already have that equipped, so set the removal flag to false.
			// This will ensure the garbage removal doesnt remove it
			eqp->pendingRemoval = false;
			return;
		}
	}

	equipmentMesh* eqp = new equipmentMesh();
	eqp->nodeNumber = nodeNumber;
	eqp->pendingRemoval = false;
	Sim::findObject(shapeDataID, eqp->shapeData);
	equipment.push_back(eqp);
	
	eqp->shapeInstance = new TSShapeInstance(eqp->shapeData->shape , isClientObject());
}


USAGE
//The definition of the mesh that gets mounted
datablock ShapeBaseData(mesh_testShield)
{
   offset = "-0.1 0 -0.9";
   shapeFile = "starter.fps/data/shapes/weapons/shield02.dts";
   scale = "1.5 1.5 1.5";
   rotation = "0 0 1 -72";
   emap = true;   
};

// Mounts the shield to Mount0. %player needs to be a valid ShapeBase
%player.mountEquipment(mesh_testShield, "mount0");
Page«First 1 2 3 Next»
#41
11/15/2010 (3:21 am)
Anyone have a port of this to T3d?
#42
04/01/2011 (3:03 pm)
This ported to T3D pretty easily...

Follow the TGEA changes...

Note: the vehicle tires were vanishing because the prepBatchRender function got redefined (adding the equipmentIndex variable)... so the vehicles were then calling it from GameBase... so instead of changing prepBatchRender, i copied the function to prepBatchEQRender... and left the original alone... all works fine...

use this for the prepRenderImage loop (took out the debug render..):

<code>
// eq change
for (U32 i = 0; i < equipment.size(); i++)
{
TSShapeInstance* pShapeInstance = equipment[i]->shapeInstance;
if (pShapeInstance)
{
if (mCloakLevel == 0.0f && mFadeVal == 1.0f)
{
pShapeInstance->setCurrentDetail( 0 );
prepBatchEQRender( state, -1, i );
}
}
} // end eq render

</code>

use this for the prepBatchRender loop:

<code>
if( equipmentIndex != -1 )
{
TSShapeInstance* pShapeInstance = equipment[equipmentIndex]->shapeInstance;
if( pShapeInstance )
{
GFX->pushWorldMatrix();
MatrixF mat;
getRenderEquipmentTransform(equipment[equipmentIndex]->nodeNumber,&mat);
GFX->setWorldMatrix( mat );
pShapeInstance->animate();
pShapeInstance->render(rdata);
GFX->popWorldMatrix();
}
}

</code>

Howya make code comments? meh!...
Page«First 1 2 3 Next»