points and vectors

:open_mouth: 30 vector and point classes!?

  • LPoint2d

  • LPoint2f

  • LPoint3d

  • LPoint3f

  • LPoint4d

  • LPoint4f

  • Point2

  • Point2D

  • Point3

  • Point3D

  • Point4

  • Point4D

  • LVecBase2d

  • LVecBase2f

  • LVecBase3d

  • LVecBase3f

  • LVecBase4d

  • LVecBase4f

  • LVector2d

  • LVector2f

  • LVector3d

  • LVector3f

  • LVector4d

  • LVector4f

  • Vec2

  • Vec2D

  • Vec3

  • Vec3D

  • Vec4

  • Vec4D

I would think 6 should suffice (i.e. vector and a point for 2,3 and 4 dimensions.)

The API refrence was not much help in distinguishing them. Are any of these intended for us to use, or are they all internal to the scenegraph?

If I need to do some general 3d math which of these classes should I use?

Also, if I want to adjust the position of a node fluidly with my scripted AI, or with a manual control does panda have a built in way of doing that using vectors?

I would think 6 should suffice (i.e. vector and a point for 2,3 and 4 dimensions.) 

Well, as far as that goes, six classes do suffice:

These are the names of the classes in Python. In C++, these six classes have the following names instead:

The reason they have different names between C++ and Python is largely due to a philosophical difference of opinion between two of the early developers of Panda, one of whom worked largely in C++, and one who worked in Python. One felt that class names should be verbose and distinctive, and unlikely to collide with class names from other third-party packages; the other felt that frequently-used names should be short and easy to type. Each left their mark on their respective side of the language barrier. For good or bad, we’re stuck with this dichotomy forever, since much code has already been written using these names.

Now, LVector3f/Vec3 describe a vector–that is, a locationless direction in space–while LPoint3f/Point3 describe a point–a directionless location in space. But sometimes, you want a multi-component number that is neither; for instance, an H, P, R triple, or an R, G, B, A, quad. For this reason, we also have the classes:

or, as they are known in Python:

These classes are the base classes of LVector3f/Vec3 and LPoint3f/Point3, and they provide most of the common functionality between the two, except for the things that are specific to either vectors or points. They’re simply three-component numbers that have no other semantic meaning.

Finally, all of the above are single-precision floats (hence the trailing “f” in the C++ names). Panda uses single-precision arithmetic to represent most vertex data, since that’s all that a graphics card will support anyway. However, occasionally, you really want double-precision arithmetic. For instance, the egg library uses double-precision values, since that makes the most sense in an abstract representation. For purposes such as this, Panda provides the following double-precision classes:

and, in their Python names:

So, there you have it. The whole sordid tale for why there are so many different kinds of vector representations, and exactly what each name represents.


1 Like

Ah, the D in Vec3D stands for double, not dimension.

Since I’m doing all of my coding in Python, I guess I won’t have to worry much about the C++ versions.

Further reading in the documentation suggests support for dot and cross products, and operator overloading, although most funtions aren’t documented.

Do these operations work between types? (e.g. can I cross a Vec3 with a Vec3D? Or add?) Or do I need to convert between them first? Are there built in ways of doing that, or would I have to rebuild them component-wise?

Do NodePath functions like getPos() and getHPR() return Point3 's and Vec3 's, or something else?

Right, for the most part. Though you will occasionally be confronted with an error message, generated from the C++ side, that uses the C++ class names, so it will be useful to at least know there is a correlation.

Right. We assume a basic familiarity with fundamental linear-algebra operations. Operator overloading is designed to work more-or-less intelligently between Vec3 and Point3; for instance, Vec3 + Vec3 -> Vec3, Point3 + Vec3 -> Point3; Point3 - Point3 -> Vec3.

There is also supposed to be Point3 * Mat4 -> (transform as point), and Vec3 * Mat4 -> (transform as vector). These overloads work in C++, but apparently they didn’t make it over to the Python side, now that I look at it. Funny that no one noticed.

I’m afraid you will have to rebuild the objects componentwise if you want to convert between single- and double-precision.

getPos() returns a Point3, getHpr() returns a VBase3(). In general, we made an effort to make functions return the correct types. There might be the odd exception here and there.


VBase3, that means that


So what are the three components returned by getHpr()? (Heading,Pitch,Roll) in degrees? (Heading,Pitch,Roll) in radians? (i,j,k) unit vector? something else?

It sounds like a unit Vec3 would be the most useful for 3D math, so how do you get that from a getHpr()?

This is heading, pitch, roll, in degrees, as more-or-less described here:

If you want a Vec3, then maybe you want to be dealing with Quaternions, instead of Euler (HPR) angles. A quaternion is basically an axis of rotation, plus an angle of rotation. You can call quat.getAxis() and quat.getAngle() to get these two components separately. There are lots of math resources on the net to help you understand quaternions.

There are also a number of methods to convert back and forth between HPR, Quaternion, and Mat3 (or Mat4), all of which represent rotations in different ways. For instance, Quat.setHpr() or Quat.getHpr() will convert from HPR to Quaternion and vice-versa, respectively. There’s also composeMatrix() to construct a Mat3 or Mat4 given a HPR (and other components such as scale and shear), and decomposeMatrix() to go back the other way. Furthermore, if you’re dealing with NodePaths you probably don’t need to convert these rotations at all, since any of these rotation forms can be extracted directly from a NodePath (which automatically converts from whatever you give it to whatever you ask for).

Maybe by Vec3, you mean the orientation of a Y axis after the rotation has been applied–that is, the direction you will be looking in when you apply the specified rotation. I’ll call this a Vec3 “direction”. Note that this is not a complete description of a rotation, since it doesn’t account for the twist around that vector. This is also not the same vector returned by the quat.getAxis()–that returns the axis about which a rotation is performed, not the direction of the final rotation. There are a few cases when this kind of Vec3 direction is useful, but frankly, it doesn’t come up often.

If you want to compute this kind of Vec3 direction, though, you can do it by applying the specified rotation to the Y axis: Vec3(0, 1, 0). It’s possible to do this using either Mat3 or Quat representations of the rotation, but you can’t compose HPR rotations directly. So, to compute this Vec3 via a Quat:

q1 = Quat()
myDirection = q1.xform(Vec3(0, 1, 0))

Or, to compute it via a Mat3:

m = Mat3()
composeMatrix(m, VBase3(1, 1, 1), VBase3(0, 0, 0), myHpr)
myDirection = m.xform(Vec3(0, 1, 0))

These examples, of course, assume that you are starting from a HPR. If you already have your rotation in Quat or Mat3 form, you can just use the xform call directly.

Note that HPR isn’t a particularly useful representation to perform computations with. It’s provided because it’s so easy to understand and visualize by a human; it’s easy to make up or read a heading, pitch, roll number and know roughly what rotation is described. Not so with quaternions or matrices, which are often just mysterious bunches of numbers internally. So usually HPR is used for those rotations that are directly entered or read by a human, and quaternions or matrices are used for performing the actual computations.

Finally, there are methods on NodePath to perform some common tasks conveniently. For instance, NodePath.lookAt() will calculate the rotation necessary to point a node’s Y axis towards some specified point in space (this is the inverse of the aforementioned HPR-to-direction question: given a Vec3 direction, what is the corresponding HPR rotation?). NodePath.getRelativeVector() will retrieve the Vec3 that corresponds to some vector as seen from another point of view. It follows, then, that you could also use code like this to convert HPR to a Vec3 direction:

v = node.getParent().getRelativeVector(node, Vec3(0, 1, 0))

Which is to say, rotate the node by the specified HPR, then compute the angle of the node’s Y axis from the point of view of the node’s parent. Of course, it would make sense to use a NodePath to do this only if you are already dealing with nodes in the scene graph; which maybe you are.