Browse Source

*** empty log message ***

Mark Mine 25 years ago
parent
commit
f7f44a3e18

+ 1 - 0
direct/src/configfiles/direct.pth

@@ -2,6 +2,7 @@ lib
 lib/py
 src/actor
 src/directtools
+src/directdevices
 src/directnotify
 src/distributed
 src/ffi

+ 193 - 0
direct/src/directdevices/DirectDeviceManager.py

@@ -0,0 +1,193 @@
+""" Class used to create and control vrpn devices """
+from PandaObject import *
+
+class DirectDeviceManager(VrpnClient, PandaObject):
+    def __init__(self, server = None):
+        # Determine which server to use
+        if server != None:
+            # One give as constructor argument
+            self.server = server
+        else:
+            # Check config file, if that fails, use default
+            self.server = base.config.GetString('vrpn-server', 'spacedyne')
+        
+        # Create a vrpn client
+        VrpnClient.__init__(self, self.server)
+    
+    def createButtons(self, device):
+        return DirectButtons(self, device)
+    
+    def createAnalogs(self, device):
+        return DirectAnalogs(self, device)
+
+    def createDials(self, device):
+        return DirectDials(self, device)
+
+    def createTimecodeReader(self, device):
+        return DirectTimecodeReader(self, device)
+
+class DirectButtons(ButtonNode, PandaObject):
+    buttonCount = 0
+    def __init__(self, vrpnClient, device):
+        # Keep track of number of buttons created
+        DirectButtons.buttonCount += 1
+        # Create a unique name for this button object
+        self.name = 'DirectButtons-' + `DirectButtons.buttonCount`
+        # Create a new button node for the given device
+        ButtonNode.__init__(self, vrpnClient, device)
+        # Attach node to data graph
+        self.nodePath = base.dataRoot.attachNewNode(self)
+    
+    def __getitem__(self, index):
+        if (index < 0) | (index > self.getNumButtons()):
+            raise IndexError
+        return self.getButtonState(index)
+    
+    def __len__(self):
+        return self.getNumButtons()
+    
+    def enable(self):
+        self.nodePath.reparentTo(base.dataRoot)
+    
+    def disable(self):
+        self.nodePath.reparentTo(base.dataUnused)
+    
+    def getName(self):
+        return self.name
+    
+    def getNodePath(self):
+        return self.nodePath
+    
+    def __repr__(self):
+        str = self.name + ': '
+        for val in self:
+            str = str + '%d' % val + ' '
+        return str
+
+class DirectAnalogs(AnalogNode, PandaObject):
+    analogCount = 0
+    def __init__(self, vrpnClient, device):
+        # Keep track of number of analogs created
+        DirectAnalogs.analogCount += 1
+        # Create a unique name for this analog object
+        self.name = 'DirectAnalogs-' + `DirectAnalogs.analogCount`
+        # Create a new analog node for the given device
+        AnalogNode.__init__(self, vrpnClient, device)
+        # Attach node to data graph
+        self.nodePath = base.dataRoot.attachNewNode(self)
+    
+    def __getitem__(self, index):
+        if (index < 0) | index > self.getNumControls():
+            raise IndexError
+        return self.getControlState(index)
+    
+    def __len__(self):
+        return self.getNumControls()
+    
+    def enable(self):
+        self.nodePath.reparentTo(base.dataRoot)
+    
+    def disable(self):
+        self.nodePath.reparentTo(base.dataUnused)
+    
+    def getName(self):
+        return self.name
+    
+    def getNodePath(self):
+        return self.nodePath
+    
+    def __repr__(self):
+        str = self.name + ': '
+        for val in self:
+            str = str + '%.3f' % val + ' '
+        return str
+
+class DirectDials(DialNode, PandaObject):
+    dialCount = 0
+    def __init__(self, vrpnClient, device):
+        # Keep track of number of dials created
+        DirectDials.dialCount += 1
+        # Create a unique name for this dial object
+        self.name = 'DirectDials-' + `DirectDials.dialCount`
+        # Create a new dial node for the given device
+        DialNode.__init__(self, vrpnClient, device)
+        # Attach node to data graph
+        self.nodePath = base.dataRoot.attachNewNode(self)
+    
+    def __getitem__(self, index):
+        if (index < 0) | (index > self.getNumDials()):
+            raise IndexError
+        return self.readDial(index)
+    
+    def __len__(self):
+        return self.getNumDials()
+    
+    def enable(self):
+        self.nodePath.reparentTo(base.dataRoot)
+    
+    def disable(self):
+        self.nodePath.reparentTo(base.dataUnused)
+    
+    def getName(self):
+        return self.name
+    
+    def getNodePath(self):
+        return self.nodePath
+    
+    def __repr__(self):
+        str = self.name + ': '
+        for val in self:
+            str = str + '%.3f' % val + ' '
+        return str
+
+class DirectTimecodeReader(AnalogNode, PandaObject):
+    timecodeReaderCount = 0
+    def __init__(self, vrpnClient, device):
+        # Keep track of number of timecodeReader created
+        DirectTimecodeReader.timecodeReaderCount += 1
+        # Create a unique name for this dial object
+        self.name = ('DirectTimecodeReader-' +
+                     `DirectTimecodeReader.timecodeReaderCount`)
+        # Initialize components of timecode
+        self.frames = 0
+        self.seconds = 0
+        self.minutes = 0
+        self.hours = 0
+        # Create a new dial node for the given device
+        AnalogNode.__init__(self, vrpnClient, device)
+        # Attach node to data graph
+        self.nodePath = base.dataRoot.attachNewNode(self)
+    
+    def enable(self):
+        self.nodePath.reparentTo(base.dataRoot)
+    
+    def disable(self):
+        self.nodePath.reparentTo(base.dataUnused)
+    
+    def getName(self):
+        return self.name
+    
+    def getNodePath(self):
+        return self.nodePath
+    
+    def getTime(self):
+        # Assume only one card, use channel 0
+        timeBits = int(self.getControlState(0))
+        self.frames = ((timeBits & 0xF) +
+                       (((timeBits & 0xF0) >> 4) * 10))
+        self.seconds = (((timeBits & 0x0F00) >> 8) +
+                        (((timeBits & 0xF000) >> 12) * 10))
+        self.minutes = ((((timeBits & 0x0F0000) >> 16) * 10) +
+                        ((timeBits & 0xF00000) >> 20))
+        self.hours = ((((timeBits & 0xF0000000) >> 24) * 10) +
+                      ((timeBits & 0xF0000000) >> 28))
+        self.totalSeconds = ((self.hours * 3600) +
+                             (self.minutes * 60) +
+                             self.seconds +
+                             (self.frames / 30.0))
+        return (self.hours, self.minutes, self.seconds, self.frames,
+                self.totalSeconds)
+    
+    def __repr__(self):
+        str = ('%s: %d:%d:%d:%d' % ((self.name,) + self.getTime()[:-1]))
+        return str

+ 209 - 0
direct/src/directdevices/DirectJoybox.py

@@ -0,0 +1,209 @@
+""" Class used to create and control joybox device """
+from PandaObject import *
+from DirectDeviceManager import *
+from DirectGeometry import CLAMP
+import OnscreenText
+
+JOY_MIN = -0.95
+JOY_MAX = 0.95
+JOY_RANGE = JOY_MAX - JOY_MIN
+JOY_DEADBAND = 0.05
+# BUTTONS
+L_STICK = 0
+L_UPPER = 1
+L_LOWER = 2
+R_STICK = 3
+R_UPPER = 4
+R_LOWER = 5
+# ANALOGS
+L_LEFT_RIGHT = 0
+L_FWD_BACK = 1
+L_TWIST = 2
+L_SLIDE = 3
+R_LEFT_RIGHT = 4
+R_FWD_BACK = 5
+R_TWIST = 6
+R_SLIDE = 7
+
+class DirectJoybox(PandaObject):
+    joyboxCount = 0
+    xyzScale = 1.0
+    hprScale = 1.0
+    def __init__(self, nodePath = direct.camera):
+        # See if device manager has been initialized
+        if direct.deviceManager == None:
+            direct.deviceManager = DirectDeviceManager()
+        # Set name
+        self.name = 'Joybox-' + `DirectJoybox.joyboxCount`
+        # Get buttons and analogs
+        self.device = base.config.GetString('joybox-device', 'CerealBox')
+        self.analogs = direct.deviceManager.createAnalogs(self.device)
+        self.buttons = direct.deviceManager.createButtons(self.device)
+        self.aList = [0,0,0,0,0,0,0,0]
+        self.bList = [0,0,0,0,0,0,0,0]
+        self.mapping = [0,1,2,4,5,6]
+        self.modifier = [1,1,1,1,1,1]
+        # Button registry
+        self.addButtonEvents()        
+        # Initialize time
+        self.lastTime = globalClock.getTime()
+        # Record node path
+        self.nodePath = nodePath
+        # Text object to display current mode
+        self.readout = OnscreenText.OnscreenText( '', -0.9, -0.95 )
+        # Pick initial mode
+        self.updateFunc = self.joeFly
+        # Spawn update task
+        self.enable()
+    
+    def enable(self):
+        taskMgr.spawnMethodNamed(self.updateTask, self.name + '-updateTask')
+    
+    def disable(self):
+        taskMgr.removeTasksNamed(self.name + '-updateTask')
+
+    def addButtonEvents(self):
+        self.breg = ButtonRegistry.ptr()
+        # MRM: Hard coded!
+        for i in range(8):
+            self.buttons.setButtonMap(
+                i, self.breg.getButton(self.getEventName(i)))
+        self.eventThrower = self.buttons.getNodePath().attachNewNode(
+            ButtonThrower())
+    
+    def setNodePath(self, nodePath):
+        self.nodePath = nodePath
+    def getNodePath(self):
+        return self.nodePath
+    def getEventName(self, index):
+        return self.name + '-button-' + `index`
+
+    def updateTask(self, state):
+        self.updateVals()
+        self.updateFunc()
+        return Task.cont
+    
+    def updateVals(self):
+        # Update delta time
+        cTime = globalClock.getTime()
+        self.deltaTime = cTime - self.lastTime
+        self.lastTime = cTime
+        # Update analogs
+        for i in range(len(self.analogs)):
+            try:
+                self.aList[i] = self.normalizeAnalogChannel(i)
+            except IndexError:
+                # That channel may not have been updated yet
+                pass
+        # Update buttons
+        for i in range(len(self.buttons)):
+            try:
+                self.bList[i] = self.buttons[i]
+            except IndexError:
+                # That channel may not have been updated yet
+                pass
+    
+    def normalizeAnalog(self, val, min = -1, max = -1):
+        val = CLAMP(val, JOY_MIN, JOY_MAX)
+        if abs(val) < JOY_DEADBAND:
+            val = 0.0
+        return ((max - min) * ((val - JOY_MIN) / JOY_RANGE)) + min
+    
+    def normalizeAnalogChannel(self, chan, min = -1, max = 1):
+        if (chan == 2) | (chan == 6):
+            return self.normalizeAnalog(self.analogs[chan] * 3.0, min, max)
+        else:
+            return self.normalizeAnalog(self.analogs[chan], min, max)
+
+    def showMode(self, modeText):
+        def hideText(state, s = self):
+            s.readout.setText('')
+            return Task.done
+        taskMgr.removeTasksNamed(self.name + '-showMode')
+        # Update display
+        self.readout.setText(modeText)
+        t = taskMgr.doMethodLater(3.0, hideText, self.name + '-showMode')
+        t.uponDeath = hideText
+
+    def setMode(self, func, name):
+        self.disable()
+        self.updateFunc = func
+        self.showMode(name)
+        self.enable()
+        
+    def joeMode(self):
+        self.setMode(self.joeFly, 'Joe Mode')
+    
+    def joeFly(self):
+        hprScale = (self.normalizeAnalogChannel(3, 0.1, 200) *
+                    DirectJoybox.hprScale)
+        posScale = (self.normalizeAnalogChannel(7, 0.1, 100) *
+                    DirectJoybox.xyzScale)
+        # XYZ
+        x = self.aList[4]
+        y = self.aList[5]
+        if self.bList[L_STICK]:
+            z = 0.0
+        else:
+            z = self.aList[L_FWD_BACK]
+        pos = Vec3(x,y,z) * (posScale * self.deltaTime)
+        # HPR
+        h = -1 * self.aList[R_TWIST]
+        if self.bList[L_STICK]:
+            p = -1 * self.aList[L_FWD_BACK]
+        else:
+            p = 0.0
+        r = 0.0
+        hpr = Vec3(h,p,r) * (hprScale * self.deltaTime)
+        # Move node path
+        self.nodePath.setPosHpr(self.nodePath, pos, hpr)
+
+    def joyboxFly(self):
+        hprScale = (self.normalizeAnalogChannel(3, 0.1, 200) *
+                    DirectJoybox.hprScale)
+        posScale = (self.normalizeAnalogChannel(7, 0.1, 100) *
+                    DirectJoybox.xyzScale)
+        x = self.analogs[self.mapping[0]] * self.modifier[0]
+        y = self.analogs[self.mapping[1]] * self.modifier[1]
+        z = self.analogs[self.mapping[2]] * self.modifier[2]
+        pos = Vec3(x,y,z) * (posScale * self.deltaTime)
+        
+        h = self.analogs[self.mapping[3]] * self.modifier[3]
+        p = self.analogs[self.mapping[4]] * self.modifier[4]
+        r = self.analogs[self.mapping[5]] * self.modifier[5]
+        hpr = Vec3(h,p,r) * (hprScale * self.deltaTime)
+        # Move node path
+        self.nodePath.setPosHpr(self.nodePath, pos, hpr)
+
+    def demoMode(self):
+        self.mapping = [4,5,1,6,0,0]
+        self.modifier = [1,1,1,-1,0,0]
+        self.setMode(self.joyboxFly, 'Demo Mode')
+
+    def driveMode(self):
+        self.mapping = [0,5,1,4,1,0]
+        self.modifier = [1,1,1,-1,0,0]
+        self.setMode(self.joyboxFly, 'Drive Mode')
+
+    def hprXyzMode(self):
+        self.mapping = [4,5,6,2,1,0]
+        self.modifier = [1,1,-1,-1,-1,1]
+        self.setMode(self.joyboxFly, 'HprXyz Mode')
+
+    def lookaroundMode(self):
+        self.mapping = [0,0,0,4,5,0]
+        self.modifier = [0,0,0,-1,-1,0]
+        self.setMode(self.joyboxFly, 'Lookaround Mode')
+
+    def walkthruMode(self):
+        self.mapping = [4,5,2,6,1,0]
+        self.modifier = [1,1,-1,-1,-1, 1]
+        self.setMode(self.joyboxFly, 'Walkthru Mode')
+
+def jbTest():
+    jb = DirectJoybox()
+    jb.joeMode()
+    jb.accept(jb.getEventName(R_UPPER), jb.joeMode)
+    direct.cameraControl.accept(jb.getEventName(L_UPPER),
+                                direct.cameraControl.orbitUprightCam)
+    return jb

+ 7 - 0
direct/src/directtools/DirectSession.py

@@ -6,6 +6,7 @@ from DirectGrid import *
 from DirectGeometry import *
 from DirectLights import *
 from DirectSessionPanel import *
+from DirectDeviceManager import *
 import Placer
 import OnscreenText
 import types
@@ -50,6 +51,12 @@ class DirectSession(PandaObject):
         # self.readout.textNode.setCardColor(0.5, 0.5, 0.5, 0.5)
         self.readout.reparentTo( hidden )
 
+        # Create a vrpn client vrpn-server or default
+        if base.config.GetBool('want-vrpn', 0):
+            self.deviceManager = DirectDeviceManager()
+        else:
+            self.deviceManager = None
+
         self.fControl = 0
         self.fAlt = 0
         self.fShift = 0

+ 2 - 0
direct/src/particles/ForceGroup.py

@@ -71,6 +71,8 @@ class ForceGroup(DirectObject):
     # Utility functions 
     def __getitem__(self, index):
 	"""__getItem__(self, index)"""
+        if (index < 0) | (index > self.node.getNumForces()):
+            raise IndexError
 	return self.node.getForce(index)
 
     def __len__(self):

+ 4 - 8
direct/src/particles/ParticleEffect.py

@@ -49,8 +49,7 @@ class ParticleEffect(NodePath):
 	self.forceGroupDict[forceGroup.getName()] = forceGroup
 
 	# Associate the force group with all particles
-	flist = forceGroup.asList()
-	for f in flist:
+	for f in forceGroup:
 	    self.addForce(f)
 
     def addForce(self, force):
@@ -65,8 +64,7 @@ class ParticleEffect(NodePath):
 	self.forceGroupDict[forceGroup.getName()] = None
 
 	# Remove forces from all particles
-	flist = forceGroup.asList()
-	for f in flist:
+	for f in forceGroup:
 	    self.removeForce(f)
 
     def removeForce(self, force):
@@ -81,8 +79,7 @@ class ParticleEffect(NodePath):
 
 	# Associate all forces in all force groups with the particles
 	for fg in self.forceGroupDict.values():
-	    flist = fg.asList()
-	    for f in flist:
+	    for f in fg:
 		particles.addForce(f)
 
     def removeParticles(self, particles):
@@ -92,8 +89,7 @@ class ParticleEffect(NodePath):
 	
 	# Remove all forces from the particles
 	for fg in self.forceGroupDict.values():
-	    flist = fg.asList()
-	    for f in flist:
+	    for f in fg:
 		particles.removeForce(f)
 
     def getParticlesList(self):

+ 2 - 2
direct/src/tkpanels/ParticlePanel.py

@@ -1627,7 +1627,7 @@ class ParticlePanel(AppShell):
                               forceGroup.getName())
         self.forcePage = self.forceGroupNotebook.add(self.forcePageName)
         self.forcePagesDict[self.forcePageName] = self.forcePage
-        for force in forceGroup.asList():
+        for force in forceGroup:
             self.addForceWidget(forceGroup, force)
 
     def addForceWidget(self, forceGroup, force):
@@ -1635,7 +1635,7 @@ class ParticlePanel(AppShell):
         pageName = self.forcePageName
         # How many forces of the same type in the force group object
         count = 0
-        for f in forceGroup.asList():
+        for f in forceGroup:
             if f.getClassType().eq(force.getClassType()):
                 count += 1
         if isinstance(force, LinearVectorForce):