Don't mind the mess!
We're currently in the process of migrating the Panda3D Manual to a new service. This is a temporary layout in the meantime.
In a previous section, we introduced ways to apply an explicit transformation to a model's texture coordinates, with methods like
setTexScale(). In addition to this explicit control, Panda3D offers a simple mechanism to apply an automatic texture transform each frame, as computed from the relative transform between any two nodes.
nodePath.setTexProjector(textureStage, fromNodePath, toNodePath)
When you have enabled this mode, the relative scene-graph transform
the result of
automatically applied as a texture-coordinate transform to the
indicated textureStage. The result is more-or-less as if you executed
the following command every frame:
There is no need for either
toNodePath to have any relation to the nodePath that is
setTexProjector() call; they can be any two
arbitrary NodePaths. If either of them is just
NodePath(), it stands for the top of the graph.
This has several useful applications. We have already introduced
one application, in conjunction
MWorldPosition, to move the generated texture
coordinates from the root of the graph to the model itself.
Interval-animated texture transforms
Another handy application for a TexProjector is to enable the use of the various LerpIntervals to animate a texture transform. Although there are no LerpIntervals that directly animate texture transforms, you can make a LerpInterval animate a NodePath--and then set up a TexProjector effect to follow that NodePath. For example:
smiley = loader.loadModel('smiley.egg') lerper = NodePath('lerper') smiley.setTexProjector(TextureStage.getDefault(), NodePath(), lerper) i = lerper.posInterval(5, VBase3(0, 1, 0)) i.loop()
Note that you don't even have to parent the animated NodePath into the
scene graph. In the above example, we have set up the interval
i to repeatedly move the standalone NodePath
lerper from position (0, 0, 0) to (0, 1, 0) over 5
smiley is assigned a TexProjector that
copies the relative transform from
lerper--that is, the net transform of
lerper--it means we are really animating the texture
smiley from (0, 0) to (0, 1) (the Z
coordinate is ignored for an ordinary 2-D texture).
Another useful application of the TexProjector is to implement projected textures--that is, a texture applied to geometry as if it has been projected from a lens somewhere in the world, something like a slide projector. You can use this to implement a flashlight effect, for instance, or simple projected shadows.
This works because the TexProjector effect does one additional trick:
if the second NodePath in the
happens to be a LensNode, then the TexProjector automatically applies
the lens's projection matrix to the texture coordinates (in addition to applying the relative transform between the nodes).
To implement projected textures, you need to do three steps:
1. Apply the texture you want to the model you want to project it onto, usually on its own TextureStage, so that it is multitextured.
2. Put the
MWorldPosition TexGen mode on the model. This
copies the model's vertex positions into its texture coordinates, for
your texture's TextureStage.
projector is the NodePath to
the LensNode you want to project from.
For your convenience, the NodePath class defines the following method that performs these three steps at once:
nodePath.projectTexture(textureStage, texture, lensNodePath)
For instance, we could use it to project the bamboo texture ("envir-reeds.png") onto the ripple.egg model, like this:
You could move around the projector in the world, or even change the lens field of view, and the bamboo image would follow it. (In the above image, the camera model and the projection lines are made visible only for illustration purposes; normally you wouldn't see them.)
This image was generated with the following code:
from direct.directbase.DirectStart import * from direct.actor import Actor from panda3d.core import * base.setBackgroundColor(1, 1, 1, 1) ripple = Actor.Actor('ripple.egg') ripple.reparentTo(render) ripple.setScale(10) ripple.pose('animation', 17) dl = DirectionalLight('dl') dlnp = camera.attachNewNode(dl) ripple.setLight(dlnp) proj = render.attachNewNode(LensNode('proj')) lens = PerspectiveLens() proj.node().setLens(lens) proj.node().showFrustum() proj.find('frustum').setColor(1, 0, 0, 1) camModel = loader.loadModel('camera.egg') camModel.reparentTo(proj) proj.reparentTo(render) proj.setPos(1.5, -7.3, 2.9) proj.setHpr(22, -15, 0) tex = loader.loadTexture('maps/envir-reeds.png') tex.setWrapU(SamplerState.WMBorderColor) tex.setWrapV(SamplerState.WMBorderColor) tex.setBorderColor((1, 1, 1, 0)) ts = TextureStage('ts') ts.setSort(1) ts.setMode(TextureStage.MDecal) ripple.projectTexture(ts, tex, proj) base.disableMouse() camera.setPos(-7.8, -22.4, 0) camera.setHpr(-21, 0, 0) base.graphicsEngine.renderFrame() base.screenshot('projected_bamboo.jpg', defaultFilename=0)