Adding support for multi-PC or multi-piping.

This commit is contained in:
khillesl 2001-08-09 21:54:08 +00:00
parent 7c7300ff74
commit 8b5ea4acc5
9 changed files with 656 additions and 25 deletions

View File

@ -0,0 +1,151 @@
"""ClusterClient: Master for mutlipiping or PC clusters. """
from PandaModules import *
from TaskManagerGlobal import *
from ShowBaseGlobal import *
import Task
import DirectNotifyGlobal
import Datagram
import DirectObject
from ClusterMsgs import *
import time
class ClusterConfigItem:
def __init__(self, serverFunction, serverName, pos, hpr, port):
self.serverName = serverName
self.serverFunction = serverFunction
self.xyz = pos
self.hpr = hpr
self.port = port
class DisplayConnection:
def __init__(self,qcm,serverName,port,msgHandler):
self.msgHandler = msgHandler
gameServerTimeoutMs = base.config.GetInt("game-server-timeout-ms",
20000)
# A big old 20 second timeout.
self.tcpConn = qcm.openTCPClientConnection(
serverName, port, gameServerTimeoutMs)
# Test for bad connection
if self.tcpConn == None:
return None
else:
self.tcpConn.setNoDelay(1)
self.qcr=QueuedConnectionReader(qcm, 0)
self.qcr.addConnection(self.tcpConn)
self.cw=ConnectionWriter(qcm, 0)
def sendCamOffset(self,xyz,hpr):
ClusterManager.notify.debug("send cam offset...")
ClusterManager.notify.debug( ("packet %d xyz,hpr=%f %f %f %f %f %f" %
(self.msgHandler.packetNumber,xyz[0],xyz[1],xyz[2],
hpr[0],hpr[1],hpr[2])) )
datagram = self.msgHandler.makeCamOffsetDatagram(xyz, hpr)
self.cw.send(datagram, self.tcpConn)
def sendMoveCam(self,xyz,hpr):
ClusterManager.notify.debug("send cam move...")
ClusterManager.notify.debug( ("packet %d xyz,hpr=%f %f %f %f %f %f" %
(self.msgHandler.packetNumber,xyz[0],xyz[1],xyz[2],
hpr[0],hpr[1],hpr[2])) )
datagram = self.msgHandler.makeMoveCamDatagram(xyz, hpr)
self.cw.send(datagram, self.tcpConn)
# the following should only be called by a synchronized cluster manger
def getSwapReady(self):
datagram = self.msgHandler.blockingRead(self.qcr)
(type,dgi) = self.msgHandler.readHeader(datagram)
if type != CLUSTER_SWAP_READY:
self.notify.warning( ('was expecting SWAP_READY, got %d' % type) )
# the following should only be called by a synchronized cluster manger
def sendSwapNow(self):
ClusterManager.notify.debug( ("dispaly connect send swap now, packet %d" % self.msgHandler.packetNumber))
datagram = self.msgHandler.makeSwapNowDatagram()
self.cw.send(datagram, self.tcpConn)
class ClusterManager(DirectObject.DirectObject):
notify = DirectNotifyGlobal.directNotify.newCategory("ClusterClient")
MGR_NUM = 1000000
def __init__(self, dispConfigs):
self.qcm=QueuedConnectionManager()
self.dispList = []
self.msgHandler = MsgHandler(ClusterManager.MGR_NUM,self.notify)
for dispConfig in dispConfigs:
thisDisp = DisplayConnection(self.qcm,dispConfig.serverName,
dispConfig.port,self.msgHandler)
if thisDisp == None:
self.notify.error( ('Could not open %s on %s port %d' %
(dispConfig.serverFunction,
dispConfig.serverName,
dispConfig.port)) )
else:
self.dispList.append(thisDisp)
self.dispList[len(self.dispList)-1].sendCamOffset(
dispConfig.xyz,dispConfig.hpr)
self.startMoveCamTask()
def moveCamera(self, xyz, hpr):
for disp in self.dispList:
disp.sendMoveCam(xyz,hpr)
def startMoveCamTask(self):
task = Task.Task(self.moveCameraTask,49)
taskMgr.spawnTaskNamed(task, "moveCamTask")
return None
def moveCameraTask(self,task):
self.moveCamera(
base.camera.getPos(render),
base.camera.getHpr(render))
return Task.cont
class ClusterManagerSync(ClusterManager):
def __init__(self, dispConfigs):
ClusterManager.__init__(self, dispConfigs)
#I probably don't need this
self.waitForSwap = 0
self.ready = 0
self.startSwapCoordinatorTask()
def startSwapCoordinatorTask(self):
task = Task.Task(self.swapCoordinator,51)
taskMgr.spawnTaskNamed(task, "clientSwapCoordinator")
return None
def swapCoordinator(self,task):
self.ready = 1
if self.waitForSwap:
self.waitForSwap=0
self.notify.debug("START get swaps----------------------------------")
localClock = ClockObject()
t1 = localClock.getRealTime()
for disp in self.dispList:
disp.getSwapReady()
self.notify.debug("----------------START swap now--------------------")
t2 = localClock.getRealTime()
for disp in self.dispList:
disp.sendSwapNow()
self.notify.debug("------------------------------START swap----------")
t3 = localClock.getRealTime()
base.win.swap()
t4 = localClock.getRealTime()
self.notify.debug("------------------------------------------END swap")
self.notify.debug( ("times=%f %f %f %f" % (t1,t2,t3,t4)) )
self.notify.debug( ("deltas=%f %f %f" % (t2-t1,t3-t2,t4-t3)) )
return Task.cont
def moveCamera(self,xyz,hpr):
if self.ready:
self.notify.debug('moving synced camera')
ClusterManager.moveCamera(self,xyz,hpr)
self.waitForSwap=1

View File

@ -0,0 +1,52 @@
from PandaObject import *
from ClusterClient import *
import string
# this is an array of offsets for the various servers. For example,
# mono-modelcave-pipe0 has one server with both a pos and hpr of 0.
# For mono-modelcave-pipe0, I decided to set the offsets in the
# actual configuration for the display.
ClientConfigs = {'mono-modelcave-pipe0': [ [Vec3(0,0,0),Vec3(0,0,0)] ]
# The following is a fake configuration to show an example with two servers.
# 'two-server' : [ [Vec3(0,0,0),Vec3(-60,0,0)],
# [Vec3(0,0,0),Vec3(60,0,0) ] ]
def createCluster():
# setup camera offsets based on chanconfig
# This should ideally be independent of chan-config. In other
# words, there might be some other configuration for clustering,
# from which the number and offset of the servers' cameras are set.
chanConfig = base.config.GetString('chan-config', 'single')
if base.config.GetBool('display-client'):
if not ClientConfigs.has_key(chanConfig):
base.notify.warning('Display Client flag set, but %s is not a display client configuration.' % chanConfig)
return None
thisClientConfig = ClientConfigs[chanConfig]
displayConfigs = []
for i in range(len(thisClientConfig)):
configString = 'display%d' % i
serverString = base.config.GetString(configString, '')
if serverString == '':
base.notify.warning('%s should be defined in Configrc to use %s as display client.' % [configString,chanConfig])
base.notify.warning('%s will not be used.' % configString)
else:
serverInfo = string.split(serverString)
if len(serverInfo) > 1:
port = int(serverInfo[1])
else:
port = CLUSTER_PORT
displayConfigs.append(ClusterConfigItem(configString,
serverInfo[0], thisClientConfig[i][0], thisClientConfig[i][1],port))
synced = base.config.GetBool('sync-display',0)
if synced:
base.win.setSync(1)
return ClusterManagerSync(displayConfigs)
else:
return ClusterManager(displayConfigs)

View File

@ -0,0 +1,114 @@
"""ClusterMsgs module: Message types for Cluster rendering"""
CLUSTER_NOTHING = -1
CLUSTER_CAM_OFFSET = 1
CLUSTER_POS_UPDATE = 2
CLUSTER_SWAP_READY = 3
CLUSTER_SWAP_NOW = 4
#Port number for cluster rendering
CLUSTER_PORT = 1970
from ShowBaseGlobal import *
from PandaModules import *
from TaskManagerGlobal import *
import Task
import DirectNotifyGlobal
import Datagram
import time
class MsgHandler:
"""MsgHandler: wrapper for PC clusters/multi-piping networking"""
def __init__(self,packetStart, notify):
"""packetStart can be used to distinguish which MsgHandler sends a
given packet."""
self.packetNumber = packetStart
self.notify = notify
def nonBlockingRead(self,qcr):
availGetVal = qcr.dataAvailable()
if availGetVal:
datagram = NetDatagram()
readRetVal = qcr.getData(datagram)
if readRetVal:
dgi = DatagramIterator(datagram)
number = dgi.getUint32()
type = dgi.getUint8()
self.notify.debug( ("Packet %d type %d recieved" % (number,type)) )
else:
self.notify.warning("getData returned false")
else:
type = CLUSTER_NOTHING
dgi = None
return (type,dgi)
def readHeader(self,datagram):
dgi = DatagramIterator(datagram)
number = dgi.getUint32()
type = dgi.getUint8()
self.notify.info( ("Packet %d type %d recieved" % (number,type)) )
return (type,dgi)
def blockingRead(self,qcr):
availGetVal = 0
while not availGetVal:
availGetVal = qcr.dataAvailable()
if not availGetVal:
time.sleep(0.002)
type = CLUSTER_NOTHING
datagram = NetDatagram()
readRetVal = qcr.getData(datagram)
if not readRetVal:
self.notify.warning("getData returned false")
return datagram
def makeCamOffsetDatagram(self,xyz,hpr):
datagram = Datagram.Datagram()
datagram.addUint32(self.packetNumber)
self.packetNumber = self.packetNumber + 1
datagram.addUint8(CLUSTER_CAM_OFFSET)
datagram.addFloat32(xyz[0])
datagram.addFloat32(xyz[1])
datagram.addFloat32(xyz[2])
datagram.addFloat32(hpr[0])
datagram.addFloat32(hpr[1])
datagram.addFloat32(hpr[2])
return datagram
def makeMoveCamDatagram(self,xyz,hpr):
datagram = Datagram.Datagram()
datagram.addUint32(self.packetNumber)
self.packetNumber = self.packetNumber + 1
datagram.addUint8(CLUSTER_POS_UPDATE)
datagram.addFloat32(xyz[0])
datagram.addFloat32(xyz[1])
datagram.addFloat32(xyz[2])
datagram.addFloat32(hpr[0])
datagram.addFloat32(hpr[1])
datagram.addFloat32(hpr[2])
return datagram
def makeSwapNowDatagram(self):
datagram = Datagram.Datagram()
datagram.addUint32(self.packetNumber)
self.packetNumber = self.packetNumber + 1
datagram.addUint8(CLUSTER_SWAP_NOW)
return datagram
def makeSwapReadyDatagram(self):
datagram = Datagram.Datagram()
datagram.addUint32(self.packetNumber)
self.packetNumber = self.packetNumber + 1
datagram.addUint8(CLUSTER_SWAP_READY)
return datagram

View File

@ -0,0 +1,241 @@
"""ServerRepository module: contains the ServerRepository class"""
from ShowBaseGlobal import *
from ClusterMsgs import *
import DirectObject
import Datagram
#import DatagramIterator
#import NetDatagram
import __builtin__
import time
from PandaModules import *
from TaskManagerGlobal import *
from MsgTypes import *
import Task
import DirectNotifyGlobal
# Cam offset handling is a little sloppy. The problem is that there is a
# single arc used for both movement of the camera, and the offset of the
# group.
# Also, I'm not sure multiple camera-group configurations are working for the
# cluster system.
class ClusterServer(DirectObject.DirectObject):
notify = DirectNotifyGlobal.directNotify.newCategory("ClusterServer")
MSG_NUM = 2000000
def __init__(self,cameraGroup,camera):
self.qcm = QueuedConnectionManager()
self.qcl = QueuedConnectionListener(self.qcm, 0)
self.qcr = QueuedConnectionReader(self.qcm, 0)
self.cw = ConnectionWriter(self.qcm,0)
port = base.config.GetInt("cluster-server-port",CLUSTER_PORT)
self.tcpRendezvous = self.qcm.openTCPServerRendezvous(port, 1)
print self.tcpRendezvous
self.cameraGroup = cameraGroup
self.camera = camera
self.qcl.addConnection(self.tcpRendezvous)
self.msgHandler = MsgHandler(ClusterServer.MSG_NUM,self.notify)
self.startListenerPollTask()
self.startReaderPollTask()
self.posOffset = Vec3(0,0,0)
self.hprOffset = Vec3(0,0,0)
return None
def startListenerPollTask(self):
task = Task.Task(self.listenerPoll)
taskMgr.spawnTaskNamed(task, "serverListenerPollTask")
return None
def listenerPoll(self, task):
if self.qcl.newConnectionAvailable():
print "New connection is available"
rendezvous = PointerToConnection()
netAddress = NetAddress()
newConnection = PointerToConnection()
retVal = self.qcl.getNewConnection(rendezvous, netAddress,
newConnection)
if retVal:
# Crazy dereferencing
newConnection=newConnection.p()
self.qcr.addConnection(newConnection)
print "Got a connection!"
self.lastConnection = newConnection
else:
self.notify.warning(
"getNewConnection returned false")
return Task.cont
def startReaderPollTask(self):
task = Task.Task(self.readerPollUntilEmpty,-10)
taskMgr.spawnTaskNamed(task, "serverReaderPollTask")
return None
def readerPollUntilEmpty(self, task):
while self.readerPollOnce():
pass
return Task.cont
def readerPollOnce(self):
availGetVal = self.qcr.dataAvailable()
if availGetVal:
datagram = NetDatagram()
readRetVal = self.qcr.getData(datagram)
if readRetVal:
self.handleDatagram(datagram)
else:
self.notify.warning("getData returned false")
return availGetVal
def handleCamOffset(self,dgi):
x=dgi.getFloat32()
y=dgi.getFloat32()
z=dgi.getFloat32()
h=dgi.getFloat32()
p=dgi.getFloat32()
r=dgi.getFloat32()
self.notify.debug((' new offset=%f %f %f %f %f %f' %
(x,y,z,h,p,r)))
self.posOffset = Vec3(x,y,z)
self.hprOffset = Vec3(h,p,r)
def handleCamMovement(self,dgi):
x=dgi.getFloat32()
y=dgi.getFloat32()
z=dgi.getFloat32()
h=dgi.getFloat32()
p=dgi.getFloat32()
r=dgi.getFloat32()
self.notify.debug((' new position=%f %f %f %f %f %f' %
(x,y,z,h,p,r)))
finalX = x + self.posOffset[0]
finalY = y + self.posOffset[1]
finalZ = z + self.posOffset[2]
finalH = h + self.hprOffset[0]
finalP = p + self.hprOffset[1]
finalR = r + self.hprOffset[2]
self.cameraGroup.setPosHpr(render,finalX,finalY,finalZ,
finalH,finalP,finalR)
def handleDatagram(self, datagram):
(type, dgi) = msgHandler.nonBlockingRead(self.qcr)
if type==CLUSTER_CAM_OFFSET:
self.handleCamOffset(dgi)
elif type==CLUSTER_POS_UPDATE:
self.handleCamMovement(dgi)
elif type==CLUSTER_SWAP_READY:
pass
elif type==CLUSTER_SWAP_NOW:
pass
else:
self.notify.warning("recieved unknown packet")
return type
class ClusterServerSync(ClusterServer):
def __init__(self,cameraGroup,camera):
self.notify.info('starting ClusterServerSync')
self.startReading = 0
self.posRecieved = 0
ClusterServer.__init__(self,cameraGroup,camera)
self.startSwapCoordinator()
return None
def startListenerPollTask(self):
task = Task.Task(self.listenerPoll,-2)
taskMgr.spawnTaskNamed(task, "serverListenerPollTask")
return None
def listenerPoll(self, task):
if self.qcl.newConnectionAvailable():
print "New connection is available"
rendezvous = PointerToConnection()
netAddress = NetAddress()
newConnection = PointerToConnection()
retVal = self.qcl.getNewConnection(rendezvous, netAddress,
newConnection)
if retVal:
# Crazy dereferencing
newConnection=newConnection.p()
self.qcr.addConnection(newConnection)
print "Got a connection!"
self.lastConnection = newConnection
datagram = self.msgHandler.blockingRead(self.qcr)
(type,dgi) = self.msgHandler.readHeader(datagram)
if type==CLUSTER_CAM_OFFSET:
self.handleCamOffset(dgi)
else:
self.notify.warning("Wanted cam offset, got something else")
self.startReading = 1
# now that we have the offset read, can start reading
else:
self.notify.warning("getNewConnection returned false")
return Task.cont
def startReaderPollTask(self):
task = Task.Task(self.readPos,-1)
taskMgr.spawnTaskNamed(task, "serverReadPosTask")
return None
def readPos(self, task):
if self.startReading and self.qcr.isConnectionOk(self.lastConnection):
datagram = self.msgHandler.blockingRead(self.qcr)
(type,dgi) = self.msgHandler.readHeader(datagram)
if type == CLUSTER_POS_UPDATE:
self.posRecieved = 1
self.handleCamMovement(dgi)
elif type == CLUSTER_CAM_OFFSET:
self.handleCamOffset(dgi)
else:
self.notify.warning('expected pos or orientation, instead got %d' % type)
else:
self.startReading = 0 # keep this 0 as long as connection not ok
return Task.cont
def sendSwapReady(self):
self.notify.debug( ('send swap ready packet %d' %
self.msgHandler.packetNumber ) )
datagram = self.msgHandler.makeSwapReadyDatagram()
self.cw.send(datagram, self.lastConnection)
def startSwapCoordinator(self):
task = Task.Task(self.swapCoordinatorTask, 51)
taskMgr.spawnTaskNamed(task, "serverSwapCoordinator")
return None
def swapCoordinatorTask(self, task):
if self.posRecieved:
self.posRecieved = 0
localClock = ClockObject()
# print "START send-------------------------------"
t1 = localClock.getRealTime()
self.sendSwapReady()
# print "-----------START read--------------------"
t2 = localClock.getRealTime()
datagram = self.msgHandler.blockingRead(self.qcr)
(type,dgi) = self.msgHandler.readHeader(datagram)
if type == CLUSTER_SWAP_NOW:
self.notify.debug('swapping')
# print "---------------------START SWAP----------"
t3 = localClock.getRealTime()
base.win.swap()
t4 = localClock.getRealTime()
# print "---------------------------------END SWAP"
# print "times=",t1,t2,t3,t4
# print "deltas=",t2-t1,t3-t2,t4-t3
else:
self.notify.warning("did not get expected swap now")
return Task.cont

View File

@ -1,6 +1,7 @@
lib
lib/py
src/actor
src/cluster
src/directtools
src/directdevices
src/directnotify

View File

@ -1,13 +1,25 @@
from PandaObject import *
def setCameraOffsets(camList):
def setCameraOffsets(camList,camGroup):
# setup camera offsets based on chanconfig
chanConfig = base.config.GetString('chan-config', 'single')
if chanConfig == 'cave3':
camList[0].setHpr(camList[0], -90, 0, 0)
camList[2].setHpr(camList[2], 90, 0, 0)
camList[0].setH(camGroup[0],-60)
camList[2].setH(camGroup[0], 60)
elif chanConfig == 'cave3+god':
camList[0].setHpr(camList[0], -90, 0, 0)
camList[2].setHpr(camList[2], 90, 0, 0)
camList[0].setH(camGroup[0], -60)
camList[2].setH(camGroup[0], 60)
elif chanConfig == 'mono-cave':
camList[4].setH(camGroup[0], 120)
camList[5].setH(camGroup[0], 60)
elif chanConfig == 'mono-modelcave-pipe0':
camList[0].setH(camGroup[0], 120)
camList[1].setH(camGroup[0], 60)
camList[2].setH(camGroup[0], 0)
elif chanConfig == 'mono-modelcave-pipe1':
camList[0].setH(camGroup[0], -120)
camList[1].setH(camGroup[0], -60)

View File

@ -294,7 +294,9 @@ class DirectCameraControl(PandaObject):
nodePath = render.findPathDownTo(node)
if camera not in nodePath.getAncestry():
# Compute hit point
hitPt = direct.iRay.parentToHitPt(entry)
# KEH: use current display region ray
# hitPt = direct.iRay.parentToHitPt(entry)
hitPt = direct.drList.getCurrentDr().iRay.parentToHitPt(entry)
# Move coa marker to new point
self.updateCoa(hitPt, ref = self.coaMarkerRef)
else:
@ -304,6 +306,7 @@ class DirectCameraControl(PandaObject):
def computeCOA(self, node, hitPt, hitPtDist):
coa = Point3(0)
dr = direct.drList.getCurrentDr()
if self.fLockCOA:
# COA is locked, use existing point
# Use existing point
@ -315,8 +318,8 @@ class DirectCameraControl(PandaObject):
# Set center of action
coa.assign(hitPt)
# Handle case of bad coa point (too close or too far)
if ((hitPtDist < (1.1 * direct.dr.near)) or
(hitPtDist > direct.dr.far)):
if ((hitPtDist < (1.1 * dr.near)) or
(hitPtDist > dr.far)):
# Just use existing point
coa.assign(self.coaMarker.getPos(direct.camera))
# Reset hit point count
@ -334,7 +337,7 @@ class DirectCameraControl(PandaObject):
coa.set(0,dist,0)
# Compute COA Dist
coaDist = Vec3(coa - ZERO_POINT).length()
if coaDist < (1.1 * direct.dr.near):
if coaDist < (1.1 * dr.near):
coa.set(0,100,0)
coaDist = 100
# Update coa and marker
@ -346,7 +349,9 @@ class DirectCameraControl(PandaObject):
coaDist = Vec3(self.coa - ZERO_POINT).length()
# Place the marker in render space
if ref == None:
ref = base.cam
# KEH: use the current display region
# ref = base.cam
ref = direct.drList.getCurrentDr().cam
self.coaMarker.setPos(ref, self.coa)
# Resize it
self.updateCoaMarkerSize(coaDist)
@ -360,7 +365,9 @@ class DirectCameraControl(PandaObject):
def updateCoaMarkerSize(self, coaDist = None):
if not coaDist:
coaDist = Vec3(self.coaMarker.getPos( direct.camera )).length()
sf = COA_MARKER_SF * coaDist * math.tan(deg2Rad(direct.dr.fovV))
# KEH: use current display region for fov
# sf = COA_MARKER_SF * coaDist * math.tan(deg2Rad(direct.dr.fovV))
sf = COA_MARKER_SF * coaDist * math.tan(deg2Rad(direct.drList.getCurrentDr().fovV))
if sf == 0.0:
sf = 0.1
self.coaMarker.setScale(sf)

View File

@ -8,6 +8,8 @@ from DirectGeometry import *
from DirectLights import *
from DirectSessionPanel import *
from DirectCamConfig import *
from ClusterServer import *
from ClusterConfig import *
from tkSimpleDialog import askstring
import Placer
import EntryScale
@ -31,6 +33,9 @@ class DirectSession(PandaObject):
self.font = Loader.Loader.loadFont(loader,
"models/fonts/Comic",
priority = 100)
self.iAmAClient = base.config.GetBool("display-client",0)
self.clusterManager = createCluster()
self.iAmAServer = base.config.GetBool("display-server",0)
self.fEnabled = 0
self.drList = DisplayRegionList()
self.iRayList = map(lambda x: x.iRay, self.drList)
@ -150,6 +155,15 @@ class DirectSession(PandaObject):
from TkGlobal import *
self.panel = DirectSessionPanel(parent = tkroot)
if self.iAmAServer:
if base.config.GetBool('sync-display',0):
self.serverManager = ClusterServerSync(base.cameraList[0],
self.drList[0].cam)
base.win.setSync(1)
else:
self.serverManager = ClusterServer(base.cameraList[0],
self.drList[0].cam)
def enable(self):
# Make sure old tasks are shut down
self.disable()
@ -642,20 +656,26 @@ class DisplayRegionList(PandaObject):
i = 0
for cameraGroup in base.cameraList:
# This is following the old way of setting up
# display regions. A display region is set up for
# each camera node in the scene graph. This was done
# so that only display regions in the scene graph are
# considered. The right way to do this is to set up
# a display region for each real display region, and then
# keep track of which are currently active (e.g. use a flag)
# processing only them.
camList=cameraGroup.findAllMatches('**/+Camera')
for cameraIndex in range(camList.getNumPaths()):
camera = camList[cameraIndex]
if camera.getName()=='<noname>':
camera.setName('Camera%d' % cameraIndex)
group = base.groupList[cameraIndex]
self.displayRegionList.append(
DisplayRegionContext(base.win,
camera))
camera,group))
if camera.getName()!='<noname>' or len(camera.getName())==0:
self.displayRegionLookup[camera.getName()]=i
i = i + 1
for cameraIndex in range(len(base.groupList)):
self.displayRegionList[cameraIndex].setGroup(
base.groupList[cameraIndex])
self.accept("CamChange",self.camUpdate)
self.accept("DIRECT_mouse1",self.mouseUpdate)
self.accept("DIRECT_mouse2",self.mouseUpdate)
@ -668,7 +688,7 @@ class DisplayRegionList(PandaObject):
cameraList = []
for dr in self.displayRegionList:
cameraList.append(dr.cam)
setCameraOffsets(cameraList)
setCameraOffsets(cameraList,base.cameraList)
def __getitem__(self, index):
return self.displayRegionList[index]
@ -693,11 +713,17 @@ class DisplayRegionList(PandaObject):
def setFov(self, hfov, vfov):
for dr in self.displayRegionList:
dr.camNode.setFov(hfov, vfov)
if dr.isSideways:
dr.camNode.setFov(vfov, hfov)
else:
dr.camNode.setFov(hfov, vfov)
def setHfov(self, fov):
for dr in self.displayRegionList:
dr.camNode.setHfov(fov)
if dr.isSideways:
dr.camNode.setVfov(fov)
else:
dr.camNode.setHfov(fov)
def setVfov(self, fov):
for dr in self.displayRegionList:
@ -742,18 +768,24 @@ class DisplayRegionList(PandaObject):
def contextTask(self, state):
# Window Data
self.mouseUpdate()
# hack to test movement
return Task.cont
class DisplayRegionContext:
def __init__(self, win, cam):
def __init__(self, win, cam, group):
self.win = win
self.cam = cam
self.camNode = self.cam.getNode(0)
self.group = group
self.iRay = SelectionRay(self.cam)
self.nearVec = Vec3(0)
self.mouseX = 0.0
self.mouseY = 0.0
# A Camera node can have more than one display region
# associated with it. Here I assume that there is only
# one display region per camera, since we are defining a
# display region on a per-camera basis. See note in
# DisplayRegionList.__init__()
numDrs = self.camNode.getNumDrs()
self.dr = self.camNode.getDr(0)
left = self.dr.getLeft()
@ -764,14 +796,33 @@ class DisplayRegionContext:
self.originY = top+bottom-1
self.scaleX = 1.0/(right-left)
self.scaleY = 1.0/(top-bottom)
self.setOrientation()
self.camUpdate()
def __getitem__(self,key):
return self.__dict__[key]
def setGroup(self, groupIndex):
self.groupIndex = groupIndex
def setOrientation(self):
hpr = self.cam.getHpr(base.camList[self.group])
if hpr[2] < 135 and hpr[2]>45 or hpr[2]>225 and hpr[2]<315:
self.isSideways = 1
elif hpr[2] > -135 and hpr[2] < -45 and hpr[2] < -225 and hpr[2] > -315:
self.isSideways = 1
else:
self.isSideways = 0
# The following take into consideration sideways displays
def getHfov(self):
if self.isSideways:
return self.camNode.getVfov()
else:
return self.camNode.getHfov()
def getVfov(self):
if self.isSideways:
return self.camNode.getHfov()
else:
return self.camNode.getVfov()
def camUpdate(self):
# Window Data

View File

@ -67,9 +67,6 @@ class ShowBase:
self.render.setColorOff()
self.hidden = NodePath(NamedNode('hidden'))
# This will be the list of cameras, one per display region
# For now, we only have one display region, so just create the
# default camera
self.dataRoot = NodePath(NamedNode('dataRoot'), DataRelation.getClassType())
# Cache the node so we do not ask for it every frame
@ -83,10 +80,15 @@ class ShowBase:
self.oldexitfunc = getattr(sys, 'exitfunc', None)
sys.exitfunc = self.exitfunc
# cameraList is a list of camera group nodes. There may
# be more than one display region/camera node beneath each
# one.
self.cameraList = []
for i in range(chanConfig.getNumGroups()):
self.cameraList.append(self.render.attachNewNode(
chanConfig.getGroupNode(i)))
# this is how we know which display region cameras belong to which
# camera group. display region i belongs to group self.groupList[i]
self.groupList = []
for i in range(chanConfig.getNumDrs()):
self.groupList.append(chanConfig.getGroupMembership(i))