Common State Changes
This page lists some of the most common changes you can make to a 3D node. This
page is really only a quick cheat-sheet summary: the detailed documentation for
these operations comes later in the manual. A full list of manipulations can be
found on the API reference page for the NodePath
class.
Positioning Nodes
Two of the most common changes are position and orientation.
myNodePath.setPos(X, Y, Z)
myNodePath.setHpr(Yaw, Pitch, Roll)
By default in Panda3D, the X axis points to the right, the Y axis is forward,
and Z is up. An object’s rotation is usually described using Euler angles called
Heading, Pitch, and Roll (sometimes called Yaw, Pitch, and Roll in other
packages)–these specify angle rotations in degrees. (If you are more
comfortable using quaternions, the setQuat()
method can be
used to specify the rotation as a quaternion.)
You can change an object’s size, either uniformly, or with a different value of x, y, and z.
myNodePath.setScale(S)
Sometimes it is convenient to adjust a single component individually:
myNodePath.setX(X)
myNodePath.setY(Y)
myNodePath.setZ(Z)
myNodePath.setH(H)
myNodePath.setP(P)
myNodePath.setR(R)
myNodePath.setSx(SX)
myNodePath.setSy(SY)
myNodePath.setSz(SZ)
Or all at the same time:
myNodePath.setPosHprScale(X, Y, Z, H, P, R, SX, SY, SZ)
You can also query the current transform information for any of the above:
myNodePath.getPos()
myNodePath.getX()
myNodePath.getY()
myNodePath.getZ()
As a more advanced feature, you may also set or query the position (or any of the above transform properties) of a particular NodePath with respect to another one. To do this, specify the relative NodePath as the first parameter:
myNodePath.setPos(otherNodePath, X, Y, Z)
myNodePath.getPos(otherNodePath)
Putting a NodePath as the first parameter to any of the transform setters or
getters makes it a relative operation. The above setPos()
means to set myNodePath to the position (X, Y, Z), relative to otherNodePath–
that is, the position myNodePath would be in if it were a child of otherNodePath
and its position were set to (X, Y, Z). The getPos()
call
returns the position myNodePath would have if it were a child of otherNodePath.
It is also important to note that you can use the NodePath in its own relative sets and gets. This may be helpful in situations where you are concerned with distances. For example:
# Move myNodePath 3 units forward in the x
myNodePath.setPos(myNodePath, 3, 0, 0)
These relative sets and gets are a very powerful feature of Panda’s scene graph, but they can also be confusing; don’t worry if it doesn’t make sense right now.
The lookAt()
method rotates a model to face another object;
that is, it rotates the first object so that its +Y axis points toward the
second object. Note that a particular model might or might not have been
generated with the +Y axis forward, so this doesn’t necessarily make a model
“look at” the given object.
myNodePath.lookAt(otherObject)
Tip
If you have trouble to place, scale or rotate your nodes you can use the
place()
function to bring up a small GUI which will help you. You need to
have Tkinter installed to use it.
myNodePath.place()
Changing the Parent
One of the most fundamental scene graph manipulations is changing a node’s parent. You need to do this at least once after you load a model, to put it under render for viewing:
myModel.reparentTo(render)
As you become more comfortable with scene graph operations, you may find yourself taking more and more advantage of a deeply nested scene graph, and you may start to parent your models to other nodes than just render. Sometimes it is convenient to create an empty node for this purpose, for instance, to group several models together:
dummyNode = render.attachNewNode("Dummy Node Name")
myModel.reparentTo(dummyNode)
myOtherModel.reparentTo(dummyNode)
Since a node inherits its position information from its parent node, when you
reparent a node in the scene graph you might inadvertently change its position
in the world. If you need to avoid this, you can use a special variant on
reparentTo()
:
myModel.wrtReparentTo(newParent)
The “wrt” prefix stands for “with respect to”. This special method works like
reparentTo()
, except that it automatically recomputes the
local transform on myModel to compensate for the change in transform under the
new parent, so that the node ends up in the same position relative to the world.
Note that the computation required to perform
wrtReparentTo()
is a floating-point matrix computation and
is therefore inherently imprecise. This means that if you use
wrtReparentTo()
repeatedly, thousands of times on the same
node, it may eventually accumulate enough numerical inaccuracies to introduce a
slight scale on the object (for instance, a scale of 1, 1, 0.99999); if left
unchecked, this scale could eventually become noticeable.
Beginners tend to overuse this method; you should not use
wrtReparentTo()
unless there is a real reason to use it.
Changing the Color
Color changes are another common alteration. Values for color are floating point numbers from 0 to 1, 0 being black, 1 being white.
myNodePath.setColor(R, G, B, A)
If models have textures, they may not be distinguishable or even visible at certain color settings. Setting the color to white may restore the visibility of the texture, but it is better to simply clear the current color settings.
myNodePath.clearColor()
Note the fourth component of color is alpha. This is usually used to indicate transparency, and it is usually 1.0 to indicate the object is not transparent. If you set the alpha to a value between 0 and 1, you can fade the object to invisible. However, in order for the alpha value to be respected, you must first enable transparency:
myNodePath.setTransparency(TransparencyAttrib.MAlpha)
The parameter to setTransparency()
is usually
TransparencyAttrib.M_alpha
, which is ordinary transparency. You can also
explicitly turn transparency off with TransparencyAttrib.M_none
. (Other
transparency modes are possible, but that is a more advanced topic. Some older
code may pass just 0 or 1 for this parameter, but it is better to name the
mode.) If you don’t explicitly enable transparency first, the alpha component of
color may be ignored. Be sure you don’t enable transparency unnecessarily, since
it does enable a more expensive rendering mode.
Setting an object’s color completely replaces any color on the vertices.
However, if you have created a model with per-vertex color, you might prefer to
modulate the object’s color without losing the per-vertex color. For this there
is the setColorScale()
variant, which multiplies the
indicated color values by the object’s existing color:
myNodePath.setColorScale(R, G, B, A)
One use of setColorScale()
is to apply it at the top of the
scene graph (e.g. render) to darken the entire scene uniformly, for instance to
implement a fade-to-black effect.
Since alpha is so important, there is also a method for scaling it without affecting the other color components:
myNodePath.setAlphaScale(SA)
Hiding and Showing
To temporarily prevent an object from being drawn on all cameras, use
hide()
and show()
:
myNodePath.hide()
myNodePath.show()
If you want to hide an object for one camera but not another, you can use the
hide()
and show()
commands in conjunction
with the Camera.setCameraMask()
function:
camera1.node().setCameraMask(BitMask32.bit(0))
camera2.node().setCameraMask(BitMask32.bit(1))
myNodePath.hide(BitMask32.bit(0))
myNodePath.show(BitMask32.bit(1))
# Now myNodePath will only be shown on camera2...
Please note that using hide/show without an argument will mess up any hide/shows
with the argument (show(bit) will not undo a hide()…) To hide an object from
all cameras instead use nodepath.hide(BitMask32.all_on())
.
To set the camera mask for the default camera use base.cam, not base.camera, as base.camera is not an actual camera but a dummy node to hold cameras. Please see the camera section for information on how to set up multiple cameras.
Any object that is parented to the object that is hidden will also be hidden.
However, you can call showThrough()
on the nested element
to force it to show up even if its parent node is hidden.
Hiding a model will only cause it to stop rendering, but other operations (such
as checking for collisions) will still continue to take place. To deactivate a
node and its children entirely, you can call the stash()
and
unstash()
methods instead.
Storing Custom Information
Also, by using the functions setTag()
and
getTag()
you can store your own information in key-value
pairs. For example:
myNodePath.setTag("Key", "value")
You can also store Python objects as tags by using the
setPythonTag()
function with the same arguments.
Removing Nodes
To completely remove a node from the scene graph you can call the following, which has the effect of emptying the node and releasing the memory taken up by the node. Use it only when you have no further use for the node:
myModel.removeNode()
Please note, however, that this does not really do much more than just calling
detachNode()
followed by dropping the myModel variable.
If the model is still referenced from other places, such as the model pool, it
will still take up memory. If releasing the model from memory is desired, use
the following code:
ModelPool.releaseModel("path/to/model.egg")