mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-03 10:22:45 -04:00
Synced version of cluster code with select/deselect and limited command support
This commit is contained in:
parent
54563e7a00
commit
55a3ae9bff
@ -1,123 +1,28 @@
|
||||
"""ClusterClient: Master for mutlipiping or PC clusters. """
|
||||
"""ClusterClient: Master for mutli-piping or PC clusters. """
|
||||
|
||||
from PandaModules import *
|
||||
from ClusterMsgs import *
|
||||
from ClusterConfig import *
|
||||
import DirectNotifyGlobal
|
||||
import DirectObject
|
||||
import Task
|
||||
|
||||
class ClusterConfigItem:
|
||||
def __init__(self, serverFunction, serverName, port):
|
||||
self.serverName = serverName
|
||||
self.serverFunction = serverFunction
|
||||
self.port = port
|
||||
# Camera Offset
|
||||
self.xyz = Vec3(0)
|
||||
self.hpr = Vec3(0)
|
||||
# Camera Frustum Data
|
||||
self.fFrustum = 0
|
||||
self.focalLength = None
|
||||
self.filmSize = None
|
||||
self.filmOffset = None
|
||||
def setCamOffset(self, xyz, hpr):
|
||||
self.xyz = xyz
|
||||
self.hpr = hpr
|
||||
def setCamFrustum(self, focalLength, filmSize, filmOffset):
|
||||
self.fFrustum = 1
|
||||
self.focalLength = focalLength
|
||||
self.filmSize = filmSize
|
||||
self.filmOffset = filmOffset
|
||||
|
||||
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 sendCamFrustum(self,focalLength, filmSize, filmOffset):
|
||||
ClusterManager.notify.debug("send cam frustum...")
|
||||
ClusterManager.notify.debug(
|
||||
(("packet %d" % self.msgHandler.packetNumber) +
|
||||
(" fl, fs, fo=%0.3f, (%0.3f, %0.3f), (%0.3f, %0.3f)" %
|
||||
(focalLength, filmSize[0], filmSize[1],
|
||||
filmOffset[0], filmOffset[1])))
|
||||
)
|
||||
datagram = self.msgHandler.makeCamFrustumDatagram(
|
||||
focalLength, filmSize, filmOffset)
|
||||
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):
|
||||
while 1:
|
||||
(datagram, dgi,type) = self.msgHandler.blockingRead(self.qcr)
|
||||
if type == CLUSTER_SWAP_READY:
|
||||
break
|
||||
else:
|
||||
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(
|
||||
"display connect send swap now, packet %d" %
|
||||
self.msgHandler.packetNumber)
|
||||
datagram = self.msgHandler.makeSwapNowDatagram()
|
||||
self.cw.send(datagram, self.tcpConn)
|
||||
|
||||
def sendCommandString(self, commandString):
|
||||
ClusterManager.notify.debug("send command string: %s" % commandString)
|
||||
datagram = self.msgHandler.makeCommandStringDatagram(commandString)
|
||||
self.cw.send(datagram, self.tcpConn)
|
||||
|
||||
def sendExit(self):
|
||||
ClusterManager.notify.debug(
|
||||
"display connect send exit, packet %d" %
|
||||
self.msgHandler.packetNumber)
|
||||
datagram = self.msgHandler.makeExitDatagram()
|
||||
self.cw.send(datagram, self.tcpConn)
|
||||
|
||||
class ClusterManager(DirectObject.DirectObject):
|
||||
class ClusterClient(DirectObject.DirectObject):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory("ClusterClient")
|
||||
MGR_NUM = 1000000
|
||||
|
||||
def __init__(self, configList):
|
||||
self.qcm=QueuedConnectionManager()
|
||||
self.serverList = []
|
||||
self.msgHandler = MsgHandler(ClusterManager.MGR_NUM,self.notify)
|
||||
self.msgHandler = ClusterMsgHandler(ClusterClient.MGR_NUM, self.notify)
|
||||
for serverConfig in configList:
|
||||
server = DisplayConnection(self.qcm,serverConfig.serverName,
|
||||
serverConfig.port,self.msgHandler)
|
||||
if server == None:
|
||||
self.notify.error( ('Could not open %s on %s port %d' %
|
||||
(serverConfig.serverFunction,
|
||||
serverConfig.serverName,
|
||||
serverConfig.port)) )
|
||||
self.notify.error('Could not open %s on %s port %d' %
|
||||
(serverConfig.serverConfigName,
|
||||
serverConfig.serverName,
|
||||
serverConfig.port))
|
||||
else:
|
||||
server.sendCamOffset(serverConfig.xyz,serverConfig.hpr)
|
||||
if serverConfig.fFrustum:
|
||||
@ -128,6 +33,7 @@ class ClusterManager(DirectObject.DirectObject):
|
||||
self.startMoveCamTask()
|
||||
|
||||
def moveCamera(self, xyz, hpr):
|
||||
self.notify.debug('moving unsynced camera')
|
||||
for server in self.serverList:
|
||||
server.sendMoveCam(xyz,hpr)
|
||||
|
||||
@ -136,16 +42,34 @@ class ClusterManager(DirectObject.DirectObject):
|
||||
|
||||
def moveCameraTask(self,task):
|
||||
self.moveCamera(
|
||||
direct.camera.getPos(render),
|
||||
direct.camera.getHpr(render))
|
||||
base.camera.getPos(render),
|
||||
base.camera.getHpr(render))
|
||||
return Task.cont
|
||||
|
||||
def clusterCommand(self, commandString):
|
||||
def cmd(self, commandString, fLocally = 1):
|
||||
# Execute remotely
|
||||
for server in self.serverList:
|
||||
server.sendCommandString(commandString)
|
||||
# Execute locally
|
||||
exec( commandString, globals())
|
||||
if fLocally:
|
||||
# Execute locally
|
||||
exec( commandString, globals() )
|
||||
|
||||
def getNodePathFindCmd(self, nodePath):
|
||||
import string
|
||||
pathString = `nodePath`
|
||||
index = string.find(pathString, '/')
|
||||
if index != -1:
|
||||
rootName = pathString[:index]
|
||||
searchString = pathString[index+1:]
|
||||
return rootName + ('.find("%s")' % searchString)
|
||||
else:
|
||||
return rootName
|
||||
|
||||
def selectNodePath(self, nodePath):
|
||||
self.cmd(self.getNodePathFindCmd(nodePath) + '.select()', 0)
|
||||
|
||||
def deselectNodePath(self, nodePath):
|
||||
self.cmd(self.getNodePathFindCmd(nodePath) + '.deselect()', 0)
|
||||
|
||||
def exit(self):
|
||||
# Execute remotely
|
||||
@ -155,10 +79,10 @@ class ClusterManager(DirectObject.DirectObject):
|
||||
import sys
|
||||
sys.exit()
|
||||
|
||||
class ClusterManagerSync(ClusterManager):
|
||||
class ClusterClientSync(ClusterClient):
|
||||
|
||||
def __init__(self, configList):
|
||||
ClusterManager.__init__(self, configList)
|
||||
ClusterClient.__init__(self, configList)
|
||||
#I probably don't need this
|
||||
self.waitForSwap = 0
|
||||
self.ready = 0
|
||||
@ -190,11 +114,167 @@ class ClusterManagerSync(ClusterManager):
|
||||
def moveCamera(self,xyz,hpr):
|
||||
if self.ready:
|
||||
self.notify.debug('moving synced camera')
|
||||
ClusterManager.moveCamera(self,xyz,hpr)
|
||||
ClusterClient.moveCamera(self,xyz,hpr)
|
||||
self.waitForSwap=1
|
||||
|
||||
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):
|
||||
ClusterClient.notify.debug("send cam offset...")
|
||||
ClusterClient.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 sendCamFrustum(self,focalLength, filmSize, filmOffset):
|
||||
ClusterClient.notify.info("send cam frustum...")
|
||||
ClusterClient.notify.info(
|
||||
(("packet %d" % self.msgHandler.packetNumber) +
|
||||
(" fl, fs, fo=%0.3f, (%0.3f, %0.3f), (%0.3f, %0.3f)" %
|
||||
(focalLength, filmSize[0], filmSize[1],
|
||||
filmOffset[0], filmOffset[1])))
|
||||
)
|
||||
datagram = self.msgHandler.makeCamFrustumDatagram(
|
||||
focalLength, filmSize, filmOffset)
|
||||
self.cw.send(datagram, self.tcpConn)
|
||||
|
||||
def sendMoveCam(self,xyz,hpr):
|
||||
ClusterClient.notify.debug("send cam move...")
|
||||
ClusterClient.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.makeCamMovementDatagram(xyz, hpr)
|
||||
self.cw.send(datagram, self.tcpConn)
|
||||
|
||||
# the following should only be called by a synchronized cluster manger
|
||||
def getSwapReady(self):
|
||||
while 1:
|
||||
(datagram, dgi, type) = self.msgHandler.blockingRead(self.qcr)
|
||||
if type == CLUSTER_SWAP_READY:
|
||||
break
|
||||
else:
|
||||
self.notify.warning('was expecting SWAP_READY, got %d' % type)
|
||||
|
||||
# the following should only be called by a synchronized cluster manger
|
||||
def sendSwapNow(self):
|
||||
ClusterClient.notify.debug(
|
||||
"display connect send swap now, packet %d" %
|
||||
self.msgHandler.packetNumber)
|
||||
datagram = self.msgHandler.makeSwapNowDatagram()
|
||||
self.cw.send(datagram, self.tcpConn)
|
||||
|
||||
def sendCommandString(self, commandString):
|
||||
ClusterClient.notify.debug("send command string: %s" % commandString)
|
||||
datagram = self.msgHandler.makeCommandStringDatagram(commandString)
|
||||
self.cw.send(datagram, self.tcpConn)
|
||||
|
||||
def sendExit(self):
|
||||
ClusterClient.notify.debug(
|
||||
"display connect send exit, packet %d" %
|
||||
self.msgHandler.packetNumber)
|
||||
datagram = self.msgHandler.makeExitDatagram()
|
||||
self.cw.send(datagram, self.tcpConn)
|
||||
|
||||
class ClusterConfigItem:
|
||||
def __init__(self, serverConfigName, serverName, port):
|
||||
self.serverConfigName = serverConfigName
|
||||
self.serverName = serverName
|
||||
self.port = port
|
||||
# Camera Offset
|
||||
self.xyz = Vec3(0)
|
||||
self.hpr = Vec3(0)
|
||||
# Camera Frustum Data
|
||||
self.fFrustum = 0
|
||||
self.focalLength = None
|
||||
self.filmSize = None
|
||||
self.filmOffset = None
|
||||
def setCamOffset(self, xyz, hpr):
|
||||
self.xyz = xyz
|
||||
self.hpr = hpr
|
||||
def setCamFrustum(self, focalLength, filmSize, filmOffset):
|
||||
self.fFrustum = 1
|
||||
self.focalLength = focalLength
|
||||
self.filmSize = filmSize
|
||||
self.filmOffset = filmOffset
|
||||
|
||||
def createClusterClient():
|
||||
# setup camera offsets based on cluster-config
|
||||
clusterConfig = base.config.GetString('cluster-config', 'single-server')
|
||||
# No cluster config specified!
|
||||
if not ClientConfigs.has_key(clusterConfig):
|
||||
base.notify.warning(
|
||||
'createClusterClient: %s cluster-config is undefined.' %
|
||||
clusterConfig)
|
||||
return None
|
||||
# Get display config for each server in the cluster
|
||||
displayConfigs = []
|
||||
configList = ClientConfigs[clusterConfig]
|
||||
numConfigs = len(configList)
|
||||
for i in range(numConfigs):
|
||||
configData = configList[i]
|
||||
displayName = configData.get('display name', ('display%d' % i))
|
||||
displayMode = configData.get('display mode', 'server')
|
||||
# Init Cam Offset
|
||||
pos = configData.get('pos', Vec3(0))
|
||||
hpr = configData.get('hpr', Vec3(0))
|
||||
# Init Frustum if specified
|
||||
fl = configData.get('focal length', None)
|
||||
fs = configData.get('film size', None)
|
||||
fo = configData.get('film offset', None)
|
||||
if displayMode == 'client':
|
||||
base.camera.setPosHpr(pos,hpr)
|
||||
lens = base.cam.node().getLens()
|
||||
lens.setFocalLength(fl)
|
||||
lens.setFilmSize(fs[0], fs[1])
|
||||
lens.setFilmOffset(fo[0], fo[1])
|
||||
else:
|
||||
serverConfigName = 'cluster-server-%s' % displayName
|
||||
serverString = base.config.GetString(serverConfigName, '')
|
||||
if serverString == '':
|
||||
base.notify.warning(
|
||||
'%s undefined in Configrc: expected by %s display client.'%
|
||||
(serverConfigName,clusterConfig))
|
||||
base.notify.warning('%s will not be used.' % serverConfigName)
|
||||
else:
|
||||
serverInfo = string.split(serverString)
|
||||
serverName = serverInfo[0]
|
||||
if len(serverInfo) > 1:
|
||||
port = int(serverInfo[1])
|
||||
else:
|
||||
# Use default port
|
||||
port = CLUSTER_PORT
|
||||
cci = ClusterConfigItem(
|
||||
serverConfigName,
|
||||
serverName,
|
||||
port)
|
||||
# Init cam offset
|
||||
cci.setCamOffset(pos, hpr)
|
||||
# Init frustum if specified
|
||||
if fl and fs and fo:
|
||||
cci.setCamFrustum(fl, fs, fo)
|
||||
displayConfigs.append(cci)
|
||||
# Create Cluster Managers (opening connections to servers)
|
||||
# Are the servers going to be synced?
|
||||
if base.config.GetBool('cluster-sync', 0):
|
||||
base.win.setSync(1)
|
||||
return ClusterClientSync(displayConfigs)
|
||||
else:
|
||||
return ClusterClient(displayConfigs)
|
||||
|
||||
|
||||
|
@ -2,144 +2,101 @@ 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.
|
||||
# A dictionary of information for various cluster configurations.
|
||||
# Dictionary is keyed on cluster-config string
|
||||
# Each dictionary contains a list of display configurations, one for
|
||||
# each display in the cluster
|
||||
# Information that can be specified for each display:
|
||||
# display name: Name of display (used in Configrc to specify server)
|
||||
# display type: Used to flag client vs. server
|
||||
# pos: positional offset of display's camera from main cluster group
|
||||
# hpr: orientation offset of display's camera from main cluster group
|
||||
# focal length: display's focal length (in mm)
|
||||
# film size: display's film size (in inches)
|
||||
# film offset: offset of film back (in inches)
|
||||
# Note: Note, this overrides offsets specified in DirectCamConfig.py
|
||||
# For now we only specify frustum for first display region of configuration
|
||||
# TODO: Need to handle multiple display regions per cluster node and to
|
||||
# generalize to non cluster situations
|
||||
|
||||
# Specify offset from client for each server's camera group
|
||||
# Note: If server chan-config consists of multiple display regions
|
||||
# each display region can have an additional offset as specified in
|
||||
# DirectCamConfig.py
|
||||
ClientConfigs = {
|
||||
'mono-modelcave-pipe0': [{'pos' : Vec3(0),
|
||||
'single-server' : [{'display name' : 'display0',
|
||||
'pos' : Vec3(0),
|
||||
'hpr' : Vec3(0)}
|
||||
],
|
||||
'single-server' : [{'pos' : Vec3(0),
|
||||
'hpr' : Vec3(0)}
|
||||
],
|
||||
'two-server' : [{'pos' : Vec3(0),
|
||||
'two-server' : [{'display name' : 'display0',
|
||||
'pos' : Vec3(0),
|
||||
'hpr' : Vec3(-60,0,0)},
|
||||
{'pos' : Vec3(0),
|
||||
{'display name' : 'display1',
|
||||
'pos' : Vec3(0),
|
||||
'hpr' : Vec3(60,0,0)}
|
||||
],
|
||||
'cavetest-old' : [{'pos' : Vec3(-0.105, -0.020, 5.000),
|
||||
'hpr' : Vec3(51.213, 0.000, 0.000),
|
||||
'focal length' : 0.809,
|
||||
'film size' : (1.000, 0.831),
|
||||
'film offset' : (0.000, 0.173),
|
||||
},
|
||||
{'pos' : Vec3(-0.105, -0.020, 5.000),
|
||||
'hpr' : Vec3(-0.370, 0.000, 0.000),
|
||||
'focal length' : 0.815,
|
||||
'film size' : (1.000, 0.831),
|
||||
'film offset' : (0.000, 0.173),
|
||||
},
|
||||
{'pos' : Vec3(-0.105, -0.020, 5.000),
|
||||
'hpr' : Vec3(-51.675, 0.000, 0.000),
|
||||
'focal length' : 0.820,
|
||||
'film size' : (1.000, 0.830),
|
||||
'film offset' : (-0.000, 0.173),
|
||||
},
|
||||
{'pos' : Vec3(0.105, -0.020, 5.000),
|
||||
'hpr' : Vec3(51.675, 0.000, 0.000),
|
||||
'focal length' : 0.820,
|
||||
'film size' : (1.000, 0.830),
|
||||
'film offset' : (0.000, 0.173),
|
||||
},
|
||||
],
|
||||
'cavetest' : [{'pos' : Vec3(-0.105, -0.020, 5.000),
|
||||
'hpr' : Vec3(51.213, 0.000, 0.000),
|
||||
'focal length' : 0.809,
|
||||
'film size' : (1.000, 0.831),
|
||||
'film offset' : (0.000, 0.173),
|
||||
},
|
||||
{'pos' : Vec3(-0.105, -0.020, 5.000),
|
||||
'hpr' : Vec3(-0.370, 0.000, 0.000),
|
||||
'focal length' : 0.815,
|
||||
'film size' : (1.000, 0.831),
|
||||
'film offset' : (0.000, 0.173),
|
||||
},
|
||||
{'pos' : Vec3(-0.105, -0.020, 5.000),
|
||||
'hpr' : Vec3(-51.675, 0.000, 0.000),
|
||||
'focal length' : 0.820,
|
||||
'film size' : (1.000, 0.830),
|
||||
'film offset' : (-0.000, 0.173),
|
||||
},
|
||||
{'pos' : Vec3(0.105, -0.020, 5.000),
|
||||
'hpr' : Vec3(51.675, 0.000, 0.000),
|
||||
'focal length' : 0.820,
|
||||
'film size' : (1.000, 0.830),
|
||||
'film offset' : (0.000, 0.173),
|
||||
},
|
||||
{'pos' : Vec3(0.105, -0.020, 5.000),
|
||||
'hpr' : Vec3(0.370, 0.000, 0.000),
|
||||
'focal length' : 0.815,
|
||||
'film size' : (1.000, 0.831),
|
||||
'film offset' : (0.000, 0.173),
|
||||
},
|
||||
{'pos' : Vec3(0.105, -0.020, 5.000),
|
||||
'hpr' : Vec3(-51.213, 0.000, 0.000),
|
||||
'focal length' : 0.809,
|
||||
'film size' : (1.000, 0.831),
|
||||
'film offset' : (-0.000, 0.173),
|
||||
},
|
||||
'mono-modelcave-pipe0': [{'display name' : 'display0',
|
||||
'pos' : Vec3(0),
|
||||
'hpr' : Vec3(0)},
|
||||
{'display name' : 'display1',
|
||||
'pos' : Vec3(0),
|
||||
'hpr' : Vec3(0)}
|
||||
],
|
||||
'cavetestsmall' : [{'display name' : 'la',
|
||||
'pos' : Vec3(-0.105, -0.020, 5.000),
|
||||
'hpr' : Vec3(51.213, 0.000, 0.000),
|
||||
'focal length' : 0.809,
|
||||
'film size' : (1.000, 0.831),
|
||||
'film offset' : (0.000, 0.173),
|
||||
},
|
||||
{'display name' : 'lb',
|
||||
'display mode' : 'client',
|
||||
'pos' : Vec3(-0.105, -0.020, 5.000),
|
||||
'hpr' : Vec3(-0.370, 0.000, 0.000),
|
||||
'focal length' : 0.815,
|
||||
'film size' : (1.000, 0.831),
|
||||
'film offset' : (0.000, 0.173),
|
||||
},
|
||||
],
|
||||
'cavetest' : [{'display name' : 'la',
|
||||
'pos' : Vec3(-0.105, -0.020, 5.000),
|
||||
'hpr' : Vec3(51.213, 0.000, 0.000),
|
||||
'focal length' : 0.809,
|
||||
'film size' : (1.000, 0.831),
|
||||
'film offset' : (0.000, 0.173),
|
||||
},
|
||||
{'display name' : 'lb',
|
||||
'display mode' : 'client',
|
||||
'pos' : Vec3(-0.105, -0.020, 5.000),
|
||||
'hpr' : Vec3(-0.370, 0.000, 0.000),
|
||||
'focal length' : 0.815,
|
||||
'film size' : (1.000, 0.831),
|
||||
'film offset' : (0.000, 0.173),
|
||||
},
|
||||
{'display name' : 'lc',
|
||||
'pos' : Vec3(-0.105, -0.020, 5.000),
|
||||
'hpr' : Vec3(-51.675, 0.000, 0.000),
|
||||
'focal length' : 0.820,
|
||||
'film size' : (1.000, 0.830),
|
||||
'film offset' : (-0.000, 0.173),
|
||||
},
|
||||
{'display name' : 'ra',
|
||||
'pos' : Vec3(0.105, -0.020, 5.000),
|
||||
'hpr' : Vec3(51.675, 0.000, 0.000),
|
||||
'focal length' : 0.820,
|
||||
'film size' : (1.000, 0.830),
|
||||
'film offset' : (0.000, 0.173),
|
||||
},
|
||||
{'display name' : 'rb',
|
||||
'pos' : Vec3(0.105, -0.020, 5.000),
|
||||
'hpr' : Vec3(0.370, 0.000, 0.000),
|
||||
'focal length' : 0.815,
|
||||
'film size' : (1.000, 0.831),
|
||||
'film offset' : (0.000, 0.173),
|
||||
},
|
||||
{'display name' : 'rc',
|
||||
'pos' : Vec3(0.105, -0.020, 5.000),
|
||||
'hpr' : Vec3(-51.213, 0.000, 0.000),
|
||||
'focal length' : 0.809,
|
||||
'film size' : (1.000, 0.831),
|
||||
'film offset' : (-0.000, 0.173),
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
def createClusterManager():
|
||||
# setup camera offsets based on cluster-config
|
||||
clusterConfig = base.config.GetString('cluster-config', 'single-server')
|
||||
# No cluster config specified!
|
||||
if not ClientConfigs.has_key(clusterConfig):
|
||||
base.notify.warning(
|
||||
'display-client flag set, but %s cluster-config is undefined.' %
|
||||
clusterConfig)
|
||||
return None
|
||||
# Get display config for each server in the cluster
|
||||
displayConfigs = []
|
||||
configList = ClientConfigs[clusterConfig]
|
||||
numConfigs = len(configList)
|
||||
for i in range(1,numConfigs):
|
||||
configData = configList[i]
|
||||
serverConfigName = 'display%d' % i
|
||||
serverString = base.config.GetString(serverConfigName, '')
|
||||
if serverString == '':
|
||||
base.notify.warning(
|
||||
'%s undefined in Configrc: expected by %s display client.' %
|
||||
(serverConfigName,clusterConfig))
|
||||
base.notify.warning('%s will not be used.' % serverConfigName)
|
||||
else:
|
||||
serverInfo = string.split(serverString)
|
||||
serverName = serverInfo[0]
|
||||
if len(serverInfo) > 1:
|
||||
port = int(serverInfo[1])
|
||||
else:
|
||||
# Use default port
|
||||
port = CLUSTER_PORT
|
||||
cci = ClusterConfigItem(
|
||||
serverConfigName,
|
||||
serverName,
|
||||
port)
|
||||
# Init Cam Offset
|
||||
pos = configData.get('pos', Vec3(0))
|
||||
hpr = configData.get('hpr', Vec3(0))
|
||||
cci.setCamOffset(pos, hpr)
|
||||
# Init Frustum if specified
|
||||
fl = configData.get('focalLength', None)
|
||||
fs = configData.get('filmSize', None)
|
||||
fo = configData.get('filmOffset', None)
|
||||
if fl and fs and fo:
|
||||
cci.setCamFrustum(fl, fs, fo)
|
||||
displayConfigs.append(cci)
|
||||
# Create Cluster Managers (opening connections to servers)
|
||||
# Are the servers going to be synced?
|
||||
synced = base.config.GetBool('sync-display', 0)
|
||||
if synced:
|
||||
base.win.setSync(1)
|
||||
return ClusterManagerSync(displayConfigs)
|
||||
else:
|
||||
return ClusterManager(displayConfigs)
|
||||
|
||||
|
||||
|
||||
|
@ -1,11 +1,7 @@
|
||||
"""ClusterMsgs module: Message types for Cluster rendering"""
|
||||
|
||||
# This module is intended to supply routines and dataformats common to
|
||||
# both ClusterClient and ClusterServer. There is a bit of sloppiness
|
||||
# though. For example:
|
||||
# This is where datagrams are constructed for sending, but datagrams
|
||||
# recieved are handled outside of here, after the header (message type
|
||||
# and number) are read here.
|
||||
# both ClusterClient and ClusterServer.
|
||||
|
||||
from PandaModules import *
|
||||
import Datagram
|
||||
@ -24,11 +20,11 @@ CLUSTER_EXIT = 100
|
||||
#Port number for cluster rendering
|
||||
CLUSTER_PORT = 1970
|
||||
|
||||
class MsgHandler:
|
||||
"""MsgHandler: wrapper for PC clusters/multi-piping networking"""
|
||||
class ClusterMsgHandler:
|
||||
"""ClusterMsgHandler: wrapper for PC clusters/multi-piping networking"""
|
||||
def __init__(self,packetStart, notify):
|
||||
# packetStart can be used to distinguish which MsgHandler sends a
|
||||
# given packet.
|
||||
# packetStart can be used to distinguish which ClusterMsgHandler
|
||||
# sends a given packet.
|
||||
self.packetNumber = packetStart
|
||||
self.notify = notify
|
||||
|
||||
@ -76,7 +72,7 @@ class MsgHandler:
|
||||
dgi = DatagramIterator(datagram)
|
||||
number = dgi.getUint32()
|
||||
type = dgi.getUint8()
|
||||
self.notify.debug("Packet %d type %d recieved" % (number,type))
|
||||
self.notify.debug("Packet %d type %d received" % (number,type))
|
||||
return (dgi,type)
|
||||
|
||||
def makeCamOffsetDatagram(self,xyz,hpr):
|
||||
@ -92,6 +88,16 @@ class MsgHandler:
|
||||
datagram.addFloat32(hpr[2])
|
||||
return datagram
|
||||
|
||||
def parseCamOffsetDatagram(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))
|
||||
return (x,y,z,h,p,r)
|
||||
|
||||
def makeCamFrustumDatagram(self,focalLength, filmSize, filmOffset):
|
||||
datagram = Datagram.Datagram()
|
||||
datagram.addUint32(self.packetNumber)
|
||||
@ -104,7 +110,16 @@ class MsgHandler:
|
||||
datagram.addFloat32(filmOffset[1])
|
||||
return datagram
|
||||
|
||||
def makeMoveCamDatagram(self,xyz,hpr):
|
||||
def parseCamFrustumDatagram(self, dgi):
|
||||
focalLength = dgi.getFloat32()
|
||||
filmSize = (dgi.getFloat32(), dgi.getFloat32())
|
||||
filmOffset = (dgi.getFloat32(),dgi.getFloat32())
|
||||
self.notify.debug('fl, fs, fo=%f, (%f, %f), (%f, %f)' %
|
||||
(focalLength, filmSize[0], filmSize[1],
|
||||
filmOffset[0], filmOffset[1]))
|
||||
return (focalLength, filmSize, filmOffset)
|
||||
|
||||
def makeCamMovementDatagram(self,xyz,hpr):
|
||||
datagram = Datagram.Datagram()
|
||||
datagram.addUint32(self.packetNumber)
|
||||
self.packetNumber = self.packetNumber + 1
|
||||
@ -117,6 +132,17 @@ class MsgHandler:
|
||||
datagram.addFloat32(hpr[2])
|
||||
return datagram
|
||||
|
||||
def parseCamMovementDatagram(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)))
|
||||
return (x,y,z,h,p,r)
|
||||
|
||||
def makeCommandStringDatagram(self, commandString):
|
||||
datagram = Datagram.Datagram()
|
||||
datagram.addUint32(self.packetNumber)
|
||||
@ -125,6 +151,10 @@ class MsgHandler:
|
||||
datagram.addString(commandString)
|
||||
return datagram
|
||||
|
||||
def parseCommandStringDatagram(self, dgi):
|
||||
command = dgi.getString()
|
||||
return command
|
||||
|
||||
def makeSwapNowDatagram(self):
|
||||
datagram = Datagram.Datagram()
|
||||
datagram.addUint32(self.packetNumber)
|
||||
|
@ -7,10 +7,6 @@ import DirectNotifyGlobal
|
||||
import DirectObject
|
||||
import Task
|
||||
|
||||
# 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.
|
||||
|
||||
@ -18,138 +14,120 @@ class ClusterServer(DirectObject.DirectObject):
|
||||
notify = DirectNotifyGlobal.directNotify.newCategory("ClusterServer")
|
||||
MSG_NUM = 2000000
|
||||
|
||||
def __init__(self,cameraGroup,camera):
|
||||
def __init__(self,cameraJig,camera):
|
||||
# Store information about the cluster's camera
|
||||
self.cameraGroup = cameraGroup
|
||||
self.cameraJig = cameraJig
|
||||
self.camera = camera
|
||||
self.lens = camera.node().getLens()
|
||||
# Initialize camera offsets
|
||||
self.posOffset = Vec3(0,0,0)
|
||||
self.hprOffset = Vec3(0,0,0)
|
||||
# Create network layer objects
|
||||
self.lastConnection = None
|
||||
self.fPosReceived = 0
|
||||
# Create network layer objects
|
||||
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)
|
||||
port = base.config.GetInt('cluster-server-port', CLUSTER_PORT)
|
||||
self.tcpRendezvous = self.qcm.openTCPServerRendezvous(port, 1)
|
||||
self.qcl.addConnection(self.tcpRendezvous)
|
||||
self.msgHandler = MsgHandler(ClusterServer.MSG_NUM,self.notify)
|
||||
self.msgHandler = ClusterMsgHandler(ClusterServer.MSG_NUM, self.notify)
|
||||
# Start cluster tasks
|
||||
self.startListenerPollTask()
|
||||
self.startReaderPollTask()
|
||||
|
||||
def startListenerPollTask(self):
|
||||
# Run this task near the start of frame, sometime after the dataloop
|
||||
taskMgr.add(self.listenerPollTask, "serverListenerPollTask",-40)
|
||||
|
||||
def listenerPollTask(self, task):
|
||||
""" Task to listen for a new connection from the client """
|
||||
# Run this task after the dataloop
|
||||
if self.qcl.newConnectionAvailable():
|
||||
print "New connection is available"
|
||||
self.notify.info("New connection is available")
|
||||
rendezvous = PointerToConnection()
|
||||
netAddress = NetAddress()
|
||||
newConnection = PointerToConnection()
|
||||
retVal = self.qcl.getNewConnection(rendezvous, netAddress,
|
||||
newConnection)
|
||||
if retVal:
|
||||
if self.qcl.getNewConnection(rendezvous,netAddress,newConnection):
|
||||
# Crazy dereferencing
|
||||
newConnection=newConnection.p()
|
||||
self.qcr.addConnection(newConnection)
|
||||
print "Got a connection!"
|
||||
self.lastConnection = newConnection
|
||||
self.notify.info("Got a connection!")
|
||||
else:
|
||||
self.notify.warning(
|
||||
"getNewConnection returned false")
|
||||
self.notify.warning("getNewConnection returned false")
|
||||
return Task.cont
|
||||
|
||||
def startReaderPollTask(self):
|
||||
""" Task to handle datagrams from client """
|
||||
# Run this task just after the listener poll task and dataloop
|
||||
# Run this task just after the listener poll task
|
||||
taskMgr.add(self.readerPollTask, "serverReaderPollTask", -39)
|
||||
|
||||
def readerPollTask(self, state):
|
||||
# Process all available datagrams
|
||||
""" Non blocking task to read all available datagrams """
|
||||
while 1:
|
||||
(datagram, dgi,type) = self.msgHandler.nonBlockingRead(self.qcr)
|
||||
# Queue is empty, done for now
|
||||
if type is CLUSTER_NONE:
|
||||
break
|
||||
else:
|
||||
handleDatagram(dgi, type)
|
||||
# Got a datagram, handle it
|
||||
self.handleDatagram(dgi, type)
|
||||
return Task.cont
|
||||
|
||||
def handleDatagram(self, dgi, type):
|
||||
if type == CLUSTER_NONE:
|
||||
""" Process a datagram depending upon type flag """
|
||||
if (type == CLUSTER_NONE):
|
||||
pass
|
||||
elif type == CLUSTER_EXIT:
|
||||
elif (type == CLUSTER_EXIT):
|
||||
import sys
|
||||
sys.exit()
|
||||
elif type == CLUSTER_CAM_OFFSET:
|
||||
elif (type == CLUSTER_CAM_OFFSET):
|
||||
self.handleCamOffset(dgi)
|
||||
elif type == CLUSTER_CAM_FRUSTUM:
|
||||
elif (type == CLUSTER_CAM_FRUSTUM):
|
||||
self.handleCamFrustum(dgi)
|
||||
elif type == CLUSTER_CAM_MOVEMENT:
|
||||
elif (type == CLUSTER_CAM_MOVEMENT):
|
||||
self.handleCamMovement(dgi)
|
||||
elif type == CLUSTER_COMMAND_STRING:
|
||||
elif (type == CLUSTER_COMMAND_STRING):
|
||||
self.handleCommandString(dgi)
|
||||
elif (type == CLUSTER_SWAP_READY):
|
||||
pass
|
||||
elif (type == CLUSTER_SWAP_NOW):
|
||||
self.notify.debug('swapping')
|
||||
base.win.swap()
|
||||
else:
|
||||
self.notify.warning("Recieved unknown packet type:" % type)
|
||||
self.notify.warning("Received unknown packet type:" % type)
|
||||
return type
|
||||
|
||||
|
||||
# Server specific tasks
|
||||
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)
|
||||
""" Set offset of camera from cameraJig """
|
||||
(x,y,z,h,p,r) = self.msgHandler.parseCamOffsetDatagram(dgi)
|
||||
self.lens.setIodOffset(x)
|
||||
self.lens.setViewHpr(h,p,r)
|
||||
|
||||
def handleCamFrustum(self,dgi):
|
||||
focalLength=dgi.getFloat32()
|
||||
filmSize=(dgi.getFloat32(), dgi.getFloat32())
|
||||
filmOffset=(dgi.getFloat32(),dgi.getFloat32())
|
||||
self.notify.debug(' fl, fs, fo=%f, (%f, %f), (%f, %f)' %
|
||||
(focalLength, filmSize[0], filmSize[1],
|
||||
filmOffset[0], filmOffset[1]))
|
||||
self.lens.setFocalLength(focalLength)
|
||||
self.lens.setFilmSize(filmSize[0], filmSize[1])
|
||||
self.lens.setFilmOffset(filmOffset[0], filmOffset[1])
|
||||
""" Adjust camera frustum based on parameters sent by client """
|
||||
(fl,fs,fo) = self.msgHandler.parseCamFrustumDatagram(dgi)
|
||||
self.lens.setFocalLength(fl)
|
||||
self.lens.setFilmSize(fs[0], fs[1])
|
||||
self.lens.setFilmOffset(fo[0], fo[1])
|
||||
|
||||
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)
|
||||
""" Update cameraJig position to reflect latest position """
|
||||
(x,y,z,h,p,r) = self.msgHandler.parseCamMovementDatagram(dgi)
|
||||
self.cameraJig.setPosHpr(render,x,y,z,h,p,r)
|
||||
self.fPosReceived = 1
|
||||
|
||||
def handleCommandString(self, dgi):
|
||||
command = dgi.getString()
|
||||
exec( command, globals())
|
||||
""" Handle arbitrary command string from client """
|
||||
command = self.msgHandler.parseCommandStringDatagram(dgi)
|
||||
exec( command, globals() )
|
||||
|
||||
class ClusterServerSync(ClusterServer):
|
||||
|
||||
def __init__(self,cameraGroup,camera):
|
||||
def __init__(self,cameraJig,camera):
|
||||
self.notify.info('starting ClusterServerSync')
|
||||
self.posRecieved = 0
|
||||
ClusterServer.__init__(self,cameraGroup,camera)
|
||||
ClusterServer.__init__(self,cameraJig,camera)
|
||||
self.startSwapCoordinator()
|
||||
return None
|
||||
|
||||
def readerPollTask(self, task):
|
||||
if self.lastConnection is None:
|
||||
@ -158,42 +136,32 @@ class ClusterServerSync(ClusterServer):
|
||||
# Process datagrams till you get a postion update
|
||||
type = CLUSTER_NONE
|
||||
while type != CLUSTER_CAM_MOVEMENT:
|
||||
# Block until you get a new datagram
|
||||
(datagram,dgi,type) = self.msgHandler.blockingRead(self.qcr)
|
||||
if type == CLUSTER_CAM_MOVEMENT:
|
||||
# Move camera
|
||||
self.handleCamMovement(dgi)
|
||||
# Set flag for swap coordinator
|
||||
self.posRecieved = 1
|
||||
elif type == CLUSTER_CAM_OFFSET:
|
||||
# Update camera offset
|
||||
self.handleCamOffset(dgi)
|
||||
elif type == CLUSTER_COMMAND_STRING:
|
||||
# Got a command, execute it
|
||||
self.handleCommandString(dgi)
|
||||
# Process datagram
|
||||
self.handleDatagram(dgi,type)
|
||||
return Task.cont
|
||||
|
||||
def sendSwapReady(self):
|
||||
self.notify.debug( ('send swap ready packet %d' %
|
||||
self.msgHandler.packetNumber ) )
|
||||
self.notify.debug(
|
||||
'send swap ready packet %d' % self.msgHandler.packetNumber)
|
||||
datagram = self.msgHandler.makeSwapReadyDatagram()
|
||||
self.cw.send(datagram, self.lastConnection)
|
||||
|
||||
def startSwapCoordinator(self):
|
||||
taskMgr.add(self.swapCoordinatorTask, "serverSwapCoordinator", 51)
|
||||
return None
|
||||
|
||||
def swapCoordinatorTask(self, task):
|
||||
if self.posRecieved:
|
||||
self.posRecieved = 0
|
||||
if self.fPosReceived:
|
||||
self.fPosReceived = 0
|
||||
# Alert client that this server is ready to swap
|
||||
self.sendSwapReady()
|
||||
# Wait for swap command (processing any intermediate datagrams)
|
||||
while 1:
|
||||
(datagram,dgi,type) = self.msgHandler.blockingRead(self.qcr)
|
||||
self.handleDatagram(dgi,type)
|
||||
if type == CLUSTER_SWAP_NOW:
|
||||
self.notify.debug('swapping')
|
||||
base.win.swap()
|
||||
break
|
||||
else:
|
||||
self.handleDatagram(dgi,type)
|
||||
return Task.cont
|
||||
|
||||
|
||||
|
@ -9,7 +9,6 @@ class DirectLight(NodePath):
|
||||
NodePath.__init__(self)
|
||||
# Record light and name
|
||||
self.light = light
|
||||
self.name = light.getName()
|
||||
|
||||
# Upcast the light object to its node base pointer
|
||||
if isinstance(light, Spotlight):
|
||||
@ -25,7 +24,8 @@ class DirectLight(NodePath):
|
||||
pass
|
||||
|
||||
def getName(self):
|
||||
return self.name
|
||||
return self.light.getName()
|
||||
|
||||
def getLight(self):
|
||||
return self.light
|
||||
|
||||
|
@ -113,6 +113,9 @@ class DirectManipulationControl(PandaObject):
|
||||
self.fScaling = 0
|
||||
direct.selected.highlightAll()
|
||||
self.objectHandles.showAllHandles()
|
||||
if direct.clusterMode == 'client':
|
||||
direct.cluster.cmd(
|
||||
'direct.manipulationControl.objectHandles.showAllHandles()')
|
||||
self.objectHandles.hideGuides()
|
||||
# Restart followSelectedNodePath task
|
||||
self.spawnFollowSelectedNodePathTask()
|
||||
@ -177,6 +180,11 @@ class DirectManipulationControl(PandaObject):
|
||||
self.objectHandles.showGuides()
|
||||
self.objectHandles.hideAllHandles()
|
||||
self.objectHandles.showHandle(self.constraint)
|
||||
if direct.clusterMode == 'client':
|
||||
oh = 'direct.manipulationControl.objectHandles'
|
||||
direct.cluster.cmd(oh + '.showGuides()', 0)
|
||||
direct.cluster.cmd(oh + '.hideAllHandles()', 0)
|
||||
direct.cluster.cmd(oh + ('.showHandle("%s")'% self.constraint))
|
||||
# Record relationship between selected nodes and widget
|
||||
direct.selected.getWrtAll()
|
||||
# hide the bbox of the selected objects during interaction
|
||||
|
@ -13,8 +13,6 @@ class DirectNodePath(NodePath):
|
||||
# Initialize the superclass
|
||||
NodePath.__init__(self)
|
||||
self.assign(nodePath)
|
||||
# Get a reasonable name
|
||||
self.name = self.getName()
|
||||
# Create a bounding box
|
||||
self.bbox = DirectBoundingBox(self)
|
||||
center = self.bbox.getCenter()
|
||||
@ -45,10 +43,6 @@ class DirectNodePath(NodePath):
|
||||
def getMax(self):
|
||||
return self.bbox.getMax()
|
||||
|
||||
def __repr__(self):
|
||||
return ('NodePath:\t%s\n' % self.name)
|
||||
|
||||
|
||||
class SelectedNodePaths(PandaObject):
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
@ -91,6 +85,9 @@ class SelectedNodePaths(PandaObject):
|
||||
self.selectedDict[dnp.id()] = dnp
|
||||
# And update last
|
||||
__builtin__.last = self.last = dnp
|
||||
# Update cluster servers if this is a cluster client
|
||||
if direct.clusterMode == 'client':
|
||||
direct.cluster.selectNodePath(dnp)
|
||||
return dnp
|
||||
|
||||
def deselect(self, nodePath):
|
||||
@ -109,6 +106,9 @@ class SelectedNodePaths(PandaObject):
|
||||
self.deselectedDict[id] = dnp
|
||||
# Send a message
|
||||
messenger.send('DIRECT_deselectedNodePath', [dnp])
|
||||
# Update cluster servers if this is a cluster client
|
||||
if direct.clusterMode == 'client':
|
||||
direct.cluster.deselectNodePath(dnp)
|
||||
return dnp
|
||||
|
||||
def getSelectedAsList(self):
|
||||
@ -173,6 +173,17 @@ class SelectedNodePaths(PandaObject):
|
||||
|
||||
def moveWrtWidgetAll(self):
|
||||
self.forEachSelectedNodePathDo(self.moveWrtWidget)
|
||||
# Update cluster if current display is a cluster client
|
||||
if (direct.clusterMode == 'client') and (last is not None):
|
||||
pos = Point3(0)
|
||||
hpr = VBase3(0)
|
||||
scale = VBase3(1)
|
||||
decomposeMatrix(last.getMat(), scale, hpr, pos)
|
||||
direct.cluster.cmd(
|
||||
'last.setPosHprScale(%f,%f,%f,%f,%f,%f,%f,%f,%f)' %
|
||||
(pos[0], pos[1], pos[2],
|
||||
hpr[0], hpr[1], hpr[2],
|
||||
scale[0], scale[1], scale[2]))
|
||||
|
||||
def moveWrtWidget(self, nodePath):
|
||||
nodePath.setMat(direct.widget, nodePath.mDnp2Widget)
|
||||
|
@ -7,7 +7,6 @@ from DirectGrid import *
|
||||
from DirectGeometry import *
|
||||
from DirectLights import *
|
||||
from DirectSessionPanel import *
|
||||
from DirectCamConfig import *
|
||||
from ClusterClient import *
|
||||
from ClusterServer import *
|
||||
from tkSimpleDialog import askstring
|
||||
@ -241,7 +240,7 @@ class DirectSession(PandaObject):
|
||||
|
||||
self.oobeVis = loader.loadModelOnce('models/misc/camera')
|
||||
if self.oobeVis:
|
||||
self.oobeVis.arc().setFinal(1)
|
||||
self.oobeVis.node().setFinal(1)
|
||||
|
||||
if self.oobeMode:
|
||||
# Position a target point to lerp the oobe camera to
|
||||
@ -259,8 +258,7 @@ class DirectSession(PandaObject):
|
||||
# Remove any transformation on the models arc
|
||||
self.oobeVis.clearMat()
|
||||
# Make oobeCamera be a sibling of wherever camera is now.
|
||||
cameraParent = NodePath(self.camera)
|
||||
cameraParent.shorten(1)
|
||||
cameraParent = self.camera.getParent()
|
||||
# Prepare oobe camera
|
||||
self.oobeCamera.reparentTo(cameraParent)
|
||||
self.oobeCamera.iPosHpr(self.trueCamera)
|
||||
@ -396,6 +394,12 @@ class DirectSession(PandaObject):
|
||||
self.undo()
|
||||
elif (input == ']') or (input == '}'):
|
||||
self.redo()
|
||||
|
||||
#Pass along certain events if this display is a cluster client
|
||||
if self.clusterMode == 'client':
|
||||
if input in ('v','b','l','p', 'r', 'shift-r', 's', 't',
|
||||
'shift-a', 'w'):
|
||||
self.cluster.cmd('messenger.send("%s")' % input,0)
|
||||
|
||||
def select(self, nodePath, fMultiSelect = 0, fResetAncestry = 1):
|
||||
dnp = self.selected.select(nodePath, fMultiSelect)
|
||||
@ -409,7 +413,7 @@ class DirectSession(PandaObject):
|
||||
# Update the selectedNPReadout
|
||||
self.selectedNPReadout.reparentTo(aspect2d)
|
||||
self.selectedNPReadout.setText(
|
||||
'Selected:' + dnp.name)
|
||||
'Selected:' + dnp.getName())
|
||||
# Show the manipulation widget
|
||||
self.widget.showWidget()
|
||||
# Update camera controls coa to this point
|
||||
|
Loading…
x
Reference in New Issue
Block a user