"lock" camera to arbitrary vector

wow thanks! I’ll try it out later today. I appreciate it, this is exactly what I was hoping I’d stumble across.

BTW, is it just me, or is the camera system in panda3d a bit confusing? Most other aspects of the engine are pretty intuitive to me, but working with the camera seems to involve several different ‘random’ objects/nodes that you have to manipulate, and I haven’t found clear documentation regarding this.

For myself, I find that it’s only really confusing if one sticks to the built-in camera controls; otherwise, it’s just another node, like any other, and which can be transformed pretty much like any other.

Trackball is indeed responsible for the built-in camera controls.

I wonder if for your use case, it would simply be enough to reset the heading and roll components of the trackball in a task.

def resetcam(task):
    tb = base.trackball.node()
    tb.set_h(0)
    tb.set_p(0)
    return task.cont

base.taskMgr.add(resetcam, 'reset-cam')

If you want to disable trucking, you could set the trackball to “roll” mode:

base.trackball.node().set_control_mode(Trackball.CM_roll)

I found a way to make the Trackball inoperative while the middle-mouse button is held, which you can combine with setting it to roll mode to enable rolling using the left-mouse button (zooming is still allowed with the right mouse button). This would also disable trucking—it’s not clear whether you want to keep that enabled or not.

# This tells it to do nothing as long as mouse 2 is held
base.trackball.node().require_button(MouseButton.two(), False)

# Roll mode allows rolling the camera with the left mouse button
base.trackball.node().set_control_mode(Trackball.CM_roll)
2 Likes

Ah, that does seem like the simplest solution indeed!

That’s pretty clever. It still doesn’t prevent the user from holding down both left and right mouse buttons together to rotate the trackball, though.

Here’s an alternative version of my code that implements rdb’s solution:

from panda3d.core import *
from direct.showbase.ShowBase import ShowBase
from direct.showbase.DirectObject import DirectObject


class NavigationManager:

    def __init__(self, showbase, cam_vec):

        self.showbase = showbase
        self.cam_vec = cam_vec
        self.listener = listener = DirectObject()
        listener.accept_once("l", self.lock_cam)

    def lock_cam(self):

        mat = Mat4()
        rotate_to(mat, Vec3.forward(), self.cam_vec.normalized())
        tmp_node = NodePath("tmp")
        tmp_node.set_mat(invert(mat))
        tb = self.showbase.trackball.node()
        tb.set_hpr(tmp_node.get_hpr())
        self.listener.accept_once("u", self.unlock_cam)

        # This tells it to do nothing as long as mouse 2 is held
        tb.require_button(MouseButton.two(), False)

        # Roll mode allows rolling the camera with the left mouse button
        tb.set_control_mode(Trackball.CM_roll)

    def unlock_cam(self):

        self.listener.accept_once("l", self.lock_cam)

        # This removes the previously set button requirement
        self.showbase.trackball.node().clear_button(MouseButton.two())

        # Reset to default mode
        self.showbase.trackball.node().set_control_mode(Trackball.CM_default)


showbase = ShowBase()
smiley = showbase.loader.load_model("smiley")
smiley.reparent_to(showbase.render)
dir_vec = Vec3(-1., 1., -1.)
nav_mgr = NavigationManager(showbase, dir_vec)
showbase.run()
1 Like