How to lerp quaternion?

My camera’s position is static, while my hero may walk around. I want the camera to be always looking at my hero, to rotate with lerp effect, so I did this:

def lerp_quaternion(q1, q2, factor):
    if q1.getAngle() < 180 and q2.getAngle() > 180:
        # in this situation, there is a sudden change of rotation.
        # how to handle this?
        pass
    return q1 + (q2 - q1) * factor

Got any ideas about how to deal with the sudden change of quaternion? :unamused: :unamused: :mrgreen:

Thanks!

New to Panda3D. In Unity3D, the class Quaternion has a function named Lerp to achieve this. What about Panda3D? :open_mouth: :open_mouth: :blush: :blush:

:open_mouth: :question: anybody?

There is a LerpQuatInterval in P3D, maybe that can help:
panda3d.org/reference/1.9.4 … atInterval

If not, there’s the little known(?) fitDestAngle2Src:

…or you may try just calling camera.look_at(hero) each frame in a task :wink:

Here is a spherical lerp function I was using a while back to interpolate rotations in ODE (I have since switched to Bullet physics which does this internally). You can try dropping it into your code (import math, obviously).

def slerp(q1, q2, t):
    costheta = q1.dot(q2)
    if costheta < 0.0:
        costheta = -costheta
        q1 = q1.conjugate()
    elif costheta > 1.0:
        costheta = 1.0

    theta = math.acos(costheta)
    if abs(theta) < 0.01:
        return q2

    sintheta = math.sqrt(1.0 - costheta * costheta)
    if abs(sintheta) < 0.01:
        return (q1+q2)*0.5

    r1 = math.sin((1.0 - t) * theta) / sintheta
    r2 = math.sin(t * theta) / sintheta
    return (q1*r1) + (q2*r2)

If you just want the camera “locked on” to a moving object you don’t really need to interpolate, as wezu suggested just call “look_at” every frame. But for example if you want to select things to look at and have the camera smoothly pan to them rather than instantly snap, I also wrote a little function to dynamically create LerpIntervals for a “targetting” system.

lookinterval = None

def turnToward(target, slave):
    global lookinterval
    lerptarget = slave.find("lerptarget")
    if lerptarget.isEmpty():
        lerptarget = slave.attachNewNode("lerptarget")
    lerptarget.lookAt(target)
    bt = "easeInOut"
    if lookinterval is not None:
        if lookinterval.isPlaying():
            lookinterval.pause()
            bt = "easeOut"
    
    lookinterval = slave.quatInterval(0.5, lerptarget.getHpr(slave.getParent()), blendType=bt)
    lookinterval.start()

Here “slave” can be the camera or any other node. It attaches a reference node “lerptarget” to slave, uses lookAt to point that node at target, then smoothly interpolates slave’s rotation to match lerptarget. This may leak memory…I’m not sure if paused unreferenced intervals are held by the engine forever. It was just an experiment but might give you a little idea of how to write your own version.

1 Like

thank you! slerp seems to be what i need, i’ll give it a try! :unamused: :smiley: :smiley: :smiley: