Student Recreation of "Colossus"
Psychedelic Flythrough of the Mind of a Computer
VR Tetris: grasp the pieces with two VR-gloves

Panda3D Manual: Texture Transforms

It is possible to apply a matrix to transform the (u, v) texture coordinates of a model before rendering. In this way, you can adjust the position, rotation, or scale of a texture, sliding the texture around to suit your particular needs.

Use the following NodePath methods to do this:

nodePath.setTexOffset(TextureStage, uOffset, vOffset)
nodePath.setTexScale(TextureStage, uScale, vScale)
nodePath.setTexRotate(TextureStage, degrees)

If you don't have a particular TextureStage, use TextureStage.getDefault() as the first parameter.

Note that the operation in each case is applied to the (u, v) texture coordinates, not to the texture; so it will have the opposite effect on the texture. For instance, the call nodePath.setTexScale(ts, 2, 2) will effectively double the values of the texture coordinates on the model, which doubles the space over which the texture is applied, and thus makes the texture appear half as large.

The above methods apply a 2-d transform to your texture coordinates, which is appropriate, since texture coordinates are usually two-dimensional. However, sometimes you are working with 3-d texture coordinates, and you really do want to apply a 3-d transform. For those cases, there are the following methods:

nodePath.setTexPos(TextureStage, uOffset, vOffset, wOffset)
nodePath.setTexScale(TextureStage, uScale, vScale, wScale)
nodePath.setTexHpr(TextureStage, h, p, r)

And there is also one generic form:

nodePath.setTexTransform(TextureStage, transform);

This last method sets a generic TransformState object. This is the same kind of 4x4 transform matrix object that you can get from a NodePath via e.g., NodePath.getTransform(). You can also construct a new TransformState via a number of methods like TransformState.makePos(VBase3(0, 1, 0)). If you intend to apply a 2-d transform only, you should restrict yourself to methods like TransformState.makePos2d(VBase2(0, 1)); using only 2-d operations may allow the graphics backend to use a slightly simpler calculation.

Note that the texture transform is associated with a particular TextureStage; it is not a fixed property of the model or its texture coordinates. You can therefore apply a different texture transform to each different TextureStage, so that if you have multiple textures in effect on a particular node, they need not all be in the same place, even if they all use the same texture coordinates. For instance, this technique was used to generate the sample images in the Texture Blend Modes section. In fact, the following code was used to place this sample texture (excerpted):

smiley = loader.loadModel('smiley.egg')
ts = TextureStage('ts')
pattern = loader.loadTexture('color_pattern.png')
smiley.setTexture(ts, pattern)
smiley.setTexScale(ts, 8, 4)
smiley.setTexOffset(ts, -4, -2)

and the resulting texture:

Multitexture sample

In the above example, we have applied a scale of (8, 4) to reduce the size of the decal image substantially, and then we specified an offset of (-4, -2) to slide it around in the positive (u, v) direction to smiley's face (since the (0, 0) coordinate happens to be on smiley's backside). However, these operations affect only the decal image; the original smiley texture is unchanged from its normal position, even though both textures are using the same texture coordinates.