Setting relative rotation angles not working?

So I’m aware there is a set_hpr(NodePath, LVecBase3f) that is supposed to rotate an object relative to another one. But I have not found this to be working at all, in that it does the exact same thing with or without a NodePath there (and yes, the referenced NodePath does have a different axis).

In fact, my results are always as mentioned in this post
[Very confuse about Hpr.)
z -> rotate globally around z
y/x -> rotate locally around y/x

I’m using panda1.9. Has anyone else had this problem?

You say that the other NodePath has a different “axis” - do you mean that it has a different position? If so, then does it also have a different HPR?

If I recall correctly, the relative HPR methods set the target NodePath’s H, P and R relative to the other NodePath’s H, P and R, respectively, but don’t, I believe, make use of the other NodePath’s position.

It does have a different position, but I’ve tried both (same xyz, different xyz) and no difference.

So perhaps I’m looking at ‘relative’ wrong. I want to rotate my new object, let’s say pitch, a certain number of degrees, but over the ‘pitch axis’ of the relative node.

NodePath relativenode, mynode;
mynode.set_p(relativenode, 10.0);

So I’m expecting that ‘mynode’ will rotate 10.0 degrees over the pitch axis of ‘relativenode’, which should now be 45.0 counterclockwise of where it was originally.
Am I looking at this right?

Ah, I see, no. You do indeed seem to be misinterpreting what is meant by “relative HPR” in this context (presuming that I’m not mistaken).

As I noted above, as I understand it the “relative to this node” version of “set_hpr” should rotate the given NodePath relative to the HPR of the NodePath parameter, without changing the point about which it rotates, which I now understand to be your desire.

If I’m correct, then I suggest reparenting the NodePath to be rotated to another NodePath (perhaps temporarily, depending on your purposes), which should be placed at the point about which you wish to rotate, and then rotating the new NodePath. For example, roughly:

// This is the NodePath that you want to rotate.
NodePath myNodePath;
// This is the NodePath relative to which you want
//  to rotate the above NodePath
NodePath anotherNodePath;
// This is the new desired HPR
Vec3 newHPR;

// This is a new, dummy NodePath used only for the desired rotation.
NodePath tempNodePath;



// You might now reparent "myNodePath" to whatever
//  it was previously a child of, if desired.

Ok, i’m going a bit overboard here with the pics :stuck_out_tongue:
I just really want to make sure I get this.

I understand your code, but I actually DONT want to rotate around an object (orbit), but instead rotate around itself (revolve), just about different axes of itself.

Here is my understanding of relative nodes and tell me where I’m getting it wrong. Nothing here is parented.

Ok, here, i’ve got a global axis frame (unaltered) and an object (A) at x=5 and y=1.

Now, I rotate object A 45 degrees clockwise about z (relative to the global frame, or itself, I don’t think it matters here)

Let’s say I add object B (blue circle). If I add it relative to A, with -x about 4, i’d expect it to be at B2, not B1. And when I do it in panda, that is what i get, meaning it is operating in a new x-axis, the x-axis of object A.

So now, I want to rotate object B about its y axis. What is it’s current axis? I would expect that if I rotate without posing anything relative, then it should rotate like the following

And if I want to rotate relative to the global frame, then it would rotate like this

But unfortunately, adding reference nodes don’t change the axes of rotation.
And what I find strange is that if I call set_h (with or without a reference node) it will always rotate like the second one. If I call set_r or set_p (with or without a reference node) it will always rotate like the first one.

Ah, hmm, I see. First of all, my apologies - it seems that I misunderstood your earlier posts.

That is odd indeed - what you’re attempting looks to me as though it should work; you may well have found a bug, or I may well be missing something. In this case, I fear that I’m going to defer to someone with greater knowledge than mine - sorry. :confused:

Ah, no problem! I actually think it may have to do with order of operations to euler angles in sequence in the source, which may be why the set_h always works fine, but the roll and pitch do not? I’m not sure though.

I’ve read your questions several times, and looked at your pictures; and while I understand your pictures, I don’t understand precisely what you expect to happen or precisely what you observe to be happening. Part of the problem is somewhat ambiguous phrases like “add [object B] relative to object A”. Does this mean you mean B a child of A in the scene graph, or does it mean you used a relative set operation to set the transform of B relative to the transform of A?

Perhaps it would help if you posted a simple sample program that illustrates the effect you observe, and describe the effect that you think you should observe instead.

To clarify, though, a relative setH() operation is very rarely useful. The problem is that “H” has different meanings in different coordinate spaces, but setH() is defined to only change H, which is always defined in your current object’s coordinate space. So when you say B.setH(A, x), it always changes B’s “H” value to a different value without affecting B’s “P” and “R” values, and this means it must alway perform a rotation around B’s vertical axis, regardless of the node you specified it relative to.

If you want to perform a rotation relative to another node’s coordinate space, it usually makes sense to use relative setHpr() instead. This allows the node to change all three components, giving it no restrictions on matching the other node’s coordinate space.


Sorry, I did mention that nothing here is parented, but I didn’t put attention to it.
There is no parenting happening at all. I purely what a coordinate frame from another object, but not be parented to it at all.

Basically what I’m trying to do is rotate an object by dragging it. I want all of the rotations to happen with respect to the scene (so the fifth image). Here is part of that code below:

AsyncTask::DoneStatus MyClass::drag(GenericAsyncTask* task, void* data) 
  MyClass* class = static_cast<MyClass*>(data);
  NodePath ref = class->window_framework_->get_render(); // global reference frame, want these axes!

  // grab the initial hpr of the object
  static LPoint3f original_hpr;
  if (task->get_elapsed_frames() == 0)
    original_hpr = class->object_to_rotate_.get_hpr();

  double newangle = lots_of_complicated_math;

  if (dragging_x_rotation) {
    class->object_to_rotate_.set_hpr(ref, original_hpr.get_x(), newangle, original_hpr.get_z()); 
  } else if (dragging_y_rotation) {
    class->object_to_rotate_.set_hpr(ref, original_hpr.get_x(), original_hpr.get_y(), newangle); 
  } else if (dragging_z_rotation) {
    class->object_to_rotate_.set_hpr(ref, newangle, original_hpr.get_y(), original_hpr.get_z()); 
  return AsyncTask::DS_cont;

This task will keep looping, and ideally i can drag my mouse around and change the object’s orientation on the fly about the axis that i specified.

The newangle (magnitude or rotation) isn’t important here. The axis over which I rotate is what is important. So say I want to rotate about x (relative to the whole scene), so i stick a plane that intersects this object and is perpendicular to my scene’s x-axis. When I rotate about x, the volume of each part of the object separated by the plane should not change as i rotate it.
Instead it continues to rotate over its original x axis, so its two segments separated by the plane are changing now. :frowning:

It’s rotating as you mentioned here.

This is true for the roll and pitch, but when it comes to heading, that isn’t the case, and instead, it does what I want it to do :stuck_out_tongue:

So I want to rotate it like in image 5. This is how heading is doing it.
It sounds like your saying objects can only rotate as it is in image 4, which is how roll and pitch are doing it.


original_hpr = class->object_to_rotate_.get_hpr(ref);


no effect :frowning:

sorry, that’s not entirely true, it changes my angles (for dragging, i.e., how much i rotate around an axis), but the axes are still not relative to ‘ref’

Alright, i’ve made a video to demonstrate my problem. I’m basically trying to mimic ROS RVIZ’s interactive markers.

Here’s the video.

What happens is when I click on one of the rings, it should rotate around that ring. I’m just clicking and holding and a counter in the task just continually updates it’s angle.

What I expect to see:
Here I’m not referencing any nodes relatively. So I expect all three axes to operate the same way. At this point I can’t confirm which is right, but they should all either rotate around their local axes, or rotate around the reference axes of the world.

What I actually see:
Any initial axis rotation works fine. But after I’ve rotated at least one axis, I get this issue:

  • rotating about z always rotates about global z
  • rotating about x always rotates about local x
  • rotating about y is never the same :stuck_out_tongue:
    I’m calling them all the same way, why are they operating in different coordinate frames??

Example of the code:

AsyncTask::DoneStatus MyClass::drag(GenericAsyncTask* task, void* data) 
  MyClass* class = static_cast<MyClass*>(data); 

  count += 0.5;
  if (count >= 360.0) {
    count = 0.0;

  if (dragging_x_rotation) { 
  } else if (dragging_y_rotation) { 
  } else if (dragging_z_rotation) { 
  return AsyncTask::DS_cont; 

Ah, I believe that I see what you’re doing.

Hmm… I may very well be wrong in this, but have you tried rotating your object relative to itself? Your “three rings” would then be parented to the object (not the object to the rings) and their position and HPR both set to zero.

From what I see there, you more or less want to rotate the object around its own axis perpendicular to the selected ring, with the rings being a representation of the object’s local axes, not so?

Note that you would then presumably be rotating the object by degrees, rather than setting the relevant value.

For example, rather than

count = count + 0.5

try something along the lines of

myObj.setH(myObj, 0.5)

(Although I suggest getting a delta-time - the time since the start of the last update - and including that in the change in rotation.)

I think there’s a fundamental misunderstanding here. set_h() is not the same thing as rotating about Z, set_p() is not the same thing as rotating about X, and set_r() is not the same thing as rotating about Y.

Each of these methods changes the specified angle of the Euler triple, while leaving the other two unchanged. It’s true that if the other two angles in the Euler triple are 0, then setting H, P, or R is equivalent to a rotation around Z, X, or Y, respectively. But these angles don’t operate independently, and once you have any nonzero value in any of the components, you’ve changed the meaning of the other two components.

If you want to achieve rotation about some arbitrary axis, you need to use a completely different approach. There are several possibilities. One naive way is to do a rotation relative to oneself:

np.set_p(np, 10)

This is kind of akin to np.set_p(np.get_p() + 10), except that it works the way you are expecting it to work even in the presence of existing rotation. The downside to this kind of rotation operation is that small errors can accumulate over time.


Thank you David and Thaumaturge.

Alright David, I understand the functionality now.

So both of you recommended doing the np.set_p(np, 10) approach, which I have confirmed that it does rotate around the correct axes.
Unfortunately however, the way the mouse control is set up, I need to set absolute values (so 90.0 would go to 90.0, not add 90.0).
(the example code and video was purely just to demonstrate a concept, not actually how i control the rotation with the mouse)

Thaumaturge, I think I can leverage what you mention and attempt to calculate mouse position differences each time the task runs, though perhaps it is slightly overkill?
David, do you have a alternate option that still uses absolute values?

Considering using LQuaterion::set_from_axis_angle() if you want to specify a particular angle. To accumulate rotation from multiple angles, you can multiply quaternions together; but you have to decide what order you expect them to be composed in (it makes a difference).

But it sounds like you’ve got two conflicting goals: you want to provide an absolute angle, but you also want to accumulate rotations from different angles? I don’t know how to reconcile those two goals, but I have only a vague understanding of precisely what effect you are trying to build here.


Given the description that you’ve given to us, might it not be easier to change how you handle mouse control?

Hmm… Forgive me if I’m missing something (I fear that I’m pretty tired today), but if I understand correctly, you’re calculating a new absolute angle (or set thereof) from the mouse position, presumably relative to some original position, is that correct?

If so, why not simply store the mouse position at the end of each call of this method; let us call that “lastMouse” for now. Then, on the next call to the method, subtract that from the current mouse position to find the mouse movement since last update, and use that to generate a relative change in angle, rather than an absolute angle as you’re doing now.

(You’d likely also have an edge case in the first click; you likely want to set “lastMouse” to the mouse position when the mouse is first clicked, in order that the system doesn’t attempt to compare the new position to the position of the mouse as of the previous release of the mouse button.)