blob: 0bb469b9ca3109bf8a9dceb5016a97ea92178732 [file]
//========= Copyright Valve Corporation ============//
#include "hmd_opencv_sandbox.h"
#include "chew.h"
CMainApplication * APP;
//---------------------------------------------------------------------------------------------------------------------
// Purpose: Returns true if the action is active and its state is true
//---------------------------------------------------------------------------------------------------------------------
bool GetDigitalActionState(vr::VRActionHandle_t action, vr::VRInputValueHandle_t *pDevicePath = nullptr )
{
vr::InputDigitalActionData_t actionData;
vr::VRInput()->GetDigitalActionData(action, &actionData, sizeof(actionData), vr::k_ulInvalidInputValueHandle );
if (pDevicePath)
{
*pDevicePath = vr::k_ulInvalidInputValueHandle;
if (actionData.bActive)
{
vr::InputOriginInfo_t originInfo;
if (vr::VRInputError_None == vr::VRInput()->GetOriginTrackedDeviceInfo(actionData.activeOrigin, &originInfo, sizeof(originInfo)))
{
*pDevicePath = originInfo.devicePath;
}
}
}
return actionData.bActive && actionData.bState;
}
//---------------------------------------------------------------------------------------------------------------------
// Purpose: Returns true if the action is active and had a falling edge
//---------------------------------------------------------------------------------------------------------------------
bool GetDigitalActionFallingEdge( vr::VRActionHandle_t action, vr::VRInputValueHandle_t *pDevicePath = nullptr )
{
vr::InputDigitalActionData_t actionData;
vr::VRInput()->GetDigitalActionData( action, &actionData, sizeof( actionData ), vr::k_ulInvalidInputValueHandle );
if ( pDevicePath )
{
*pDevicePath = vr::k_ulInvalidInputValueHandle;
if ( actionData.bActive )
{
vr::InputOriginInfo_t originInfo;
if ( vr::VRInputError_None == vr::VRInput()->GetOriginTrackedDeviceInfo( actionData.activeOrigin, &originInfo, sizeof( originInfo ) ) )
{
*pDevicePath = originInfo.devicePath;
}
}
}
return actionData.bActive && actionData.bChanged && !actionData.bState;
}
//-----------------------------------------------------------------------------
// Purpose: This is called by SDL in PromptAssertion.
//-----------------------------------------------------------------------------
static FILE _iob[] = { *stdin, *stdout, *stderr };
extern "C" FILE * __cdecl __iob_func( void ) { return _iob; }
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CMainApplication::CMainApplication( int argc, char *argv[] )
: m_nCompanionWindowWidth( 2048 )
, m_nCompanionWindowHeight( 1024 )
, m_shdCompanionWindowProgram( CONTENT_FOLDER"/companion" )
, m_pIVRSystem( NULL )
, m_bDebugOpenGL( false )
, m_bVerbose( false )
, m_bPerf( false )
, m_bVblank( false )
, m_strPoseClasses( "" )
, m_CameraApp( this )
, bQuit( false )
, m_geoCompanion( "CompanionGeo" )
, m_shdRenderModel( CONTENT_FOLDER"/rendermodel" )
{
APP = this;
for( int i = 1; i < argc; i++ )
{
if( !stricmp( argv[i], "-gldebug" ) )
{
m_bDebugOpenGL = true;
}
else if( !stricmp( argv[i], "-verbose" ) )
{
m_bVerbose = true;
}
else if( !stricmp( argv[i], "-novblank" ) )
{
m_bVblank = false;
}
}
// other initialization tasks are done in BInit
memset(m_rDevClassChar, 0, sizeof(m_rDevClassChar));
};
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CMainApplication::~CMainApplication()
{
// work is done in Shutdown
dprintf( 0, "Shutdown" );
if ( m_pCompanionWindow )
{
SDL_DestroyWindow( m_pCompanionWindow );
m_pCompanionWindow = NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose: Helper to get a string from a tracked device property and turn it
// into a std::string
//-----------------------------------------------------------------------------
std::string GetTrackedDeviceString( vr::TrackedDeviceIndex_t unDevice, vr::TrackedDeviceProperty prop, vr::TrackedPropertyError *peError = NULL )
{
uint32_t unRequiredBufferLen = vr::VRSystem()->GetStringTrackedDeviceProperty( unDevice, prop, NULL, 0, peError );
if( unRequiredBufferLen == 0 )
return "";
char *pchBuffer = new char[ unRequiredBufferLen ];
unRequiredBufferLen = vr::VRSystem()->GetStringTrackedDeviceProperty( unDevice, prop, pchBuffer, unRequiredBufferLen, peError );
std::string sResult = pchBuffer;
delete [] pchBuffer;
return sResult;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CMainApplication::BInit()
{
if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_TIMER ) < 0 )
{
printf( "%s - SDL could not initialize! SDL Error: %s\n", __FUNCTION__, SDL_GetError() );
return false;
}
vr::EVRInitError eError = vr::VRInitError_None;
m_pIVRSystem = vr::VR_Init( &eError, vr::VRApplication_Scene );
if ( eError != vr::VRInitError_None )
{
m_pIVRSystem = NULL;
printf( "Unable to init VR runtime: %s", vr::VR_GetVRInitErrorAsEnglishDescription( eError ) );
return false;
}
int nWindowPosX = 700;
int nWindowPosY = 100;
Uint32 unWindowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 4 );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 1 );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );
SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 0 );
SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, 0 );
if ( m_bDebugOpenGL )
{
SDL_GL_SetAttribute( SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG );
}
m_pCompanionWindow = SDL_CreateWindow( "hmd_opencv_sandbox", nWindowPosX, nWindowPosY, m_nCompanionWindowWidth, m_nCompanionWindowHeight, unWindowFlags );
if ( m_pCompanionWindow == NULL )
{
printf( "%s - Window could not be created! SDL Error: %s\n", __FUNCTION__, SDL_GetError() );
return false;
}
m_pContext = SDL_GL_CreateContext( m_pCompanionWindow );
if ( m_pContext == NULL )
{
printf( "%s - OpenGL context could not be created! SDL Error: %s\n", __FUNCTION__, SDL_GetError() );
return false;
}
chewInit();
m_strDisplay = GetTrackedDeviceString( vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SerialNumber_String );
std::string strWindowTitle = "hmd_opencv_sandbox - " + m_strDisplay;
SDL_SetWindowTitle( m_pCompanionWindow, strWindowTitle.c_str() );
m_fNearClip = 0.1f;
m_fFarClip = 60.0f;
if (!BInitGL())
{
printf("%s - Unable to initialize OpenGL!\n", __FUNCTION__);
return false;
}
if (!BInitCompositor())
{
printf("%s - Failed to initialize VR Compositor!\n", __FUNCTION__);
return false;
}
std::string basepath;
{
char curdir[MAX_PATH];
GetCurrentDirectoryA( MAX_PATH, curdir );
basepath = curdir;
}
//If we want to be relative to the executable instead of the working folder we can do this.
//std::string basepath = Path_StripFilename( Path_GetExecutablePath() );
vr::VRInput()->SetActionManifestPath( Path_MakeAbsolute( CONTENT_FOLDER"/hmd_opencv_sandbox_actions.json", basepath ).c_str() );
vr::VRInput()->GetActionSetHandle( "/actions/demo", &m_actionsetDemo );
vr::VRInput()->GetActionHandle( "/actions/demo/in/advance_demo", &m_actionAdvanceDemo );
vr::VRInput()->GetActionHandle( "/actions/demo/in/AnalogInput", &m_actionAnalongInput );
vr::VRInput()->GetInputSourceHandle( "/user/hand/left", &m_rHand[Left].m_source );
vr::VRInput()->GetActionHandle( "/actions/demo/in/Hand_Left", &m_rHand[Left].m_actionPose );
vr::VRInput()->GetInputSourceHandle( "/user/hand/right", &m_rHand[Right].m_source );
vr::VRInput()->GetActionHandle( "/actions/demo/in/Hand_Right", &m_rHand[Right].m_actionPose );
m_CameraApp.BInit();
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Outputs the string in message to debugging output.
// All other parameters are ignored.
// Does not return any meaningful value or reference.
//-----------------------------------------------------------------------------
void APIENTRY DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const char* message, const void* userParam)
{
dprintf( 0, "GL Error: %s\n", message );
}
//-----------------------------------------------------------------------------
// Purpose: Initialize OpenGL. Returns true if OpenGL has been successfully
// initialized, false if shaders could not be created.
// If failure occurred in a module other than shaders, the function
// may return true or throw an error.
//-----------------------------------------------------------------------------
bool CMainApplication::BInitGL()
{
if( m_bDebugOpenGL )
{
glDebugMessageCallback( (GLDEBUGPROC)DebugCallback, nullptr);
glDebugMessageControl( GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE );
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
}
SetupStereoRenderTargets();
SetupCompanionWindow();
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Initialize Compositor. Returns true if the compositor was
// successfully initialized, false otherwise.
//-----------------------------------------------------------------------------
bool CMainApplication::BInitCompositor()
{
if ( !vr::VRCompositor() )
{
printf( "Compositor initialization failed. See log file for details\n" );
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMainApplication::Shutdown()
{
if( m_pIVRSystem )
{
vr::VR_Shutdown();
m_pIVRSystem = NULL;
}
m_CameraApp.Shutdown();
for( std::vector< GeometryObject * >::iterator i = m_vecRenderModels.begin(); i != m_vecRenderModels.end(); i++ )
{
delete (*i);
}
m_vecRenderModels.clear();
if( m_bDebugOpenGL )
{
glDebugMessageControl( GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_FALSE );
glDebugMessageCallback(nullptr, nullptr);
}
glDeleteRenderbuffers( 1, &leftEyeDesc.m_nDepthBufferId );
glDeleteTextures( 1, &leftEyeDesc.m_nRenderTextureId );
glDeleteFramebuffers( 1, &leftEyeDesc.m_nRenderFramebufferId );
glDeleteTextures( 1, &leftEyeDesc.m_nResolveTextureId );
glDeleteFramebuffers( 1, &leftEyeDesc.m_nResolveFramebufferId );
glDeleteRenderbuffers( 1, &rightEyeDesc.m_nDepthBufferId );
glDeleteTextures( 1, &rightEyeDesc.m_nRenderTextureId );
glDeleteFramebuffers( 1, &rightEyeDesc.m_nRenderFramebufferId );
glDeleteTextures( 1, &rightEyeDesc.m_nResolveTextureId );
glDeleteFramebuffers( 1, &rightEyeDesc.m_nResolveFramebufferId );
dprintf( 0, "Shutdown complete. Exiting\n" );
exit( 0 );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CMainApplication::HandleInput()
{
SDL_Event sdlEvent;
bool bRet = false;
while ( SDL_PollEvent( &sdlEvent ) != 0 )
{
if ( sdlEvent.type == SDL_QUIT )
{
bRet = true;
}
else if ( sdlEvent.type == SDL_KEYDOWN )
{
if ( sdlEvent.key.keysym.sym == SDLK_ESCAPE )
{
bRet = true;
}
else
{
APP->m_CameraApp.KeyDown( sdlEvent.key.keysym.sym );
}
}
}
// Process SteamVR events
vr::VREvent_t event;
while( m_pIVRSystem->PollNextEvent( &event, sizeof( event ) ) )
{
ProcessVREvent( event );
}
// Process SteamVR action state
// UpdateActionState is called each frame to update the state of the actions themselves. The application
// controls which action sets are active with the provided array of VRActiveActionSet_t structs.
vr::VRActiveActionSet_t actionSet = { 0 };
actionSet.ulActionSet = m_actionsetDemo;
vr::VRInput()->UpdateActionState( &actionSet, sizeof(actionSet), 1 );
vr::VRInputValueHandle_t ulAdvanceDemo;
if ( GetDigitalActionFallingEdge( m_actionAdvanceDemo, &ulAdvanceDemo ) )
{
if ( ulAdvanceDemo == m_rHand[Left].m_source )
{
m_CameraApp.AdvanceSettings( -1 );
}
if ( ulAdvanceDemo == m_rHand[Right].m_source )
{
m_CameraApp.AdvanceSettings( 1 );
}
}
#if 0
vr::VRInputValueHandle_t ulAdvanceDemo;
if ( GetDigitalActionState( m_actionAdvanceDemo, &ulAdvanceDemo ) )
{
if ( ulAdvanceDemo == m_rHand[Left].m_source )
{
m_CameraApp.AdvanceSettings( -1 );
}
if ( ulAdvanceDemo == m_rHand[Right].m_source )
{
m_CameraApp.AdvanceSettings( 1 );
}
}
#endif
vr::InputAnalogActionData_t analogData;
if ( vr::VRInput()->GetAnalogActionData( m_actionAnalongInput, &analogData, sizeof( analogData ), vr::k_ulInvalidInputValueHandle ) == vr::VRInputError_None && analogData.bActive )
{
m_vAnalogValue[0] = analogData.x;
m_vAnalogValue[1] = analogData.y;
}
m_rHand[Left].m_bShowController = true;
m_rHand[Right].m_bShowController = true;
for ( EHand eHand = Left; eHand <= Right; ((int&)eHand)++ )
{
vr::InputPoseActionData_t poseData;
vr::EVRInputError e = vr::VRInput()->GetPoseActionDataRelativeToNow( m_rHand[eHand].m_actionPose, vr::TrackingUniverseStanding, 0, &poseData, sizeof( poseData ), vr::k_ulInvalidInputValueHandle );
if ( e != vr::VRInputError_None || !poseData.bActive || !poseData.pose.bPoseIsValid )
{
m_rHand[eHand].m_bShowController = false;
}
else
{
m_rHand[eHand].m_rmat4Pose = ConvertSteamVRMatrixToMatrix4( poseData.pose.mDeviceToAbsoluteTracking );
vr::InputOriginInfo_t originInfo;
if ( vr::VRInput()->GetOriginTrackedDeviceInfo( poseData.activeOrigin, &originInfo, sizeof( originInfo ) ) == vr::VRInputError_None
&& originInfo.trackedDeviceIndex != vr::k_unTrackedDeviceIndexInvalid )
{
std::string sRenderModelName = GetTrackedDeviceString( originInfo.trackedDeviceIndex, vr::Prop_RenderModelName_String );
if ( sRenderModelName != m_rHand[eHand].m_sRenderModelName )
{
dprintf( 0, "Found controller. Loading rendermodel %s\n", sRenderModelName.c_str() );
m_rHand[eHand].m_pRenderModel = FindOrLoadRenderModel( sRenderModelName.c_str() );
m_rHand[eHand].m_sRenderModelName = sRenderModelName;
}
}
}
}
return bRet;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMainApplication::RunMainLoop()
{
SDL_StartTextInput();
SDL_ShowCursor( SDL_DISABLE );
while ( !bQuit )
{
bQuit = HandleInput();
RenderFrame();
}
dprintf( 0, "Main loop exited.\n" );
SDL_StopTextInput();
}
//-----------------------------------------------------------------------------
// Purpose: Processes a single VR event
//-----------------------------------------------------------------------------
void CMainApplication::ProcessVREvent( const vr::VREvent_t & event )
{
switch( event.eventType )
{
case vr::VREvent_TrackedDeviceDeactivated:
{
dprintf( 0, "Device %u detached.\n", event.trackedDeviceIndex );
}
break;
case vr::VREvent_TrackedDeviceUpdated:
{
dprintf( 0, "Device %u updated.\n", event.trackedDeviceIndex );
}
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMainApplication::RenderFrame()
{
std::stringstream errors;
if ( m_shdCompanionWindowProgram.CheckShader( errors ) < 0 || m_shdRenderModel.CheckShader( errors ) < 0 )
{
std::cout << errors.str() << std::endl;
}
SetupCameras();
if ( m_pIVRSystem )
{
m_trmErrors.Prerender();
m_trmProfile.Prerender();
m_CameraApp.Prerender();
//Do rendering
RenderStereoTargets();
RenderCompanionWindow();
vr::Texture_t leftEyeTexture = { (void*)(uintptr_t)leftEyeDesc.m_nResolveTextureId, vr::TextureType_OpenGL, vr::ColorSpace_Gamma };
vr::VRCompositor()->Submit( vr::Eye_Left, &leftEyeTexture );
vr::Texture_t rightEyeTexture = { (void*)(uintptr_t)rightEyeDesc.m_nResolveTextureId, vr::TextureType_OpenGL, vr::ColorSpace_Gamma };
vr::VRCompositor()->Submit( vr::Eye_Right, &rightEyeTexture );
}
//$ HACKHACK. From gpuview profiling, it looks like there is a bug where two renders and a present
// happen right before and after the vsync causing all kinds of jittering issues. This glFinish()
// appears to clear that up. Temporary fix while I try to get nvidia to investigate this problem.
// 1/29/2014 mikesart
glFinish();
// SwapWindow
{
SDL_GL_SwapWindow( m_pCompanionWindow );
}
// Clear
{
// We want to make sure the glFinish waits for the entire present to complete, not just the submission
// of the command. So, we do a clear here right here so the glFinish will wait fully for the swap.
glColorMask( TRUE, TRUE, TRUE, TRUE );//This ensures that only alpha will be effected
glClearColor( 0, 0, 0, 0 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
}
// Flush and wait for swap.
if ( m_bVblank )
{
glFlush();
glFinish();
}
UpdateHMDMatrixPose();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMainApplication::SetupCameras()
{
m_mat4ProjectionLeft = GetHMDMatrixProjectionEye( vr::Eye_Left );
m_mat4ProjectionRight = GetHMDMatrixProjectionEye( vr::Eye_Right );
m_mat4eyePosLeft = GetHMDMatrixPoseEye( vr::Eye_Left );
m_mat4eyePosRight = GetHMDMatrixPoseEye( vr::Eye_Right );
}
//-----------------------------------------------------------------------------
// Purpose: Creates a frame buffer. Returns true if the buffer was set up.
// Returns false if the setup failed.
//-----------------------------------------------------------------------------
bool CMainApplication::CreateFrameBuffer( int nWidth, int nHeight, FramebufferDesc &framebufferDesc )
{
glGenFramebuffers(1, &framebufferDesc.m_nRenderFramebufferId );
glBindFramebuffer(GL_FRAMEBUFFER, framebufferDesc.m_nRenderFramebufferId);
glGenRenderbuffers(1, &framebufferDesc.m_nDepthBufferId);
glBindRenderbuffer(GL_RENDERBUFFER, framebufferDesc.m_nDepthBufferId);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT, nWidth, nHeight );
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, framebufferDesc.m_nDepthBufferId );
glGenTextures(1, &framebufferDesc.m_nRenderTextureId );
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, framebufferDesc.m_nRenderTextureId );
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, nWidth, nHeight, true);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, framebufferDesc.m_nRenderTextureId, 0);
glGenFramebuffers(1, &framebufferDesc.m_nResolveFramebufferId );
glBindFramebuffer(GL_FRAMEBUFFER, framebufferDesc.m_nResolveFramebufferId);
glGenTextures(1, &framebufferDesc.m_nResolveTextureId );
glBindTexture(GL_TEXTURE_2D, framebufferDesc.m_nResolveTextureId );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, nWidth, nHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebufferDesc.m_nResolveTextureId, 0);
// check FBO status
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
return false;
}
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CMainApplication::SetupStereoRenderTargets()
{
if ( !m_pIVRSystem )
return false;
m_pIVRSystem->GetRecommendedRenderTargetSize( &m_nRenderWidth, &m_nRenderHeight );
CreateFrameBuffer( m_nRenderWidth, m_nRenderHeight, leftEyeDesc );
CreateFrameBuffer( m_nRenderWidth, m_nRenderHeight, rightEyeDesc );
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMainApplication::SetupCompanionWindow()
{
if ( !m_pIVRSystem )
return;
m_geoCompanion.MakeUnitSquare( 0 );
m_geoCompanion.Check();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMainApplication::RenderStereoTargets()
{
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
glEnable( GL_MULTISAMPLE );
// Left Eye
glBindFramebuffer( GL_FRAMEBUFFER, leftEyeDesc.m_nRenderFramebufferId );
glViewport(0, 0, m_nRenderWidth, m_nRenderHeight );
RenderScene( vr::Eye_Left );
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
glDisable( GL_MULTISAMPLE );
glBindFramebuffer(GL_READ_FRAMEBUFFER, leftEyeDesc.m_nRenderFramebufferId);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, leftEyeDesc.m_nResolveFramebufferId );
glBlitFramebuffer( 0, 0, m_nRenderWidth, m_nRenderHeight, 0, 0, m_nRenderWidth, m_nRenderHeight,
GL_COLOR_BUFFER_BIT,
GL_LINEAR );
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0 );
glEnable( GL_MULTISAMPLE );
// Right Eye
glBindFramebuffer( GL_FRAMEBUFFER, rightEyeDesc.m_nRenderFramebufferId );
glViewport(0, 0, m_nRenderWidth, m_nRenderHeight );
RenderScene( vr::Eye_Right );
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
glDisable( GL_MULTISAMPLE );
glBindFramebuffer(GL_READ_FRAMEBUFFER, rightEyeDesc.m_nRenderFramebufferId );
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, rightEyeDesc.m_nResolveFramebufferId );
glBlitFramebuffer( 0, 0, m_nRenderWidth, m_nRenderHeight, 0, 0, m_nRenderWidth, m_nRenderHeight,
GL_COLOR_BUFFER_BIT,
GL_LINEAR );
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0 );
}
//-----------------------------------------------------------------------------
// Purpose: Renders a scene with respect to nEye.
//-----------------------------------------------------------------------------
void CMainApplication::RenderScene( vr::Hmd_Eye nEye )
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
gCurrentViewProjection = GetCurrentViewProjectionMatrix( nEye );
//Render any controllers
for ( EHand eHand = Left; eHand <= Right; ((int&)eHand)++ )
{
if ( !m_rHand[eHand].m_bShowController || !m_rHand[eHand].m_pRenderModel )
continue;
const Matrix4 & matDeviceToTracking = m_rHand[eHand].m_rmat4Pose;
Matrix4 matMVP = gCurrentViewProjection * matDeviceToTracking;
m_shdRenderModel.Use();
glUniformMatrix4fv( 0, 1, GL_FALSE, matMVP.get() );
m_rHand[eHand].m_pRenderModel->Render();
if ( eHand == Left )
{
Matrix4 premat;
premat.identity();
premat.translate( 1.2f, 0.5f, 0.0 );
premat.scale( 0.2f, 0.2f, 1.0 );
premat.rotateZ( 110 ); //How much to rotate the screen, itself.
premat.rotateX( 120 );
premat.rotateZ( 60 ); //How much to swing around back.
m_trmErrors.SetXFormPre( premat );
m_trmErrors.SetXForm( matDeviceToTracking );
glEnable( GL_CULL_FACE );
glCullFace( GL_FRONT );
m_trmErrors.Render();
glDisable( GL_CULL_FACE );
}
if ( eHand == Right )
{
Matrix4 premat;
premat.identity();
premat.rotateZ( 180 );
premat.translate( 1.2f, -0.5f, 0.0 );
premat.scale( 0.2f, 0.2f, 1.0 );
premat.rotateZ( 180 - 110 );
premat.rotateX( 120 );
premat.rotateZ( 110 + 180 );
m_trmProfile.SetXFormPre( premat );
m_trmProfile.SetXForm( matDeviceToTracking );
glEnable( GL_CULL_FACE );
glCullFace( GL_FRONT );
m_trmProfile.Render();
glDisable( GL_CULL_FACE );
}
}
//Render the rest of the scene.
m_CameraApp.RenderScene( nEye );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMainApplication::RenderCompanionWindow()
{
glDisable(GL_DEPTH_TEST);
glViewport( 0, 0, m_nCompanionWindowWidth/2, m_nCompanionWindowHeight );
m_shdCompanionWindowProgram.Use();
// render left eye (first half of index array )
glBindTexture(GL_TEXTURE_2D, leftEyeDesc.m_nResolveTextureId );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
m_geoCompanion.Render();
glUseProgram( 0 );
m_CameraApp.CompanionRender();
}
//-----------------------------------------------------------------------------
// Purpose: Gets a Matrix Projection Eye with respect to nEye.
//-----------------------------------------------------------------------------
Matrix4 CMainApplication::GetHMDMatrixProjectionEye( vr::Hmd_Eye nEye )
{
if ( !m_pIVRSystem )
return Matrix4();
vr::HmdMatrix44_t mat = m_pIVRSystem->GetProjectionMatrix( nEye, m_fNearClip, m_fFarClip );
return Matrix4(
mat.m[0][0], mat.m[1][0], mat.m[2][0], mat.m[3][0],
mat.m[0][1], mat.m[1][1], mat.m[2][1], mat.m[3][1],
mat.m[0][2], mat.m[1][2], mat.m[2][2], mat.m[3][2],
mat.m[0][3], mat.m[1][3], mat.m[2][3], mat.m[3][3]
);
}
//-----------------------------------------------------------------------------
// Purpose: Gets an HMDMatrixPoseEye with respect to nEye.
//-----------------------------------------------------------------------------
Matrix4 CMainApplication::GetHMDMatrixPoseEye( vr::Hmd_Eye nEye )
{
if ( !m_pIVRSystem )
return Matrix4();
vr::HmdMatrix34_t matEyeRight = m_pIVRSystem->GetEyeToHeadTransform( nEye );
Matrix4 matrixObj(
matEyeRight.m[0][0], matEyeRight.m[1][0], matEyeRight.m[2][0], 0.0,
matEyeRight.m[0][1], matEyeRight.m[1][1], matEyeRight.m[2][1], 0.0,
matEyeRight.m[0][2], matEyeRight.m[1][2], matEyeRight.m[2][2], 0.0,
matEyeRight.m[0][3], matEyeRight.m[1][3], matEyeRight.m[2][3], 1.0f
);
return matrixObj.invert();
}
//-----------------------------------------------------------------------------
// Purpose: Gets a Current View Projection Matrix with respect to nEye,
// which may be an Eye_Left or an Eye_Right.
//-----------------------------------------------------------------------------
Matrix4 CMainApplication::GetCurrentViewProjectionMatrix( vr::Hmd_Eye nEye )
{
Matrix4 matMVP;
if( nEye == vr::Eye_Left )
{
matMVP = m_mat4ProjectionLeft * m_mat4eyePosLeft * m_mat4HMDPose;
}
else if( nEye == vr::Eye_Right )
{
matMVP = m_mat4ProjectionRight * m_mat4eyePosRight * m_mat4HMDPose;
}
return matMVP;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMainApplication::UpdateHMDMatrixPose()
{
if ( !m_pIVRSystem )
return;
vr::VRCompositor()->WaitGetPoses(m_rTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, NULL, 0 );
m_strPoseClasses = "";
for ( int nDevice = 0; nDevice < vr::k_unMaxTrackedDeviceCount; ++nDevice )
{
if ( m_rTrackedDevicePose[nDevice].bPoseIsValid )
{
m_rmat4DevicePose[nDevice] = ConvertSteamVRMatrixToMatrix4( m_rTrackedDevicePose[nDevice].mDeviceToAbsoluteTracking );
if (m_rDevClassChar[nDevice]==0)
{
switch ( m_pIVRSystem->GetTrackedDeviceClass(nDevice))
{
case vr::TrackedDeviceClass_Controller: m_rDevClassChar[nDevice] = 'C'; break;
case vr::TrackedDeviceClass_HMD: m_rDevClassChar[nDevice] = 'H'; break;
case vr::TrackedDeviceClass_Invalid: m_rDevClassChar[nDevice] = 'I'; break;
case vr::TrackedDeviceClass_GenericTracker: m_rDevClassChar[nDevice] = 'G'; break;
case vr::TrackedDeviceClass_TrackingReference: m_rDevClassChar[nDevice] = 'T'; break;
default: m_rDevClassChar[nDevice] = '?'; break;
}
}
m_strPoseClasses += m_rDevClassChar[nDevice];
}
}
if ( m_rTrackedDevicePose[vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid )
{
m_mat4HMDPose = m_rmat4DevicePose[vr::k_unTrackedDeviceIndex_Hmd];
m_mat4HMDPose.invert();
}
}
//-----------------------------------------------------------------------------
// Purpose: Finds a render model we've already loaded or loads a new one
//-----------------------------------------------------------------------------
GeometryObject *CMainApplication::FindOrLoadRenderModel( const char *pchRenderModelName )
{
GeometryObject *pRenderModel = NULL;
for( std::vector< GeometryObject * >::iterator i = m_vecRenderModels.begin(); i != m_vecRenderModels.end(); i++ )
{
if( !stricmp( (*i)->GetName().c_str(), pchRenderModelName ) )
{
pRenderModel = *i;
break;
}
}
// load the model if we didn't find one
if( !pRenderModel )
{
vr::RenderModel_t *pModel;
vr::EVRRenderModelError error;
while ( 1 )
{
error = vr::VRRenderModels()->LoadRenderModel_Async( pchRenderModelName, &pModel );
if ( error != vr::VRRenderModelError_Loading )
break;
OGUSleep( 1000 );
}
if ( error != vr::VRRenderModelError_None )
{
dprintf( 0,"Unable to load render model %s - %s\n", pchRenderModelName, vr::VRRenderModels()->GetRenderModelErrorNameFromEnum( error ) );
return NULL; // move on to the next tracked device
}
vr::RenderModel_TextureMap_t *pTexture;
while ( 1 )
{
error = vr::VRRenderModels()->LoadTexture_Async( pModel->diffuseTextureId, &pTexture );
if ( error != vr::VRRenderModelError_Loading )
break;
OGUSleep( 1000 );
}
if ( error != vr::VRRenderModelError_None )
{
dprintf( 0, "Unable to load render texture id:%d for render model %s\n", pModel->diffuseTextureId, pchRenderModelName );
vr::VRRenderModels()->FreeRenderModel( pModel );
return NULL; // move on to the next tracked device
}
pRenderModel = new GeometryObject( pchRenderModelName );
if( pRenderModel->CreateFromRenderModel( pModel ) || pRenderModel->ApplyTextureRM( pTexture ) )
{
dprintf( 0, "Unable to create GL model from render model %s\n", pchRenderModelName );
delete pRenderModel;
pRenderModel = NULL;
}
else
{
m_vecRenderModels.push_back( pRenderModel );
}
vr::VRRenderModels()->FreeRenderModel( pModel );
vr::VRRenderModels()->FreeTexture( pTexture );
}
return pRenderModel;
}
#undef main
extern "C" { int main( int argc, char * argv[] ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
CMainApplication *pMainApplication = new CMainApplication( argc, argv );
if (!pMainApplication->BInit())
{
pMainApplication->Shutdown();
}
pMainApplication->RunMainLoop();
dprintf( 0, "Shutting down.\n" );
pMainApplication->Shutdown();
}