Multiple textures on player
by Leslie Young · 01/31/2007 (2:43 pm) · 29 comments
I was using TGE1.5 + AFX Core when writing this resource.
Have a look at this Blog entry for bigger images and here for a video (10Mb xvid) of this in action inside my character editor.
This resource can be really helpful to get something similar to character editing present in MMOGs but please note that a player will have his own unique texture if you call console/script functions from this resource on that player and that might mean a lot of textures in memory if you got a lot of players. So you might want to go with something a bit more lightweight than this like the setShapeName() function and Mesh Hiding resource.
I started off with this resource http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=9694
but that didn't really have all the functionality I needed. For my game I need the ability to not only change the base texture for a player, but also hair, and extra armour shape textures separately (using mesh hiding), all inside the same DTS. With the mentioned resource even the hair and other mesh data in the DTS would take on the texture you applied to the body. With this resource only the body would be affected and you can still hide and show other mesh data like different hair styles and use setSkinName() to affect those. In fact, you could apply layers of textures to the hair or other mesh data even, separately.
Only two files in the engine to edit ;) I indicated my changes and additions in BOLD. I include the lines of code before and after the change you need to make, as they currently appear in the engine, so that it is easier to find the place to put it in other versions of the engine.
Also notice that I make use of #define/ #ifdef/ #else which helps me disable/enable the resource while testing it but you can remove that if you like. I tend to do this with all resources I implement so that I can easily disable it if something seems to be broken by the resource I implemented without having to go through the code commenting/uncommenting everything.
Open up PLAYER.H and make the changes as indicated.
in struct PlayerData: public ShapeBaseData {
in class Player: public ShapeBase - With the following your code might differ somehwhat, especially non 1.5+AFX users, so don't copy paste verbatim.
REPLACE
and few lines down do the following
and that is it for player.h
Next on to PLAYER.CC
In the PlayerData::PlayerData() , at the end.
In void PlayerData::initPersistFields() , at end.
near beginning of void PlayerData::packData(BitStream* stream)
near beginning of void PlayerData::unpackData(BitStream* stream)
Player::Player()
U32 Player::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
void Player::unpackUpdate(NetConnection *con, BitStream *stream)
and then at the end of the PLAYER.CC file add the following.
You will notice comments in the above code, READ THEM! :)
In your player datablock (in script) you need to help my code out a little by adding the following.
And then make use of the console functions as you need them.
%player.setLayerTexture(layer_index, zeroLayerTexture, layer_texture_path, pointx, pointy, rotation, delayed) This funtion is used to change the texture layers.
layer_index = an index to the layer to replace, set up a system where you have for example, eye colour on slot 1, lip colour on slot 2, tattoos on arms on slot 3, etc. Layers are drawn from smallest number first, so placing your tattoos, scars, etc on layers 1-5 and then clothing on layers 6-10 will allow the clothing to appear over the tattoos.
zeroLayerTexture = the base texture, perhaps a nude skin in some colour, pas something like "base.body.female" , or " brown.body.female ", etc for example
layer_texture_path = full path to a texturte for this layer, for example "base/data/shapes/player/blye_eye.png"
pointx, pointy = where on the base texture the new layer will be drawn, easy way to get this point is to just open you base texture and then check where your new eye texture (tatoo, scar, etc) would align properly with the image on the abse texrure and get the coords at the top/left.
rotation = useful liof you need to rotate the layer somewhat, in degrees 0 - 360
delayed = if you are going to call setLayerTexture a few times to set a few layers, make this "1" for them all and then for the last one you pass "0" to have the actual new texture generated, else a new base texture is created each time you call this fucntion.
%player.updateZeroLayerTexture(zeroLayerTexture, delayed) - I added this funtion to help changing the base texture (normally some nude texture in some colour) where my players could select skin colour, note that this is the same as zeroLayerTexture in setLayerTexture.
This resource can pretty much be used for any texture you need to add to the body, even armour or clothing. If you have a look at the screenshots and the video you will notice I can change the armour, stockings, eye colours, lips, basically anything that can be drawn onto the body. I plan on adding new armour shapes so I need to create an editor that makes use of this resources and mesh hiding and setSninName to get the proper textures into place for certain armours.
I've noticed that this worked pretty well in my player editor but when I try to apply the changes/ layers to a player character after it was created (server side) the changes would not show, only if I add a schedule to delay the changes would they pop up. Now what I think one could do to make this work properly, so you don't need the delay, is to make sure you only apply the layers after the PlayGui onWake was called cause that is where I call my editor's init functions for example and then the player shows up with layers just fine.
[EDIT]
I've found another way of making sure the player's skin updates. Rather than placing some random delay in after creating the new client player I now check for when its ghosted before doing the updates, so in the function you create a new player you would create a schedule that would then check until there is a ghost object and only then update the player.
Have a look at this Blog entry for bigger images and here for a video (10Mb xvid) of this in action inside my character editor.
This resource can be really helpful to get something similar to character editing present in MMOGs but please note that a player will have his own unique texture if you call console/script functions from this resource on that player and that might mean a lot of textures in memory if you got a lot of players. So you might want to go with something a bit more lightweight than this like the setShapeName() function and Mesh Hiding resource.
I started off with this resource http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=9694
but that didn't really have all the functionality I needed. For my game I need the ability to not only change the base texture for a player, but also hair, and extra armour shape textures separately (using mesh hiding), all inside the same DTS. With the mentioned resource even the hair and other mesh data in the DTS would take on the texture you applied to the body. With this resource only the body would be affected and you can still hide and show other mesh data like different hair styles and use setSkinName() to affect those. In fact, you could apply layers of textures to the hair or other mesh data even, separately.
Only two files in the engine to edit ;) I indicated my changes and additions in BOLD. I include the lines of code before and after the change you need to make, as they currently appear in the engine, so that it is easier to find the place to put it in other versions of the engine.
Also notice that I make use of #define/ #ifdef/ #else which helps me disable/enable the resource while testing it but you can remove that if you like. I tend to do this with all resources I implement so that I can easily disable it if something seems to be broken by the resource I implemented without having to go through the code commenting/uncommenting everything.
Open up PLAYER.H and make the changes as indicated.
#ifndef _PLAYER_H_ #define _PLAYER_H_ [b]#define LESLIE_MULTITEXTURE_LAYER // leslie - this enable mod: allow multi texture layers, usefull for tattoos, scars, etc. even armour[/b] #ifndef _SHAPEBASE_H_ #include "game/shapeBase.h"
in struct PlayerData: public ShapeBaseData {
F32 maxLookAngle; ///< Highest angle (radians) the player can look F32 maxFreelookAngle; ///< Max left/right angle the player can look [b] #ifdef LESLIE_MULTITEXTURE_LAYER StringTableEntry playerBasePath; StringTableEntry playerBaseTexture; #endif[/b] ...
in class Player: public ShapeBase - With the following your code might differ somehwhat, especially non 1.5+AFX users, so don't copy paste verbatim.
REPLACE
NextFreeMask = Parent::NextFreeMask << 4WITH
#ifdef LESLIE_MULTITEXTURE_LAYER layerTextureMask = Parent::NextFreeMask << 4, NextFreeMask = Parent::NextFreeMask << 5 #else NextFreeMask = Parent::NextFreeMask << 4 #endif
and few lines down do the following
U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); void unpackUpdate(NetConnection *conn, BitStream *stream); [b] #ifdef LESLIE_MULTITEXTURE_LAYER static const S16 TEXTURELAYERS = 30; // increase/decrease this as you need more/less slots StringTableEntry zeroLayerTexture; // this is your base texture on which the layers are drawn StringTableEntry layerTextureName[TEXTURELAYERS]; // textures to be applied to base textures Point2I pointsList[TEXTURELAYERS]; S32 rotationList[TEXTURELAYERS]; static bool blit(GBitmap* src, GBitmap* dst, Point2I pt, F32 rot ); void setLayerTexture(U32 layer, const char* baseTextureName, const char* texture, Point2I points, S32 rotation, bool delayed); void updateZeroLayerTexture(const char* baseTextureName, bool delayed); bool generateTexture(); #endif [/b]
and that is it for player.h
Next on to PLAYER.CC
In the PlayerData::PlayerData() , at the end.
groundImpactShakeDuration = 1.0; groundImpactShakeFalloff = 10.0; [b] #ifdef LESLIE_MULTITEXTURE_LAYER playerBasePath = ""; playerBaseTexture = ""; #endif[/b] }
In void PlayerData::initPersistFields() , at end.
addField("groundImpactShakeDuration", TypeF32, Offset(groundImpactShakeDuration, PlayerData));
addField("groundImpactShakeFalloff", TypeF32, Offset(groundImpactShakeFalloff, PlayerData));
[b]
#ifdef LESLIE_MULTITEXTURE_LAYER
addField("playerBasePath", TypeString, Offset(playerBasePath, PlayerData));
addField("playerBaseTexture", TypeString, Offset(playerBaseTexture, PlayerData));
#endif[/b]
}near beginning of void PlayerData::packData(BitStream* stream)
void PlayerData::packData(BitStream* stream)
{
Parent::packData(stream);
[b]
#ifdef LESLIE_MULTITEXTURE_LAYER
stream->writeString( playerBasePath );
stream->writeString( playerBaseTexture );
#endif
[/b]
stream->writeFlag(renderFirstPerson);near beginning of void PlayerData::unpackData(BitStream* stream)
void PlayerData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
[b]
#ifdef LESLIE_MULTITEXTURE_LAYER
playerBasePath = stream->readSTString();
playerBaseTexture = stream->readSTString();
#endif
[/b]
renderFirstPerson = stream->readFlag();Player::Player()
Player::Player()
{[b]
#ifdef LESLIE_MULTITEXTURE_LAYER
zeroLayerTexture = "";
for( U32 i = 0; i < TEXTURELAYERS; i++ )
{
rotationList[i] = 0;
pointsList[i].set( 1, 1 );
layerTextureName[i] = "";
}
#endif
[/b]
mTypeMask |= PlayerObjectType;U32 Player::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
U32 Player::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
{
U32 retMask = Parent::packUpdate(con, mask, stream);
[b]
#ifdef LESLIE_MULTITEXTURE_LAYER
if(stream->writeFlag(mask & layerTextureMask))
{
stream->writeString(zeroLayerTexture);
for(int x = 0; x < TEXTURELAYERS; x++)
{
stream->writeString(layerTextureName[x]);
stream->writeInt(pointsList[x].x, 10);
stream->writeInt(pointsList[x].y, 10);
stream->writeInt(rotationList[x], 10);
}
}
#endif[/b]void Player::unpackUpdate(NetConnection *con, BitStream *stream)
void Player::unpackUpdate(NetConnection *con, BitStream *stream)
{
Parent::unpackUpdate(con,stream);
[b]
#ifdef LESLIE_MULTITEXTURE_LAYER
if(stream->readFlag())
{
zeroLayerTexture = stream->readSTString();
for(int x = 0; x < TEXTURELAYERS; x++)
{
layerTextureName[x] = stream->readSTString();
pointsList[x].x = stream->readInt(10);
pointsList[x].y = stream->readInt(10);
rotationList[x] = stream->readInt(10);
}
if(isProperlyAdded()) generateTexture();
}
#endif[/b]and then at the end of the PLAYER.CC file add the following.
//--------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------
// leslie - multi layer texture support
#ifdef LESLIE_MULTITEXTURE_LAYER
// texture_path must be full path including img extension (PNG) .. example, "base/data/shapes/player/scar.png"
// zeroLayerTexture is your base texture onto which the layers will be drawn.. the player base texture or skin
// this one will also allow changing the layer texture without creating the new base texture just yet
// which is good if you want to first set all the layers and then create the new texture with one call (use delayed=1)
// NOTE: use updateZeroLayerTexture (or even setLayerTexture) with delayed=0 to get the actual update to happen
ConsoleMethod( Player, setLayerTexture, void, 9, 9, "(layer_index, zeroLayerTexture, layer_texture_path, pointx, pointy, rotation, delayed)")
{
object->setLayerTexture(dAtoi(argv[2]), argv[3], argv[4], Point2I(dAtoi(argv[5]), dAtoi(argv[6])), dAtof(argv[7]), dAtoi(argv[8]) );
}
// NOTE: pass "none" as textureName and the texture layer will be removed
void Player::setLayerTexture(U32 layer, const char* pZeroLayerTexture, const char* textureName, Point2I points, S32 rotation, bool delayed)
{
if(layer < 0 || layer > TEXTURELAYERS) return;
if (dStrcmp(layerTextureName[layer], textureName)==0) return; // the same, dont bother
if (dStrcmp(textureName,"none")==0)
{ // want to potentially remove this layer's texture from rendering
if (dStrlen(layerTextureName[layer])>0) layerTextureName[layer] = "";
else return; // no need to update, there is no texture here
}
else layerTextureName[layer] = StringTable->insert(textureName);
zeroLayerTexture = StringTable->insert(pZeroLayerTexture);
pointsList[layer] = points;
rotationList[layer] = rotation;
if (!delayed) // see info on setLayerTextureDelayed() for description on what this is
setMaskBits(layerTextureMask); // this causes the new texture to be created eventually
}
// in my game i need to change the base texture a lot. this function will update the base (zeroLayer)
// only so that you dont need to use above function which needs layer info
// note that delayed has similar effect as to description for setLayerTexture
ConsoleMethod( Player, updateZeroLayerTexture, void, 4, 4, "(zeroLayerTexture, delayed)")
{
object->updateZeroLayerTexture(argv[2], dAtoi(argv[3]) );
}
void Player::updateZeroLayerTexture(const char* pZeroLayerTexture, bool delayed)
{
zeroLayerTexture = StringTable->insert(pZeroLayerTexture);
// could prolly optimize this and add another mask for zeroLayer updates,
// but it wont happen a lot in my game after it has started, so i dont care :/
if (!delayed)
setMaskBits(layerTextureMask);
}
//--------------------------------------------------------------------------------------------------------
// function from this resource http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=7650
bool Player::blit(GBitmap* base, GBitmap* layer, Point2I pt, F32 rot)
{
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;
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;
}
bool Player::generateTexture()
{
if(!isGhost()) return false;
GBitmap* baseBitmap;
if( !zeroLayerTexture )
{
Con::errorf(ConsoleLogEntry::General, "zeroLayerTexture invalid [%]", zeroLayerTexture);
return false;
}
if( !mDataBlock->playerBaseTexture )
{
Con::errorf(ConsoleLogEntry::General, "playerBaseTexture invalid [%s]", mDataBlock->playerBaseTexture);
return false;
}
if( !mDataBlock->playerBasePath )
{
Con::errorf(ConsoleLogEntry::General, "playerBasePath invalid [%s]", mDataBlock->playerBasePath);
return false;
}
char fullpath[256]={'/0'};
TSMaterialList* pMatList = mShapeInstance->getMaterialList();
S32 pMatPos = -1;
for (S32 j = 0; j < pMatList->mMaterialNames.size(); j++)
{
const char* pName = pMatList->mMaterialNames[j];
if (pName == NULL) continue;
if (dStrstr(pName, mDataBlock->playerBaseTexture))
{ // found the material (Please note the .JPG here. You either need to always use .JPG for
// all the bases (zeroLayerTexture) of your player or change this to .PNG and only use .PNG
// Just keep in mind that your base must be RGB, not RGBA, so no alpha channel in there ;)
// It is important that your zeroLayerTexture is a name that will allow the below code to
// create a proper path, so if your base texture is "base.body.player" you need to use that
// and not just "base.body" as you would do with the setSkinName(...) function, hope you get the idea :p
pMatPos = j;
dSprintf(fullpath, sizeof(fullpath), "%s%s.jpg", mDataBlock->playerBasePath, zeroLayerTexture);
break;
}
}
baseBitmap = (GBitmap*)ResourceManager->loadInstance( fullpath );
if( baseBitmap == NULL ) {
Con::errorf(ConsoleLogEntry::General, "zeroLayerTexture (%s) invalid file", fullpath);
return false;
}
if( baseBitmap->getFormat() != GBitmap::RGB ) {
Con::errorf(ConsoleLogEntry::General, "zeroLayerTexture (%s) not RGB", fullpath);
return false;
}
for( U32 i = 0; i < TEXTURELAYERS; i++ )
{
if( layerTextureName[i] != NULL && dStrlen(layerTextureName[i])>0 )
{
GBitmap* layerBitmap = (GBitmap*)ResourceManager->loadInstance( layerTextureName[i] );
if( layerBitmap == NULL ) {
Con::errorf(ConsoleLogEntry::General, "layerTextureName (%s) invalid file", layerTextureName[i]);
continue;
}
if( layerBitmap->getFormat() != GBitmap::RGBA ) {
Con::errorf(ConsoleLogEntry::General, "layerTextureName (%s) not RGBA", layerTextureName[i]);
continue;
}
if( !blit( baseBitmap, layerBitmap, pointsList[i], rotationList[i] ) ) {
Con::errorf(ConsoleLogEntry::General, "Unable to blit layerTextureName (%s)", layerTextureName[i]);
continue;
}
delete layerBitmap;
}
}
// NOTE: dunno if making this texture name is of any real use
// could i pass NULL to texture.set(...) ?
// ALSO: might need a way of clearing out the old tetxure before replacing with
// a new one or will Torque handle this?
char texName[50] = "[[628152915ff27]]";
dSprintf(texName, sizeof(texName), "unique_texture_name_%s", this->getIdString());
TextureHandle texture;
texture.set(texName, baseBitmap, MeshTexture);
if (pMatPos > -1)
{
pMatList->mMaterials[pMatPos] = TextureHandle( texture );
}
return true;
}
#endif
// leslie
//--------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------You will notice comments in the above code, READ THEM! :)
In your player datablock (in script) you need to help my code out a little by adding the following.
shapeFile = "base/data/shapes/player/female_player.dts"; [b] playerBaseTexture = "base.body.female"; playerBasePath = "base/data/shapes/player/";[/b]
And then make use of the console functions as you need them.
%player.setLayerTexture(layer_index, zeroLayerTexture, layer_texture_path, pointx, pointy, rotation, delayed) This funtion is used to change the texture layers.
layer_index = an index to the layer to replace, set up a system where you have for example, eye colour on slot 1, lip colour on slot 2, tattoos on arms on slot 3, etc. Layers are drawn from smallest number first, so placing your tattoos, scars, etc on layers 1-5 and then clothing on layers 6-10 will allow the clothing to appear over the tattoos.
zeroLayerTexture = the base texture, perhaps a nude skin in some colour, pas something like "base.body.female" , or " brown.body.female ", etc for example
layer_texture_path = full path to a texturte for this layer, for example "base/data/shapes/player/blye_eye.png"
pointx, pointy = where on the base texture the new layer will be drawn, easy way to get this point is to just open you base texture and then check where your new eye texture (tatoo, scar, etc) would align properly with the image on the abse texrure and get the coords at the top/left.
rotation = useful liof you need to rotate the layer somewhat, in degrees 0 - 360
delayed = if you are going to call setLayerTexture a few times to set a few layers, make this "1" for them all and then for the last one you pass "0" to have the actual new texture generated, else a new base texture is created each time you call this fucntion.
%player.updateZeroLayerTexture(zeroLayerTexture, delayed) - I added this funtion to help changing the base texture (normally some nude texture in some colour) where my players could select skin colour, note that this is the same as zeroLayerTexture in setLayerTexture.
This resource can pretty much be used for any texture you need to add to the body, even armour or clothing. If you have a look at the screenshots and the video you will notice I can change the armour, stockings, eye colours, lips, basically anything that can be drawn onto the body. I plan on adding new armour shapes so I need to create an editor that makes use of this resources and mesh hiding and setSninName to get the proper textures into place for certain armours.
I've noticed that this worked pretty well in my player editor but when I try to apply the changes/ layers to a player character after it was created (server side) the changes would not show, only if I add a schedule to delay the changes would they pop up. Now what I think one could do to make this work properly, so you don't need the delay, is to make sure you only apply the layers after the PlayGui onWake was called cause that is where I call my editor's init functions for example and then the player shows up with layers just fine.
[EDIT]
I've found another way of making sure the player's skin updates. Rather than placing some random delay in after creating the new client player I now check for when its ghosted before doing the updates, so in the function you create a new player you would create a schedule that would then check until there is a ghost object and only then update the player.
function GameConnection::createPlayer ...
{
....
schedule(0,0,"initPlayer", %client, %player);
...
}
function iniPlayer(%client, %player)
{
%ghost = %client.GetGhostIndex(%player);
if (%ghost == -1) schedule(100,0,"initPlayer", %client, %player); // wait little longer
else {
// ... do the player skin changes now
}
}About the author
Recent Blogs
• TGEA GroundCover Terrain Surface Reference• Still around
• KBM update
• Finally some updates
• KBM.. More update
#3
02/02/2007 (5:48 pm)
very cool
#4
02/04/2007 (4:23 am)
This is really nice.
#5
02/04/2007 (12:41 pm)
This is an amazing resource - very well done! :)
#6
Been dying for this resource.
Just goes to show, pay it forward and it will come back to you.
First time I've seen the old Ruin Online skins for Cubix used (even partially) by anyone.
02/04/2007 (6:25 pm)
Heehee, THANK YOU!Been dying for this resource.
Just goes to show, pay it forward and it will come back to you.
First time I've seen the old Ruin Online skins for Cubix used (even partially) by anyone.
#7
Yeah, those old Ruin online skins are coming in real handy for testing stuff in my game :p
02/04/2007 (9:09 pm)
thanks guys :)Yeah, those old Ruin online skins are coming in real handy for testing stuff in my game :p
#9
02/08/2007 (5:42 am)
Good job man. ;)
#10
02/15/2007 (7:11 pm)
Hey, outstanding resource! Just to clarify --- when changing skin/hair/eye colors is it swapping different textures or is it applying a color to a base texture?
#11
I don't know if it's the best way, but it works for me.
02/15/2007 (8:15 pm)
I've been using semi-transparent png files to "tint" my character's body parts.I don't know if it's the best way, but it works for me.
#12
02/15/2007 (8:36 pm)
BrokeAss --- how do you apply the tint? Can you point me to a resource?
#13
02/15/2007 (9:32 pm)
I'm using this resource and I add the semi-transparent png as a layer.
#14
Your layers are drawn from 0 upward, so use lower numbers for things like scars, and higher numbers for clothing. You might want to change TEXTURELAYERS = 30; for your needs, so do check through the code.
Scott, he probably made a translucent png file with the same dimensions as the base texture and then used that as layer 0, which would mean all of the base texture's area would be drawn over, but since the layer is using translucent colour you still see some of the base through it and thus tinted. With some modifications to this resource you could probably use a smaller tint texture and stretch it over the base texture in the drawing code. I would rather just create more base textures in the colour I need, giving me the ability to clean it up nicely where the normal tinting would mess the base up a bit, but it all depends on your implementation and how many tints you want ;)
02/15/2007 (9:38 pm)
This resource will create a new bitmap in memory from your base texture (which should probably be some nude texture) and the apply the layers onto that by drawing the layer textures onto the base. (Player::generateTexture() << in here).Your layers are drawn from 0 upward, so use lower numbers for things like scars, and higher numbers for clothing. You might want to change TEXTURELAYERS = 30; for your needs, so do check through the code.
Scott, he probably made a translucent png file with the same dimensions as the base texture and then used that as layer 0, which would mean all of the base texture's area would be drawn over, but since the layer is using translucent colour you still see some of the base through it and thus tinted. With some modifications to this resource you could probably use a smaller tint texture and stretch it over the base texture in the drawing code. I would rather just create more base textures in the colour I need, giving me the ability to clean it up nicely where the normal tinting would mess the base up a bit, but it all depends on your implementation and how many tints you want ;)
#15
02/16/2007 (8:21 am)
Is this resource network aware? Since there are textures being created dynamically...do the changes on my avatar show up for other clients?
#16
02/21/2007 (9:41 pm)
It should work but I did not test it as the game I am using this for do not need a multi-player session.
#17
04/06/2007 (5:20 am)
Wow, this is seriously cool! thanks for the resource!
#18
06/10/2007 (5:21 pm)
Excellent resource, thank you very much Leslie!
#19
think at the same time i will port it over to teh vehicle classes too. woudl be very cool in there as well for my game.
06/17/2007 (6:38 pm)
this is a good resource, will have to put this in once i get the time.think at the same time i will port it over to teh vehicle classes too. woudl be very cool in there as well for my game.
#20
What size .pngs are you using may I ask?
07/04/2007 (11:45 am)
BrockeAss Games,Quote:I've been using semi-transparent png files to "tint" my character's body parts.
I don't know if it's the best way, but it works for me.
What size .pngs are you using may I ask?

Thx again.
Torque Owner game4Rest
colyd studio
Thank you so much.