From 30f14e0b2c5a0561e73ada0a70bc1d7ebd24b459 Mon Sep 17 00:00:00 2001 From: Dustin Barnard Date: Wed, 12 Sep 2007 23:51:55 +0000 Subject: [PATCH] *** empty log message *** --- direct/src/cluster/ClusterClient.py | 187 +++++++++++++++++++++++++++- direct/src/cluster/ClusterMsgs.py | 55 ++++++-- direct/src/cluster/ClusterServer.py | 78 +++++++++++- 3 files changed, 308 insertions(+), 12 deletions(-) diff --git a/direct/src/cluster/ClusterClient.py b/direct/src/cluster/ClusterClient.py index 4bf096c98b..7ebc7ab7e2 100644 --- a/direct/src/cluster/ClusterClient.py +++ b/direct/src/cluster/ClusterClient.py @@ -51,6 +51,16 @@ class ClusterClient(DirectObject.DirectObject): self.qcm=QueuedConnectionManager() self.serverList = [] self.msgHandler = ClusterMsgHandler(ClusterClient.MGR_NUM, self.notify) + + # A dictionary of objects that can be accessed by name + self.objectMappings = {} + + # a dictionary of name objects and the corresponding names of + # objects they are to control on the server side + self.controlMappings = {} + self.controlOffsets = {} + self.taggedObjects = {} + for serverConfig in configList: server = DisplayConnection( self.qcm, serverConfig.serverName, @@ -78,6 +88,27 @@ class ClusterClient(DirectObject.DirectObject): self.notify.debug('post startMoveCam') self.startMoveSelectedTask() + + def startReaderPollTask(self): + """ Task to handle datagrams from server """ + # Run this task just after the listener poll task + taskMgr.add(self._readerPollTask, "clientReaderPollTask", -39) + + def _readerPollTask(self, state): + """ Non blocking task to read all available datagrams """ + + for server in self.serverList: + datagrams = server.poll() + for data in datagrams: + self.handleDatagram(data[0],data[1]) + + return Task.cont + + + def startControlObjectTask(self): + self.notify.debug("moving control objects") + taskMgr.add(self.controlObjectTask,"controlObjectTask") + def startSynchronizeTimeTask(self): self.notify.debug('broadcasting frame time') taskMgr.add(self.synchronizeTimeTask, "synchronizeTimeTask", -40) @@ -94,6 +125,27 @@ class ClusterClient(DirectObject.DirectObject): self.notify.debug('adding move cam') taskMgr.add(self.moveCameraTask, "moveCamTask", 49) + + def controlObjectTask(self, task): + for object in self.controlMappings: + name = self.controlMappings[object][0] + serverList = self.controlMappings[object][1] + if (self.objectMapping.has_key(object)): + self.moveObject(self.objectMappings[object],name,serverList, + self.controlOffsets[object]) + + return Task.cont + + def moveObject(self, nodePath, object, serverList, offset): + self.notify.debug('moving object '+object) + xyz = nodePath.getPos(render) + offset + hpr = nodePath.getHpr(render) + scale = nodePath.getScale(render) + hidden = nodePath.isHidden() + for server in serverList: + self.serverList[server].sendMoveNamedObject(xyz,hpr,scale,hidden,object) + + def moveCameraTask(self, task): self.moveCamera( base.camera.getPos(render), @@ -120,6 +172,39 @@ class ClusterClient(DirectObject.DirectObject): server.sendMoveSelected(xyz, hpr, scale) return Task.cont + + def addNamedObjectMapping(self,object,name): + if (not self.objectMappings.has_key(name)): + self.objectMappings[name] = object + else: + self.notify.debug('attempt to add duplicate named object: '+name) + + def removeObjectMapping(self,name): + if (self.objectMappings.has_key(name)): + self.objectMappings.pop(name) + + + def addControlMapping(self,objectName,controlledName, serverList = None, + offset = None): + if (not self.controlMappings.has_key(objectName)): + if (serverList == None): + serverList = range(len(self.serverList)) + if (offset == None): + offset = Vec3(0,0,0) + + self.controlMappings[objectName] = [controlledName,serverList] + self.controlOffsets[objectName] = offset + else: + self.notify.debug('attempt to add duplicate controlled object: '+name) + + def setControlMappingOffset(self,objectName,offset): + if (self.controlMappings.has_key(objectName)): + self.controlOffsets[objectName] = offset + + def removeControlMapping(self,name): + if (self.controlMappings.has_key(name)): + self.controlMappings.pop(name) + def getNodePathFindCmd(self, nodePath): import string pathString = `nodePath` @@ -131,10 +216,53 @@ class ClusterClient(DirectObject.DirectObject): else: return rootName + def getNodePathName(self, nodePath): + import string + pathString = `nodePath` + index = string.find(pathString, '/') + if index != -1: + name = pathString[index+1:] + return name + else: + return pathString + + + def addObjectTag(self,object,selectFunction,deselectFunction,selectArgs,deselectArgs): + newTag = {} + newTag["selectFunction"] = selectFunction + newTag["selectArgs"] = selectArgs + newTag["deselectFunction"] = deselectFunction + newTag["deselectArgs"] = deselectArgs + self.taggedObjects[object] = newTag + + + def removeObjectTag(self,object): + + self.taggedObjects.pop(object) + + def selectNodePath(self, nodePath): - self(self.getNodePathFindCmd(nodePath) + '.select()', 0) + name = self.getNodePathName(nodePath) + if self.taggedObjects.has_key(name): + taskMgr.remove("moveSelectedTask") + tag = self.taggedObjects[name] + function = tag["selectFunction"] + args = tag["selectArgs"] + if (function != None): + function(*args) + else: + self(self.getNodePathFindCmd(nodePath) + '.select()', 0) + def deselectNodePath(self, nodePath): + name = self.getNodePathName(nodePath) + if self.taggedObjects.has_key(name): + tag = self.taggedObjects[name] + function = tag["deselectFunction"] + args = tag["deselectArgs"] + if (function != None): + function(*args) + self.startMoveSelectedTask() self(self.getNodePathFindCmd(nodePath) + '.deselect()', 0) def sendCamFrustum(self, focalLength, filmSize, filmOffset, indexList=[]): @@ -163,6 +291,31 @@ class ClusterClient(DirectObject.DirectObject): # Execute locally exec(commandString, __builtins__) + + def handleDatagram(self,dgi,type): + if (type == CLUSTER_NONE): + pass + elif (type == CLUSTER_NAMED_OBJECT_MOVEMENT): + self.handleNamedMovement(dgi) + else: + self.notify.warning("Received unsupported packet type:" % type) + return type + + def handleNamedMovement(self, dgi): + """ Update cameraJig position to reflect latest position """ + (name,x, y, z, h, p, r, sx, sy, sz, hidden) = self.msgHandler.parseNamedMovementDatagram( + dgi) + if (self.objectMappings.has_key(name)): + self.objectMappings[name].setPosHpr(render, x, y, z, h, p, r) + self.objectMappings[name].setScale(render,sx,sy,sz) + if (hidden): + self.objectMappings[name].hide() + else: + self.objectMappings[name].show() + else: + self.notify.debug("recieved unknown named object command: "+name) + + def exit(self): # Execute remotely for server in self.serverList: @@ -178,6 +331,7 @@ class ClusterClientSync(ClusterClient): #I probably don't need this self.waitForSwap = 0 self.ready = 0 + print "creating synced client" self.startSwapCoordinatorTask() def startSwapCoordinatorTask(self): @@ -200,6 +354,8 @@ class ClusterClientSync(ClusterClient): base.graphicsEngine.flipFrame() self.notify.debug( "------------------------------------------END swap") + + #print "syncing" return Task.cont def moveCamera(self, xyz, hpr): @@ -226,6 +382,25 @@ class DisplayConnection: self.qcr.addConnection(self.tcpConn) self.cw=ConnectionWriter(qcm, 0) + + + def poll(self): + """ Non blocking task to read all available datagrams """ + dataGrams = [] + while 1: + (datagram, dgi, type) = self.msgHandler.nonBlockingRead(self.qcr) + # Queue is empty, done for now + if type is CLUSTER_NONE: + break + else: + # Got a datagram, add it to the list + dataGrams.append([dgi, type, datagram]) + + return dataGrams + + + + def sendCamOffset(self, xyz, hpr): ClusterClient.notify.debug("send cam offset...") ClusterClient.notify.debug(("packet %d xyz, hpr=%f %f %f %f %f %f" % @@ -246,6 +421,16 @@ class DisplayConnection: focalLength, filmSize, filmOffset) self.cw.send(datagram, self.tcpConn) + + def sendMoveNamedObject(self, xyz, hpr, name): + ClusterClient.notify.debug("send named object 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.makeNamedObjectMovementDatagram(xyz, hpr, + name) + 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" % diff --git a/direct/src/cluster/ClusterMsgs.py b/direct/src/cluster/ClusterMsgs.py index 69d4d1fdfb..845a79ec3c 100644 --- a/direct/src/cluster/ClusterMsgs.py +++ b/direct/src/cluster/ClusterMsgs.py @@ -9,16 +9,17 @@ from direct.distributed.PyDatagramIterator import PyDatagramIterator import time #these are the types of messages that are currently supported. -CLUSTER_NONE = 0 -CLUSTER_CAM_OFFSET = 1 -CLUSTER_CAM_FRUSTUM = 2 -CLUSTER_CAM_MOVEMENT = 3 -CLUSTER_SWAP_READY = 4 -CLUSTER_SWAP_NOW = 5 -CLUSTER_COMMAND_STRING = 6 -CLUSTER_SELECTED_MOVEMENT = 7 -CLUSTER_TIME_DATA = 8 -CLUSTER_EXIT = 100 +CLUSTER_NONE = 0 +CLUSTER_CAM_OFFSET = 1 +CLUSTER_CAM_FRUSTUM = 2 +CLUSTER_CAM_MOVEMENT = 3 +CLUSTER_SWAP_READY = 4 +CLUSTER_SWAP_NOW = 5 +CLUSTER_COMMAND_STRING = 6 +CLUSTER_SELECTED_MOVEMENT = 7 +CLUSTER_TIME_DATA = 8 +CLUSTER_NAMED_OBJECT_MOVEMENT = 9 +CLUSTER_EXIT = 100 #Port number for cluster rendering # DAEMON PORT IS PORT USED FOR STARTUP MESSAGE EXCHANGE @@ -156,6 +157,24 @@ class ClusterMsgHandler: datagram.addFloat32(hpr[2]) return datagram + def makeNamedObjectMovementDatagram(self, xyz, hpr, scale, hidden, name): + datagram = PyDatagram() + datagram.addUint32(self.packetNumber) + self.packetNumber = self.packetNumber + 1 + datagram.addUint8(CLUSTER_NAMED_OBJECT_MOVEMENT) + datagram.addString(name) + datagram.addFloat32(xyz[0]) + datagram.addFloat32(xyz[1]) + datagram.addFloat32(xyz[2]) + datagram.addFloat32(hpr[0]) + datagram.addFloat32(hpr[1]) + datagram.addFloat32(hpr[2]) + datagram.addFloat32(scale[0]) + datagram.addFloat32(scale[1]) + datagram.addFloat32(scale[2]) + datagram.addBool(hidden) + return datagram + def parseCamMovementDatagram(self, dgi): x=dgi.getFloat32() y=dgi.getFloat32() @@ -167,6 +186,21 @@ class ClusterMsgHandler: (x, y, z, h, p, r))) return (x, y, z, h, p, r) + def parseNamedMovementDatagram(self, dgi): + name = dgi.getString() + x=dgi.getFloat32() + y=dgi.getFloat32() + z=dgi.getFloat32() + h=dgi.getFloat32() + p=dgi.getFloat32() + r=dgi.getFloat32() + sx = dgi.getFloat32() + sy = dgi.getFloat32() + sz = dgi.getFloat32() + hidden = dgi.getBool() + return (name,x, y, z, h, p, r, sx, sy, sz, hidden) + + def makeSelectedMovementDatagram(self, xyz, hpr, scale): datagram = PyDatagram() datagram.addUint32(self.packetNumber) @@ -181,6 +215,7 @@ class ClusterMsgHandler: datagram.addFloat32(scale[0]) datagram.addFloat32(scale[1]) datagram.addFloat32(scale[2]) + #datagram.addBool(hidden) return datagram def parseSelectedMovementDatagram(self, dgi): diff --git a/direct/src/cluster/ClusterServer.py b/direct/src/cluster/ClusterServer.py index 1128383d36..cc4f9d4b99 100644 --- a/direct/src/cluster/ClusterServer.py +++ b/direct/src/cluster/ClusterServer.py @@ -54,6 +54,12 @@ class ClusterServer(DirectObject.DirectObject): globalClock.setMode(ClockObject.MSlave) # Send verification of startup to client self.daemon = DirectD() + + self.objectMappings = {} + self.controlMappings = {} + self.controlOffsets = {} + + # These must be passed in as bootstrap arguments and stored in # the __builtins__ namespace try: @@ -66,6 +72,8 @@ class ClusterServer(DirectObject.DirectObject): clusterDaemonPort = CLUSTER_DAEMON_PORT self.daemon.serverReady(clusterDaemonClient, clusterDaemonPort) + + def startListenerPollTask(self): # Run this task near the start of frame, sometime after the dataLoop taskMgr.add(self.listenerPollTask, "serverListenerPollTask", -40) @@ -88,6 +96,58 @@ class ClusterServer(DirectObject.DirectObject): self.notify.warning("getNewConnection returned false") return Task.cont + + def addNamedObjectMapping(self,object,name): + if (not self.objectMappings.has_key(name)): + self.objectMappings[name] = object + else: + self.notify.debug('attempt to add duplicate named object: '+name) + + def removeObjectMapping(self,name): + if (self.objectMappings.has_key(name)): + self.objectMappings.pop(name) + + + def addControlMapping(self,objectName,controlledName, offset = None): + if (not self.controlMappings.has_key(objectName)): + self.controlMappings[objectName] = controlledName + if (offset == None): + offset = Vec3(0,0,0) + self.controlOffsets[objectName] = offset + else: + self.notify.debug('attempt to add duplicate controlled object: '+name) + + def setControlMappingOffset(self,objectName,offset): + if (self.controlMappings.has_key(objectName)): + self.controlOffsets[objectName] = offset + + + def removeControlMapping(self,name): + if (self.controlMappings.has_key(name)): + self.controlMappings.pop(name) + + + def startControlObjectTask(self): + self.notify.debug("moving control objects") + taskMgr.add(self.controlObjectTask,"controlObjectTask") + + def controlObjectTask(self, task): + for object in self.controlMappings: + name = self.controlMappings[object] + if (self.objectMappings.has_key(object)): + self.moveObject(self.objectMappings[object],name,self.controlOffsets[object]) + + return Task.cont + + def moveObject(self, nodePath, object, offset): + self.notify.debug('moving object '+object) + xyz = nodePath.getPos(render) + offset + hpr = nodePath.getHpr(render) + scale = nodePath.getScale(render) + hidden = nodePath.isHidden() + datagram = self.msgHandler.makeNamedObjectMovementDatagram(xyz,hpr,scale,hidden,object) + self.cw.send(datagram, self.lastConnection) + def startReaderPollTask(self): """ Task to handle datagrams from client """ # Run this task just after the listener poll task @@ -171,6 +231,8 @@ class ClusterServer(DirectObject.DirectObject): elif (type == CLUSTER_TIME_DATA): self.notify.debug('time data') self.handleTimeData(dgi) + elif (type == CLUSTER_NAMED_OBJECT_MOVEMENT): + self.handleNamedMovement(dgi) else: self.notify.warning("Received unknown packet type:" % type) return type @@ -189,11 +251,25 @@ class ClusterServer(DirectObject.DirectObject): self.lens.setFilmSize(fs[0], fs[1]) self.lens.setFilmOffset(fo[0], fo[1]) + def handleNamedMovement(self, dgi): + """ Update cameraJig position to reflect latest position """ + (name,x, y, z, h, p, r,sx,sy,sz, hidden) = self.msgHandler.parseNamedMovementDatagram( + dgi) + if (self.objectMappings.has_key(name)): + self.objectMappings[name].setPosHpr(render, x, y, z, h, p, r) + self.objectMappings[name].setScale(render,sx,sy,sz) + if (hidden): + self.objectMappings[name].hide() + else: + self.objectMappings[name].show() + else: + self.notify.debug("recieved unknown named object command: "+name) + def handleCamMovement(self, dgi): """ 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 + self.fPosReceived = 1 def handleSelectedMovement(self, dgi): """ Update cameraJig position to reflect latest position """