Game Development Community

dev|Pro Game Development Curriculum

Making skins work more than once in TGEA 1.8.1

by Charles Fusner ("Cog3125") · 06/04/2009 (4:48 pm) · 7 comments

While experimenting with TGEA 1.8.1, I found that setSkinName, while it did change whole materials, not just textures, would only work one time. I found this was a known issue and in this thread...

http://www.garagegames.com/community/forums/viewthread/74084

...I got pointed in the direction of the right code to look at. I came up with a generalized solution that worked fairly well for me, and posted it there. Recently, I got some feedback (thanks Andrew) that it is more straightforward than some other public solutions that have been proposed, so basically, this is just a reformat of those instructions as a public resource to make it a little easier to find for anyone who might still need it.

Step A:
In tsShapeInstance.h, find the definition for the TSShapeInstance class, and near the beginning add a new private member variable

// Add me just before the first "public:" in the 
// class definition for TSShapeInstance...
private:
     StringTableEntry mSkinName;

Step B:
In tsShapeInstance.cpp, find the method TSShapeInstance::buildInstanceData (there are multiple constructors, but they both call this method). Near the end of this method, add an initialization for our new member as follows...

mSkinName = StringTable->insert("base", false);

Step C:
Also in tsShapeInstance.cpp, find the method TSShapeInstance::reSkin. There are several simple, but key changes for this method...

1.) Near the very top, find the line which reads

const char* defaultBaseName = "base";

... and get rid of it. We're going to be substituting our new member variable for all instances of this variable, so it's now obsolete. Substituting a StringTableEntry works, because StringTableEntry is typed as a const char * anyway, and has the incidental side benefit that string storage space is minimized for multiple instances of this TSShapeInstance which wear the same skin.
So, of course, our next changes involve getting rid of references to that old variable.

2.) Scroll down a bit, and look for the lines

else
    newBaseName = String(defaultBaseName);

And replace it with...

else
    newBaseName = String(mSkinName);

3.) Scroll down a bit further this time, and find the lines that read...

bool replacedRoot = makeSkinPath(pathName, NAME_BUFFER_LENGTH,
                                 resourcePath, pName, defaultBaseName, 
                                 newBaseName);


Replace this with...

bool replacedRoot = makeSkinPath(pathName, NAME_BUFFER_LENGTH, 
                                 resourcePath, pName, mSkinName, 
                                 newBaseName);


Step D:
Lastly, we need to set the mSkinName member with the name of the new base for next time.

Be careful where you put this line! Make sure you insert it at the VERY END OF THE METHOD, OUTSIDE THE FOR LOOP. If you put it anywhere inside the for loop, you create an unfortunate bug where if the object has multiple materials to be skinned only the first one actually changes.

So, at the very end of the method, add...

mSkinName = StringTable->insert(newBaseName,false);

Now, when the first call to reSkin is made, mSkinName contains "base" (from the constructor), which is appropriate, since all textures to be skinned should start out this way. Each time a re-skin is performed, mSkinName is set to the last thing we changed to, so it keeps working no matter how many re-skins you do.

#1
06/04/2009 (5:42 pm)
great thx :)

This change also works in AFX 1.8.1

just a couple of point to note.

getskinname now returns NULL and not the skinname

if you accedently setskinname(..) with an incorrect skinname, then try it with a correct skinname its broke again.

so if you,
%obj.setskinname("base"); //ok
%obj.setskinname("green"); //ok
%obj.setskinname("red"); //ok
%obj.setskinname("blu"); //(typo should be blue) texture dosn't change
%obj.setskinname("green"); // %obj is still red
%obj.setskinname("base"); // %obj still red

took me a while to realise I had a typo and was wondering why setskinname had broke again.

Just make sure you name all your skins correctly :-)

A great fix
#3
06/09/2009 (1:26 pm)
Quick question about exporting the multiple textures. In 3dsMax I have an object with a texture applied to it called 'base.house2.png'. I exported it as is then opened the base.house2.png in photoshop, tinted it purple, and saved it as 'purple.house2.png'. Should I be applying the texture in MAX somehow?
P.S. When I try obj.setSkinName("purple") the object turns invisible.
I have a material.cs in the same folder as the DTS and textures that looks like this:
new Material(House2Base)
{
   mapTo = "base";
   baseTex[0] = "base";
};

new Material(House2Purple)
{
   mapTo = "purple";
   baseTex[0] = "purple";
};
#4
06/09/2009 (2:43 pm)
@Jeff Brown

Hi

try this and see if it works, change your material.cs to:-
new Material(House2Base)   
{   
   mapTo = "base.house2";   
   baseTex[0] = "base.house2";   
};   
  
new Material(House2Purple)   
{   
   mapTo = "purple.house2";   
   baseTex[0] = "purple.house2";   
};
you need to tell it the whole texture name else it could get confused with say. "purple.rock.jpg"
then try:-
obj.setSkinName("purple");

obj.setSkinName("base");
and see if that works :-)
#5
06/09/2009 (10:11 pm)
@David - It works perfectly, thanks a lot.
#6
08/24/2009 (12:41 am)
using a debug exe, when changing the texture, on exit i found this error:

"There is a texture object leak, check the log for more details."

gfxtextureobject.cpp @38
#7
08/24/2009 (1:13 am)
this work for me, (with this resource i can change only one time):
www.garagegames.com/community/forums/viewthread/58560/1#comment-668147