Automatic Texture Coordinates
In addition to using texture coordinates that are built into the model, it is also possible to generate texture coordinates at runtime. Usually you would use this technique to achieve some particular effect, such as projective texturing or environment mapping, but sometimes you may simply want to apply a texture to a model that does not already have texture coordinates, and this is the only way to do that.
The texture coordinates generated by this technique are generated on-the-fly, and are not stored within the model. When you turn off the generation mode, the texture coordinates cease to exist.
Use the NodePath.set_tex_gen()
method to enable automatic generation of
texture coordinates:
nodePath.setTexGen(textureStage, texGenMode)
The texGenMode parameter specifies how the texture coordinates are to be computed, and may be any of the following options. In the list below, “eye” means the coordinate space of the observing camera, and “world” means world coordinates, e.g. the coordinate space of render, the root of the scene graph.
- TexGenAttrib.MWorldPosition
Copies the (x, y, z) position of each vertex, in world space, to the (u, v, w) texture coordinates.
- TexGenAttrib.MEyePosition
Copies the (x, y, z) position of each vertex, in camera space, to the (u, v, w) texture coordinates.
- TexGenAttrib.MWorldNormal
Copies the (x, y, z) lighting normal of each vertex, in world space, to the (u, v, w) texture coordinates.
- TexGenAttrib.MEyeNormal
Copies the (x, y, z) lighting normal of each vertex, in camera space, to the (u, v, w) texture coordinates.
- TexGenAttrib.MEyeSphereMap
Generates (u, v) texture coordinates based on the lighting normal and the view vector to apply a standard reflection sphere map.
- TexGenAttrib.MEyeCubeMap
Generates (u, v, w) texture coordinates based on the lighting normal and the view vector to apply a standard reflection cube map.
- TexGenAttrib.MWorldCubeMap
Generates (u, v, w) texture coordinates based on the lighting normal and the view vector to apply a standard reflection cube map.
- TexGenAttrib.MPointSprite
Generates (u, v) texture coordinates in the range (0, 0) to (1, 1) for large points so that the full texture covers the square. This is a special mode that should only be applied when you are rendering sprites, special point geometry that are rendered as squares. It doesn’t make sense to apply this mode to any other kind of geometry. Normally you wouldn’t set this mode directly; let the SpriteParticleRenderer do it for you.
Note that several of the above options generate 3-D texture coordinates: (u, v, w) instead of just (u, v). The third coordinate may be important if you have a 3-D texture or a cube map (described later), but if you just have an ordinary 2-D texture the extra coordinate is ignored. (However, even with a 2-D texture, you might apply a 3-D transform to the texture coordinates, which would bring the third coordinate back into the equation.)
Also, note that almost all of these options have a very narrow purpose; you would generally use most of these only to perform the particular effect that they were designed for. This manual will discuss these special-purpose TexGen modes in later sections, as each effect is discussed; for now, you only need to understand that they exist, and not worry about exactly what they do.
The mode that is most likely to have general utility is the first one:
MWorldPosition
. This mode converts each vertex’s (x, y, z) position into
world space, and then copies those three numeric values to the (u, v, w) texture
coordinates. This means, for instance, that if you apply a normal 2-D texture to
the object, the object’s (x, y) position will be used to look up colors in the
texture.
For instance, the teapot.egg sample model that ships with Panda has no texture coordinates built into the model, so you cannot normally apply a texture to it. But you can enable automatic generation of texture coordinates and then apply a texture:
teapot = loader.loadModel('teapot.egg')
tex = loader.loadTexture('maps/color-grid.rgb')
teapot.setTexGen(TextureStage.getDefault(), TexGenAttrib.MWorldPosition)
teapot.setTexture(tex)
And you end up with something like this:
You can use this in conjunction with a texture transform to further manipulate the texture coordinates. For instance, to rotate the texture 90 degrees, you could do something like this:
teapot.setTexTransform(TextureStage.getDefault(), TransformState.makeHpr((0, 90, 0)))
Finally, consider that the only two choices for the coordinate frame of the texture coordinate generation are “world” and “eye”, for the root NodePath and the camera NodePath, respectively. But what if you want to generate the texture coordinates relative to some other node, say the teapot itself? The above images are all well and good for a teapot that happens to be situated at the origin, but suppose we want the teapot to remain the same when we move it somewhere else in the world?
If you use only MWorldPosition
, then when you change the teapot’s position,
for instance by parenting it to a moving node, the teapot will seem to move
while its texture pattern stays in place–maybe not the effect you had in mind.
What you probably intended was for the teapot to take its texture pattern along
with it as it moves around. To do this, you will need to compute the texture
coordinates in the space of the teapot node, rather than in world space.
Panda3D provides the capability to generate texture coordinates in the
coordinate space of any arbitrary node you like. To do this, use
MWorldPosition
in conjunction with Panda’s “texture projector”, which
applies the relative transform between any two arbitrary NodePaths to the
texture transform; you can use it to compute the relative transform from world
space to teapot space, like this:
teapot.setTexGen(TextureStage.getDefault(), TexGenAttrib.MWorldPosition)
teapot.setTexProjector(TextureStage.getDefault(), render, teapot)
It may seem a little circuitous to convert the teapot vertices to world space to
generate the texture coordinates, and then convert the texture coordinates back
to teapot space again–after all, didn’t they start out in teapot space? It
would have saved a lot of effort just to keep them there! Why doesn’t Panda just
provide an MObjectPosition
mode that would convert texture coordinates from
the object’s native position?
That’s a fair question, and MObjectPosition
would be a fine idea for a model
as simple as the teapot, which is after all just one node. But for more
sophisticated models, which can contain multiple sub-nodes each with their own
coordinate space, the idea of MObjectPosition
is less useful, unless you
truly wanted each sub-node to be re-textured within its own coordinate space.
Rather than provide this feature of questionable value, Panda3D prefers to give
you the ability to specify the particular coordinate space you had in mind,
unambiguously.
Note that you only want to call NodePath.set_tex_projector()
when you
are using mode MWorldPosition
. The other modes are generally computed from
vectors (for instance, normals), not positions, and it usually doesn’t make
sense to apply a relative transform to a vector.