Camera Controls

I’m trying to create a camera handler that will enable the camera to zoom, pan, and orbit.

My current setup uses camera to control panning and orbiting and base.cam to control zooming. camera is at (0,0,0) with hpr set to (0,-45,0). base.cam is at (0,-50,0) with hpr set to (0,0,0).

Since base.cam’s hpr doesn’t change, I assume that it’s always directed towards camera. Zooming works fine until I change camera’s hpr. Then, when I change camera’s position and try to decrease base.cam’s y value, I find that it doesn’t always move towards camera.

I’ve taken a look at Zhao’s RTS cam, which does what I want, but don’t understand why my method doesn’t work:

This is what I have:

#Default settings
base.disableMouse()
def resetCamera():
    base.cam.setPos(0, -50, 0)
    camera.setHpr(0, -45, 0)

#Zooming
def zoomIn():
    base.cam.setPos(base.cam, 0, -5, 0)
def zoomOut():
    base.cam.setPos(base.cam, 0, 5, 0)

#Panning
def startPan(x,y):
    task = taskMgr.add(panTask, 'panTask')
    task.x = x
    task.y = y
def panTask(task):
    camera.setPos(camera, task.x, 0, task.y)
    return task.cont
def endPan():
    taskMgr.remove('panTask')

#Orbiting
def startDrag():
    global gMx,gMy
    task = taskMgr.add(dragTask, 'dragTask')
    task.mx,task.my = base.mouseWatcherNode.getMouse()
def dragTask(task):
    mx,my = base.mouseWatcherNode.getMouse()
    if (mx,my != task.mx,task.my):
        camera.setHpr(camera, 180 * (task.mx - mx), -180 * (task.my - my), 0)
        task.mx = mx
        task.my = my
    return task.cont
def endDrag():
    taskMgr.remove('dragTask')

Zooming, panning, and orbiting work on their own, but not in conjunction with each other.

Does this work for you?:

import direct.directbase.DirectStart
from panda3d.core import *

base.disableMouse() # disable default mouse controls

# hide mouse cursor, comment these 3 lines to see the cursor
props = WindowProperties()
props.setCursorHidden(True) 
base.win.requestProperties(props)

# a scene
environ = loader.loadModel('environment')
environ.setScale(0.1)
environ.setZ(-5)
environ.reparentTo(render)

# model for the camera to orbit along
model = loader.loadModel('smiley')
model.reparentTo(render)

# dummy node for camera, we will rotate the dummy node fro camera rotation
parentnode = render.attachNewNode('camparent')
parentnode.reparentTo(model) # inherit transforms
parentnode.setEffect(CompassEffect.make(render)) # NOT inherit rotation

# the camera
base.cam.reparentTo(parentnode)
base.cam.lookAt(parentnode)
base.cam.setY(-10) # camera distance from model

# camera zooming
base.accept('wheel_up', lambda : base.cam.setY(base.cam.getY()+200 * globalClock.getDt()))
base.accept('wheel_down', lambda : base.cam.setY(base.cam.getY()-200 * globalClock.getDt()))


# global vars for camera rotation
heading = 0
pitch = 0

# camera rotation task
def thirdPersonCameraTask(task):
	global heading
	global pitch
	
	md = base.win.getPointer(0)
		
	x = md.getX()
	y = md.getY()
	
	if base.win.movePointer(0, 300, 300):
		heading = heading - (x - 300) * 0.5
		pitch = pitch - (y - 300) * 0.5
	
	parentnode.setHpr(heading, pitch,0)
	
	return task.cont

taskMgr.add(thirdPersonCameraTask, 'thirdPersonCameraTask')		
		
	
run()

use mouse wheel fro zooming

Or if you need WSAD controls:

import direct.directbase.DirectStart
from panda3d.core import *
  
base.disableMouse() # disable default mouse controls

# Load the scene.
environ = loader.loadModel('environment')
environ.setScale(0.1)
environ.setZ(-5)
environ.reparentTo(render)

# model for the camera to orbit along
model = loader.loadModel('smiley')
model.reparentTo(render)

# dummy node for camera
parentnode = render.attachNewNode('camparent')
parentnode.reparentTo(model) # inherit transforms
parentnode.setEffect(CompassEffect.make(render)) # NOT inherit rotation

keyMap = {"a":0, "d":0, "w":0, "s":0}

#Records the state of the arrow keys
def setKey(key, value):
	keyMap[key] = value

# the camera
base.cam.reparentTo(parentnode)
base.cam.lookAt(parentnode)
base.cam.setY(-10) # camera distance from model

def cameraMovement(task):
	if (keyMap["a"]!=0):
		parentnode.setH(parentnode.getH()-200 * globalClock.getDt())
	if (keyMap["d"]!=0):
		parentnode.setH(parentnode.getH()+200 * globalClock.getDt())
	if (keyMap["w"]!=0):
		parentnode.setP(parentnode.getP()-200 * globalClock.getDt())
	if (keyMap["s"]!=0):
		parentnode.setP(parentnode.getP()+200 * globalClock.getDt())
	
	return task.cont


taskMgr.add(cameraMovement, 'cameraMovement')

# camera zooming
base.accept('wheel_up', lambda : base.cam.setY(base.cam.getY()+200 * globalClock.getDt()))
base.accept('wheel_down', lambda : base.cam.setY(base.cam.getY()-200 * globalClock.getDt()))

# camera rotation 
base.accept('a', setKey, ["a",1])
base.accept('a-up', setKey, ["a",0])
base.accept('d', setKey, ["d",1])
base.accept('d-up', setKey, ["d",0])
base.accept('w', setKey, ["w",1])
base.accept('w-up', setKey, ["w",0])
base.accept('s', setKey, ["s",1])
base.accept('s-up', setKey, ["s",0])


run()

Thanks Anon, that works beautifully.

Why do you need to create a parent node for base.cam:

# dummy node for camera, we will rotate the dummy node fro camera rotation 
parentnode = render.attachNewNode('camparent') 
parentnode.reparentTo(model) # inherit transforms 
parentnode.setEffect(CompassEffect.make(render)) # NOT inherit rotation

instead of using camera (and setting the compass effect)?
[/code]

Well, if you parent a node to another node, move the child node far from the parent node (assuming they both were at 0,0,0) and rotate the parent node, then the child will orbit around it:

But of course the parent node will rotate too (duh), so I thought I could make an empty node as the parent, and then make it a child of the node (model) you want to orbit the camera around. As you can see that empty node is set to inherit only the position of the model we rotate around (so if you will rotate the actual model, it won’t affect the camera). Since the real model is not a child of that node that we rotate (but a parent) it won’t rotate like in the picture.