I am trying to implement something I’ve seen in some commercial games. Basically a single model is used for a character (say a soldier). But there are several texture sets which are made for the model, so you can have differnt teams,colors, etc.
The textures are stored in a subfolder under the model’s path.
What I’d like to do is programmatically allow the user select which texture to use by letting them specify a folder.
Now I haven’t tried it yet, but think I figure out the setTexture sample code in the manual to switch this for simple models with 1 texture if it’s always the same filename. seems simple enough.
But if some models have multiple textures, is there a way I can just change the paths used by all textures in a model?
Say the default textures are:
/std/body.tga
/std/helmet.tga
/std/boots.tga
the user chooses to use the set in /green_soldier/
I would need to load and set:
/green_soldier/body.tga
/green_soldier/helmet.tga
/green_soldier/boots.tga
so whats the best approach ? (assuming I don’t know beforehand the name of the texture files or the number of files a model uses)
for example, if x is an Actor I’ve constructed:
print x.findAllTextures returns:
male_basic_bump
male_basic
which is the name of the 2 textures on this model (without the file extension, .png or paths)
print x.getTexture() returns:
None
As per my previous post, is there a way to return a list of the parts in the model that have textures? I could then interate through the list , updating the texture (using setTexture) for each with a new user-selected path.
It’s possible to iterate through all the parts that have textures, but you’d have to flag them all anyway. If you’re going to flag them all, you already know what they are, don’t you? Why not just iterate through the list of names that you flagged?
The answer is different depending on whether you’re talking about Actors or ordinary models. With an Actor, you need to “flag” all the pieces that you will modify individually; otherwise all the pieces will all be combined into a single GeomNode. You would do this by passing the egg file through egg-optchar, with the -flag option. I believe this is described in the manual. With an ordinary model, you (usually) don’t have to do this.
That being said, it is also possible to iterate through the model and find just the textures, as you suggested, and replace them one-at-a-time, even if you have not flagged any pieces. It is a little complicated, though, because you have to use the low-level state interface to pull out the individual states within a single GeomNode. Still, this code (untested) ought to do the trick. It should work equally well for an Actor or for an ordinary model.
for np in model.findAllMatches('**/+GeomNode'):
gn = np.node()
for gi in range(gn.getNumGeoms()):
state = gn.getGeomState(gi)
ta = state.getAttrib(TextureAttrib.getClassType())
if ta:
for si in range(ta.getNumOnstages()):
stage = ta.getOnStage(si)
tex = ta.getOnTexture(stage)
filename = tex.getFilename()
filename2 = Filename('/my/new/path', filename.getBasename())
tex2 = loader.loadTexture(filename2)
ta = ta.addOnStage(ta.addOnStage(stage, tex2))
state = state.setAttrib(ta)
gn.setGeomState(gi, state)
The code worked great!
I’m going to make it a callable function and put in some error-checking.
There were a couple of line I had to chg to get it to run, in case anyone needs something like this in the future (see below - I commented out the lines I changed and my corrected line is underneath - just typos i think)
for np in model.findAllMatches('**/+GeomNode'):
gn = np.node()
for gi in range(gn.getNumGeoms()):
state = gn.getGeomState(gi)
ta = state.getAttrib(TextureAttrib.getClassType())
if ta:
#for si in range(ta.getNumOnstages()):
for si in range(ta.getNumOnStages()):
stage = ta.getOnStage(si)
tex = ta.getOnTexture(stage)
filename = tex.getFilename()
filename2 = Filename('art/characters/batman/skins/standard', filename.getBasename())
tex2 = loader.loadTexture(filename2)
#ta = ta.addOnStage(ta.addOnStage(stage, tex2))
ta = ta.addOnStage(stage, tex2)
state = state.setAttrib(ta)
gn.setGeomState(gi, state)
The final code above is using addOnStage, so my understanding (which could be wrong) is that this is applying a second texture to the geomnode?
Is there a way I could simply replace the texture on the geomnode completely , not add a second texture. I use some gloss/normal maps and have noticed some strange effects , which I’m thinking may be caused by layering these textures rather than completely replacing them.
Still looking at this.
Specifically, it seems that the basic (diffuse) texture I use is replaced OK, but I also have normal(bump) and gloss (reflection) textures which are specified in the egg file.
When I use the code above, I still see the effects of those textures in the model, even though I have a different set of normal and gloss textures in the new folder I’m specifying.
Not sure if those type of textures need to be handled differently? Or is there a way to explicitly clear them before I call addonStage for the new texture?
You have to find all of the TextureStages on the model. Each TextureStage can have a different texture. If you apply only a texture with the one-parameter setTexture() call, you have only replaced the default TextureStage, without changing the others.
The code you paste above is a very clumsy way to find all of the TextureStages. An easier way is to use model.findAllTextureStages().
Read up about multitexture in the manual to learn more about the way TextureStages work.
I think the problem I see (strange effects of bump or glow maps) is being caused by textures being applied in the wrong order.
Basically, the code above does not guarantee what order it will loop through e texturestages in.
Not at my pc right now, so I can’t try solutions (will try later)
But is there a way to make sure the list of stages I get back is always in the same order?