Trigo question (difficulties with heading, pitch and roll)


I’m having some difficulties to understand the real meaning of the heading pitch and roll.

Taking them separately is ok but when using everything together, here the problems start.

1- Is the heading what’s the object always looks to ?
2- Do the X and Y axis move with the object or are they always fix and the object takes Heading Pitch and as its own coordinate ?
3- Should we compare that with the U and V axis we used at school for the landmark changes ?

The answer at these questions will be a good start to me to understand.
Thanks in advance.

It is very easy :slight_smile:
Just stand up and look ahead. Heading (setH) is where you look horizontally. If you turn left or right (i.e. around Z axis, in default terms of Panda3D), you change your heading. If you look up and down (i.e. around X axis), you change your pitch (setP). If you bent you neck to the shoulders (i.e. around Y axis), you change your roll (setR).

X, Y and Z are not fixed. Normally, you use coordinate space of the parent nodepath (the one right below the translated nodepath), in my example, it is earth under your feet. The coordinate space moves/rotates together with the nodepath. Say, if you look directly to the north, your heading is 0. And it doesn’t matter how the Earth moves or rotates: you still look to its north, and heading is 0.

Additionally, when calling setHpr, you can supply additional argument: the nodepath which coordinate space you wish to use. I.e. me.setH(Sun, 0) means doesn’t matter how the Earth is rotated, I wish to look towards ‘north of the Sun’. Or, if you want to turn 90 degrees around yourself, call me.setH(me, 90): this will rotate you by 90 degrees relative to your own current heading.
Same applies to getHpr() methods.

UV axes are usually used for 2D space, XYZ are for 3D.

Hope this answers your questions.

Thanks Burikoff, this does help :slight_smile:
1 - But then, for moving an object forward, why must we increase the value of the Y coordinate and not the heading’s value ?

An other question, let’s go further with the trigo :
Suppose that I’m heading somewhere between the X and Y axis and I want to trhough an object in front of me.
As for moving forward we just need to increase the Y value, I tried to use the same idea :

        self.myObject.setPos(x, y,0)
        self.trajectory = ProjectileInterval(self.myObject, startPos = (x,y,0), endPos = Point3(x,y+2, 0) , duration = 1)
        #print dir(self.trajectory)

So here I gues that I shall use the trigo functions which will lead me to the next question.

I’m heading somewhere between the X and Y axis :
2pi = X axis
pi/2 = Y axis
A = Heading

The new values for endPos will be something like :

x = h*cos(alpha)
y= h*sin(alpha)

2 - Ok but how do I find the alpha then ?

X axis goes from left to right. Y - from behind to ahead. Z - from down to up (hope, this is correct English, he-he, it is not my native language).

To set new position for an object, like moving it forward, yes, you should set its new position along Y in its own coordinate space (i.e. me.setY(me, 1) makes ‘me’ move 1 unit ahead). Since own coordinate space of the object is moved/rotated/scaled together with the object itself, its own Y axis always points forward. If you don’t provide relative nodepath, the position of the object is set in accordance with its parent nodepath (i.e. me.setPos(1, 1, 1) will set me on the given spot on earth).

Same applies to intervals: it gradually moves nodepath from its current spot in the parent’s coordinate space to another spot over the period of time. There is optional other=… parameter that allows you to pass another nodepath and make movement relative to it. But - important! - do not try to pass the nodepath itself or its children to it. Since every frame your nodepath’s position is re-computed, you will never arrive to the destination.

Panda does all math for you. In most cases, you don’t need to worry about it.

Aaaah Okay!
You mean that it’s easiest to move objects according to themselves or other objects than calculating each time their new positions.
That make sence to avoid the maths… Indeed…

But how to write:

setY(me, 2)

in the endPos of

self.trajectory = ProjectileInterval(self.myObject, startPos = (x,y,0), endPos = Point3(x,y+2, 0) , duration = 1)

If someone is still having problems with HPR:


This code creates interval the moves self.myObject 2 units ahead from its current position over the period of 1 second (I assume self.myObject is reparented to render):

endPos = render.getRelativePoint(self.myObject, Point3(0, 2, 0))
self.trajectory = self.myObject.posInterval(endPos=endPos, duration=1)

Is it what you need?
ProjectileInterval is needed for moving nodepaths along parabolic trajectory (like flying cannon-ball), and it requires more arguments then you gave.

(Panda3d uses the right-handed system and calls the Y+ axis “forward”, Z+ axis “up”, and X+ axis as “right”. While this is purely arbitrary, understanding this is necessary in order for my examples to work as you might expect.)

Rotations in HPR are built from three separate axis, all mutually perpendicular from each other – This is the very nature of what Euler rotations are. But you can also work in other representations, such as quaternions or even manipulate the direct matrix representation itself. HPR is a merely a convenient method for referring to an object’s orientation, either relative or absolutely.

If you do getHpr(), you are requesting the object’s local rotation in HPR. Heading will indeed reflect which way the object is pointing along its up axis. But if you use getHpr(render), then it will be that object’s rotation but expressed in world space. Now, the heading will reflect how that object is pointing in WORLD coordinates. The up axis will be Z+ in the world. Rotations are just like Cartesian coordinates, they are relative.

Not that getHpr()/getHpr(render) will be the same if the object is directly parented to render, so it will always be in world space. If you get a little confused, the best way to make the math simpler is to temporarily work in world coordinates and then convert back to local space. (Most Panda3d functions can do this implicitly, by giving it a reference frame, such as my getHpr(render) example above.)

Your trig is the raw math behind a singular Euler axis. The A is the unit vector describing that rotation’s axis. You can convert HPR into a unit sphere at any time, though I don’t believe Panda3d has helper functions to do that directly from a HPR. You can, however, do so from a Quaternion:

quat = object.getQuat()  # local rotation
forward = quad.getForward()
up = quat.getUp()
right = quat.getRight()

As for your Projectile examine – If I am understanding right, you want to calculate a direction that will act as the end point for a ProjectileInterval, but only pointed for 2 units worth of distance from the front of MyObject.

getRelativePoint() is a really handy function. It allows you to return points from one coordinate space in another. Birukoff’s example demonstrates how you can use it to find a relative point that is 2 units ahead of the forward vector of your origin object.

You could also do the math as such:

fwd = self.myObject.getQuat().getForward()
endPos = self.myObject.getPos() + fwd * 2.0
self.trajectory = self.myObject.posInterval(endPos=endPos, duration=1)

But, as you can see, getRelativePoint() saves wear and tear on your fingers and is cleaner to read. :slight_smile:

According to this scheme, rotations in respect to x and y (pitch and roll) are clockwise, but rotation in respect to z (head) is counter-clockwise.
Is this coincident with panda3d’s behavior? I.e. to make a clockwise head rotation you have to give a smaller angle than precedent, unlike pitch and roll ones?

All rotations are counter-clockwise about the respective axis, when viewed in the direction of the positive axis. This is exactly as pictured. Remember that the meaning of counter-clockwise and clockwise depend on the viewing direction; and remember that Panda’s axes are +x to the right, +y forward, and +z up.


Exactly, but to be honest, in that picture, the rotations viewed from +x and +y appear just clockwise, to me. Or am I wrong?

Ah, my apologies. You are correct; the picture is wrong. The arrows for P and R should be drawn in the reverse direction.

And I also said it slightly wrong: the rotation direction is counter-clockwise when viewed from the + axis direction, or clockwise when viewed toward the + axis direction.


Thanks, now it’s really clear. I suggest to post a correct picture if there is one.