SetSkinName only works onetime?
by Kiyaku · in Torque Game Engine Advanced · 04/09/2008 (10:46 am) · 14 replies
Hi,
i use setSkinName to change the skins of my models (woohoo, who guessed).
Anyway, it works perfectly when i assign a skin the first time, but after it, i can't reskin it anymore.
Do i first have to free the skin somehow?
i use setSkinName to change the skins of my models (woohoo, who guessed).
Anyway, it works perfectly when i assign a skin the first time, but after it, i can't reskin it anymore.
Do i first have to free the skin somehow?
#2
04/09/2008 (11:12 am)
Yes 1.7
#3
When you reskin something, the shape needs to make a clone of the original resource's material list. In the past, when it went to replace an item in this list, it would only replace the texture handle. But TGEA actually replaces the whole material entry. When it does this, the original name of the material no longer exists in the material list and so when you go to do another reskin it can't find that name (which is used as a key essentially), and thus fails to reskin the shape.
I've fixed this in the project I'm working on by getting both the cloned material list and the original material list. I then loop over the original material list looking for the name (the key). When it finds that entry, it remembers the index and then replaces the material at that index on the cloned material list. Since I use the original material list for the lookup, it will always find the base name (which is key to figuring out which index in the list to replace).
Even though this solves the issue, I don't like it for a number of reasons. For instance, I'm not guaranteed that the original material list and the cloned material list will be in the same order when I need to reskin a shape. However, for the most part this should resolve the skinning issue you are seeing.
04/09/2008 (4:54 pm)
TGEA's material system changed in various ways from TGE resulting in this bug. To hackishly fix the issue, you will have to change how TSShapeInstance::reSkin looks up the names from the material list.When you reskin something, the shape needs to make a clone of the original resource's material list. In the past, when it went to replace an item in this list, it would only replace the texture handle. But TGEA actually replaces the whole material entry. When it does this, the original name of the material no longer exists in the material list and so when you go to do another reskin it can't find that name (which is used as a key essentially), and thus fails to reskin the shape.
I've fixed this in the project I'm working on by getting both the cloned material list and the original material list. I then loop over the original material list looking for the name (the key). When it finds that entry, it remembers the index and then replaces the material at that index on the cloned material list. Since I use the original material list for the lookup, it will always find the base name (which is key to figuring out which index in the list to replace).
Even though this solves the issue, I don't like it for a number of reasons. For instance, I'm not guaranteed that the original material list and the cloned material list will be in the same order when I need to reskin a shape. However, for the most part this should resolve the skinning issue you are seeing.
#4
I get what you are saying, though my knowledge about changing engine based functions is limited yet since i just started learning torque some days ago.
Could you give me some hints, tips, tricks or recommand some recources that could help to rewrite the reSkin function? I would appreciate that a lot.
Greetings,
Sven
04/09/2008 (11:18 pm)
Thanks a lot Robert for this detailed description about the problem.I get what you are saying, though my knowledge about changing engine based functions is limited yet since i just started learning torque some days ago.
Could you give me some hints, tips, tricks or recommand some recources that could help to rewrite the reSkin function? I would appreciate that a lot.
Greetings,
Sven
#5
This is a quick and dirty change I made to reSkin in TGEA 1.0.1.
I added some code just before the call to makeSkinPath and
I changed one argument for the call to makeSkinPath.
04/10/2008 (9:40 am)
Kiyaku,This is a quick and dirty change I made to reSkin in TGEA 1.0.1.
I added some code just before the call to makeSkinPath and
I changed one argument for the call to makeSkinPath.
for (S32 j = 0; j < pMatList->mMaterialNames.size(); j++) {
// Get the name of this material.
const char* pName = pMatList->mMaterialNames[j];
// Bail if no name.
if (pName == NULL) {
continue;
}
// ======== added this ============================
// There was a problem were swapping skin would only work
// the first time. That was because in the call below to
// makeSkinPath, the defaultBaseName - 'base' was being
// passed in as the oldBaseName. This means that if the
// current texture's base name was not 'base' then it
// would not replace the old base/root name with the new
// root/base name. I added this to extract the current
// basename and use it as the old base name.
char oldBaseName[64];
oldBaseName[0] = '[[6281bb0a010a6]]';
if(strchr(pName, '.')) // If the name contains a '.'
{
int len = strcspn(pName, "."); // How long is the base name?
if(len) // If the name does not strart with a '.'
{
strncpy(oldBaseName, pName, len); // Copy the base name
oldBaseName[len] = '[[6281bb0a010a6]]'; // And make it a string
}
}
// ==== down to here ===============================
// Make a texture file pathname with the new root if this name
// has the old root in it; otherwise just make a path with the
// original name.
bool replacedRoot = makeSkinPath(pathName, NAME_BUFFER_LENGTH, resourcePath,
pName, oldBaseName, newBaseName); //defaultBaseName, newBaseName);
// ============ replaced ^^^^^^ =============================
#6
thanks a lot for the code.
I put it inot the reskin function at the same position as you did and also renamed the defaultBaseName to oldBaseName. Then i compiled the whole project but it's still not working somehow.
Did you already try the code in 1.7?
04/10/2008 (10:49 am)
Hi,thanks a lot for the code.
I put it inot the reskin function at the same position as you did and also renamed the defaultBaseName to oldBaseName. Then i compiled the whole project but it's still not working somehow.
Did you already try the code in 1.7?
#7
Kiyaku,
No, I have not merged my changes with TGEA 1.7 yet, but I did do a compare and both functions reSkin and makeSkinPath do not seem to have changed from 1.0.1 to 1.7.
Now, I assume when you say that it still not working, that you mean it still just allows you to swap skins once. And I assume when you say that you renamed defaultBaseName you mean you just replaced the fifth argument in the call to makeSkinPath with oldBaseName.
* Check to make sure that the skin file names are of the form.
If for example your texture name is icon.png then you might have files called
base.icon.png
red.icon.png
blue.icon.png
* Check to make sure that all of these skins are in the same directory.
A quick way to try swapping skins would be to run the game, go into the editor with F11, click on the object to get it's id number. If for example that id number was 2580 then open the console and using the example skins above enter:
2580.setSkinName("red"); hit return
then enter
2580.setSkinName("blue"); hit return
If all else fails then run a debug build of the engine with a breakpoint set on the call to makeSkinPath. When you execute your version of 2580.setSkinName("red"); in the console then you should get breakpoint and oldBaseName should equal "base" and newBaseName should equal "red" (your new base name). After executing your version of 2580.setSkinName("blue"); in the console then you should get breakpoint and oldBaseName should equal "red" (your first base name) and newBaseName should equal "blue" (your new base name).
Let me know if you are still having problems.
04/10/2008 (8:21 pm)
EDIT: Sorry if this sounds overly simplistic, and I hope it makes sense.Kiyaku,
No, I have not merged my changes with TGEA 1.7 yet, but I did do a compare and both functions reSkin and makeSkinPath do not seem to have changed from 1.0.1 to 1.7.
Now, I assume when you say that it still not working, that you mean it still just allows you to swap skins once. And I assume when you say that you renamed defaultBaseName you mean you just replaced the fifth argument in the call to makeSkinPath with oldBaseName.
* Check to make sure that the skin file names are of the form
If for example your texture name is icon.png then you might have files called
base.icon.png
red.icon.png
blue.icon.png
* Check to make sure that all of these skins are in the same directory.
A quick way to try swapping skins would be to run the game, go into the editor with F11, click on the object to get it's id number. If for example that id number was 2580 then open the console and using the example skins above enter:
2580.setSkinName("red"); hit return
then enter
2580.setSkinName("blue"); hit return
If all else fails then run a debug build of the engine with a breakpoint set on the call to makeSkinPath. When you execute your version of 2580.setSkinName("red"); in the console then you should get breakpoint and oldBaseName should equal "base" and newBaseName should equal "red" (your new base name). After executing your version of 2580.setSkinName("blue"); in the console then you should get breakpoint and oldBaseName should equal "red" (your first base name) and newBaseName should equal "blue" (your new base name).
Let me know if you are still having problems.
#8
the names of the skins are correct, i checked that before with changing the skins over the ingame console (though i had to do it on different blocks cause i can't change the skin more than once).
I also tried the same code and model with the same skins in TGE and it worked perfectly.
So the only thing that i could try is the debug build as you mentioned. I'm gonna do that and see if it bring any results.
Greetings,
Kiyaku
04/10/2008 (10:19 pm)
Hi Mary,the names of the skins are correct, i checked that before with changing the skins over the ingame console (though i had to do it on different blocks cause i can't change the skin more than once).
I also tried the same code and model with the same skins in TGE and it worked perfectly.
So the only thing that i could try is the debug build as you mentioned. I'm gonna do that and see if it bring any results.
Greetings,
Kiyaku
#9
04/11/2008 (9:36 am)
Okay i got it to work, your code works perfectly now, thanks a lot again!
#10
Just post here about how you solved it, for the next person that finds this in 6 months.
04/11/2008 (10:20 am)
OK, great! Just post here about how you solved it, for the next person that finds this in 6 months.
#11
The code itself works perfectly!
04/14/2008 (6:49 am)
Actually it was a problem with my files that they couldn't write the new exe but it gave no error or warning or something ^^'The code itself works perfectly!
#12
Step A:
In tsShapeInstance.h, find the definition for the TSShapeInstance class, and near the beginning add a new private member variable
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...
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
... 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
And replace it with...
3.) Scroll down a bit further this time, and find the lines that read...
Replace this with...
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...
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.
03/30/2009 (5:12 pm)
In case anyone is still actively seeking answers on this,here's a solution I've been experimenting with in TGEA 1.8.1. There's several steps (in 2 files you need to modify) so hopefully I don't run up against a word limit describing it, but I'll try to be brief w/o being obscure :)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.
#13
06/02/2009 (10:13 pm)
Charles, thanks for the fix! Works nicely, and looks to be way less involved than the others in the resources. (Using 1.8.1, here.) Have you thought about putting that up in Resources?
#14
I'm glad to hear someone confirm this worked for them. I know I kinda came into this discussion pretty late into the game, but I was hoping someone could still get some value out of it anyway.
06/04/2009 (4:52 pm)
Hey, thanks for the feedback, Andrew! You're absolutely right, it probably would be a lot easier to find if I formally posted it as a resource (which I'll do now). I'm glad to hear someone confirm this worked for them. I know I kinda came into this discussion pretty late into the game, but I was hoping someone could still get some value out of it anyway.
Associate Scott Burns
GG Alumni