I was extracting this out of my game and my editor into a separate module – something I’ve been wanting to do for a long time now, yet could never get to it – and thought people could find it useful. There are many snippets like this laying around here, but I’m not sure any of them has those three things (fpp, orbit and pan) together and easy to use, so there you go.
The movement is without the character controller stuff, just something like “fly” or “noclip” mode you can find in games, but you don’t have to use it – it’s there for stuff like editors or aforementioned game modes or the likes.
EDIT: A little bug fix.
EDIT2: Some parts of the code were referring to base.win instead of self.targetWin
from panda3d.core import Vec3, Quat, Point3, WindowProperties
class MouseLook(object):
MLMFPPNoCenter = 0
MLMOrbit = 1
MLMPan = 2
MLMFPPCenter = 3
def __init__(self, targetCam = None, targetWin = None):
self.setTargetCamera(targetCam)
self.setTargetWin(targetWin)
self.orbitCenter = Point3(0, 0, 0)
self.mouseLookMode = self.MLMFPPCenter
self.zoomOrtho = False
self.limitH = {
"left": None,
"right": None,
"relative": render,
}
self.movementSpeed = 128.0
self.wheelSpeed = 8.0
self.mouseLookSpeed = [0.1, 0.1]
self.camMove = dict(
forward = False,
backward = False,
strafeLeft = False,
strafeRight = False,
down = False,
up = False,
)
def zoom(self, direction):
step = 64
top = 32768
bottom = 64
fsH, size = self.lens.getFilmSize()
size -= direction * step
if size < bottom:
size = bottom
for vp in self.editorBase.viewports[1:4]:
vp.zoomLocal(size)
def zoomLocal(self, size):
fsH = size * self.aspectRatio
fsV = size
self.lens.setFilmSize(fsH, fsV)
def enable(self, ownsTask = True):
self.prevX = self.targetWin.getPointer(0).getX()
self.prevY = self.targetWin.getPointer(0).getY()
if ownsTask:
taskMgr.add(self.update, "UpdateMouseLook")
def disable(self):
taskMgr.remove("UpdateMouseLook")
def clearMovement(self):
self.camMove = dict(
forward = False,
backward = False,
strafeLeft = False,
strafeRight = False,
down = False,
up = False,
)
def setLimitH(self, left, right, relative = None):
if relative is None:
relative = render
self.limitH = {
"left": left,
"right": right,
"relative": relative
}
def enableMovement(self):
base.accept("w", self.setCamMove, extraArgs = ["forward", True])
base.accept("w-up", self.setCamMove, extraArgs = ["forward", False])
base.accept("s", self.setCamMove, extraArgs = ["backward", True])
base.accept("s-up", self.setCamMove, extraArgs = ["backward", False])
base.accept("a", self.setCamMove, extraArgs = ["strafeLeft", True])
base.accept("a-up", self.setCamMove, extraArgs = ["strafeLeft", False])
base.accept("d", self.setCamMove, extraArgs = ["strafeRight", True])
base.accept("d-up", self.setCamMove, extraArgs = ["strafeRight", False])
base.accept("control", self.setCamMove, extraArgs = ["down", True])
base.accept("control-up", self.setCamMove, extraArgs = ["down", False])
base.accept("space", self.setCamMove, extraArgs = ["up", True])
base.accept("space-up", self.setCamMove, extraArgs = ["up", False])
base.accept("wheel_up", self.moveCamera, extraArgs = [Vec3(0, 1, 0)])
base.accept("wheel_down", self.moveCamera, extraArgs = [Vec3(0, -1, 0)])
def setCamMove(self, key, val):
self.camMove[key] = val
def update(self, task = None):
mouse = self.targetWin.getPointer(0)
x = mouse.getX()
y = mouse.getY()
deltaX = (x - self.prevX) * self.mouseLookSpeed[0]
deltaY = (y - self.prevY) * self.mouseLookSpeed[1]
self.prevX = x
self.prevY = y
if self.mouseLookMode == self.MLMFPPNoCenter:
print "update fpp no center"
self.updateFPPNoCenter(deltaX, deltaY)
elif self.mouseLookMode == self.MLMOrbit:
print "update orbit"
self.updateOrbit(deltaX, deltaY)
elif self.mouseLookMode == self.MLMPan:
print "update pan"
self.updatePan(deltaX, deltaY)
if self.mouseLookMode == self.MLMFPPCenter:
print "update fpp center"
self.updateFPPCenter()
if self.limitH["left"] is not None:
rel = self.limitH["relative"]
h = self.targetCamera.getH(rel)
if h < self.limitH["left"]:
h = self.limitH["left"]
elif h > self.limitH["right"]:
h = self.limitH["right"]
self.targetCamera.setH(rel, h)
linVel = Vec3(0,0,0)
if self.camMove["forward"]: linVel[1] = self.movementSpeed
if self.camMove["backward"]: linVel[1] = -self.movementSpeed
if self.camMove["strafeLeft"]: linVel[0] = -self.movementSpeed
if self.camMove["strafeRight"]: linVel[0] = self.movementSpeed
if self.camMove["up"]: linVel[2] = self.movementSpeed
if self.camMove["down"]: linVel[2] = -self.movementSpeed
linVel *= globalClock.getDt()
self.moveCamera(linVel)
if task is not None:
return task.cont
def moveCamera(self, vector):
self.targetCamera.setPos(self.targetCamera, vector * self.wheelSpeed)
def rotateAround(self, node, point, axis, angle, relative):
quat = Quat()
quat.setFromAxisAngle(angle, render.getRelativeVector(relative, axis))
relativePos = node.getPos(render) - point
relativePosRotated = quat.xform(relativePos)
absolutePosRotated = relativePosRotated + point
node.setPos(render, absolutePosRotated)
node.setQuat(render, node.getQuat(render) * quat)
def setTargetCamera(self, cam):
if cam is None:
self.targetCamera = base.camera
else:
self.targetCamera = cam
def setTargetWin(self, win):
if win is None:
self.targetWin = base.win
else:
self.targetWin = win
def setMouseModeRelative(self, state):
props = WindowProperties()
if not state:
props.setMouseMode(WindowProperties.MAbsolute)
else:
props.setMouseMode(WindowProperties.MRelative)
self.targetWin.requestProperties(props)
def setCursorHidden(self, state):
props = WindowProperties()
props.setCursorHidden(state)
self.targetWin.requestProperties(props)
def updateFPPNoCenter(self, deltaX, deltaY):
p = self.targetCamera.getP() - deltaY
h = self.targetCamera.getH() - deltaX
if abs(p) > 90:
p = 90 * cmp(p, 0)
self.targetCamera.setP(p)
self.targetCamera.setH(h)
def updateFPPCenter(self):
winSizeX = self.targetWin.getXSize()/2
winSizeY = self.targetWin.getYSize()/2
mouse = self.targetWin.getPointer(0)
x = mouse.getX() - winSizeX
y = mouse.getY() - winSizeY
h = x * (self.mouseLookSpeed[0])
h = self.targetCamera.getH() - h
self.targetCamera.setH(h)
p = y * (self.mouseLookSpeed[1])
p = self.targetCamera.getP() - p
if p < 90.0 and p > -90.0:
self.targetCamera.setP(p)
self.targetWin.movePointer(0, winSizeX, winSizeY)
def updateOrbit(self, deltaX, deltaY):
self.rotateAround(self.targetCamera, self.orbitCenter, Vec3(0, 0, 1), -deltaX, render)
self.rotateAround(self.targetCamera, self.orbitCenter, Vec3(1, 0, 0), -deltaY, self.targetCamera)
def updatePan(self, deltaX, deltaY):
vector = Vec3(-deltaX, 0, deltaY) * 1/globalClock.getDt() * 0.01
self.moveCamera(vector)
if __name__ == "__main__":
from direct.showbase.ShowBase import ShowBase
import sys
ShowBase()
base.camLens.setFov(90.0)
base.disableMouse()
base.setFrameRateMeter(True)
globalClock.setMode(globalClock.MLimited)
globalClock.setFrameRate(120)
smiley = loader.loadModel("smiley")
smiley.reparentTo(render)
smiley.setScale(128)
base.accept("escape", sys.exit)
base.camera.setY(-1024)
m = MouseLook()
m.enable()
m.enableMovement()
m.setMouseModeRelative(True)
m.setCursorHidden(True)
mode = m.mouseLookMode
def cycle():
global mode
mode += 1
if mode > 3:
mode = 0
print mode
m.mouseLookMode = mode
base.accept("f1", cycle)
run()