Dynamic changeable player textures
by Peter Simard · 02/28/2006 (6:58 pm) · 14 comments
Player.h Changes
Player.cc Changes
Usage
As I said at the top of the resource, sometimes setting the texture layer will not work until a new player is created, so hopefully someone can find the problem. Also, make sure you change the path to you textures in the setLayerTexture method.
[b]struct PlayerData: public ShapeBaseData {[/b]
....
StringTableEntry baseTextureName;
....
}
[b]class Player: public ShapeBase {[/b]
....
// Make a list of the slots that can change here
// You define where the coordiates are in the constructor
enum LayerNames
{
chest,
legs,
hands,
feet
};
enum TextureLayersConsts
{
TEXTURELAYERS = 10
};
enum MaskBits {
....
layerTextureMask = Parent::NextFreeMask << 7, // <-- Replace 7 with your next free mask #
....
};
TextureHandle texture;
StringTableEntry layerTextureName[TEXTURELAYERS];
Point2I pointsList[TEXTURELAYERS];
F32 rotationList[TEXTURELAYERS];
static bool blit(GBitmap* src, GBitmap* dst, Point2I pt, F32 rot );
void setLayerTexture(U32 layer, const char* texture);
bool generateTexture();
....
}Player.cc Changes
[b]PlayerData::PlayerData()[/b]
{
baseTextureName = NULL;
....
}
[b]void PlayerData::initPersistFields()[/b]
{
addField("baseTexture", TypeString, Offset(baseTextureName, PlayerData));
....
}
[b]void PlayerData::packData(BitStream* stream)[/b]
{
Parent::packData(stream);
stream->writeString( baseTextureName );
....
}
[b]void PlayerData::unpackData(BitStream* stream)[/b]
{
Parent::unpackData(stream);
baseTextureName = stream->readSTString();
....
}
[b]Player::Player()[/b]
{
for( U32 i = 0; i < TEXTURELAYERS; i++ )
{
pointsList[i].set( -1, -1 );
}
dMemset( layerTextureName, NULL, sizeof( layerTextureName ) );
// Set the offset here for each slot.
// For example here the chest texture will be pasted in at 440, 450
// and the legs will be at 100, 50
pointsList[chest].set(440 , 450);
pointsList[legs].set(100 , 50);
texture = NULL;
....
}
[b]ConsoleMethod( Player, setLayerTexture, void, 4, 4, "")[/b]
{
object->setLayerTexture(dAtoi(argv[2]), argv[3]);
}
[b]void Player::setLayerTexture(U32 layer, const char* textureName)[/b]
{
char textureFullName[128];
dSprintf(textureFullName, sizeof(textureFullName), "starter.fps/data/shapes/player/%s", textureName);
GBitmap* bitmap = (GBitmap*)ResourceManager->loadInstance( textureFullName );
if(!bitmap)
return;
if(layer < 0 || layer > TEXTURELAYERS)
return;
layerTextureName[layer] = StringTable->insert(textureFullName);
//Con::printf("SETLAYER TO: %s", layerTextureName[layer]);
setMaskBits(layerTextureMask);
}
[b]bool Player::blit(GBitmap* base, GBitmap* layer, Point2I pt, F32 rot )[/b]
{
const U32 halfWidth = 0; //layer->getWidth() / 2;
const U32 halfHeight = 0; //layer->getHeight() / 2;
// TODO: add condition to check to make sure it is ok with rotation taken into account
if( pt.x + halfWidth > base->getWidth() ||
pt.x - halfWidth < 0 ||
pt.y + halfHeight > base->getHeight() ||
pt.y - halfHeight < 0
)
return false;
//Con::printf("Blitting base X %i Y %i", base->getWidth(), base->getHeight());
for ( U32 w = 0; w < layer->getWidth(); w++ )
for ( U32 h = 0; h < layer->getHeight(); h++ )
{
U8* basePixel;
if( rot == 0 )
{
basePixel = base->getAddress( pt.x - halfWidth + w, pt.y - halfHeight + h );
}
else
{
F32 angle = mDegToRad( rot );
const S32 x = (S32)(((S32)w - (S32)halfWidth)*cos(angle) - ((S32)h - (S32)halfHeight)*sin(angle)) + pt.x;
const S32 y = (S32)(((S32)w - (S32)halfWidth)*sin(angle) + ((S32)h - (S32)halfHeight)*cos(angle)) + pt.y;
basePixel = base->getAddress(x,y);
}
U8* layerPixel = layer->getAddress( w, h );
const float alpha = layerPixel[3]/255.0;
basePixel[0] = (U8)(alpha*layerPixel[0] + (1-alpha)*basePixel[0]);
basePixel[1] = (U8)(alpha*layerPixel[1] + (1-alpha)*basePixel[1]);
basePixel[2] = (U8)(alpha*layerPixel[2] + (1-alpha)*basePixel[2]);
}
return true;
}
[b]bool Player::generateTexture()[/b]
{
if(!isGhost())
return false;
GBitmap* baseBitmap;
if( mDataBlock->baseTextureName )
{
baseBitmap = (GBitmap*)ResourceManager->loadInstance( mDataBlock->baseTextureName );
if( baseBitmap == NULL )
{
Con::errorf(ConsoleLogEntry::General, "TextureLayersData(%s)::onAdd: baseTextureName (%s) invalid file", getName(), mDataBlock->baseTextureName);
return false;
}
if( baseBitmap->getFormat() != GBitmap::RGB )
{
Con::errorf(ConsoleLogEntry::General, "TextureLayersData(%s)::onAdd: baseTextureName (%s) not RGB", getName(), mDataBlock->baseTextureName);
return false;
}
}
else
{
Con::errorf(ConsoleLogEntry::General, "TextureLayersData(%s)::onAdd: baseTextureName invalid", getName());
return false;
}
for( U32 i = 0; i < TEXTURELAYERS; i++ )
{
if( layerTextureName[i] != NULL )
{
GBitmap* layerBitmap = (GBitmap*)ResourceManager->loadInstance( layerTextureName[i] );
if( layerBitmap == NULL )
{
Con::errorf(ConsoleLogEntry::General, "TextureLayersData(%s)::onAdd: layerTextureName (%s) invalid file", getName(), layerTextureName[i]);
continue;
}
if( layerBitmap->getFormat() != GBitmap::RGBA )
{
Con::errorf(ConsoleLogEntry::General, "TextureLayersData(%s)::onAdd: layerTextureName (%s) not RGBA", getName(), layerTextureName[i]);
continue;
}
if( !blit( baseBitmap, layerBitmap, pointsList[i], rotationList[i] ) )
{
Con::errorf(ConsoleLogEntry::General, "TextureLayersData(%s)::onAdd: unable to blit layerTextureName (%s)", getName(), layerTextureName[i]);
continue;
}
delete layerBitmap;
}
}
texture.set(NULL,baseBitmap,BitmapTexture);
return true;
}
[b]U32 Player::packUpdate(NetConnection *con, U32 mask, BitStream *stream)[/b]
{
U32 retMask = Parent::packUpdate(con, mask, stream);
if(stream->writeFlag(mask & layerTextureMask))
{
for(int x = 0; x < TEXTURELAYERS; x++)
{
stream->writeString(layerTextureName[x]);
//Con::printf("WRITING TEX #%i TO %s", x, layerTextureName[x]);
}
}
....
}
[b]void Player::unpackUpdate(NetConnection *con, BitStream *stream)[/b]
{
Parent::unpackUpdate(con,stream);
if(stream->readFlag())
{
for(int x = 0; x < TEXTURELAYERS; x++)
{
layerTextureName[x] = stream->readSTString();
//Con::printf("SET TEX #%i TO %s", x, layerTextureName[x]);
}
if(isProperlyAdded())
generateTexture();
}
....
}
[b]void Player::renderImage(SceneState* state, SceneRenderImage* image)[/b]
{
....
if(texture)
mShapeInstance->setOverrideTexture(texture);
mShapeInstance->setupFog(fogAmount,state->getFogColor());
mShapeInstance->animate();
mShapeInstance->render();
if(texture)
mShapeInstance->clearOverrideTexture();
....
}Usage
%slotNumber = 1;
%textureName = "pants.png";
%player.setLayerTexture(%slotNumber, %textureName);As I said at the top of the resource, sometimes setting the texture layer will not work until a new player is created, so hopefully someone can find the problem. Also, make sure you change the path to you textures in the setLayerTexture method.
#2
03/01/2006 (5:15 am)
Is the referenced multi-texture resource needed as a base?
#3
2. Does that other resource have the same issue yours does (if related)?
3. Looks like for the other resource, realtime changes was/is something to be done, does yours hand this?
Thanks,
John
03/01/2006 (12:43 pm)
1. Not sure I get it, how is your resource different from the one you mentioned at HERE? 2. Does that other resource have the same issue yours does (if related)?
3. Looks like for the other resource, realtime changes was/is something to be done, does yours hand this?
Thanks,
John
#4
The main difference between this resource and the original is it can change dynamicly. This means if the player picks up a new chest piece and equips it, it will update it in real time. You do not need the old resource for this.
03/01/2006 (7:54 pm)
I added some bold tags to show where a function starts. Look for the elipses to know where the rest of the code show go.The main difference between this resource and the original is it can change dynamicly. This means if the player picks up a new chest piece and equips it, it will update it in real time. You do not need the old resource for this.
#5
03/08/2006 (2:32 am)
#6
03/29/2006 (12:54 am)
Im sorry, but I really cant understand this resource, please include better inplementation instructions, such as find this inside whatever function, after it place this, somthing a little easier than just the function bolded, and periods for where it ends? I dont understand how to implement it...
#7
03/29/2006 (8:21 am)
Hey Tristan, this is already included in the MMORPG Enhancement Kit (which you already own), although I never did manage to figure out the little bug he mentioned.
#8
I have been looking this code and investigating the engine for more than 2 days, and im almost sure this line if(texture) mShapeInstance->setOverrideTexture(texture);
simply kills the cloacking feature, is that right?
Im trying to do this same thing in a more simple way, i try to check all the resources before adding em, and im sure there must be a better way to handle multitexturing.
Maybe its just my paranoia hehe.
I opened this post to talk about the subject
http://www.garagegames.com/mg/forums/result.thread.php?qt=44630
05/23/2006 (6:08 pm)
Even when this resource is great, and it solves the problem.I have been looking this code and investigating the engine for more than 2 days, and im almost sure this line if(texture) mShapeInstance->setOverrideTexture(texture);
simply kills the cloacking feature, is that right?
Im trying to do this same thing in a more simple way, i try to check all the resources before adding em, and im sure there must be a better way to handle multitexturing.
Maybe its just my paranoia hehe.
I opened this post to talk about the subject
http://www.garagegames.com/mg/forums/result.thread.php?qt=44630
#9
'Player::setLayerTexture': cannt acces protected member declared in class 'Player'
I am using tge 1.4
08/19/2006 (10:41 pm)
When i am compiling I have error :'Player::setLayerTexture': cannt acces protected member declared in class 'Player'
I am using tge 1.4
#11
Regards,
Dreamer
10/03/2006 (5:08 pm)
Try using SkinMask instead of adding the custom mask, I've noticed a random bug with any new masks that are added in that they sometimes randomly fail to fire properly. I've never had this same problem when re-using existing masks.Regards,
Dreamer
#12
Also wanted to remind anyone else using this awesome resource, that the BaseTexture has to be RGB format. If you decide to use a non-RGB texture and comment out the lines that prevent you from using non-RGB, your textures will have "side-effects."
Happy New Years.
01/02/2007 (6:46 pm)
Noticed that every once in a while, in Player::unpackUpdate, the generateTexture() is not being called because isProperlyAdded() returns False. Not sure why it returns False.Also wanted to remind anyone else using this awesome resource, that the BaseTexture has to be RGB format. If you decide to use a non-RGB texture and comment out the lines that prevent you from using non-RGB, your textures will have "side-effects."
Happy New Years.
#13
Just wondering, does this method work for non-skin tight armor?
Thanks.
11/08/2007 (5:30 pm)
Hi. New here but having a blast.Just wondering, does this method work for non-skin tight armor?
Thanks.
#14
11/22/2007 (12:37 am)
Has anyone seen the random bug Peter mentioned? If so did you manage to fix it? 
Torque Owner Dreamer
Default Studio Name
Looks like a great resource though!