#ifdef BT_ENABLE_VR //========= Copyright Valve Corporation ============// #include "../OpenGLWindow/SimpleOpenGL3App.h" #include "../OpenGLWindow/OpenGLInclude.h" #include "Bullet3Common/b3Quaternion.h" #include "Bullet3Common/b3Transform.h" #include "../ExampleBrowser/OpenGLGuiHelper.h" #include "../CommonInterfaces/CommonExampleInterface.h" #include "../CommonInterfaces/CommonGUIHelperInterface.h" #include "BulletCollision/CollisionDispatch/btCollisionObject.h" #include "BulletCollision/CollisionShapes/btCollisionShape.h" #include "BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h" #include "LinearMath/btIDebugDraw.h" int gSharedMemoryKey = -1; int gDebugDrawFlags = 0; //how can you try typing on a keyboard, without seeing it? //it is pretty funny, to see the desktop in VR! #include #include #include #include #include "lodepng.h" #include "Matrices.h" #include "pathtools.h" CommonExampleInterface* sExample; int sPrevPacketNum=0; OpenGLGuiHelper* sGuiPtr = 0; static vr::VRControllerState_t sPrevStates[vr::k_unMaxTrackedDeviceCount] = { 0 }; #if defined(POSIX) #include "unistd.h" #endif #ifdef _WIN32 #include #endif void ThreadSleep( unsigned long nMilliseconds ) { #if defined(_WIN32) ::Sleep( nMilliseconds ); #elif defined(POSIX) usleep( nMilliseconds * 1000 ); #endif } class CGLRenderModel { public: CGLRenderModel( const std::string & sRenderModelName ); ~CGLRenderModel(); bool BInit( const vr::RenderModel_t & vrModel, const vr::RenderModel_TextureMap_t & vrDiffuseTexture ); void Cleanup(); void Draw(); const std::string & GetName() const { return m_sModelName; } private: GLuint m_glVertBuffer; GLuint m_glIndexBuffer; GLuint m_glVertArray; GLuint m_glTexture; GLsizei m_unVertexCount; std::string m_sModelName; }; static bool g_bPrintf = true; //----------------------------------------------------------------------------- // Purpose: //------------------------------------------------------------------------------ class CMainApplication { public: CMainApplication( int argc, char *argv[] ); virtual ~CMainApplication(); bool BInit(); bool BInitGL(); bool BInitCompositor(); void getControllerTransform(int unDevice, b3Transform& tr); void SetupRenderModels(); void Shutdown(); void RunMainLoop(); bool HandleInput(); void ProcessVREvent( const vr::VREvent_t & event ); void RenderFrame(); bool SetupTexturemaps(); void SetupScene(); void AddCubeToScene( Matrix4 mat, std::vector &vertdata ); void AddCubeVertex( float fl0, float fl1, float fl2, float fl3, float fl4, std::vector &vertdata ); void DrawControllers(); bool SetupStereoRenderTargets(); void SetupDistortion(); void SetupCameras(); void RenderStereoTargets(); void RenderDistortion(); void RenderScene( vr::Hmd_Eye nEye ); Matrix4 GetHMDMatrixProjectionEye( vr::Hmd_Eye nEye ); Matrix4 GetHMDMatrixPoseEye( vr::Hmd_Eye nEye ); Matrix4 GetCurrentViewProjectionMatrix( vr::Hmd_Eye nEye ); void UpdateHMDMatrixPose(); Matrix4 ConvertSteamVRMatrixToMatrix4( const vr::HmdMatrix34_t &matPose ); GLuint CompileGLShader( const char *pchShaderName, const char *pchVertexShader, const char *pchFragmentShader ); bool CreateAllShaders(); void SetupRenderModelForTrackedDevice( vr::TrackedDeviceIndex_t unTrackedDeviceIndex ); CGLRenderModel *FindOrLoadRenderModel( const char *pchRenderModelName ); private: bool m_bDebugOpenGL; bool m_bVerbose; bool m_bPerf; bool m_bVblank; bool m_bGlFinishHack; vr::IVRSystem *m_pHMD; vr::IVRRenderModels *m_pRenderModels; std::string m_strDriver; std::string m_strDisplay; vr::TrackedDevicePose_t m_rTrackedDevicePose[ vr::k_unMaxTrackedDeviceCount ]; Matrix4 m_rmat4DevicePose[ vr::k_unMaxTrackedDeviceCount ]; bool m_rbShowTrackedDevice[ vr::k_unMaxTrackedDeviceCount ]; private: SimpleOpenGL3App* m_app; uint32_t m_nWindowWidth; uint32_t m_nWindowHeight; bool m_hasContext; private: // OpenGL bookkeeping int m_iTrackedControllerCount; int m_iTrackedControllerCount_Last; int m_iValidPoseCount; int m_iValidPoseCount_Last; bool m_bShowCubes; std::string m_strPoseClasses; // what classes we saw poses for this frame char m_rDevClassChar[ vr::k_unMaxTrackedDeviceCount ]; // for each device, a character representing its class int m_iSceneVolumeWidth; int m_iSceneVolumeHeight; int m_iSceneVolumeDepth; float m_fScaleSpacing; float m_fScale; int m_iSceneVolumeInit; // if you want something other than the default 20x20x20 float m_fNearClip; float m_fFarClip; GLuint m_iTexture; unsigned int m_uiVertcount; GLuint m_glSceneVertBuffer; GLuint m_unSceneVAO; GLuint m_unLensVAO; GLuint m_glIDVertBuffer; GLuint m_glIDIndexBuffer; unsigned int m_uiIndexSize; GLuint m_glControllerVertBuffer; GLuint m_unControllerVAO; unsigned int m_uiControllerVertcount; Matrix4 m_mat4HMDPose; Matrix4 m_mat4eyePosLeft; Matrix4 m_mat4eyePosRight; Matrix4 m_mat4ProjectionCenter; Matrix4 m_mat4ProjectionLeft; Matrix4 m_mat4ProjectionRight; struct VertexDataScene { Vector3 position; Vector2 texCoord; }; struct VertexDataLens { Vector2 position; Vector2 texCoordRed; Vector2 texCoordGreen; Vector2 texCoordBlue; }; GLuint m_unSceneProgramID; GLuint m_unLensProgramID; GLuint m_unControllerTransformProgramID; GLuint m_unRenderModelProgramID; GLint m_nSceneMatrixLocation; GLint m_nControllerMatrixLocation; GLint m_nRenderModelMatrixLocation; struct FramebufferDesc { GLuint m_nDepthBufferId; GLuint m_nRenderTextureId; GLuint m_nRenderFramebufferId; GLuint m_nResolveTextureId; GLuint m_nResolveFramebufferId; }; FramebufferDesc leftEyeDesc; FramebufferDesc rightEyeDesc; bool CreateFrameBuffer( int nWidth, int nHeight, FramebufferDesc &framebufferDesc ); uint32_t m_nRenderWidth; uint32_t m_nRenderHeight; std::vector< CGLRenderModel * > m_vecRenderModels; CGLRenderModel *m_rTrackedDeviceToRenderModel[ vr::k_unMaxTrackedDeviceCount ]; }; //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- CMainApplication::CMainApplication( int argc, char *argv[] ) : m_app(NULL) , m_hasContext(false) , m_nWindowWidth( 1280 ) , m_nWindowHeight( 720 ) , m_unSceneProgramID( 0 ) , m_unLensProgramID( 0 ) , m_unControllerTransformProgramID( 0 ) , m_unRenderModelProgramID( 0 ) , m_pHMD( NULL ) , m_pRenderModels( NULL ) , m_bDebugOpenGL( false ) , m_bVerbose( false ) , m_bPerf( false ) , m_bVblank( false ) , m_bGlFinishHack( true ) , m_glControllerVertBuffer( 0 ) , m_unControllerVAO( 0 ) , m_unLensVAO( 0 ) , m_unSceneVAO( 0 ) , m_nSceneMatrixLocation( -1 ) , m_nControllerMatrixLocation( -1 ) , m_nRenderModelMatrixLocation( -1 ) , m_iTrackedControllerCount( 0 ) , m_iTrackedControllerCount_Last( -1 ) , m_iValidPoseCount( 0 ) , m_iValidPoseCount_Last( -1 ) , m_iSceneVolumeInit( 20 ) , m_strPoseClasses("") , m_bShowCubes( false ) { 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; } else if( !stricmp( argv[i], "-noglfinishhack" ) ) { m_bGlFinishHack = false; } else if( !stricmp( argv[i], "-noprintf" ) ) { g_bPrintf = false; } else if ( !stricmp( argv[i], "-cubevolume" ) && ( argc > i + 1 ) && ( *argv[ i + 1 ] != '-' ) ) { m_iSceneVolumeInit = atoi( argv[ i + 1 ] ); i++; } } // other initialization tasks are done in BInit memset(m_rDevClassChar, 0, sizeof(m_rDevClassChar)); }; //----------------------------------------------------------------------------- // Purpose: Destructor //----------------------------------------------------------------------------- CMainApplication::~CMainApplication() { // work is done in Shutdown b3Printf( "Shutdown" ); } //----------------------------------------------------------------------------- // Purpose: Helper to get a string from a tracked device property and turn it // into a std::string //----------------------------------------------------------------------------- std::string GetTrackedDeviceString( vr::IVRSystem *pHmd, vr::TrackedDeviceIndex_t unDevice, vr::TrackedDeviceProperty prop, vr::TrackedPropertyError *peError = NULL ) { uint32_t unRequiredBufferLen = pHmd->GetStringTrackedDeviceProperty( unDevice, prop, NULL, 0, peError ); if( unRequiredBufferLen == 0 ) return ""; char *pchBuffer = new char[ unRequiredBufferLen ]; unRequiredBufferLen = pHmd->GetStringTrackedDeviceProperty( unDevice, prop, pchBuffer, unRequiredBufferLen, peError ); std::string sResult = pchBuffer; delete [] pchBuffer; return sResult; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CMainApplication::BInit() { // Loading the SteamVR Runtime vr::EVRInitError eError = vr::VRInitError_None; m_pHMD = vr::VR_Init( &eError, vr::VRApplication_Scene ); if ( eError != vr::VRInitError_None ) { m_pHMD = NULL; char buf[1024]; sprintf_s( buf, sizeof( buf ), "Unable to init VR runtime: %s", vr::VR_GetVRInitErrorAsEnglishDescription( eError ) ); b3Warning( "VR_Init Failed %s", buf); return false; } m_pRenderModels = (vr::IVRRenderModels *)vr::VR_GetGenericInterface( vr::IVRRenderModels_Version, &eError ); if( !m_pRenderModels ) { m_pHMD = NULL; vr::VR_Shutdown(); char buf[1024]; sprintf_s( buf, sizeof( buf ), "Unable to get render model interface: %s", vr::VR_GetVRInitErrorAsEnglishDescription( eError ) ); b3Warning( "VR_Init Failed %s", buf); return false; } // int nWindowPosX = 700; // int nWindowPosY = 100; m_nWindowWidth = 1280; m_nWindowHeight = 720; /* //SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY ); 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_app = new SimpleOpenGL3App("SimpleOpenGL3App",m_nWindowWidth,m_nWindowHeight,true); sGuiPtr = new OpenGLGuiHelper(m_app,false); sGuiPtr->setVRMode(true); //sGuiPtr = new DummyGUIHelper; CommonExampleOptions options(sGuiPtr); sExample = StandaloneExampleCreateFunc(options); sExample->initPhysics(); sExample->resetCamera(); #if 0 int cubeIndex = m_app->registerCubeShape(1,1,1); b3Quaternion orn(0,0,0,1); { b3Vector3 color=b3MakeVector3(0.3,0.3,0.6); b3Vector3 pos = b3MakeVector3(0,0,0); b3Vector3 scaling=b3MakeVector3 (1,.1,1); m_app->m_renderer->registerGraphicsInstance(cubeIndex,pos,orn,color,scaling); } { b3Vector3 color=b3MakeVector3(0.3,0.6,0.3); b3Vector3 pos = b3MakeVector3(0,0.3,0); b3Vector3 scaling=b3MakeVector3 (.1,.1,.1); m_app->m_renderer->registerGraphicsInstance(cubeIndex,pos,orn,color,scaling); } #endif m_app->m_renderer->writeTransforms(); /* if (m_pWindow == NULL) { printf( "%s - Window could not be created! SDL Error: %s\n", __FUNCTION__, SDL_GetError() ); return false; } */ /*m_pContext = SDL_GL_CreateContext(m_pWindow); if (m_pContext == NULL) { printf( "%s - OpenGL context could not be created! SDL Error: %s\n", __FUNCTION__, SDL_GetError() ); return false; } glewExperimental = GL_TRUE; GLenum nGlewError = glewInit(); if (nGlewError != GLEW_OK) { printf( "%s - Error initializing GLEW! %s\n", __FUNCTION__, glewGetErrorString( nGlewError ) ); return false; } glGetError(); // to clear the error caused deep in GLEW if ( SDL_GL_SetSwapInterval( m_bVblank ? 1 : 0 ) < 0 ) { printf( "%s - Warning: Unable to set VSync! SDL Error: %s\n", __FUNCTION__, SDL_GetError() ); return false; } */ m_strDriver = "No Driver"; m_strDisplay = "No Display"; m_strDriver = GetTrackedDeviceString( m_pHMD, vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_TrackingSystemName_String ); m_strDisplay = GetTrackedDeviceString( m_pHMD, vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SerialNumber_String ); std::string strWindowTitle = "hellovr_bullet - " + m_strDriver + " " + m_strDisplay; m_app->m_window->setWindowTitle(strWindowTitle.c_str() ); // cube array m_iSceneVolumeWidth = m_iSceneVolumeInit; m_iSceneVolumeHeight = m_iSceneVolumeInit; m_iSceneVolumeDepth = m_iSceneVolumeInit; m_fScale = 0.3f; m_fScaleSpacing = 4.0f; m_fNearClip = 0.1f; m_fFarClip = 3000.0f; m_iTexture = 0; m_uiVertcount = 0; // m_MillisecondsTimer.start(1, this); // m_SecondsTimer.start(1000, this); 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; } return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- /*void APIENTRY DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const char* message, const void* userParam) { b3Printf( "GL Error: %s\n", message ); } */ static void APIENTRY DebugCallback (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam) { b3Printf( "GL Error: %s\n", message ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CMainApplication::BInitGL() { if( m_bDebugOpenGL ) { const GLvoid *userParam=0; glDebugMessageCallback(DebugCallback, userParam); glDebugMessageControl( GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE ); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); } if( !CreateAllShaders() ) return false; SetupTexturemaps(); SetupScene(); SetupCameras(); SetupStereoRenderTargets(); SetupDistortion(); SetupRenderModels(); return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CMainApplication::BInitCompositor() { vr::EVRInitError peError = vr::VRInitError_None; if ( !vr::VRCompositor() ) { printf( "Compositor initialization failed. See log file for details\n" ); return false; } return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CMainApplication::Shutdown() { if( m_pHMD ) { vr::VR_Shutdown(); m_pHMD = NULL; } for( std::vector< CGLRenderModel * >::iterator i = m_vecRenderModels.begin(); i != m_vecRenderModels.end(); i++ ) { delete (*i); } m_vecRenderModels.clear(); if( m_hasContext) { if (m_glSceneVertBuffer) { glDebugMessageControl( GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_FALSE ); glDebugMessageCallback(nullptr, nullptr); glDeleteBuffers(1, &m_glSceneVertBuffer); glDeleteBuffers(1, &m_glIDVertBuffer); glDeleteBuffers(1, &m_glIDIndexBuffer); } if ( m_unSceneProgramID ) { glDeleteProgram( m_unSceneProgramID ); } if ( m_unControllerTransformProgramID ) { glDeleteProgram( m_unControllerTransformProgramID ); } if ( m_unRenderModelProgramID ) { glDeleteProgram( m_unRenderModelProgramID ); } if ( m_unLensProgramID ) { glDeleteProgram( m_unLensProgramID ); } 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 ); if( m_unLensVAO != 0 ) { glDeleteVertexArrays( 1, &m_unLensVAO ); } if( m_unSceneVAO != 0 ) { glDeleteVertexArrays( 1, &m_unSceneVAO ); } if( m_unControllerVAO != 0 ) { glDeleteVertexArrays( 1, &m_unControllerVAO ); } } sExample->exitPhysics(); delete sExample; delete m_app; m_app=0; } void CMainApplication::getControllerTransform(int unDevice, b3Transform& tr) { const Matrix4 & matOrg = m_rmat4DevicePose[unDevice]; tr.setIdentity(); tr.setOrigin(b3MakeVector3(matOrg[12],matOrg[13],matOrg[14]));//pos[1])); b3Matrix3x3 bmat; for (int i=0;i<3;i++) { for (int j=0;j<3;j++) { bmat[i][j] = matOrg[i+4*j]; } } tr.setBasis(bmat); b3Transform y2z; y2z.setIdentity(); y2z.setRotation(b3Quaternion(0,B3_HALF_PI,0)); tr = y2z*tr; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CMainApplication::HandleInput() { bool bRet = false; // Process SteamVR events vr::VREvent_t event; while( m_pHMD->PollNextEvent( &event, sizeof( event ) ) ) { ProcessVREvent( event ); } // Process SteamVR controller state for( vr::TrackedDeviceIndex_t unDevice = 0; unDevice < vr::k_unMaxTrackedDeviceCount; unDevice++ ) { vr::VRControllerState_t state; if( m_pHMD->GetControllerState( unDevice, &state ) ) { //we need to have the 'move' events, so no early out here //if (sPrevStates[unDevice].unPacketNum != state.unPacketNum) if( m_pHMD->GetTrackedDeviceClass( unDevice) == vr::TrackedDeviceClass_Controller ) { sPrevStates[unDevice].unPacketNum = state.unPacketNum; for (int button = 0; button < vr::k_EButton_Max; button++) { uint64_t trigger = vr::ButtonMaskFromId((vr::EVRButtonId)button); bool isTrigger = (state.ulButtonPressed&trigger) != 0; if (isTrigger) { b3Transform tr; getControllerTransform(unDevice, tr); float pos[3] = { tr.getOrigin()[0], tr.getOrigin()[1], tr.getOrigin()[2] }; b3Quaternion born = tr.getRotation(); float orn[4] = { born[0], born[1], born[2], born[3] }; //pressed now, not pressed before -> raise a button down event if ((sPrevStates[unDevice].ulButtonPressed&trigger)==0) { // printf("Device PRESSED: %d, button %d\n", unDevice, button); if (button==2) { glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); ///todo(erwincoumans) can't use reguar debug drawer, because physics/graphics are not in sync //add a special debug drawer that deals with this //gDebugDrawFlags = btIDebugDraw::DBG_DrawContactPoints //btIDebugDraw::DBG_DrawConstraintLimits+ //btIDebugDraw::DBG_DrawConstraints ; } sExample->vrControllerButtonCallback(unDevice, button, 1, pos, orn); } else { // printf("Device MOVED: %d\n", unDevice); sExample->vrControllerMoveCallback(unDevice, pos, orn); } } else { if( m_pHMD->GetTrackedDeviceClass( unDevice) == vr::TrackedDeviceClass_Controller ) { b3Transform tr; getControllerTransform(unDevice, tr); float pos[3] = { tr.getOrigin()[0], tr.getOrigin()[1], tr.getOrigin()[2] }; b3Quaternion born = tr.getRotation(); float orn[4] = { born[0], born[1], born[2], born[3] }; // printf("Device RELEASED: %d, button %d\n", unDevice,button); //not pressed now, but pressed before -> raise a button up event if ((sPrevStates[unDevice].ulButtonPressed&trigger) != 0) { if (button==2) { gDebugDrawFlags = 0; glPolygonMode( GL_FRONT_AND_BACK, GL_FILL); } sExample->vrControllerButtonCallback(unDevice, button, 0, pos, orn); } else { sExample->vrControllerMoveCallback(unDevice, pos, orn); } } } } } m_rbShowTrackedDevice[ unDevice ] = state.ulButtonPressed == 0; } sPrevStates[unDevice] = state; } return bRet; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CMainApplication::RunMainLoop() { bool bQuit = false; while ( !bQuit && !m_app->m_window->requestedExit()) { bQuit = HandleInput(); RenderFrame(); } } //----------------------------------------------------------------------------- // Purpose: Processes a single VR event //----------------------------------------------------------------------------- void CMainApplication::ProcessVREvent( const vr::VREvent_t & event ) { switch( event.eventType ) { case vr::VREvent_TrackedDeviceActivated: { SetupRenderModelForTrackedDevice( event.trackedDeviceIndex ); b3Printf( "Device %u attached. Setting up render model.\n", event.trackedDeviceIndex ); } break; case vr::VREvent_TrackedDeviceDeactivated: { b3Printf( "Device %u detached.\n", event.trackedDeviceIndex ); } break; case vr::VREvent_TrackedDeviceUpdated: { b3Printf( "Device %u updated.\n", event.trackedDeviceIndex ); } break; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CMainApplication::RenderFrame() { // for now as fast as possible if ( m_pHMD ) { DrawControllers(); RenderStereoTargets(); RenderDistortion(); vr::Texture_t leftEyeTexture = {(void*)leftEyeDesc.m_nResolveTextureId, vr::API_OpenGL, vr::ColorSpace_Gamma }; vr::VRCompositor()->Submit(vr::Eye_Left, &leftEyeTexture ); vr::Texture_t rightEyeTexture = {(void*)rightEyeDesc.m_nResolveTextureId, vr::API_OpenGL, vr::ColorSpace_Gamma }; vr::VRCompositor()->Submit(vr::Eye_Right, &rightEyeTexture ); } if ( m_bVblank && m_bGlFinishHack ) { //$ 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 { m_app->swapBuffer(); //SDL_GL_SwapWindow( m_pWindow ); } // 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. glClearColor( 0, 0, 0, 1 ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); } // Flush and wait for swap. if ( m_bVblank ) { glFlush(); glFinish(); } // Spew out the controller and pose count whenever they change. if ( m_iTrackedControllerCount != m_iTrackedControllerCount_Last || m_iValidPoseCount != m_iValidPoseCount_Last ) { m_iValidPoseCount_Last = m_iValidPoseCount; m_iTrackedControllerCount_Last = m_iTrackedControllerCount; b3Printf( "PoseCount:%d(%s) Controllers:%d\n", m_iValidPoseCount, m_strPoseClasses.c_str(), m_iTrackedControllerCount ); } UpdateHMDMatrixPose(); } //----------------------------------------------------------------------------- // Purpose: Compiles a GL shader program and returns the handle. Returns 0 if // the shader couldn't be compiled for some reason. //----------------------------------------------------------------------------- GLuint CMainApplication::CompileGLShader( const char *pchShaderName, const char *pchVertexShader, const char *pchFragmentShader ) { GLuint unProgramID = glCreateProgram(); GLuint nSceneVertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource( nSceneVertexShader, 1, &pchVertexShader, NULL); glCompileShader( nSceneVertexShader ); GLint vShaderCompiled = GL_FALSE; glGetShaderiv( nSceneVertexShader, GL_COMPILE_STATUS, &vShaderCompiled); if ( vShaderCompiled != GL_TRUE) { b3Printf("%s - Unable to compile vertex shader %d!\n", pchShaderName, nSceneVertexShader); glDeleteProgram( unProgramID ); glDeleteShader( nSceneVertexShader ); return 0; } glAttachShader( unProgramID, nSceneVertexShader); glDeleteShader( nSceneVertexShader ); // the program hangs onto this once it's attached GLuint nSceneFragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource( nSceneFragmentShader, 1, &pchFragmentShader, NULL); glCompileShader( nSceneFragmentShader ); GLint fShaderCompiled = GL_FALSE; glGetShaderiv( nSceneFragmentShader, GL_COMPILE_STATUS, &fShaderCompiled); if (fShaderCompiled != GL_TRUE) { b3Printf("%s - Unable to compile fragment shader %d!\n", pchShaderName, nSceneFragmentShader ); glDeleteProgram( unProgramID ); glDeleteShader( nSceneFragmentShader ); return 0; } glAttachShader( unProgramID, nSceneFragmentShader ); glDeleteShader( nSceneFragmentShader ); // the program hangs onto this once it's attached glLinkProgram( unProgramID ); GLint programSuccess = GL_TRUE; glGetProgramiv( unProgramID, GL_LINK_STATUS, &programSuccess); if ( programSuccess != GL_TRUE ) { b3Printf("%s - Error linking program %d!\n", pchShaderName, unProgramID); glDeleteProgram( unProgramID ); return 0; } glUseProgram( unProgramID ); glUseProgram( 0 ); return unProgramID; } //----------------------------------------------------------------------------- // Purpose: Creates all the shaders used by HelloVR SDL //----------------------------------------------------------------------------- bool CMainApplication::CreateAllShaders() { m_unSceneProgramID = CompileGLShader( "Scene", // Vertex Shader "#version 410\n" "uniform mat4 matrix;\n" "layout(location = 0) in vec4 position;\n" "layout(location = 1) in vec2 v2UVcoordsIn;\n" "layout(location = 2) in vec3 v3NormalIn;\n" "out vec2 v2UVcoords;\n" "void main()\n" "{\n" " v2UVcoords = v2UVcoordsIn;\n" " gl_Position = matrix * position;\n" "}\n", // Fragment Shader "#version 410 core\n" "uniform sampler2D mytexture;\n" "in vec2 v2UVcoords;\n" "out vec4 outputColor;\n" "void main()\n" "{\n" " outputColor = texture(mytexture, v2UVcoords);\n" "}\n" ); m_nSceneMatrixLocation = glGetUniformLocation( m_unSceneProgramID, "matrix" ); if( m_nSceneMatrixLocation == -1 ) { b3Printf( "Unable to find matrix uniform in scene shader\n" ); return false; } m_unControllerTransformProgramID = CompileGLShader( "Controller", // vertex shader "#version 410\n" "uniform mat4 matrix;\n" "layout(location = 0) in vec4 position;\n" "layout(location = 1) in vec3 v3ColorIn;\n" "out vec4 v4Color;\n" "void main()\n" "{\n" " v4Color.xyz = v3ColorIn; v4Color.a = 1.0;\n" " gl_Position = matrix * position;\n" "}\n", // fragment shader "#version 410\n" "in vec4 v4Color;\n" "out vec4 outputColor;\n" "void main()\n" "{\n" " outputColor = v4Color;\n" "}\n" ); m_nControllerMatrixLocation = glGetUniformLocation( m_unControllerTransformProgramID, "matrix" ); if( m_nControllerMatrixLocation == -1 ) { b3Printf( "Unable to find matrix uniform in controller shader\n" ); return false; } m_unRenderModelProgramID = CompileGLShader( "render model", // vertex shader "#version 410\n" "uniform mat4 matrix;\n" "layout(location = 0) in vec4 position;\n" "layout(location = 1) in vec3 v3NormalIn;\n" "layout(location = 2) in vec2 v2TexCoordsIn;\n" "out vec2 v2TexCoord;\n" "void main()\n" "{\n" " v2TexCoord = v2TexCoordsIn;\n" " gl_Position = matrix * vec4(position.xyz, 1);\n" "}\n", //fragment shader "#version 410 core\n" "uniform sampler2D diffuse;\n" "in vec2 v2TexCoord;\n" "out vec4 outputColor;\n" "void main()\n" "{\n" " outputColor = texture( diffuse, v2TexCoord);\n" "}\n" ); m_nRenderModelMatrixLocation = glGetUniformLocation( m_unRenderModelProgramID, "matrix" ); if( m_nRenderModelMatrixLocation == -1 ) { b3Printf( "Unable to find matrix uniform in render model shader\n" ); return false; } m_unLensProgramID = CompileGLShader( "Distortion", // vertex shader "#version 410 core\n" "layout(location = 0) in vec4 position;\n" "layout(location = 1) in vec2 v2UVredIn;\n" "layout(location = 2) in vec2 v2UVGreenIn;\n" "layout(location = 3) in vec2 v2UVblueIn;\n" "noperspective out vec2 v2UVred;\n" "noperspective out vec2 v2UVgreen;\n" "noperspective out vec2 v2UVblue;\n" "void main()\n" "{\n" " v2UVred = v2UVredIn;\n" " v2UVgreen = v2UVGreenIn;\n" " v2UVblue = v2UVblueIn;\n" " gl_Position = position;\n" "}\n", // fragment shader "#version 410 core\n" "uniform sampler2D mytexture;\n" "noperspective in vec2 v2UVred;\n" "noperspective in vec2 v2UVgreen;\n" "noperspective in vec2 v2UVblue;\n" "out vec4 outputColor;\n" "void main()\n" "{\n" " float fBoundsCheck = ( (dot( vec2( lessThan( v2UVgreen.xy, vec2(0.05, 0.05)) ), vec2(1.0, 1.0))+dot( vec2( greaterThan( v2UVgreen.xy, vec2( 0.95, 0.95)) ), vec2(1.0, 1.0))) );\n" " if( fBoundsCheck > 1.0 )\n" " { outputColor = vec4( 0, 0, 0, 1.0 ); }\n" " else\n" " {\n" " float red = texture(mytexture, v2UVred).x;\n" " float green = texture(mytexture, v2UVgreen).y;\n" " float blue = texture(mytexture, v2UVblue).z;\n" " outputColor = vec4( red, green, blue, 1.0 ); }\n" "}\n" ); return m_unSceneProgramID != 0 && m_unControllerTransformProgramID != 0 && m_unRenderModelProgramID != 0 && m_unLensProgramID != 0; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CMainApplication::SetupTexturemaps() { std::string sExecutableDirectory = Path_StripFilename( Path_GetExecutablePath() ); std::string strFullPath = Path_MakeAbsolute( "../cube_texture.png", sExecutableDirectory ); std::vector imageRGBA; unsigned nImageWidth, nImageHeight; unsigned nError = lodepng::decode( imageRGBA, nImageWidth, nImageHeight, strFullPath.c_str() ); if ( nError != 0 ) return false; glGenTextures(1, &m_iTexture ); glBindTexture( GL_TEXTURE_2D, m_iTexture ); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, nImageWidth, nImageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, &imageRGBA[0] ); glGenerateMipmap(GL_TEXTURE_2D); 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_MIPMAP_LINEAR ); GLfloat fLargest; glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest); glBindTexture( GL_TEXTURE_2D, 0 ); return ( m_iTexture != 0 ); } //----------------------------------------------------------------------------- // Purpose: create a sea of cubes //----------------------------------------------------------------------------- void CMainApplication::SetupScene() { if ( !m_pHMD ) return; std::vector vertdataarray; Matrix4 matScale; matScale.scale( m_fScale, m_fScale, m_fScale ); Matrix4 matTransform; matTransform.translate( -( (float)m_iSceneVolumeWidth * m_fScaleSpacing ) / 2.f, -( (float)m_iSceneVolumeHeight * m_fScaleSpacing ) / 2.f, -( (float)m_iSceneVolumeDepth * m_fScaleSpacing ) / 2.f); Matrix4 mat = matScale * matTransform; for( int z = 0; z< m_iSceneVolumeDepth; z++ ) { for( int y = 0; y< m_iSceneVolumeHeight; y++ ) { for( int x = 0; x< m_iSceneVolumeWidth; x++ ) { AddCubeToScene( mat, vertdataarray ); mat = mat * Matrix4().translate( m_fScaleSpacing, 0, 0 ); } mat = mat * Matrix4().translate( -((float)m_iSceneVolumeWidth) * m_fScaleSpacing, m_fScaleSpacing, 0 ); } mat = mat * Matrix4().translate( 0, -((float)m_iSceneVolumeHeight) * m_fScaleSpacing, m_fScaleSpacing ); } m_uiVertcount = vertdataarray.size()/5; glGenVertexArrays( 1, &m_unSceneVAO ); glBindVertexArray( m_unSceneVAO ); glGenBuffers( 1, &m_glSceneVertBuffer ); glBindBuffer( GL_ARRAY_BUFFER, m_glSceneVertBuffer ); glBufferData( GL_ARRAY_BUFFER, sizeof(float) * vertdataarray.size(), &vertdataarray[0], GL_STATIC_DRAW); glBindBuffer( GL_ARRAY_BUFFER, m_glSceneVertBuffer ); GLsizei stride = sizeof(VertexDataScene); uintptr_t offset = 0; glEnableVertexAttribArray( 0 ); glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, stride , (const void *)offset); offset += sizeof(Vector3); glEnableVertexAttribArray( 1 ); glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, stride, (const void *)offset); glBindVertexArray( 0 ); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); m_hasContext = true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CMainApplication::AddCubeVertex( float fl0, float fl1, float fl2, float fl3, float fl4, std::vector &vertdata ) { vertdata.push_back( fl0 ); vertdata.push_back( fl1 ); vertdata.push_back( fl2 ); vertdata.push_back( fl3 ); vertdata.push_back( fl4 ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CMainApplication::AddCubeToScene( Matrix4 mat, std::vector &vertdata ) { // Matrix4 mat( outermat.data() ); Vector4 A = mat * Vector4( 0, 0, 0, 1 ); Vector4 B = mat * Vector4( 1, 0, 0, 1 ); Vector4 C = mat * Vector4( 1, 1, 0, 1 ); Vector4 D = mat * Vector4( 0, 1, 0, 1 ); Vector4 E = mat * Vector4( 0, 0, 1, 1 ); Vector4 F = mat * Vector4( 1, 0, 1, 1 ); Vector4 G = mat * Vector4( 1, 1, 1, 1 ); Vector4 H = mat * Vector4( 0, 1, 1, 1 ); // triangles instead of quads AddCubeVertex( E.x, E.y, E.z, 0, 1, vertdata ); //Front AddCubeVertex( F.x, F.y, F.z, 1, 1, vertdata ); AddCubeVertex( G.x, G.y, G.z, 1, 0, vertdata ); AddCubeVertex( G.x, G.y, G.z, 1, 0, vertdata ); AddCubeVertex( H.x, H.y, H.z, 0, 0, vertdata ); AddCubeVertex( E.x, E.y, E.z, 0, 1, vertdata ); AddCubeVertex( B.x, B.y, B.z, 0, 1, vertdata ); //Back AddCubeVertex( A.x, A.y, A.z, 1, 1, vertdata ); AddCubeVertex( D.x, D.y, D.z, 1, 0, vertdata ); AddCubeVertex( D.x, D.y, D.z, 1, 0, vertdata ); AddCubeVertex( C.x, C.y, C.z, 0, 0, vertdata ); AddCubeVertex( B.x, B.y, B.z, 0, 1, vertdata ); AddCubeVertex( H.x, H.y, H.z, 0, 1, vertdata ); //Top AddCubeVertex( G.x, G.y, G.z, 1, 1, vertdata ); AddCubeVertex( C.x, C.y, C.z, 1, 0, vertdata ); AddCubeVertex( C.x, C.y, C.z, 1, 0, vertdata ); AddCubeVertex( D.x, D.y, D.z, 0, 0, vertdata ); AddCubeVertex( H.x, H.y, H.z, 0, 1, vertdata ); AddCubeVertex( A.x, A.y, A.z, 0, 1, vertdata ); //Bottom AddCubeVertex( B.x, B.y, B.z, 1, 1, vertdata ); AddCubeVertex( F.x, F.y, F.z, 1, 0, vertdata ); AddCubeVertex( F.x, F.y, F.z, 1, 0, vertdata ); AddCubeVertex( E.x, E.y, E.z, 0, 0, vertdata ); AddCubeVertex( A.x, A.y, A.z, 0, 1, vertdata ); AddCubeVertex( A.x, A.y, A.z, 0, 1, vertdata ); //Left AddCubeVertex( E.x, E.y, E.z, 1, 1, vertdata ); AddCubeVertex( H.x, H.y, H.z, 1, 0, vertdata ); AddCubeVertex( H.x, H.y, H.z, 1, 0, vertdata ); AddCubeVertex( D.x, D.y, D.z, 0, 0, vertdata ); AddCubeVertex( A.x, A.y, A.z, 0, 1, vertdata ); AddCubeVertex( F.x, F.y, F.z, 0, 1, vertdata ); //Right AddCubeVertex( B.x, B.y, B.z, 1, 1, vertdata ); AddCubeVertex( C.x, C.y, C.z, 1, 0, vertdata ); AddCubeVertex( C.x, C.y, C.z, 1, 0, vertdata ); AddCubeVertex( G.x, G.y, G.z, 0, 0, vertdata ); AddCubeVertex( F.x, F.y, F.z, 0, 1, vertdata ); } //----------------------------------------------------------------------------- // Purpose: Draw all of the controllers as X/Y/Z lines //----------------------------------------------------------------------------- void CMainApplication::DrawControllers() { // don't draw controllers if somebody else has input focus if( m_pHMD->IsInputFocusCapturedByAnotherProcess() ) return; std::vector vertdataarray; m_uiControllerVertcount = 0; m_iTrackedControllerCount = 0; for ( vr::TrackedDeviceIndex_t unTrackedDevice = vr::k_unTrackedDeviceIndex_Hmd + 1; unTrackedDevice < vr::k_unMaxTrackedDeviceCount; ++unTrackedDevice ) { if ( !m_pHMD->IsTrackedDeviceConnected( unTrackedDevice ) ) continue; if( m_pHMD->GetTrackedDeviceClass( unTrackedDevice ) != vr::TrackedDeviceClass_Controller ) continue; m_iTrackedControllerCount += 1; if( !m_rTrackedDevicePose[ unTrackedDevice ].bPoseIsValid ) continue; const Matrix4 & mat = m_rmat4DevicePose[unTrackedDevice]; Vector4 center = mat * Vector4( 0, 0, 0, 1 ); for ( int i = 0; i < 3; ++i ) { Vector3 color( 0, 0, 0 ); Vector4 point( 0, 0, 0, 1 ); point[i] += 0.05f; // offset in X, Y, Z color[i] = 1.0; // R, G, B point = mat * point; vertdataarray.push_back( center.x ); vertdataarray.push_back( center.y ); vertdataarray.push_back( center.z ); vertdataarray.push_back( color.x ); vertdataarray.push_back( color.y ); vertdataarray.push_back( color.z ); vertdataarray.push_back( point.x ); vertdataarray.push_back( point.y ); vertdataarray.push_back( point.z ); vertdataarray.push_back( color.x ); vertdataarray.push_back( color.y ); vertdataarray.push_back( color.z ); m_uiControllerVertcount += 2; } Vector4 start = mat * Vector4( 0, 0, -0.02f, 1 ); Vector4 end = mat * Vector4( 0, 0, -39.f, 1 ); Vector3 color( .92f, .92f, .71f ); vertdataarray.push_back( start.x );vertdataarray.push_back( start.y );vertdataarray.push_back( start.z ); vertdataarray.push_back( color.x );vertdataarray.push_back( color.y );vertdataarray.push_back( color.z ); vertdataarray.push_back( end.x );vertdataarray.push_back( end.y );vertdataarray.push_back( end.z ); vertdataarray.push_back( color.x );vertdataarray.push_back( color.y );vertdataarray.push_back( color.z ); m_uiControllerVertcount += 2; } // Setup the VAO the first time through. if ( m_unControllerVAO == 0 ) { glGenVertexArrays( 1, &m_unControllerVAO ); glBindVertexArray( m_unControllerVAO ); glGenBuffers( 1, &m_glControllerVertBuffer ); glBindBuffer( GL_ARRAY_BUFFER, m_glControllerVertBuffer ); GLuint stride = 2 * 3 * sizeof( float ); GLuint offset = 0; glEnableVertexAttribArray( 0 ); glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, stride, (const void *)offset); offset += sizeof( Vector3 ); glEnableVertexAttribArray( 1 ); glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, stride, (const void *)offset); glBindVertexArray( 0 ); } glBindBuffer( GL_ARRAY_BUFFER, m_glControllerVertBuffer ); // set vertex data if we have some if( vertdataarray.size() > 0 ) { //$ TODO: Use glBufferSubData for this... glBufferData( GL_ARRAY_BUFFER, sizeof(float) * vertdataarray.size(), &vertdataarray[0], GL_STREAM_DRAW ); } } //----------------------------------------------------------------------------- // 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: //----------------------------------------------------------------------------- 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_pHMD ) return false; m_pHMD->GetRecommendedRenderTargetSize( &m_nRenderWidth, &m_nRenderHeight ); CreateFrameBuffer( m_nRenderWidth, m_nRenderHeight, leftEyeDesc ); CreateFrameBuffer( m_nRenderWidth, m_nRenderHeight, rightEyeDesc ); return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CMainApplication::SetupDistortion() { if ( !m_pHMD ) return; GLushort m_iLensGridSegmentCountH = 43; GLushort m_iLensGridSegmentCountV = 43; float w = (float)( 1.0/float(m_iLensGridSegmentCountH-1)); float h = (float)( 1.0/float(m_iLensGridSegmentCountV-1)); float u, v = 0; std::vector vVerts(0); VertexDataLens vert; //left eye distortion verts float Xoffset = -1; for( int y=0; yComputeDistortion(vr::Eye_Left, u, v); vert.texCoordRed = Vector2(dc0.rfRed[0], 1 - dc0.rfRed[1]); vert.texCoordGreen = Vector2(dc0.rfGreen[0], 1 - dc0.rfGreen[1]); vert.texCoordBlue = Vector2(dc0.rfBlue[0], 1 - dc0.rfBlue[1]); vVerts.push_back( vert ); } } //right eye distortion verts Xoffset = 0; for( int y=0; yComputeDistortion( vr::Eye_Right, u, v ); vert.texCoordRed = Vector2(dc0.rfRed[0], 1 - dc0.rfRed[1]); vert.texCoordGreen = Vector2(dc0.rfGreen[0], 1 - dc0.rfGreen[1]); vert.texCoordBlue = Vector2(dc0.rfBlue[0], 1 - dc0.rfBlue[1]); vVerts.push_back( vert ); } } std::vector vIndices; GLushort a,b,c,d; GLushort offset = 0; for( GLushort y=0; ystepSimulation(1./60.); glClearColor( 0.15f, 0.15f, 0.18f, 1.0f ); // nice background color, but not black glEnable( GL_MULTISAMPLE ); m_app->m_instancingRenderer->init(); Matrix4 rotYtoZ = rotYtoZ.identity(); //some Bullet apps (especially robotics related) require Z as up-axis) if (m_app->getUpAxis()==2) { rotYtoZ.rotateX(-90); } // Left Eye { Matrix4 viewMatLeft = m_mat4eyePosLeft * m_mat4HMDPose * rotYtoZ; Matrix4 viewMatCenter = m_mat4HMDPose * rotYtoZ; //0,1,2,3 //4,5,6,7, //8,9,10,11 //12,13,14,15 //m_mat4eyePosLeft.get()[10] //m_app->m_instancingRenderer->getActiveCamera()->setCameraTargetPosition( // m_mat4eyePosLeft.get()[3], // m_mat4eyePosLeft.get()[7], // m_mat4eyePosLeft.get()[11]); Matrix4 m; m = viewMatCenter; const float* mat = m.invertAffine().get(); /*printf("camera:\n,%f,%f,%f,%f\n%f,%f,%f,%f\n%f,%f,%f,%f\n%f,%f,%f,%f", mat[0],mat[1],mat[2],mat[3], mat[4],mat[5],mat[6],mat[7], mat[8],mat[9],mat[10],mat[11], mat[12],mat[13],mat[14],mat[15]); */ float dist=1; m_app->m_instancingRenderer->getActiveCamera()->setCameraTargetPosition( mat[12]-dist*mat[8], mat[13]-dist*mat[9], mat[14]-dist*mat[10] ); m_app->m_instancingRenderer->getActiveCamera()->setCameraUpVector(mat[0],mat[1],mat[2]); m_app->m_instancingRenderer->getActiveCamera()->setVRCamera(viewMatLeft.get(),m_mat4ProjectionLeft.get()); m_app->m_instancingRenderer->updateCamera(m_app->getUpAxis()); } glBindFramebuffer( GL_FRAMEBUFFER, leftEyeDesc.m_nRenderFramebufferId ); glViewport(0, 0, m_nRenderWidth, m_nRenderHeight ); m_app->m_window->startRendering(); RenderScene( vr::Eye_Left ); m_app->m_instancingRenderer->setRenderFrameBuffer((unsigned int)leftEyeDesc.m_nRenderFramebufferId); if (gDebugDrawFlags) { sExample->physicsDebugDraw(gDebugDrawFlags); } { sExample->renderScene(); } //m_app->m_instancingRenderer->renderScene(); DrawGridData gridUp; gridUp.upAxis = m_app->getUpAxis(); // m_app->drawGrid(gridUp); 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 { Matrix4 viewMatRight = m_mat4eyePosRight * m_mat4HMDPose * rotYtoZ; m_app->m_instancingRenderer->getActiveCamera()->setVRCamera(viewMatRight.get(),m_mat4ProjectionRight.get()); m_app->m_instancingRenderer->updateCamera(m_app->getUpAxis()); } glBindFramebuffer( GL_FRAMEBUFFER, rightEyeDesc.m_nRenderFramebufferId ); glViewport(0, 0, m_nRenderWidth, m_nRenderHeight ); m_app->m_window->startRendering(); RenderScene( vr::Eye_Right ); m_app->m_instancingRenderer->setRenderFrameBuffer((unsigned int)rightEyeDesc.m_nRenderFramebufferId); //m_app->m_renderer->renderScene(); if (gDebugDrawFlags) { sExample->physicsDebugDraw(gDebugDrawFlags); } { sExample->renderScene(); } //m_app->drawGrid(gridUp); 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: //----------------------------------------------------------------------------- void CMainApplication::RenderScene( vr::Hmd_Eye nEye ) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); if( m_bShowCubes ) { glUseProgram( m_unSceneProgramID ); glUniformMatrix4fv( m_nSceneMatrixLocation, 1, GL_FALSE, GetCurrentViewProjectionMatrix( nEye ).get() ); glBindVertexArray( m_unSceneVAO ); glBindTexture( GL_TEXTURE_2D, m_iTexture ); glDrawArrays( GL_TRIANGLES, 0, m_uiVertcount ); glBindVertexArray( 0 ); } bool bIsInputCapturedByAnotherProcess = m_pHMD->IsInputFocusCapturedByAnotherProcess(); if( !bIsInputCapturedByAnotherProcess ) { // draw the controller axis lines glUseProgram( m_unControllerTransformProgramID ); glUniformMatrix4fv( m_nControllerMatrixLocation, 1, GL_FALSE, GetCurrentViewProjectionMatrix( nEye ).get() ); glBindVertexArray( m_unControllerVAO ); glDrawArrays( GL_LINES, 0, m_uiControllerVertcount ); glBindVertexArray( 0 ); } // ----- Render Model rendering ----- glUseProgram( m_unRenderModelProgramID ); for( uint32_t unTrackedDevice = 0; unTrackedDevice < vr::k_unMaxTrackedDeviceCount; unTrackedDevice++ ) { if( !m_rTrackedDeviceToRenderModel[ unTrackedDevice ] || !m_rbShowTrackedDevice[ unTrackedDevice ] ) continue; const vr::TrackedDevicePose_t & pose = m_rTrackedDevicePose[ unTrackedDevice ]; if( !pose.bPoseIsValid ) continue; if( bIsInputCapturedByAnotherProcess && m_pHMD->GetTrackedDeviceClass( unTrackedDevice ) == vr::TrackedDeviceClass_Controller ) continue; const Matrix4 & matDeviceToTracking = m_rmat4DevicePose[ unTrackedDevice ]; Matrix4 matMVP = GetCurrentViewProjectionMatrix( nEye ) * matDeviceToTracking; glUniformMatrix4fv( m_nRenderModelMatrixLocation, 1, GL_FALSE, matMVP.get() ); m_rTrackedDeviceToRenderModel[ unTrackedDevice ]->Draw(); } glUseProgram( 0 ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CMainApplication::RenderDistortion() { glDisable(GL_DEPTH_TEST); glViewport( 0, 0, m_nWindowWidth, m_nWindowHeight ); glBindVertexArray( m_unLensVAO ); glUseProgram( m_unLensProgramID ); //render left lens (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_MIPMAP_LINEAR ); glDrawElements( GL_TRIANGLES, m_uiIndexSize/2, GL_UNSIGNED_SHORT, 0 ); //render right lens (second half of index array ) glBindTexture(GL_TEXTURE_2D, rightEyeDesc.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_MIPMAP_LINEAR ); glDrawElements( GL_TRIANGLES, m_uiIndexSize/2, GL_UNSIGNED_SHORT, (const void *)(m_uiIndexSize) ); glBindVertexArray( 0 ); glUseProgram( 0 ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- Matrix4 CMainApplication::GetHMDMatrixProjectionEye( vr::Hmd_Eye nEye ) { if ( !m_pHMD ) return Matrix4(); vr::HmdMatrix44_t mat = m_pHMD->GetProjectionMatrix( nEye, m_fNearClip, m_fFarClip, vr::API_OpenGL); 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: //----------------------------------------------------------------------------- Matrix4 CMainApplication::GetHMDMatrixPoseEye( vr::Hmd_Eye nEye ) { if ( !m_pHMD ) return Matrix4(); vr::HmdMatrix34_t matEyeRight = m_pHMD->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: //----------------------------------------------------------------------------- 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_pHMD ) return; vr::VRCompositor()->WaitGetPoses(m_rTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, NULL, 0 ); m_iValidPoseCount = 0; m_strPoseClasses = ""; for ( int nDevice = 0; nDevice < vr::k_unMaxTrackedDeviceCount; ++nDevice ) { if ( m_rTrackedDevicePose[nDevice].bPoseIsValid ) { m_iValidPoseCount++; m_rmat4DevicePose[nDevice] = ConvertSteamVRMatrixToMatrix4( m_rTrackedDevicePose[nDevice].mDeviceToAbsoluteTracking ); if (m_rDevClassChar[nDevice]==0) { switch (m_pHMD->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_Other: m_rDevClassChar[nDevice] = 'O'; 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].invert(); } } //----------------------------------------------------------------------------- // Purpose: Finds a render model we've already loaded or loads a new one //----------------------------------------------------------------------------- CGLRenderModel *CMainApplication::FindOrLoadRenderModel( const char *pchRenderModelName ) { CGLRenderModel *pRenderModel = NULL; for( std::vector< CGLRenderModel * >::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; ThreadSleep( 1 ); } if ( error != vr::VRRenderModelError_None ) { b3Printf( "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; ThreadSleep( 1 ); } if ( error != vr::VRRenderModelError_None ) { b3Printf( "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 CGLRenderModel( pchRenderModelName ); if ( !pRenderModel->BInit( *pModel, *pTexture ) ) { b3Printf( "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; } //----------------------------------------------------------------------------- // Purpose: Create/destroy GL a Render Model for a single tracked device //----------------------------------------------------------------------------- void CMainApplication::SetupRenderModelForTrackedDevice( vr::TrackedDeviceIndex_t unTrackedDeviceIndex ) { if( unTrackedDeviceIndex >= vr::k_unMaxTrackedDeviceCount ) return; // try to find a model we've already set up std::string sRenderModelName = GetTrackedDeviceString( m_pHMD, unTrackedDeviceIndex, vr::Prop_RenderModelName_String ); CGLRenderModel *pRenderModel = FindOrLoadRenderModel( sRenderModelName.c_str() ); if( !pRenderModel ) { std::string sTrackingSystemName = GetTrackedDeviceString( m_pHMD, unTrackedDeviceIndex, vr::Prop_TrackingSystemName_String ); b3Printf( "Unable to load render model for tracked device %d (%s.%s)", unTrackedDeviceIndex, sTrackingSystemName.c_str(), sRenderModelName.c_str() ); } else { m_rTrackedDeviceToRenderModel[ unTrackedDeviceIndex ] = pRenderModel; m_rbShowTrackedDevice[ unTrackedDeviceIndex ] = true; } } //----------------------------------------------------------------------------- // Purpose: Create/destroy GL Render Models //----------------------------------------------------------------------------- void CMainApplication::SetupRenderModels() { memset( m_rTrackedDeviceToRenderModel, 0, sizeof( m_rTrackedDeviceToRenderModel ) ); if( !m_pHMD ) return; for( uint32_t unTrackedDevice = vr::k_unTrackedDeviceIndex_Hmd + 1; unTrackedDevice < vr::k_unMaxTrackedDeviceCount; unTrackedDevice++ ) { if( !m_pHMD->IsTrackedDeviceConnected( unTrackedDevice ) ) continue; SetupRenderModelForTrackedDevice( unTrackedDevice ); } } //----------------------------------------------------------------------------- // Purpose: Converts a SteamVR matrix to our local matrix class //----------------------------------------------------------------------------- Matrix4 CMainApplication::ConvertSteamVRMatrixToMatrix4( const vr::HmdMatrix34_t &matPose ) { Matrix4 matrixObj( matPose.m[0][0], matPose.m[1][0], matPose.m[2][0], 0.0, matPose.m[0][1], matPose.m[1][1], matPose.m[2][1], 0.0, matPose.m[0][2], matPose.m[1][2], matPose.m[2][2], 0.0, matPose.m[0][3], matPose.m[1][3], matPose.m[2][3], 1.0f ); return matrixObj; } //----------------------------------------------------------------------------- // Purpose: Create/destroy GL Render Models //----------------------------------------------------------------------------- CGLRenderModel::CGLRenderModel( const std::string & sRenderModelName ) : m_sModelName( sRenderModelName ) { m_glIndexBuffer = 0; m_glVertArray = 0; m_glVertBuffer = 0; m_glTexture = 0; } CGLRenderModel::~CGLRenderModel() { Cleanup(); } //----------------------------------------------------------------------------- // Purpose: Allocates and populates the GL resources for a render model //----------------------------------------------------------------------------- bool CGLRenderModel::BInit( const vr::RenderModel_t & vrModel, const vr::RenderModel_TextureMap_t & vrDiffuseTexture ) { // create and bind a VAO to hold state for this model glGenVertexArrays( 1, &m_glVertArray ); glBindVertexArray( m_glVertArray ); // Populate a vertex buffer glGenBuffers( 1, &m_glVertBuffer ); glBindBuffer( GL_ARRAY_BUFFER, m_glVertBuffer ); glBufferData( GL_ARRAY_BUFFER, sizeof( vr::RenderModel_Vertex_t ) * vrModel.unVertexCount, vrModel.rVertexData, GL_STATIC_DRAW ); // Identify the components in the vertex buffer glEnableVertexAttribArray( 0 ); glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, sizeof( vr::RenderModel_Vertex_t ), (void *)offsetof( vr::RenderModel_Vertex_t, vPosition ) ); glEnableVertexAttribArray( 1 ); glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, sizeof( vr::RenderModel_Vertex_t ), (void *)offsetof( vr::RenderModel_Vertex_t, vNormal ) ); glEnableVertexAttribArray( 2 ); glVertexAttribPointer( 2, 2, GL_FLOAT, GL_FALSE, sizeof( vr::RenderModel_Vertex_t ), (void *)offsetof( vr::RenderModel_Vertex_t, rfTextureCoord ) ); // Create and populate the index buffer glGenBuffers( 1, &m_glIndexBuffer ); glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_glIndexBuffer ); glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof( uint16_t ) * vrModel.unTriangleCount * 3, vrModel.rIndexData, GL_STATIC_DRAW ); glBindVertexArray( 0 ); // create and populate the texture glGenTextures(1, &m_glTexture ); glBindTexture( GL_TEXTURE_2D, m_glTexture ); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, vrDiffuseTexture.unWidth, vrDiffuseTexture.unHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, vrDiffuseTexture.rubTextureMapData ); // If this renders black ask McJohn what's wrong. glGenerateMipmap(GL_TEXTURE_2D); 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_MIPMAP_LINEAR ); GLfloat fLargest; glGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest ); glBindTexture( GL_TEXTURE_2D, 0 ); m_unVertexCount = vrModel.unTriangleCount * 3; return true; } //----------------------------------------------------------------------------- // Purpose: Frees the GL resources for a render model //----------------------------------------------------------------------------- void CGLRenderModel::Cleanup() { if( m_glVertBuffer ) { glDeleteBuffers(1, &m_glIndexBuffer); glDeleteVertexArrays( 1, &m_glVertArray ); glDeleteBuffers(1, &m_glVertBuffer); m_glIndexBuffer = 0; m_glVertArray = 0; m_glVertBuffer = 0; } } //----------------------------------------------------------------------------- // Purpose: Draws the render model //----------------------------------------------------------------------------- void CGLRenderModel::Draw() { glBindVertexArray( m_glVertArray ); glActiveTexture( GL_TEXTURE0 ); glBindTexture( GL_TEXTURE_2D, m_glTexture ); glDrawElements( GL_TRIANGLES, m_unVertexCount, GL_UNSIGNED_SHORT, 0 ); glBindVertexArray( 0 ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int main(int argc, char *argv[]) { CMainApplication *pMainApplication = new CMainApplication( argc, argv ); if (!pMainApplication->BInit()) { pMainApplication->Shutdown(); return 1; } pMainApplication->RunMainLoop(); pMainApplication->Shutdown(); return 0; } #endif //BT_ENABLE_VR