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