//========= 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(); }