Cycling through cameras with one button

Hi,
I have 3 cameras in my world. I have 3 methods that switch to activate each one here:

        dr = self.camNode.getDisplayRegion(0)

        def switch_to_camera3():
            dr.setCamera(self.camera3)
            self.current_camera = self.camera3
            self.update_text_color()

        def switch_to_camera2():
            dr.setCamera(self.camera2)
            self.current_camera = self.camera2
            self.update_text_color()

        def switch_to_original_cam():
            dr.setCamera(self.cam)
            self.current_camera = self.cam
            self.update_text_color()

        self.accept('o', switch_to_camera2)
        self.accept('o', switch_to_camera3)
        self.accept('o', switch_to_original_cam)

I am trying to cycle through each of them pressing one button (‘o’). To be honest, I thought I could just call self.accept() 3 times, but that does not work. Does anyone have other suggestions?

Lists will help you with this, just keep the camera descriptors in the list.

from direct.showbase.ShowBase import ShowBase

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        self.list_cameras = ['camera_1', 'camera_2', 'camera_3']
        self.index_active_camera = 0

        self.accept('o', self.switch_cameras)

    def switch_cameras(self):
        if self.index_active_camera < len(self.list_cameras)-1:
            self.index_active_camera += 1
        else:
            self.index_active_camera = 0

        print(self.list_cameras[self.index_active_camera])

app = Game()
app.run()

To clarify this point: Yup, when multiple calls are made to “accept” for the same event, it’s the last one that takes effect, I do believe.

Think of it as an assignment, as with any variable: you’re assigning a function to the specified event. As such, when you perform two such assignments, the second overwrites the first–and so on with subsequent assignments.

As to solutions to your problem, I second serega’s suggestion!

Ok, it seems like I’m getting somewhere with @serega-kkz’s suggestion.

I added more functionality for the cycle to do what it should actually do:

        def switch_cameras():
            if self.index_active_camera < len(self.list_cameras)-1:
                self.index_active_camera += 1
            else:
                self.index_active_camera = 0

            # Set the active camera
            current_camera = self.list_cameras[self.index_active_camera]
            self.camera.reparentTo(current_camera)

            # Update the camera used for rendering
            dr.setCamera(current_camera)

        self.list_cameras = [self.cam, self.camera2, self.camera3]
        self.index_active_camera = 0

        self.accept('o', switch_cameras)

But the error I’m now getting is:

Assertion failed: Detected attempt to create a cycle in the scene graph: render/cam3/camera/cam : ModelNode camera at line 2637 of panda/src/pgraph/pandaNode.cxx
Assertion failed: reparented at line 406 of panda/src/pgraph/nodePath.cxx
Traceback (most recent call last):
  File "/home/user/panda3d/winter/test/.venv/lib/python3.10/site-packages/direct/showbase/EventManager.py", line 49, in eventLoopTask
    self.doEvents()
  File "/home/user/panda3d/winter/test/.venv/lib/python3.10/site-packages/direct/showbase/EventManager.py", line 43, in doEvents
    processFunc(dequeueFunc())
  File "/home/user/panda3d/winter/test/.venv/lib/python3.10/site-packages/direct/showbase/EventManager.py", line 99, in processEvent
    messenger.send(eventName, paramList)
  File "/home/user/panda3d/winter/test/.venv/lib/python3.10/site-packages/direct/showbase/Messenger.py", line 337, in send
    self.__dispatch(acceptorDict, event, sentArgs, foundWatch)
  File "/home/user/panda3d/winter/test/.venv/lib/python3.10/site-packages/direct/showbase/Messenger.py", line 422, in __dispatch
    result = method (*(extraArgs + sentArgs))
  File "/home/user/panda3d/winter/test/shape.py", line 311, in switch_cameras
    self.camera.reparentTo(current_camera)
AssertionError: Detected attempt to create a cycle in the scene graph: render/cam3/camera/cam : ModelNode camera at line 2637 of panda/src/pgraph/pandaNode.cxx
:task(error): Exception occurred in PythonTask eventManager
Traceback (most recent call last):
  File "/home/user/panda3d/winter/test/shape.py", line 361, in <module>
    app.run()
  File "/home/user/panda3d/winter/test/.venv/lib/python3.10/site-packages/direct/showbase/ShowBase.py", line 3331, in run
    self.taskMgr.run()
  File "/home/user/panda3d/winter/test/.venv/lib/python3.10/site-packages/direct/task/Task.py", line 553, in run
    self.step()
  File "/home/user/panda3d/winter/test/.venv/lib/python3.10/site-packages/direct/task/Task.py", line 504, in step
    self.mgr.poll()
  File "/home/user/panda3d/winter/test/.venv/lib/python3.10/site-packages/direct/showbase/EventManager.py", line 49, in eventLoopTask
    self.doEvents()
  File "/home/user/panda3d/winter/test/.venv/lib/python3.10/site-packages/direct/showbase/EventManager.py", line 43, in doEvents
    processFunc(dequeueFunc())
  File "/home/user/panda3d/winter/test/.venv/lib/python3.10/site-packages/direct/showbase/EventManager.py", line 99, in processEvent
    messenger.send(eventName, paramList)
  File "/home/user/panda3d/winter/test/.venv/lib/python3.10/site-packages/direct/showbase/Messenger.py", line 337, in send
    self.__dispatch(acceptorDict, event, sentArgs, foundWatch)
  File "/home/user/panda3d/winter/test/.venv/lib/python3.10/site-packages/direct/showbase/Messenger.py", line 422, in __dispatch
    result = method (*(extraArgs + sentArgs))
  File "/home/user/panda3d/winter/test/shape.py", line 311, in switch_cameras
    self.camera.reparentTo(current_camera)
AssertionError: Detected attempt to create a cycle in the scene graph: render/cam3/camera/cam : ModelNode camera at line 2637 of panda/src/pgraph/pandaNode.cxx

It works the first 2 times, but once I start the cycle over on the third ‘o’ press, I get this error that I’m making a cycle. Are either of you familiar with this?

The problem has been reported in the error. You reparent to the camera from one to the other, which you don’t need to do.

It is better to have the camera reparented to the render node once. And don’t call this method anymore:
self.camera.reparentTo(current_camera)

Thank you, everything works now! :slightly_smiling_face:

1 Like