Add second texture to geomnode

Hi,

I’m writing a grid of map squares procedurally, adding each square as a single geom and texturing said geom. (This is roughly based on a code snippet someone else posted) Applying the texture to a geom is done by adding a renderattrib to the geomstate. I then flatten everything at the end and seem to get reasonable performance.

What I would like to do is layer on extra textures to some of the tiles to create variation. (IE base grass tile, add second layer for small rocks). I’m struggling with how to define a new renderattrib that adds this second texture as a decal, or even just makes a second texturestage and they are composited.

Here is my code:


self._geomNode = GeomNode('gnode')
        self._size = (100,100)
        self._tiles = [] 
        self._gvd = GeomVertexData('map_maker', GeomVertexFormat.getV3t2(), Geom.UHStatic)   
        self._vertex_writer = GeomVertexWriter(self._gvd, 'vertex') 
        self._texcoord_writer = GeomVertexWriter(self._gvd, 'texcoord')
        
        for x in xrange(0,self._size[0]):
            self._tiles.append([])
            for z in xrange(0,self._size[1]): 
                geom = Geom(self._gvd)
                prim = GeomTriangles(Geom.UHStatic)
                
                self._vertex_writer.addData3f(x, 0, z) 
                self._texcoord_writer.addData2f(0, 0) 
                self._vertex_writer.addData3f(x, 0, z+1) 
                self._texcoord_writer.addData2f(0, 1) 
                self._vertex_writer.addData3f(x+1, 0, z+1) 
                self._texcoord_writer.addData2f(1, 1) 
                self._vertex_writer.addData3f(x+1, 0, z) 
                self._texcoord_writer.addData2f(1, 0)  
                d = ((x*self._size[0]) + z) * 4
                prim.addVertices(d, d + 2, d + 1) 
                prim.addVertices(d, d + 3, d + 2)
                prim.closePrimitive()
                geom.addPrimitive(prim) 
                i = randint(0, len(tiles)-1)
                self._tiles[x].append( {
                                        'geom':geom,
                                        'prim':prim,
                                        'tile':tiles[i],
                                        'geomIndex': d/4
                                        }
                                      ) 
        #rock = loader.loadTexture('assets/img/modifiers/rock.png') 
        #rock.setMagfilter(Texture.FTLinearMipmapLinear) 
        #rock.setMinfilter(Texture.FTLinearMipmapLinear)
        
        for x in self._tiles:
            for z in x:
                self._geomNode.addGeom(z['geom']) 
                self._geomNode.setGeomState(z['geomIndex'], self._geomNode.getGeomState(z['geomIndex']).addAttrib\
                                            (TextureAttrib.make(z['tile']._tex)))

Basically I’d like to add an if statement at the very bottom within the double loop that applies the rock texture as a decal over the geom.

Thanks in advance!

I think the question you’re asking is how to apply multiple textures when you’re using the low-level RenderState interface to construct the state, instead of using the higher-level interface on NodePath.

(Of course, using the NodePath interface, it’s easy–you just call nodePath.setTexture() for each texture you want to add. It’s up to you to construct the appropriate TextureStages, of course.)

There are several ways to approach this. The first way is the most direct: I’ll explain the low-level interface. When you create a TextureAttrib, you can use TextureAttrib.make(tex) to create an attrib that applies a single texture on the default TextureStage. You can also use TextureAttrib.make() (no parameters) to create an attrib that applies no textures.

Once you have a TextureAttrib, you can add additional textures to it with:

attrib = attrib.addOnStage(ts, tex)

Note that you must save the result of addOnStage() in the original attrib variable–this method doesn’t modify the attrib in-place, but instead returns a new attrib that has the new texture added.

Then you can add this attrib to your RenderState the way you are doing now.

Another way to approach this same problem, though, would be to avoid using the low-level interface to states altogether, and simply construct a different GeomNode for each of your Geoms, and apply the needed states to each GeomNode. It all gets flattened down to the same thing anyway after you call flattenStrong(), so this approach doesn’t add any runtime overhead; but it allows you to use the more convenient NodePath interfaces to apply state.

The only downside to this approach is that the flattenStrong() operation will probably be a little bit slower. Whether that’s a problem or not depends on your application, of course.

David

Thanks David! I tried almost that same thing, but never got the addOnStage call to work… Your very clear explanation straightened almost everything out!

I’m now running into alpha blending issues. I’m working with PNG’s and setting TransparencyAttrib.MAlpha on the nodepath before I flatten it. The rock texture is mostly alpha. Here is a screenshot:

Looks like I have two problems - default blend mode is additive and second my alpha is not being recognized. I know these things are addressed in the manual so I’ll go take a look.

Thanks again!

In case anyone is reading through this later:

ts.setMode(TextureStage.MDecal) 

is the call you are looking for.