761 lines · 29.4 KB · C++
RawDownload
////  ApplicationCompositor.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 "ApplicationCompositor.h" #include <glm/gtc/type_ptr.hpp> #include <avatar/AvatarManager.h>#include <gpu/GLBackend.h>#include <CursorManager.h>#include <Tooltip.h> #include "Application.h"  // Used to animate the magnification windowsstatic const float MAG_SPEED = 0.08f; static const quint64 MSECS_TO_USECS = 1000ULL;static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS; static const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };static const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f };static const float reticleSize = TWO_PI / 100.0f; static const float CONNECTION_STATUS_BORDER_COLOR[] = { 1.0f, 0.0f, 0.0f };static const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f; static const float CURSOR_PIXEL_SIZE = 32.0f;static const float MOUSE_PITCH_RANGE = 1.0f * PI;static const float MOUSE_YAW_RANGE = 0.5f * TWO_PI;static const glm::vec2 MOUSE_RANGE(MOUSE_YAW_RANGE, MOUSE_PITCH_RANGE); static gpu::BufferPointer _hemiVertices;static gpu::BufferPointer _hemiIndices;static int _hemiIndexCount{ 0 };EntityItemID ApplicationCompositor::_noItemId;static QString _tooltipId; // Return a point's cartesian coordinates on a sphere from pitch and yawglm::vec3 getPoint(float yaw, float pitch) {    return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)),                     glm::sin(-pitch),                     glm::cos(-pitch) * (-glm::cos(yaw)));} //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.0f;    } else {        q = (-b + distSqrt) / 2.0f;    }     // 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;    }} ApplicationCompositor::ApplicationCompositor() {    memset(_reticleActive, 0, sizeof(_reticleActive));    memset(_magActive, 0, sizeof(_reticleActive));    memset(_magSizeMult, 0, sizeof(_magSizeMult));     auto geometryCache = DependencyManager::get<GeometryCache>();        _reticleQuad = geometryCache->allocateID();    _magnifierQuad = geometryCache->allocateID();    _magnifierBorder = geometryCache->allocateID();     auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();    connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) {        if (_hoverItemId != entityItemID) {            _hoverItemId = entityItemID;            _hoverItemEnterUsecs = usecTimestampNow();            auto properties = entityScriptingInterface->getEntityProperties(_hoverItemId);            _hoverItemHref = properties.getHref();            auto cursor = Cursor::Manager::instance().getCursor();            if (!_hoverItemHref.isEmpty()) {                cursor->setIcon(Cursor::Icon::LINK);            } else {                cursor->setIcon(Cursor::Icon::DEFAULT);            }        }    });     connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) {        if (_hoverItemId == entityItemID) {            _hoverItemId = _noItemId;            _hoverItemHref.clear();            auto cursor = Cursor::Manager::instance().getCursor();            cursor->setIcon(Cursor::Icon::DEFAULT);            if (!_tooltipId.isEmpty()) {                qDebug() << "Closing tooltip " << _tooltipId;                Tooltip::closeTip(_tooltipId);                _tooltipId.clear();            }        }    });} ApplicationCompositor::~ApplicationCompositor() {}  void ApplicationCompositor::bindCursorTexture(gpu::Batch& batch, uint8_t cursorIndex) {    auto& cursorManager = Cursor::Manager::instance();    auto cursor = cursorManager.getCursor(cursorIndex);    auto iconId = cursor->getIcon();    if (!_cursors.count(iconId)) {        auto iconPath = cursorManager.getIconImage(cursor->getIcon());        _cursors[iconId] = DependencyManager::get<TextureCache>()->            getImageTexture(iconPath);    }    batch.setUniformTexture(0, _cursors[iconId]);} // Draws the FBO texture for the screenvoid ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {    if (_alpha == 0.0f) {        return;    }     GLuint texture = qApp->getApplicationOverlay().getOverlayTexture();    if (!texture) {        return;    }     updateTooltips();     vec2 canvasSize = qApp->getCanvasSize();     //Handle fading and deactivation/activation of UI    gpu::Batch batch;     renderArgs->_context->syncCache();    auto geometryCache = DependencyManager::get<GeometryCache>();     geometryCache->useSimpleDrawPipeline(batch);    batch.setModelTransform(Transform());    batch.setViewTransform(Transform());    batch.setProjectionTransform(mat4());    batch._glBindTexture(GL_TEXTURE_2D, texture);    batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);    batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);    geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha));     // Doesn't actually render    renderPointers(batch);     //draw the mouse pointer    // Get the mouse coordinates and convert to NDC [-1, 1]    vec2 mousePosition = toNormalizedDeviceScale(vec2(qApp->getMouse()), canvasSize);    // Invert the Y axis    mousePosition.y *= -1.0f;     Transform model;    model.setTranslation(vec3(mousePosition, 0));    vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize;    model.setScale(vec3(mouseSize, 1.0f));    batch.setModelTransform(model);    bindCursorTexture(batch);    vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f };    geometryCache->renderUnitQuad(batch, vec4(1));    renderArgs->_context->render(batch);}  vec2 getPolarCoordinates(const PalmData& palm) {    MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();    auto avatarOrientation = myAvatar->getOrientation();    auto eyePos = myAvatar->getDefaultEyePosition();    glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm);    // Direction of the tip relative to the eye    glm::vec3 tipDirection = tip - eyePos;    // orient into avatar space    tipDirection = glm::inverse(avatarOrientation) * tipDirection;    // Normalize for trig functions    tipDirection = glm::normalize(tipDirection);    // Convert to polar coordinates    glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y));    return polar;} // Draws the FBO texture for Oculus rift.void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int eye) {    if (_alpha == 0.0f) {        return;    }     GLuint texture = qApp->getApplicationOverlay().getOverlayTexture();    if (!texture) {        return;    }     updateTooltips();     vec2 canvasSize = qApp->getCanvasSize();    _textureAspectRatio = aspect(canvasSize);     renderArgs->_context->syncCache();    auto geometryCache = DependencyManager::get<GeometryCache>();     gpu::Batch batch;    geometryCache->useSimpleDrawPipeline(batch);    batch._glDisable(GL_DEPTH_TEST);    batch._glDisable(GL_CULL_FACE);    batch._glBindTexture(GL_TEXTURE_2D, texture);    batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);    batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);    batch.setViewTransform(Transform());    batch.setProjectionTransform(qApp->getEyeProjection(eye));     mat4 eyePose = qApp->getEyePose(eye);    glm::mat4 overlayXfm = glm::inverse(eyePose); #ifdef DEBUG_OVERLAY    {        batch.setModelTransform(glm::translate(mat4(), vec3(0, 0, -2)));        geometryCache->renderUnitQuad(batch, glm::vec4(1));    }#else    {        batch.setModelTransform(overlayXfm);        drawSphereSection(batch);    }#endif     // Doesn't actually render    renderPointers(batch);    vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize);     bindCursorTexture(batch);     MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();    //Controller Pointers    for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) {        PalmData& palm = myAvatar->getHand()->getPalms()[i];        if (palm.isActive()) {            glm::vec2 polar = getPolarCoordinates(palm);            // Convert to quaternion            mat4 pointerXfm = glm::mat4_cast(quat(vec3(polar.y, -polar.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));            mat4 reticleXfm = overlayXfm * pointerXfm;            reticleXfm = glm::scale(reticleXfm, reticleScale);            batch.setModelTransform(reticleXfm);            // Render reticle at location            geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);        }    }     //Mouse Pointer    if (_reticleActive[MOUSE]) {        glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(),            _reticlePosition[MOUSE].y()));        mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));        mat4 reticleXfm = overlayXfm * pointerXfm;        reticleXfm = glm::scale(reticleXfm, reticleScale);        batch.setModelTransform(reticleXfm);        geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);    }        renderArgs->_context->render(batch);}  void ApplicationCompositor::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const {    cursorPos *= qApp->getCanvasSize();    const glm::vec2 projection = screenToSpherical(cursorPos);    // The overlay space orientation of the mouse coordinates    const glm::quat orientation(glm::vec3(-projection.y, projection.x, 0.0f));    // FIXME We now have the direction of the ray FROM THE DEFAULT HEAD POSE.      // Now we need to account for the actual camera position relative to the overlay    glm::vec3 overlaySpaceDirection = glm::normalize(orientation * IDENTITY_FRONT);      // We need the RAW camera orientation and position, because this is what the overlay is    // rendered relative to    const glm::vec3 overlayPosition = qApp->getCamera()->getPosition();    const glm::quat overlayOrientation = qApp->getCamera()->getRotation();     // Intersection UI overlay space    glm::vec3 worldSpaceDirection = overlayOrientation * overlaySpaceDirection;    glm::vec3 worldSpaceIntersection = (glm::normalize(worldSpaceDirection) * _oculusUIRadius) + overlayPosition;    glm::vec3 worldSpaceHeadPosition = (overlayOrientation * glm::vec3(qApp->getHeadPose()[3])) + overlayPosition;     // Intersection in world space    origin = worldSpaceHeadPosition;    direction = glm::normalize(worldSpaceIntersection - worldSpaceHeadPosition);} //Caculate the click location using one of the sixense controllers. Scale is not appliedQPoint ApplicationCompositor::getPalmClickLocation(const PalmData *palm) const {    QPoint rv;    auto canvasSize = qApp->getCanvasSize();    if (qApp->isHMDMode()) {        glm::vec2 polar = getPolarCoordinates(*palm);        glm::vec2 point = sphericalToScreen(-polar);        rv.rx() = point.x;        rv.ry() = point.y;    } else {        MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();        glm::dmat4 projection;        qApp->getProjectionMatrix(&projection);        glm::quat invOrientation = glm::inverse(myAvatar->getOrientation());        glm::vec3 eyePos = myAvatar->getDefaultEyePosition();        glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm);        glm::vec3 tipPos = invOrientation * (tip - eyePos);         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) * canvasSize.x);        rv.setY((1.0 - ((ndcSpacePos.y + 1.0) / 2.0)) * canvasSize.y);    }    return rv;} //Finds the collision point of a world space raybool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const {    MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();        glm::quat inverseOrientation = glm::inverse(myAvatar->getOrientation());     glm::vec3 relativePosition = inverseOrientation * (position - myAvatar->getDefaultEyePosition());    glm::vec3 relativeDirection = glm::normalize(inverseOrientation * direction);     float t;    if (raySphereIntersect(relativeDirection, relativePosition, _oculusUIRadius * myAvatar->getScale(), &t)){        result = position + direction * t;        return true;    }     return false;} //Renders optional pointersvoid ApplicationCompositor::renderPointers(gpu::Batch& batch) {    if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) {        //If we are in oculus, render reticle later        QPoint position = QPoint(qApp->getTrueMouseX(), qApp->getTrueMouseY());        _reticlePosition[MOUSE] = position;        _reticleActive[MOUSE] = true;        _magActive[MOUSE] = _magnifier;        _reticleActive[LEFT_CONTROLLER] = false;        _reticleActive[RIGHT_CONTROLLER] = false;    } else if (qApp->getLastMouseMoveWasSimulated() && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) {        //only render controller pointer if we aren't already rendering a mouse pointer        _reticleActive[MOUSE] = false;        _magActive[MOUSE] = false;        renderControllerPointers(batch);    }}        void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) {    MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();     //Static variables used for storing controller state    static quint64 pressedTime[NUMBER_OF_RETICLES] = { 0ULL, 0ULL, 0ULL };    static bool isPressed[NUMBER_OF_RETICLES] = { false, false, false };    static bool stateWhenPressed[NUMBER_OF_RETICLES] = { false, false, false };     const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData();     for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) {        const int index = palmIndex - 1;         const PalmData* palmData = NULL;         if (palmIndex >= handData->getPalms().size()) {            return;        }         if (handData->getPalms()[palmIndex].isActive()) {            palmData = &handData->getPalms()[palmIndex];        } else {            continue;        }         int controllerButtons = palmData->getControllerButtons();         //Check for if we should toggle or drag the magnification window        if (controllerButtons & BUTTON_3) {            if (isPressed[index] == false) {                //We are now dragging the window                isPressed[index] = true;                //set the pressed time in us                pressedTime[index] = usecTimestampNow();                stateWhenPressed[index] = _magActive[index];            }        } else if (isPressed[index]) {            isPressed[index] = false;            //If the button was only pressed for < 250 ms            //then disable it.             const int MAX_BUTTON_PRESS_TIME = 250 * MSECS_TO_USECS;            if (usecTimestampNow() < pressedTime[index] + MAX_BUTTON_PRESS_TIME) {                _magActive[index] = !stateWhenPressed[index];            }        }         //if we have the oculus, we should make the cursor smaller since it will be        //magnified        if (qApp->isHMDMode()) {             QPoint point = getPalmClickLocation(palmData);             _reticlePosition[index] = point;             //When button 2 is pressed we drag the mag window            if (isPressed[index]) {                _magActive[index] = true;            }             // If oculus is enabled, we draw the crosshairs later            continue;        }         auto canvasSize = qApp->getCanvasSize();        int mouseX, mouseY;        if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) {            QPoint res = getPalmClickLocation(palmData);            mouseX = res.x();            mouseY = res.y();        } else {            // Get directon relative to avatar orientation            glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection();             // Get the angles, scaled between (-0.5,0.5)            float xAngle = (atan2(direction.z, direction.x) + M_PI_2);            float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2));             // Get the pixel range over which the xAngle and yAngle are scaled            float cursorRange = canvasSize.x * SixenseManager::getInstance().getCursorPixelRangeMult();             mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle);            mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle);        }         //If the cursor is out of the screen then don't render it        if (mouseX < 0 || mouseX >= (int)canvasSize.x || mouseY < 0 || mouseY >= (int)canvasSize.y) {            _reticleActive[index] = false;            continue;

Lines 1–500 of 761.