How to rotate object and get HPR?

I have an object (say fromObj) stored in NodePath at a 3D point location (say fromPoint). Its HPR is (0,0,0). I want to rotate it such that its X-axis points to another point toPoint in space. I want to compute the HPR that does this and use it for some other purpose.

I tried fromObj.lookAt(toPoint), but this points its Y-axis at toPoint. I want its X-axis to point at toPoint :frowning:

How do I compute the HPR that will rotate an object such that its X-axis points to a given location in space? :slight_smile:

I could not find how to do this using Panda3D APIs. But I did it by raw calculations as follows:

def getHprFromTo( fromPt, toPt ):
    HPR to rotate *from* point to look at *to* point

    # Translate points so that fromPt is origin
    pos2 = toPt - fromPt

    # Find which XY-plane quadrant toPt lies in
    #    +Y
    #    ^
    #  2 | 1
    # ---o---> +X
    #  3 | 4

    quad = 0

    if pos2.x < 0:
        if pos2.y < 0:
            quad = 3
            quad = 2
        if pos2.y < 0:
            quad = 4

    # Get heading angle

    ax   = abs( pos2.x )
    ay   = abs( pos2.y )
    head = math.degrees( math.atan2( ay, ax ) )

    # Adjust heading angle based on quadrant

    if 2 == quad:
        head = 180 - head
    elif 3 == quad:
        head = 180 + head
    elif 4 == quad:
        head = 360 - head

    # Compute roll angle 

    v    = Vec3( pos2.x, pos2.y, 0 )
    vd   = abs( v.length() )
    az   = abs( pos2.z )
    roll = math.degrees( math.atan2( az, vd ) )

    # Adjust if toPt lies below XY-plane

    if pos2.z < 0:
        roll = - roll

    # Make HPR
    return Vec3( head, 0, -roll )

The basic idea is imagine your head stationary at fromPoint in space. Your head (or eyes) is facing along +ve X axis, +ve Y axis comes out of your left ear and your up vector is +ve Z axis. Somewhere in the space around you is the toPoint.

To look at it, you first rotate your head (like in horror movies) in the global XY plane until toPoint is above or below you. This is the heading angle.

Now you lift your head up or down to finally to get the toPoint in your sight. This is the roll angle. Note that this is roll and not pitch, though it seems like that. This is because it is roll in the coordinate system of your head. And this gives you the HPR!

You can simply use lookAt followed by a 90 degree rotation around the Z axis.

For the record, to get a quaternion describing the rotation to do a specific lookAt without actually performing the lookAt, you can use the lower-level lookAt interface:

from panda3d.core import lookAt, Quat

quat = Quat()
lookAt(quat, target - nodePath.getPos(), Vec3.up())

The lookAt function calculates the quaternion (a less ambiguous way to define orientation than HPR angles) for the given forward vector. This is what nodePath.lookAt does under the hood.

If you want to get the HPR angle from this, you can then use getHpr() on that quaternion.

I am aware of the lookAt function too. Your code makes the nodePath’s Y-axis point towards the target.

How do you get the HPR to make the nodePath’s X-axis point towards the target?

As I believe that rdb said, rotating your object by 90 degrees around its z-axis (after applying lookAt) should do the trick.

Or, another way to say the same thing: attach your node to a parent node. Do yourNode.setH(90). Then do parentNode.lookAt(whatever).


Thanks guys! Your suggestions were right. I got it working with this code:

sourceNP.lookAt( targetNP )
sourceNP.setHpr( sourceNP, Vec3( 90, 0, 0 ) )
hpr = sourceNP.getHpr()