WorldViz User Forum  

Go Back   WorldViz User Forum > Vizard

Reply
 
Thread Tools Rate Thread Display Modes
  #1  
Old 02-14-2012, 05:46 PM
AySz88 AySz88 is offline
Member
 
Join Date: Aug 2011
Posts: 13
Intermittent orthographic stereo projection problems

I have an application where I am using both stereo and orthographic projection. For apparently arbitrary reasons, everything in the scene will suddenly have an incorrect disparity, all too far forward or too far back, and sometimes with a spurious vertical component - as if the projection has gotten changed somewhere. It seems to happen just before or after "important" events fire, such as timer events.

I have code that can reproduce the bug reliably for me, but very subtle changes (such as changing the frequencies of the aforementioned timers) will lead to the bug going in and out of existence, so I don't know if it would be helpful at all.

Any suggestions as to what could be going wrong? I also have been trying to narrow down what exactly changes - my hunch is the screen distance, but is there any way to query its value?

[edit] I do have trackers that can be linked to the viewpoint when I'm running in cave mode, but I break the links with the viewpoint when I switch to orthographic projection (by giving a None tracker to CaveView, IIRC). Are there any problems with doing it this way?

Last edited by AySz88; 02-14-2012 at 05:50 PM.
Reply With Quote
  #2  
Old 02-15-2012, 08:49 AM
farshizzo farshizzo is offline
WorldViz Team Member
 
Join Date: Mar 2003
Posts: 2,849
Are you using the vizcave module? Depending on how you setup the Cave object, it will update the projection matrix every frame to account for the latest position of the head tracker. If you have other code running in a timer that tries to change the projection, then this could be the cause of the conflict. It's hard to say without seeing some sample code.
Reply With Quote
  #3  
Old 02-15-2012, 02:10 PM
AySz88 AySz88 is offline
Member
 
Join Date: Aug 2011
Posts: 13
I do use the vizcave module, but even when I make sure no viewpoints are moving around (like by replacing the trackers with something that doesn't move), I get a lot of different disparity values. The disparity only seems to change when certain important code is run, too (it is when I re-apply the orthographic projection, for example).

Here's some code that reproduces the problem. Press 4 or 6 to have it repeatedly display the "stimulus". In case of no stereo equipment, the most obvious symptom is that the white box doesn't always stay in the same centered location from one trial to the next - it appears left or right by some unpredictable amount. (With stereo equipment, it's more apparent that what is happening is that the disparity is changing.) It usually starts happening by the 5th trial.

Here's the file for the stimulus part with as much code as I dared to remove taken out. I don't think most of this is too important, except that I do call orthoUpdateCaveAndView() in the restart() helper function, which is run each time you press 4 or 6.

Code:
import viz
import vizact
import vizshape
import sunyhardware

def makeCylinderSpheres(radius, height, vertexCount, vertexRadius, vac=None, dc=None):
	# FIXME quite slow - profile for problems
	import random
	from math import sin, cos, pi
	
	if vac and vertexRadius != 1.0:
		print "WARNING: setting vertex radius to 1.0 meters"
		print "  for VisualAngleConstantizer to scale later"
		vertexRadius = 1.0
	
	cylinder = viz.addGroup()
	
	# Distribution of verts in theta, stratifed, parameters by trial and error
	# TODO parameterize
	skipWidth = 4.0*pi/5.0
	sliceWidth = pi/3.0
	# in height
	base = -height*0.5
	climb = height/(vertexCount-1)
	
	for vertNum in xrange(vertexCount):
		# HACK Don't render the middle few to leave room for fixation dot
		if abs(vertNum - vertexCount/2.0) <= 1.1:
			continue;
		theta = skipWidth*vertNum + sliceWidth*random.random()
		pos = [radius*sin(theta), base + vertNum*climb, radius*cos(theta)]
		
		sphere = vizshape.addSphere(radius=vertexRadius, parent=cylinder)
		sphere.setPosition(*pos)
		
		if dc:
			dc.add(sphere)
		if vac:
			vac.add(sphere, vertexRadius*2)
	
	return cylinder

# TODO error message if HiBall not available?

(METERS, DEGREES) = xrange(2) # valid units

(offX, offY, offZ) = sunyhardware.getValue(('xZero', 'yZero', 'zZero'))

DEFAULT_PARAMS = {
	'vertexRadius': 0.01, # radius of spheres, in visual angle or length
	'vertexUnits': METERS, # unit of the above radius ('degrees' only works if not orthographic)
	# Rotation options
	'rotateSpeed': 180.0, # Degrees per second, positive = front goes left
	'spinAxis': (0,1,0), # Axis of rotation in world space, not local cube space
	
	'fixationSize': 0.02,
	'fixationColor': [0.5,0.5,0.5],
	'fixationFrameSize': 0.005, # just the frame around fixationSize
	'fixationFrameColor': [1.0,1.0,1.0],
	
	# Graphical options
	'orthographic': True,
	'objectConstructor': makeCylinderSpheres,
	# Define either 'dcTable' or a 'dc' DepthColorizer object; 'dc' takes priority over 'dcTable'
	'dcTable': ( # (distance from head, [H,S,V])
		( 1.7, [240, 0.75, 0.0]),
		( 1.7, [240, 0.75, 1.0]),
		( 2.3, [360, 0.75, 1.0]),
		( 2.3, [360, 0.75, 0.0])
	),
	
	#Interface options
	'interfaceParams': {
		'usePosition': False, #True,
		'centerThreshold': 180, #offX, # Dividing line between left and right (degrees or meters)
		'centerIgnore': 5, #0.02 # Zone (in each direction) in which answer is ambiguous and ignored (degrees or meters)
		'reverse': True, # Move or point rightwards for a 'correct' answer normally, leftwards for not
	},
	'indicParams': {
		# visual indicators for pointing left/center/right
		'leftObjPos': [-0.5, -0.25, 0], # Relative to center position
		'ambigObjPos': [0.0, -0.5, 0], # Relative to center position
		'rightObjPos': [0.5, -0.25, 0], # Relative to center position
	}
}

DEFAULT_PARAMS.update({
	'objectParams': {
		'radius': 0.10, # of the circle around the cylinder
		'height': 0.45,
		'vertexCount': 24,
		},
	'startAngle': (0,0,0), # Euler angles of initial rotation
	'centerPosition': [offX, offY, offZ]
	})

class Stimulus(viz.EventClass):
	""" TODO documentation """
	
	TIMER_ID = 0
	
	def __init__(self, stimParams):
		viz.EventClass.__init__(self)
		
		self.headTrkr = stimParams['headTrkr']
		
		# Construct stimulus object to display
		params = stimParams['objectParams'].copy()
		
		params['dc']=None
		params['vertexRadius'] = stimParams['vertexRadius']
		self.obj = stimParams['objectConstructor'](**params)
		
		# Apply actual spin to object
		self.spinObject = viz.addGroup()
		self.obj.parent(self.spinObject)
		
		self.spinObject.setPosition(stimParams['centerPosition'])
		spinAxis = stimParams['spinAxis']
		spin = vizact.spin(spinAxis[0], spinAxis[1], spinAxis[2], stimParams['rotateSpeed'], viz.FOREVER)
		self.spinObject.addAction(spin)
		self.obj.visible(viz.OFF)
		self.spinObject.disable(viz.ANIMATIONS)
		
		# Create fixation block
		self.fixGroup = viz.addGroup()
		
		self.fixation = vizshape.addQuad(
			size=(stimParams['fixationSize'], stimParams['fixationSize']),
			parent = self.fixGroup)
		self.fixation.color(stimParams['fixationColor'])
		self.fixation.billboard(viz.BILLBOARD_VIEW)
		
		totalFrameSize = stimParams['fixationSize'] + stimParams['fixationFrameSize']
		self.fixationFrame = vizshape.addQuad(size=(totalFrameSize, totalFrameSize),
			parent = self.fixGroup)
		self.fixationFrame.color(stimParams['fixationFrameColor'])
		self.fixationFrame.billboard(viz.BILLBOARD_VIEW)
		
		self.fixGroup.setPosition(stimParams['centerPosition'])
		self.fixGroup.visible(viz.OFF)
		
		# Save parameters for later
		self.stimParams = stimParams
	
	def startFixation(self, trialParams):
		self.obj.setEuler(*self.stimParams['startAngle'])
		
		viz.ipd(trialParams['ipd'])
		
		self.fixGroup.visible(viz.ON)
	
	def startStimulus(self, trialParams):
		self.spinObject.enable(viz.ANIMATIONS)
		self.obj.visible(viz.ON)
	
	def endStimulus(self, stairCallback):
		self.fixGroup.visible(viz.OFF)
		self.obj.visible(viz.OFF)
		self.spinObject.disable(viz.ANIMATIONS)
		
		#self.callback(viz.SENSOR_UP_EVENT, self.onButtonUp)
		self.callback(viz.KEYUP_EVENT, self.onKeyUp)
		
		self.stairCallback = stairCallback
	
	def onKeyUp(self, key):
		if key == '4' or key == '6':
			self.callback(viz.KEYUP_EVENT, None)
			return self.stairCallback(key == '4', False)
		else:
			pass # TODO 'try again' prompt

if __name__=='__main__':
	viz.go()
	
	stimParams = DEFAULT_PARAMS.copy()
	
	trackers = sunyhardware.getAllSensors()
	(stimParams['headTrkr'], stimParams['stylusTrkr'], stimParams['stylusBtn']) = trackers
	
	# test multiple simultaneous stimulus objects
	stim = Stimulus(stimParams)
	#stim2 = Stimulus(stimParams)
	
	RESTART_DELAY = 0.5
	
	import random
	
	def delayedRestart(*args):
		viz.callback(viz.TIMER_EVENT, restart)
		viz.starttimer(0, RESTART_DELAY)
		
	def restart(*args):
		print 'starting 1'
		headTrkr = stimParams['headTrkr']
		sunyhardware.orthoUpdateCaveAndView(
			tracker = headTrkr.head,
			cave = headTrkr.cave,
			view = headTrkr.view,
			ortho = stimParams['orthographic'])
		stim.startFixation({'ipd': random.uniform(-0.05, 0.05)})
		stim.startStimulus({})
		
		viz.callback(viz.TIMER_EVENT, endStim)
		viz.starttimer(0, 1.0)
	
	def endStim(*args):
		print 'ended 1'
		viz.killtimer(0)
		#stim.endStimulus(delayedRestart2)
		stim.endStimulus(delayedRestart)
	
	delayedRestart()
...and in the next post(s) are the potentially more-relevant hardware abstraction code. The most relevant functions are loadCave() (where I define helper functions makeOrthographic() and makePerspective()) and orthoUpdateCaveAndView(), which does the dirty work of turning off caves and such.

I edited the file so you'd get dummy VRPN sensors from localhost, which definitely don't move at all. It throws slightly-annoying "no response from server" warnings, though.
Reply With Quote
  #4  
Old 02-15-2012, 02:12 PM
AySz88 AySz88 is offline
Member
 
Join Date: Aug 2011
Posts: 13
(The bulk of the file is mostly due to documentation, which I'm keeping intact in case it's useful.)

Part 1 of sunyhardware.py:

Code:
"""
This module provides functions and classes related to the utilization of
virtual reality hardware with Vizard in the lab of _______ at SUNY Optometry.

Variables: None.
Functions: getValue, loadCave, linkView,
	isHiBallAvailable, getTracker, getAllSensors.
Classes: Keyboard, HiBall.

For additional information about a particular function or class, please see
the documentation string for that attribute.

Usage examples:
>>> (headTrkr, stylusTrkr, stylusBtn) = sunyhardware.getAllSensors()
>>> tracker = HiBall()
>>> screenWidth = getValue('screenWidth')
>>> print loadCave.__doc__

<removed>
August 4, 2008
	edited by:
	<removed>
	July 29, 2011
"""

from __future__ import division #implements "/" as division without truncation regardless of its operands
import viz #standard Vizard module
import vizcave #Vizard virtual reality cave module

#private variables (measurements taken by hand [AEY 8/11/2011] in meters)
	#1024x768, projector settings: 'size' = 1.060, 'vertical stretch' = 1.000
	#most are a rough average of two measurements
_screenHeight = 1.779
_screenWidth = 2.396
_screenHeightOffset = (
	0.448	#floor-to-frame
	+ 0.034	#plus frame width
	+ 0.030	#plus frame-to-image
	)
_screenWidthOffset = (
	-0.060	#origin-to-right-wall
	+ 0.270	#plus wall-to-big-frame
	+ 0.034	#plus frame width
	+ 0.030	#plus frame-to-image
	)
_screenDepthOffset = (
	0.573	#origin-to-wall
	+ 0.083	#plus wall-to-frame
	+ 0.023	#plus frame width
	+ 0.008	#plus glass-to-image (glass thickness)
	)
_eyesHeightOffset = 0.140 #approx dist of eyes below HiBall (MAR+SJH 1/23/2009, RBM subject)
_eyesDepthOffset = 0.110 #approx dist of eyes in front of HiBall (MAR+SJH 1/23/2009, RBM subject)

_stylusOffset = [-0.00175, -0.1050, -0.1693] #TODO from HiBall stylus calibration procedure, currently 2003 numbers
	# intended for link.preTrans(); see Help (Reference > Linking > Operators on Links)

def getValue(parameter):
	"""
	Returns the read-only value of the named private attribute.
	
	Currently accepted parameter names include: 'screenHeight', 'screenWidth',
	'screenHeightOffset', 'screenWidthOffset', 'screenDepthOffset',
	'eyesHeightOffset', 'eyesDepthOffset'.
	
	Definition:
	getValue(parameter)
	
	Usage:
	>>> screenWidth = getValue('screenWidth')
	"""
	
	if parameter=='screenWidth':
		return _screenWidth
	elif parameter=='screenHeight':
		return _screenHeight
	elif parameter=='screenHeightOffset':
		return _screenHeightOffset
	elif parameter=='screenWidthOffset':
		return _screenWidthOffset
	elif parameter=='screenDepthOffset':
		return _screenDepthOffset
	elif parameter=='eyesHeightOffset':
		return _eyesHeightOffset
	elif parameter=='eyesDepthOffset':
		return _eyesDepthOffset
	elif parameter=='xZero':
		return -_screenWidthOffset - _screenWidth/2
	elif parameter=='yZero':
		return _screenHeightOffset + _screenHeight/2
	elif parameter=='zZero':
		return _screenDepthOffset
	elif parameter=='stylusOffset':
		return _stylusOffset
	elif isinstance(parameter,tuple): # must use tuples since strings are lists
		return [getValue(x) for x in parameter]

def getVisualBounds(origin):
	# Given HiBall coordinates of a viewpoint, return elevations and azimuths of each edge of screen (l,r,t,b)
	from math import atan2, degrees
	(x0, y0, z0) = getValue(('xZero', 'yZero', 'zZero'))
	(w, h) = getValue(('screenWidth', 'screenHeight'))
	[x,y,z] = origin
	lrtb = (x0-w/2-x, x0+w/2-x, y0+h/2-y, y0-h/2-y)
	angles = map(lambda opposite: atan2(opposite,z), lrtb)
	print "bounds at", origin
	print "... are", map(degrees, angles)
	return angles

def loadCave(tracker=None,orthographic=False):
	"""
	Returns a Vizard cave optionally driven by tracker coordinates.
	
	Creates a vizcave wall based on coordinates for the Christie Mirage S+4K
	projection screen. Creates a stereo vizcave cave to which the wall and
	optionally provided tracker are added. The cave can optionally be placed
	in orthographic projection (defaults to perspective). The tracker, if
	provided, sends position and orientation information to the cave, which
	is used in the calculation of the viewing frustum. Note that the active
	viewport is not updated with the tracking position; please use linkView
	to do so.
	
	Definition:
	loadCave(tracker=None,orthographic=False)
	
	Usage:
	>>> cave = loadCave(tracker)
	"""
	
	#save default inter-pupilary distance (IPD)
	ipd = viz.MainWindow.getIPD()
	if not isinstance(tracker,viz.VizView):
		ipd = -ipd #KLUDGE - this solves eye-swap problem, but still not sure where this problem comes from
	
	def makeOrthographic():
		# TODO Doesn't change IPD based on orientation of eyes with head
		# Requires the wall to be in the X-Y plane
		# to set up tracker so that stereo IPD will be correct?
		
		# NOTE this is the "intended" way of doing this, but has drawbacks
		viz.MainView.eyeheight(0)
		viz.MainView.reset(viz.RESET_POS)
		viz.MainWindow.ortho(
			-_screenWidthOffset - _screenWidth,
			-_screenWidthOffset,
			_screenHeightOffset,
			_screenHeightOffset + _screenHeight,
			-1, -1, # automatic near and far clip
			eye = viz.BOTH_EYE)
		viz.MainWindow.screenDistance(_screenDepthOffset)
		
		return None
	
	cave = vizcave.Cave()
	
	def makePerspective():
		#setup wall using world coordinates
		upperLeft = [-_screenWidthOffset-_screenWidth, _screenHeightOffset+_screenHeight, _screenDepthOffset]
		upperRight = [-_screenWidthOffset, _screenHeightOffset+_screenHeight, _screenDepthOffset]
		lowerLeft = [-_screenWidthOffset-_screenWidth, _screenHeightOffset, _screenDepthOffset]
		lowerRight = [-_screenWidthOffset, _screenHeightOffset, _screenDepthOffset]
		wall = vizcave.Wall('wall',upperLeft,upperRight,lowerLeft,lowerRight)
		
		#setup cave with wall and tracker (optional)
		cave.setIPD(ipd)
		cave.addWall(wall)
		if tracker:
			cave.setTracker(pos=tracker, ori=tracker)
	
	tracker.makeOrthographic = makeOrthographic
	tracker.makePerspective = makePerspective
	
	if orthographic:
		tracker.makeOrthographic()
	else:
		tracker.makePerspective()
	
	return cave
	
	# FAILED METHOD
	# Below code **messes up stereo** when giving orthographic-like projection
	"""
	#setup wall using world coordinates
	upperLeft = [-_screenWidthOffset-_screenWidth, _screenHeightOffset+_screenHeight, _screenDepthOffset]
	upperRight = [-_screenWidthOffset, _screenHeightOffset+_screenHeight, _screenDepthOffset]
	lowerLeft = [-_screenWidthOffset-_screenWidth, _screenHeightOffset, _screenDepthOffset]
	lowerRight = [-_screenWidthOffset, _screenHeightOffset, _screenDepthOffset]
	wall = vizcave.Wall('wall',upperLeft,upperRight,lowerLeft,lowerRight)
	
	#setup cave with wall and tracker (optional)
	cave = vizcave.Cave()
	cave.setIPD(ipd)
	cave.addWall(wall)
	if tracker:
		# HACK if orthographic, shift viewpoint as if view is from far away (image as through a telescope)
		cave.orthoTracker = viz.link(tracker, viz.NullLinkable)
		cave.orthoTracker.postTrans([0,0,-100])
		
		def makeOrthographic():
			cave.setTracker(pos=cave.orthoTracker, ori=tracker)
		def makePerspective():
			cave.setTracker(pos=tracker, ori=tracker) #named arguments used for clarity (ori required for stereo)
		
		cave.makeOrthographic = makeOrthographic
		cave.makePerspective = makePerspective
		
		if orthographic:
			cave.makeOrthographic()
		else:
			cave.makePerspective()
	return cave
	"""

def linkView(tracker,withCave=False):
	"""
	Returns a view based on tracker coordinates.
	
	Links the provided tracker to the Vizard main viewpoint. If
	withCave=True, links the tracker to main view through the cave
	view, otherwise links directly to the main view (default).
	
	Definition:
	linkView(tracker,withCave=False)
	
	Usage:
	>>> view = linkView(tracker,True)
	"""
	
	# HACK CaveView doesn't require a cave...at the moment
	return vizcave.CaveView(tracker)
	"""
	if withCave:
		#link tracker to CaveView
		view = vizcave.CaveView(tracker) #added flexibility to change viewpoint manually
	else:
		#link tracker directly to viz.MainView
		view = viz.link(tracker,viz.MainView)
		view.setDstFlag(viz.LINK_POS_RAW) #use the raw tracker position coordinates
	return view
	"""

def orthoUpdateCaveAndView(tracker, cave, view, ortho):
	if ortho:
		tracker.makeOrthographic()
		cave.setTracker(pos=None, ori=None)
		view.setView(viz.NullLinkable) # disassociate CaveView and tracker
		viz.cam.reset()
	else:
		tracker.makePerspective()
		cave.setTracker(pos=tracker,ori=tracker)
		view.setView(viz.MainView)

Last edited by AySz88; 02-15-2012 at 02:14 PM. Reason: take out names; not intended for distribution (yet)
Reply With Quote
  #5  
Old 02-15-2012, 02:13 PM
AySz88 AySz88 is offline
Member
 
Join Date: Aug 2011
Posts: 13
Part 2 of sunyhardware.py:
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__
Reply With Quote
  #6  
Old 02-15-2012, 02:26 PM
farshizzo farshizzo is offline
WorldViz Team Member
 
Join Date: Mar 2003
Posts: 2,849
As I mentioned, the Cave object will automatically call <window>.frustum every frame if you specify a tracker. I noticed your code will call <window>.ortho under certain circumstances. So one call will be overriding the other, depending on which is called first. Do any of your code paths allow for <window>.ortho to be called while a tracker is specified for the cave object?
Reply With Quote
  #7  
Old 02-15-2012, 02:37 PM
AySz88 AySz88 is offline
Member
 
Join Date: Aug 2011
Posts: 13
The code always does the following at the same time as calling <window>.ortho(). (Testing it just now, doing it just before or just after makes no difference.)
Code:
cave.setTracker(pos=None, ori=None)
view.setView(viz.NullLinkable) # disassociate CaveView and tracker
viz.cam.reset()
This should cause the cave object to stop calling <window>.frustum, correct?
Reply With Quote
  #8  
Old 02-15-2012, 03:10 PM
farshizzo farshizzo is offline
WorldViz Team Member
 
Join Date: Mar 2003
Posts: 2,849
That should stop the cave object from automatically changing the frustum. Try adding the following line as well:
Code:
viz.MainWindow.ipdVector(None)
I noticed the IPD values changes drastically from trial to trial. Are you sure this isn't causing the problem?
Reply With Quote
  #9  
Old 02-15-2012, 03:24 PM
AySz88 AySz88 is offline
Member
 
Join Date: Aug 2011
Posts: 13
Yes - the center of the object is at the same distance as the screendistance used for the orthographic projection. Thus the square mark should always have zero disparity, so it shouldn't move no matter what the IPD is.

Does that line of code force the IPD value to zero? That wouldn't work for my purposes, since I do need stereo - it is adjusting the IPD value so as to "turn up" or "turn down" the stereo cue on demand, as needed by the rest of the experiment.
Reply With Quote
  #10  
Old 02-15-2012, 04:13 PM
farshizzo farshizzo is offline
WorldViz Team Member
 
Join Date: Mar 2003
Posts: 2,849
The IPD vector is used by vizcave to change the direction the IPD shift is applied to the view matrix. I think adding that line should fix your problem.
Reply With Quote
  #11  
Old 02-17-2012, 12:50 PM
AySz88 AySz88 is offline
Member
 
Join Date: Aug 2011
Posts: 13
Just got a chance to try it, and it works! Thanks!
Reply With Quote
Reply

Tags
bug, heisenbug, orthographic, stereo

Thread Tools
Display Modes Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
orthographic projection in a head-tracked cave environment michaelrepucci Vizard 5 12-14-2011 10:29 AM
vizcave and quad-buffered stereo michaelrepucci Vizard 7 11-29-2011 11:15 AM
Stereo Projection Moh200jo Vizard 3 06-10-2009 06:33 PM
Stereo problems on 3D monitor timbo Vizard 3 08-13-2008 02:07 PM
stereo projection issue asimbh Vizard 3 10-05-2007 10:22 AM


All times are GMT -7. The time now is 08:04 AM.


Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Copyright 2002-2018 WorldViz LLC