Moving the camera around an actor by using the mouse

Hello,
I am new in program with Panda3D
and now I have a few questions.

Things that work:

  • loding the terrain from a heightmap
  • scroling in and out (using: mouse wheel)
  • walking (using: w)
  • turning around (using: a & d)
  • turning camera (using: q & e)

My first question is like the subject suggests
how do I get that the camera moves around a actor using the right mouse button?

Here is my script:
I used the sample Roaming Ralph
and changed some things

from math import pi, sin, cos
from direct.showbase.ShowBase import ShowBase
from direct.task import Task
from direct.actor.Actor import Actor
from direct.interval.IntervalGlobal import *
from pandac.PandaModules import * 
import sys

loadPrcFileData('', 'window-title My Game')

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

		self.speed = 0.1
		self.isMoving = False
		render.setShaderAuto() 
		base.disableMouse()
		self.props = WindowProperties()
		
		# create a basic terrain
		self.geomipterrain = GeoMipTerrain ('geomipterrain')
		self.geomipterrain.setBlockSize (16)
		self.geomipterrain.setNearFar (80, 100)
		self.geomipterrain.setHeightfield ('models/heightmap.pgm')
		self.geomipterrain.setAutoFlatten (GeoMipTerrain.AFMOff)
		self.geomipterrain.setFocalPoint(Point3(0,0,0))
		self.geomipterrain.setBruteforce(True)
		self.geomipterrain.setMinLevel(3)

		self.terrainRoot = self.geomipterrain.getRoot()
		self.terrainRoot.setSz (100)  # significant height differences
		self.terrainRoot.setPos (Point3(-257/2, -257/2, 0)) # re-center the terrain
		self.terrainRoot.reparentTo (render)

		# generate terrain squares
		self.geomipterrain.generate()

		# assign a texture
		self.terrainRoot.clearTexture()
		ts = TextureStage('baseColor')
		tex = loader.loadTexture('models/ground.jpg')
		tex.setMagfilter (Texture.FTLinear)
		tex.setMinfilter (Texture.FTLinearMipmapLinear)
		tex.setWrapU(Texture.WMRepeat)
		tex.setWrapV(Texture.WMRepeat)
		self.terrainRoot.setTexture(ts, tex)
		self.terrainRoot.setTexScale(ts, 32, 32)

		# define a shiny material so i can also see some specular lighting on our
		# terrain when autoshader is on
		terrainMat = Material()
		terrainMat.setShininess (12.5)
		terrainMat.setAmbient (VBase4 (1, 1, 1, 1))
		terrainMat.setDiffuse (VBase4 (1, 1, 1, 1))
		terrainMat.setEmission (VBase4 (0, 0, 0, 0))
		terrainMat.setSpecular (VBase4 (1, 1, 1, 1))
		self.terrainRoot.setMaterial (terrainMat) 
		
		
		self.keyMap = {"left":0, "right":0, "forward":0, "jump":0, "cam-left":0, "cam-right":0,
						"mouse1":0, "mouse2":0, "cam-out":0, "cam-in":0 }
		self.mousePos = {"X":0, "Y":0,"X2":0, "Y2":0}
		self.camPos = {"W":0,"H":0}
		base.win.setClearColor(Vec4(0,0,0,1))
		
		self.ralph = Actor("models/ralph.egg.pz",
								 {"run":"models/ralph-run.egg.pz",
								  "walk":"models/ralph-walk.egg.pz"})
		self.ralph.reparentTo(render)
		self.ralph.setScale(.2)
		self.ralph.setPos(0,0,0)
		
		# Create Jump Interval
		self.ralph.jumpUp = LerpFunc(self.move_z,fromData=0,toData=2,duration=0.1,extraArgs=[self.ralph])
		self.ralph.jumpDown = LerpFunc(self.move_z,fromData=2,toData=0,duration=0.1,extraArgs=[self.ralph])
		
		# Create a floater object. I use the "floater" as a temporary
		# variable in a variety of calculations.
		self.floater = NodePath(PandaNode("floater"))
		self.floater.reparentTo(render)
		
		# dummy node for camera, we will rotate the dummy node fro camera rotation
		self.camnode = render.attachNewNode('camparent')
		self.camnode.reparentTo(self.ralph) # inherit transforms
		#self.camnode.setEffect(CompassEffect.make(render)) # NOT inherit rotation
		self.camnode.setZ(3)
		#self.camnode.setX(5)

		self.camera.reparentTo(self.camnode)
		self.camera.setX(0)
		self.camera.setY(20)
		self.camera.setZ(0)
		#self.camera.lookAt(self.camnode)
		self.camera.lookAt(self.ralph)
		 # camera distance from model
		
		#self.camera.setZ(10)
		
		# Accept the control keys for movement and rotation

		self.accept("escape", sys.exit)
		self.accept("a", self.setKey, ["left",1])
		self.accept("d", self.setKey, ["right",1])
		self.accept("w", self.setKey, ["forward",1])
		self.accept("q", self.setKey, ["cam-left",1])
		self.accept("e", self.setKey, ["cam-right",1])
		self.accept("space", self.setKey, ["jump",1])
		self.accept("mouse1", self.setMouse, ["mouse1",1])
		self.accept("mouse3", self.setMouse2, [True])
		self.accept("wheel_up", self.setKey, ["cam-in",1])
		self.accept("wheel_down", self.setKey, ["cam-out",1])
		self.accept("a-up", self.setKey, ["left",0])
		self.accept("d-up", self.setKey, ["right",0])
		self.accept("w-up", self.setKey, ["forward",0])
		self.accept("q-up", self.setKey, ["cam-left",0])
		self.accept("e-up", self.setKey, ["cam-right",0])
		self.accept("space-up", self.setKey, ["jump",0])
		self.accept("mouse1-up", self.setMouse, ["mouse1",0])
		self.accept("mouse3-up", self.setMouse2, [False])
		self.accept("u", self.debug )

		self.taskMgr.add(self.move,"moveTask")

		# Game state variables
		self.isMoving = False
		self.CamDist = 10.0
		self.jumpCalc = {"Time":0.0,"Activ":0}
		self.heading = 0
		self.pitch = 0
		
		# Set up the camera

		base.camera.setPos(self.ralph.getX(),self.ralph.getY()+self.CamDist,2)
	
	def debug(self):
		print "H:",self.heading," P:",self.pitch
	
	def setKey(self, key, value):
		self.keyMap[key] = value
	
	def setMouse2(self,state):
		self.mousePos["X"]=base.win.getPointer(0).getX() 
		self.mousePos["Y"]=base.win.getPointer(0).getY() 
		if state:
			base.win.movePointer(0, 300, 300)
			self.taskMgr.add(self.thirdPersonCameraTask, 'thirdPersonCameraTask') 
		else:
			self.taskMgr.remove('thirdPersonCameraTask') 
		self.props.setCursorHidden(state)
		base.win.requestProperties(self.props)
	
	def setMouse(self, key, value):
		self.keyMap[key] = value
		self.mousePos["X"]=base.win.getPointer(0).getX() 
		self.mousePos["Y"]=base.win.getPointer(0).getY() 
		#print "X:",self.mousePos["X"]
		#print "Y:",self.mousePos["Y"]
		
	def move_z(self,value,actor):
		actor.setZ(actor,value)
			
	def move(self, task):
		self.ralph.setZ(self.terrainRoot, 
								self.geomipterrain.getElevation(self.ralph.getX(self.terrainRoot), 
								self.ralph.getY(self.terrainRoot)))
		self.geomipterrain.update() 
		# If the camera-left key is pressed, move camera left.
		# If the camera-right key is pressed, move camera right.

		#base.camera.lookAt(self.ralph)
		if (self.keyMap["cam-left"]!=0):
			angelDegrees = self.camPos["W"] / (pi / 180.0) - 5.0
			self.camPos["W"] = angelDegrees * (pi / 180.0)
		if (self.keyMap["cam-right"]!=0):
			angelDegrees = self.camPos["W"] / (pi / 180.0) + 5.0
			self.camPos["W"] = angelDegrees * (pi / 180.0)
		if (self.keyMap["cam-out"]!=0):
			self.keyMap["cam-out"]=0
			if (self.CamDist < 10):
				self.CamDist = self.CamDist + 1.0
		if (self.keyMap["cam-in"]!=0):
			self.keyMap["cam-in"]=0
			if (self.CamDist > 1):
				self.CamDist = self.CamDist - 1.0
				
		self.camera.setY(8 * self.CamDist)
		
		#save ralph's initial position so that we can restore it,
		# in case he falls off the map or runs into something.

		startpos = self.ralph.getPos()

		# If a move-key is pressed, move ralph in the specified direction.

		if (self.keyMap["left"]!=0):
			self.ralph.setH(self.ralph.getH() + 300 * globalClock.getDt())
		if (self.keyMap["right"]!=0):
			self.ralph.setH(self.ralph.getH() - 300 * globalClock.getDt())
		if (self.keyMap["forward"]!=0):
			self.ralph.setY(self.ralph, -25 * globalClock.getDt())
		if (self.keyMap["jump"]!=0):
			self.jumpCalc["Activ"]=1
			self.keyMap["jump"]=0

		self.jump()
		# If ralph is moving, loop the run animation.
		# If he is standing still, stop the animation.

		if ((self.keyMap["forward"]!=0) or (self.keyMap["left"]!=0) or (self.keyMap["right"]!=0))  and (self.jumpCalc["Activ"]==0):
			if self.isMoving is False:
				self.ralph.loop("run")
				self.isMoving = True
		else:
			if self.isMoving:
				self.ralph.stop()
				self.ralph.pose("walk",5)
				self.isMoving = False

		# should move the camera around ralph
			
		#if (self.keyMap["mouse2"]!=0):
		#	x = self.mousePos["X"] - base.win.getPointer(0).getX() 
		#	y = self.mousePos["Y"] - base.win.getPointer(0).getY() 
			#self.mousePos["X"]=base.win.getPointer(0).getX() 
			#self.mousePos["Y"]=base.win.getPointer(0).getY() 
		#	p=base.mouseWatcherNode.getMouse() 
		#	base.win.movePointer(0, self.mousePos["X2"], self.mousePos["Y2"])
			#print "X:",x," Y:",y
			#base.camera.setX(base.camera.getX()+x*10)
		#	if x > 0:
		#		angelDegrees = self.camPos["W"] / (pi / 180.0) + 5.0
		#		self.camPos["W"] = angelDegrees * (pi / 180.0)
		#	if x < 0: 
		#		angelDegrees = self.camPos["W"] / (pi / 180.0) - 5.0
		#		self.camPos["W"] = angelDegrees * (pi / 180.0)
		
		
		#self.camera.setPos(20 * sin(self.camPos["W"]),-20.0 * cos(self.camPos["W"]), 3)
		#base.camera.setZ(self.ralph.getZ()+(self.CamDist/30.0*10.0))
		#sets the distance of the camera
		#camvec = self.ralph.getPos() - base.camera.getPos()
		#camvec.setZ(0)
		#camdist = camvec.length()
		#camvec.normalize()
		#if (camdist > self.CamDist):
		#	base.camera.setPos(base.camera.getPos() + camvec*(camdist-self.CamDist))
		#	camdist = self.CamDist
		#if (camdist < self.CamDist):
		#	base.camera.setPos(base.camera.getPos() - camvec*(self.CamDist-camdist))
		#	camdist = self.CamDist

			
			
		#self.floater.setPos(self.ralph.getPos())
		#self.floater.setZ(self.ralph.getZ() + 1.0)
		#base.camera.lookAt(self.floater)
		
		return Task.cont
		
	def jump(self):
		if self.jumpCalc["Activ"]==0:
			return
		jumphight=0.1
		
		if(self.jumpCalc["Time"] > 0):
			Sequence(self.ralph.jumpUp).start()
			
		#if(self.jumpCalc["Time"] > 0.2):
		#	Sequence(self.ralph.jumpDown).start()
			
		if(self.jumpCalc["Time"] > 0.3):
			self.jumpCalc["Time"] = 0
			self.jumpCalc["Activ"] = 0
			
		self.jumpCalc["Time"]+= globalClock.getDt()
		
	def thirdPersonCameraTask(self,task):
		md = base.win.getPointer(0)
		 
		x = md.getX()
		y = md.getY()
		
		if base.win.movePointer(0, 300, 300):
		  self.heading = self.heading - (x - 300) * 0.5
		  self.pitch = self.pitch - (y - 300) * 0.5
		
		if self.pitch > 90:
			self.pitch = 90
		if self.pitch < 0:
			self.pitch = 0
		
		self.camnode.setHpr(self.heading, self.pitch,0)
		
		return task.cont

		
app = MyApp()
app.run()

2 screens:

an other question:
on the second screen you can see that the actor is standing in the ground
how can i fix that ?

I would be happy about some comments
sorry for bad englisch, german …

And heres my camera script:
[orbiting camera controls)

thank you very much
#code updated

About your second question:

getElevation() can be used for collisions with geomipterrain. If you set it up correctly and Ralph does go up and down, then I would guess you could just do something like this instead:

ralph.setZ(terrain.getElevation() + 1)

or something, just so he wont look like his floating in the air too much and also wont intersect with the terrain at too angular places like that.

that is what i do:

self.ralph.setZ(self.terrainRoot,               self.geomipterrain.getElevation(self.ralph.getX(self.terrainRoot),                     self.ralph.getY(self.terrainRoot))) 

get the Elevation at ralph’s x and y coords

it is working but not for all places

maybe the problem is in the heightmap ?

if you are using a heightmap set brute force to true and see if the issue still persists if so its due to the way culling of the terrain happens, you can fix this by setting up a distance system to do this if the camera is only so far away. Im not sure the exact code here i’ll update later when i get home if I remember or it was not added yet.

Bruteforce is already true

The value returned by getElevation is in the terrain’s coordinate space. So in your case, you need to multiply the result by 100.

still the same
cause

.setZ(.GetElevation(x,y)*100)

same effect like

.setZ(terrain,.GetElevation(x,y))

just relatively to terrain

Well my issue that I had when I was using this method at first was that 1 lod was set wrong But if brute is ture then thats not it. The next thing I had was I was still using collisions plus the set z so at some points where he didnt collide with the ground he would sink to his waist like yours, this had to do with not compensating for where the node was on the actor. Maybe something to look at.

Honestly I am out after these cause I use the same code and heightmap and I never have this issue.

it is just in front and behind the hill