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

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

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
else:
quad = 2
else:
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:

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.