mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 02:42:49 -04:00
Adding support for multi-PC or multi-piping.
This commit is contained in:
parent
7c7300ff74
commit
8b5ea4acc5
151
direct/src/cluster/ClusterClient.py
Normal file
151
direct/src/cluster/ClusterClient.py
Normal 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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
52
direct/src/cluster/ClusterConfig.py
Normal file
52
direct/src/cluster/ClusterConfig.py
Normal 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)
|
||||||
|
|
||||||
|
|
||||||
|
|
114
direct/src/cluster/ClusterMsgs.py
Normal file
114
direct/src/cluster/ClusterMsgs.py
Normal 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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
241
direct/src/cluster/ClusterServer.py
Normal file
241
direct/src/cluster/ClusterServer.py
Normal 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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
|||||||
lib
|
lib
|
||||||
lib/py
|
lib/py
|
||||||
src/actor
|
src/actor
|
||||||
|
src/cluster
|
||||||
src/directtools
|
src/directtools
|
||||||
src/directdevices
|
src/directdevices
|
||||||
src/directnotify
|
src/directnotify
|
||||||
|
@ -1,13 +1,25 @@
|
|||||||
from PandaObject import *
|
from PandaObject import *
|
||||||
|
|
||||||
def setCameraOffsets(camList):
|
def setCameraOffsets(camList,camGroup):
|
||||||
# setup camera offsets based on chanconfig
|
# setup camera offsets based on chanconfig
|
||||||
chanConfig = base.config.GetString('chan-config', 'single')
|
chanConfig = base.config.GetString('chan-config', 'single')
|
||||||
if chanConfig == 'cave3':
|
if chanConfig == 'cave3':
|
||||||
camList[0].setHpr(camList[0], -90, 0, 0)
|
camList[0].setH(camGroup[0],-60)
|
||||||
camList[2].setHpr(camList[2], 90, 0, 0)
|
camList[2].setH(camGroup[0], 60)
|
||||||
elif chanConfig == 'cave3+god':
|
elif chanConfig == 'cave3+god':
|
||||||
camList[0].setHpr(camList[0], -90, 0, 0)
|
camList[0].setH(camGroup[0], -60)
|
||||||
camList[2].setHpr(camList[2], 90, 0, 0)
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -294,7 +294,9 @@ class DirectCameraControl(PandaObject):
|
|||||||
nodePath = render.findPathDownTo(node)
|
nodePath = render.findPathDownTo(node)
|
||||||
if camera not in nodePath.getAncestry():
|
if camera not in nodePath.getAncestry():
|
||||||
# Compute hit point
|
# 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
|
# Move coa marker to new point
|
||||||
self.updateCoa(hitPt, ref = self.coaMarkerRef)
|
self.updateCoa(hitPt, ref = self.coaMarkerRef)
|
||||||
else:
|
else:
|
||||||
@ -304,6 +306,7 @@ class DirectCameraControl(PandaObject):
|
|||||||
|
|
||||||
def computeCOA(self, node, hitPt, hitPtDist):
|
def computeCOA(self, node, hitPt, hitPtDist):
|
||||||
coa = Point3(0)
|
coa = Point3(0)
|
||||||
|
dr = direct.drList.getCurrentDr()
|
||||||
if self.fLockCOA:
|
if self.fLockCOA:
|
||||||
# COA is locked, use existing point
|
# COA is locked, use existing point
|
||||||
# Use existing point
|
# Use existing point
|
||||||
@ -315,8 +318,8 @@ class DirectCameraControl(PandaObject):
|
|||||||
# Set center of action
|
# Set center of action
|
||||||
coa.assign(hitPt)
|
coa.assign(hitPt)
|
||||||
# Handle case of bad coa point (too close or too far)
|
# Handle case of bad coa point (too close or too far)
|
||||||
if ((hitPtDist < (1.1 * direct.dr.near)) or
|
if ((hitPtDist < (1.1 * dr.near)) or
|
||||||
(hitPtDist > direct.dr.far)):
|
(hitPtDist > dr.far)):
|
||||||
# Just use existing point
|
# Just use existing point
|
||||||
coa.assign(self.coaMarker.getPos(direct.camera))
|
coa.assign(self.coaMarker.getPos(direct.camera))
|
||||||
# Reset hit point count
|
# Reset hit point count
|
||||||
@ -334,7 +337,7 @@ class DirectCameraControl(PandaObject):
|
|||||||
coa.set(0,dist,0)
|
coa.set(0,dist,0)
|
||||||
# Compute COA Dist
|
# Compute COA Dist
|
||||||
coaDist = Vec3(coa - ZERO_POINT).length()
|
coaDist = Vec3(coa - ZERO_POINT).length()
|
||||||
if coaDist < (1.1 * direct.dr.near):
|
if coaDist < (1.1 * dr.near):
|
||||||
coa.set(0,100,0)
|
coa.set(0,100,0)
|
||||||
coaDist = 100
|
coaDist = 100
|
||||||
# Update coa and marker
|
# Update coa and marker
|
||||||
@ -346,7 +349,9 @@ class DirectCameraControl(PandaObject):
|
|||||||
coaDist = Vec3(self.coa - ZERO_POINT).length()
|
coaDist = Vec3(self.coa - ZERO_POINT).length()
|
||||||
# Place the marker in render space
|
# Place the marker in render space
|
||||||
if ref == None:
|
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)
|
self.coaMarker.setPos(ref, self.coa)
|
||||||
# Resize it
|
# Resize it
|
||||||
self.updateCoaMarkerSize(coaDist)
|
self.updateCoaMarkerSize(coaDist)
|
||||||
@ -360,7 +365,9 @@ class DirectCameraControl(PandaObject):
|
|||||||
def updateCoaMarkerSize(self, coaDist = None):
|
def updateCoaMarkerSize(self, coaDist = None):
|
||||||
if not coaDist:
|
if not coaDist:
|
||||||
coaDist = Vec3(self.coaMarker.getPos( direct.camera )).length()
|
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:
|
if sf == 0.0:
|
||||||
sf = 0.1
|
sf = 0.1
|
||||||
self.coaMarker.setScale(sf)
|
self.coaMarker.setScale(sf)
|
||||||
|
@ -8,6 +8,8 @@ from DirectGeometry import *
|
|||||||
from DirectLights import *
|
from DirectLights import *
|
||||||
from DirectSessionPanel import *
|
from DirectSessionPanel import *
|
||||||
from DirectCamConfig import *
|
from DirectCamConfig import *
|
||||||
|
from ClusterServer import *
|
||||||
|
from ClusterConfig import *
|
||||||
from tkSimpleDialog import askstring
|
from tkSimpleDialog import askstring
|
||||||
import Placer
|
import Placer
|
||||||
import EntryScale
|
import EntryScale
|
||||||
@ -31,6 +33,9 @@ class DirectSession(PandaObject):
|
|||||||
self.font = Loader.Loader.loadFont(loader,
|
self.font = Loader.Loader.loadFont(loader,
|
||||||
"models/fonts/Comic",
|
"models/fonts/Comic",
|
||||||
priority = 100)
|
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.fEnabled = 0
|
||||||
self.drList = DisplayRegionList()
|
self.drList = DisplayRegionList()
|
||||||
self.iRayList = map(lambda x: x.iRay, self.drList)
|
self.iRayList = map(lambda x: x.iRay, self.drList)
|
||||||
@ -150,6 +155,15 @@ class DirectSession(PandaObject):
|
|||||||
from TkGlobal import *
|
from TkGlobal import *
|
||||||
self.panel = DirectSessionPanel(parent = tkroot)
|
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):
|
def enable(self):
|
||||||
# Make sure old tasks are shut down
|
# Make sure old tasks are shut down
|
||||||
self.disable()
|
self.disable()
|
||||||
@ -642,20 +656,26 @@ class DisplayRegionList(PandaObject):
|
|||||||
i = 0
|
i = 0
|
||||||
|
|
||||||
for cameraGroup in base.cameraList:
|
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')
|
camList=cameraGroup.findAllMatches('**/+Camera')
|
||||||
for cameraIndex in range(camList.getNumPaths()):
|
for cameraIndex in range(camList.getNumPaths()):
|
||||||
camera = camList[cameraIndex]
|
camera = camList[cameraIndex]
|
||||||
if camera.getName()=='<noname>':
|
if camera.getName()=='<noname>':
|
||||||
camera.setName('Camera%d' % cameraIndex)
|
camera.setName('Camera%d' % cameraIndex)
|
||||||
|
group = base.groupList[cameraIndex]
|
||||||
self.displayRegionList.append(
|
self.displayRegionList.append(
|
||||||
DisplayRegionContext(base.win,
|
DisplayRegionContext(base.win,
|
||||||
camera))
|
camera,group))
|
||||||
if camera.getName()!='<noname>' or len(camera.getName())==0:
|
if camera.getName()!='<noname>' or len(camera.getName())==0:
|
||||||
self.displayRegionLookup[camera.getName()]=i
|
self.displayRegionLookup[camera.getName()]=i
|
||||||
i = i + 1
|
i = i + 1
|
||||||
for cameraIndex in range(len(base.groupList)):
|
|
||||||
self.displayRegionList[cameraIndex].setGroup(
|
|
||||||
base.groupList[cameraIndex])
|
|
||||||
self.accept("CamChange",self.camUpdate)
|
self.accept("CamChange",self.camUpdate)
|
||||||
self.accept("DIRECT_mouse1",self.mouseUpdate)
|
self.accept("DIRECT_mouse1",self.mouseUpdate)
|
||||||
self.accept("DIRECT_mouse2",self.mouseUpdate)
|
self.accept("DIRECT_mouse2",self.mouseUpdate)
|
||||||
@ -668,7 +688,7 @@ class DisplayRegionList(PandaObject):
|
|||||||
cameraList = []
|
cameraList = []
|
||||||
for dr in self.displayRegionList:
|
for dr in self.displayRegionList:
|
||||||
cameraList.append(dr.cam)
|
cameraList.append(dr.cam)
|
||||||
setCameraOffsets(cameraList)
|
setCameraOffsets(cameraList,base.cameraList)
|
||||||
|
|
||||||
def __getitem__(self, index):
|
def __getitem__(self, index):
|
||||||
return self.displayRegionList[index]
|
return self.displayRegionList[index]
|
||||||
@ -693,11 +713,17 @@ class DisplayRegionList(PandaObject):
|
|||||||
|
|
||||||
def setFov(self, hfov, vfov):
|
def setFov(self, hfov, vfov):
|
||||||
for dr in self.displayRegionList:
|
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):
|
def setHfov(self, fov):
|
||||||
for dr in self.displayRegionList:
|
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):
|
def setVfov(self, fov):
|
||||||
for dr in self.displayRegionList:
|
for dr in self.displayRegionList:
|
||||||
@ -742,18 +768,24 @@ class DisplayRegionList(PandaObject):
|
|||||||
def contextTask(self, state):
|
def contextTask(self, state):
|
||||||
# Window Data
|
# Window Data
|
||||||
self.mouseUpdate()
|
self.mouseUpdate()
|
||||||
|
# hack to test movement
|
||||||
return Task.cont
|
return Task.cont
|
||||||
|
|
||||||
|
|
||||||
class DisplayRegionContext:
|
class DisplayRegionContext:
|
||||||
def __init__(self, win, cam):
|
def __init__(self, win, cam, group):
|
||||||
self.win = win
|
self.win = win
|
||||||
self.cam = cam
|
self.cam = cam
|
||||||
self.camNode = self.cam.getNode(0)
|
self.camNode = self.cam.getNode(0)
|
||||||
|
self.group = group
|
||||||
self.iRay = SelectionRay(self.cam)
|
self.iRay = SelectionRay(self.cam)
|
||||||
self.nearVec = Vec3(0)
|
self.nearVec = Vec3(0)
|
||||||
self.mouseX = 0.0
|
self.mouseX = 0.0
|
||||||
self.mouseY = 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()
|
numDrs = self.camNode.getNumDrs()
|
||||||
self.dr = self.camNode.getDr(0)
|
self.dr = self.camNode.getDr(0)
|
||||||
left = self.dr.getLeft()
|
left = self.dr.getLeft()
|
||||||
@ -764,14 +796,33 @@ class DisplayRegionContext:
|
|||||||
self.originY = top+bottom-1
|
self.originY = top+bottom-1
|
||||||
self.scaleX = 1.0/(right-left)
|
self.scaleX = 1.0/(right-left)
|
||||||
self.scaleY = 1.0/(top-bottom)
|
self.scaleY = 1.0/(top-bottom)
|
||||||
|
self.setOrientation()
|
||||||
self.camUpdate()
|
self.camUpdate()
|
||||||
|
|
||||||
def __getitem__(self,key):
|
def __getitem__(self,key):
|
||||||
return self.__dict__[key]
|
return self.__dict__[key]
|
||||||
|
|
||||||
def setGroup(self, groupIndex):
|
def setOrientation(self):
|
||||||
self.groupIndex = groupIndex
|
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):
|
def camUpdate(self):
|
||||||
# Window Data
|
# Window Data
|
||||||
|
@ -67,9 +67,6 @@ class ShowBase:
|
|||||||
self.render.setColorOff()
|
self.render.setColorOff()
|
||||||
|
|
||||||
self.hidden = NodePath(NamedNode('hidden'))
|
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())
|
self.dataRoot = NodePath(NamedNode('dataRoot'), DataRelation.getClassType())
|
||||||
# Cache the node so we do not ask for it every frame
|
# Cache the node so we do not ask for it every frame
|
||||||
@ -83,10 +80,15 @@ class ShowBase:
|
|||||||
self.oldexitfunc = getattr(sys, 'exitfunc', None)
|
self.oldexitfunc = getattr(sys, 'exitfunc', None)
|
||||||
sys.exitfunc = self.exitfunc
|
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 = []
|
self.cameraList = []
|
||||||
for i in range(chanConfig.getNumGroups()):
|
for i in range(chanConfig.getNumGroups()):
|
||||||
self.cameraList.append(self.render.attachNewNode(
|
self.cameraList.append(self.render.attachNewNode(
|
||||||
chanConfig.getGroupNode(i)))
|
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 = []
|
self.groupList = []
|
||||||
for i in range(chanConfig.getNumDrs()):
|
for i in range(chanConfig.getNumDrs()):
|
||||||
self.groupList.append(chanConfig.getGroupMembership(i))
|
self.groupList.append(chanConfig.getGroupMembership(i))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user