Code:
class Keyboard:
"""
Implements a simulated positional tracker using the keyboard.
This class is especially useful for debugging, when access to the
HiBall hardware is unavailable or undesirable. The key mapping is
displayed in the main window. An associated cave object, with
tracker-adjusted frustum, and link to the active viewpoint are
created by default.
Variables: head, cave, view.
Functions: write.
The variable head provides access to the Vizard tracker object, the
variable cave provides access to the Vizard cave object with which
the tracker is associated, and the variable view provides access to
the view to which the tracker is linked. For additional information
about a particular function, please see the documentation string
for that function.
Definition:
Keyboard(makeCave=True,orthographic=False,trackCave=True,makeLink=True)
Usage:
>>> tracker = Keyboard()
>>> position = tracker.head.getPosition()
"""
head = cave = view = None
def __init__(self,makeCave=True,orthographic=False,trackCave=True,makeLink=True):
#keyboard simulated tracker
import viztracker
self.head = viztracker.add()
self.head.setPosition(-_screenWidth/2-_screenWidthOffset,_screenHeight/2+_screenHeightOffset+_eyesHeightOffset,-2) #start in the "middle" of the room
#display keyboard mapping
import vizinfo #Vizard 2D GUI commands
self.info = vizinfo.add('Left/Right = Q/E\n'+
'Up/Down = R/F\n'+
'Forwards/Backwards = W/S\n\n'+
'Roll Left/Right = G/J\n'+
'Pitch Up/Down = Y/H\n'+
'Yaw Left/Right = A/D')
self.info.title('Keyboard Tracker Mapping')
#add cave and link viewpoint
if makeCave:
if trackCave:
self.cave = loadCave(self.head,orthographic)
else:
self.cave = loadCave(None,orthographic)
if makeLink:
self.view = linkView(self.head,makeCave)
def write(self,timerID):
"""
Prints the simulated keyboard coordinates to standard output.
Accepts the timerID as specified by viz.callback. Prints the
current tracker position and orientation whenever a timer
expires.
Definition:
write(timerID)
Usage:
>>> viz.callback(viz.TIMER_EVENT,write)
>>> viz.starttimer(0,3,viz.FOREVER)
"""
print 'Keyboard [x, y, z] = [%.3f, %.3f, %.3f]'%tuple(self.head.getPosition())
print 'Keyboard [yaw, pitch, roll] = [%.3f, %.3f, %.3f]'%tuple(self.head.getEuler())
class HiBall:
"""
Implements a connection to the HiBall Wide Area Tracker via VRPN.
This class simplifies access to the HiBall hardware. It loads the
required VRPN plug-in and creates the tracker object(s). An
associated cave object, with tracker-adjusted frustum, and link
to the active viewpoint are created by default.
The HiBall sensor zero point (X=Y=Z=0) is located on floor below
front-right LED. The default directional vectors (+X=Left,
+Y=Backwards, +Z=Up) are remapped (+X=Right, +Y=Up, +Z=Forwards).
The default orientational vectors are correspondingly remapped.
For the headtracker, the location tracked is between the eyes of
a generic individual. For the stylus, the location tracked is at
the base of the HiBall sensor. (TODO calibrate for the stylus point)
Variables: head, cave, view.
Functions: write.
The variable head provides access to a viz.Linkable tracking the
appropriate location (not necessarily a VizExtensionSensor), the
variable cave provides access to the Vizard cave object with which
the tracker is associated, and the variable view provides access to
the view to which the tracker is linked. For additional information
about a particular function, please see the documentation string
for that function.
Definition:
HiBall(makeCave=True,orthographic=False,trackCave=True,makeLink=True,sensor=SENSOR_HEADTRACKER)
Usage:
>>> tracker = HiBall()
>>> position = tracker.head.getPosition()
"""
SENSOR_HEADTRACKER = 0;
SENSOR_STYLUS = 1; #handheld stylus (borrowed from UNC as of 7/2011)
SENSOR_DEFAULT = SENSOR_HEADTRACKER;
vrpn = viz.add('vrpn7.dle') #VRPN 7.15 plug-in
head = cave = view = None
def __init__(self,makeCave=True,orthographic=False,trackCave=True,makeLink=True,sensor=SENSOR_DEFAULT):
"""VRPN head tracker"""
# the 0 in 'Tracker0' is NOT the sensor number
self.raw = self.__class__.vrpn.addTracker('Tracker0@localhost', sensor)
self.raw.swapPos([-1,3,-2])
self.raw.swapQuat([1,-3,2,4])
# Find appropriate offset in local coordinate space
if sensor == self.__class__.SENSOR_HEADTRACKER:
offset = (0,-_eyesHeightOffset,-_eyesDepthOffset)
elif sensor == self.__class__.SENSOR_STYLUS:
offset = _stylusOffset
else:
offset = (0,0,0)
print 'Unknown sensor %s; please update sunyhardware'%sensor
link = viz.link(self.raw, viz.NullLinkable)
link.preTrans(offset)
self.head = link # Links are themselves linkables
self.sensor = sensor; # just for .write()
#add cave and link viewpoint
if makeCave:
if trackCave:
self.cave = loadCave(self.head,orthographic)
else:
self.cave = loadCave(None,orthographic)
if makeLink:
self.view = linkView(self.head,makeCave)
def write(self,timerID):
"""
Prints the HiBall coordinates to standard output.
Accepts the timerID as specified by viz.callback. Prints the
current tracker position and orientation whenever any timer
expires.
Definition:
write(timerID)
Usage:
>>> viz.callback(viz.TIMER_EVENT,write)
>>> viz.starttimer(0,3,viz.FOREVER)
"""
print 'HiBall%s'%self.sensor, '[x, y, z] = [%.3f, %.3f, %.3f]'%tuple(self.head.getPosition())
print 'HiBall%s'%self.sensor, '[yaw, pitch, roll] = [%.3f, %.3f, %.3f]'%tuple(self.head.getEuler())
def isHiBallAvailable():
"""
Tries to connect to the HiBall server (computer name 'hiball')
on port 3883, and returns true when successful.
Doesn't actually do anything (similar to a ping).
"""
import socket #built-in Python socket module
try:
udpSock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #create UDP socket
udpSock.connect(('hiball',3883)) #try to connect
udpSock.close() #close socket test connection
return True
except socket.gaierror:
return False
def getTracker(makeCave=True,orthographic=False,trackCave=True,makeLink=True,sensor=HiBall.SENSOR_DEFAULT):
"""
Factory function to return a HiBall or Keyboard tracker.
This class is a factory function, which returns a HiBall tracker
object when the HiBall system is present, or a simulated Keyboard
tracker object otherwise. For additional information about the
HiBall and Keyboard classes, please see the class documentation
strings.
The boolean flags makeCave, orthographic, trackCave, and makeLink
specify the type of virtual reality environment to construct:
makeCave=True uses hard-coded cave parameters (see loadCave),
orthographic=True results in orthographic projection (object size
will need to be reduced by a factor of 100), trackCave=True allows
the tracker object to update the cave frustums, and makeLink=True
links the tracker object's position to the cave's view matrix.
Definition:
getTracker(makeCave=True,orthographic=False,trackCave=True,makeLink=True,sensor=HiBall.SENSOR_DEFAULT)
Usage:
>>> tracker = getTracker(sensor=HiBall.SENSOR_HEADTRACKER)
>>> position = tracker.head.getPosition()
"""
# return appropriate tracker object
if isHiBallAvailable():
return HiBall(makeCave,orthographic,trackCave,makeLink,sensor)
else:
return Keyboard(makeCave,orthographic,trackCave,makeLink)
def getAllSensors():
"""
Returns all three sensors (head and stylus trackers, and stylus button)
after constructing them using 'reasonable' default settings. This sets
up the cave with perspective projection, with tracking, and links the
headtracker to the cave's view matrix.
The head tracker tracks the orientation of the head and the location of
the point between the eyes (of a generic subject). The stylus tracker
provides the position of the stylus at the bottom of the sensor and its
orientation.
Note that if the hiball server is not available, it will return a
keyboard tracker as the headtracker, and None for the stylus tracker
and stylus button.
Usage:
>>> (headTracker, stylusTracker, button) = getAllSensors()
>>> # EXAMPLES FOR DEBUGGING
>>> def writeAll(timerID):
headTracker.write(timerID)
if stylusTracker: stylusTracker.write(timerID)
if stylusButton: print 'Button =', stylusButton.buttonState(), '(buttons:' + str(stylusButton.buttonCount()) + ')'
>>> viz.callback(viz.TIMER_EVENT,writeAll)
>>> viz.starttimer(0,3,viz.FOREVER)
>>> def onButtonUp(e):
if e.object is stylusButton:
pass #YOUR CODE HERE
>>> viz.callback(viz.SENSOR_UP_EVENT,onButtonUp)
>>> def onButtonDown(e):
if e.object is stylusButton:
pass #YOUR CODE HERE
>>> viz.callback(viz.SENSOR_DOWN_EVENT,onButtonDown)
"""
if True:#isHiBallAvailable():
headTracker = HiBall(sensor=HiBall.SENSOR_HEADTRACKER)
stylusTracker = HiBall(makeCave=False,
makeLink=False,
sensor=HiBall.SENSOR_STYLUS)
button = HiBall.vrpn.addButton('Button0@localhost')
else:
headTracker = Keyboard()
stylusTracker = None
button = None
return (headTracker, stylusTracker, button)
if __name__=='__main__': #if module is run as a script
#display the documentation string
print __doc__