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:
Add this new class above your ShapeBase definition:
Add a new mask to your ShapeBaseMasks. Make sure you assign its number to the next available one in the list.
Add this inside your ShapeBase public block
shapebase.cc changes
Add this to the top of ShapeBaseData::ShapeBaseData()
Add these inside of ShapeBaseData::initPersistantFields()
Insert these lines into ShapeBaseData::packData()
Now add these lines to ShapeBaseData::unPackData()
At the top of ShapeBase::onRemove()
Inside of ShapeBase::renderObject() add the following line:
In ShapeBase::packUpdate() make the following changes
In ShapeBase::unpackUpdate() make the following changes
Add this code to the end of the file
USAGE
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");
#2
07/03/2006 (12:37 pm)
Just curious, how is this different from using mountObject?
#3
07/04/2006 (7:54 pm)
Lovely! Just what most people need/want/wished for!
#4
Wrenched this into my TGE 1.3 + TLK 1.3.5 + CG + ArcaneFX engine in no time.
Compiled and worked on the first try!
Thank you much, I'm off to modify my Item datablocks to take advantage of this (I'm now eyeballing the extra models from the soldier pack).
Also, if you are not using several mount nodes in your player model, you can use the shield hack found in one the melee resources here (using bones as mount points).
THANK YOU!
Ari
07/09/2006 (5:33 pm)
Woot!Wrenched this into my TGE 1.3 + TLK 1.3.5 + CG + ArcaneFX engine in no time.
Compiled and worked on the first try!
Thank you much, I'm off to modify my Item datablocks to take advantage of this (I'm now eyeballing the extra models from the soldier pack).
Also, if you are not using several mount nodes in your player model, you can use the shield hack found in one the melee resources here (using bones as mount points).
THANK YOU!
Ari
#5
For a long time mount points have been stressing me out.
Having to go into the source art and add mount points can be a pain.
Torque stock doesn't support very many either (there are mods for up to 32).
Anyways, I figured I had 60 seconds to burn while I was coding and tried this:
The pack was mounted to my head and even moved with the "look" animation.
So, on to toe rings, eye lashes and sun glasses!
Here is a quick and dirty example of the Rucksack from the Soldier pack.
Thanks again Peter, this really opened my eyes.
Ari
"When you're indie, you gotta pay it forward..."
07/09/2006 (9:41 pm)
Ok, I did a little experiment inspired by this resource.For a long time mount points have been stressing me out.
Having to go into the source art and add mount points can be a pain.
Torque stock doesn't support very many either (there are mods for up to 32).
Anyways, I figured I had 60 seconds to burn while I was coding and tried this:
//I used Eye instead of mount1 just to see if "there is no spoon". %user.mountEquipment(mesh_Rucksack, "Eye");It worked...
The pack was mounted to my head and even moved with the "look" animation.
So, on to toe rings, eye lashes and sun glasses!
Here is a quick and dirty example of the Rucksack from the Soldier pack.
datablock ShapeBaseData(mesh_Rucksack)
{
offset = "0 -0.03 -0.36";
shapefile = "~/data/shapes/ruck.dts";
scale = "1 0.75 1";
rotation = "0 0 1 0";
emap = true;
};
datablock ShapeBaseImageData(RucksackImage)
{
shapefile = "~/data/shapes/ruck.dts";
item = Rucksack;
mountPoint = 1;
offset = "0 0 0";
};
datablock ItemData(Rucksack)
{
category = "Backpacks";
className = "Wearables";
image = "RucksackImage";
shapefile = "~/data/shapes/ruck.dts";
mass = 55;
elasticity = 0.2;
friction = 0.6;
pickupRadius = 3;
pickUpName = "a Rucksack";
directUse = 1;
computeCRC = true;
};
function Rucksack::onUse(%this, %user)
{
%user.mountEquipment(mesh_Rucksack, "mount1");
}
Thanks again Peter, this really opened my eyes.Ari
"When you're indie, you gotta pay it forward..."
#6
I'm checking my stream but everything seems ok.
Anyone else having this issue?
Ari
07/11/2006 (4:55 pm)
I'm losing position and rotation on dedicated server.I'm checking my stream but everything seems ok.
Anyone else having this issue?
Ari
#7
I see you defined a ShapeBaseImage in the code you posted above. Is it possible your also mounting images too and they are conflicting? You should be able to get rid of them completly.
07/11/2006 (5:03 pm)
Ari, I'm not sure what your problem is, this works fine for me on a dedicated server.I see you defined a ShapeBaseImage in the code you posted above. Is it possible your also mounting images too and they are conflicting? You should be able to get rid of them completly.
#8
If you have time, want to try using "Bip01 Head" as mount point and let me know, if you get the same results on dedicated as I do?
I'm rewriting my scripts, I'll post back if I get it all working.
Ari
07/11/2006 (5:30 pm)
It's probably self inflicted, and my "there is no spoon" theory may actually be "I'm a newb". :pIf you have time, want to try using "Bip01 Head" as mount point and let me know, if you get the same results on dedicated as I do?
I'm rewriting my scripts, I'll post back if I get it all working.
Ari
#9
07/21/2006 (2:43 am)
@BrokeAss Games: Any further news on this? I cant wait to try out this resource...
#10
07/21/2006 (8:55 am)
Nice work but ya forgot to make the offset and rotation propegate where you have the scale propegating.void ShapeBaseData::packData(BitStream* stream)
{
Parent::packData(stream);
stream->write(mountScale.x);
stream->write(mountScale.y);
stream->write(mountScale.z);That and it's corresponding unpackdata function need to have the remaining data propegated :)
#11
07/21/2006 (9:15 am)
Thanks for the fix. My code is pretty heavily modified and things seem to get lost when I rip stuff out =)
#12
07/21/2006 (9:24 am)
No problems, mind if I modify this and integrate it with the node hiding and skin change stuff already in the MMORPG Kit?
#13
07/21/2006 (9:56 am)
Not at all, the more people that can benefit from the resource the better.
#14
You can mount to any node you want.
Currently I use "cam" for the face and head.
Ari
07/27/2006 (10:21 pm)
Btw, there is no spoon.You can mount to any node you want.
Currently I use "cam" for the face and head.
Ari
#15
I'm trying to write a tool which predefines these values for the datablocks and am having a wee spot o trouble figuring out how to get the offset and rotation into the mountOffset matrix. When I mount the object I send in the scale, position, rotation.
Here's a shot: Scale works great, haven't quite nailed down the transform and rotations yet. The code for displaying and enumerating the bones list and selecting certain bones is all added to tsshapeinstance and our custom guiObjectView. When this is finished I will release it as a resource. Just need to put the final pieces on if someone could edumacate me.
07/28/2006 (4:01 am)
One thing I don't understand is how the rotation gets into the transform matrix. I can see the readAffineTransform which reads mountOffset into mountOffset, and the scale is simple, but how does the rotation get in there?I'm trying to write a tool which predefines these values for the datablocks and am having a wee spot o trouble figuring out how to get the offset and rotation into the mountOffset matrix. When I mount the object I send in the scale, position, rotation.
Here's a shot: Scale works great, haven't quite nailed down the transform and rotations yet. The code for displaying and enumerating the bones list and selecting certain bones is all added to tsshapeinstance and our custom guiObjectView. When this is finished I will release it as a resource. Just need to put the final pieces on if someone could edumacate me.
#16
The offset and rotation both gets set in the same matrix:
The key here is the TypeMatrixPosition and TypeMatrixRotation. Even though both variable that are set in script goto the same matrix, the variable type will modify different parts of the mountOffset matrix. Hope that helps.
07/28/2006 (6:38 am)
That resource would be a huge help for my team!The offset and rotation both gets set in the same matrix:
addField("offset", TypeMatrixPosition, Offset(mountOffset,ShapeBaseData));
addField("rotation", TypeMatrixRotation, Offset(mountOffset,ShapeBaseData));The key here is the TypeMatrixPosition and TypeMatrixRotation. Even though both variable that are set in script goto the same matrix, the variable type will modify different parts of the mountOffset matrix. Hope that helps.
#17
This is one a guiObjectView, so I am passing simple mount arguments (scale, pos, rot) and need to then make the correct transofrm matrix from the pos and rot. The final result is a set of values which can be stored in the datablock for the actual equipment mounting to shapebase.
07/28/2006 (6:57 am)
That is helpful, except I need to translate this as the usage is slightly different here. I guess the question should be: given a variable holding a position transform (ex "0 1 0") and a variable holding a rotation (ex "0.5 0 0 0"), and both of those are passed to a function, how do I get them into the same matrix variable?This is one a guiObjectView, so I am passing simple mount arguments (scale, pos, rot) and need to then make the correct transofrm matrix from the pos and rot. The final result is a set of values which can be stored in the datablock for the actual equipment mounting to shapebase.
#18
08/04/2006 (9:44 am)
"Advanced 3D Game Programming All In One" has a section on matrices.
#19
10/01/2006 (7:49 pm)
Does this support deforming mountables? Say you mount a shirt onto your character, when the model twists or bends over will the mounted model shape to the characters body or will it remain upright?
#20
I have the following:
Would that take care of both offset and rotation?
*EDIT*- Had an idea, changed stuff in my post's code block.
11/13/2006 (1:47 pm)
Hey all, great resource! I just had a quick question about the fix Dreamer mentioned:I have the following:
stream->write(mountOffset);
Would that take care of both offset and rotation?
*EDIT*- Had an idea, changed stuff in my post's code block.

Torque 3D Owner Dave Young
Dave Young Games