I have a model in .bam format that I created in Blender. I assigned it UV coordinates and a texture using Blender. It looks fine in Panda3D, until I try switching out the texture for another image with a different color scheme. With the following code, the new texture is multiplied by the old one, even though it should completely replace the old texture:
buoyModel = loader.loadModel('buoy.bam')
tex = loader.loadTexture('buoy_yellow.png')
ts = TextureStage('ts')
buoyModel.setTexture(ts, tex, 0)
Anyone know what I’m doing wrong? Thanks!
I’ve found that I can replace the texture by modifying the RenderState of the model’s Geom:
buoyModel = loader.loadModel('buoy.egg')
tex = loader.loadTexture('buoy_green.png')
texAttrib = TextureAttrib.make(tex)
geomNode = buoyModel.find('**/Sphere').node()
newRenderState = geomNode.getGeomState(0).addAttrib(texAttrib, 1)
As expected, this completely replaces the original texture. After I do this, however, calling buoyModel.setTexture(anotherTex, 2) does nothing, even if I set the Geom’s TextureAttrib to a blank one (TextureAttrib.make() or TextureAttrib.makeDefault()). Is this a bug? I’d like to be able to change the texture without digging in to the Geoms.
In order to completely replace an existing texture on a model, you have to have a pointer to the TextureStage that was used to apply the texture to the model, and use that same TextureStage.
Since your example code creates a new TextureStage, it will of course add to the texture stack, rather than replacing the existing texture. Think of each TextureStage as a slot to hold a texture. If you want to replace a particular texture, you have to store it in the same slot. Each time you apply a new TextureStage, you are stacking a new texture slot into the box.
Normally, if your model doesn’t apply any custom-named UV’s or any other fancy texture properties, then the egg loader will apply its textures to the default TextureStage, which is why you can usually override a texture on a simple model with simply model.setTexture(newTex, 1), which also uses the default TextureStage.
Since your model uses custom-named UV’s, it will have a custom TextureStage implicitly created to apply this texture. So you can find that TextureStage in the model and reuse it with something like:
ts = model.findTextureStage('*')
model.setTexture(ts, tex, 1)
Note that the above also works for the case that the model uses the default TextureStage.
Hey David, thanks for help. I didn’t realize that my exported model had a special TextureStage.
For anyone else having this problem, you might find the following code handy. It removes textures from every node in a model, so setTexture(tex) will work.
from panda3d.core import GeomNode, TextureAttrib
''' Remove the textures from every node at and under the given NodePath. '''
node = model.node()
if isinstance(node, GeomNode):
for i in xrange(node.getNumGeoms()):
renderState = node.getGeomState(i)
newRenderState = renderState.removeAttrib(TextureAttrib)
for child in model.getChildren():
You can also simply do:
The first line disables all textures at this node and below; the second line sets only myTex. The same override parameter, 1 in this case, must be supplied to both functions; the override value must be higher than 0 (or whatever is already in the model) in order to make your new texture operation supercede the textures already on the model.
Hmm, when I try using
I just get an all-white model. It seems like the second call doesn’t take.
from direct.directbase.DirectStart import *
smiley = loader.loadModel('smiley.egg')
grid = loader.loadTexture('maps/color-grid.rgb')
Do you see a white sphere, or one with a grid applied to it? I see a grid.
I see a grid, and this works for my model, too. I realized that I’m actually turning the texture off on the model, and then setting the new texture on a nodepath a couple levels above the model. Of course, the “off” setting lower in the tree was overriding the texture I was setting above it.
Thanks for the help!