Browse Source

*** empty log message ***

Dustin Barnard 18 years ago
parent
commit
30f14e0b2c

+ 186 - 1
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" %

+ 45 - 10
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):

+ 77 - 1
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 """