void OculusVRDebug::OnRender(const ovrSession session, const ovrTrackingState &trackingState, const ovrEyeRenderDesc *eyeRenderDescs, const ovrSizei &eyeTextureSize)
{
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glActiveTexture(GL_TEXTURE0);

    static const float xPos     = -0.3f;
    static const float ySpacing = 0.05f;
    std::stringstream statsStream;

    char buf[128];
    float hmdYaw, hmdPitch, hmdRoll;
    ovrHmdDesc hmdDesc = ovr_GetHmdDesc(session);
    OVR::Quatf headOrientation(trackingState.HeadPose.ThePose.Orientation);
    headOrientation.GetEulerAngles<OVR::Axis_Y, OVR::Axis_X, OVR::Axis_Z>(&hmdYaw, &hmdPitch, &hmdRoll);

    OVR::OVR_sprintf(buf, sizeof(buf), "HMD YPR:%2.0f %2.0f %2.0f  HMD: %s", hmdYaw * PIdiv180inv, hmdPitch * PIdiv180inv, hmdRoll * PIdiv180inv, hmdDesc.ProductName);
    m_font->drawText(buf, xPos, 0.1f, 0.f);

    OVR::OVR_sprintf(buf, sizeof(buf), "FPS: %.1f  ms/frame: %.1f  Frame: %03d %d", m_fps, m_secondsPerFrame * 1000.0f, m_frameCounter, m_totalFrameCounter % 2);
    m_font->drawText(buf, xPos, 0.1f - ySpacing, 0.f);

    OVR::OVR_sprintf(buf, sizeof(buf), "Pos: %2.2f %2.2f %2.2f  Tracking: %s", g_cameraDirector.GetActiveCamera()->Position().m_x, 
                                                                 g_cameraDirector.GetActiveCamera()->Position().m_y,
                                                                 g_cameraDirector.GetActiveCamera()->Position().m_z,
                                                                 trackingState.StatusFlags & ovrStatus_PositionTracked ? "YES" : "NO");
    m_font->drawText(buf, xPos, 0.1f - ySpacing * 2.f, 0.f);


    OVR::OVR_sprintf(buf, sizeof(buf), "EyeHeight: %2.2f IPD: %2.1fmm", ovr_GetFloat(session, OVR_KEY_EYE_HEIGHT, 0.f), ovr_GetFloat(session, OVR_KEY_IPD, 0.f) * 1000.f);
    m_font->drawText(buf, xPos, 0.1f - ySpacing * 3.f, 0.f);

    // Average FOVs
    OVR::FovPort leftFov  = eyeRenderDescs[0].Fov;
    OVR::FovPort rightFov = eyeRenderDescs[1].Fov;

    // Rendered size changes based on selected options & dynamic rendering.
    OVR::OVR_sprintf(buf, sizeof(buf), "FOV %2.1fx%2.1f, Resolution: %ix%i", (leftFov.GetHorizontalFovDegrees() + rightFov.GetHorizontalFovDegrees()) * 0.5f,
                                                                             (leftFov.GetVerticalFovDegrees() + rightFov.GetVerticalFovDegrees()) * 0.5,
                                                                             eyeTextureSize.w, eyeTextureSize.h);
    m_font->drawText(buf, xPos, 0.1f - ySpacing * 4.f, 0.f);

    // latency readings
    float latencies[5] = {};
    if (ovr_GetFloatArray(session, "DK2Latency", latencies, 5) == 5)
    {
        char text[5][32];
        for (int i = 0; i < 5; ++i)
        {
            FormatLatencyReading(text[i], sizeof(text[i]), latencies[i]);
        }

        statsStream.str("");
        statsStream << "M2P Latency  Ren: " << text[0] << " TWrp: " << text[1];
        m_font->drawText(statsStream.str(), xPos, 0.1f - ySpacing * 5.f, 0.f);

        statsStream.str("");
        statsStream << "PostPresent: " << text[2] << " Err: " << text[3] << " " << text[4];
        m_font->drawText(statsStream.str(), xPos, 0.1f - ySpacing * 6.f, 0.f);
    }
}
void Application::UpdateCamera(float dt)
{
    // don't make movement too sickening in VR
    static const float movementSpeed = VREnabled() ? 1.f : 8.f;

    if (KeyPressed(KEY_A))
        g_cameraDirector.GetActiveCamera()->Strafe(-movementSpeed * dt);

    if (KeyPressed(KEY_D))
        g_cameraDirector.GetActiveCamera()->Strafe(movementSpeed * dt);

    if (KeyPressed(KEY_W))
        g_cameraDirector.GetActiveCamera()->MoveForward(-movementSpeed * dt);

    if (KeyPressed(KEY_S))
        g_cameraDirector.GetActiveCamera()->MoveForward(movementSpeed * dt);

    // do the barrel roll!
    if (KeyPressed(KEY_Q))
        g_cameraDirector.GetActiveCamera()->rotateZ(2.f * dt);

    if (KeyPressed(KEY_E))
        g_cameraDirector.GetActiveCamera()->rotateZ(-2.f * dt);

    // move straight up/down
    if (KeyPressed(KEY_R))
        g_cameraDirector.GetActiveCamera()->MoveUpward(movementSpeed * dt);

    if (KeyPressed(KEY_F))
        g_cameraDirector.GetActiveCamera()->MoveUpward(-movementSpeed * dt);
}
void Application::OnRender()
{
    //update global MVP matrix in primary shader    
    glUniformMatrix4fv(ShaderManager::GetInstance()->UseShaderProgram(ShaderManager::BasicShader).uniforms[ModelViewProjectionMatrix], 1, GL_FALSE, &(g_renderContext.ModelViewProjectionMatrix[0]));

    // render the bsp
    if (m_q3map)
    {
        // no need to calculate separate view matrix if we're in VR (OVR handles it for us)
        if (!VREnabled())
        {
            g_cameraDirector.GetActiveCamera()->OnRender();
        }

        m_q3map->OnRenderStart();
        m_q3map->Render();
        m_q3map->OnRenderFinish();
    }

    // render map stats
    switch (m_debugRenderState)
    {
    case RenderMapStats:
        m_q3stats->Render();
        break;
    case RenderVRData:
        g_oculusVR.RenderDebug();
        break;
    case RenderVRTrackingCamera:
        g_oculusVR.RenderTrackerFrustum();
    default:
        break;
    }
}
void Application::OnMouseMove(int x, int y)
{
    if (!VREnabled())
    {
        g_cameraDirector.GetActiveCamera()->OnMouseMove(x, y);
    }
}
void Application::OnUpdate(float dt)
{
    UpdateCamera(dt);

    // determine which faces are visible
    if (m_q3map)
        m_q3map->CalculateVisibleFaces(g_cameraDirector.GetActiveCamera()->Position());

    if (VREnabled())
    {
        g_oculusVR.UpdateDebug();
    }
}
void Application::OnStart(int argc, char **argv, bool vrMode)
{
    m_VREnabled = vrMode;   
    glEnable(GL_MULTISAMPLE);

    Q3BspLoader loader;
    // assume the parameter with a string ".bsp" is the map we want to load
    for (int i = 1; i < argc; ++i)
    {
        if (std::string(argv[i]).find(".bsp") != std::string::npos)
        {
            m_q3map = loader.Load(argv[i]);
            break;
        }
    }

    Math::Vector3f startPos;

    if (m_q3map)
    {
        m_q3map->Init();
        m_q3map->ToggleRenderFlag(Q3RenderUseLightmaps);

        // try to locate the first info_player_deathmatch entity and place the camera there
        startPos = FindPlayerStart(static_cast<Q3BspMap *>(m_q3map)->entities.ents);
    }

    g_cameraDirector.AddCamera(startPos / Q3BspMap::s_worldScale,
                               Math::Vector3f(0.f, 0.f, 1.f),
                               Math::Vector3f(1.f, 0.f, 0.f),
                               Math::Vector3f(0.f, 1.f, 0.f));

    // set to "clean" perspective matrix
    g_cameraDirector.GetActiveCamera()->SetMode(Camera::CAM_FPS);

    m_q3stats = new Q3StatsUI(m_q3map);
}