Amazingly i could not find that kind of snippet anywhere. So i cranked up my rusty brain, remembered little trig i learned at school and made this self-contained snippet:
from direct.showbase.DirectObject import DirectObject
from math import pi, sin, cos
def clamp(val, minVal, maxVal):
return min(max(val, minVal), maxVal)
class OrbitCameraController(DirectObject):
def __init__(self, camera, distance=10, distance_limits=(5, 50), name='OrbitCameraController'):
self.camera = camera
self.distance = distance
self.distance_limits = distance_limits
self._is_rotating = False
self._rotate_start = (0, 0)
self._h = self._v = 0
self._vertical_limits = (-pi / 2 + 0.1, pi / 2 - 0.1)
self.accept('mouse3', self._setRotating, [True])
self.accept('mouse3-up', self._setRotating, [False])
self.accept('wheel_up', self.zoom, [1])
self.accept('wheel_down', self.zoom, [-1])
self._camera_task = base.taskMgr.add(self._task, name)
self._orbitCamera(0, 0) # knock camera into orbit
def zoom(self, d):
self.distance = clamp(self.distance + d, *self.distance_limits)
self._orbitCamera(0, 0)
def _setRotating(self, rotating=True):
self._is_rotating = rotating
base.setMouseHidden(rotating)
if not rotating:
base.win.movePointer(0, *self._rotate_start)
else:
md = base.win.getPointer(0)
self._rotate_start = (md.getX(), md.getY())
def _task(self, task):
if not self._is_rotating:
return task.cont
if not base.mouseWatcherNode.hasMouse():
return task.cont
md = base.win.getPointer(0)
old_x, old_y = self._rotate_start
dx = md.getX() - old_x
dy = md.getY() - old_y
if dx or dy:
base.win.movePointer(0, *self._rotate_start)
self._orbitCamera(dx, dy)
return task.cont
def _orbitCamera(self, dx, dy):
distance = self.distance
h = self._h - dx * 0.01
v = self._v + dy * 0.01
v = clamp(v, *self._vertical_limits)
posZ = sin(v) * distance
horizontal_plane_dist = cos(v) * distance
posX = cos(h) * horizontal_plane_dist
posY = sin(h) * horizontal_plane_dist
self._h = h
self._v = v
self.camera.setPos(posX, posY, posZ)
self.camera.lookAt(self.camera.getParent())
Camera orbits around it’s parent node. Zooming with mouse wheel works.
Comments would be appreciated, especially on code in _orbitCamera(). I went most straight-forward way by using vertical (zy plane) and horizontal (xy plane) angles, and solved two triangles to get correct pos of camera. Maybe there is better way?