void StereoscopicRenderingApp::update()
{
	float	d, f;
	Area	area;

	switch( mFocusMethod )
	{
	case SET_CONVERGENCE:
		// auto-focus by calculating distance to center of interest
		d = (mCamera.getCenterOfInterestPoint() - mCamera.getEyePoint()).length();
		f = math<float>::min( 5.0f, d * 0.5f );

		// The setConvergence() method will not change the eye separation distance, 
		// which may cause the parallax effect to become uncomfortably big. 
		mCamera.setConvergence( f );
		mCamera.setEyeSeparation( 0.05f );
		break;
	case SET_FOCUS:
		// auto-focus by calculating distance to center of interest
		d = (mCamera.getCenterOfInterestPoint() - mCamera.getEyePoint()).length();
		f = math<float>::min( 5.0f, d * 0.5f );

		// The setConvergence( value, true ) method will automatically calculate a fitting value for the eye separation distance.
		// There is still no guarantee that the parallax effect stays within comfortable levels,
		// because there may be objects very near to the camera compared to the point we are looking at.
		mCamera.setConvergence( f, true );
		break;
	case AUTO_FOCUS:
		// Here, we use the gl::StereoAutoFocuser class to determine the best focal length,
		// based on the contents of the current depth buffer. This is by far the best method of
		// the three, because it guarantees the parallax effect will never be out of bounds.
		// Depending on the rendering method, we can sample different area's of the screen
		// to optimally detect details. This is not required, however.
		// Use the UP and DOWN keys to adjust the intensity of the parallax effect.
		switch( mRenderMethod ) 
		{
		case MONO:
			break;
		case SIDE_BY_SIDE:
			// sample half the left eye, half the right eye
			area = gl::getViewport();
			area.expand( -area.getWidth()/4, 0 );
			mAF.autoFocus( &mCamera, area );
			break;
		case OVER_UNDER:
			// sample half the left eye, half the right eye
			area = gl::getViewport();
			area.expand( 0, -area.getHeight()/4 );
			mAF.autoFocus( &mCamera, area );
			break;
		case ANAGLYPH_RED_CYAN:
			// sample the depth buffer of one of the FBO's
			mAF.autoFocus( &mCamera, mFbo );
			break;
		}
		break;
	}
}
void StereoscopicRenderingApp::draw()
{
	// clear color and depth buffers
	gl::clear( mColorBackground ); 
	
	// stereoscopic rendering
	switch( mRenderMethod ) 
	{
	case MONO:
		// render mono camera
		mCamera.disableStereo();
		render();
		break;
	case ANAGLYPH_RED_CYAN:
		renderAnaglyph( getWindowSize(), Color(1, 0, 0), Color(0, 1, 1) );
		break;
	case SIDE_BY_SIDE:
		renderSideBySide( getWindowSize() );
		break;
	case OVER_UNDER:
		renderOverUnder( getWindowSize() );
		break;
	case INTERLACED_HORIZONTAL:
		renderInterlacedHorizontal( getWindowSize() );
		break;
	}

	// draw auto focus visualizer
	if( mDrawAutoFocus ) mAF.draw();
}
void StereoscopicRenderingApp::renderUI()
{   
    float w = (float) getWindowWidth() * 0.5f;
    float h = (float) getWindowHeight();

	std::string renderMode, focusMode;
	switch(mRenderMethod) {
		case MONO: renderMode = "Mono"; break;
		case SIDE_BY_SIDE: renderMode = "Side By Side"; break;
		case OVER_UNDER: renderMode = "Over Under"; break;
		case ANAGLYPH_RED_CYAN: renderMode = "Anaglyph Red Cyan"; break;
		case INTERLACED_HORIZONTAL: renderMode = "Interlaced Horizontal"; break;
	}
	switch(mFocusMethod) {
		case SET_CONVERGENCE: focusMode = "setConvergence(d, false)"; break;
		case SET_FOCUS: focusMode = "setConvergence(d, true)"; break;
		case AUTO_FOCUS: focusMode = "autoFocus(cam)"; break;
	}

    std::string labels( "Render mode (F1-F5):\nFocus mode (1-3):\nFocal Length:\nEye Distance:\nAuto Focus Depth (Up/Down):\nAuto Focus Speed (Left/Right):" );
    boost::format values = boost::format( "%s\n%s\n%.2f\n%.2f\n%.2f\n%.2f" ) % renderMode % focusMode % mCamera.getConvergence() % mCamera.getEyeSeparation() % mAF.getDepth() % mAF.getSpeed();

#if(defined CINDER_MSW)
    gl::enableAlphaBlending();
    gl::drawString( labels, Vec2f( w - 350.0f, h - 150.0f ), Color::black(), mFont );
    gl::drawStringRight( values.str(), Vec2f( w + 350.0f, h - 150.0f ), Color::black(), mFont );
    gl::disableAlphaBlending();
#else
    // \n is not supported on the mac, so we draw separate strings
    std::vector< std::string > left, right;
	left = ci::split( labels, "\n", false );
	right = ci::split( values.str(), "\n", false );

    gl::enableAlphaBlending();
    for(size_t i=0;i<4;++i) {       
        gl::drawString( left[i], Vec2f( w - 350.0f, h - 150.0f + i * mFont.getSize() * 0.9f ), Color::black(), mFont );
        gl::drawStringRight( right[i], Vec2f( w + 350.0f, h - 150.0f + i * mFont.getSize() * 0.9f ), Color::black(), mFont );
    }
    gl::disableAlphaBlending();
#endif
}
void StereoscopicRenderingApp::keyDown( KeyEvent event )
{
	switch( event.getCode() )
	{
	case KeyEvent::KEY_ESCAPE:
		quit();
		break;
	case KeyEvent::KEY_f:
		// toggle full screen
		setFullScreen( ! isFullScreen() );
		break;
	case KeyEvent::KEY_v:
		// toggle vertical sync
		gl::enableVerticalSync( !gl::isVerticalSyncEnabled() );
		break;
	case KeyEvent::KEY_d:
		// toggle visualizer
		mDrawAutoFocus = !mDrawAutoFocus;
		break;
	case KeyEvent::KEY_u:
		// toggle interface
		mDrawUI = !mDrawUI;
		break;
	case KeyEvent::KEY_UP:
		// increase the parallax effect (towards negative parallax) 
		if(mFocusMethod == AUTO_FOCUS)
			mAF.setDepth( mAF.getDepth() + 0.01f );
		break;
	case KeyEvent::KEY_DOWN:
		// decrease the parallax effect (towards positive parallax) 
		if(mFocusMethod == AUTO_FOCUS)
			mAF.setDepth( mAF.getDepth() - 0.01f );
		break;
	case KeyEvent::KEY_SPACE:
		// reset the parallax effect to 'no parallax for the nearest object'
		mAF.setDepth( 1.0f );
		break;
	case KeyEvent::KEY_LEFT:
		// reduce the auto focus speed
		mAF.setSpeed( mAF.getSpeed() - 0.01f );
		break;
	case KeyEvent::KEY_RIGHT:
		// increase the auto focus speed
		mAF.setSpeed( mAF.getSpeed() + 0.01f );
		break;
	case KeyEvent::KEY_1:
		mFocusMethod = SET_CONVERGENCE;
		break;
	case KeyEvent::KEY_2:
		mFocusMethod = SET_FOCUS;
		break;
	case KeyEvent::KEY_3:
		mFocusMethod = AUTO_FOCUS;
		break;
	case KeyEvent::KEY_F1:
		mRenderMethod = MONO;
		createFbo();
		break;
	case KeyEvent::KEY_F2:
		mRenderMethod = ANAGLYPH_RED_CYAN;
		createFbo();
		break;
	case KeyEvent::KEY_F3:
		mRenderMethod = SIDE_BY_SIDE;
		createFbo();
		break;
	case KeyEvent::KEY_F4:
		mRenderMethod = OVER_UNDER;
		createFbo();
		break;
	case KeyEvent::KEY_F5:
		mRenderMethod = INTERLACED_HORIZONTAL;
		createFbo();
		break;
	}
}
void StereoscopicRenderingApp::draw()
{
	// find dimensions of each viewport 
	int w = getWindowWidth();
	int h = getWindowHeight();

	// clear color and depth buffers
	gl::clear( mBackgroundColor ); 
	
	// stereoscopic rendering
	switch( mRenderMethod ) 
	{
	case MONO:
		// render mono camera
		mCamera.disableStereo();
		render();
		break;
	case SIDE_BY_SIDE:
		// store current viewport
		glPushAttrib( GL_VIEWPORT_BIT );

		// draw to left half of window only
		gl::setViewport( Area(0, 0, w / 2, h) );

		// render left camera
		mCamera.enableStereoLeft();
		render();

		// draw to right half of window only
		gl::setViewport( Area(w / 2, 0, w, h) );

		// render right camera
		mCamera.enableStereoRight();
		render();

		// restore viewport
		glPopAttrib();
		break;
	case OVER_UNDER:
		// store current viewport
		glPushAttrib( GL_VIEWPORT_BIT );

		// draw to top half of window only
		gl::setViewport( Area(0, 0, w, h / 2) );

		// render left camera
		mCamera.enableStereoLeft();
		render();

		// draw to bottom half of window only
		gl::setViewport( Area(0, h / 2, w, h) );

		// render right camera
		mCamera.enableStereoRight();
		render();

		// restore viewport
		glPopAttrib();
		break;
	case ANAGLYPH_RED_CYAN:
		// store current viewport
		glPushAttrib( GL_VIEWPORT_BIT );

		// bind the left FBO and adjust the viewport to its bounds
		mAnaglyphLeft.bindFramebuffer();
		gl::setViewport( mAnaglyphLeft.getBounds() );

		// because glClear() does not respect the color mask, 
		// clear the color (and depth) buffers using a red filtered background color
		gl::clear( mBackgroundColor * Color( 1, 0, 0 ) );

		// set up color mask to only draw red and render left camera
		glColorMask( true, false, false, true );
		mCamera.enableStereoLeft();
		render();
		glColorMask( true, true, true, true );

		mAnaglyphLeft.unbindFramebuffer();
		
		// bind the right FBO and adjust the viewport to its bounds
		mAnaglyphRight.bindFramebuffer();
		gl::setViewport( mAnaglyphRight.getBounds() );
		
		// because glClear() does not respect the color mask, 
		// clear the color (and depth) buffers using a cyan filtered background color
		gl::clear( mBackgroundColor * Color( 0, 1, 1 ) );
		
		// set up color mask to only draw cyan and render right camera
		glColorMask( false, true, true, true );
		mCamera.enableStereoRight();
		render();
		glColorMask( true, true, true, true );

		mAnaglyphRight.unbindFramebuffer();

		// restore viewport
		glPopAttrib();

		// draw the FBO's on top of each other using a special additive blending operation
		gl::color( Color::white() );
		
		gl::draw( mAnaglyphLeft.getTexture(), Rectf( 0, (float) h, (float) w, 0 ) );	

		glEnable( GL_BLEND );
		glBlendFunc( GL_ONE, GL_ONE );
		gl::draw( mAnaglyphRight.getTexture(), Rectf( 0, (float) h, (float) w, 0) ); 
		glDisable( GL_BLEND );
		break;
	}

	// draw auto focus visualizer
	if( mDrawAutoFocus ) mAF.draw();
}