1262 lines · 44.2 KB · C++
RawDownload
////  ApplicationOverlay.cpp//  interface/src/ui/overlays////  Created by Benjamin Arnold on 5/27/14.//  Copyright 2014 High Fidelity, Inc.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "InterfaceConfig.h" #include <QOpenGLFramebufferObject>#include <PerfStat.h> #include "Application.h"#include "ApplicationOverlay.h"#include "devices/OculusManager.h" #include "ui/Stats.h" // Used to fade the UIconst float FADE_SPEED = 0.08f;// Used to animate the magnification windowsconst float MAG_SPEED = 0.08f; const quint64 MSECS_TO_USECS = 1000ULL; // Fast helper functionsinline float max(float a, float b) {    return (a > b) ? a : b;} inline float min(float a, float b) {    return (a < b) ? a : b;} ApplicationOverlay::ApplicationOverlay() :     _framebufferObject(NULL),    _textureFov(DEFAULT_OCULUS_UI_ANGULAR_SIZE * RADIANS_PER_DEGREE),    _alpha(1.0f),    _oculusuiRadius(1.0f),    _crosshairTexture(0) {     memset(_reticleActive, 0, sizeof(_reticleActive));    memset(_magActive, 0, sizeof(_reticleActive));    memset(_magSizeMult, 0, sizeof(_magSizeMult));} ApplicationOverlay::~ApplicationOverlay() {    if (_framebufferObject != NULL) {        delete _framebufferObject;    }} const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f }; const float CONNECTION_STATUS_BORDER_COLOR[] = { 1.0f, 0.0f, 0.0f };const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f; // Renders the overlays either to a texture or to the screenvoid ApplicationOverlay::renderOverlay(bool renderToTexture) {     PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()");     _textureFov = Menu::getInstance()->getOculusUIAngularSize() * RADIANS_PER_DEGREE;     Application* application = Application::getInstance();     Overlays& overlays = application->getOverlays();    GLCanvas* glWidget = application->getGLWidget();    MyAvatar* myAvatar = application->getAvatar();     //Handle fading and deactivation/activation of UI    if (Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)) {        _alpha += FADE_SPEED;        if (_alpha > 1.0f) {            _alpha = 1.0f;        }    } else {        _alpha -= FADE_SPEED;        if (_alpha <= 0.0f) {            _alpha = 0.0f;        }    }     if (renderToTexture) {        getFramebufferObject()->bind();        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    }    glEnable(GL_BLEND);    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);      // Render 2D overlay    glMatrixMode(GL_PROJECTION);    glPushMatrix();     glLoadIdentity();    gluOrtho2D(0, glWidget->width(), glWidget->height(), 0);    glDisable(GL_DEPTH_TEST);    glDisable(GL_LIGHTING);     renderAudioMeter();     if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse)) {        myAvatar->renderHeadMouse(glWidget->width(), glWidget->height());    }     renderStatsAndLogs();     // give external parties a change to hook in    emit application->renderingOverlay();     overlays.render2D();     renderPointers();     renderDomainConnectionStatusBorder();     glPopMatrix();      glMatrixMode(GL_MODELVIEW);    glEnable(GL_DEPTH_TEST);    glEnable(GL_LIGHTING);    glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);     if (renderToTexture) {        getFramebufferObject()->release();    }} // Draws the FBO texture for the screenvoid ApplicationOverlay::displayOverlayTexture() {     if (_alpha == 0.0f) {        return;    }     Application* application = Application::getInstance();    GLCanvas* glWidget = application->getGLWidget();      glEnable(GL_TEXTURE_2D);    glActiveTexture(GL_TEXTURE0);    glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture());     glMatrixMode(GL_PROJECTION);    glPushMatrix();     glLoadIdentity();    gluOrtho2D(0, glWidget->getDeviceWidth(), glWidget->getDeviceHeight(), 0);    glDisable(GL_DEPTH_TEST);    glDisable(GL_LIGHTING);    glEnable(GL_BLEND);     glBegin(GL_QUADS);    glColor4f(1.0f, 1.0f, 1.0f, _alpha);    glTexCoord2f(0, 0); glVertex2i(0, glWidget->getDeviceHeight());    glTexCoord2f(1, 0); glVertex2i(glWidget->getDeviceWidth(), glWidget->getDeviceHeight());    glTexCoord2f(1, 1); glVertex2i(glWidget->getDeviceWidth(), 0);    glTexCoord2f(0, 1); glVertex2i(0, 0);    glEnd();    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);     glPopMatrix();    glDisable(GL_TEXTURE_2D);} void ApplicationOverlay::computeOculusPickRay(float x, float y, glm::vec3& direction) const {    MyAvatar* myAvatar = Application::getInstance()->getAvatar();     //invert y direction    y = 1.0 - y;     //Get position on hemisphere UI    x = sin((x - 0.5f) * _textureFov);    y = sin((y - 0.5f) * _textureFov);     float dist = sqrt(x * x + y * y);    float z = -sqrt(1.0f - dist * dist);     glm::vec3 relativePosition = myAvatar->getDefaultEyePosition() +        glm::normalize(myAvatar->getOrientation() * glm::vec3(x, y, z));     //Rotate the UI pick ray by the avatar orientation    direction = glm::normalize(relativePosition - Application::getInstance()->getCamera()->getPosition());} // Calculates the click location on the screen by taking into account any// opened magnification windows.void ApplicationOverlay::getClickLocation(int &x, int &y) const {    int dx;    int dy;    const float xRange = MAGNIFY_WIDTH * MAGNIFY_MULT / 2.0f;    const float yRange = MAGNIFY_HEIGHT * MAGNIFY_MULT / 2.0f;     //Loop through all magnification windows    for (int i = 0; i < NUMBER_OF_MAGNIFIERS; i++) {        if (_magActive[i]) {            dx = x - _magX[i];            dy = y - _magY[i];            //Check to see if they clicked inside a mag window            if (abs(dx) <= xRange && abs(dy) <= yRange) {                //Move the click to the actual UI location by inverting the magnification                x = dx / MAGNIFY_MULT + _magX[i];                y = dy / MAGNIFY_MULT + _magY[i];                return;            }        }    }} //Checks if the given ray intersects the sphere at the origin. result will store a multiplier that should//be multiplied by dir and added to origin to get the location of the collisionbool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r, float* result){    //Source: http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection     //Compute A, B and C coefficients    float a = glm::dot(dir, dir);    float b = 2 * glm::dot(dir, origin);    float c = glm::dot(origin, origin) - (r * r);     //Find discriminant    float disc = b * b - 4 * a * c;     // if discriminant is negative there are no real roots, so return     // false as ray misses sphere    if (disc < 0) {        return false;    }     // compute q as described above    float distSqrt = sqrtf(disc);    float q;    if (b < 0) {        q = (-b - distSqrt) / 2.0;    } else {        q = (-b + distSqrt) / 2.0;    }     // compute t0 and t1    float t0 = q / a;    float t1 = c / q;     // make sure t0 is smaller than t1    if (t0 > t1) {        // if t0 is bigger than t1 swap them around        float temp = t0;        t0 = t1;        t1 = temp;    }     // if t1 is less than zero, the object is in the ray's negative direction    // and consequently the ray misses the sphere    if (t1 < 0) {        return false;    }     // if t0 is less than zero, the intersection point is at t1    if (t0 < 0) {        *result = t1;        return true;    } else { // else the intersection point is at t0        *result = t0;        return true;    }} //Caculate the click location using one of the sixense controllers. Scale is not appliedQPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const {     Application* application = Application::getInstance();    GLCanvas* glWidget = application->getGLWidget();    MyAvatar* myAvatar = application->getAvatar();     glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm);    glm::vec3 eyePos = myAvatar->getHead()->getEyePosition();    glm::quat orientation = glm::inverse(myAvatar->getOrientation());    glm::vec3 dir = orientation * glm::normalize(application->getCamera()->getPosition() - tip); //direction of ray goes towards camera    glm::vec3 tipPos = orientation * (tip - eyePos);     QPoint rv;     if (OculusManager::isConnected()) {        float t;         //We back the ray up by dir to ensure that it will not start inside the UI.        glm::vec3 adjustedPos = tipPos - dir;        //Find intersection of crosshair ray.         if (raySphereIntersect(dir, adjustedPos, _oculusuiRadius * myAvatar->getScale(), &t)){            glm::vec3 collisionPos = adjustedPos + dir * t;            //Normalize it in case its not a radius of 1            collisionPos = glm::normalize(collisionPos);            //If we hit the back hemisphere, mark it as not a collision            if (collisionPos.z > 0) {                rv.setX(INT_MAX);                rv.setY(INT_MAX);            } else {                 float u = asin(collisionPos.x) / (_textureFov)+0.5f;                float v = 1.0 - (asin(collisionPos.y) / (_textureFov)+0.5f);                 rv.setX(u * glWidget->width());                rv.setY(v * glWidget->height());            }        } else {            //if they did not click on the overlay, just set the coords to INT_MAX            rv.setX(INT_MAX);            rv.setY(INT_MAX);        }    } else {        glm::dmat4 projection;        application->getProjectionMatrix(&projection);         glm::vec4 clipSpacePos = glm::vec4(projection * glm::dvec4(tipPos, 1.0));        glm::vec3 ndcSpacePos;        if (clipSpacePos.w != 0) {            ndcSpacePos = glm::vec3(clipSpacePos) / clipSpacePos.w;        }         rv.setX(((ndcSpacePos.x + 1.0) / 2.0) * glWidget->width());        rv.setY((1.0 - ((ndcSpacePos.y + 1.0) / 2.0)) * glWidget->height());    }    return rv;} //Finds the collision point of a world space raybool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const {    Application* application = Application::getInstance();    MyAvatar* myAvatar = application->getAvatar();        glm::quat orientation = myAvatar->getOrientation();     glm::vec3 relativePosition = orientation * (position - myAvatar->getHead()->getEyePosition());    glm::vec3 relativeDirection = orientation * direction;     float t;    if (raySphereIntersect(relativeDirection, relativePosition, _oculusuiRadius * myAvatar->getScale(), &t)){        result = position + direction * t;        return true;    }     return false;} // Draws the FBO texture for Oculus rift.void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {     if (_alpha == 0.0f) {        return;    }     Application* application = Application::getInstance();     MyAvatar* myAvatar = application->getAvatar();     glActiveTexture(GL_TEXTURE0);       glEnable(GL_BLEND);    glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);    glEnable(GL_DEPTH_TEST);    glDisable(GL_LIGHTING);    glEnable(GL_TEXTURE_2D);     glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture());     glMatrixMode(GL_MODELVIEW);     glDepthMask(GL_TRUE);        glEnable(GL_ALPHA_TEST);    glAlphaFunc(GL_GREATER, 0.01f);     //Update and draw the magnifiers     glPushMatrix();    const glm::quat& orientation = myAvatar->getOrientation();    const glm::vec3& position = myAvatar->getDefaultEyePosition();     glm::mat4 rotation = glm::toMat4(orientation);     glTranslatef(position.x, position.y, position.z);    glMultMatrixf(&rotation[0][0]);    for (int i = 0; i < NUMBER_OF_MAGNIFIERS; i++) {         if (_magActive[i]) {            _magSizeMult[i] += MAG_SPEED;            if (_magSizeMult[i] > 1.0f) {                _magSizeMult[i] = 1.0f;            }        } else {            _magSizeMult[i] -= MAG_SPEED;            if (_magSizeMult[i] < 0.0f) {                _magSizeMult[i] = 0.0f;            }        }         if (_magSizeMult[i] > 0.0f) {            //Render magnifier, but dont show border for mouse magnifier            renderMagnifier(_magX[i], _magY[i], _magSizeMult[i], i != MOUSE);        }    }    glPopMatrix();     glDepthMask(GL_FALSE);       glDisable(GL_ALPHA_TEST);     glColor4f(1.0f, 1.0f, 1.0f, _alpha);     renderTexturedHemisphere();     renderPointersOculus(myAvatar->getDefaultEyePosition());     glDepthMask(GL_TRUE);    glBindTexture(GL_TEXTURE_2D, 0);    glDisable(GL_TEXTURE_2D);     glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);    glEnable(GL_LIGHTING);     glColor4f(1.0f, 1.0f, 1.0f, 1.0f); } // Draws the FBO texture for 3DTV.void ApplicationOverlay::displayOverlayTexture3DTV(Camera& whichCamera, float aspectRatio, float fov) {     if (_alpha == 0.0f) {        return;    }     Application* application = Application::getInstance();     MyAvatar* myAvatar = application->getAvatar();    const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation();     glActiveTexture(GL_TEXTURE0);     glEnable(GL_BLEND);    glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);    glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture());    glEnable(GL_DEPTH_TEST);    glDisable(GL_LIGHTING);    glEnable(GL_TEXTURE_2D);     glMatrixMode(GL_MODELVIEW);     glPushMatrix();    glLoadIdentity();    // Transform to world space    glm::quat rotation = whichCamera.getRotation();    glm::vec3 axis2 = glm::axis(rotation);    glRotatef(-glm::degrees(glm::angle(rotation)), axis2.x, axis2.y, axis2.z);    glTranslatef(viewMatrixTranslation.x, viewMatrixTranslation.y, viewMatrixTranslation.z);     // Translate to the front of the camera    glm::vec3 pos = whichCamera.getPosition();    glm::quat rot = myAvatar->getOrientation();    glm::vec3 axis = glm::axis(rot);     glTranslatef(pos.x, pos.y, pos.z);    glRotatef(glm::degrees(glm::angle(rot)), axis.x, axis.y, axis.z);     glColor4f(1.0f, 1.0f, 1.0f, _alpha);     //Render    const GLfloat distance = 1.0f;     const GLfloat halfQuadHeight = distance * tan(fov);    const GLfloat halfQuadWidth = halfQuadHeight * aspectRatio;    const GLfloat quadWidth = halfQuadWidth * 2.0f;    const GLfloat quadHeight = halfQuadHeight * 2.0f;     GLfloat x = -halfQuadWidth;    GLfloat y = -halfQuadHeight;    glDisable(GL_DEPTH_TEST);     glBegin(GL_QUADS);     glTexCoord2f(0.0f, 1.0f); glVertex3f(x, y + quadHeight, -distance);    glTexCoord2f(1.0f, 1.0f); glVertex3f(x + quadWidth, y + quadHeight, -distance);    glTexCoord2f(1.0f, 0.0f); glVertex3f(x + quadWidth, y, -distance);    glTexCoord2f(0.0f, 0.0f); glVertex3f(x, y, -distance);     glEnd();     if (_crosshairTexture == 0) {        _crosshairTexture = Application::getInstance()->getGLWidget()->bindTexture(QImage(Application::resourcesPath() + "images/sixense-reticle.png"));    }     //draw the mouse pointer    glBindTexture(GL_TEXTURE_2D, _crosshairTexture);     const float reticleSize = 40.0f / application->getGLWidget()->width() * quadWidth;    x -= reticleSize / 2.0f;

Lines 1–500 of 1262.