#1
|
|||
|
|||
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 06:50 PM. |
#2
|
|||
|
|||
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.
|
#3
|
|||
|
|||
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() 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. |
#4
|
|||
|
|||
(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 03:14 PM. Reason: take out names; not intended for distribution (yet) |
#5
|
|||
|
|||
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__ |
#6
|
|||
|
|||
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?
|
#7
|
|||
|
|||
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() |
#8
|
|||
|
|||
That should stop the cave object from automatically changing the frustum. Try adding the following line as well:
Code:
viz.MainWindow.ipdVector(None) |
#9
|
|||
|
|||
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. |
#10
|
|||
|
|||
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.
|
#11
|
|||
|
|||
Just got a chance to try it, and it works! Thanks!
|
Tags |
bug, heisenbug, orthographic, stereo |
|
|
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
orthographic projection in a head-tracked cave environment | michaelrepucci | Vizard | 5 | 12-14-2011 11:29 AM |
vizcave and quad-buffered stereo | michaelrepucci | Vizard | 7 | 11-29-2011 12:15 PM |
Stereo Projection | Moh200jo | Vizard | 3 | 06-10-2009 07:33 PM |
Stereo problems on 3D monitor | timbo | Vizard | 3 | 08-13-2008 03:07 PM |
stereo projection issue | asimbh | Vizard | 3 | 10-05-2007 11:22 AM |