#ifdef BT_ENABLE_VR #ifdef BT_USE_CUSTOM_PROFILER #endif //========= Copyright Valve Corporation ============// #include "../OpenGLWindow/SimpleOpenGL3App.h" #include "../OpenGLWindow/OpenGLInclude.h" #include "Bullet3Common/b3Quaternion.h" #include "Bullet3Common/b3Transform.h" #include "Bullet3Common/b3CommandLineArgs.h" #include "../Utils/b3Clock.h" #include "../Utils/ChromeTraceUtil.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" #ifdef __APPLE__ #define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED #import #endif //__APPLE__ #include "LinearMath/btIDebugDraw.h" int gSharedMemoryKey = -1; static int gDebugDrawFlags = 0; static bool gDisplayDistortion = false; static bool gDisableDesktopGL = false; static int maxNumObjectCapacity = 128 * 1024; static int maxShapeCapacityInBytes = 128 * 1024 * 1024; #include #include #include #include #include "strtools.h" #include "compat.h" #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 #ifdef __linux__ #define APIENTRY #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); SimpleOpenGL3App *getApp() { return m_app; } 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; b3Clock m_clock; 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 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; } b3KeyboardCallback prevKeyboardCallback = 0; void MyKeyboardCallback(int key, int state) { if (key == 'p') { if (state) { b3ChromeUtilsStartTimings(); } else { b3ChromeUtilsStopTimingsAndWriteJsonFile("timings"); } } if (sExample) { sExample->keyboardCallback(key, state); } if (prevKeyboardCallback) prevKeyboardCallback(key, state); } #include "../SharedMemory/SharedMemoryPublic.h" extern bool useShadowMap; static bool gEnableVRRenderControllers = true; static bool gEnableVRRendering = true; static int gUpAxis = 2; void VRPhysicsServerVisualizerFlagCallback(int flag, bool enable) { if (flag == COV_ENABLE_Y_AXIS_UP) { //either Y = up or Z gUpAxis = enable ? 1 : 2; } if (flag == COV_ENABLE_SHADOWS) { useShadowMap = enable; } if (flag == COV_ENABLE_GUI) { //there is no regular GUI here, but disable the } if (flag == COV_ENABLE_VR_RENDER_CONTROLLERS) { gEnableVRRenderControllers = enable; } if (flag == COV_ENABLE_RENDERING) { gEnableVRRendering = enable; } if (flag == COV_ENABLE_WIREFRAME) { if (enable) { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); //gDebugDrawFlags |= btIDebugDraw::DBG_DrawWireframe; } else { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); //gDebugDrawFlags &= ~btIDebugDraw::DBG_DrawWireframe; } } } //----------------------------------------------------------------------------- // 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, maxNumObjectCapacity, maxShapeCapacityInBytes); sGuiPtr = new OpenGLGuiHelper(m_app, false); sGuiPtr->setVisualizerFlagCallback(VRPhysicsServerVisualizerFlagCallback); sGuiPtr->setVRMode(true); //sGuiPtr = new DummyGUIHelper; prevKeyboardCallback = m_app->m_window->getKeyboardCallback(); m_app->m_window->setKeyboardCallback(MyKeyboardCallback); 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; } 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::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); } } if (sExample) { 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, sizeof(vr::VRControllerState_t))) { 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]}; //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_HMD) { Matrix4 rotYtoZ = rotYtoZ.identity(); //some Bullet apps (especially robotics related) require Z as up-axis) if (m_app->getUpAxis() == 2) { rotYtoZ.rotateX(-90); } Matrix4 viewMatCenter = m_mat4HMDPose * rotYtoZ; const float *mat = viewMatCenter.invertAffine().get(); pos[0] = mat[12]; pos[1] = mat[13]; pos[2] = mat[14]; b3Matrix3x3 bmat; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { bmat[i][j] = mat[i + 4 * j]; } } b3Quaternion orn2; bmat.getRotation(orn2); orn[0] = orn2[0]; orn[1] = orn2[1]; orn[2] = orn2[2]; orn[3] = orn2[3]; sExample->vrHMDMoveCallback(unDevice, pos, orn); } if (m_pHMD->GetTrackedDeviceClass(unDevice) == vr::TrackedDeviceClass_GenericTracker) { sExample->vrGenericTrackerMoveCallback(unDevice, pos, orn); } 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); btAssert(vr::k_unControllerStateAxisCount >= 5); float allAxis[10]; //store x,y times 5 controllers int index = 0; for (int i = 0; i < 5; i++) { allAxis[index++] = state.rAxis[i].x; allAxis[index++] = state.rAxis[i].y; } bool isTrigger = (state.ulButtonPressed & trigger) != 0; if (isTrigger) { //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); sExample->vrControllerButtonCallback(unDevice, button, 1, pos, orn); } else { // printf("Device MOVED: %d\n", unDevice); sExample->vrControllerMoveCallback(unDevice, pos, orn, state.rAxis[1].x, allAxis); } } else { if (m_pHMD->GetTrackedDeviceClass(unDevice) == vr::TrackedDeviceClass_Controller) { // 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; } sExample->vrControllerButtonCallback(unDevice, button, 0, pos, orn); } else { sExample->vrControllerMoveCallback(unDevice, pos, orn, state.rAxis[1].x, allAxis); } } } } } // 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()) { this->m_app->setUpAxis(gUpAxis); b3ChromeUtilsEnableProfiling(); if (gEnableVRRendering) { B3_PROFILE("main"); bQuit = HandleInput(); RenderFrame(); } else { b3Clock::usleep(0); sExample->updateGraphics(); } } } //----------------------------------------------------------------------------- // 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) { { B3_PROFILE("DrawControllers"); DrawControllers(); } RenderStereoTargets(); if (!gDisableDesktopGL) { if (gDisplayDistortion) { B3_PROFILE("RenderDistortion"); RenderDistortion(); } else { //todo: should use framebuffer_multisample_blit_scaled //See https://twitter.com/id_aa_carmack/status/268488838425481217?lang=en glBindFramebuffer(GL_FRAMEBUFFER, 0); glDisable(GL_MULTISAMPLE); glBindFramebuffer(GL_READ_FRAMEBUFFER, rightEyeDesc.m_nRenderFramebufferId); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); 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); } } vr::Texture_t leftEyeTexture = {(void *)leftEyeDesc.m_nResolveTextureId, vr::TextureType_OpenGL, vr::ColorSpace_Gamma}; vr::VRCompositor()->Submit(vr::Eye_Left, &leftEyeTexture); vr::Texture_t rightEyeTexture = {(void *)rightEyeDesc.m_nResolveTextureId, vr::TextureType_OpenGL, vr::ColorSpace_Gamma}; vr::VRCompositor()->Submit(vr::Eye_Right, &rightEyeTexture); } if (m_bVblank && m_bGlFinishHack) { B3_PROFILE("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 { B3_PROFILE("m_app->swapBuffer"); if (!gDisableDesktopGL) { m_app->swapBuffer(); } //SDL_GL_SwapWindow( m_pWindow ); } // Clear { B3_PROFILE("glClearColor"); // 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) { B3_PROFILE("glFlushglFinish"); glFlush(); glFinish(); } // Spew out the controller and pose count whenever they change. if (m_iTrackedControllerCount != m_iTrackedControllerCount_Last || m_iValidPoseCount != m_iValidPoseCount_Last) { B3_PROFILE("debug pose"); 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); } { B3_PROFILE("UpdateHMDMatrixPose"); 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, &fLargest); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, 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 //----------------------------------------------------------------------------- extern int gGraspingController; 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; y < m_iLensGridSegmentCountV; y++) { for (int x = 0; x < m_iLensGridSegmentCountH; x++) { u = x * w; v = 1 - y * h; vert.position = Vector2(Xoffset + u, -1 + 2 * y * h); vr::DistortionCoordinates_t dc0; bool result = m_pHMD->ComputeDistortion(vr::Eye_Left, u, v, &dc0); btAssert(result); 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; y < m_iLensGridSegmentCountV; y++) { for (int x = 0; x < m_iLensGridSegmentCountH; x++) { u = x * w; v = 1 - y * h; vert.position = Vector2(Xoffset + u, -1 + 2 * y * h); vr::DistortionCoordinates_t dc0; bool result = m_pHMD->ComputeDistortion(vr::Eye_Right, u, v, &dc0); btAssert(result); 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; y < m_iLensGridSegmentCountV - 1; y++) { for (GLushort x = 0; x < m_iLensGridSegmentCountH - 1; x++) { a = m_iLensGridSegmentCountH * y + x + offset; b = m_iLensGridSegmentCountH * y + x + 1 + offset; c = (y + 1) * m_iLensGridSegmentCountH + x + 1 + offset; d = (y + 1) * m_iLensGridSegmentCountH + x + offset; vIndices.push_back(a); vIndices.push_back(b); vIndices.push_back(c); vIndices.push_back(a); vIndices.push_back(c); vIndices.push_back(d); } } offset = (m_iLensGridSegmentCountH) * (m_iLensGridSegmentCountV); for (GLushort y = 0; y < m_iLensGridSegmentCountV - 1; y++) { for (GLushort x = 0; x < m_iLensGridSegmentCountH - 1; x++) { a = m_iLensGridSegmentCountH * y + x + offset; b = m_iLensGridSegmentCountH * y + x + 1 + offset; c = (y + 1) * m_iLensGridSegmentCountH + x + 1 + offset; d = (y + 1) * m_iLensGridSegmentCountH + x + offset; vIndices.push_back(a); vIndices.push_back(b); vIndices.push_back(c); vIndices.push_back(a); vIndices.push_back(c); vIndices.push_back(d); } } m_uiIndexSize = vIndices.size(); glGenVertexArrays(1, &m_unLensVAO); glBindVertexArray(m_unLensVAO); glGenBuffers(1, &m_glIDVertBuffer); glBindBuffer(GL_ARRAY_BUFFER, m_glIDVertBuffer); glBufferData(GL_ARRAY_BUFFER, vVerts.size() * sizeof(VertexDataLens), &vVerts[0], GL_STATIC_DRAW); glGenBuffers(1, &m_glIDIndexBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_glIDIndexBuffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, vIndices.size() * sizeof(GLushort), &vIndices[0], GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(VertexDataLens), (void *)offsetof(VertexDataLens, position)); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(VertexDataLens), (void *)offsetof(VertexDataLens, texCoordRed)); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(VertexDataLens), (void *)offsetof(VertexDataLens, texCoordGreen)); glEnableVertexAttribArray(3); glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(VertexDataLens), (void *)offsetof(VertexDataLens, texCoordBlue)); glBindVertexArray(0); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); glDisableVertexAttribArray(2); glDisableVertexAttribArray(3); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CMainApplication::RenderStereoTargets() { B3_PROFILE("CMainApplication::RenderStereoTargets"); btScalar dtSec = btScalar(m_clock.getTimeInSeconds()); dtSec = btMin(dtSec, btScalar(0.1)); sExample->stepSimulation(dtSec); m_clock.reset(); 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()); m_app->m_instancingRenderer->getActiveCamera()->setVRCamera(viewMatLeft.get(), m_mat4ProjectionLeft.get()); } glBindFramebuffer(GL_FRAMEBUFFER, leftEyeDesc.m_nRenderFramebufferId); m_app->m_window->startRendering(); glViewport(0, 0, m_nRenderWidth, m_nRenderHeight); RenderScene(vr::Eye_Left); m_app->m_instancingRenderer->setRenderFrameBuffer((unsigned int)leftEyeDesc.m_nRenderFramebufferId); if (gDebugDrawFlags) { sExample->physicsDebugDraw(gDebugDrawFlags); } //else { 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()); m_app->m_instancingRenderer->getActiveCamera()->setVRCamera(viewMatRight.get(), m_mat4ProjectionRight.get()); } glBindFramebuffer(GL_FRAMEBUFFER, rightEyeDesc.m_nRenderFramebufferId); m_app->m_window->startRendering(); glViewport(0, 0, m_nRenderWidth, m_nRenderHeight); RenderScene(vr::Eye_Right); m_app->m_instancingRenderer->setRenderFrameBuffer((unsigned int)rightEyeDesc.m_nRenderFramebufferId); //m_app->m_renderer->renderScene(); if (gDebugDrawFlags) { sExample->physicsDebugDraw(gDebugDrawFlags); } //else { sExample->renderScene(); } //m_app->drawGrid(gridUp); m_app->m_instancingRenderer->setRenderFrameBuffer(0); 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) { B3_PROFILE("RenderScene"); //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 (gEnableVRRenderControllers) { 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); 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; { B3_PROFILE("WaitGetPoses"); vr::VRCompositor()->WaitGetPoses(m_rTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, NULL, 0); } m_iValidPoseCount = 0; m_strPoseClasses = ""; { B3_PROFILE("for loop"); 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_TrackingReference: m_rDevClassChar[nDevice] = 'T'; break; case vr::TrackedDeviceClass_GenericTracker: m_rDevClassChar[nDevice] = 'G'; break; default: m_rDevClassChar[nDevice] = '?'; break; } } m_strPoseClasses += m_rDevClassChar[nDevice]; } } } { B3_PROFILE("m_mat4HMDPose invert"); 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::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, &fLargest); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, 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[]) { b3CommandLineArgs args(argc, argv); if (args.CheckCmdLineFlag("disable_desktop_gl")) { gDisableDesktopGL = true; } if (args.CheckCmdLineFlag("tracing")) { b3ChromeUtilsStartTimings(); b3ChromeUtilsEnableProfiling(); } args.GetCmdLineArgument("max_num_object_capacity", maxNumObjectCapacity); args.GetCmdLineArgument("max_shape_capacity_in_bytes", maxShapeCapacityInBytes); args.GetCmdLineArgument("shared_memory_key", gSharedMemoryKey); #ifdef BT_USE_CUSTOM_PROFILER b3SetCustomEnterProfileZoneFunc(dcEnter); b3SetCustomLeaveProfileZoneFunc(dcLeave); #endif CMainApplication *pMainApplication = new CMainApplication(argc, argv); if (!pMainApplication->BInit()) { pMainApplication->Shutdown(); return 1; } if (sExample) { //until we have a proper VR gui, always assume we want the hard-coded default robot assets char *newargv[2]; char *t0 = (char *)"--robotassets"; newargv[0] = t0; newargv[1] = t0; sExample->processCommandLineArgs(2, newargv); sExample->processCommandLineArgs(argc, argv); } char *gVideoFileName = 0; args.GetCmdLineArgument("mp4", gVideoFileName); if (gVideoFileName) pMainApplication->getApp()->dumpFramesToVideo(gVideoFileName); #ifndef B3_USE_GLFW #ifdef _WIN32 //request disable VSYNC typedef bool(APIENTRY * PFNWGLSWAPINTERVALFARPROC)(int); PFNWGLSWAPINTERVALFARPROC wglSwapIntervalEXT = 0; wglSwapIntervalEXT = (PFNWGLSWAPINTERVALFARPROC)wglGetProcAddress("wglSwapIntervalEXT"); if (wglSwapIntervalEXT) wglSwapIntervalEXT(0); #endif #endif #ifdef __APPLE__ GLint sync = 0; CGLContextObj ctx = CGLGetCurrentContext(); CGLSetParameter(ctx, kCGLCPSwapInterval, &sync); #endif pMainApplication->RunMainLoop(); pMainApplication->Shutdown(); if (args.CheckCmdLineFlag("tracing")) { b3ChromeUtilsStopTimingsAndWriteJsonFile("timings"); } return 0; } #endif //BT_ENABLE_VR