Showing the changes to interface/src (61 of 214 files). Show all
Unified · Split
| @@ -66,6 +66,9 @@ · 65 unmodified lines | |||
| 66 | #include <EntityScriptingInterface.h> | 66 | #include <EntityScriptingInterface.h> |
| 67 | #include <ErrorDialog.h> | 67 | #include <ErrorDialog.h> |
| 68 | #include <GlowEffect.h> | 68 | #include <GlowEffect.h> |
| 69 | #include <gpu/Batch.h> | ||
| 70 | #include <gpu/Context.h> | ||
| 71 | #include <gpu/GLBackend.h> | ||
| 69 | #include <HFActionEvent.h> | 72 | #include <HFActionEvent.h> |
| 70 | #include <HFBackEvent.h> | 73 | #include <HFBackEvent.h> |
| 71 | #include <InfoView.h> | 74 | #include <InfoView.h> |
| @@ -83,12 +86,14 @@ · 11 unmodified lines | |||
| 83 | #include <PerfStat.h> | 86 | #include <PerfStat.h> |
| 84 | #include <PhysicsEngine.h> | 87 | #include <PhysicsEngine.h> |
| 85 | #include <ProgramObject.h> | 88 | #include <ProgramObject.h> |
| 89 | #include <RenderDeferredTask.h> | ||
| 86 | #include <ResourceCache.h> | 90 | #include <ResourceCache.h> |
| 87 | #include <SceneScriptingInterface.h> | 91 | #include <SceneScriptingInterface.h> |
| 88 | #include <ScriptCache.h> | 92 | #include <ScriptCache.h> |
| 89 | #include <SettingHandle.h> | 93 | #include <SettingHandle.h> |
| 90 | #include <SoundCache.h> | 94 | #include <SoundCache.h> |
| 91 | #include <TextRenderer.h> | 95 | #include <TextRenderer.h> |
| 96 | #include <Tooltip.h> | ||
| 92 | #include <UserActivityLogger.h> | 97 | #include <UserActivityLogger.h> |
| 93 | #include <UUID.h> | 98 | #include <UUID.h> |
| 94 | #include <VrMenu.h> | 99 | #include <VrMenu.h> |
| @@ -106,11 +111,9 @@ · 11 unmodified lines | |||
| 106 | 111 | ||
| 107 | #include "avatar/AvatarManager.h" | 112 | #include "avatar/AvatarManager.h" |
| 108 | 113 | ||
| 109 | #include "audio/AudioToolBox.h" | ||
| 110 | #include "audio/AudioIOStatsRenderer.h" | 114 | #include "audio/AudioIOStatsRenderer.h" |
| 111 | #include "audio/AudioScope.h" | 115 | #include "audio/AudioScope.h" |
| 112 | 116 | ||
| 113 | #include "devices/CameraToolBox.h" | ||
| 114 | #include "devices/DdeFaceTracker.h" | 117 | #include "devices/DdeFaceTracker.h" |
| 115 | #include "devices/Faceshift.h" | 118 | #include "devices/Faceshift.h" |
| 116 | #include "devices/Leapmotion.h" | 119 | #include "devices/Leapmotion.h" |
| @@ -120,12 +123,6 @@ · 3 unmodified lines | |||
| 120 | #include "devices/OculusManager.h" | 123 | #include "devices/OculusManager.h" |
| 121 | #include "devices/TV3DManager.h" | 124 | #include "devices/TV3DManager.h" |
| 122 | 125 | ||
| 123 | #include "gpu/Batch.h" | ||
| 124 | #include "gpu/Context.h" | ||
| 125 | #include "gpu/GLBackend.h" | ||
| 126 | |||
| 127 | #include "RenderDeferredTask.h" | ||
| 128 | |||
| 129 | #include "scripting/AccountScriptingInterface.h" | 126 | #include "scripting/AccountScriptingInterface.h" |
| 130 | #include "scripting/AudioDeviceScriptingInterface.h" | 127 | #include "scripting/AudioDeviceScriptingInterface.h" |
| 131 | #include "scripting/ClipboardScriptingInterface.h" | 128 | #include "scripting/ClipboardScriptingInterface.h" |
| @@ -141,6 +138,7 @@ · 9 unmodified lines | |||
| 141 | #include "SpeechRecognizer.h" | 138 | #include "SpeechRecognizer.h" |
| 142 | #endif | 139 | #endif |
| 143 | 140 | ||
| 141 | #include "ui/AvatarInputs.h" | ||
| 144 | #include "ui/DataWebDialog.h" | 142 | #include "ui/DataWebDialog.h" |
| 145 | #include "ui/DialogsManager.h" | 143 | #include "ui/DialogsManager.h" |
| 146 | #include "ui/LoginDialog.h" | 144 | #include "ui/LoginDialog.h" |
| @@ -281,8 +279,6 @@ bool setupEssentials(int& argc, char** argv) { · 134 unmodified lines | |||
| 281 | auto animationCache = DependencyManager::set<AnimationCache>(); | 279 | auto animationCache = DependencyManager::set<AnimationCache>(); |
| 282 | auto ddeFaceTracker = DependencyManager::set<DdeFaceTracker>(); | 280 | auto ddeFaceTracker = DependencyManager::set<DdeFaceTracker>(); |
| 283 | auto modelBlender = DependencyManager::set<ModelBlender>(); | 281 | auto modelBlender = DependencyManager::set<ModelBlender>(); |
| 284 | auto audioToolBox = DependencyManager::set<AudioToolBox>(); | ||
| 285 | auto cameraToolBox = DependencyManager::set<CameraToolBox>(); | ||
| 286 | auto avatarManager = DependencyManager::set<AvatarManager>(); | 282 | auto avatarManager = DependencyManager::set<AvatarManager>(); |
| 287 | auto lodManager = DependencyManager::set<LODManager>(); | 283 | auto lodManager = DependencyManager::set<LODManager>(); |
| 288 | auto jsConsole = DependencyManager::set<StandAloneJSConsole>(); | 284 | auto jsConsole = DependencyManager::set<StandAloneJSConsole>(); |
| @@ -335,8 +331,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : · 46 unmodified lines | |||
| 335 | _cursorVisible(true), | 331 | _cursorVisible(true), |
| 336 | _lastMouseMove(usecTimestampNow()), | 332 | _lastMouseMove(usecTimestampNow()), |
| 337 | _lastMouseMoveWasSimulated(false), | 333 | _lastMouseMoveWasSimulated(false), |
| 338 | _touchAvgX(0.0f), | ||
| 339 | _touchAvgY(0.0f), | ||
| 340 | _isTouchPressed(false), | 334 | _isTouchPressed(false), |
| 341 | _mousePressed(false), | 335 | _mousePressed(false), |
| 342 | _enableProcessOctreeThread(true), | 336 | _enableProcessOctreeThread(true), |
| @@ -814,9 +808,9 @@ void Application::initializeGL() { · 471 unmodified lines | |||
| 814 | _idleLoopStdev.reset(); | 808 | _idleLoopStdev.reset(); |
| 815 | 809 | ||
| 816 | if (_justStarted) { | 810 | if (_justStarted) { |
| 817 | float startupTime = (float)_applicationStartupTime.elapsed() / 1000.0; | 811 | float startupTime = (float)_applicationStartupTime.elapsed() / 1000.0f; |
| 818 | _justStarted = false; | 812 | _justStarted = false; |
| 819 | qCDebug(interfaceapp, "Startup time: %4.2f seconds.", startupTime); | 813 | qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTime); |
| 820 | } | 814 | } |
| 821 | 815 | ||
| 822 | // update before the first render | 816 | // update before the first render |
| @@ -831,6 +825,7 @@ void Application::initializeUi() { · 8 unmodified lines | |||
| 831 | LoginDialog::registerType(); | 825 | LoginDialog::registerType(); |
| 832 | MessageDialog::registerType(); | 826 | MessageDialog::registerType(); |
| 833 | VrMenu::registerType(); | 827 | VrMenu::registerType(); |
| 828 | Tooltip::registerType(); | ||
| 834 | UpdateDialog::registerType(); | 829 | UpdateDialog::registerType(); |
| 835 | 830 | ||
| 836 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); | 831 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); |
| @@ -844,7 +839,7 @@ void Application::initializeUi() { · 7 unmodified lines | |||
| 844 | VrMenu::executeQueuedLambdas(); | 839 | VrMenu::executeQueuedLambdas(); |
| 845 | offscreenUi->setMouseTranslator([this](const QPointF& p){ | 840 | offscreenUi->setMouseTranslator([this](const QPointF& p){ |
| 846 | if (OculusManager::isConnected()) { | 841 | if (OculusManager::isConnected()) { |
| 847 | glm::vec2 pos = _applicationOverlay.screenToOverlay(toGlm(p)); | 842 | glm::vec2 pos = _compositor.screenToOverlay(toGlm(p)); |
| 848 | return QPointF(pos.x, pos.y); | 843 | return QPointF(pos.x, pos.y); |
| 849 | } | 844 | } |
| 850 | return QPointF(p); | 845 | return QPointF(p); |
| @@ -877,6 +872,7 @@ void Application::paintGL() { · 26 unmodified lines | |||
| 877 | OculusManager::beginFrameTiming(); | 872 | OculusManager::beginFrameTiming(); |
| 878 | } | 873 | } |
| 879 | 874 | ||
| 875 | |||
| 880 | PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings)); | 876 | PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings)); |
| 881 | bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); | 877 | bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); |
| 882 | PerformanceWarning warn(showWarnings, "Application::paintGL()"); | 878 | PerformanceWarning warn(showWarnings, "Application::paintGL()"); |
| @@ -884,6 +880,12 @@ void Application::paintGL() { · 1 unmodified line | |||
| 884 | 880 | ||
| 885 | { | 881 | { |
| 886 | PerformanceTimer perfTimer("renderOverlay"); | 882 | PerformanceTimer perfTimer("renderOverlay"); |
| 883 | /* | ||
| 884 | gpu::Context context(new gpu::GLBackend()); | ||
| 885 | RenderArgs renderArgs(&context, nullptr, getViewFrustum(), lodManager->getOctreeSizeScale(), | ||
| 886 | lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, | ||
| 887 | RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); | ||
| 888 | */ | ||
| 887 | _applicationOverlay.renderOverlay(&renderArgs); | 889 | _applicationOverlay.renderOverlay(&renderArgs); |
| 888 | } | 890 | } |
| 889 | 891 | ||
| @@ -912,13 +914,18 @@ void Application::paintGL() { · 22 unmodified lines | |||
| 912 | } | 914 | } |
| 913 | 915 | ||
| 914 | } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { | 916 | } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { |
| 915 | _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + | ||
| 916 | _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale()); | ||
| 917 | if (OculusManager::isConnected()) { | 917 | if (OculusManager::isConnected()) { |
| 918 | _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation()); | 918 | _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation()); |
| 919 | } else { | 919 | } else { |
| 920 | _myCamera.setRotation(_myAvatar->getHead()->getOrientation()); | 920 | _myCamera.setRotation(_myAvatar->getHead()->getOrientation()); |
| 921 | } | 921 | } |
| 922 | if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { | ||
| 923 | _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + | ||
| 924 | _myCamera.getRotation() * glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale()); | ||
| 925 | } else { | ||
| 926 | _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + | ||
| 927 | _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale()); | ||
| 928 | } | ||
| 922 | 929 | ||
| 923 | } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { | 930 | } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { |
| 924 | _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); | 931 | _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); |
| @@ -967,9 +974,7 @@ void Application::paintGL() { · 42 unmodified lines | |||
| 967 | glPopMatrix(); | 974 | glPopMatrix(); |
| 968 | 975 | ||
| 969 | renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; | 976 | renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; |
| 970 | if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { | 977 | if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { |
| 971 | _rearMirrorTools->render(&renderArgs, true, _glWidget->mapFromGlobal(QCursor::pos())); | ||
| 972 | } else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { | ||
| 973 | renderRearViewMirror(&renderArgs, _mirrorViewRect); | 978 | renderRearViewMirror(&renderArgs, _mirrorViewRect); |
| 974 | } | 979 | } |
| 975 | 980 | ||
| @@ -977,6 +982,7 @@ void Application::paintGL() { · 1 unmodified line | |||
| 977 | 982 | ||
| 978 | auto finalFbo = DependencyManager::get<GlowEffect>()->render(&renderArgs); | 983 | auto finalFbo = DependencyManager::get<GlowEffect>()->render(&renderArgs); |
| 979 | 984 | ||
| 985 | |||
| 980 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); | 986 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); |
| 981 | glBindFramebuffer(GL_READ_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo)); | 987 | glBindFramebuffer(GL_READ_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo)); |
| 982 | glBlitFramebuffer(0, 0, _renderResolution.x, _renderResolution.y, | 988 | glBlitFramebuffer(0, 0, _renderResolution.x, _renderResolution.y, |
| @@ -984,9 +990,10 @@ void Application::paintGL() { · 1 unmodified line | |||
| 984 | GL_COLOR_BUFFER_BIT, GL_NEAREST); | 990 | GL_COLOR_BUFFER_BIT, GL_NEAREST); |
| 985 | glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); | 991 | glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); |
| 986 | 992 | ||
| 987 | _applicationOverlay.displayOverlayTexture(); | 993 | _compositor.displayOverlayTexture(&renderArgs); |
| 988 | } | 994 | } |
| 989 | 995 | ||
| 996 | |||
| 990 | if (!OculusManager::isConnected() || OculusManager::allowSwap()) { | 997 | if (!OculusManager::isConnected() || OculusManager::allowSwap()) { |
| 991 | _glWidget->swapBuffers(); | 998 | _glWidget->swapBuffers(); |
| 992 | } | 999 | } |
| @@ -1044,27 +1051,24 @@ void Application::resizeGL() { · 51 unmodified lines | |||
| 1044 | } else { | 1051 | } else { |
| 1045 | renderSize = _glWidget->getDeviceSize() * getRenderResolutionScale(); | 1052 | renderSize = _glWidget->getDeviceSize() * getRenderResolutionScale(); |
| 1046 | } | 1053 | } |
| 1047 | if (_renderResolution == toGlm(renderSize)) { | ||
| 1048 | return; | ||
| 1049 | } | ||
| 1050 | 1054 | ||
| 1051 | _renderResolution = toGlm(renderSize); | 1055 | if (_renderResolution != toGlm(renderSize)) { |
| 1052 | DependencyManager::get<TextureCache>()->setFrameBufferSize(renderSize); | 1056 | _renderResolution = toGlm(renderSize); |
| 1053 | resetCamerasOnResizeGL(_myCamera, _renderResolution); | 1057 | DependencyManager::get<TextureCache>()->setFrameBufferSize(renderSize); |
| 1058 | resetCamerasOnResizeGL(_myCamera, _renderResolution); | ||
| 1054 | 1059 | ||
| 1055 | glViewport(0, 0, _renderResolution.x, _renderResolution.y); // shouldn't this account for the menu??? | 1060 | glViewport(0, 0, _renderResolution.x, _renderResolution.y); // shouldn't this account for the menu??? |
| 1056 | 1061 | ||
| 1057 | updateProjectionMatrix(); | 1062 | updateProjectionMatrix(); |
| 1058 | glLoadIdentity(); | 1063 | glLoadIdentity(); |
| 1064 | } | ||
| 1059 | 1065 | ||
| 1060 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); | 1066 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); |
| 1061 | offscreenUi->resize(_glWidget->size()); | 1067 | |
| 1068 | auto canvasSize = _glWidget->size(); | ||
| 1069 | offscreenUi->resize(canvasSize); | ||
| 1062 | _glWidget->makeCurrent(); | 1070 | _glWidget->makeCurrent(); |
| 1063 | 1071 | ||
| 1064 | // update Stats width | ||
| 1065 | // let's set horizontal offset to give stats some margin to mirror | ||
| 1066 | int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; | ||
| 1067 | Stats::getInstance()->resetWidth(_renderResolution.x, horizontalOffset); | ||
| 1068 | } | 1072 | } |
| 1069 | 1073 | ||
| 1070 | void Application::updateProjectionMatrix() { | 1074 | void Application::updateProjectionMatrix() { |
| @@ -1568,27 +1572,9 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { · 497 unmodified lines | |||
| 1568 | _keyboardMouseDevice.mousePressEvent(event); | 1572 | _keyboardMouseDevice.mousePressEvent(event); |
| 1569 | 1573 | ||
| 1570 | if (event->button() == Qt::LeftButton) { | 1574 | if (event->button() == Qt::LeftButton) { |
| 1571 | _mouseDragStartedX = getTrueMouseX(); | 1575 | _mouseDragStarted = getTrueMouse(); |
| 1572 | _mouseDragStartedY = getTrueMouseY(); | ||
| 1573 | _mousePressed = true; | 1576 | _mousePressed = true; |
| 1574 | 1577 | ||
| 1575 | if (mouseOnScreen()) { | ||
| 1576 | if (DependencyManager::get<AudioToolBox>()->mousePressEvent(getMouseX(), getMouseY())) { | ||
| 1577 | // stop propagation | ||
| 1578 | return; | ||
| 1579 | } | ||
| 1580 | |||
| 1581 | if (DependencyManager::get<CameraToolBox>()->mousePressEvent(getMouseX(), getMouseY())) { | ||
| 1582 | // stop propagation | ||
| 1583 | return; | ||
| 1584 | } | ||
| 1585 | |||
| 1586 | if (_rearMirrorTools->mousePressEvent(getMouseX(), getMouseY())) { | ||
| 1587 | // stop propagation | ||
| 1588 | return; | ||
| 1589 | } | ||
| 1590 | } | ||
| 1591 | |||
| 1592 | // nobody handled this - make it an action event on the _window object | 1578 | // nobody handled this - make it an action event on the _window object |
| 1593 | HFActionEvent actionEvent(HFActionEvent::startType(), | 1579 | HFActionEvent actionEvent(HFActionEvent::startType(), |
| 1594 | computePickRay(event->x(), event->y())); | 1580 | computePickRay(event->x(), event->y())); |
| @@ -1606,16 +1592,7 @@ void Application::mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceI · 11 unmodified lines | |||
| 1606 | return; | 1592 | return; |
| 1607 | } | 1593 | } |
| 1608 | 1594 | ||
| 1609 | if (activeWindow() == _window) { | 1595 | _controllerScriptingInterface.emitMouseDoublePressEvent(event); |
| 1610 | if (event->button() == Qt::LeftButton) { | ||
| 1611 | if (mouseOnScreen()) { | ||
| 1612 | if (DependencyManager::get<CameraToolBox>()->mouseDoublePressEvent(getMouseX(), getMouseY())) { | ||
| 1613 | // stop propagation | ||
| 1614 | return; | ||
| 1615 | } | ||
| 1616 | } | ||
| 1617 | } | ||
| 1618 | } | ||
| 1619 | } | 1596 | } |
| 1620 | 1597 | ||
| 1621 | void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { | 1598 | void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { |
| @@ -1637,13 +1614,6 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { · 15 unmodified lines | |||
| 1637 | if (event->button() == Qt::LeftButton) { | 1614 | if (event->button() == Qt::LeftButton) { |
| 1638 | _mousePressed = false; | 1615 | _mousePressed = false; |
| 1639 | 1616 | ||
| 1640 | if (Menu::getInstance()->isOptionChecked(MenuOption::Stats) && mouseOnScreen()) { | ||
| 1641 | // let's set horizontal offset to give stats some margin to mirror | ||
| 1642 | int horizontalOffset = MIRROR_VIEW_WIDTH; | ||
| 1643 | Stats::getInstance()->checkClick(getMouseX(), getMouseY(), | ||
| 1644 | getMouseDragStartedX(), getMouseDragStartedY(), horizontalOffset); | ||
| 1645 | } | ||
| 1646 | |||
| 1647 | // fire an action end event | 1617 | // fire an action end event |
| 1648 | HFActionEvent actionEvent(HFActionEvent::endType(), | 1618 | HFActionEvent actionEvent(HFActionEvent::endType(), |
| 1649 | computePickRay(event->x(), event->y())); | 1619 | computePickRay(event->x(), event->y())); |
| @@ -1671,22 +1641,18 @@ void Application::touchUpdateEvent(QTouchEvent* event) { · 21 unmodified lines | |||
| 1671 | bool validTouch = false; | 1641 | bool validTouch = false; |
| 1672 | if (activeWindow() == _window) { | 1642 | if (activeWindow() == _window) { |
| 1673 | const QList<QTouchEvent::TouchPoint>& tPoints = event->touchPoints(); | 1643 | const QList<QTouchEvent::TouchPoint>& tPoints = event->touchPoints(); |
| 1674 | _touchAvgX = 0.0f; | 1644 | _touchAvg = vec2(); |
| 1675 | _touchAvgY = 0.0f; | ||
| 1676 | int numTouches = tPoints.count(); | 1645 | int numTouches = tPoints.count(); |
| 1677 | if (numTouches > 1) { | 1646 | if (numTouches > 1) { |
| 1678 | for (int i = 0; i < numTouches; ++i) { | 1647 | for (int i = 0; i < numTouches; ++i) { |
| 1679 | _touchAvgX += tPoints[i].pos().x(); | 1648 | _touchAvg += toGlm(tPoints[i].pos()); |
| 1680 | _touchAvgY += tPoints[i].pos().y(); | ||
| 1681 | } | 1649 | } |
| 1682 | _touchAvgX /= (float)(numTouches); | 1650 | _touchAvg /= (float)(numTouches); |
| 1683 | _touchAvgY /= (float)(numTouches); | ||
| 1684 | validTouch = true; | 1651 | validTouch = true; |
| 1685 | } | 1652 | } |
| 1686 | } | 1653 | } |
| 1687 | if (!_isTouchPressed) { | 1654 | if (!_isTouchPressed) { |
| 1688 | _touchDragStartedAvgX = _touchAvgX; | 1655 | _touchDragStartedAvg = _touchAvg; |
| 1689 | _touchDragStartedAvgY = _touchAvgY; | ||
| 1690 | } | 1656 | } |
| 1691 | _isTouchPressed = validTouch; | 1657 | _isTouchPressed = validTouch; |
| 1692 | } | 1658 | } |
| @@ -1722,8 +1688,7 @@ void Application::touchEndEvent(QTouchEvent* event) { · 29 unmodified lines | |||
| 1722 | _keyboardMouseDevice.touchEndEvent(event); | 1688 | _keyboardMouseDevice.touchEndEvent(event); |
| 1723 | 1689 | ||
| 1724 | // put any application specific touch behavior below here.. | 1690 | // put any application specific touch behavior below here.. |
| 1725 | _touchDragStartedAvgX = _touchAvgX; | 1691 | _touchDragStartedAvg = _touchAvg; |
| 1726 | _touchDragStartedAvgY = _touchAvgY; | ||
| 1727 | _isTouchPressed = false; | 1692 | _isTouchPressed = false; |
| 1728 | 1693 | ||
| 1729 | } | 1694 | } |
| @@ -1872,9 +1837,9 @@ void Application::setFullscreen(bool fullscreen) { · 142 unmodified lines | |||
| 1872 | Menu::getInstance()->getActionForOption(MenuOption::Fullscreen)->setChecked(fullscreen); | 1837 | Menu::getInstance()->getActionForOption(MenuOption::Fullscreen)->setChecked(fullscreen); |
| 1873 | } | 1838 | } |
| 1874 | 1839 | ||
| 1875 | // The following code block is useful on platforms that can have a visible | 1840 | // The following code block is useful on platforms that can have a visible |
| 1876 | // app menu in a fullscreen window. However the OSX mechanism hides the | 1841 | // app menu in a fullscreen window. However the OSX mechanism hides the |
| 1877 | // application menu for fullscreen apps, so the check is not required. | 1842 | // application menu for fullscreen apps, so the check is not required. |
| 1878 | #ifndef Q_OS_MAC | 1843 | #ifndef Q_OS_MAC |
| 1879 | if (Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) { | 1844 | if (Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) { |
| 1880 | if (fullscreen) { | 1845 | if (fullscreen) { |
| @@ -1939,16 +1904,15 @@ void Application::setEnableVRMode(bool enableVRMode) { · 58 unmodified lines | |||
| 1939 | // attempt to reconnect the Oculus manager - it's possible this was a workaround | 1904 | // attempt to reconnect the Oculus manager - it's possible this was a workaround |
| 1940 | // for the sixense crash | 1905 | // for the sixense crash |
| 1941 | OculusManager::disconnect(); | 1906 | OculusManager::disconnect(); |
| 1942 | OculusManager::connect(); | 1907 | OculusManager::connect(_glWidget->context()->contextHandle()); |
| 1908 | _glWidget->setFocus(); | ||
| 1909 | _glWidget->makeCurrent(); | ||
| 1910 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 1943 | } | 1911 | } |
| 1944 | OculusManager::recalibrate(); | 1912 | OculusManager::recalibrate(); |
| 1945 | } else { | 1913 | } else { |
| 1946 | OculusManager::abandonCalibration(); | 1914 | OculusManager::abandonCalibration(); |
| 1947 | 1915 | OculusManager::disconnect(); | |
| 1948 | _mirrorCamera.setHmdPosition(glm::vec3()); | ||
| 1949 | _mirrorCamera.setHmdRotation(glm::quat()); | ||
| 1950 | _myCamera.setHmdPosition(glm::vec3()); | ||
| 1951 | _myCamera.setHmdRotation(glm::quat()); | ||
| 1952 | } | 1916 | } |
| 1953 | 1917 | ||
| 1954 | resizeGL(); | 1918 | resizeGL(); |
| @@ -1960,44 +1924,34 @@ void Application::setLowVelocityFilter(bool lowVelocityFilter) { · 5 unmodified lines | |||
| 1960 | 1924 | ||
| 1961 | bool Application::mouseOnScreen() const { | 1925 | bool Application::mouseOnScreen() const { |
| 1962 | if (OculusManager::isConnected()) { | 1926 | if (OculusManager::isConnected()) { |
| 1963 | return getMouseX() >= 0 && getMouseX() <= _glWidget->getDeviceWidth() && | 1927 | ivec2 mouse = getMouse(); |
| 1964 | getMouseY() >= 0 && getMouseY() <= _glWidget->getDeviceHeight(); | 1928 | if (!glm::all(glm::greaterThanEqual(mouse, ivec2()))) { |
| 1929 | return false; | ||
| 1930 | } | ||
| 1931 | ivec2 size = toGlm(_glWidget->getDeviceSize()); | ||
| 1932 | if (!glm::all(glm::lessThanEqual(mouse, size))) { | ||
| 1933 | return false; | ||
| 1934 | } | ||
| 1965 | } | 1935 | } |
| 1966 | return true; | 1936 | return true; |
| 1967 | } | 1937 | } |
| 1968 | 1938 | ||
| 1969 | int Application::getMouseX() const { | 1939 | ivec2 Application::getMouse() const { |
| 1970 | if (OculusManager::isConnected()) { | ||
| 1971 | glm::vec2 pos = _applicationOverlay.screenToOverlay(glm::vec2(getTrueMouseX(), getTrueMouseY())); | ||
| 1972 | return pos.x; | ||
| 1973 | } | ||
| 1974 | return getTrueMouseX(); | ||
| 1975 | } | ||
| 1976 | |||
| 1977 | int Application::getMouseY() const { | ||
| 1978 | if (OculusManager::isConnected()) { | 1940 | if (OculusManager::isConnected()) { |
| 1979 | glm::vec2 pos = _applicationOverlay.screenToOverlay(glm::vec2(getTrueMouseX(), getTrueMouseY())); | 1941 | return _compositor.screenToOverlay(getTrueMouse()); |
| 1980 | return pos.y; | ||
| 1981 | } | 1942 | } |
| 1982 | return getTrueMouseY(); | 1943 | return getTrueMouse(); |
| 1983 | } | 1944 | } |
| 1984 | 1945 | ||
| 1985 | int Application::getMouseDragStartedX() const { | 1946 | ivec2 Application::getMouseDragStarted() const { |
| 1986 | if (OculusManager::isConnected()) { | 1947 | if (OculusManager::isConnected()) { |
| 1987 | glm::vec2 pos = _applicationOverlay.screenToOverlay(glm::vec2(getTrueMouseDragStartedX(), | 1948 | return _compositor.screenToOverlay(getTrueMouseDragStarted()); |
| 1988 | getTrueMouseDragStartedY())); | ||
| 1989 | return pos.x; | ||
| 1990 | } | 1949 | } |
| 1991 | return getTrueMouseDragStartedX(); | 1950 | return getTrueMouseDragStarted(); |
| 1992 | } | 1951 | } |
| 1993 | 1952 | ||
| 1994 | int Application::getMouseDragStartedY() const { | 1953 | ivec2 Application::getTrueMouseDragStarted() const { |
| 1995 | if (OculusManager::isConnected()) { | 1954 | return _mouseDragStarted; |
| 1996 | glm::vec2 pos = _applicationOverlay.screenToOverlay(glm::vec2(getTrueMouseDragStartedX(), | ||
| 1997 | getTrueMouseDragStartedY())); | ||
| 1998 | return pos.y; | ||
| 1999 | } | ||
| 2000 | return getTrueMouseDragStartedY(); | ||
| 2001 | } | 1955 | } |
| 2002 | 1956 | ||
| 2003 | FaceTracker* Application::getActiveFaceTracker() { | 1957 | FaceTracker* Application::getActiveFaceTracker() { |
| @@ -2179,13 +2133,6 @@ void Application::init() { · 175 unmodified lines | |||
| 2179 | 2133 | ||
| 2180 | _mirrorCamera.setMode(CAMERA_MODE_MIRROR); | 2134 | _mirrorCamera.setMode(CAMERA_MODE_MIRROR); |
| 2181 | 2135 | ||
| 2182 | OculusManager::connect(); | ||
| 2183 | if (OculusManager::isConnected()) { | ||
| 2184 | QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen), | ||
| 2185 | "trigger", | ||
| 2186 | Qt::QueuedConnection); | ||
| 2187 | } | ||
| 2188 | |||
| 2189 | TV3DManager::connect(); | 2136 | TV3DManager::connect(); |
| 2190 | if (TV3DManager::isConnected()) { | 2137 | if (TV3DManager::isConnected()) { |
| 2191 | QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen), | 2138 | QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen), |
| @@ -2249,13 +2196,6 @@ void Application::init() { · 57 unmodified lines | |||
| 2249 | _entityClipboardRenderer.setViewFrustum(getViewFrustum()); | 2196 | _entityClipboardRenderer.setViewFrustum(getViewFrustum()); |
| 2250 | _entityClipboardRenderer.setTree(&_entityClipboard); | 2197 | _entityClipboardRenderer.setTree(&_entityClipboard); |
| 2251 | 2198 | ||
| 2252 | _rearMirrorTools = new RearMirrorTools(_mirrorViewRect); | ||
| 2253 | |||
| 2254 | connect(_rearMirrorTools, SIGNAL(closeView()), SLOT(closeMirrorView())); | ||
| 2255 | connect(_rearMirrorTools, SIGNAL(restoreView()), SLOT(restoreMirrorView())); | ||
| 2256 | connect(_rearMirrorTools, SIGNAL(shrinkView()), SLOT(shrinkMirrorView())); | ||
| 2257 | connect(_rearMirrorTools, SIGNAL(resetView()), SLOT(resetSensors())); | ||
| 2258 | |||
| 2259 | // initialize the GlowEffect with our widget | 2199 | // initialize the GlowEffect with our widget |
| 2260 | bool glow = Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect); | 2200 | bool glow = Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect); |
| 2261 | DependencyManager::get<GlowEffect>()->init(glow); | 2201 | DependencyManager::get<GlowEffect>()->init(glow); |
| @@ -2434,6 +2374,12 @@ void Application::cameraMenuChanged() { · 172 unmodified lines | |||
| 2434 | } | 2374 | } |
| 2435 | } | 2375 | } |
| 2436 | 2376 | ||
| 2377 | void Application::rotationModeChanged() { | ||
| 2378 | if (!Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { | ||
| 2379 | _myAvatar->setHeadPitch(0); | ||
| 2380 | } | ||
| 2381 | } | ||
| 2382 | |||
| 2437 | void Application::updateCamera(float deltaTime) { | 2383 | void Application::updateCamera(float deltaTime) { |
| 2438 | PerformanceTimer perfTimer("updateCamera"); | 2384 | PerformanceTimer perfTimer("updateCamera"); |
| 2439 | bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); | 2385 | bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); |
| @@ -3155,7 +3101,7 @@ PickRay Application::computePickRay(float x, float y) const { · 715 unmodified lines | |||
| 3155 | y /= size.y; | 3101 | y /= size.y; |
| 3156 | PickRay result; | 3102 | PickRay result; |
| 3157 | if (isHMDMode()) { | 3103 | if (isHMDMode()) { |
| 3158 | getApplicationOverlay().computeHmdPickRay(glm::vec2(x, y), result.origin, result.direction); | 3104 | getApplicationCompositor().computeHmdPickRay(glm::vec2(x, y), result.origin, result.direction); |
| 3159 | } else { | 3105 | } else { |
| 3160 | if (QThread::currentThread() == activeRenderingThread) { | 3106 | if (QThread::currentThread() == activeRenderingThread) { |
| 3161 | getDisplayViewFrustum()->computePickRay(x, y, result.origin, result.direction); | 3107 | getDisplayViewFrustum()->computePickRay(x, y, result.origin, result.direction); |
| @@ -3184,13 +3130,11 @@ QImage Application::renderAvatarBillboard(RenderArgs* renderArgs) { · 22 unmodified lines | |||
| 3184 | renderRearViewMirror(renderArgs, QRect(0, _glWidget->getDeviceHeight() - BILLBOARD_SIZE, | 3130 | renderRearViewMirror(renderArgs, QRect(0, _glWidget->getDeviceHeight() - BILLBOARD_SIZE, |
| 3185 | BILLBOARD_SIZE, BILLBOARD_SIZE), | 3131 | BILLBOARD_SIZE, BILLBOARD_SIZE), |
| 3186 | true); | 3132 | true); |
| 3187 | |||
| 3188 | QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32); | 3133 | QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32); |
| 3189 | glReadPixels(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE, GL_BGRA, GL_UNSIGNED_BYTE, image.bits()); | 3134 | glReadPixels(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE, GL_BGRA, GL_UNSIGNED_BYTE, image.bits()); |
| 3190 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); | 3135 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| 3191 | 3136 | ||
| 3192 | glBindFramebuffer(GL_FRAMEBUFFER, 0); | 3137 | glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| 3193 | |||
| 3194 | return image; | 3138 | return image; |
| 3195 | } | 3139 | } |
| 3196 | 3140 | ||
| @@ -3386,9 +3330,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 189 unmodified lines | |||
| 3386 | 3330 | ||
| 3387 | // flip x if in mirror mode (also requires reversing winding order for backface culling) | 3331 | // flip x if in mirror mode (also requires reversing winding order for backface culling) |
| 3388 | if (theCamera.getMode() == CAMERA_MODE_MIRROR) { | 3332 | if (theCamera.getMode() == CAMERA_MODE_MIRROR) { |
| 3389 | glScalef(-1.0f, 1.0f, 1.0f); | 3333 | //glScalef(-1.0f, 1.0f, 1.0f); |
| 3390 | glFrontFace(GL_CW); | 3334 | //glFrontFace(GL_CW); |
| 3391 | |||
| 3392 | } else { | 3335 | } else { |
| 3393 | glFrontFace(GL_CCW); | 3336 | glFrontFace(GL_CCW); |
| 3394 | } | 3337 | } |
| @@ -3411,7 +3354,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 16 unmodified lines | |||
| 3411 | viewTransform.setTranslation(theCamera.getPosition()); | 3354 | viewTransform.setTranslation(theCamera.getPosition()); |
| 3412 | viewTransform.setRotation(rotation); | 3355 | viewTransform.setRotation(rotation); |
| 3413 | if (theCamera.getMode() == CAMERA_MODE_MIRROR) { | 3356 | if (theCamera.getMode() == CAMERA_MODE_MIRROR) { |
| 3414 | viewTransform.setScale(Transform::Vec3(-1.0f, 1.0f, 1.0f)); | 3357 | // viewTransform.setScale(Transform::Vec3(-1.0f, 1.0f, 1.0f)); |
| 3415 | } | 3358 | } |
| 3416 | if (renderArgs->_renderSide != RenderArgs::MONO) { | 3359 | if (renderArgs->_renderSide != RenderArgs::MONO) { |
| 3417 | glm::mat4 invView = glm::inverse(_untranslatedViewMatrix); | 3360 | glm::mat4 invView = glm::inverse(_untranslatedViewMatrix); |
| @@ -3548,6 +3491,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 130 unmodified lines | |||
| 3548 | 3491 | ||
| 3549 | renderContext._maxDrawnOpaqueItems = sceneInterface->getEngineMaxDrawnOpaqueItems(); | 3492 | renderContext._maxDrawnOpaqueItems = sceneInterface->getEngineMaxDrawnOpaqueItems(); |
| 3550 | renderContext._maxDrawnTransparentItems = sceneInterface->getEngineMaxDrawnTransparentItems(); | 3493 | renderContext._maxDrawnTransparentItems = sceneInterface->getEngineMaxDrawnTransparentItems(); |
| 3494 | renderContext._maxDrawnOverlay3DItems = sceneInterface->getEngineMaxDrawnOverlay3DItems(); | ||
| 3551 | 3495 | ||
| 3552 | renderArgs->_shouldRender = LODManager::shouldRender; | 3496 | renderArgs->_shouldRender = LODManager::shouldRender; |
| 3553 | 3497 | ||
| @@ -3564,7 +3508,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 10 unmodified lines | |||
| 3564 | 3508 | ||
| 3565 | sceneInterface->setEngineFeedTransparentItems(engineRC->_numFeedTransparentItems); | 3509 | sceneInterface->setEngineFeedTransparentItems(engineRC->_numFeedTransparentItems); |
| 3566 | sceneInterface->setEngineDrawnTransparentItems(engineRC->_numDrawnTransparentItems); | 3510 | sceneInterface->setEngineDrawnTransparentItems(engineRC->_numDrawnTransparentItems); |
| 3567 | 3511 | ||
| 3512 | sceneInterface->setEngineFeedOverlay3DItems(engineRC->_numFeedOverlay3DItems); | ||
| 3513 | sceneInterface->setEngineDrawnOverlay3DItems(engineRC->_numDrawnOverlay3DItems); | ||
| 3568 | } | 3514 | } |
| 3569 | //Render the sixense lasers | 3515 | //Render the sixense lasers |
| 3570 | if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { | 3516 | if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { |
| @@ -3672,8 +3618,8 @@ glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) { · 101 unmodified lines | |||
| 3672 | // +-----------------------+ | 3618 | // +-----------------------+ |
| 3673 | // -1,-1 1,-1 | 3619 | // -1,-1 1,-1 |
| 3674 | 3620 | ||
| 3675 | glm::vec2 screenPoint((projectedPoint.x + 1.0) * horizontalScale, | 3621 | glm::vec2 screenPoint((projectedPoint.x + 1.0f) * horizontalScale, |
| 3676 | ((projectedPoint.y + 1.0) * -verticalScale) + _glWidget->getDeviceHeight()); | 3622 | ((projectedPoint.y + 1.0f) * -verticalScale) + _glWidget->getDeviceHeight()); |
| 3677 | 3623 | ||
| 3678 | return screenPoint; | 3624 | return screenPoint; |
| 3679 | } | 3625 | } |
| @@ -3691,7 +3637,7 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi · 11 unmodified lines | |||
| 3691 | _mirrorCamera.setPosition(_myAvatar->getPosition() + | 3637 | _mirrorCamera.setPosition(_myAvatar->getPosition() + |
| 3692 | _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * BILLBOARD_DISTANCE * _myAvatar->getScale()); | 3638 | _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * BILLBOARD_DISTANCE * _myAvatar->getScale()); |
| 3693 | 3639 | ||
| 3694 | } else if (RearMirrorTools::rearViewZoomLevel.get() == BODY) { | 3640 | } else if (!AvatarInputs::getInstance()->mirrorZoomed()) { |
| 3695 | _mirrorCamera.setPosition(_myAvatar->getChestPosition() + | 3641 | _mirrorCamera.setPosition(_myAvatar->getChestPosition() + |
| 3696 | _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); | 3642 | _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); |
| 3697 | 3643 | ||
| @@ -3726,7 +3672,7 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi · 28 unmodified lines | |||
| 3726 | } else { | 3672 | } else { |
| 3727 | // if not rendering the billboard, the region is in device independent coordinates; must convert to device | 3673 | // if not rendering the billboard, the region is in device independent coordinates; must convert to device |
| 3728 | QSize size = DependencyManager::get<TextureCache>()->getFrameBufferSize(); | 3674 | QSize size = DependencyManager::get<TextureCache>()->getFrameBufferSize(); |
| 3729 | float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale(); | 3675 | float ratio = (float)QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale(); |
| 3730 | int x = region.x() * ratio, y = region.y() * ratio, width = region.width() * ratio, height = region.height() * ratio; | 3676 | int x = region.x() * ratio, y = region.y() * ratio, width = region.width() * ratio, height = region.height() * ratio; |
| 3731 | glViewport(x, size.height() - y - height, width, height); | 3677 | glViewport(x, size.height() - y - height, width, height); |
| 3732 | glScissor(x, size.height() - y - height, width, height); | 3678 | glScissor(x, size.height() - y - height, width, height); |
| @@ -3741,10 +3687,6 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi · 8 unmodified lines | |||
| 3741 | displaySide(renderArgs, _mirrorCamera, true, billboard); | 3687 | displaySide(renderArgs, _mirrorCamera, true, billboard); |
| 3742 | glPopMatrix(); | 3688 | glPopMatrix(); |
| 3743 | 3689 | ||
| 3744 | if (!billboard) { | ||
| 3745 | _rearMirrorTools->render(renderArgs, false, _glWidget->mapFromGlobal(QCursor::pos())); | ||
| 3746 | } | ||
| 3747 | |||
| 3748 | // reset Viewport and projection matrix | 3690 | // reset Viewport and projection matrix |
| 3749 | glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); | 3691 | glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); |
| 3750 | glDisable(GL_SCISSOR_TEST); | 3692 | glDisable(GL_SCISSOR_TEST); |
| @@ -3884,7 +3826,7 @@ void Application::nodeKilled(SharedNodePointer node) { · 133 unmodified lines | |||
| 3884 | _entityServerJurisdictions.unlock(); | 3826 | _entityServerJurisdictions.unlock(); |
| 3885 | 3827 | ||
| 3886 | qCDebug(interfaceapp, "model server going away...... v[%f, %f, %f, %f]", | 3828 | qCDebug(interfaceapp, "model server going away...... v[%f, %f, %f, %f]", |
| 3887 | rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s); | 3829 | (double)rootDetails.x, (double)rootDetails.y, (double)rootDetails.z, (double)rootDetails.s); |
| 3888 | 3830 | ||
| 3889 | // Add the jurisditionDetails object to the list of "fade outs" | 3831 | // Add the jurisditionDetails object to the list of "fade outs" |
| 3890 | if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnOctreeServerChanges)) { | 3832 | if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnOctreeServerChanges)) { |
| @@ -3970,7 +3912,8 @@ int Application::parseOctreeStats(const QByteArray& packet, const SharedNodePoin · 79 unmodified lines | |||
| 3970 | jurisdiction->unlock(); | 3912 | jurisdiction->unlock(); |
| 3971 | 3913 | ||
| 3972 | qCDebug(interfaceapp, "stats from new %s server... [%f, %f, %f, %f]", | 3914 | qCDebug(interfaceapp, "stats from new %s server... [%f, %f, %f, %f]", |
| 3973 | qPrintable(serverType), rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s); | 3915 | qPrintable(serverType), |
| 3916 | (double)rootDetails.x, (double)rootDetails.y, (double)rootDetails.z, (double)rootDetails.s); | ||
| 3974 | 3917 | ||
| 3975 | // Add the jurisditionDetails object to the list of "fade outs" | 3918 | // Add the jurisditionDetails object to the list of "fade outs" |
| 3976 | if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnOctreeServerChanges)) { | 3919 | if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnOctreeServerChanges)) { |
| @@ -4750,11 +4693,9 @@ void Application::postLambdaEvent(std::function<void()> f) { · 773 unmodified lines | |||
| 4750 | } | 4693 | } |
| 4751 | 4694 | ||
| 4752 | void Application::initPlugins() { | 4695 | void Application::initPlugins() { |
| 4753 | OculusManager::init(); | ||
| 4754 | } | 4696 | } |
| 4755 | 4697 | ||
| 4756 | void Application::shutdownPlugins() { | 4698 | void Application::shutdownPlugins() { |
| 4757 | OculusManager::deinit(); | ||
| 4758 | } | 4699 | } |
| 4759 | 4700 | ||
| 4760 | glm::vec3 Application::getHeadPosition() const { | 4701 | glm::vec3 Application::getHeadPosition() const { |
| @@ -4774,12 +4715,8 @@ QSize Application::getDeviceSize() const { · 13 unmodified lines | |||
| 4774 | return _glWidget->getDeviceSize(); | 4715 | return _glWidget->getDeviceSize(); |
| 4775 | } | 4716 | } |
| 4776 | 4717 | ||
| 4777 | int Application::getTrueMouseX() const { | 4718 | ivec2 Application::getTrueMouse() const { |
| 4778 | return _glWidget->mapFromGlobal(QCursor::pos()).x(); | 4719 | return toGlm(_glWidget->mapFromGlobal(QCursor::pos())); |
| 4779 | } | ||
| 4780 | |||
| 4781 | int Application::getTrueMouseY() const { | ||
| 4782 | return _glWidget->mapFromGlobal(QCursor::pos()).y(); | ||
| 4783 | } | 4720 | } |
| 4784 | 4721 | ||
| 4785 | bool Application::isThrottleRendering() const { | 4722 | bool Application::isThrottleRendering() const { |
| @@ -4809,3 +4746,25 @@ qreal Application::getDevicePixelRatio() { · 23 unmodified lines | |||
| 4809 | return _window ? _window->windowHandle()->devicePixelRatio() : 1.0; | 4746 | return _window ? _window->windowHandle()->devicePixelRatio() : 1.0; |
| 4810 | } | 4747 | } |
| 4811 | 4748 | ||
| 4749 | mat4 Application::getEyeProjection(int eye) const { | ||
| 4750 | if (isHMDMode()) { | ||
| 4751 | return OculusManager::getEyeProjection(eye); | ||
| 4752 | } | ||
| 4753 | |||
| 4754 | return _viewFrustum.getProjection(); | ||
| 4755 | } | ||
| 4756 | |||
| 4757 | mat4 Application::getEyePose(int eye) const { | ||
| 4758 | if (isHMDMode()) { | ||
| 4759 | return OculusManager::getEyePose(eye); | ||
| 4760 | } | ||
| 4761 | |||
| 4762 | return mat4(); | ||
| 4763 | } | ||
| 4764 | |||
| 4765 | mat4 Application::getHeadPose() const { | ||
| 4766 | if (isHMDMode()) { | ||
| 4767 | return OculusManager::getHeadPose(); | ||
| 4768 | } | ||
| 4769 | return mat4(); | ||
| 4770 | } | ||
| @@ -62,12 +62,12 @@ · 61 unmodified lines | |||
| 62 | #include "ui/ModelsBrowser.h" | 62 | #include "ui/ModelsBrowser.h" |
| 63 | #include "ui/NodeBounds.h" | 63 | #include "ui/NodeBounds.h" |
| 64 | #include "ui/OctreeStatsDialog.h" | 64 | #include "ui/OctreeStatsDialog.h" |
| 65 | #include "ui/RearMirrorTools.h" | ||
| 66 | #include "ui/SnapshotShareDialog.h" | 65 | #include "ui/SnapshotShareDialog.h" |
| 67 | #include "ui/LodToolsDialog.h" | 66 | #include "ui/LodToolsDialog.h" |
| 68 | #include "ui/LogDialog.h" | 67 | #include "ui/LogDialog.h" |
| 69 | #include "ui/overlays/Overlays.h" | 68 | #include "ui/overlays/Overlays.h" |
| 70 | #include "ui/ApplicationOverlay.h" | 69 | #include "ui/ApplicationOverlay.h" |
| 70 | #include "ui/ApplicationCompositor.h" | ||
| 71 | #include "ui/RunningScriptsWidget.h" | 71 | #include "ui/RunningScriptsWidget.h" |
| 72 | #include "ui/ToolWindow.h" | 72 | #include "ui/ToolWindow.h" |
| 73 | #include "ui/UserInputMapper.h" | 73 | #include "ui/UserInputMapper.h" |
| @@ -221,15 +221,21 @@ public: · 147 unmodified lines | |||
| 221 | const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; } | 221 | const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; } |
| 222 | const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; } | 222 | const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; } |
| 223 | bool mouseOnScreen() const; | 223 | bool mouseOnScreen() const; |
| 224 | int getMouseX() const; | 224 | |
| 225 | int getMouseY() const; | 225 | ivec2 getMouse() const; |
| 226 | glm::ivec2 getTrueMousePosition() const; | 226 | ivec2 getTrueMouse() const; |
| 227 | int getTrueMouseX() const; | 227 | ivec2 getMouseDragStarted() const; |
| 228 | int getTrueMouseY() const; | 228 | ivec2 getTrueMouseDragStarted() const; |
| 229 | int getMouseDragStartedX() const; | 229 | |
| 230 | int getMouseDragStartedY() const; | 230 | // TODO get rid of these and use glm types directly |
| 231 | int getTrueMouseDragStartedX() const { return _mouseDragStartedX; } | 231 | int getMouseX() const { return getMouse().x; } |
| 232 | int getTrueMouseDragStartedY() const { return _mouseDragStartedY; } | 232 | int getMouseY() const { return getMouse().y; } |
| 233 | int getTrueMouseX() const { return getTrueMouse().x; } | ||
| 234 | int getTrueMouseY() const { return getTrueMouse().y; } | ||
| 235 | int getMouseDragStartedX() const { return getMouseDragStarted().x; } | ||
| 236 | int getMouseDragStartedY() const { return getMouseDragStarted().y; } | ||
| 237 | int getTrueMouseDragStartedX() const { return getTrueMouseDragStarted().x; } | ||
| 238 | int getTrueMouseDragStartedY() const { return getTrueMouseDragStarted().y; } | ||
| 233 | bool getLastMouseMoveWasSimulated() const { return _lastMouseMoveWasSimulated; } | 239 | bool getLastMouseMoveWasSimulated() const { return _lastMouseMoveWasSimulated; } |
| 234 | 240 | ||
| 235 | FaceTracker* getActiveFaceTracker(); | 241 | FaceTracker* getActiveFaceTracker(); |
| @@ -238,6 +244,8 @@ public: · 2 unmodified lines | |||
| 238 | QSystemTrayIcon* getTrayIcon() { return _trayIcon; } | 244 | QSystemTrayIcon* getTrayIcon() { return _trayIcon; } |
| 239 | ApplicationOverlay& getApplicationOverlay() { return _applicationOverlay; } | 245 | ApplicationOverlay& getApplicationOverlay() { return _applicationOverlay; } |
| 240 | const ApplicationOverlay& getApplicationOverlay() const { return _applicationOverlay; } | 246 | const ApplicationOverlay& getApplicationOverlay() const { return _applicationOverlay; } |
| 247 | ApplicationCompositor& getApplicationCompositor() { return _compositor; } | ||
| 248 | const ApplicationCompositor& getApplicationCompositor() const { return _compositor; } | ||
| 241 | Overlays& getOverlays() { return _overlays; } | 249 | Overlays& getOverlays() { return _overlays; } |
| 242 | 250 | ||
| 243 | float getFps() const { return _fps; } | 251 | float getFps() const { return _fps; } |
| @@ -330,6 +338,9 @@ public: · 86 unmodified lines | |||
| 330 | bool isHMDMode() const; | 338 | bool isHMDMode() const; |
| 331 | glm::quat getHeadOrientation() const; | 339 | glm::quat getHeadOrientation() const; |
| 332 | glm::vec3 getHeadPosition() const; | 340 | glm::vec3 getHeadPosition() const; |
| 341 | glm::mat4 getHeadPose() const; | ||
| 342 | glm::mat4 getEyePose(int eye) const; | ||
| 343 | glm::mat4 getEyeProjection(int eye) const; | ||
| 333 | 344 | ||
| 334 | QRect getDesirableApplicationGeometry(); | 345 | QRect getDesirableApplicationGeometry(); |
| 335 | RunningScriptsWidget* getRunningScriptsWidget() { return _runningScriptsWidget; } | 346 | RunningScriptsWidget* getRunningScriptsWidget() { return _runningScriptsWidget; } |
| @@ -451,6 +462,8 @@ private slots: · 115 unmodified lines | |||
| 451 | void setFullscreen(bool fullscreen); | 462 | void setFullscreen(bool fullscreen); |
| 452 | void setEnable3DTVMode(bool enable3DTVMode); | 463 | void setEnable3DTVMode(bool enable3DTVMode); |
| 453 | void setEnableVRMode(bool enableVRMode); | 464 | void setEnableVRMode(bool enableVRMode); |
| 465 | |||
| 466 | void rotationModeChanged(); | ||
| 454 | 467 | ||
| 455 | glm::vec2 getScaledScreenPoint(glm::vec2 projectedPoint); | 468 | glm::vec2 getScaledScreenPoint(glm::vec2 projectedPoint); |
| 456 | 469 | ||
| @@ -548,21 +561,17 @@ private: · 91 unmodified lines | |||
| 548 | 561 | ||
| 549 | OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers | 562 | OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers |
| 550 | 563 | ||
| 551 | KeyboardMouseDevice _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad | 564 | KeyboardMouseDevice _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad |
| 552 | 565 | UserInputMapper _userInputMapper; // User input mapper allowing to mapp different real devices to the action channels that the application has to offer | |
| 553 | UserInputMapper _userInputMapper; // User input mapper allowing to mapp different real devices to the action channels that the application has to offer | 566 | MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be) |
| 554 | 567 | Camera _myCamera; // My view onto the world | |
| 555 | MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be) | ||
| 556 | |||
| 557 | Camera _myCamera; // My view onto the world | ||
| 558 | Camera _mirrorCamera; // Cammera for mirror view | 568 | Camera _mirrorCamera; // Cammera for mirror view |
| 559 | QRect _mirrorViewRect; | 569 | QRect _mirrorViewRect; |
| 560 | RearMirrorTools* _rearMirrorTools; | ||
| 561 | 570 | ||
| 562 | Setting::Handle<bool> _firstRun; | 571 | Setting::Handle<bool> _firstRun; |
| 563 | Setting::Handle<QString> _previousScriptLocation; | 572 | Setting::Handle<QString> _previousScriptLocation; |
| 564 | Setting::Handle<QString> _scriptsLocationHandle; | 573 | Setting::Handle<QString> _scriptsLocationHandle; |
| 565 | Setting::Handle<float> _fieldOfView; | 574 | Setting::Handle<float> _fieldOfView; |
| 566 | 575 | ||
| 567 | Transform _viewTransform; | 576 | Transform _viewTransform; |
| 568 | glm::mat4 _untranslatedViewMatrix; | 577 | glm::mat4 _untranslatedViewMatrix; |
| @@ -580,18 +589,17 @@ private: · 11 unmodified lines | |||
| 580 | Environment _environment; | 589 | Environment _environment; |
| 581 | 590 | ||
| 582 | bool _cursorVisible; | 591 | bool _cursorVisible; |
| 583 | int _mouseDragStartedX; | 592 | ivec2 _mouseDragStarted; |
| 584 | int _mouseDragStartedY; | 593 | |
| 585 | quint64 _lastMouseMove; | 594 | quint64 _lastMouseMove; |
| 586 | bool _lastMouseMoveWasSimulated; | 595 | bool _lastMouseMoveWasSimulated; |
| 587 | 596 | ||
| 588 | glm::vec3 _mouseRayOrigin; | 597 | glm::vec3 _mouseRayOrigin; |
| 589 | glm::vec3 _mouseRayDirection; | 598 | glm::vec3 _mouseRayDirection; |
| 590 | 599 | ||
| 591 | float _touchAvgX; | 600 | vec2 _touchAvg; |
| 592 | float _touchAvgY; | 601 | vec2 _touchDragStartedAvg; |
| 593 | float _touchDragStartedAvgX; | 602 | |
| 594 | float _touchDragStartedAvgY; | ||
| 595 | bool _isTouchPressed; // true if multitouch has been pressed (clear when finished) | 603 | bool _isTouchPressed; // true if multitouch has been pressed (clear when finished) |
| 596 | 604 | ||
| 597 | bool _mousePressed; // true if mouse has been pressed (clear when finished) | 605 | bool _mousePressed; // true if mouse has been pressed (clear when finished) |
| @@ -669,6 +677,7 @@ private: · 71 unmodified lines | |||
| 669 | 677 | ||
| 670 | Overlays _overlays; | 678 | Overlays _overlays; |
| 671 | ApplicationOverlay _applicationOverlay; | 679 | ApplicationOverlay _applicationOverlay; |
| 680 | ApplicationCompositor _compositor; | ||
| 672 | }; | 681 | }; |
| 673 | 682 | ||
| 674 | #endif // hifi_Application_h | 683 | #endif // hifi_Application_h |
| @@ -49,8 +49,6 @@ Camera::Camera() : · 48 unmodified lines | |||
| 49 | _mode(CAMERA_MODE_THIRD_PERSON), | 49 | _mode(CAMERA_MODE_THIRD_PERSON), |
| 50 | _position(0.0f, 0.0f, 0.0f), | 50 | _position(0.0f, 0.0f, 0.0f), |
| 51 | _projection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), 16.0f/9.0f, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)), | 51 | _projection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), 16.0f/9.0f, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)), |
| 52 | _hmdPosition(), | ||
| 53 | _hmdRotation(), | ||
| 54 | _isKeepLookingAt(false), | 52 | _isKeepLookingAt(false), |
| 55 | _lookingAt(0.0f, 0.0f, 0.0f) | 53 | _lookingAt(0.0f, 0.0f, 0.0f) |
| 56 | { | 54 | { |
| @@ -74,20 +72,6 @@ void Camera::setRotation(const glm::quat& rotation) { · 17 unmodified lines | |||
| 74 | } | 72 | } |
| 75 | } | 73 | } |
| 76 | 74 | ||
| 77 | void Camera::setHmdPosition(const glm::vec3& hmdPosition) { | ||
| 78 | _hmdPosition = hmdPosition; | ||
| 79 | if (_isKeepLookingAt) { | ||
| 80 | lookAt(_lookingAt); | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | void Camera::setHmdRotation(const glm::quat& hmdRotation) { | ||
| 85 | _hmdRotation = hmdRotation; | ||
| 86 | if (_isKeepLookingAt) { | ||
| 87 | lookAt(_lookingAt); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | void Camera::setMode(CameraMode mode) { | 75 | void Camera::setMode(CameraMode mode) { |
| 92 | _mode = mode; | 76 | _mode = mode; |
| 93 | emit modeUpdated(modeToString(mode)); | 77 | emit modeUpdated(modeToString(mode)); |
| @@ -45,26 +45,22 @@ public: · 44 unmodified lines | |||
| 45 | 45 | ||
| 46 | void setRotation(const glm::quat& rotation); | 46 | void setRotation(const glm::quat& rotation); |
| 47 | void setProjection(const glm::mat4 & projection); | 47 | void setProjection(const glm::mat4 & projection); |
| 48 | void setHmdPosition(const glm::vec3& hmdPosition); | ||
| 49 | void setHmdRotation(const glm::quat& hmdRotation); | ||
| 50 | void setMode(CameraMode m); | 48 | void setMode(CameraMode m); |
| 51 | 49 | ||
| 52 | glm::quat getRotation() const { return _rotation * _hmdRotation; } | 50 | glm::quat getRotation() const { return _rotation; } |
| 53 | const glm::mat4& getProjection() const { return _projection; } | 51 | const glm::mat4& getProjection() const { return _projection; } |
| 54 | const glm::vec3& getHmdPosition() const { return _hmdPosition; } | ||
| 55 | const glm::quat& getHmdRotation() const { return _hmdRotation; } | ||
| 56 | CameraMode getMode() const { return _mode; } | 52 | CameraMode getMode() const { return _mode; } |
| 57 | 53 | ||
| 58 | public slots: | 54 | public slots: |
| 59 | QString getModeString() const; | 55 | QString getModeString() const; |
| 60 | void setModeString(const QString& mode); | 56 | void setModeString(const QString& mode); |
| 61 | 57 | ||
| 62 | glm::vec3 getPosition() const { return _position + _hmdPosition; } | 58 | glm::vec3 getPosition() const { return _position; } |
| 63 | void setPosition(const glm::vec3& position); | 59 | void setPosition(const glm::vec3& position); |
| 64 | 60 | ||
| 65 | void setOrientation(const glm::quat& orientation) { setRotation(orientation); } | ||
| 66 | glm::quat getOrientation() const { return getRotation(); } | 61 | glm::quat getOrientation() const { return getRotation(); } |
| 67 | 62 | void setOrientation(const glm::quat& orientation) { setRotation(orientation); } | |
| 63 | |||
| 68 | PickRay computePickRay(float x, float y); | 64 | PickRay computePickRay(float x, float y); |
| 69 | 65 | ||
| 70 | // These only work on independent cameras | 66 | // These only work on independent cameras |
| @@ -86,8 +82,6 @@ private: · 15 unmodified lines | |||
| 86 | glm::vec3 _position; | 82 | glm::vec3 _position; |
| 87 | glm::quat _rotation; | 83 | glm::quat _rotation; |
| 88 | glm::mat4 _projection; | 84 | glm::mat4 _projection; |
| 89 | glm::vec3 _hmdPosition; | ||
| 90 | glm::quat _hmdRotation; | ||
| 91 | bool _isKeepLookingAt; | 85 | bool _isKeepLookingAt; |
| 92 | glm::vec3 _lookingAt; | 86 | glm::vec3 _lookingAt; |
| 93 | }; | 87 | }; |
| @@ -39,11 +39,11 @@ bool GLCanvas::isThrottleRendering() const { · 38 unmodified lines | |||
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | int GLCanvas::getDeviceWidth() const { | 41 | int GLCanvas::getDeviceWidth() const { |
| 42 | return width() * (windowHandle() ? windowHandle()->devicePixelRatio() : 1.0f); | 42 | return width() * (windowHandle() ? (float)windowHandle()->devicePixelRatio() : 1.0f); |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | int GLCanvas::getDeviceHeight() const { | 45 | int GLCanvas::getDeviceHeight() const { |
| 46 | return height() * (windowHandle() ? windowHandle()->devicePixelRatio() : 1.0f); | 46 | return height() * (windowHandle() ? (float)windowHandle()->devicePixelRatio() : 1.0f); |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | void GLCanvas::initializeGL() { | 49 | void GLCanvas::initializeGL() { |
| @@ -12,7 +12,7 @@ · 11 unmodified lines | |||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | #include <avatar/AvatarActionHold.h> | 14 | #include <avatar/AvatarActionHold.h> |
| 15 | #include <ObjectActionPullToPoint.h> | 15 | #include <ObjectActionOffset.h> |
| 16 | #include <ObjectActionSpring.h> | 16 | #include <ObjectActionSpring.h> |
| 17 | 17 | ||
| 18 | #include "InterfaceActionFactory.h" | 18 | #include "InterfaceActionFactory.h" |
| @@ -27,8 +27,8 @@ EntityActionPointer InterfaceActionFactory::factory(EntitySimulation* simulation · 8 unmodified lines | |||
| 27 | switch (type) { | 27 | switch (type) { |
| 28 | case ACTION_TYPE_NONE: | 28 | case ACTION_TYPE_NONE: |
| 29 | return nullptr; | 29 | return nullptr; |
| 30 | case ACTION_TYPE_PULL_TO_POINT: | 30 | case ACTION_TYPE_OFFSET: |
| 31 | action = (EntityActionPointer) new ObjectActionPullToPoint(id, ownerEntity); | 31 | action = (EntityActionPointer) new ObjectActionOffset(id, ownerEntity); |
| 32 | break; | 32 | break; |
| 33 | case ACTION_TYPE_SPRING: | 33 | case ACTION_TYPE_SPRING: |
| 34 | action = (EntityActionPointer) new ObjectActionSpring(id, ownerEntity); | 34 | action = (EntityActionPointer) new ObjectActionSpring(id, ownerEntity); |
| @@ -205,11 +205,11 @@ QString LODManager::getLODFeedbackText() { · 204 unmodified lines | |||
| 205 | int relativeToTwentyTwenty = 20 / relativeToDefault; | 205 | int relativeToTwentyTwenty = 20 / relativeToDefault; |
| 206 | 206 | ||
| 207 | QString result; | 207 | QString result; |
| 208 | if (relativeToDefault > 1.01) { | 208 | if (relativeToDefault > 1.01f) { |
| 209 | result = QString("20:%1 or %2 times further than average vision%3").arg(relativeToTwentyTwenty).arg(relativeToDefault,0,'f',2).arg(granularityFeedback); | 209 | result = QString("20:%1 or %2 times further than average vision%3").arg(relativeToTwentyTwenty).arg(relativeToDefault,0,'f',2).arg(granularityFeedback); |
| 210 | } else if (relativeToDefault > 0.99) { | 210 | } else if (relativeToDefault > 0.99f) { |
| 211 | result = QString("20:20 or the default distance for average vision%1").arg(granularityFeedback); | 211 | result = QString("20:20 or the default distance for average vision%1").arg(granularityFeedback); |
| 212 | } else if (relativeToDefault > 0.01) { | 212 | } else if (relativeToDefault > 0.01f) { |
| 213 | result = QString("20:%1 or %2 of default distance for average vision%3").arg(relativeToTwentyTwenty).arg(relativeToDefault,0,'f',3).arg(granularityFeedback); | 213 | result = QString("20:%1 or %2 of default distance for average vision%3").arg(relativeToTwentyTwenty).arg(relativeToDefault,0,'f',3).arg(granularityFeedback); |
| 214 | } else { | 214 | } else { |
| 215 | result = QString("%2 of default distance for average vision%3").arg(relativeToDefault,0,'f',3).arg(granularityFeedback); | 215 | result = QString("%2 of default distance for average vision%3").arg(relativeToDefault,0,'f',3).arg(granularityFeedback); |
| @@ -287,6 +287,12 @@ Menu::Menu() { · 286 unmodified lines | |||
| 287 | addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, | 287 | addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, |
| 288 | 0, //QML Qt::SHIFT | Qt::Key_H, | 288 | 0, //QML Qt::SHIFT | Qt::Key_H, |
| 289 | true); | 289 | true); |
| 290 | addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, | ||
| 291 | 0, // QML Qt::Key_H, | ||
| 292 | false, qApp, SLOT(cameraMenuChanged())); | ||
| 293 | |||
| 294 | addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::CenterPlayerInView, | ||
| 295 | 0, false, qApp, SLOT(rotationModeChanged())); | ||
| 290 | 296 | ||
| 291 | addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools, | 297 | addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools, |
| 292 | #ifdef Q_OS_MAC | 298 | #ifdef Q_OS_MAC |
| @@ -201,6 +201,7 @@ namespace MenuOption { · 200 unmodified lines | |||
| 201 | const QString FrameTimer = "Show Timer"; | 201 | const QString FrameTimer = "Show Timer"; |
| 202 | const QString Fullscreen = "Fullscreen"; | 202 | const QString Fullscreen = "Fullscreen"; |
| 203 | const QString FullscreenMirror = "Fullscreen Mirror"; | 203 | const QString FullscreenMirror = "Fullscreen Mirror"; |
| 204 | const QString CenterPlayerInView = "Center Player In View"; | ||
| 204 | const QString GlowWhenSpeaking = "Glow When Speaking"; | 205 | const QString GlowWhenSpeaking = "Glow When Speaking"; |
| 205 | const QString NamesAboveHeads = "Names Above Heads"; | 206 | const QString NamesAboveHeads = "Names Above Heads"; |
| 206 | const QString HMDTools = "HMD Tools"; | 207 | const QString HMDTools = "HMD Tools"; |
| @@ -33,19 +33,19 @@ _basePath(basePath), · 32 unmodified lines | |||
| 33 | _geometry(geometry) | 33 | _geometry(geometry) |
| 34 | { | 34 | { |
| 35 | setWindowTitle("Set Model Properties"); | 35 | setWindowTitle("Set Model Properties"); |
| 36 | 36 | ||
| 37 | QFormLayout* form = new QFormLayout(); | 37 | QFormLayout* form = new QFormLayout(); |
| 38 | setLayout(form); | 38 | setLayout(form); |
| 39 | 39 | ||
| 40 | form->addRow("Name:", _name = new QLineEdit()); | 40 | form->addRow("Name:", _name = new QLineEdit()); |
| 41 | 41 | ||
| 42 | form->addRow("Texture Directory:", _textureDirectory = new QPushButton()); | 42 | form->addRow("Texture Directory:", _textureDirectory = new QPushButton()); |
| 43 | connect(_textureDirectory, SIGNAL(clicked(bool)), SLOT(chooseTextureDirectory())); | 43 | connect(_textureDirectory, SIGNAL(clicked(bool)), SLOT(chooseTextureDirectory())); |
| 44 | 44 | ||
| 45 | form->addRow("Scale:", _scale = new QDoubleSpinBox()); | 45 | form->addRow("Scale:", _scale = new QDoubleSpinBox()); |
| 46 | _scale->setMaximum(FLT_MAX); | 46 | _scale->setMaximum(FLT_MAX); |
| 47 | _scale->setSingleStep(0.01); | 47 | _scale->setSingleStep(0.01); |
| 48 | 48 | ||
| 49 | if (_modelType != FSTReader::ENTITY_MODEL) { | 49 | if (_modelType != FSTReader::ENTITY_MODEL) { |
| 50 | if (_modelType == FSTReader::ATTACHMENT_MODEL) { | 50 | if (_modelType == FSTReader::ATTACHMENT_MODEL) { |
| 51 | QHBoxLayout* translation = new QHBoxLayout(); | 51 | QHBoxLayout* translation = new QHBoxLayout(); |
| @@ -57,7 +57,7 @@ _geometry(geometry) · 5 unmodified lines | |||
| 57 | form->addRow("Pivot Joint:", _pivotJoint = createJointBox()); | 57 | form->addRow("Pivot Joint:", _pivotJoint = createJointBox()); |
| 58 | connect(_pivotAboutCenter, SIGNAL(toggled(bool)), SLOT(updatePivotJoint())); | 58 | connect(_pivotAboutCenter, SIGNAL(toggled(bool)), SLOT(updatePivotJoint())); |
| 59 | _pivotAboutCenter->setChecked(true); | 59 | _pivotAboutCenter->setChecked(true); |
| 60 | 60 | ||
| 61 | } else { | 61 | } else { |
| 62 | form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox()); | 62 | form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox()); |
| 63 | form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox()); | 63 | form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox()); |
| @@ -69,22 +69,22 @@ _geometry(geometry) · 5 unmodified lines | |||
| 69 | form->addRow("Head Joint:", _headJoint = createJointBox()); | 69 | form->addRow("Head Joint:", _headJoint = createJointBox()); |
| 70 | form->addRow("Left Hand Joint:", _leftHandJoint = createJointBox()); | 70 | form->addRow("Left Hand Joint:", _leftHandJoint = createJointBox()); |
| 71 | form->addRow("Right Hand Joint:", _rightHandJoint = createJointBox()); | 71 | form->addRow("Right Hand Joint:", _rightHandJoint = createJointBox()); |
| 72 | 72 | ||
| 73 | form->addRow("Free Joints:", _freeJoints = new QVBoxLayout()); | 73 | form->addRow("Free Joints:", _freeJoints = new QVBoxLayout()); |
| 74 | QPushButton* newFreeJoint = new QPushButton("New Free Joint"); | 74 | QPushButton* newFreeJoint = new QPushButton("New Free Joint"); |
| 75 | _freeJoints->addWidget(newFreeJoint); | 75 | _freeJoints->addWidget(newFreeJoint); |
| 76 | connect(newFreeJoint, SIGNAL(clicked(bool)), SLOT(createNewFreeJoint())); | 76 | connect(newFreeJoint, SIGNAL(clicked(bool)), SLOT(createNewFreeJoint())); |
| 77 | } | 77 | } |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | | 80 | QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | |
| 81 | QDialogButtonBox::Cancel | QDialogButtonBox::Reset); | 81 | QDialogButtonBox::Cancel | QDialogButtonBox::Reset); |
| 82 | connect(buttons, SIGNAL(accepted()), SLOT(accept())); | 82 | connect(buttons, SIGNAL(accepted()), SLOT(accept())); |
| 83 | connect(buttons, SIGNAL(rejected()), SLOT(reject())); | 83 | connect(buttons, SIGNAL(rejected()), SLOT(reject())); |
| 84 | connect(buttons->button(QDialogButtonBox::Reset), SIGNAL(clicked(bool)), SLOT(reset())); | 84 | connect(buttons->button(QDialogButtonBox::Reset), SIGNAL(clicked(bool)), SLOT(reset())); |
| 85 | 85 | ||
| 86 | form->addRow(buttons); | 86 | form->addRow(buttons); |
| 87 | 87 | ||
| 88 | // reset to initialize the fields | 88 | // reset to initialize the fields |
| 89 | reset(); | 89 | reset(); |
| 90 | } | 90 | } |
| @@ -100,42 +100,42 @@ QVariantHash ModelPropertiesDialog::getMapping() const { · 9 unmodified lines | |||
| 100 | mapping.insert(NAME_FIELD, _name->text()); | 100 | mapping.insert(NAME_FIELD, _name->text()); |
| 101 | mapping.insert(TEXDIR_FIELD, _textureDirectory->text()); | 101 | mapping.insert(TEXDIR_FIELD, _textureDirectory->text()); |
| 102 | mapping.insert(SCALE_FIELD, QString::number(_scale->value())); | 102 | mapping.insert(SCALE_FIELD, QString::number(_scale->value())); |
| 103 | 103 | ||
| 104 | // update the joint indices | 104 | // update the joint indices |
| 105 | QVariantHash jointIndices; | 105 | QVariantHash jointIndices; |
| 106 | for (int i = 0; i < _geometry.joints.size(); i++) { | 106 | for (int i = 0; i < _geometry.joints.size(); i++) { |
| 107 | jointIndices.insert(_geometry.joints.at(i).name, QString::number(i)); | 107 | jointIndices.insert(_geometry.joints.at(i).name, QString::number(i)); |
| 108 | } | 108 | } |
| 109 | mapping.insert(JOINT_INDEX_FIELD, jointIndices); | 109 | mapping.insert(JOINT_INDEX_FIELD, jointIndices); |
| 110 | 110 | ||
| 111 | if (_modelType != FSTReader::ENTITY_MODEL) { | 111 | if (_modelType != FSTReader::ENTITY_MODEL) { |
| 112 | QVariantHash joints = mapping.value(JOINT_FIELD).toHash(); | 112 | QVariantHash joints = mapping.value(JOINT_FIELD).toHash(); |
| 113 | if (_modelType == FSTReader::ATTACHMENT_MODEL) { | 113 | if (_modelType == FSTReader::ATTACHMENT_MODEL) { |
| 114 | glm::vec3 pivot; | 114 | glm::vec3 pivot; |
| 115 | if (_pivotAboutCenter->isChecked()) { | 115 | if (_pivotAboutCenter->isChecked()) { |
| 116 | pivot = (_geometry.meshExtents.minimum + _geometry.meshExtents.maximum) * 0.5f; | 116 | pivot = (_geometry.meshExtents.minimum + _geometry.meshExtents.maximum) * 0.5f; |
| 117 | 117 | ||
| 118 | } else if (_pivotJoint->currentIndex() != 0) { | 118 | } else if (_pivotJoint->currentIndex() != 0) { |
| 119 | pivot = extractTranslation(_geometry.joints.at(_pivotJoint->currentIndex() - 1).transform); | 119 | pivot = extractTranslation(_geometry.joints.at(_pivotJoint->currentIndex() - 1).transform); |
| 120 | } | 120 | } |
| 121 | mapping.insert(TRANSLATION_X_FIELD, -pivot.x * _scale->value() + _translationX->value()); | 121 | mapping.insert(TRANSLATION_X_FIELD, -pivot.x * (float)_scale->value() + (float)_translationX->value()); |
| 122 | mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * _scale->value() + _translationY->value()); | 122 | mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * (float)_scale->value() + (float)_translationY->value()); |
| 123 | mapping.insert(TRANSLATION_Z_FIELD, -pivot.z * _scale->value() + _translationZ->value()); | 123 | mapping.insert(TRANSLATION_Z_FIELD, -pivot.z * (float)_scale->value() + (float)_translationZ->value()); |
| 124 | 124 | ||
| 125 | } else { | 125 | } else { |
| 126 | insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText()); | 126 | insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText()); |
| 127 | insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText()); | 127 | insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText()); |
| 128 | insertJointMapping(joints, "jointNeck", _neckJoint->currentText()); | 128 | insertJointMapping(joints, "jointNeck", _neckJoint->currentText()); |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | 131 | ||
| 132 | if (_modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL) { | 132 | if (_modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL) { |
| 133 | insertJointMapping(joints, "jointRoot", _rootJoint->currentText()); | 133 | insertJointMapping(joints, "jointRoot", _rootJoint->currentText()); |
| 134 | insertJointMapping(joints, "jointLean", _leanJoint->currentText()); | 134 | insertJointMapping(joints, "jointLean", _leanJoint->currentText()); |
| 135 | insertJointMapping(joints, "jointHead", _headJoint->currentText()); | 135 | insertJointMapping(joints, "jointHead", _headJoint->currentText()); |
| 136 | insertJointMapping(joints, "jointLeftHand", _leftHandJoint->currentText()); | 136 | insertJointMapping(joints, "jointLeftHand", _leftHandJoint->currentText()); |
| 137 | insertJointMapping(joints, "jointRightHand", _rightHandJoint->currentText()); | 137 | insertJointMapping(joints, "jointRightHand", _rightHandJoint->currentText()); |
| 138 | 138 | ||
| 139 | mapping.remove(FREE_JOINT_FIELD); | 139 | mapping.remove(FREE_JOINT_FIELD); |
| 140 | for (int i = 0; i < _freeJoints->count() - 1; i++) { | 140 | for (int i = 0; i < _freeJoints->count() - 1; i++) { |
| 141 | QComboBox* box = static_cast<QComboBox*>(_freeJoints->itemAt(i)->widget()->layout()->itemAt(0)->widget()); | 141 | QComboBox* box = static_cast<QComboBox*>(_freeJoints->itemAt(i)->widget()->layout()->itemAt(0)->widget()); |
| @@ -144,7 +144,7 @@ QVariantHash ModelPropertiesDialog::getMapping() const { · 2 unmodified lines | |||
| 144 | } | 144 | } |
| 145 | mapping.insert(JOINT_FIELD, joints); | 145 | mapping.insert(JOINT_FIELD, joints); |
| 146 | } | 146 | } |
| 147 | 147 | ||
| 148 | return mapping; | 148 | return mapping; |
| 149 | } | 149 | } |
| 150 | 150 | ||
| @@ -156,9 +156,9 @@ void ModelPropertiesDialog::reset() { · 5 unmodified lines | |||
| 156 | _name->setText(_originalMapping.value(NAME_FIELD).toString()); | 156 | _name->setText(_originalMapping.value(NAME_FIELD).toString()); |
| 157 | _textureDirectory->setText(_originalMapping.value(TEXDIR_FIELD).toString()); | 157 | _textureDirectory->setText(_originalMapping.value(TEXDIR_FIELD).toString()); |
| 158 | _scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble()); | 158 | _scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble()); |
| 159 | 159 | ||
| 160 | QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash(); | 160 | QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash(); |
| 161 | 161 | ||
| 162 | if (_modelType != FSTReader::ENTITY_MODEL) { | 162 | if (_modelType != FSTReader::ENTITY_MODEL) { |
| 163 | if (_modelType == FSTReader::ATTACHMENT_MODEL) { | 163 | if (_modelType == FSTReader::ATTACHMENT_MODEL) { |
| 164 | _translationX->setValue(_originalMapping.value(TRANSLATION_X_FIELD).toDouble()); | 164 | _translationX->setValue(_originalMapping.value(TRANSLATION_X_FIELD).toDouble()); |
| @@ -166,20 +166,20 @@ void ModelPropertiesDialog::reset() { · 1 unmodified line | |||
| 166 | _translationZ->setValue(_originalMapping.value(TRANSLATION_Z_FIELD).toDouble()); | 166 | _translationZ->setValue(_originalMapping.value(TRANSLATION_Z_FIELD).toDouble()); |
| 167 | _pivotAboutCenter->setChecked(true); | 167 | _pivotAboutCenter->setChecked(true); |
| 168 | _pivotJoint->setCurrentIndex(0); | 168 | _pivotJoint->setCurrentIndex(0); |
| 169 | 169 | ||
| 170 | } else { | 170 | } else { |
| 171 | setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString()); | 171 | setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString()); |
| 172 | setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString()); | 172 | setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString()); |
| 173 | setJointText(_neckJoint, jointHash.value("jointNeck").toString()); | 173 | setJointText(_neckJoint, jointHash.value("jointNeck").toString()); |
| 174 | } | 174 | } |
| 175 | 175 | ||
| 176 | if (_modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL) { | 176 | if (_modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL) { |
| 177 | setJointText(_rootJoint, jointHash.value("jointRoot").toString()); | 177 | setJointText(_rootJoint, jointHash.value("jointRoot").toString()); |
| 178 | setJointText(_leanJoint, jointHash.value("jointLean").toString()); | 178 | setJointText(_leanJoint, jointHash.value("jointLean").toString()); |
| 179 | setJointText(_headJoint, jointHash.value("jointHead").toString()); | 179 | setJointText(_headJoint, jointHash.value("jointHead").toString()); |
| 180 | setJointText(_leftHandJoint, jointHash.value("jointLeftHand").toString()); | 180 | setJointText(_leftHandJoint, jointHash.value("jointLeftHand").toString()); |
| 181 | setJointText(_rightHandJoint, jointHash.value("jointRightHand").toString()); | 181 | setJointText(_rightHandJoint, jointHash.value("jointRightHand").toString()); |
| 182 | 182 | ||
| 183 | while (_freeJoints->count() > 1) { | 183 | while (_freeJoints->count() > 1) { |
| 184 | delete _freeJoints->itemAt(0)->widget(); | 184 | delete _freeJoints->itemAt(0)->widget(); |
| 185 | } | 185 | } |
| @@ -16,13 +16,13 @@ · 15 unmodified lines | |||
| 16 | #include "InterfaceConfig.h" | 16 | #include "InterfaceConfig.h" |
| 17 | #include "starfield/Controller.h" | 17 | #include "starfield/Controller.h" |
| 18 | 18 | ||
| 19 | Stars::Stars() : | 19 | Stars::Stars() : |
| 20 | _controller(0l), _starsLoaded(false) { | 20 | _controller(0l), _starsLoaded(false) { |
| 21 | _controller = new starfield::Controller; | 21 | _controller = new starfield::Controller; |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | Stars::~Stars() { | 24 | Stars::~Stars() { |
| 25 | delete _controller; | 25 | delete _controller; |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | bool Stars::generate(unsigned numStars, unsigned seed) { | 28 | bool Stars::generate(unsigned numStars, unsigned seed) { |
| @@ -30,22 +30,20 @@ bool Stars::generate(unsigned numStars, unsigned seed) { · 1 unmodified line | |||
| 30 | return _starsLoaded; | 30 | return _starsLoaded; |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | bool Stars::setResolution(unsigned k) { | 33 | bool Stars::setResolution(unsigned k) { |
| 34 | return _controller->setResolution(k); | 34 | return _controller->setResolution(k); |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | void Stars::render(float fovY, float aspect, float nearZ, float alpha) { | 37 | void Stars::render(float fovY, float aspect, float nearZ, float alpha) { |
| 38 | // determine length of screen diagonal from quadrant height and aspect ratio | 38 | // determine length of screen diagonal from quadrant height and aspect ratio |
| 39 | float quadrantHeight = nearZ * tan(RADIANS_PER_DEGREE * fovY * 0.5f); | 39 | float quadrantHeight = nearZ * tanf(RADIANS_PER_DEGREE * fovY * 0.5f); |
| 40 | float halfDiagonal = sqrt(quadrantHeight * quadrantHeight * (1.0f + aspect * aspect)); | 40 | float halfDiagonal = sqrt(quadrantHeight * quadrantHeight * (1.0f + aspect * aspect)); |
| 41 | 41 | ||
| 42 | // determine fov angle in respect to the diagonal | 42 | // determine fov angle in respect to the diagonal |
| 43 | float fovDiagonal = atan(halfDiagonal / nearZ) * 2.0f; | 43 | float fovDiagonal = atanf(halfDiagonal / nearZ) * 2.0f; |
| 44 | 44 | ||
| 45 | // pull the modelview matrix off the GL stack | 45 | // pull the modelview matrix off the GL stack |
| 46 | glm::mat4 view; glGetFloatv(GL_MODELVIEW_MATRIX, glm::value_ptr(view)); | 46 | glm::mat4 view; glGetFloatv(GL_MODELVIEW_MATRIX, glm::value_ptr(view)); |
| 47 | 47 | ||
| 48 | _controller->render(fovDiagonal, aspect, glm::affineInverse(view), alpha); | 48 | _controller->render(fovDiagonal, aspect, glm::affineInverse(view), alpha); |
| 49 | } | 49 | } |
| 50 | |||
| 51 | |||
| @@ -70,6 +70,6 @@ void UIUtil::internalScaleWidgetFontSizes(QWidget* widget, float scale) { · 69 unmodified lines | |||
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | QFont font = widget->font(); | 72 | QFont font = widget->font(); |
| 73 | font.setPointSizeF(font.pointSizeF() * scale); | 73 | font.setPointSizeF(font.pointSizeF() * (double)scale); |
| 74 | widget->setFont(font); | 74 | widget->setFont(font); |
| 75 | } | 75 | } |
| @@ -41,7 +41,7 @@ void renderWorldBox(gpu::Batch& batch) { · 40 unmodified lines | |||
| 41 | static const glm::vec3 green(0.0f, 1.0f, 0.0f); | 41 | static const glm::vec3 green(0.0f, 1.0f, 0.0f); |
| 42 | static const glm::vec3 blue(0.0f, 0.0f, 1.0f); | 42 | static const glm::vec3 blue(0.0f, 0.0f, 1.0f); |
| 43 | static const glm::vec3 grey(0.5f, 0.5f, 0.5f); | 43 | static const glm::vec3 grey(0.5f, 0.5f, 0.5f); |
| 44 | 44 | ||
| 45 | auto transform = Transform{}; | 45 | auto transform = Transform{}; |
| 46 | batch.setModelTransform(transform); | 46 | batch.setModelTransform(transform); |
| 47 | geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(TREE_SCALE, 0.0f, 0.0f), red); | 47 | geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(TREE_SCALE, 0.0f, 0.0f), red); |
| @@ -49,13 +49,13 @@ void renderWorldBox(gpu::Batch& batch) { · 1 unmodified line | |||
| 49 | geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, TREE_SCALE), blue); | 49 | geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, TREE_SCALE), blue); |
| 50 | geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, TREE_SCALE), glm::vec3(TREE_SCALE, 0.0f, TREE_SCALE), grey); | 50 | geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, TREE_SCALE), glm::vec3(TREE_SCALE, 0.0f, TREE_SCALE), grey); |
| 51 | geometryCache->renderLine(batch, glm::vec3(TREE_SCALE, 0.0f, TREE_SCALE), glm::vec3(TREE_SCALE, 0.0f, 0.0f), grey); | 51 | geometryCache->renderLine(batch, glm::vec3(TREE_SCALE, 0.0f, TREE_SCALE), glm::vec3(TREE_SCALE, 0.0f, 0.0f), grey); |
| 52 | 52 | ||
| 53 | // Draw meter markers along the 3 axis to help with measuring things | 53 | // Draw meter markers along the 3 axis to help with measuring things |
| 54 | const float MARKER_DISTANCE = 1.0f; | 54 | const float MARKER_DISTANCE = 1.0f; |
| 55 | const float MARKER_RADIUS = 0.05f; | 55 | const float MARKER_RADIUS = 0.05f; |
| 56 | 56 | ||
| 57 | geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, red); | 57 | geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, red); |
| 58 | 58 | ||
| 59 | transform.setTranslation(glm::vec3(MARKER_DISTANCE, 0.0f, 0.0f)); | 59 | transform.setTranslation(glm::vec3(MARKER_DISTANCE, 0.0f, 0.0f)); |
| 60 | batch.setModelTransform(transform); | 60 | batch.setModelTransform(transform); |
| 61 | geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, red); | 61 | geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, red); |
| @@ -79,10 +79,10 @@ const glm::vec3 randVector() { · 17 unmodified lines | |||
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | static TextRenderer* textRenderer(int mono) { | 81 | static TextRenderer* textRenderer(int mono) { |
| 82 | static TextRenderer* monoRenderer = TextRenderer::getInstance(MONO_FONT_FAMILY); | 82 | static TextRenderer* monoRenderer = TextRenderer::getInstance(MONO_FONT_FAMILY); |
| 83 | static TextRenderer* proportionalRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, | 83 | static TextRenderer* proportionalRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, |
| 84 | -1, -1, false, TextRenderer::SHADOW_EFFECT); | 84 | -1, -1, false, TextRenderer::SHADOW_EFFECT); |
| 85 | static TextRenderer* inconsolataRenderer = TextRenderer::getInstance(INCONSOLATA_FONT_FAMILY, -1, INCONSOLATA_FONT_WEIGHT, | 85 | static TextRenderer* inconsolataRenderer = TextRenderer::getInstance(INCONSOLATA_FONT_FAMILY, -1, INCONSOLATA_FONT_WEIGHT, |
| 86 | false); | 86 | false); |
| 87 | switch (mono) { | 87 | switch (mono) { |
| 88 | case 1: | 88 | case 1: |
| @@ -133,46 +133,46 @@ void runTimingTests() { · 44 unmodified lines | |||
| 133 | QElapsedTimer startTime; | 133 | QElapsedTimer startTime; |
| 134 | startTime.start(); | 134 | startTime.start(); |
| 135 | float elapsedUsecs; | 135 | float elapsedUsecs; |
| 136 | 136 | ||
| 137 | float NSEC_TO_USEC = 1.0f / 1000.0f; | 137 | float NSEC_TO_USEC = 1.0f / 1000.0f; |
| 138 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; | 138 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; |
| 139 | qCDebug(interfaceapp, "QElapsedTimer::nsecElapsed() usecs: %f", elapsedUsecs); | 139 | qCDebug(interfaceapp, "QElapsedTimer::nsecElapsed() usecs: %f", (double)elapsedUsecs); |
| 140 | 140 | ||
| 141 | // Test sleep functions for accuracy | 141 | // Test sleep functions for accuracy |
| 142 | startTime.start(); | 142 | startTime.start(); |
| 143 | QThread::msleep(1); | 143 | QThread::msleep(1); |
| 144 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; | 144 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; |
| 145 | qCDebug(interfaceapp, "QThread::msleep(1) ms: %f", elapsedUsecs / 1000.0f); | 145 | qCDebug(interfaceapp, "QThread::msleep(1) ms: %f", (double)(elapsedUsecs / 1000.0f)); |
| 146 | 146 | ||
| 147 | startTime.start(); | 147 | startTime.start(); |
| 148 | QThread::sleep(1); | 148 | QThread::sleep(1); |
| 149 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; | 149 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; |
| 150 | qCDebug(interfaceapp, "QThread::sleep(1) ms: %f", elapsedUsecs / 1000.0f); | 150 | qCDebug(interfaceapp, "QThread::sleep(1) ms: %f", (double)(elapsedUsecs / 1000.0f)); |
| 151 | 151 | ||
| 152 | startTime.start(); | 152 | startTime.start(); |
| 153 | usleep(1); | 153 | usleep(1); |
| 154 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; | 154 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; |
| 155 | qCDebug(interfaceapp, "usleep(1) ms: %f", elapsedUsecs / 1000.0f); | 155 | qCDebug(interfaceapp, "usleep(1) ms: %f", (double)(elapsedUsecs / 1000.0f)); |
| 156 | 156 | ||
| 157 | startTime.start(); | 157 | startTime.start(); |
| 158 | usleep(10); | 158 | usleep(10); |
| 159 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; | 159 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; |
| 160 | qCDebug(interfaceapp, "usleep(10) ms: %f", elapsedUsecs / 1000.0f); | 160 | qCDebug(interfaceapp, "usleep(10) ms: %f", (double)(elapsedUsecs / 1000.0f)); |
| 161 | 161 | ||
| 162 | startTime.start(); | 162 | startTime.start(); |
| 163 | usleep(100); | 163 | usleep(100); |
| 164 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; | 164 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; |
| 165 | qCDebug(interfaceapp, "usleep(100) ms: %f", elapsedUsecs / 1000.0f); | 165 | qCDebug(interfaceapp, "usleep(100) ms: %f", (double)(elapsedUsecs / 1000.0f)); |
| 166 | 166 | ||
| 167 | startTime.start(); | 167 | startTime.start(); |
| 168 | usleep(1000); | 168 | usleep(1000); |
| 169 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; | 169 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; |
| 170 | qCDebug(interfaceapp, "usleep(1000) ms: %f", elapsedUsecs / 1000.0f); | 170 | qCDebug(interfaceapp, "usleep(1000) ms: %f", (double)(elapsedUsecs / 1000.0f)); |
| 171 | 171 | ||
| 172 | startTime.start(); | 172 | startTime.start(); |
| 173 | usleep(15000); | 173 | usleep(15000); |
| 174 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; | 174 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; |
| 175 | qCDebug(interfaceapp, "usleep(15000) ms: %f", elapsedUsecs / 1000.0f); | 175 | qCDebug(interfaceapp, "usleep(15000) ms: %f", (double)(elapsedUsecs / 1000.0f)); |
| 176 | 176 | ||
| 177 | // Random number generation | 177 | // Random number generation |
| 178 | startTime.start(); | 178 | startTime.start(); |
| @@ -180,7 +180,8 @@ void runTimingTests() { · 1 unmodified line | |||
| 180 | iResults[i] = rand(); | 180 | iResults[i] = rand(); |
| 181 | } | 181 | } |
| 182 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; | 182 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; |
| 183 | qCDebug(interfaceapp, "rand() stored in array usecs: %f, first result:%d", elapsedUsecs / (float) numTests, iResults[0]); | 183 | qCDebug(interfaceapp, "rand() stored in array usecs: %f, first result:%d", |
| 184 | (double)(elapsedUsecs / numTests), iResults[0]); | ||
| 184 | 185 | ||
| 185 | // Random number generation using randFloat() | 186 | // Random number generation using randFloat() |
| 186 | startTime.start(); | 187 | startTime.start(); |
| @@ -188,7 +189,8 @@ void runTimingTests() { · 1 unmodified line | |||
| 188 | fResults[i] = randFloat(); | 189 | fResults[i] = randFloat(); |
| 189 | } | 190 | } |
| 190 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; | 191 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; |
| 191 | qCDebug(interfaceapp, "randFloat() stored in array usecs: %f, first result: %f", elapsedUsecs / (float) numTests, fResults[0]); | 192 | qCDebug(interfaceapp, "randFloat() stored in array usecs: %f, first result: %f", |
| 193 | (double)(elapsedUsecs / numTests), (double)(fResults[0])); | ||
| 192 | 194 | ||
| 193 | free(iResults); | 195 | free(iResults); |
| 194 | free(fResults); | 196 | free(fResults); |
| @@ -200,7 +202,7 @@ void runTimingTests() { · 5 unmodified lines | |||
| 200 | fTest = powf(fTest, 0.5f); | 202 | fTest = powf(fTest, 0.5f); |
| 201 | } | 203 | } |
| 202 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; | 204 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; |
| 203 | qCDebug(interfaceapp, "powf(f, 0.5) usecs: %f", elapsedUsecs / (float) numTests); | 205 | qCDebug(interfaceapp, "powf(f, 0.5) usecs: %f", (double)(elapsedUsecs / (float) numTests)); |
| 204 | 206 | ||
| 205 | // Vector Math | 207 | // Vector Math |
| 206 | float distance; | 208 | float distance; |
| @@ -213,19 +215,20 @@ void runTimingTests() { · 6 unmodified lines | |||
| 213 | } | 215 | } |
| 214 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; | 216 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; |
| 215 | qCDebug(interfaceapp, "vector math usecs: %f [%f usecs total for %d tests], last result:%f", | 217 | qCDebug(interfaceapp, "vector math usecs: %f [%f usecs total for %d tests], last result:%f", |
| 216 | elapsedUsecs / (float) numTests, elapsedUsecs, numTests, distance); | 218 | (double)(elapsedUsecs / (float) numTests), (double)elapsedUsecs, numTests, (double)distance); |
| 217 | 219 | ||
| 218 | // Vec3 test | 220 | // Vec3 test |
| 219 | glm::vec3 vecA(randVector()), vecB(randVector()); | 221 | glm::vec3 vecA(randVector()), vecB(randVector()); |
| 220 | float result; | 222 | float result; |
| 221 | 223 | ||
| 222 | startTime.start(); | 224 | startTime.start(); |
| 223 | for (int i = 0; i < numTests; i++) { | 225 | for (int i = 0; i < numTests; i++) { |
| 224 | glm::vec3 temp = vecA-vecB; | 226 | glm::vec3 temp = vecA-vecB; |
| 225 | result = glm::dot(temp,temp); | 227 | result = glm::dot(temp,temp); |
| 226 | } | 228 | } |
| 227 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; | 229 | elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; |
| 228 | qCDebug(interfaceapp, "vec3 assign and dot() usecs: %f, last result:%f", elapsedUsecs / (float) numTests, result); | 230 | qCDebug(interfaceapp, "vec3 assign and dot() usecs: %f, last result:%f", |
| 231 | (double)(elapsedUsecs / numTests), (double)result); | ||
| 229 | } | 232 | } |
| 230 | 233 | ||
| 231 | bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNormalizedDirection, | 234 | bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNormalizedDirection, |
| @@ -41,32 +41,32 @@ void AudioIOStatsRenderer::render(const float* color, int width, int height) { · 40 unmodified lines | |||
| 41 | if (!_isEnabled) { | 41 | if (!_isEnabled) { |
| 42 | return; | 42 | return; |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | const int linesWhenCentered = _shouldShowInjectedStreams ? 34 : 27; | 45 | const int linesWhenCentered = _shouldShowInjectedStreams ? 34 : 27; |
| 46 | const int CENTERED_BACKGROUND_HEIGHT = STATS_HEIGHT_PER_LINE * linesWhenCentered; | 46 | const int CENTERED_BACKGROUND_HEIGHT = STATS_HEIGHT_PER_LINE * linesWhenCentered; |
| 47 | 47 | ||
| 48 | int lines = _shouldShowInjectedStreams ? _stats->getMixerInjectedStreamStatsMap().size() * 7 + 27 : 27; | 48 | int lines = _shouldShowInjectedStreams ? _stats->getMixerInjectedStreamStatsMap().size() * 7 + 27 : 27; |
| 49 | int statsHeight = STATS_HEIGHT_PER_LINE * lines; | 49 | int statsHeight = STATS_HEIGHT_PER_LINE * lines; |
| 50 | 50 | ||
| 51 | 51 | ||
| 52 | static const glm::vec4 backgroundColor = { 0.2f, 0.2f, 0.2f, 0.6f }; | 52 | static const glm::vec4 backgroundColor = { 0.2f, 0.2f, 0.2f, 0.6f }; |
| 53 | int x = std::max((width - (int)STATS_WIDTH) / 2, 0); | 53 | int x = std::max((width - (int)STATS_WIDTH) / 2, 0); |
| 54 | int y = std::max((height - CENTERED_BACKGROUND_HEIGHT) / 2, 0); | 54 | int y = std::max((height - CENTERED_BACKGROUND_HEIGHT) / 2, 0); |
| 55 | int w = STATS_WIDTH; | 55 | int w = STATS_WIDTH; |
| 56 | int h = statsHeight; | 56 | int h = statsHeight; |
| 57 | DependencyManager::get<GeometryCache>()->renderQuad(x, y, w, h, backgroundColor); | 57 | DependencyManager::get<GeometryCache>()->renderQuad(x, y, w, h, backgroundColor); |
| 58 | 58 | ||
| 59 | int horizontalOffset = x + 5; | 59 | int horizontalOffset = x + 5; |
| 60 | int verticalOffset = y; | 60 | int verticalOffset = y; |
| 61 | 61 | ||
| 62 | float scale = 0.10f; | 62 | float scale = 0.10f; |
| 63 | float rotation = 0.0f; | 63 | float rotation = 0.0f; |
| 64 | int font = 2; | 64 | int font = 2; |
| 65 | 65 | ||
| 66 | char latencyStatString[512]; | 66 | char latencyStatString[512]; |
| 67 | 67 | ||
| 68 | float audioInputBufferLatency = 0.0f, inputRingBufferLatency = 0.0f, networkRoundtripLatency = 0.0f, mixerRingBufferLatency = 0.0f, outputRingBufferLatency = 0.0f, audioOutputBufferLatency = 0.0f; | 68 | float audioInputBufferLatency = 0.0f, inputRingBufferLatency = 0.0f, networkRoundtripLatency = 0.0f, mixerRingBufferLatency = 0.0f, outputRingBufferLatency = 0.0f, audioOutputBufferLatency = 0.0f; |
| 69 | 69 | ||
| 70 | AudioStreamStats downstreamAudioStreamStats = _stats->getMixerDownstreamStats(); | 70 | AudioStreamStats downstreamAudioStreamStats = _stats->getMixerDownstreamStats(); |
| 71 | SharedNodePointer audioMixerNodePointer = DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::AudioMixer); | 71 | SharedNodePointer audioMixerNodePointer = DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::AudioMixer); |
| 72 | if (!audioMixerNodePointer.isNull()) { | 72 | if (!audioMixerNodePointer.isNull()) { |
| @@ -78,48 +78,62 @@ void AudioIOStatsRenderer::render(const float* color, int width, int height) { · 5 unmodified lines | |||
| 78 | audioOutputBufferLatency = _stats->getAudioOutputMsecsUnplayedStats().getWindowAverage(); | 78 | audioOutputBufferLatency = _stats->getAudioOutputMsecsUnplayedStats().getWindowAverage(); |
| 79 | } | 79 | } |
| 80 | float totalLatency = audioInputBufferLatency + inputRingBufferLatency + networkRoundtripLatency + mixerRingBufferLatency + outputRingBufferLatency + audioOutputBufferLatency; | 80 | float totalLatency = audioInputBufferLatency + inputRingBufferLatency + networkRoundtripLatency + mixerRingBufferLatency + outputRingBufferLatency + audioOutputBufferLatency; |
| 81 | 81 | ||
| 82 | sprintf(latencyStatString, " Audio input buffer: %7.2fms - avg msecs of samples read to the input ring buffer in last 10s", audioInputBufferLatency); | 82 | sprintf(latencyStatString, |
| 83 | " Audio input buffer: %7.2fms - avg msecs of samples read to the input ring buffer in last 10s", | ||
| 84 | (double)audioInputBufferLatency); | ||
| 83 | verticalOffset += STATS_HEIGHT_PER_LINE; | 85 | verticalOffset += STATS_HEIGHT_PER_LINE; |
| 84 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); | 86 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); |
| 85 | 87 | ||
| 86 | sprintf(latencyStatString, " Input ring buffer: %7.2fms - avg msecs of samples in input ring buffer in last 10s", inputRingBufferLatency); | 88 | sprintf(latencyStatString, |
| 89 | " Input ring buffer: %7.2fms - avg msecs of samples in input ring buffer in last 10s", | ||
| 90 | (double)inputRingBufferLatency); | ||
| 87 | verticalOffset += STATS_HEIGHT_PER_LINE; | 91 | verticalOffset += STATS_HEIGHT_PER_LINE; |
| 88 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); | 92 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); |
| 89 | 93 | ||
| 90 | sprintf(latencyStatString, " Network to mixer: %7.2fms - half of last ping value calculated by the node list", networkRoundtripLatency / 2.0f); | 94 | sprintf(latencyStatString, |
| 95 | " Network to mixer: %7.2fms - half of last ping value calculated by the node list", | ||
| 96 | (double)(networkRoundtripLatency / 2.0f)); | ||
| 91 | verticalOffset += STATS_HEIGHT_PER_LINE; | 97 | verticalOffset += STATS_HEIGHT_PER_LINE; |
| 92 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); | 98 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); |
| 93 | 99 | ||
| 94 | sprintf(latencyStatString, " AudioMixer ring buffer: %7.2fms - avg msecs of samples in audio mixer's ring buffer in last 10s", mixerRingBufferLatency); | 100 | sprintf(latencyStatString, |
| 101 | " AudioMixer ring buffer: %7.2fms - avg msecs of samples in audio mixer's ring buffer in last 10s", | ||
| 102 | (double)mixerRingBufferLatency); | ||
| 95 | verticalOffset += STATS_HEIGHT_PER_LINE; | 103 | verticalOffset += STATS_HEIGHT_PER_LINE; |
| 96 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); | 104 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); |
| 97 | 105 | ||
| 98 | sprintf(latencyStatString, " Network to client: %7.2fms - half of last ping value calculated by the node list", networkRoundtripLatency / 2.0f); | 106 | sprintf(latencyStatString, |
| 107 | " Network to client: %7.2fms - half of last ping value calculated by the node list", | ||
| 108 | (double)(networkRoundtripLatency / 2.0f)); | ||
| 99 | verticalOffset += STATS_HEIGHT_PER_LINE; | 109 | verticalOffset += STATS_HEIGHT_PER_LINE; |
| 100 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); | 110 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); |
| 101 | 111 | ||
| 102 | sprintf(latencyStatString, " Output ring buffer: %7.2fms - avg msecs of samples in output ring buffer in last 10s", outputRingBufferLatency); | 112 | sprintf(latencyStatString, |
| 113 | " Output ring buffer: %7.2fms - avg msecs of samples in output ring buffer in last 10s", | ||
| 114 | (double)outputRingBufferLatency); | ||
| 103 | verticalOffset += STATS_HEIGHT_PER_LINE; | 115 | verticalOffset += STATS_HEIGHT_PER_LINE; |
| 104 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); | 116 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); |
| 105 | 117 | ||
| 106 | sprintf(latencyStatString, " Audio output buffer: %7.2fms - avg msecs of samples in audio output buffer in last 10s", audioOutputBufferLatency); | 118 | sprintf(latencyStatString, |
| 119 | " Audio output buffer: %7.2fms - avg msecs of samples in audio output buffer in last 10s", | ||
| 120 | (double)audioOutputBufferLatency); | ||
| 107 | verticalOffset += STATS_HEIGHT_PER_LINE; | 121 | verticalOffset += STATS_HEIGHT_PER_LINE; |
| 108 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); | 122 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); |
| 109 | 123 | ||
| 110 | sprintf(latencyStatString, " TOTAL: %7.2fms\n", totalLatency); | 124 | sprintf(latencyStatString, " TOTAL: %7.2fms\n", (double)totalLatency); |
| 111 | verticalOffset += STATS_HEIGHT_PER_LINE; | 125 | verticalOffset += STATS_HEIGHT_PER_LINE; |
| 112 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); | 126 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color); |
| 113 | 127 | ||
| 114 | 128 | ||
| 115 | verticalOffset += STATS_HEIGHT_PER_LINE; // blank line | 129 | verticalOffset += STATS_HEIGHT_PER_LINE; // blank line |
| 116 | 130 | ||
| 117 | char clientUpstreamMicLabelString[] = "Upstream Mic Audio Packets Sent Gaps (by client):"; | 131 | char clientUpstreamMicLabelString[] = "Upstream Mic Audio Packets Sent Gaps (by client):"; |
| 118 | verticalOffset += STATS_HEIGHT_PER_LINE; | 132 | verticalOffset += STATS_HEIGHT_PER_LINE; |
| 119 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, clientUpstreamMicLabelString, color); | 133 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, clientUpstreamMicLabelString, color); |
| 120 | 134 | ||
| 121 | const MovingMinMaxAvg<quint64>& packetSentTimeGaps = _stats->getPacketSentTimeGaps(); | 135 | const MovingMinMaxAvg<quint64>& packetSentTimeGaps = _stats->getPacketSentTimeGaps(); |
| 122 | 136 | ||
| 123 | char stringBuffer[512]; | 137 | char stringBuffer[512]; |
| 124 | sprintf(stringBuffer, " Inter-packet timegaps (overall) | min: %9s, max: %9s, avg: %9s", | 138 | sprintf(stringBuffer, " Inter-packet timegaps (overall) | min: %9s, max: %9s, avg: %9s", |
| 125 | formatUsecTime(packetSentTimeGaps.getMin()).toLatin1().data(), | 139 | formatUsecTime(packetSentTimeGaps.getMin()).toLatin1().data(), |
| @@ -127,47 +141,47 @@ void AudioIOStatsRenderer::render(const float* color, int width, int height) { · 1 unmodified line | |||
| 127 | formatUsecTime(packetSentTimeGaps.getAverage()).toLatin1().data()); | 141 | formatUsecTime(packetSentTimeGaps.getAverage()).toLatin1().data()); |
| 128 | verticalOffset += STATS_HEIGHT_PER_LINE; | 142 | verticalOffset += STATS_HEIGHT_PER_LINE; |
| 129 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); | 143 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); |
| 130 | 144 | ||
| 131 | sprintf(stringBuffer, " Inter-packet timegaps (last 30s) | min: %9s, max: %9s, avg: %9s", | 145 | sprintf(stringBuffer, " Inter-packet timegaps (last 30s) | min: %9s, max: %9s, avg: %9s", |
| 132 | formatUsecTime(packetSentTimeGaps.getWindowMin()).toLatin1().data(), | 146 | formatUsecTime(packetSentTimeGaps.getWindowMin()).toLatin1().data(), |
| 133 | formatUsecTime(packetSentTimeGaps.getWindowMax()).toLatin1().data(), | 147 | formatUsecTime(packetSentTimeGaps.getWindowMax()).toLatin1().data(), |
| 134 | formatUsecTime(packetSentTimeGaps.getWindowAverage()).toLatin1().data()); | 148 | formatUsecTime(packetSentTimeGaps.getWindowAverage()).toLatin1().data()); |
| 135 | verticalOffset += STATS_HEIGHT_PER_LINE; | 149 | verticalOffset += STATS_HEIGHT_PER_LINE; |
| 136 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); | 150 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); |
| 137 | 151 | ||
| 138 | verticalOffset += STATS_HEIGHT_PER_LINE; // blank line | 152 | verticalOffset += STATS_HEIGHT_PER_LINE; // blank line |
| 139 | 153 | ||
| 140 | char upstreamMicLabelString[] = "Upstream mic audio stats (received and reported by audio-mixer):"; | 154 | char upstreamMicLabelString[] = "Upstream mic audio stats (received and reported by audio-mixer):"; |
| 141 | verticalOffset += STATS_HEIGHT_PER_LINE; | 155 | verticalOffset += STATS_HEIGHT_PER_LINE; |
| 142 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamMicLabelString, color); | 156 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamMicLabelString, color); |
| 143 | 157 | ||
| 144 | renderAudioStreamStats(&_stats->getMixerAvatarStreamStats(), horizontalOffset, verticalOffset, | 158 | renderAudioStreamStats(&_stats->getMixerAvatarStreamStats(), horizontalOffset, verticalOffset, |
| 145 | scale, rotation, font, color); | 159 | scale, rotation, font, color); |
| 146 | 160 | ||
| 147 | 161 | ||
| 148 | verticalOffset += STATS_HEIGHT_PER_LINE; // blank line | 162 | verticalOffset += STATS_HEIGHT_PER_LINE; // blank line |
| 149 | 163 | ||
| 150 | char downstreamLabelString[] = "Downstream mixed audio stats:"; | 164 | char downstreamLabelString[] = "Downstream mixed audio stats:"; |
| 151 | verticalOffset += STATS_HEIGHT_PER_LINE; | 165 | verticalOffset += STATS_HEIGHT_PER_LINE; |
| 152 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, downstreamLabelString, color); | 166 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, downstreamLabelString, color); |
| 153 | 167 | ||
| 154 | AudioStreamStats downstreamStats = _stats->getMixerDownstreamStats(); | 168 | AudioStreamStats downstreamStats = _stats->getMixerDownstreamStats(); |
| 155 | renderAudioStreamStats(&downstreamStats, horizontalOffset, verticalOffset, | 169 | renderAudioStreamStats(&downstreamStats, horizontalOffset, verticalOffset, |
| 156 | scale, rotation, font, color, true); | 170 | scale, rotation, font, color, true); |
| 157 | 171 | ||
| 158 | 172 | ||
| 159 | if (_shouldShowInjectedStreams) { | 173 | if (_shouldShowInjectedStreams) { |
| 160 | 174 | ||
| 161 | foreach(const AudioStreamStats& injectedStreamAudioStats, _stats->getMixerInjectedStreamStatsMap()) { | 175 | foreach(const AudioStreamStats& injectedStreamAudioStats, _stats->getMixerInjectedStreamStatsMap()) { |
| 162 | 176 | ||
| 163 | verticalOffset += STATS_HEIGHT_PER_LINE; // blank line | 177 | verticalOffset += STATS_HEIGHT_PER_LINE; // blank line |
| 164 | 178 | ||
| 165 | char upstreamInjectedLabelString[512]; | 179 | char upstreamInjectedLabelString[512]; |
| 166 | sprintf(upstreamInjectedLabelString, "Upstream injected audio stats: stream ID: %s", | 180 | sprintf(upstreamInjectedLabelString, "Upstream injected audio stats: stream ID: %s", |
| 167 | injectedStreamAudioStats._streamIdentifier.toString().toLatin1().data()); | 181 | injectedStreamAudioStats._streamIdentifier.toString().toLatin1().data()); |
| 168 | verticalOffset += STATS_HEIGHT_PER_LINE; | 182 | verticalOffset += STATS_HEIGHT_PER_LINE; |
| 169 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamInjectedLabelString, color); | 183 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamInjectedLabelString, color); |
| 170 | 184 | ||
| 171 | renderAudioStreamStats(&injectedStreamAudioStats, horizontalOffset, verticalOffset, scale, rotation, font, color); | 185 | renderAudioStreamStats(&injectedStreamAudioStats, horizontalOffset, verticalOffset, scale, rotation, font, color); |
| 172 | } | 186 | } |
| 173 | } | 187 | } |
| @@ -175,22 +189,22 @@ void AudioIOStatsRenderer::render(const float* color, int width, int height) { · 1 unmodified line | |||
| 175 | 189 | ||
| 176 | void AudioIOStatsRenderer::renderAudioStreamStats(const AudioStreamStats* streamStats, int horizontalOffset, int& verticalOffset, | 190 | void AudioIOStatsRenderer::renderAudioStreamStats(const AudioStreamStats* streamStats, int horizontalOffset, int& verticalOffset, |
| 177 | float scale, float rotation, int font, const float* color, bool isDownstreamStats) { | 191 | float scale, float rotation, int font, const float* color, bool isDownstreamStats) { |
| 178 | 192 | ||
| 179 | char stringBuffer[512]; | 193 | char stringBuffer[512]; |
| 180 | 194 | ||
| 181 | sprintf(stringBuffer, " Packet loss | overall: %5.2f%% (%d lost), last_30s: %5.2f%% (%d lost)", | 195 | sprintf(stringBuffer, " Packet loss | overall: %5.2f%% (%d lost), last_30s: %5.2f%% (%d lost)", |
| 182 | streamStats->_packetStreamStats.getLostRate() * 100.0f, | 196 | (double)(streamStats->_packetStreamStats.getLostRate() * 100.0f), |
| 183 | streamStats->_packetStreamStats._lost, | 197 | streamStats->_packetStreamStats._lost, |
| 184 | streamStats->_packetStreamWindowStats.getLostRate() * 100.0f, | 198 | (double)(streamStats->_packetStreamWindowStats.getLostRate() * 100.0f), |
| 185 | streamStats->_packetStreamWindowStats._lost); | 199 | streamStats->_packetStreamWindowStats._lost); |
| 186 | verticalOffset += STATS_HEIGHT_PER_LINE; | 200 | verticalOffset += STATS_HEIGHT_PER_LINE; |
| 187 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); | 201 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); |
| 188 | 202 | ||
| 189 | if (isDownstreamStats) { | 203 | if (isDownstreamStats) { |
| 190 | sprintf(stringBuffer, " Ringbuffer frames | desired: %u, avg_available(10s): %u+%d, available: %u+%d", | 204 | sprintf(stringBuffer, " Ringbuffer frames | desired: %u, avg_available(10s): %u+%d, available: %u+%d", |
| 191 | streamStats->_desiredJitterBufferFrames, | 205 | streamStats->_desiredJitterBufferFrames, |
| 192 | streamStats->_framesAvailableAverage, | 206 | streamStats->_framesAvailableAverage, |
| 193 | (int)(_stats->getAudioInputMsecsReadStats().getWindowAverage() / AudioConstants::NETWORK_FRAME_MSECS), | 207 | (int)((float)_stats->getAudioInputMsecsReadStats().getWindowAverage() / AudioConstants::NETWORK_FRAME_MSECS), |
| 194 | streamStats->_framesAvailable, | 208 | streamStats->_framesAvailable, |
| 195 | (int)(_stats->getAudioOutputMsecsUnplayedStats().getCurrentIntervalLastSample() | 209 | (int)(_stats->getAudioOutputMsecsUnplayedStats().getCurrentIntervalLastSample() |
| 196 | / AudioConstants::NETWORK_FRAME_MSECS)); | 210 | / AudioConstants::NETWORK_FRAME_MSECS)); |
| @@ -200,10 +214,10 @@ void AudioIOStatsRenderer::renderAudioStreamStats(const AudioStreamStats* stream · 3 unmodified lines | |||
| 200 | streamStats->_framesAvailableAverage, | 214 | streamStats->_framesAvailableAverage, |
| 201 | streamStats->_framesAvailable); | 215 | streamStats->_framesAvailable); |
| 202 | } | 216 | } |
| 203 | 217 | ||
| 204 | verticalOffset += STATS_HEIGHT_PER_LINE; | 218 | verticalOffset += STATS_HEIGHT_PER_LINE; |
| 205 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); | 219 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); |
| 206 | 220 | ||
| 207 | sprintf(stringBuffer, " Ringbuffer stats | starves: %u, prev_starve_lasted: %u, frames_dropped: %u, overflows: %u", | 221 | sprintf(stringBuffer, " Ringbuffer stats | starves: %u, prev_starve_lasted: %u, frames_dropped: %u, overflows: %u", |
| 208 | streamStats->_starveCount, | 222 | streamStats->_starveCount, |
| 209 | streamStats->_consecutiveNotMixedCount, | 223 | streamStats->_consecutiveNotMixedCount, |
| @@ -211,18 +225,18 @@ void AudioIOStatsRenderer::renderAudioStreamStats(const AudioStreamStats* stream · 1 unmodified line | |||
| 211 | streamStats->_overflowCount); | 225 | streamStats->_overflowCount); |
| 212 | verticalOffset += STATS_HEIGHT_PER_LINE; | 226 | verticalOffset += STATS_HEIGHT_PER_LINE; |
| 213 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); | 227 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); |
| 214 | 228 | ||
| 215 | sprintf(stringBuffer, " Inter-packet timegaps (overall) | min: %9s, max: %9s, avg: %9s", | 229 | sprintf(stringBuffer, " Inter-packet timegaps (overall) | min: %9s, max: %9s, avg: %9s", |
| 216 | formatUsecTime(streamStats->_timeGapMin).toLatin1().data(), | 230 | formatUsecTime(streamStats->_timeGapMin).toLatin1().data(), |
| 217 | formatUsecTime(streamStats->_timeGapMax).toLatin1().data(), | 231 | formatUsecTime(streamStats->_timeGapMax).toLatin1().data(), |
| 218 | formatUsecTime(streamStats->_timeGapAverage).toLatin1().data()); | 232 | formatUsecTime(streamStats->_timeGapAverage).toLatin1().data()); |
| 219 | verticalOffset += STATS_HEIGHT_PER_LINE; | 233 | verticalOffset += STATS_HEIGHT_PER_LINE; |
| 220 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); | 234 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); |
| 221 | 235 | ||
| 222 | sprintf(stringBuffer, " Inter-packet timegaps (last 30s) | min: %9s, max: %9s, avg: %9s", | 236 | sprintf(stringBuffer, " Inter-packet timegaps (last 30s) | min: %9s, max: %9s, avg: %9s", |
| 223 | formatUsecTime(streamStats->_timeGapWindowMin).toLatin1().data(), | 237 | formatUsecTime(streamStats->_timeGapWindowMin).toLatin1().data(), |
| 224 | formatUsecTime(streamStats->_timeGapWindowMax).toLatin1().data(), | 238 | formatUsecTime(streamStats->_timeGapWindowMax).toLatin1().data(), |
| 225 | formatUsecTime(streamStats->_timeGapWindowAverage).toLatin1().data()); | 239 | formatUsecTime(streamStats->_timeGapWindowAverage).toLatin1().data()); |
| 226 | verticalOffset += STATS_HEIGHT_PER_LINE; | 240 | verticalOffset += STATS_HEIGHT_PER_LINE; |
| 227 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); | 241 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color); |
| 228 | } | 242 | } |
| @@ -1,114 +0,0 @@ | |||
| 1 | // | ||
| 2 | // AudioToolBox.cpp | ||
| 3 | // interface/src/audio | ||
| 4 | // | ||
| 5 | // Created by Stephen Birarda on 2014-12-16. | ||
| 6 | // Copyright 2014 High Fidelity, Inc. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | |||
| 12 | #include "InterfaceConfig.h" | ||
| 13 | |||
| 14 | #include <AudioClient.h> | ||
| 15 | #include <GLCanvas.h> | ||
| 16 | #include <PathUtils.h> | ||
| 17 | #include <GeometryCache.h> | ||
| 18 | #include <gpu/GLBackend.h> | ||
| 19 | |||
| 20 | #include "Application.h" | ||
| 21 | #include "AudioToolBox.h" | ||
| 22 | |||
| 23 | // Mute icon configration | ||
| 24 | const int MUTE_ICON_SIZE = 24; | ||
| 25 | |||
| 26 | AudioToolBox::AudioToolBox() : | ||
| 27 | _iconPulseTimeReference(usecTimestampNow()) | ||
| 28 | { | ||
| 29 | } | ||
| 30 | |||
| 31 | bool AudioToolBox::mousePressEvent(int x, int y) { | ||
| 32 | if (_iconBounds.contains(x, y)) { | ||
| 33 | DependencyManager::get<AudioClient>()->toggleMute(); | ||
| 34 | return true; | ||
| 35 | } | ||
| 36 | return false; | ||
| 37 | } | ||
| 38 | |||
| 39 | void AudioToolBox::render(int x, int y, int padding, bool boxed) { | ||
| 40 | glEnable(GL_TEXTURE_2D); | ||
| 41 | |||
| 42 | if (!_micTexture) { | ||
| 43 | _micTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/mic.svg"); | ||
| 44 | } | ||
| 45 | if (!_muteTexture) { | ||
| 46 | _muteTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/mic-mute.svg"); | ||
| 47 | } | ||
| 48 | if (_boxTexture) { | ||
| 49 | _boxTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/audio-box.svg"); | ||
| 50 | } | ||
| 51 | |||
| 52 | auto audioIO = DependencyManager::get<AudioClient>(); | ||
| 53 | |||
| 54 | if (boxed) { | ||
| 55 | bool isClipping = ((audioIO->getTimeSinceLastClip() > 0.0f) && (audioIO->getTimeSinceLastClip() < 1.0f)); | ||
| 56 | const int BOX_LEFT_PADDING = 5; | ||
| 57 | const int BOX_TOP_PADDING = 10; | ||
| 58 | const int BOX_WIDTH = 266; | ||
| 59 | const int BOX_HEIGHT = 44; | ||
| 60 | |||
| 61 | QRect boxBounds = QRect(x - BOX_LEFT_PADDING, y - BOX_TOP_PADDING, BOX_WIDTH, BOX_HEIGHT); | ||
| 62 | glm::vec4 quadColor; | ||
| 63 | |||
| 64 | if (isClipping) { | ||
| 65 | quadColor = glm::vec4(1.0f, 0.0f, 0.0f, 1.0f); | ||
| 66 | } else { | ||
| 67 | quadColor = glm::vec4(0.41f, 0.41f, 0.41f, 1.0f); | ||
| 68 | } | ||
| 69 | glm::vec2 topLeft(boxBounds.left(), boxBounds.top()); | ||
| 70 | glm::vec2 bottomRight(boxBounds.right(), boxBounds.bottom()); | ||
| 71 | static const glm::vec2 texCoordTopLeft(1,1); | ||
| 72 | static const glm::vec2 texCoordBottomRight(0, 0); | ||
| 73 | glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_boxTexture)); | ||
| 74 | DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor); | ||
| 75 | } | ||
| 76 | |||
| 77 | float iconColor = 1.0f; | ||
| 78 | |||
| 79 | _iconBounds = QRect(x + padding, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE); | ||
| 80 | if (!audioIO->isMuted()) { | ||
| 81 | glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_micTexture)); | ||
| 82 | iconColor = 1.0f; | ||
| 83 | } else { | ||
| 84 | glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_muteTexture)); | ||
| 85 | |||
| 86 | // Make muted icon pulsate | ||
| 87 | static const float PULSE_MIN = 0.4f; | ||
| 88 | static const float PULSE_MAX = 1.0f; | ||
| 89 | static const float PULSE_FREQUENCY = 1.0f; // in Hz | ||
| 90 | qint64 now = usecTimestampNow(); | ||
| 91 | if (now - _iconPulseTimeReference > (qint64)USECS_PER_SECOND) { | ||
| 92 | // Prevents t from getting too big, which would diminish glm::cos precision | ||
| 93 | _iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND); | ||
| 94 | } | ||
| 95 | float t = (float)(now - _iconPulseTimeReference) / (float)USECS_PER_SECOND; | ||
| 96 | float pulseFactor = (glm::cos(t * PULSE_FREQUENCY * 2.0f * PI) + 1.0f) / 2.0f; | ||
| 97 | iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor; | ||
| 98 | } | ||
| 99 | |||
| 100 | glm::vec4 quadColor(iconColor, iconColor, iconColor, 1.0f); | ||
| 101 | |||
| 102 | glm::vec2 topLeft(_iconBounds.left(), _iconBounds.top()); | ||
| 103 | glm::vec2 bottomRight(_iconBounds.right(), _iconBounds.bottom()); | ||
| 104 | glm::vec2 texCoordTopLeft(1,1); | ||
| 105 | glm::vec2 texCoordBottomRight(0,0); | ||
| 106 | |||
| 107 | if (_boxQuadID == GeometryCache::UNKNOWN_ID) { | ||
| 108 | _boxQuadID = DependencyManager::get<GeometryCache>()->allocateID(); | ||
| 109 | } | ||
| 110 | |||
| 111 | DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor, _boxQuadID); | ||
| 112 | glBindTexture(GL_TEXTURE_2D, 0); | ||
| 113 | glDisable(GL_TEXTURE_2D); | ||
| 114 | } | ||
| @@ -1,36 +0,0 @@ | |||
| 1 | // | ||
| 2 | // AudioToolBox.h | ||
| 3 | // interface/src/audio | ||
| 4 | // | ||
| 5 | // Created by Stephen Birarda on 2014-12-16. | ||
| 6 | // Copyright 2014 High Fidelity, Inc. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | |||
| 12 | #ifndef hifi_AudioToolBox_h | ||
| 13 | #define hifi_AudioToolBox_h | ||
| 14 | |||
| 15 | #include <DependencyManager.h> | ||
| 16 | #include <GeometryCache.h> | ||
| 17 | #include <QOpenGLTexture> | ||
| 18 | |||
| 19 | class AudioToolBox : public Dependency { | ||
| 20 | SINGLETON_DEPENDENCY | ||
| 21 | public: | ||
| 22 | void render(int x, int y, int padding, bool boxed); | ||
| 23 | |||
| 24 | bool mousePressEvent(int x, int y); | ||
| 25 | protected: | ||
| 26 | AudioToolBox(); | ||
| 27 | private: | ||
| 28 | gpu::TexturePointer _micTexture; | ||
| 29 | gpu::TexturePointer _muteTexture; | ||
| 30 | gpu::TexturePointer _boxTexture; | ||
| 31 | int _boxQuadID = GeometryCache::UNKNOWN_ID; | ||
| 32 | QRect _iconBounds; | ||
| 33 | qint64 _iconPulseTimeReference = 0; | ||
| 34 | }; | ||
| 35 | |||
| 36 | #endif // hifi_AudioToolBox_h | ||
| @@ -57,7 +57,7 @@ const float CHAT_MESSAGE_SCALE = 0.0015f; · 56 unmodified lines | |||
| 57 | const float CHAT_MESSAGE_HEIGHT = 0.1f; | 57 | const float CHAT_MESSAGE_HEIGHT = 0.1f; |
| 58 | const float DISPLAYNAME_FADE_TIME = 0.5f; | 58 | const float DISPLAYNAME_FADE_TIME = 0.5f; |
| 59 | const float DISPLAYNAME_FADE_FACTOR = pow(0.01f, 1.0f / DISPLAYNAME_FADE_TIME); | 59 | const float DISPLAYNAME_FADE_FACTOR = pow(0.01f, 1.0f / DISPLAYNAME_FADE_TIME); |
| 60 | const float DISPLAYNAME_ALPHA = 0.95f; | 60 | const float DISPLAYNAME_ALPHA = 1.0f; |
| 61 | const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f; | 61 | const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f; |
| 62 | 62 | ||
| 63 | namespace render { | 63 | namespace render { |
| @@ -107,6 +107,7 @@ Avatar::Avatar() : · 43 unmodified lines | |||
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | Avatar::~Avatar() { | 109 | Avatar::~Avatar() { |
| 110 | assert(_motionState == nullptr); | ||
| 110 | for(auto attachment : _unusedAttachments) { | 111 | for(auto attachment : _unusedAttachments) { |
| 111 | delete attachment; | 112 | delete attachment; |
| 112 | } | 113 | } |
| @@ -280,9 +281,9 @@ enum TextRendererType { · 167 unmodified lines | |||
| 280 | }; | 281 | }; |
| 281 | 282 | ||
| 282 | static TextRenderer3D* textRenderer(TextRendererType type) { | 283 | static TextRenderer3D* textRenderer(TextRendererType type) { |
| 283 | static TextRenderer3D* chatRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, 24, -1, | 284 | static TextRenderer3D* chatRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, -1, |
| 284 | false, TextRenderer3D::SHADOW_EFFECT); | 285 | false, TextRenderer3D::SHADOW_EFFECT); |
| 285 | static TextRenderer3D* displayNameRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, 12); | 286 | static TextRenderer3D* displayNameRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY); |
| 286 | 287 | ||
| 287 | switch(type) { | 288 | switch(type) { |
| 288 | case CHAT: | 289 | case CHAT: |
| @@ -323,7 +324,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo · 34 unmodified lines | |||
| 323 | _referential->update(); | 324 | _referential->update(); |
| 324 | } | 325 | } |
| 325 | 326 | ||
| 326 | auto batch = renderArgs->_batch; | 327 | auto& batch = *renderArgs->_batch; |
| 327 | 328 | ||
| 328 | if (postLighting && | 329 | if (postLighting && |
| 329 | glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), _position) < 10.0f) { | 330 | glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), _position) < 10.0f) { |
| @@ -354,9 +355,9 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo · 24 unmodified lines | |||
| 354 | Transform pointerTransform; | 355 | Transform pointerTransform; |
| 355 | pointerTransform.setTranslation(position); | 356 | pointerTransform.setTranslation(position); |
| 356 | pointerTransform.setRotation(rotation); | 357 | pointerTransform.setRotation(rotation); |
| 357 | batch->setModelTransform(pointerTransform); | 358 | batch.setModelTransform(pointerTransform); |
| 358 | deferredLighting->bindSimpleProgram(*batch); | 359 | deferredLighting->bindSimpleProgram(batch); |
| 359 | geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor); | 360 | geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor); |
| 360 | } | 361 | } |
| 361 | } | 362 | } |
| 362 | 363 | ||
| @@ -377,9 +378,9 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo · 14 unmodified lines | |||
| 377 | Transform pointerTransform; | 378 | Transform pointerTransform; |
| 378 | pointerTransform.setTranslation(position); | 379 | pointerTransform.setTranslation(position); |
| 379 | pointerTransform.setRotation(rotation); | 380 | pointerTransform.setRotation(rotation); |
| 380 | batch->setModelTransform(pointerTransform); | 381 | batch.setModelTransform(pointerTransform); |
| 381 | deferredLighting->bindSimpleProgram(*batch); | 382 | deferredLighting->bindSimpleProgram(batch); |
| 382 | geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor); | 383 | geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor); |
| 383 | } | 384 | } |
| 384 | } | 385 | } |
| 385 | } | 386 | } |
| @@ -464,8 +465,8 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo · 78 unmodified lines | |||
| 464 | } | 465 | } |
| 465 | Transform transform; | 466 | Transform transform; |
| 466 | transform.setTranslation(position); | 467 | transform.setTranslation(position); |
| 467 | batch->setModelTransform(transform); | 468 | batch.setModelTransform(transform); |
| 468 | DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(*batch, LOOK_AT_INDICATOR_RADIUS | 469 | DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, LOOK_AT_INDICATOR_RADIUS |
| 469 | , 15, 15, LOOK_AT_INDICATOR_COLOR); | 470 | , 15, 15, LOOK_AT_INDICATOR_COLOR); |
| 470 | } | 471 | } |
| 471 | } | 472 | } |
| @@ -492,14 +493,14 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo · 20 unmodified lines | |||
| 492 | Transform transform; | 493 | Transform transform; |
| 493 | transform.setTranslation(_position); | 494 | transform.setTranslation(_position); |
| 494 | transform.setScale(height); | 495 | transform.setScale(height); |
| 495 | batch->setModelTransform(transform); | 496 | batch.setModelTransform(transform); |
| 496 | 497 | ||
| 497 | if (_voiceSphereID == GeometryCache::UNKNOWN_ID) { | 498 | if (_voiceSphereID == GeometryCache::UNKNOWN_ID) { |
| 498 | _voiceSphereID = DependencyManager::get<GeometryCache>()->allocateID(); | 499 | _voiceSphereID = DependencyManager::get<GeometryCache>()->allocateID(); |
| 499 | } | 500 | } |
| 500 | 501 | ||
| 501 | DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch); | 502 | DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch); |
| 502 | DependencyManager::get<GeometryCache>()->renderSphere(*batch, sphereRadius, 15, 15, | 503 | DependencyManager::get<GeometryCache>()->renderSphere(batch, sphereRadius, 15, 15, |
| 503 | glm::vec4(SPHERE_COLOR[0], SPHERE_COLOR[1], SPHERE_COLOR[2], 1.0f - angle / MAX_SPHERE_ANGLE), true, | 504 | glm::vec4(SPHERE_COLOR[0], SPHERE_COLOR[1], SPHERE_COLOR[2], 1.0f - angle / MAX_SPHERE_ANGLE), true, |
| 504 | _voiceSphereID); | 505 | _voiceSphereID); |
| 505 | } | 506 | } |
| @@ -507,14 +508,12 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo · 1 unmodified line | |||
| 507 | } | 508 | } |
| 508 | 509 | ||
| 509 | const float DISPLAYNAME_DISTANCE = 20.0f; | 510 | const float DISPLAYNAME_DISTANCE = 20.0f; |
| 510 | setShowDisplayName(renderArgs->_renderMode == RenderArgs::NORMAL_RENDER_MODE && distanceToTarget < DISPLAYNAME_DISTANCE); | 511 | setShowDisplayName(distanceToTarget < DISPLAYNAME_DISTANCE); |
| 511 | 512 | ||
| 512 | if (renderArgs->_renderMode != RenderArgs::NORMAL_RENDER_MODE || (isMyAvatar() && | 513 | auto cameraMode = Application::getInstance()->getCamera()->getMode(); |
| 513 | Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON)) { | 514 | if (!isMyAvatar() || cameraMode != CAMERA_MODE_FIRST_PERSON) { |
| 514 | return; | 515 | renderDisplayName(batch, *renderArgs->_viewFrustum); |
| 515 | } | 516 | } |
| 516 | |||
| 517 | renderDisplayName(renderArgs); | ||
| 518 | } | 517 | } |
| 519 | 518 | ||
| 520 | glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { | 519 | glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { |
| @@ -654,8 +653,8 @@ float Avatar::getBillboardSize() const { · 133 unmodified lines | |||
| 654 | return _scale * BILLBOARD_DISTANCE * tanf(glm::radians(BILLBOARD_FIELD_OF_VIEW / 2.0f)); | 653 | return _scale * BILLBOARD_DISTANCE * tanf(glm::radians(BILLBOARD_FIELD_OF_VIEW / 2.0f)); |
| 655 | } | 654 | } |
| 656 | 655 | ||
| 657 | glm::vec3 Avatar::getDisplayNamePosition() { | 656 | glm::vec3 Avatar::getDisplayNamePosition() const { |
| 658 | glm::vec3 namePosition; | 657 | glm::vec3 namePosition(0.0f); |
| 659 | if (getSkeletonModel().getNeckPosition(namePosition)) { | 658 | if (getSkeletonModel().getNeckPosition(namePosition)) { |
| 660 | namePosition += getBodyUpDirection() * getHeadHeight() * 1.1f; | 659 | namePosition += getBodyUpDirection() * getHeadHeight() * 1.1f; |
| 661 | } else { | 660 | } else { |
| @@ -665,81 +664,73 @@ glm::vec3 Avatar::getDisplayNamePosition() { · 3 unmodified lines | |||
| 665 | return namePosition; | 664 | return namePosition; |
| 666 | } | 665 | } |
| 667 | 666 | ||
| 668 | float Avatar::calculateDisplayNameScaleFactor(const glm::vec3& textPosition, bool inHMD) { | 667 | Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, float fontSize) const { |
| 669 | 668 | Transform result; | |
| 670 | // We need to compute the scale factor such as the text remains with fixed size respect to window coordinates | 669 | // We assume textPosition is whithin the frustum |
| 671 | // We project a unit vector and check the difference in screen coordinates, to check which is the | 670 | glm::vec3 textPosition = getDisplayNamePosition(); |
| 672 | // correction scale needed | 671 | |
| 673 | // save the matrices for later scale correction factor | 672 | // Compute viewProjection matrix |
| 674 | // The up vector must be relative to the rotation current rotation matrix: | 673 | glm::mat4 projMat, viewMat; |
| 675 | // we set the identity | 674 | Transform view; |
| 675 | frustum.evalProjectionMatrix(projMat); | ||
| 676 | frustum.evalViewTransform(view); | ||
| 677 | glm::mat4 viewProj = projMat * view.getInverseMatrix(viewMat); | ||
| 678 | |||
| 679 | // Used to determine correct scale | ||
| 676 | glm::vec3 testPoint0 = textPosition; | 680 | glm::vec3 testPoint0 = textPosition; |
| 677 | glm::vec3 testPoint1 = textPosition + (Application::getInstance()->getCamera()->getRotation() * IDENTITY_UP); | 681 | glm::vec3 testPoint1 = testPoint0 + glm::normalize(frustum.getUp()); |
| 678 | 682 | // testPoints projections | |
| 679 | double textWindowHeight; | 683 | glm::vec4 p0 = viewProj * glm::vec4(testPoint0, 1.0); |
| 680 | 684 | glm::vec4 p1 = viewProj * glm::vec4(testPoint1, 1.0); | |
| 685 | |||
| 686 | // TODO REMOVE vvv | ||
| 681 | GLint viewportMatrix[4]; | 687 | GLint viewportMatrix[4]; |
| 682 | glGetIntegerv(GL_VIEWPORT, viewportMatrix); | 688 | glGetIntegerv(GL_VIEWPORT, viewportMatrix); |
| 683 | glm::dmat4 modelViewMatrix; | 689 | glm::dmat4 modelViewMatrix; |
| 684 | float windowSizeX = viewportMatrix[2] - viewportMatrix[0]; | ||
| 685 | float windowSizeY = viewportMatrix[3] - viewportMatrix[1]; | 690 | float windowSizeY = viewportMatrix[3] - viewportMatrix[1]; |
| 691 | // TODO REMOVE ^^^ | ||
| 692 | |||
| 693 | const float DESIRED_HIGHT_ON_SCREEN = 20; // In pixels (this is double on retinas) | ||
| 694 | |||
| 695 | // Projected point are between -1.0f and 1.0f, hence 0.5f * windowSizeY | ||
| 696 | double pixelHeight = 0.5f * windowSizeY * glm::abs((p1.y / p1.w) - (p0.y / p0.w)); // | ||
| 697 | // Handles pixel density (especially for macs retina displays) | ||
| 698 | double devicePixelRatio = qApp->getDevicePixelRatio() * qApp->getRenderResolutionScale(); // pixels / unit | ||
| 699 | |||
| 700 | // Compute correct scale to apply | ||
| 701 | float scale = DESIRED_HIGHT_ON_SCREEN / (fontSize * pixelHeight) * devicePixelRatio; | ||
| 702 | |||
| 703 | // Compute pixel alignment offset | ||
| 704 | float clipToPix = 0.5f * windowSizeY / p1.w; // Got from clip to pixel coordinates | ||
| 705 | glm::vec4 screenPos = clipToPix * p1; // in pixels coords | ||
| 706 | glm::vec4 screenOffset = (glm::round(screenPos) - screenPos) / clipToPix; // in clip coords | ||
| 707 | glm::vec3 worldOffset = glm::vec3(screenOffset.x, screenOffset.y, 0.0f) / (float)pixelHeight; | ||
| 708 | |||
| 709 | // Compute orientation | ||
| 710 | glm::vec3 eulerAngles = ::safeEulerAngles(frustum.getOrientation()); | ||
| 711 | eulerAngles.z = 0.0f; // Cancel roll | ||
| 712 | glm::quat orientation(eulerAngles); // back to quaternions | ||
| 713 | |||
| 714 | // Set transform (The order IS important) | ||
| 715 | result.setTranslation(textPosition); | ||
| 716 | result.setRotation(orientation); // Always face the screen | ||
| 717 | result.postTranslate(worldOffset); // Pixel alignment | ||
| 718 | result.setScale(scale); | ||
| 719 | return result; | ||
| 686 | 720 | ||
| 687 | glm::dmat4 projectionMatrix; | ||
| 688 | Application::getInstance()->getModelViewMatrix(&modelViewMatrix); | ||
| 689 | Application::getInstance()->getProjectionMatrix(&projectionMatrix); | ||
| 690 | |||
| 691 | |||
| 692 | glm::dvec4 p0 = modelViewMatrix * glm::dvec4(testPoint0, 1.0); | ||
| 693 | p0 = projectionMatrix * p0; | ||
| 694 | glm::dvec2 result0 = glm::vec2(windowSizeX * (p0.x / p0.w + 1.0f) * 0.5f, windowSizeY * (p0.y / p0.w + 1.0f) * 0.5f); | ||
| 695 | |||
| 696 | glm::dvec4 p1 = modelViewMatrix * glm::dvec4(testPoint1, 1.0); | ||
| 697 | p1 = projectionMatrix * p1; | ||
| 698 | glm::vec2 result1 = glm::vec2(windowSizeX * (p1.x / p1.w + 1.0f) * 0.5f, windowSizeY * (p1.y / p1.w + 1.0f) * 0.5f); | ||
| 699 | textWindowHeight = abs(result1.y - result0.y); | ||
| 700 | |||
| 701 | // need to scale to compensate for the font resolution due to the device | ||
| 702 | float scaleFactor = QApplication::desktop()->windowHandle()->devicePixelRatio() * | ||
| 703 | ((textWindowHeight > EPSILON) ? 1.0f / textWindowHeight : 1.0f); | ||
| 704 | if (inHMD) { | ||
| 705 | const float HMDMODE_NAME_SCALE = 0.65f; | ||
| 706 | scaleFactor *= HMDMODE_NAME_SCALE; | ||
| 707 | } else { | ||
| 708 | scaleFactor *= Application::getInstance()->getRenderResolutionScale(); | ||
| 709 | } | ||
| 710 | return scaleFactor; | ||
| 711 | } | 721 | } |
| 712 | 722 | ||
| 713 | void Avatar::renderDisplayName(RenderArgs* renderArgs) { | 723 | void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum) const { |
| 714 | auto batch = renderArgs->_batch; | ||
| 715 | |||
| 716 | bool shouldShowReceiveStats = DependencyManager::get<AvatarManager>()->shouldShowReceiveStats() && !isMyAvatar(); | 724 | bool shouldShowReceiveStats = DependencyManager::get<AvatarManager>()->shouldShowReceiveStats() && !isMyAvatar(); |
| 717 | 725 | ||
| 726 | // If we have nothing to draw, or it's tottaly transparent, return | ||
| 718 | if ((_displayName.isEmpty() && !shouldShowReceiveStats) || _displayNameAlpha == 0.0f) { | 727 | if ((_displayName.isEmpty() && !shouldShowReceiveStats) || _displayNameAlpha == 0.0f) { |
| 719 | return; | 728 | return; |
| 720 | } | 729 | } |
| 721 | 730 | auto renderer = textRenderer(DISPLAYNAME); | |
| 722 | // which viewing mode? | ||
| 723 | bool inHMD = Application::getInstance()->isHMDMode(); | ||
| 724 | |||
| 725 | glm::vec3 textPosition = getDisplayNamePosition(); | ||
| 726 | |||
| 727 | // we need "always facing camera": we must remove the camera rotation from the stac | ||
| 728 | glm::quat rotation = Application::getInstance()->getCamera()->getRotation(); | ||
| 729 | |||
| 730 | // TODO: Fix scaling - at some point this or the text rendering changed in scale. | ||
| 731 | float scaleFactor = calculateDisplayNameScaleFactor(textPosition, inHMD); | ||
| 732 | scaleFactor /= 3.5f; | ||
| 733 | |||
| 734 | Transform textTransform; | ||
| 735 | textTransform.setTranslation(textPosition); | ||
| 736 | textTransform.setRotation(rotation); | ||
| 737 | textTransform.setScale(scaleFactor); | ||
| 738 | 731 | ||
| 739 | // optionally render timing stats for this avatar with the display name | 732 | // optionally render timing stats for this avatar with the display name |
| 740 | QString renderedDisplayName = _displayName; | 733 | QString renderedDisplayName = _displayName; |
| 741 | QRect nameDynamicRect = _displayNameBoundingRect; | ||
| 742 | |||
| 743 | if (shouldShowReceiveStats) { | 734 | if (shouldShowReceiveStats) { |
| 744 | float kilobitsPerSecond = getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT; | 735 | float kilobitsPerSecond = getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT; |
| 745 | 736 | ||
| @@ -747,42 +738,43 @@ void Avatar::renderDisplayName(RenderArgs* renderArgs) { · 1 unmodified line | |||
| 747 | if (!renderedDisplayName.isEmpty()) { | 738 | if (!renderedDisplayName.isEmpty()) { |
| 748 | statsFormat.prepend(" - "); | 739 | statsFormat.prepend(" - "); |
| 749 | } | 740 | } |
| 750 | 741 | renderedDisplayName += statsFormat.arg(QString::number(kilobitsPerSecond, 'f', 2)).arg(getReceiveRate()); | |
| 751 | QString statsText = statsFormat.arg(QString::number(kilobitsPerSecond, 'f', 2)).arg(getReceiveRate()); | ||
| 752 | renderedDisplayName += statsText; | ||
| 753 | |||
| 754 | glm::vec2 extent = textRenderer(DISPLAYNAME)->computeExtent(renderedDisplayName); | ||
| 755 | nameDynamicRect = QRect(0, 0, (int)extent.x, (int)extent.y); | ||
| 756 | } | 742 | } |
| 757 | |||
| 758 | int text_x = -nameDynamicRect.width() / 2; | ||
| 759 | int text_y = -nameDynamicRect.height() / 2; | ||
| 760 | |||
| 761 | // draw a gray background | ||
| 762 | int left = text_x; | ||
| 763 | int right = left + nameDynamicRect.width(); | ||
| 764 | int bottom = text_y; | ||
| 765 | int top = bottom + nameDynamicRect.height(); | ||
| 766 | const int border = 8; | ||
| 767 | bottom -= border; | ||
| 768 | left -= border; | ||
| 769 | top += border; | ||
| 770 | right += border; | ||
| 771 | 743 | ||
| 744 | // Compute display name extent/position offset | ||
| 745 | glm::vec2 extent = renderer->computeExtent(renderedDisplayName); | ||
| 746 | QRect nameDynamicRect = QRect(0, 0, (int)extent.x, (int)extent.y); | ||
| 747 | const int text_x = -nameDynamicRect.width() / 2; | ||
| 748 | const int text_y = -nameDynamicRect.height() / 2; | ||
| 749 | |||
| 750 | // Compute background position/size | ||
| 751 | static const float SLIGHTLY_BEHIND = -0.05f; | ||
| 752 | const int border = 0.1f * nameDynamicRect.height(); | ||
| 753 | const int left = text_x - border; | ||
| 754 | const int bottom = text_y - border; | ||
| 755 | const int width = nameDynamicRect.width() + 2.0f * border; | ||
| 756 | const int height = nameDynamicRect.height() + 2.0f * border; | ||
| 757 | const int bevelDistance = 0.1f * height; | ||
| 758 | |||
| 759 | // Display name and background colors | ||
| 772 | glm::vec4 textColor(0.93f, 0.93f, 0.93f, _displayNameAlpha); | 760 | glm::vec4 textColor(0.93f, 0.93f, 0.93f, _displayNameAlpha); |
| 773 | glm::vec4 backgroundColor(0.2f, 0.2f, 0.2f, | 761 | glm::vec4 backgroundColor(0.2f, 0.2f, 0.2f, |
| 774 | _displayNameAlpha * DISPLAYNAME_BACKGROUND_ALPHA / DISPLAYNAME_ALPHA); | 762 | (_displayNameAlpha / DISPLAYNAME_ALPHA) * DISPLAYNAME_BACKGROUND_ALPHA); |
| 763 | |||
| 764 | // Compute display name transform | ||
| 765 | auto textTransform = calculateDisplayNameTransform(frustum, renderer->getFontSize()); | ||
| 775 | 766 | ||
| 767 | // Render background slightly behind to avoid z-fighting | ||
| 776 | auto backgroundTransform = textTransform; | 768 | auto backgroundTransform = textTransform; |
| 777 | backgroundTransform.postTranslate(glm::vec3(0.0f, 0.0f, -0.001f)); | 769 | backgroundTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_BEHIND)); |
| 778 | batch->setModelTransform(backgroundTransform); | 770 | batch.setModelTransform(backgroundTransform); |
| 779 | DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch); | 771 | DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch); |
| 780 | DependencyManager::get<GeometryCache>()->renderBevelCornersRect(*batch, left, bottom, right - left, top - bottom, 3, | 772 | DependencyManager::get<GeometryCache>()->renderBevelCornersRect(batch, left, bottom, width, height, |
| 781 | backgroundColor); | 773 | bevelDistance, backgroundColor); |
| 774 | // Render actual name | ||
| 782 | QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit(); | 775 | QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit(); |
| 783 | 776 | batch.setModelTransform(textTransform); | |
| 784 | batch->setModelTransform(textTransform); | 777 | renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor); |
| 785 | textRenderer(DISPLAYNAME)->draw(*batch, text_x, -text_y, nameUTF8.data(), textColor); | ||
| 786 | } | 778 | } |
| 787 | 779 | ||
| 788 | bool Avatar::findRayIntersection(RayIntersectionInfo& intersection) const { | 780 | bool Avatar::findRayIntersection(RayIntersectionInfo& intersection) const { |
| @@ -987,13 +979,6 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) { · 198 unmodified lines | |||
| 987 | } | 979 | } |
| 988 | } | 980 | } |
| 989 | 981 | ||
| 990 | void Avatar::setDisplayName(const QString& displayName) { | ||
| 991 | AvatarData::setDisplayName(displayName); | ||
| 992 | // FIXME is this a sufficient replacement for tightBoundingRect? | ||
| 993 | glm::vec2 extent = textRenderer(DISPLAYNAME)->computeExtent(displayName); | ||
| 994 | _displayNameBoundingRect = QRect(0, 0, (int)extent.x, (int)extent.y); | ||
| 995 | } | ||
| 996 | |||
| 997 | void Avatar::setBillboard(const QByteArray& billboard) { | 982 | void Avatar::setBillboard(const QByteArray& billboard) { |
| 998 | AvatarData::setBillboard(billboard); | 983 | AvatarData::setBillboard(billboard); |
| 999 | 984 | ||
| @@ -1092,7 +1077,12 @@ float Avatar::getSkeletonHeight() const { · 92 unmodified lines | |||
| 1092 | float Avatar::getHeadHeight() const { | 1077 | float Avatar::getHeadHeight() const { |
| 1093 | Extents extents = getHead()->getFaceModel().getMeshExtents(); | 1078 | Extents extents = getHead()->getFaceModel().getMeshExtents(); |
| 1094 | if (!extents.isEmpty() && extents.isValid()) { | 1079 | if (!extents.isEmpty() && extents.isValid()) { |
| 1095 | return extents.maximum.y - extents.minimum.y; | 1080 | |
| 1081 | // HACK: We have a really odd case when fading out for some models where this value explodes | ||
| 1082 | float result = extents.maximum.y - extents.minimum.y; | ||
| 1083 | if (result >= 0.0f && result < 100.0f * _scale ) { | ||
| 1084 | return result; | ||
| 1085 | } | ||
| 1096 | } | 1086 | } |
| 1097 | 1087 | ||
| 1098 | extents = _skeletonModel.getMeshExtents(); | 1088 | extents = _skeletonModel.getMeshExtents(); |
| @@ -1116,7 +1106,7 @@ void Avatar::setShowDisplayName(bool showDisplayName) { · 17 unmodified lines | |||
| 1116 | } | 1106 | } |
| 1117 | 1107 | ||
| 1118 | // For myAvatar, the alpha update is not done (called in simulate for other avatars) | 1108 | // For myAvatar, the alpha update is not done (called in simulate for other avatars) |
| 1119 | if (DependencyManager::get<AvatarManager>()->getMyAvatar() == this) { | 1109 | if (isMyAvatar()) { |
| 1120 | if (showDisplayName) { | 1110 | if (showDisplayName) { |
| 1121 | _displayNameAlpha = DISPLAYNAME_ALPHA; | 1111 | _displayNameAlpha = DISPLAYNAME_ALPHA; |
| 1122 | } else { | 1112 | } else { |
| @@ -1129,7 +1119,6 @@ void Avatar::setShowDisplayName(bool showDisplayName) { · 6 unmodified lines | |||
| 1129 | } else { | 1119 | } else { |
| 1130 | _displayNameTargetAlpha = 0.0f; | 1120 | _displayNameTargetAlpha = 0.0f; |
| 1131 | } | 1121 | } |
| 1132 | |||
| 1133 | } | 1122 | } |
| 1134 | 1123 | ||
| 1135 | // virtual | 1124 | // virtual |
| @@ -98,6 +98,7 @@ public: · 97 unmodified lines | |||
| 98 | //getters | 98 | //getters |
| 99 | bool isInitialized() const { return _initialized; } | 99 | bool isInitialized() const { return _initialized; } |
| 100 | SkeletonModel& getSkeletonModel() { return _skeletonModel; } | 100 | SkeletonModel& getSkeletonModel() { return _skeletonModel; } |
| 101 | const SkeletonModel& getSkeletonModel() const { return _skeletonModel; } | ||
| 101 | const QVector<Model*>& getAttachmentModels() const { return _attachmentModels; } | 102 | const QVector<Model*>& getAttachmentModels() const { return _attachmentModels; } |
| 102 | glm::vec3 getChestPosition() const; | 103 | glm::vec3 getChestPosition() const; |
| 103 | float getScale() const { return _scale; } | 104 | float getScale() const { return _scale; } |
| @@ -131,7 +132,7 @@ public: · 27 unmodified lines | |||
| 131 | /// \return whether or not the plane penetrated | 132 | /// \return whether or not the plane penetrated |
| 132 | bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); | 133 | bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); |
| 133 | 134 | ||
| 134 | virtual bool isMyAvatar() { return false; } | 135 | virtual bool isMyAvatar() const { return false; } |
| 135 | 136 | ||
| 136 | virtual QVector<glm::quat> getJointRotations() const; | 137 | virtual QVector<glm::quat> getJointRotations() const; |
| 137 | virtual glm::quat getJointRotation(int index) const; | 138 | virtual glm::quat getJointRotation(int index) const; |
| @@ -141,7 +142,6 @@ public: · 3 unmodified lines | |||
| 141 | virtual void setFaceModelURL(const QUrl& faceModelURL); | 142 | virtual void setFaceModelURL(const QUrl& faceModelURL); |
| 142 | virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); | 143 | virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); |
| 143 | virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData); | 144 | virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData); |
| 144 | virtual void setDisplayName(const QString& displayName); | ||
| 145 | virtual void setBillboard(const QByteArray& billboard); | 145 | virtual void setBillboard(const QByteArray& billboard); |
| 146 | 146 | ||
| 147 | void setShowDisplayName(bool showDisplayName); | 147 | void setShowDisplayName(bool showDisplayName); |
| @@ -232,10 +232,10 @@ protected: · 84 unmodified lines | |||
| 232 | float getSkeletonHeight() const; | 232 | float getSkeletonHeight() const; |
| 233 | float getHeadHeight() const; | 233 | float getHeadHeight() const; |
| 234 | float getPelvisFloatingHeight() const; | 234 | float getPelvisFloatingHeight() const; |
| 235 | glm::vec3 getDisplayNamePosition(); | 235 | glm::vec3 getDisplayNamePosition() const; |
| 236 | 236 | ||
| 237 | float calculateDisplayNameScaleFactor(const glm::vec3& textPosition, bool inHMD); | 237 | Transform calculateDisplayNameTransform(const ViewFrustum& frustum, float fontSize) const; |
| 238 | void renderDisplayName(RenderArgs* renderArgs); | 238 | void renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum) const; |
| 239 | virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel = 0.0f); | 239 | virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel = 0.0f); |
| 240 | virtual bool shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const; | 240 | virtual bool shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const; |
| 241 | virtual void fixupModelsInScene(); | 241 | virtual void fixupModelsInScene(); |
| @@ -29,6 +29,12 @@ AvatarActionHold::~AvatarActionHold() { · 28 unmodified lines | |||
| 29 | 29 | ||
| 30 | void AvatarActionHold::updateActionWorker(float deltaTimeStep) { | 30 | void AvatarActionHold::updateActionWorker(float deltaTimeStep) { |
| 31 | auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | 31 | auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); |
| 32 | |||
| 33 | if (!tryLockForRead()) { | ||
| 34 | // don't risk hanging the thread running the physics simulation | ||
| 35 | return; | ||
| 36 | } | ||
| 37 | |||
| 32 | glm::vec3 palmPosition; | 38 | glm::vec3 palmPosition; |
| 33 | if (_hand == "right") { | 39 | if (_hand == "right") { |
| 34 | palmPosition = myAvatar->getRightPalmPosition(); | 40 | palmPosition = myAvatar->getRightPalmPosition(); |
| @@ -40,8 +46,11 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { · 5 unmodified lines | |||
| 40 | auto offset = rotation * _relativePosition; | 46 | auto offset = rotation * _relativePosition; |
| 41 | auto position = palmPosition + offset; | 47 | auto position = palmPosition + offset; |
| 42 | rotation *= _relativeRotation; | 48 | rotation *= _relativeRotation; |
| 49 | unlock(); | ||
| 43 | 50 | ||
| 44 | lockForWrite(); | 51 | if (!tryLockForWrite()) { |
| 52 | return; | ||
| 53 | } | ||
| 45 | _positionalTarget = position; | 54 | _positionalTarget = position; |
| 46 | _rotationalTarget = rotation; | 55 | _rotationalTarget = rotation; |
| 47 | unlock(); | 56 | unlock(); |
| @@ -13,8 +13,18 @@ · 12 unmodified lines | |||
| 13 | 13 | ||
| 14 | #include <QScriptEngine> | 14 | #include <QScriptEngine> |
| 15 | 15 | ||
| 16 | #ifdef __GNUC__ | ||
| 17 | #pragma GCC diagnostic push | ||
| 18 | #pragma GCC diagnostic ignored "-Wdouble-promotion" | ||
| 19 | #endif | ||
| 20 | |||
| 16 | #include <glm/gtx/string_cast.hpp> | 21 | #include <glm/gtx/string_cast.hpp> |
| 17 | 22 | ||
| 23 | #ifdef __GNUC__ | ||
| 24 | #pragma GCC diagnostic pop | ||
| 25 | #endif | ||
| 26 | |||
| 27 | |||
| 18 | #include <GlowEffect.h> | 28 | #include <GlowEffect.h> |
| 19 | #include <PerfStat.h> | 29 | #include <PerfStat.h> |
| 20 | #include <RegisteredMetaTypes.h> | 30 | #include <RegisteredMetaTypes.h> |
| @@ -104,6 +114,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { · 83 unmodified lines | |||
| 104 | // DO NOT update or fade out uninitialized Avatars | 114 | // DO NOT update or fade out uninitialized Avatars |
| 105 | ++avatarIterator; | 115 | ++avatarIterator; |
| 106 | } else if (avatar->shouldDie()) { | 116 | } else if (avatar->shouldDie()) { |
| 117 | removeAvatarMotionState(avatar); | ||
| 107 | _avatarFades.push_back(avatarIterator.value()); | 118 | _avatarFades.push_back(avatarIterator.value()); |
| 108 | avatarIterator = _avatarHash.erase(avatarIterator); | 119 | avatarIterator = _avatarHash.erase(avatarIterator); |
| 109 | } else { | 120 | } else { |
| @@ -153,12 +164,13 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe · 43 unmodified lines | |||
| 153 | } | 164 | } |
| 154 | 165 | ||
| 155 | // protected | 166 | // protected |
| 156 | void AvatarManager::removeAvatarMotionState(Avatar* avatar) { | 167 | void AvatarManager::removeAvatarMotionState(AvatarSharedPointer avatar) { |
| 157 | AvatarMotionState* motionState= avatar->_motionState; | 168 | auto rawPointer = std::static_pointer_cast<Avatar>(avatar); |
| 169 | AvatarMotionState* motionState= rawPointer->_motionState; | ||
| 158 | if (motionState) { | 170 | if (motionState) { |
| 159 | // clean up physics stuff | 171 | // clean up physics stuff |
| 160 | motionState->clearObjectBackPointer(); | 172 | motionState->clearObjectBackPointer(); |
| 161 | avatar->_motionState = nullptr; | 173 | rawPointer->_motionState = nullptr; |
| 162 | _avatarMotionStates.remove(motionState); | 174 | _avatarMotionStates.remove(motionState); |
| 163 | _motionStatesToAdd.remove(motionState); | 175 | _motionStatesToAdd.remove(motionState); |
| 164 | _motionStatesToDelete.push_back(motionState); | 176 | _motionStatesToDelete.push_back(motionState); |
| @@ -171,7 +183,7 @@ void AvatarManager::removeAvatar(const QUuid& sessionUUID) { · 6 unmodified lines | |||
| 171 | if (avatarIterator != _avatarHash.end()) { | 183 | if (avatarIterator != _avatarHash.end()) { |
| 172 | std::shared_ptr<Avatar> avatar = std::dynamic_pointer_cast<Avatar>(avatarIterator.value()); | 184 | std::shared_ptr<Avatar> avatar = std::dynamic_pointer_cast<Avatar>(avatarIterator.value()); |
| 173 | if (avatar != _myAvatar && avatar->isInitialized()) { | 185 | if (avatar != _myAvatar && avatar->isInitialized()) { |
| 174 | removeAvatarMotionState(avatar.get()); | 186 | removeAvatarMotionState(avatar); |
| 175 | _avatarFades.push_back(avatarIterator.value()); | 187 | _avatarFades.push_back(avatarIterator.value()); |
| 176 | _avatarHash.erase(avatarIterator); | 188 | _avatarHash.erase(avatarIterator); |
| 177 | } | 189 | } |
| @@ -187,7 +199,7 @@ void AvatarManager::clearOtherAvatars() { · 9 unmodified lines | |||
| 187 | // don't remove myAvatar or uninitialized avatars from the list | 199 | // don't remove myAvatar or uninitialized avatars from the list |
| 188 | ++avatarIterator; | 200 | ++avatarIterator; |
| 189 | } else { | 201 | } else { |
| 190 | removeAvatarMotionState(avatar.get()); | 202 | removeAvatarMotionState(avatar); |
| 191 | _avatarFades.push_back(avatarIterator.value()); | 203 | _avatarFades.push_back(avatarIterator.value()); |
| 192 | avatarIterator = _avatarHash.erase(avatarIterator); | 204 | avatarIterator = _avatarHash.erase(avatarIterator); |
| 193 | } | 205 | } |
| @@ -73,7 +73,7 @@ private: · 72 unmodified lines | |||
| 73 | // virtual overrides | 73 | // virtual overrides |
| 74 | virtual AvatarSharedPointer newSharedAvatar(); | 74 | virtual AvatarSharedPointer newSharedAvatar(); |
| 75 | virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer); | 75 | virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer); |
| 76 | void removeAvatarMotionState(Avatar* avatar); | 76 | void removeAvatarMotionState(AvatarSharedPointer avatar); |
| 77 | virtual void removeAvatar(const QUuid& sessionUUID); | 77 | virtual void removeAvatar(const QUuid& sessionUUID); |
| 78 | 78 | ||
| 79 | QVector<AvatarSharedPointer> _avatarFades; | 79 | QVector<AvatarSharedPointer> _avatarFades; |
| @@ -77,7 +77,7 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { · 76 unmodified lines | |||
| 77 | const float AUDIO_LONG_TERM_AVERAGING_SECS = 30.0f; | 77 | const float AUDIO_LONG_TERM_AVERAGING_SECS = 30.0f; |
| 78 | _averageLoudness = glm::mix(_averageLoudness, _audioLoudness, glm::min(deltaTime / AUDIO_AVERAGING_SECS, 1.0f)); | 78 | _averageLoudness = glm::mix(_averageLoudness, _audioLoudness, glm::min(deltaTime / AUDIO_AVERAGING_SECS, 1.0f)); |
| 79 | 79 | ||
| 80 | if (_longTermAverageLoudness == -1.0) { | 80 | if (_longTermAverageLoudness == -1.0f) { |
| 81 | _longTermAverageLoudness = _averageLoudness; | 81 | _longTermAverageLoudness = _averageLoudness; |
| 82 | } else { | 82 | } else { |
| 83 | _longTermAverageLoudness = glm::mix(_longTermAverageLoudness, _averageLoudness, glm::min(deltaTime / AUDIO_LONG_TERM_AVERAGING_SECS, 1.0f)); | 83 | _longTermAverageLoudness = glm::mix(_longTermAverageLoudness, _averageLoudness, glm::min(deltaTime / AUDIO_LONG_TERM_AVERAGING_SECS, 1.0f)); |
| @@ -124,18 +124,25 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { · 40 unmodified lines | |||
| 124 | 124 | ||
| 125 | if (!(_isFaceTrackerConnected || billboard)) { | 125 | if (!(_isFaceTrackerConnected || billboard)) { |
| 126 | // Update eye saccades | 126 | // Update eye saccades |
| 127 | const float AVERAGE_MICROSACCADE_INTERVAL = 0.50f; | 127 | const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f; |
| 128 | const float AVERAGE_SACCADE_INTERVAL = 4.0f; | 128 | const float AVERAGE_SACCADE_INTERVAL = 6.0f; |
| 129 | const float MICROSACCADE_MAGNITUDE = 0.002f; | 129 | const float MICROSACCADE_MAGNITUDE = 0.002f; |
| 130 | const float SACCADE_MAGNITUDE = 0.04f; | 130 | const float SACCADE_MAGNITUDE = 0.04f; |
| 131 | 131 | const float MAXIMUM_SACCADE_SPEED = 0.8f; | |
| 132 | |||
| 132 | if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) { | 133 | if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) { |
| 133 | _saccadeTarget = MICROSACCADE_MAGNITUDE * randVector(); | 134 | _saccadeTarget = MICROSACCADE_MAGNITUDE * randVector(); |
| 134 | } else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) { | 135 | } else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) { |
| 135 | _saccadeTarget = SACCADE_MAGNITUDE * randVector(); | 136 | _saccadeTarget = SACCADE_MAGNITUDE * randVector(); |
| 136 | } | 137 | } |
| 137 | _saccade += (_saccadeTarget - _saccade) * 0.50f; | 138 | |
| 138 | 139 | glm::vec3 saccadeDelta = (_saccadeTarget - _saccade) * 0.5f; | |
| 140 | float speed = glm::length(saccadeDelta) / deltaTime; | ||
| 141 | if (speed > MAXIMUM_SACCADE_SPEED) { | ||
| 142 | saccadeDelta = saccadeDelta * MAXIMUM_SACCADE_SPEED / speed; | ||
| 143 | } | ||
| 144 | _saccade += saccadeDelta; | ||
| 145 | |||
| 139 | // Detect transition from talking to not; force blink after that and a delay | 146 | // Detect transition from talking to not; force blink after that and a delay |
| 140 | bool forceBlink = false; | 147 | bool forceBlink = false; |
| 141 | const float TALKING_LOUDNESS = 100.0f; | 148 | const float TALKING_LOUDNESS = 100.0f; |
| @@ -59,10 +59,10 @@ const float MAX_WALKING_SPEED = 2.5f; // human walking speed · 58 unmodified lines | |||
| 59 | const float MAX_BOOST_SPEED = 0.5f * MAX_WALKING_SPEED; // keyboard motor gets additive boost below this speed | 59 | const float MAX_BOOST_SPEED = 0.5f * MAX_WALKING_SPEED; // keyboard motor gets additive boost below this speed |
| 60 | const float MIN_AVATAR_SPEED = 0.05f; // speed is set to zero below this | 60 | const float MIN_AVATAR_SPEED = 0.05f; // speed is set to zero below this |
| 61 | 61 | ||
| 62 | // TODO: normalize avatar speed for standard avatar size, then scale all motion logic | 62 | // TODO: normalize avatar speed for standard avatar size, then scale all motion logic |
| 63 | // to properly follow avatar size. | 63 | // to properly follow avatar size. |
| 64 | float MAX_AVATAR_SPEED = 300.0f; | 64 | float MAX_AVATAR_SPEED = 300.0f; |
| 65 | float MAX_KEYBOARD_MOTOR_SPEED = MAX_AVATAR_SPEED; | 65 | float MAX_KEYBOARD_MOTOR_SPEED = MAX_AVATAR_SPEED; |
| 66 | float DEFAULT_KEYBOARD_MOTOR_TIMESCALE = 0.25f; | 66 | float DEFAULT_KEYBOARD_MOTOR_TIMESCALE = 0.25f; |
| 67 | float MIN_SCRIPTED_MOTOR_TIMESCALE = 0.005f; | 67 | float MIN_SCRIPTED_MOTOR_TIMESCALE = 0.005f; |
| 68 | float DEFAULT_SCRIPTED_MOTOR_TIMESCALE = 1.0e6f; | 68 | float DEFAULT_SCRIPTED_MOTOR_TIMESCALE = 1.0e6f; |
| @@ -146,7 +146,7 @@ void MyAvatar::update(float deltaTime) { · 77 unmodified lines | |||
| 146 | if (_referential) { | 146 | if (_referential) { |
| 147 | _referential->update(); | 147 | _referential->update(); |
| 148 | } | 148 | } |
| 149 | 149 | ||
| 150 | Head* head = getHead(); | 150 | Head* head = getHead(); |
| 151 | head->relaxLean(deltaTime); | 151 | head->relaxLean(deltaTime); |
| 152 | updateFromTrackers(deltaTime); | 152 | updateFromTrackers(deltaTime); |
| @@ -163,12 +163,12 @@ void MyAvatar::update(float deltaTime) { · 10 unmodified lines | |||
| 163 | 163 | ||
| 164 | void MyAvatar::simulate(float deltaTime) { | 164 | void MyAvatar::simulate(float deltaTime) { |
| 165 | PerformanceTimer perfTimer("simulate"); | 165 | PerformanceTimer perfTimer("simulate"); |
| 166 | 166 | ||
| 167 | // Play back recording | 167 | // Play back recording |
| 168 | if (_player && _player->isPlaying()) { | 168 | if (_player && _player->isPlaying()) { |
| 169 | _player->play(); | 169 | _player->play(); |
| 170 | } | 170 | } |
| 171 | 171 | ||
| 172 | if (_scale != _targetScale) { | 172 | if (_scale != _targetScale) { |
| 173 | float scale = (1.0f - SMOOTHING_RATIO) * _scale + SMOOTHING_RATIO * _targetScale; | 173 | float scale = (1.0f - SMOOTHING_RATIO) * _scale + SMOOTHING_RATIO * _targetScale; |
| 174 | setScale(scale); | 174 | setScale(scale); |
| @@ -179,7 +179,7 @@ void MyAvatar::simulate(float deltaTime) { · 4 unmodified lines | |||
| 179 | updateOrientation(deltaTime); | 179 | updateOrientation(deltaTime); |
| 180 | updatePosition(deltaTime); | 180 | updatePosition(deltaTime); |
| 181 | } | 181 | } |
| 182 | 182 | ||
| 183 | { | 183 | { |
| 184 | PerformanceTimer perfTimer("hand"); | 184 | PerformanceTimer perfTimer("hand"); |
| 185 | // update avatar skeleton and simulate hand and head | 185 | // update avatar skeleton and simulate hand and head |
| @@ -222,12 +222,12 @@ void MyAvatar::simulate(float deltaTime) { · 36 unmodified lines | |||
| 222 | head->setScale(_scale); | 222 | head->setScale(_scale); |
| 223 | head->simulate(deltaTime, true); | 223 | head->simulate(deltaTime, true); |
| 224 | } | 224 | } |
| 225 | 225 | ||
| 226 | // Record avatars movements. | 226 | // Record avatars movements. |
| 227 | if (_recorder && _recorder->isRecording()) { | 227 | if (_recorder && _recorder->isRecording()) { |
| 228 | _recorder->record(); | 228 | _recorder->record(); |
| 229 | } | 229 | } |
| 230 | 230 | ||
| 231 | // consider updating our billboard | 231 | // consider updating our billboard |
| 232 | maybeUpdateBillboard(); | 232 | maybeUpdateBillboard(); |
| 233 | } | 233 | } |
| @@ -235,18 +235,18 @@ void MyAvatar::simulate(float deltaTime) { · 1 unmodified line | |||
| 235 | // Update avatar head rotation with sensor data | 235 | // Update avatar head rotation with sensor data |
| 236 | void MyAvatar::updateFromTrackers(float deltaTime) { | 236 | void MyAvatar::updateFromTrackers(float deltaTime) { |
| 237 | glm::vec3 estimatedPosition, estimatedRotation; | 237 | glm::vec3 estimatedPosition, estimatedRotation; |
| 238 | 238 | ||
| 239 | bool inHmd = qApp->isHMDMode(); | 239 | bool inHmd = qApp->isHMDMode(); |
| 240 | 240 | ||
| 241 | if (isPlaying() && inHmd) { | 241 | if (isPlaying() && inHmd) { |
| 242 | return; | 242 | return; |
| 243 | } | 243 | } |
| 244 | 244 | ||
| 245 | if (inHmd) { | 245 | if (inHmd) { |
| 246 | estimatedPosition = qApp->getHeadPosition(); | 246 | estimatedPosition = qApp->getHeadPosition(); |
| 247 | estimatedPosition.x *= -1.0f; | 247 | estimatedPosition.x *= -1.0f; |
| 248 | _trackedHeadPosition = estimatedPosition; | 248 | _trackedHeadPosition = estimatedPosition; |
| 249 | 249 | ||
| 250 | const float OCULUS_LEAN_SCALE = 0.05f; | 250 | const float OCULUS_LEAN_SCALE = 0.05f; |
| 251 | estimatedPosition /= OCULUS_LEAN_SCALE; | 251 | estimatedPosition /= OCULUS_LEAN_SCALE; |
| 252 | } else { | 252 | } else { |
| @@ -257,7 +257,7 @@ void MyAvatar::updateFromTrackers(float deltaTime) { · 4 unmodified lines | |||
| 257 | estimatedRotation = glm::degrees(safeEulerAngles(tracker->getHeadRotation())); | 257 | estimatedRotation = glm::degrees(safeEulerAngles(tracker->getHeadRotation())); |
| 258 | } | 258 | } |
| 259 | } | 259 | } |
| 260 | 260 | ||
| 261 | // Rotate the body if the head is turned beyond the screen | 261 | // Rotate the body if the head is turned beyond the screen |
| 262 | if (Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)) { | 262 | if (Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)) { |
| 263 | const float TRACKER_YAW_TURN_SENSITIVITY = 0.5f; | 263 | const float TRACKER_YAW_TURN_SENSITIVITY = 0.5f; |
| @@ -317,7 +317,7 @@ void MyAvatar::renderDebugBodyPoints() { · 53 unmodified lines | |||
| 317 | glm::vec3 headPosition(getHead()->getEyePosition()); | 317 | glm::vec3 headPosition(getHead()->getEyePosition()); |
| 318 | float torsoToHead = glm::length(headPosition - torsoPosition); | 318 | float torsoToHead = glm::length(headPosition - torsoPosition); |
| 319 | glm::vec3 position; | 319 | glm::vec3 position; |
| 320 | qCDebug(interfaceapp, "head-above-torso %.2f, scale = %0.2f", torsoToHead, getScale()); | 320 | qCDebug(interfaceapp, "head-above-torso %.2f, scale = %0.2f", (double)torsoToHead, (double)getScale()); |
| 321 | 321 | ||
| 322 | // Torso Sphere | 322 | // Torso Sphere |
| 323 | position = torsoPosition; | 323 | position = torsoPosition; |
| @@ -433,7 +433,7 @@ void MyAvatar::startRecording() { · 109 unmodified lines | |||
| 433 | auto audioClient = DependencyManager::get<AudioClient>(); | 433 | auto audioClient = DependencyManager::get<AudioClient>(); |
| 434 | connect(audioClient.data(), &AudioClient::inputReceived, _recorder.data(), | 434 | connect(audioClient.data(), &AudioClient::inputReceived, _recorder.data(), |
| 435 | &Recorder::recordAudio, Qt::BlockingQueuedConnection); | 435 | &Recorder::recordAudio, Qt::BlockingQueuedConnection); |
| 436 | 436 | ||
| 437 | _recorder->startRecording(); | 437 | _recorder->startRecording(); |
| 438 | } | 438 | } |
| 439 | 439 | ||
| @@ -449,7 +449,7 @@ void MyAvatar::stopRecording() { · 9 unmodified lines | |||
| 449 | // stop grabbing audio from the AudioClient | 449 | // stop grabbing audio from the AudioClient |
| 450 | auto audioClient = DependencyManager::get<AudioClient>(); | 450 | auto audioClient = DependencyManager::get<AudioClient>(); |
| 451 | disconnect(audioClient.data(), 0, _recorder.data(), 0); | 451 | disconnect(audioClient.data(), 0, _recorder.data(), 0); |
| 452 | 452 | ||
| 453 | _recorder->stopRecording(); | 453 | _recorder->stopRecording(); |
| 454 | } | 454 | } |
| 455 | } | 455 | } |
| @@ -481,7 +481,7 @@ void MyAvatar::loadLastRecording() { · 25 unmodified lines | |||
| 481 | if (!_player) { | 481 | if (!_player) { |
| 482 | _player = PlayerPointer(new Player(this)); | 482 | _player = PlayerPointer(new Player(this)); |
| 483 | } | 483 | } |
| 484 | 484 | ||
| 485 | _player->loadRecording(_recorder->getRecording()); | 485 | _player->loadRecording(_recorder->getRecording()); |
| 486 | } | 486 | } |
| 487 | 487 | ||
| @@ -573,7 +573,7 @@ AnimationDetails MyAvatar::getAnimationDetailsByRole(const QString& role) { · 85 unmodified lines | |||
| 573 | AnimationDetails result; | 573 | AnimationDetails result; |
| 574 | if (QThread::currentThread() != thread()) { | 574 | if (QThread::currentThread() != thread()) { |
| 575 | QMetaObject::invokeMethod(this, "getAnimationDetailsByRole", Qt::BlockingQueuedConnection, | 575 | QMetaObject::invokeMethod(this, "getAnimationDetailsByRole", Qt::BlockingQueuedConnection, |
| 576 | Q_RETURN_ARG(AnimationDetails, result), | 576 | Q_RETURN_ARG(AnimationDetails, result), |
| 577 | Q_ARG(const QString&, role)); | 577 | Q_ARG(const QString&, role)); |
| 578 | return result; | 578 | return result; |
| 579 | } | 579 | } |
| @@ -590,7 +590,7 @@ AnimationDetails MyAvatar::getAnimationDetails(const QString& url) { · 10 unmodified lines | |||
| 590 | AnimationDetails result; | 590 | AnimationDetails result; |
| 591 | if (QThread::currentThread() != thread()) { | 591 | if (QThread::currentThread() != thread()) { |
| 592 | QMetaObject::invokeMethod(this, "getAnimationDetails", Qt::BlockingQueuedConnection, | 592 | QMetaObject::invokeMethod(this, "getAnimationDetails", Qt::BlockingQueuedConnection, |
| 593 | Q_RETURN_ARG(AnimationDetails, result), | 593 | Q_RETURN_ARG(AnimationDetails, result), |
| 594 | Q_ARG(const QString&, url)); | 594 | Q_ARG(const QString&, url)); |
| 595 | return result; | 595 | return result; |
| 596 | } | 596 | } |
| @@ -638,7 +638,7 @@ void MyAvatar::saveData() { · 41 unmodified lines | |||
| 638 | settings.setValue("scale", attachment.scale); | 638 | settings.setValue("scale", attachment.scale); |
| 639 | } | 639 | } |
| 640 | settings.endArray(); | 640 | settings.endArray(); |
| 641 | 641 | ||
| 642 | settings.beginWriteArray("animationHandles"); | 642 | settings.beginWriteArray("animationHandles"); |
| 643 | for (int i = 0; i < _animationHandles.size(); i++) { | 643 | for (int i = 0; i < _animationHandles.size(); i++) { |
| 644 | settings.setArrayIndex(i); | 644 | settings.setArrayIndex(i); |
| @@ -655,7 +655,7 @@ void MyAvatar::saveData() { · 10 unmodified lines | |||
| 655 | settings.setValue("maskedJoints", pointer->getMaskedJoints()); | 655 | settings.setValue("maskedJoints", pointer->getMaskedJoints()); |
| 656 | } | 656 | } |
| 657 | settings.endArray(); | 657 | settings.endArray(); |
| 658 | 658 | ||
| 659 | settings.setValue("displayName", _displayName); | 659 | settings.setValue("displayName", _displayName); |
| 660 | 660 | ||
| 661 | settings.endGroup(); | 661 | settings.endGroup(); |
| @@ -681,7 +681,7 @@ void MyAvatar::loadData() { · 19 unmodified lines | |||
| 681 | _targetScale = loadSetting(settings, "scale", 1.0f); | 681 | _targetScale = loadSetting(settings, "scale", 1.0f); |
| 682 | setScale(_scale); | 682 | setScale(_scale); |
| 683 | 683 | ||
| 684 | // The old preferences only stored the face and skeleton URLs, we didn't track if the user wanted to use 1 or 2 urls | 684 | // The old preferences only stored the face and skeleton URLs, we didn't track if the user wanted to use 1 or 2 urls |
| 685 | // for their avatar, So we need to attempt to detect this old case and set our new preferences accordingly. If | 685 | // for their avatar, So we need to attempt to detect this old case and set our new preferences accordingly. If |
| 686 | // the head URL is empty, then we will assume they are using a full url... | 686 | // the head URL is empty, then we will assume they are using a full url... |
| 687 | bool isOldSettings = !(settings.contains("useFullAvatar") || settings.contains("fullAvatarURL")); | 687 | bool isOldSettings = !(settings.contains("useFullAvatar") || settings.contains("fullAvatarURL")); |
| @@ -693,7 +693,7 @@ void MyAvatar::loadData() { · 5 unmodified lines | |||
| 693 | _headModelName = settings.value("headModelName", DEFAULT_HEAD_MODEL_NAME).toString(); | 693 | _headModelName = settings.value("headModelName", DEFAULT_HEAD_MODEL_NAME).toString(); |
| 694 | _bodyModelName = settings.value("bodyModelName", DEFAULT_BODY_MODEL_NAME).toString(); | 694 | _bodyModelName = settings.value("bodyModelName", DEFAULT_BODY_MODEL_NAME).toString(); |
| 695 | _fullAvatarModelName = settings.value("fullAvatarModelName", DEFAULT_FULL_AVATAR_MODEL_NAME).toString(); | 695 | _fullAvatarModelName = settings.value("fullAvatarModelName", DEFAULT_FULL_AVATAR_MODEL_NAME).toString(); |
| 696 | 696 | ||
| 697 | if (isOldSettings) { | 697 | if (isOldSettings) { |
| 698 | bool assumeFullAvatar = _headURLFromPreferences.isEmpty(); | 698 | bool assumeFullAvatar = _headURLFromPreferences.isEmpty(); |
| 699 | _useFullAvatar = assumeFullAvatar; | 699 | _useFullAvatar = assumeFullAvatar; |
| @@ -719,14 +719,14 @@ void MyAvatar::loadData() { · 19 unmodified lines | |||
| 719 | QVariantHash bodyFST = FSTReader::downloadMapping(_skeletonURLFromPreferences.toString()); | 719 | QVariantHash bodyFST = FSTReader::downloadMapping(_skeletonURLFromPreferences.toString()); |
| 720 | _bodyModelName = bodyFST["name"].toString(); | 720 | _bodyModelName = bodyFST["name"].toString(); |
| 721 | } | 721 | } |
| 722 | 722 | ||
| 723 | if (_headURLFromPreferences == DEFAULT_HEAD_MODEL_URL) { | 723 | if (_headURLFromPreferences == DEFAULT_HEAD_MODEL_URL) { |
| 724 | _headModelName = DEFAULT_HEAD_MODEL_NAME; | 724 | _headModelName = DEFAULT_HEAD_MODEL_NAME; |
| 725 | } else { | 725 | } else { |
| 726 | QVariantHash headFST = FSTReader::downloadMapping(_headURLFromPreferences.toString()); | 726 | QVariantHash headFST = FSTReader::downloadMapping(_headURLFromPreferences.toString()); |
| 727 | _headModelName = headFST["name"].toString(); | 727 | _headModelName = headFST["name"].toString(); |
| 728 | } | 728 | } |
| 729 | 729 | ||
| 730 | _fullAvatarModelName = "Default"; | 730 | _fullAvatarModelName = "Default"; |
| 731 | } | 731 | } |
| 732 | } | 732 | } |
| @@ -736,7 +736,7 @@ void MyAvatar::loadData() { · 3 unmodified lines | |||
| 736 | } else { | 736 | } else { |
| 737 | useHeadAndBodyURLs(_headURLFromPreferences, _skeletonURLFromPreferences, _headModelName, _bodyModelName); | 737 | useHeadAndBodyURLs(_headURLFromPreferences, _skeletonURLFromPreferences, _headModelName, _bodyModelName); |
| 738 | } | 738 | } |
| 739 | 739 | ||
| 740 | QVector<AttachmentData> attachmentData; | 740 | QVector<AttachmentData> attachmentData; |
| 741 | int attachmentCount = settings.beginReadArray("attachmentData"); | 741 | int attachmentCount = settings.beginReadArray("attachmentData"); |
| 742 | for (int i = 0; i < attachmentCount; i++) { | 742 | for (int i = 0; i < attachmentCount; i++) { |
| @@ -757,7 +757,7 @@ void MyAvatar::loadData() { · 14 unmodified lines | |||
| 757 | } | 757 | } |
| 758 | settings.endArray(); | 758 | settings.endArray(); |
| 759 | setAttachmentData(attachmentData); | 759 | setAttachmentData(attachmentData); |
| 760 | 760 | ||
| 761 | int animationCount = settings.beginReadArray("animationHandles"); | 761 | int animationCount = settings.beginReadArray("animationHandles"); |
| 762 | while (_animationHandles.size() > animationCount) { | 762 | while (_animationHandles.size() > animationCount) { |
| 763 | _animationHandles.takeLast()->stop(); | 763 | _animationHandles.takeLast()->stop(); |
| @@ -780,7 +780,7 @@ void MyAvatar::loadData() { · 16 unmodified lines | |||
| 780 | handle->setStartAutomatically(settings.value("startAutomatically", true).toBool()); | 780 | handle->setStartAutomatically(settings.value("startAutomatically", true).toBool()); |
| 781 | } | 781 | } |
| 782 | settings.endArray(); | 782 | settings.endArray(); |
| 783 | 783 | ||
| 784 | setDisplayName(settings.value("displayName").toString()); | 784 | setDisplayName(settings.value("displayName").toString()); |
| 785 | 785 | ||
| 786 | settings.endGroup(); | 786 | settings.endGroup(); |
| @@ -792,7 +792,7 @@ void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const { · 5 unmodified lines | |||
| 792 | settings.beginGroup(_skeletonModel.getURL().toString()); | 792 | settings.beginGroup(_skeletonModel.getURL().toString()); |
| 793 | settings.beginGroup(attachment.modelURL.toString()); | 793 | settings.beginGroup(attachment.modelURL.toString()); |
| 794 | settings.setValue("jointName", attachment.jointName); | 794 | settings.setValue("jointName", attachment.jointName); |
| 795 | 795 | ||
| 796 | settings.beginGroup(attachment.jointName); | 796 | settings.beginGroup(attachment.jointName); |
| 797 | settings.setValue("translation_x", attachment.translation.x); | 797 | settings.setValue("translation_x", attachment.translation.x); |
| 798 | settings.setValue("translation_y", attachment.translation.y); | 798 | settings.setValue("translation_y", attachment.translation.y); |
| @@ -802,7 +802,7 @@ void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const { · 3 unmodified lines | |||
| 802 | settings.setValue("rotation_y", eulers.y); | 802 | settings.setValue("rotation_y", eulers.y); |
| 803 | settings.setValue("rotation_z", eulers.z); | 803 | settings.setValue("rotation_z", eulers.z); |
| 804 | settings.setValue("scale", attachment.scale); | 804 | settings.setValue("scale", attachment.scale); |
| 805 | 805 | ||
| 806 | settings.endGroup(); | 806 | settings.endGroup(); |
| 807 | settings.endGroup(); | 807 | settings.endGroup(); |
| 808 | settings.endGroup(); | 808 | settings.endGroup(); |
| @@ -814,7 +814,7 @@ AttachmentData MyAvatar::loadAttachmentData(const QUrl& modelURL, const QString& · 5 unmodified lines | |||
| 814 | settings.beginGroup("savedAttachmentData"); | 814 | settings.beginGroup("savedAttachmentData"); |
| 815 | settings.beginGroup(_skeletonModel.getURL().toString()); | 815 | settings.beginGroup(_skeletonModel.getURL().toString()); |
| 816 | settings.beginGroup(modelURL.toString()); | 816 | settings.beginGroup(modelURL.toString()); |
| 817 | 817 | ||
| 818 | AttachmentData attachment; | 818 | AttachmentData attachment; |
| 819 | attachment.modelURL = modelURL; | 819 | attachment.modelURL = modelURL; |
| 820 | if (jointName.isEmpty()) { | 820 | if (jointName.isEmpty()) { |
| @@ -836,18 +836,18 @@ AttachmentData MyAvatar::loadAttachmentData(const QUrl& modelURL, const QString& · 15 unmodified lines | |||
| 836 | } else { | 836 | } else { |
| 837 | attachment = AttachmentData(); | 837 | attachment = AttachmentData(); |
| 838 | } | 838 | } |
| 839 | 839 | ||
| 840 | settings.endGroup(); | 840 | settings.endGroup(); |
| 841 | settings.endGroup(); | 841 | settings.endGroup(); |
| 842 | settings.endGroup(); | 842 | settings.endGroup(); |
| 843 | settings.endGroup(); | 843 | settings.endGroup(); |
| 844 | 844 | ||
| 845 | return attachment; | 845 | return attachment; |
| 846 | } | 846 | } |
| 847 | 847 | ||
| 848 | int MyAvatar::parseDataAtOffset(const QByteArray& packet, int offset) { | 848 | int MyAvatar::parseDataAtOffset(const QByteArray& packet, int offset) { |
| 849 | qCDebug(interfaceapp) << "Error: ignoring update packet for MyAvatar" | 849 | qCDebug(interfaceapp) << "Error: ignoring update packet for MyAvatar" |
| 850 | << " packetLength = " << packet.size() | 850 | << " packetLength = " << packet.size() |
| 851 | << " offset = " << offset; | 851 | << " offset = " << offset; |
| 852 | // this packet is just bad, so we pretend that we unpacked it ALL | 852 | // this packet is just bad, so we pretend that we unpacked it ALL |
| 853 | return packet.size() - offset; | 853 | return packet.size() - offset; |
| @@ -865,14 +865,14 @@ void MyAvatar::updateLookAtTargetAvatar() { · 11 unmodified lines | |||
| 865 | // | 865 | // |
| 866 | _lookAtTargetAvatar.reset(); | 866 | _lookAtTargetAvatar.reset(); |
| 867 | _targetAvatarPosition = glm::vec3(0.0f); | 867 | _targetAvatarPosition = glm::vec3(0.0f); |
| 868 | 868 | ||
| 869 | glm::vec3 lookForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FRONT; | 869 | glm::vec3 lookForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FRONT; |
| 870 | glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); | 870 | glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); |
| 871 | 871 | ||
| 872 | float smallestAngleTo = glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES) / 2.0f; | 872 | float smallestAngleTo = glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES) / 2.0f; |
| 873 | const float KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR = 1.3f; | 873 | const float KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR = 1.3f; |
| 874 | const float GREATEST_LOOKING_AT_DISTANCE = 10.0f; | 874 | const float GREATEST_LOOKING_AT_DISTANCE = 10.0f; |
| 875 | 875 | ||
| 876 | int howManyLookingAtMe = 0; | 876 | int howManyLookingAtMe = 0; |
| 877 | foreach (const AvatarSharedPointer& avatarPointer, DependencyManager::get<AvatarManager>()->getAvatarHash()) { | 877 | foreach (const AvatarSharedPointer& avatarPointer, DependencyManager::get<AvatarManager>()->getAvatarHash()) { |
| 878 | Avatar* avatar = static_cast<Avatar*>(avatarPointer.get()); | 878 | Avatar* avatar = static_cast<Avatar*>(avatarPointer.get()); |
| @@ -1004,13 +1004,13 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN · 125 unmodified lines | |||
| 1004 | } | 1004 | } |
| 1005 | 1005 | ||
| 1006 | _useFullAvatar = true; | 1006 | _useFullAvatar = true; |
| 1007 | 1007 | ||
| 1008 | if (_fullAvatarURLFromPreferences != fullAvatarURL) { | 1008 | if (_fullAvatarURLFromPreferences != fullAvatarURL) { |
| 1009 | _fullAvatarURLFromPreferences = fullAvatarURL; | 1009 | _fullAvatarURLFromPreferences = fullAvatarURL; |
| 1010 | if (modelName.isEmpty()) { | 1010 | if (modelName.isEmpty()) { |
| 1011 | QVariantHash fullAvatarFST = FSTReader::downloadMapping(_fullAvatarURLFromPreferences.toString()); | 1011 | QVariantHash fullAvatarFST = FSTReader::downloadMapping(_fullAvatarURLFromPreferences.toString()); |
| 1012 | _fullAvatarModelName = fullAvatarFST["name"].toString(); | 1012 | _fullAvatarModelName = fullAvatarFST["name"].toString(); |
| 1013 | } else { | 1013 | } else { |
| 1014 | _fullAvatarModelName = modelName; | 1014 | _fullAvatarModelName = modelName; |
| 1015 | } | 1015 | } |
| 1016 | } | 1016 | } |
| @@ -1051,7 +1051,7 @@ void MyAvatar::useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL, cons · 34 unmodified lines | |||
| 1051 | if (headName.isEmpty()) { | 1051 | if (headName.isEmpty()) { |
| 1052 | QVariantHash headFST = FSTReader::downloadMapping(_headURLFromPreferences.toString()); | 1052 | QVariantHash headFST = FSTReader::downloadMapping(_headURLFromPreferences.toString()); |
| 1053 | _headModelName = headFST["name"].toString(); | 1053 | _headModelName = headFST["name"].toString(); |
| 1054 | } else { | 1054 | } else { |
| 1055 | _headModelName = headName; | 1055 | _headModelName = headName; |
| 1056 | } | 1056 | } |
| 1057 | } | 1057 | } |
| @@ -1061,7 +1061,7 @@ void MyAvatar::useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL, cons · 3 unmodified lines | |||
| 1061 | if (bodyName.isEmpty()) { | 1061 | if (bodyName.isEmpty()) { |
| 1062 | QVariantHash bodyFST = FSTReader::downloadMapping(_skeletonURLFromPreferences.toString()); | 1062 | QVariantHash bodyFST = FSTReader::downloadMapping(_skeletonURLFromPreferences.toString()); |
| 1063 | _bodyModelName = bodyFST["name"].toString(); | 1063 | _bodyModelName = bodyFST["name"].toString(); |
| 1064 | } else { | 1064 | } else { |
| 1065 | _bodyModelName = bodyName; | 1065 | _bodyModelName = bodyName; |
| 1066 | } | 1066 | } |
| 1067 | } | 1067 | } |
| @@ -1092,7 +1092,7 @@ void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) · 24 unmodified lines | |||
| 1092 | glm::vec3 MyAvatar::getSkeletonPosition() const { | 1092 | glm::vec3 MyAvatar::getSkeletonPosition() const { |
| 1093 | CameraMode mode = Application::getInstance()->getCamera()->getMode(); | 1093 | CameraMode mode = Application::getInstance()->getCamera()->getMode(); |
| 1094 | if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) { | 1094 | if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) { |
| 1095 | // The avatar is rotated PI about the yAxis, so we have to correct for it | 1095 | // The avatar is rotated PI about the yAxis, so we have to correct for it |
| 1096 | // to get the skeleton offset contribution in the world-frame. | 1096 | // to get the skeleton offset contribution in the world-frame. |
| 1097 | const glm::quat FLIP = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); | 1097 | const glm::quat FLIP = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); |
| 1098 | glm::vec3 skeletonOffset = _skeletonOffset; | 1098 | glm::vec3 skeletonOffset = _skeletonOffset; |
| @@ -1135,9 +1135,9 @@ void MyAvatar::setScriptedMotorVelocity(const glm::vec3& velocity) { · 36 unmodified lines | |||
| 1135 | } | 1135 | } |
| 1136 | 1136 | ||
| 1137 | void MyAvatar::setScriptedMotorTimescale(float timescale) { | 1137 | void MyAvatar::setScriptedMotorTimescale(float timescale) { |
| 1138 | // we clamp the timescale on the large side (instead of just the low side) to prevent | 1138 | // we clamp the timescale on the large side (instead of just the low side) to prevent |
| 1139 | // obnoxiously large values from introducing NaN into avatar's velocity | 1139 | // obnoxiously large values from introducing NaN into avatar's velocity |
| 1140 | _scriptedMotorTimescale = glm::clamp(timescale, MIN_SCRIPTED_MOTOR_TIMESCALE, | 1140 | _scriptedMotorTimescale = glm::clamp(timescale, MIN_SCRIPTED_MOTOR_TIMESCALE, |
| 1141 | DEFAULT_SCRIPTED_MOTOR_TIMESCALE); | 1141 | DEFAULT_SCRIPTED_MOTOR_TIMESCALE); |
| 1142 | } | 1142 | } |
| 1143 | 1143 | ||
| @@ -1155,14 +1155,14 @@ void MyAvatar::clearScriptableSettings() { · 11 unmodified lines | |||
| 1155 | clearJointAnimationPriorities(); | 1155 | clearJointAnimationPriorities(); |
| 1156 | _scriptedMotorVelocity = glm::vec3(0.0f); | 1156 | _scriptedMotorVelocity = glm::vec3(0.0f); |
| 1157 | _scriptedMotorTimescale = DEFAULT_SCRIPTED_MOTOR_TIMESCALE; | 1157 | _scriptedMotorTimescale = DEFAULT_SCRIPTED_MOTOR_TIMESCALE; |
| 1158 | } | 1158 | } |
| 1159 | 1159 | ||
| 1160 | void MyAvatar::attach(const QString& modelURL, const QString& jointName, const glm::vec3& translation, | 1160 | void MyAvatar::attach(const QString& modelURL, const QString& jointName, const glm::vec3& translation, |
| 1161 | const glm::quat& rotation, float scale, bool allowDuplicates, bool useSaved) { | 1161 | const glm::quat& rotation, float scale, bool allowDuplicates, bool useSaved) { |
| 1162 | if (QThread::currentThread() != thread()) { | 1162 | if (QThread::currentThread() != thread()) { |
| 1163 | Avatar::attach(modelURL, jointName, translation, rotation, scale, allowDuplicates, useSaved); | 1163 | Avatar::attach(modelURL, jointName, translation, rotation, scale, allowDuplicates, useSaved); |
| 1164 | return; | 1164 | return; |
| 1165 | } | 1165 | } |
| 1166 | if (useSaved) { | 1166 | if (useSaved) { |
| 1167 | AttachmentData attachment = loadAttachmentData(modelURL, jointName); | 1167 | AttachmentData attachment = loadAttachmentData(modelURL, jointName); |
| 1168 | if (attachment.isValid()) { | 1168 | if (attachment.isValid()) { |
| @@ -1199,7 +1199,7 @@ const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f; · 30 unmodified lines | |||
| 1199 | 1199 | ||
| 1200 | bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const { | 1200 | bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const { |
| 1201 | const Head* head = getHead(); | 1201 | const Head* head = getHead(); |
| 1202 | return (renderArgs->_renderMode != RenderArgs::NORMAL_RENDER_MODE) || (Application::getInstance()->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON) || | 1202 | return (renderArgs->_renderMode != RenderArgs::NORMAL_RENDER_MODE) || (Application::getInstance()->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON) || |
| 1203 | (glm::length(cameraPosition - head->getEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE * _scale); | 1203 | (glm::length(cameraPosition - head->getEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE * _scale); |
| 1204 | } | 1204 | } |
| 1205 | 1205 | ||
| @@ -1230,10 +1230,10 @@ void MyAvatar::updateOrientation(float deltaTime) { · 24 unmodified lines | |||
| 1230 | 1230 | ||
| 1231 | //Invert yaw and roll when in mirror mode | 1231 | //Invert yaw and roll when in mirror mode |
| 1232 | if (Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_MIRROR) { | 1232 | if (Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_MIRROR) { |
| 1233 | YAW(euler) *= -1.0; | 1233 | YAW(euler) *= -1.0f; |
| 1234 | ROLL(euler) *= -1.0; | 1234 | ROLL(euler) *= -1.0f; |
| 1235 | } | 1235 | } |
| 1236 | 1236 | ||
| 1237 | Head* head = getHead(); | 1237 | Head* head = getHead(); |
| 1238 | head->setBaseYaw(YAW(euler)); | 1238 | head->setBaseYaw(YAW(euler)); |
| 1239 | head->setBasePitch(PITCH(euler)); | 1239 | head->setBasePitch(PITCH(euler)); |
| @@ -1246,9 +1246,9 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe · 6 unmodified lines | |||
| 1246 | return localVelocity; | 1246 | return localVelocity; |
| 1247 | } | 1247 | } |
| 1248 | // compute motor efficiency | 1248 | // compute motor efficiency |
| 1249 | // The timescale of the motor is the approximate time it takes for the motor to | 1249 | // The timescale of the motor is the approximate time it takes for the motor to |
| 1250 | // accomplish its intended localVelocity. A short timescale makes the motor strong, | 1250 | // accomplish its intended localVelocity. A short timescale makes the motor strong, |
| 1251 | // and a long timescale makes it weak. The value of timescale to use depends | 1251 | // and a long timescale makes it weak. The value of timescale to use depends |
| 1252 | // on what the motor is doing: | 1252 | // on what the motor is doing: |
| 1253 | // | 1253 | // |
| 1254 | // (1) braking --> short timescale (aggressive motor assertion) | 1254 | // (1) braking --> short timescale (aggressive motor assertion) |
| @@ -1259,8 +1259,8 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe · 4 unmodified lines | |||
| 1259 | float MIN_KEYBOARD_BRAKE_SPEED = 0.3f; | 1259 | float MIN_KEYBOARD_BRAKE_SPEED = 0.3f; |
| 1260 | float timescale = MAX_KEYBOARD_MOTOR_TIMESCALE; | 1260 | float timescale = MAX_KEYBOARD_MOTOR_TIMESCALE; |
| 1261 | bool isThrust = (glm::length2(_thrust) > EPSILON); | 1261 | bool isThrust = (glm::length2(_thrust) > EPSILON); |
| 1262 | if (_isPushing || isThrust || | 1262 | if (_isPushing || isThrust || |
| 1263 | (_scriptedMotorTimescale < MAX_KEYBOARD_MOTOR_TIMESCALE && | 1263 | (_scriptedMotorTimescale < MAX_KEYBOARD_MOTOR_TIMESCALE && |
| 1264 | _motionBehaviors | AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED)) { | 1264 | _motionBehaviors | AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED)) { |
| 1265 | // we don't want to brake if something is pushing the avatar around | 1265 | // we don't want to brake if something is pushing the avatar around |
| 1266 | timescale = _keyboardMotorTimescale; | 1266 | timescale = _keyboardMotorTimescale; |
| @@ -1277,18 +1277,18 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe · 10 unmodified lines | |||
| 1277 | float motorEfficiency = glm::clamp(deltaTime / timescale, 0.0f, 1.0f); | 1277 | float motorEfficiency = glm::clamp(deltaTime / timescale, 0.0f, 1.0f); |
| 1278 | 1278 | ||
| 1279 | glm::vec3 newLocalVelocity = localVelocity; | 1279 | glm::vec3 newLocalVelocity = localVelocity; |
| 1280 | float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) + | 1280 | float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) + |
| 1281 | (fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT])) + | 1281 | (fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT])) + |
| 1282 | fabsf(_driveKeys[UP] - _driveKeys[DOWN]); | 1282 | fabsf(_driveKeys[UP] - _driveKeys[DOWN]); |
| 1283 | if (keyboardInput) { | 1283 | if (keyboardInput) { |
| 1284 | // Compute keyboard input | 1284 | // Compute keyboard input |
| 1285 | glm::vec3 front = (_driveKeys[FWD] - _driveKeys[BACK]) * IDENTITY_FRONT; | 1285 | glm::vec3 front = (_driveKeys[FWD] - _driveKeys[BACK]) * IDENTITY_FRONT; |
| 1286 | glm::vec3 right = (_driveKeys[RIGHT] - _driveKeys[LEFT]) * IDENTITY_RIGHT; | 1286 | glm::vec3 right = (_driveKeys[RIGHT] - _driveKeys[LEFT]) * IDENTITY_RIGHT; |
| 1287 | glm::vec3 up = (_driveKeys[UP] - _driveKeys[DOWN]) * IDENTITY_UP; | 1287 | glm::vec3 up = (_driveKeys[UP] - _driveKeys[DOWN]) * IDENTITY_UP; |
| 1288 | 1288 | ||
| 1289 | glm::vec3 direction = front + right + up; | 1289 | glm::vec3 direction = front + right + up; |
| 1290 | float directionLength = glm::length(direction); | 1290 | float directionLength = glm::length(direction); |
| 1291 | 1291 | ||
| 1292 | // Compute motor magnitude | 1292 | // Compute motor magnitude |
| 1293 | if (directionLength > EPSILON) { | 1293 | if (directionLength > EPSILON) { |
| 1294 | direction /= directionLength; | 1294 | direction /= directionLength; |
| @@ -1318,7 +1318,7 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe · 23 unmodified lines | |||
| 1318 | motorEfficiency = glm::clamp(deltaTime / WALK_ACCELERATION_TIMESCALE, 0.0f, 1.0f); | 1318 | motorEfficiency = glm::clamp(deltaTime / WALK_ACCELERATION_TIMESCALE, 0.0f, 1.0f); |
| 1319 | } | 1319 | } |
| 1320 | _isPushing = true; | 1320 | _isPushing = true; |
| 1321 | } | 1321 | } |
| 1322 | newLocalVelocity = localVelocity + motorEfficiency * (_keyboardMotorVelocity - localVelocity); | 1322 | newLocalVelocity = localVelocity + motorEfficiency * (_keyboardMotorVelocity - localVelocity); |
| 1323 | } else { | 1323 | } else { |
| 1324 | _keyboardMotorVelocity = glm::vec3(0.0f); | 1324 | _keyboardMotorVelocity = glm::vec3(0.0f); |
| @@ -1382,13 +1382,13 @@ void MyAvatar::updatePosition(float deltaTime) { · 57 unmodified lines | |||
| 1382 | _targetVelocity *= MAX_AVATAR_SPEED / speed; | 1382 | _targetVelocity *= MAX_AVATAR_SPEED / speed; |
| 1383 | speed = MAX_AVATAR_SPEED; | 1383 | speed = MAX_AVATAR_SPEED; |
| 1384 | } | 1384 | } |
| 1385 | 1385 | ||
| 1386 | if (speed > MIN_AVATAR_SPEED && !_characterController.isEnabled()) { | 1386 | if (speed > MIN_AVATAR_SPEED && !_characterController.isEnabled()) { |
| 1387 | // update position ourselves | 1387 | // update position ourselves |
| 1388 | applyPositionDelta(deltaTime * _targetVelocity); | 1388 | applyPositionDelta(deltaTime * _targetVelocity); |
| 1389 | measureMotionDerivatives(deltaTime); | 1389 | measureMotionDerivatives(deltaTime); |
| 1390 | } // else physics will move avatar later | 1390 | } // else physics will move avatar later |
| 1391 | 1391 | ||
| 1392 | // update _moving flag based on speed | 1392 | // update _moving flag based on speed |
| 1393 | const float MOVING_SPEED_THRESHOLD = 0.01f; | 1393 | const float MOVING_SPEED_THRESHOLD = 0.01f; |
| 1394 | _moving = speed > MOVING_SPEED_THRESHOLD; | 1394 | _moving = speed > MOVING_SPEED_THRESHOLD; |
| @@ -1405,7 +1405,7 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float · 10 unmodified lines | |||
| 1405 | float xzDistance = sqrt(positionBA.x * positionBA.x + positionBA.z * positionBA.z); | 1405 | float xzDistance = sqrt(positionBA.x * positionBA.x + positionBA.z * positionBA.z); |
| 1406 | if (xzDistance < (radiusA + radiusB)) { | 1406 | if (xzDistance < (radiusA + radiusB)) { |
| 1407 | float yDistance = fabs(positionBA.y); | 1407 | float yDistance = fabs(positionBA.y); |
| 1408 | float halfHeights = 0.5 * (heightA + heightB); | 1408 | float halfHeights = 0.5f * (heightA + heightB); |
| 1409 | if (yDistance < halfHeights) { | 1409 | if (yDistance < halfHeights) { |
| 1410 | // cylinders collide | 1410 | // cylinders collide |
| 1411 | if (xzDistance > 0.0f) { | 1411 | if (xzDistance > 0.0f) { |
| @@ -1448,60 +1448,60 @@ void MyAvatar::maybeUpdateBillboard() { · 36 unmodified lines | |||
| 1448 | } | 1448 | } |
| 1449 | gpu::Context context(new gpu::GLBackend()); | 1449 | gpu::Context context(new gpu::GLBackend()); |
| 1450 | RenderArgs renderArgs(&context); | 1450 | RenderArgs renderArgs(&context); |
| 1451 | QImage image = Application::getInstance()->renderAvatarBillboard(&renderArgs); | 1451 | QImage image = qApp->renderAvatarBillboard(&renderArgs); |
| 1452 | _billboard.clear(); | 1452 | _billboard.clear(); |
| 1453 | QBuffer buffer(&_billboard); | 1453 | QBuffer buffer(&_billboard); |
| 1454 | buffer.open(QIODevice::WriteOnly); | 1454 | buffer.open(QIODevice::WriteOnly); |
| 1455 | image.save(&buffer, "PNG"); | 1455 | image.save(&buffer, "PNG"); |
| 1456 | _billboardValid = true; | 1456 | _billboardValid = true; |
| 1457 | 1457 | ||
| 1458 | sendBillboardPacket(); | 1458 | sendBillboardPacket(); |
| 1459 | } | 1459 | } |
| 1460 | 1460 | ||
| 1461 | void MyAvatar::increaseSize() { | 1461 | void MyAvatar::increaseSize() { |
| 1462 | if ((1.0f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) { | 1462 | if ((1.0f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) { |
| 1463 | _targetScale *= (1.0f + SCALING_RATIO); | 1463 | _targetScale *= (1.0f + SCALING_RATIO); |
| 1464 | qCDebug(interfaceapp, "Changed scale to %f", _targetScale); | 1464 | qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale); |
| 1465 | } | 1465 | } |
| 1466 | } | 1466 | } |
| 1467 | 1467 | ||
| 1468 | void MyAvatar::decreaseSize() { | 1468 | void MyAvatar::decreaseSize() { |
| 1469 | if (MIN_AVATAR_SCALE < (1.0f - SCALING_RATIO) * _targetScale) { | 1469 | if (MIN_AVATAR_SCALE < (1.0f - SCALING_RATIO) * _targetScale) { |
| 1470 | _targetScale *= (1.0f - SCALING_RATIO); | 1470 | _targetScale *= (1.0f - SCALING_RATIO); |
| 1471 | qCDebug(interfaceapp, "Changed scale to %f", _targetScale); | 1471 | qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale); |
| 1472 | } | 1472 | } |
| 1473 | } | 1473 | } |
| 1474 | 1474 | ||
| 1475 | void MyAvatar::resetSize() { | 1475 | void MyAvatar::resetSize() { |
| 1476 | _targetScale = 1.0f; | 1476 | _targetScale = 1.0f; |
| 1477 | qCDebug(interfaceapp, "Reseted scale to %f", _targetScale); | 1477 | qCDebug(interfaceapp, "Reseted scale to %f", (double)_targetScale); |
| 1478 | } | 1478 | } |
| 1479 | 1479 | ||
| 1480 | void MyAvatar::goToLocation(const glm::vec3& newPosition, | 1480 | void MyAvatar::goToLocation(const glm::vec3& newPosition, |
| 1481 | bool hasOrientation, const glm::quat& newOrientation, | 1481 | bool hasOrientation, const glm::quat& newOrientation, |
| 1482 | bool shouldFaceLocation) { | 1482 | bool shouldFaceLocation) { |
| 1483 | 1483 | ||
| 1484 | qCDebug(interfaceapp).nospace() << "MyAvatar goToLocation - moving to " << newPosition.x << ", " | 1484 | qCDebug(interfaceapp).nospace() << "MyAvatar goToLocation - moving to " << newPosition.x << ", " |
| 1485 | << newPosition.y << ", " << newPosition.z; | 1485 | << newPosition.y << ", " << newPosition.z; |
| 1486 | 1486 | ||
| 1487 | glm::vec3 shiftedPosition = newPosition; | 1487 | glm::vec3 shiftedPosition = newPosition; |
| 1488 | 1488 | ||
| 1489 | if (hasOrientation) { | 1489 | if (hasOrientation) { |
| 1490 | qCDebug(interfaceapp).nospace() << "MyAvatar goToLocation - new orientation is " | 1490 | qCDebug(interfaceapp).nospace() << "MyAvatar goToLocation - new orientation is " |
| 1491 | << newOrientation.x << ", " << newOrientation.y << ", " << newOrientation.z << ", " << newOrientation.w; | 1491 | << newOrientation.x << ", " << newOrientation.y << ", " << newOrientation.z << ", " << newOrientation.w; |
| 1492 | 1492 | ||
| 1493 | // orient the user to face the target | 1493 | // orient the user to face the target |
| 1494 | glm::quat quatOrientation = newOrientation; | 1494 | glm::quat quatOrientation = newOrientation; |
| 1495 | 1495 | ||
| 1496 | if (shouldFaceLocation) { | 1496 | if (shouldFaceLocation) { |
| 1497 | 1497 | ||
| 1498 | quatOrientation = newOrientation * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); | 1498 | quatOrientation = newOrientation * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); |
| 1499 | 1499 | ||
| 1500 | // move the user a couple units away | 1500 | // move the user a couple units away |
| 1501 | const float DISTANCE_TO_USER = 2.0f; | 1501 | const float DISTANCE_TO_USER = 2.0f; |
| 1502 | shiftedPosition = newPosition - quatOrientation * IDENTITY_FRONT * DISTANCE_TO_USER; | 1502 | shiftedPosition = newPosition - quatOrientation * IDENTITY_FRONT * DISTANCE_TO_USER; |
| 1503 | } | 1503 | } |
| 1504 | 1504 | ||
| 1505 | setOrientation(quatOrientation); | 1505 | setOrientation(quatOrientation); |
| 1506 | } | 1506 | } |
| 1507 | 1507 | ||
| @@ -1547,7 +1547,6 @@ void MyAvatar::renderLaserPointers() { · 39 unmodified lines | |||
| 1547 | 1547 | ||
| 1548 | //Gets the tip position for the laser pointer | 1548 | //Gets the tip position for the laser pointer |
| 1549 | glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) { | 1549 | glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) { |
| 1550 | const ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay(); | ||
| 1551 | glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition()); | 1550 | glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition()); |
| 1552 | 1551 | ||
| 1553 | glm::vec3 position = palm->getPosition(); | 1552 | glm::vec3 position = palm->getPosition(); |
| @@ -1556,7 +1555,8 @@ glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) { · 2 unmodified lines | |||
| 1556 | 1555 | ||
| 1557 | 1556 | ||
| 1558 | glm::vec3 result; | 1557 | glm::vec3 result; |
| 1559 | if (applicationOverlay.calculateRayUICollisionPoint(position, direction, result)) { | 1558 | const auto& compositor = qApp->getApplicationCompositor(); |
| 1559 | if (compositor.calculateRayUICollisionPoint(position, direction, result)) { | ||
| 1560 | return result; | 1560 | return result; |
| 1561 | } | 1561 | } |
| 1562 | 1562 | ||
| @@ -1569,8 +1569,8 @@ void MyAvatar::clearDriveKeys() { · 6 unmodified lines | |||
| 1569 | } | 1569 | } |
| 1570 | } | 1570 | } |
| 1571 | 1571 | ||
| 1572 | void MyAvatar::relayDriveKeysToCharacterController() { | 1572 | void MyAvatar::relayDriveKeysToCharacterController() { |
| 1573 | if (_driveKeys[UP] > 0.0f) { | 1573 | if (_driveKeys[UP] > 0.0f) { |
| 1574 | _characterController.jump(); | 1574 | _characterController.jump(); |
| 1575 | } | 1575 | } |
| 1576 | } | 1576 | } |
| @@ -90,7 +90,7 @@ public: · 89 unmodified lines | |||
| 90 | 90 | ||
| 91 | void relayDriveKeysToCharacterController(); | 91 | void relayDriveKeysToCharacterController(); |
| 92 | 92 | ||
| 93 | bool isMyAvatar() { return true; } | 93 | bool isMyAvatar() const { return true; } |
| 94 | 94 | ||
| 95 | bool isLookingAtLeftEye(); | 95 | bool isLookingAtLeftEye(); |
| 96 | 96 | ||
| @@ -1,121 +0,0 @@ | |||
| 1 | // | ||
| 2 | // CameraToolBox.cpp | ||
| 3 | // interface/src/devices | ||
| 4 | // | ||
| 5 | // Created by David Rowe on 30 Apr 2015. | ||
| 6 | // Copyright 2015 High Fidelity, Inc. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | |||
| 12 | #include "InterfaceConfig.h" | ||
| 13 | |||
| 14 | #include <GLCanvas.h> | ||
| 15 | #include <PathUtils.h> | ||
| 16 | |||
| 17 | #include "gpu/GLBackend.h" | ||
| 18 | #include "Application.h" | ||
| 19 | #include "CameraToolBox.h" | ||
| 20 | #include "FaceTracker.h" | ||
| 21 | |||
| 22 | |||
| 23 | CameraToolBox::CameraToolBox() : | ||
| 24 | _iconPulseTimeReference(usecTimestampNow()), | ||
| 25 | _doubleClickTimer(NULL) | ||
| 26 | { | ||
| 27 | } | ||
| 28 | |||
| 29 | CameraToolBox::~CameraToolBox() { | ||
| 30 | if (_doubleClickTimer) { | ||
| 31 | _doubleClickTimer->stop(); | ||
| 32 | delete _doubleClickTimer; | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | bool CameraToolBox::mousePressEvent(int x, int y) { | ||
| 37 | if (_iconBounds.contains(x, y)) { | ||
| 38 | if (!_doubleClickTimer) { | ||
| 39 | // Toggle mute after waiting to check that it's not a double-click. | ||
| 40 | const int DOUBLE_CLICK_WAIT = 200; // ms | ||
| 41 | _doubleClickTimer = new QTimer(this); | ||
| 42 | connect(_doubleClickTimer, SIGNAL(timeout()), this, SLOT(toggleMute())); | ||
| 43 | _doubleClickTimer->setSingleShot(true); | ||
| 44 | _doubleClickTimer->setInterval(DOUBLE_CLICK_WAIT); | ||
| 45 | _doubleClickTimer->start(); | ||
| 46 | } | ||
| 47 | return true; | ||
| 48 | } | ||
| 49 | return false; | ||
| 50 | } | ||
| 51 | |||
| 52 | bool CameraToolBox::mouseDoublePressEvent(int x, int y) { | ||
| 53 | if (_iconBounds.contains(x, y)) { | ||
| 54 | if (_doubleClickTimer) { | ||
| 55 | _doubleClickTimer->stop(); | ||
| 56 | delete _doubleClickTimer; | ||
| 57 | _doubleClickTimer = NULL; | ||
| 58 | } | ||
| 59 | Application::getInstance()->resetSensors(); | ||
| 60 | return true; | ||
| 61 | } | ||
| 62 | return false; | ||
| 63 | } | ||
| 64 | |||
| 65 | void CameraToolBox::toggleMute() { | ||
| 66 | delete _doubleClickTimer; | ||
| 67 | _doubleClickTimer = NULL; | ||
| 68 | |||
| 69 | FaceTracker* faceTracker = Application::getInstance()->getSelectedFaceTracker(); | ||
| 70 | if (faceTracker) { | ||
| 71 | faceTracker->toggleMute(); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | void CameraToolBox::render(int x, int y, bool boxed) { | ||
| 76 | glEnable(GL_TEXTURE_2D); | ||
| 77 | |||
| 78 | if (!_enabledTexture) { | ||
| 79 | _enabledTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/face.svg"); | ||
| 80 | } | ||
| 81 | if (!_mutedTexture) { | ||
| 82 | _mutedTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/face-mute.svg"); | ||
| 83 | } | ||
| 84 | |||
| 85 | const int MUTE_ICON_SIZE = 24; | ||
| 86 | _iconBounds = QRect(x, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE); | ||
| 87 | float iconColor = 1.0f; | ||
| 88 | if (!Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking)) { | ||
| 89 | glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_enabledTexture)); | ||
| 90 | } else { | ||
| 91 | glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_mutedTexture)); | ||
| 92 | |||
| 93 | // Make muted icon pulsate | ||
| 94 | static const float PULSE_MIN = 0.4f; | ||
| 95 | static const float PULSE_MAX = 1.0f; | ||
| 96 | static const float PULSE_FREQUENCY = 1.0f; // in Hz | ||
| 97 | qint64 now = usecTimestampNow(); | ||
| 98 | if (now - _iconPulseTimeReference > (qint64)USECS_PER_SECOND) { | ||
| 99 | // Prevents t from getting too big, which would diminish glm::cos precision | ||
| 100 | _iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND); | ||
| 101 | } | ||
| 102 | float t = (float)(now - _iconPulseTimeReference) / (float)USECS_PER_SECOND; | ||
| 103 | float pulseFactor = (glm::cos(t * PULSE_FREQUENCY * 2.0f * PI) + 1.0f) / 2.0f; | ||
| 104 | iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor; | ||
| 105 | } | ||
| 106 | |||
| 107 | glm::vec4 quadColor(iconColor, iconColor, iconColor, 1.0f); | ||
| 108 | |||
| 109 | glm::vec2 topLeft(_iconBounds.left(), _iconBounds.top()); | ||
| 110 | glm::vec2 bottomRight(_iconBounds.right(), _iconBounds.bottom()); | ||
| 111 | glm::vec2 texCoordTopLeft(1,1); | ||
| 112 | glm::vec2 texCoordBottomRight(0,0); | ||
| 113 | |||
| 114 | if (_boxQuadID == GeometryCache::UNKNOWN_ID) { | ||
| 115 | _boxQuadID = DependencyManager::get<GeometryCache>()->allocateID(); | ||
| 116 | } | ||
| 117 | |||
| 118 | DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor, _boxQuadID); | ||
| 119 | |||
| 120 | glDisable(GL_TEXTURE_2D); | ||
| 121 | } | ||
| @@ -1,45 +0,0 @@ | |||
| 1 | // | ||
| 2 | // CameraToolBox.h | ||
| 3 | // interface/src/devices | ||
| 4 | // | ||
| 5 | // Created by David Rowe on 30 Apr 2015. | ||
| 6 | // Copyright 2015 High Fidelity, Inc. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | |||
| 12 | #ifndef hifi_CameraToolBox_h | ||
| 13 | #define hifi_CameraToolBox_h | ||
| 14 | |||
| 15 | #include <QObject> | ||
| 16 | |||
| 17 | #include <DependencyManager.h> | ||
| 18 | #include <GeometryCache.h> | ||
| 19 | |||
| 20 | class CameraToolBox : public QObject, public Dependency { | ||
| 21 | Q_OBJECT | ||
| 22 | SINGLETON_DEPENDENCY | ||
| 23 | |||
| 24 | public: | ||
| 25 | void render(int x, int y, bool boxed); | ||
| 26 | bool mousePressEvent(int x, int y); | ||
| 27 | bool mouseDoublePressEvent(int x, int y); | ||
| 28 | |||
| 29 | protected: | ||
| 30 | CameraToolBox(); | ||
| 31 | ~CameraToolBox(); | ||
| 32 | |||
| 33 | private slots: | ||
| 34 | void toggleMute(); | ||
| 35 | |||
| 36 | private: | ||
| 37 | gpu::TexturePointer _enabledTexture; | ||
| 38 | gpu::TexturePointer _mutedTexture; | ||
| 39 | int _boxQuadID = GeometryCache::UNKNOWN_ID; | ||
| 40 | QRect _iconBounds; | ||
| 41 | qint64 _iconPulseTimeReference = 0; | ||
| 42 | QTimer* _doubleClickTimer; | ||
| 43 | }; | ||
| 44 | |||
| 45 | #endif // hifi_CameraToolBox_h | ||
| @@ -415,7 +415,7 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { · 414 unmodified lines | |||
| 415 | float browUp = _coefficients[_browUpCenterIndex]; | 415 | float browUp = _coefficients[_browUpCenterIndex]; |
| 416 | if (isFiltering) { | 416 | if (isFiltering) { |
| 417 | const float BROW_VELOCITY_FILTER_STRENGTH = 0.5f; | 417 | const float BROW_VELOCITY_FILTER_STRENGTH = 0.5f; |
| 418 | float velocity = fabs(browUp - _lastBrowUp) / _averageMessageTime; | 418 | float velocity = fabsf(browUp - _lastBrowUp) / _averageMessageTime; |
| 419 | float velocityFilter = glm::clamp(velocity * BROW_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f); | 419 | float velocityFilter = glm::clamp(velocity * BROW_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f); |
| 420 | _filteredBrowUp = velocityFilter * browUp + (1.0f - velocityFilter) * _filteredBrowUp; | 420 | _filteredBrowUp = velocityFilter * browUp + (1.0f - velocityFilter) * _filteredBrowUp; |
| 421 | _lastBrowUp = browUp; | 421 | _lastBrowUp = browUp; |
| @@ -438,11 +438,12 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { · 16 unmodified lines | |||
| 438 | 438 | ||
| 439 | // Velocity filter EyeBlink values | 439 | // Velocity filter EyeBlink values |
| 440 | const float DDE_EYEBLINK_SCALE = 3.0f; | 440 | const float DDE_EYEBLINK_SCALE = 3.0f; |
| 441 | float eyeBlinks[] = { DDE_EYEBLINK_SCALE * _coefficients[_leftBlinkIndex], DDE_EYEBLINK_SCALE * _coefficients[_rightBlinkIndex] }; | 441 | float eyeBlinks[] = { DDE_EYEBLINK_SCALE * _coefficients[_leftBlinkIndex], |
| 442 | DDE_EYEBLINK_SCALE * _coefficients[_rightBlinkIndex] }; | ||
| 442 | if (isFiltering) { | 443 | if (isFiltering) { |
| 443 | const float BLINK_VELOCITY_FILTER_STRENGTH = 0.3f; | 444 | const float BLINK_VELOCITY_FILTER_STRENGTH = 0.3f; |
| 444 | for (int i = 0; i < 2; i++) { | 445 | for (int i = 0; i < 2; i++) { |
| 445 | float velocity = fabs(eyeBlinks[i] - _lastEyeBlinks[i]) / _averageMessageTime; | 446 | float velocity = fabsf(eyeBlinks[i] - _lastEyeBlinks[i]) / _averageMessageTime; |
| 446 | float velocityFilter = glm::clamp(velocity * BLINK_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f); | 447 | float velocityFilter = glm::clamp(velocity * BLINK_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f); |
| 447 | _filteredEyeBlinks[i] = velocityFilter * eyeBlinks[i] + (1.0f - velocityFilter) * _filteredEyeBlinks[i]; | 448 | _filteredEyeBlinks[i] = velocityFilter * eyeBlinks[i] + (1.0f - velocityFilter) * _filteredEyeBlinks[i]; |
| 448 | _lastEyeBlinks[i] = eyeBlinks[i]; | 449 | _lastEyeBlinks[i] = eyeBlinks[i]; |
| @@ -479,9 +480,9 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { · 30 unmodified lines | |||
| 479 | if (_eyeStates[i] == EYE_CLOSING) { | 480 | if (_eyeStates[i] == EYE_CLOSING) { |
| 480 | // Close eyelid until it's fully closed | 481 | // Close eyelid until it's fully closed |
| 481 | float closingValue = _lastEyeCoefficients[i] + EYELID_MOVEMENT_RATE * _averageMessageTime; | 482 | float closingValue = _lastEyeCoefficients[i] + EYELID_MOVEMENT_RATE * _averageMessageTime; |
| 482 | if (closingValue >= 1.0) { | 483 | if (closingValue >= 1.0f) { |
| 483 | _eyeStates[i] = EYE_CLOSED; | 484 | _eyeStates[i] = EYE_CLOSED; |
| 484 | eyeCoefficients[i] = 1.0; | 485 | eyeCoefficients[i] = 1.0f; |
| 485 | } else { | 486 | } else { |
| 486 | eyeCoefficients[i] = closingValue; | 487 | eyeCoefficients[i] = closingValue; |
| 487 | } | 488 | } |
| @@ -11,13 +11,13 @@ · 10 unmodified lines | |||
| 11 | // | 11 | // |
| 12 | 12 | ||
| 13 | #include "InterfaceConfig.h" | 13 | #include "InterfaceConfig.h" |
| 14 | |||
| 15 | #include "OculusManager.h" | 14 | #include "OculusManager.h" |
| 16 | #include "ui/overlays/Text3DOverlay.h" | 15 | #include "ui/overlays/Text3DOverlay.h" |
| 17 | 16 | ||
| 17 | #include <CursorManager.h> | ||
| 18 | #include <QDesktopWidget> | 18 | #include <QDesktopWidget> |
| 19 | #include <QGuiApplication> | 19 | #include <QGuiApplication> |
| 20 | #include <QScreen> | 20 | #include <QScreen> |
| 21 | #include <QOpenGLTimerQuery> | 21 | #include <QOpenGLTimerQuery> |
| 22 | 22 | ||
| 23 | #include <glm/glm.hpp> | 23 | #include <glm/glm.hpp> |
| @@ -25,6 +25,9 @@ · 1 unmodified line | |||
| 25 | #include <avatar/AvatarManager.h> | 25 | #include <avatar/AvatarManager.h> |
| 26 | #include <avatar/MyAvatar.h> | 26 | #include <avatar/MyAvatar.h> |
| 27 | #include <GlowEffect.h> | 27 | #include <GlowEffect.h> |
| 28 | #include <GlWindow.h> | ||
| 29 | #include <gpu/GLBackend.h> | ||
| 30 | #include <OglplusHelpers.h> | ||
| 28 | #include <PathUtils.h> | 31 | #include <PathUtils.h> |
| 29 | #include <SharedUtil.h> | 32 | #include <SharedUtil.h> |
| 30 | #include <UserActivityLogger.h> | 33 | #include <UserActivityLogger.h> |
| @@ -34,8 +37,6 @@ · 3 unmodified lines | |||
| 34 | #include "InterfaceLogging.h" | 37 | #include "InterfaceLogging.h" |
| 35 | #include "Application.h" | 38 | #include "Application.h" |
| 36 | 39 | ||
| 37 | #include <gpu/GLBackend.h> | ||
| 38 | |||
| 39 | template <typename Function> | 40 | template <typename Function> |
| 40 | void for_each_eye(Function function) { | 41 | void for_each_eye(Function function) { |
| 41 | for (ovrEyeType eye = ovrEyeType::ovrEye_Left; | 42 | for (ovrEyeType eye = ovrEyeType::ovrEye_Left; |
| @@ -52,202 +53,413 @@ void for_each_eye(const ovrHmd & hmd, Function function) { · 10 unmodified lines | |||
| 52 | function(eye); | 53 | function(eye); |
| 53 | } | 54 | } |
| 54 | } | 55 | } |
| 56 | enum CalibrationState { | ||
| 57 | UNCALIBRATED, | ||
| 58 | WAITING_FOR_DELTA, | ||
| 59 | WAITING_FOR_ZERO, | ||
| 60 | WAITING_FOR_ZERO_HELD, | ||
| 61 | CALIBRATED | ||
| 62 | }; | ||
| 63 | |||
| 64 | inline glm::mat4 toGlm(const ovrMatrix4f & om) { | ||
| 65 | return glm::transpose(glm::make_mat4(&om.M[0][0])); | ||
| 66 | } | ||
| 55 | 67 | ||
| 56 | #ifdef OVR_CLIENT_DISTORTION | 68 | inline glm::mat4 toGlm(const ovrFovPort & fovport, float nearPlane = 0.01f, float farPlane = 10000.0f) { |
| 57 | ProgramObject OculusManager::_program; | 69 | return toGlm(ovrMatrix4f_Projection(fovport, nearPlane, farPlane, true)); |
| 58 | int OculusManager::_textureLocation; | 70 | } |
| 59 | int OculusManager::_eyeToSourceUVScaleLocation; | ||
| 60 | int OculusManager::_eyeToSourceUVOffsetLocation; | ||
| 61 | int OculusManager::_eyeRotationStartLocation; | ||
| 62 | int OculusManager::_eyeRotationEndLocation; | ||
| 63 | int OculusManager::_positionAttributeLocation; | ||
| 64 | int OculusManager::_colorAttributeLocation; | ||
| 65 | int OculusManager::_texCoord0AttributeLocation; | ||
| 66 | int OculusManager::_texCoord1AttributeLocation; | ||
| 67 | int OculusManager::_texCoord2AttributeLocation; | ||
| 68 | ovrVector2f OculusManager::_UVScaleOffset[ovrEye_Count][2]; | ||
| 69 | GLuint OculusManager::_vertices[ovrEye_Count] = { 0, 0 }; | ||
| 70 | GLuint OculusManager::_indices[ovrEye_Count] = { 0, 0 }; | ||
| 71 | GLsizei OculusManager::_meshSize[ovrEye_Count] = { 0, 0 }; | ||
| 72 | ovrFrameTiming OculusManager::_hmdFrameTiming; | ||
| 73 | bool OculusManager::_programInitialized = false; | ||
| 74 | #endif | ||
| 75 | 71 | ||
| 76 | ovrTexture OculusManager::_eyeTextures[ovrEye_Count]; | 72 | inline glm::vec3 toGlm(const ovrVector3f & ov) { |
| 77 | bool OculusManager::_isConnected = false; | 73 | return glm::make_vec3(&ov.x); |
| 78 | ovrHmd OculusManager::_ovrHmd; | 74 | } |
| 79 | ovrFovPort OculusManager::_eyeFov[ovrEye_Count]; | ||
| 80 | ovrVector3f OculusManager::_eyeOffset[ovrEye_Count]; | ||
| 81 | ovrEyeRenderDesc OculusManager::_eyeRenderDesc[ovrEye_Count]; | ||
| 82 | ovrSizei OculusManager::_renderTargetSize; | ||
| 83 | glm::mat4 OculusManager::_eyeProjection[ovrEye_Count]; | ||
| 84 | unsigned int OculusManager::_frameIndex = 0; | ||
| 85 | bool OculusManager::_frameTimingActive = false; | ||
| 86 | Camera* OculusManager::_camera = NULL; | ||
| 87 | ovrEyeType OculusManager::_activeEye = ovrEye_Count; | ||
| 88 | bool OculusManager::_hswDismissed = false; | ||
| 89 | |||
| 90 | float OculusManager::CALIBRATION_DELTA_MINIMUM_LENGTH = 0.02f; | ||
| 91 | float OculusManager::CALIBRATION_DELTA_MINIMUM_ANGLE = 5.0f * RADIANS_PER_DEGREE; | ||
| 92 | float OculusManager::CALIBRATION_ZERO_MAXIMUM_LENGTH = 0.01f; | ||
| 93 | float OculusManager::CALIBRATION_ZERO_MAXIMUM_ANGLE = 2.0f * RADIANS_PER_DEGREE; | ||
| 94 | quint64 OculusManager::CALIBRATION_ZERO_HOLD_TIME = 3000000; // usec | ||
| 95 | float OculusManager::CALIBRATION_MESSAGE_DISTANCE = 2.5f; | ||
| 96 | OculusManager::CalibrationState OculusManager::_calibrationState; | ||
| 97 | glm::vec3 OculusManager::_calibrationPosition; | ||
| 98 | glm::quat OculusManager::_calibrationOrientation; | ||
| 99 | quint64 OculusManager::_calibrationStartTime; | ||
| 100 | int OculusManager::_calibrationMessage = 0; | ||
| 101 | glm::vec3 OculusManager::_eyePositions[ovrEye_Count]; | ||
| 102 | // TODO expose this as a developer toggle | ||
| 103 | bool OculusManager::_eyePerFrameMode = false; | ||
| 104 | ovrEyeType OculusManager::_lastEyeRendered = ovrEye_Count; | ||
| 105 | ovrSizei OculusManager::_recommendedTexSize = { 0, 0 }; | ||
| 106 | float OculusManager::_offscreenRenderScale = 1.0; | ||
| 107 | 75 | ||
| 76 | inline glm::vec2 toGlm(const ovrVector2f & ov) { | ||
| 77 | return glm::make_vec2(&ov.x); | ||
| 78 | } | ||
| 108 | 79 | ||
| 109 | void OculusManager::initSdk() { | 80 | inline glm::ivec2 toGlm(const ovrVector2i & ov) { |
| 110 | ovr_Initialize(); | 81 | return glm::ivec2(ov.x, ov.y); |
| 111 | _ovrHmd = ovrHmd_Create(0); | ||
| 112 | if (!_ovrHmd) { | ||
| 113 | _ovrHmd = ovrHmd_CreateDebug(ovrHmd_DK2); | ||
| 114 | } | ||
| 115 | } | 82 | } |
| 116 | 83 | ||
| 117 | void OculusManager::shutdownSdk() { | 84 | inline glm::uvec2 toGlm(const ovrSizei & ov) { |
| 118 | if (_ovrHmd) { | 85 | return glm::uvec2(ov.w, ov.h); |
| 119 | ovrHmd_Destroy(_ovrHmd); | ||
| 120 | _ovrHmd = nullptr; | ||
| 121 | ovr_Shutdown(); | ||
| 122 | } | ||
| 123 | } | 86 | } |
| 124 | 87 | ||
| 125 | void OculusManager::init() { | 88 | inline glm::quat toGlm(const ovrQuatf & oq) { |
| 126 | #ifdef OVR_DIRECT_MODE | 89 | return glm::make_quat(&oq.x); |
| 127 | initSdk(); | ||
| 128 | #endif | ||
| 129 | } | 90 | } |
| 130 | 91 | ||
| 131 | void OculusManager::deinit() { | 92 | inline glm::mat4 toGlm(const ovrPosef & op) { |
| 132 | #ifdef OVR_DIRECT_MODE | 93 | glm::mat4 orientation = glm::mat4_cast(toGlm(op.Orientation)); |
| 133 | shutdownSdk(); | 94 | glm::mat4 translation = glm::translate(glm::mat4(), toGlm(op.Position)); |
| 134 | #endif | 95 | return translation * orientation; |
| 135 | } | 96 | } |
| 136 | 97 | ||
| 137 | void OculusManager::connect() { | 98 | inline ovrMatrix4f ovrFromGlm(const glm::mat4 & m) { |
| 138 | #ifndef OVR_DIRECT_MODE | 99 | ovrMatrix4f result; |
| 139 | initSdk(); | 100 | glm::mat4 transposed(glm::transpose(m)); |
| 140 | #endif | 101 | memcpy(result.M, &(transposed[0][0]), sizeof(float) * 16); |
| 141 | _calibrationState = UNCALIBRATED; | 102 | return result; |
| 142 | qCDebug(interfaceapp) << "Oculus SDK" << OVR_VERSION_STRING; | 103 | } |
| 143 | if (_ovrHmd) { | 104 | |
| 144 | if (!_isConnected) { | 105 | inline ovrVector3f ovrFromGlm(const glm::vec3 & v) { |
| 145 | UserActivityLogger::getInstance().connectedDevice("hmd", "oculus"); | 106 | return{ v.x, v.y, v.z }; |
| 107 | } | ||
| 108 | |||
| 109 | inline ovrVector2f ovrFromGlm(const glm::vec2 & v) { | ||
| 110 | return{ v.x, v.y }; | ||
| 111 | } | ||
| 112 | |||
| 113 | inline ovrSizei ovrFromGlm(const glm::uvec2 & v) { | ||
| 114 | return{ (int)v.x, (int)v.y }; | ||
| 115 | } | ||
| 116 | |||
| 117 | inline ovrQuatf ovrFromGlm(const glm::quat & q) { | ||
| 118 | return{ q.x, q.y, q.z, q.w }; | ||
| 119 | } | ||
| 120 | |||
| 121 | |||
| 122 | |||
| 123 | #ifdef Q_OS_WIN | ||
| 124 | |||
| 125 | // A base class for FBO wrappers that need to use the Oculus C | ||
| 126 | // API to manage textures via ovrHmd_CreateSwapTextureSetGL, | ||
| 127 | // ovrHmd_CreateMirrorTextureGL, etc | ||
| 128 | template <typename C> | ||
| 129 | struct RiftFramebufferWrapper : public FramebufferWrapper<C, char> { | ||
| 130 | ovrHmd hmd; | ||
| 131 | RiftFramebufferWrapper(const ovrHmd & hmd) : hmd(hmd) { | ||
| 132 | color = 0; | ||
| 133 | depth = 0; | ||
| 134 | }; | ||
| 135 | |||
| 136 | void Resize(const uvec2 & size) { | ||
| 137 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oglplus::GetName(fbo)); | ||
| 138 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); | ||
| 139 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); | ||
| 140 | this->size = size; | ||
| 141 | initColor(); | ||
| 142 | initDone(); | ||
| 143 | } | ||
| 144 | |||
| 145 | protected: | ||
| 146 | virtual void initDepth() override final { | ||
| 147 | } | ||
| 148 | }; | ||
| 149 | |||
| 150 | // A wrapper for constructing and using a swap texture set, | ||
| 151 | // where each frame you draw to a texture via the FBO, | ||
| 152 | // then submit it and increment to the next texture. | ||
| 153 | // The Oculus SDK manages the creation and destruction of | ||
| 154 | // the textures | ||
| 155 | struct SwapFramebufferWrapper : public RiftFramebufferWrapper<ovrSwapTextureSet*> { | ||
| 156 | SwapFramebufferWrapper(const ovrHmd & hmd) | ||
| 157 | : RiftFramebufferWrapper(hmd) { | ||
| 158 | } | ||
| 159 | |||
| 160 | ~SwapFramebufferWrapper() { | ||
| 161 | if (color) { | ||
| 162 | ovrHmd_DestroySwapTextureSet(hmd, color); | ||
| 163 | color = nullptr; | ||
| 146 | } | 164 | } |
| 147 | _isConnected = true; | 165 | } |
| 148 | 166 | ||
| 149 | for_each_eye([&](ovrEyeType eye) { | 167 | void Increment() { |
| 150 | _eyeFov[eye] = _ovrHmd->DefaultEyeFov[eye]; | 168 | ++color->CurrentIndex; |
| 151 | }); | 169 | color->CurrentIndex %= color->TextureCount; |
| 152 | 170 | } | |
| 153 | ovrGLConfig cfg; | 171 | |
| 154 | memset(&cfg, 0, sizeof(cfg)); | 172 | protected: |
| 155 | cfg.OGL.Header.API = ovrRenderAPI_OpenGL; | 173 | virtual void initColor() override { |
| 156 | cfg.OGL.Header.BackBufferSize = _ovrHmd->Resolution; | 174 | if (color) { |
| 157 | cfg.OGL.Header.Multisample = 1; | 175 | ovrHmd_DestroySwapTextureSet(hmd, color); |
| 158 | 176 | color = nullptr; | |
| 159 | int distortionCaps = 0 | 177 | } |
| 160 | | ovrDistortionCap_Vignette | 178 | |
| 161 | | ovrDistortionCap_Overdrive | 179 | ovrResult result = ovrHmd_CreateSwapTextureSetGL(hmd, GL_RGBA, size.x, size.y, &color); |
| 162 | | ovrDistortionCap_TimeWarp; | 180 | Q_ASSERT(OVR_SUCCESS(result)); |
| 163 | 181 | ||
| 164 | int configResult = ovrHmd_ConfigureRendering(_ovrHmd, &cfg.Config, | 182 | for (int i = 0; i < color->TextureCount; ++i) { |
| 165 | distortionCaps, _eyeFov, _eyeRenderDesc); | 183 | ovrGLTexture& ovrTex = (ovrGLTexture&)color->Textures[i]; |
| 166 | assert(configResult); | 184 | glBindTexture(GL_TEXTURE_2D, ovrTex.OGL.TexId); |
| 167 | 185 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
| 168 | 186 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
| 169 | _recommendedTexSize = ovrHmd_GetFovTextureSize(_ovrHmd, ovrEye_Left, _eyeFov[ovrEye_Left], 1.0f); | 187 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| 170 | _renderTargetSize = { _recommendedTexSize.w * 2, _recommendedTexSize.h }; | 188 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 171 | for_each_eye([&](ovrEyeType eye) { | 189 | } |
| 172 | //Get texture size | 190 | glBindTexture(GL_TEXTURE_2D, 0); |
| 173 | _eyeTextures[eye].Header.API = ovrRenderAPI_OpenGL; | 191 | } |
| 174 | _eyeTextures[eye].Header.TextureSize = _renderTargetSize; | 192 | |
| 175 | _eyeTextures[eye].Header.RenderViewport.Pos = { 0, 0 }; | 193 | virtual void initDone() override { |
| 176 | }); | 194 | } |
| 177 | _eyeTextures[ovrEye_Right].Header.RenderViewport.Pos.x = _recommendedTexSize.w; | 195 | |
| 178 | 196 | virtual void onBind(oglplus::Framebuffer::Target target) override { | |
| 179 | ovrHmd_SetEnabledCaps(_ovrHmd, ovrHmdCap_LowPersistence | ovrHmdCap_DynamicPrediction); | 197 | ovrGLTexture& tex = (ovrGLTexture&)(color->Textures[color->CurrentIndex]); |
| 180 | 198 | glFramebufferTexture2D(toEnum(target), GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex.OGL.TexId, 0); | |
| 181 | ovrHmd_ConfigureTracking(_ovrHmd, ovrTrackingCap_Orientation | ovrTrackingCap_Position | | 199 | } |
| 182 | ovrTrackingCap_MagYawCorrection, | 200 | |
| 183 | ovrTrackingCap_Orientation); | 201 | virtual void onUnbind(oglplus::Framebuffer::Target target) override { |
| 184 | 202 | glFramebufferTexture2D(toEnum(target), GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); | |
| 185 | if (!_camera) { | 203 | } |
| 186 | _camera = new Camera; | 204 | }; |
| 187 | configureCamera(*_camera); // no need to use screen dimensions; they're ignored | 205 | |
| 206 | |||
| 207 | // We use a FBO to wrap the mirror texture because it makes it easier to | ||
| 208 | // render to the screen via glBlitFramebuffer | ||
| 209 | struct MirrorFramebufferWrapper : public RiftFramebufferWrapper<ovrGLTexture*> { | ||
| 210 | MirrorFramebufferWrapper(const ovrHmd & hmd) | ||
| 211 | : RiftFramebufferWrapper(hmd) { | ||
| 212 | } | ||
| 213 | |||
| 214 | virtual ~MirrorFramebufferWrapper() { | ||
| 215 | if (color) { | ||
| 216 | ovrHmd_DestroyMirrorTexture(hmd, (ovrTexture*)color); | ||
| 217 | color = nullptr; | ||
| 188 | } | 218 | } |
| 189 | #ifdef OVR_CLIENT_DISTORTION | 219 | } |
| 190 | if (!_programInitialized) { | 220 | |
| 191 | // Shader program | 221 | private: |
| 192 | _programInitialized = true; | 222 | void initColor() override { |
| 193 | _program.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + "shaders/oculus.vert"); | 223 | if (color) { |
| 194 | _program.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + "shaders/oculus.frag"); | 224 | ovrHmd_DestroyMirrorTexture(hmd, (ovrTexture*)color); |
| 195 | _program.link(); | 225 | color = nullptr; |
| 196 | |||
| 197 | // Uniforms | ||
| 198 | _textureLocation = _program.uniformLocation("texture"); | ||
| 199 | _eyeToSourceUVScaleLocation = _program.uniformLocation("EyeToSourceUVScale"); | ||
| 200 | _eyeToSourceUVOffsetLocation = _program.uniformLocation("EyeToSourceUVOffset"); | ||
| 201 | _eyeRotationStartLocation = _program.uniformLocation("EyeRotationStart"); | ||
| 202 | _eyeRotationEndLocation = _program.uniformLocation("EyeRotationEnd"); | ||
| 203 | |||
| 204 | // Attributes | ||
| 205 | _positionAttributeLocation = _program.attributeLocation("position"); | ||
| 206 | _colorAttributeLocation = _program.attributeLocation("color"); | ||
| 207 | _texCoord0AttributeLocation = _program.attributeLocation("texCoord0"); | ||
| 208 | _texCoord1AttributeLocation = _program.attributeLocation("texCoord1"); | ||
| 209 | _texCoord2AttributeLocation = _program.attributeLocation("texCoord2"); | ||
| 210 | } | 226 | } |
| 227 | ovrResult result = ovrHmd_CreateMirrorTextureGL(hmd, GL_RGBA, size.x, size.y, (ovrTexture**)&color); | ||
| 228 | Q_ASSERT(OVR_SUCCESS(result)); | ||
| 229 | } | ||
| 230 | |||
| 231 | void initDone() override { | ||
| 232 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oglplus::GetName(fbo)); | ||
| 233 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color->OGL.TexId, 0); | ||
| 234 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); | ||
| 235 | } | ||
| 236 | }; | ||
| 237 | |||
| 238 | static SwapFramebufferWrapper* _swapFbo{ nullptr }; | ||
| 239 | static MirrorFramebufferWrapper* _mirrorFbo{ nullptr }; | ||
| 240 | static ovrLayerEyeFov _sceneLayer; | ||
| 241 | |||
| 242 | #else | ||
| 243 | |||
| 244 | static ovrTexture _eyeTextures[ovrEye_Count]; | ||
| 245 | static GlWindow* _outputWindow{ nullptr }; | ||
| 211 | 246 | ||
| 212 | //Generate the distortion VBOs | ||
| 213 | generateDistortionMesh(); | ||
| 214 | #endif | 247 | #endif |
| 215 | } else { | 248 | |
| 249 | static bool _isConnected = false; | ||
| 250 | static ovrHmd _ovrHmd; | ||
| 251 | static ovrFovPort _eyeFov[ovrEye_Count]; | ||
| 252 | static ovrVector3f _eyeOffset[ovrEye_Count]; | ||
| 253 | static ovrEyeRenderDesc _eyeRenderDesc[ovrEye_Count]; | ||
| 254 | static ovrSizei _renderTargetSize; | ||
| 255 | static glm::mat4 _eyeProjection[ovrEye_Count]; | ||
| 256 | static unsigned int _frameIndex = 0; | ||
| 257 | static bool _frameTimingActive = false; | ||
| 258 | static Camera* _camera = NULL; | ||
| 259 | static ovrEyeType _activeEye = ovrEye_Count; | ||
| 260 | static bool _hswDismissed = false; | ||
| 261 | |||
| 262 | static const float CALIBRATION_DELTA_MINIMUM_LENGTH = 0.02f; | ||
| 263 | static const float CALIBRATION_DELTA_MINIMUM_ANGLE = 5.0f * RADIANS_PER_DEGREE; | ||
| 264 | static const float CALIBRATION_ZERO_MAXIMUM_LENGTH = 0.01f; | ||
| 265 | static const float CALIBRATION_ZERO_MAXIMUM_ANGLE = 2.0f * RADIANS_PER_DEGREE; | ||
| 266 | static const quint64 CALIBRATION_ZERO_HOLD_TIME = 3000000; // usec | ||
| 267 | static const float CALIBRATION_MESSAGE_DISTANCE = 2.5f; | ||
| 268 | static CalibrationState _calibrationState; | ||
| 269 | static glm::vec3 _calibrationPosition; | ||
| 270 | static glm::quat _calibrationOrientation; | ||
| 271 | static quint64 _calibrationStartTime; | ||
| 272 | static int _calibrationMessage = 0; | ||
| 273 | static glm::vec3 _eyePositions[ovrEye_Count]; | ||
| 274 | // TODO expose this as a developer toggle | ||
| 275 | static bool _eyePerFrameMode = false; | ||
| 276 | static ovrEyeType _lastEyeRendered = ovrEye_Count; | ||
| 277 | static ovrSizei _recommendedTexSize = { 0, 0 }; | ||
| 278 | static float _offscreenRenderScale = 1.0; | ||
| 279 | static glm::mat4 _combinedProjection; | ||
| 280 | static ovrPosef _eyeRenderPoses[ovrEye_Count]; | ||
| 281 | static ovrRecti _eyeViewports[ovrEye_Count]; | ||
| 282 | static ovrVector3f _eyeOffsets[ovrEye_Count]; | ||
| 283 | |||
| 284 | glm::vec3 OculusManager::getLeftEyePosition() { return _eyePositions[ovrEye_Left]; } | ||
| 285 | glm::vec3 OculusManager::getRightEyePosition() { return _eyePositions[ovrEye_Right]; } | ||
| 286 | |||
| 287 | void OculusManager::connect(QOpenGLContext* shareContext) { | ||
| 288 | qCDebug(interfaceapp) << "Oculus SDK" << OVR_VERSION_STRING; | ||
| 289 | |||
| 290 | ovrInitParams initParams; memset(&initParams, 0, sizeof(initParams)); | ||
| 291 | |||
| 292 | #ifdef DEBUG | ||
| 293 | initParams.Flags |= ovrInit_Debug; | ||
| 294 | #endif | ||
| 295 | |||
| 296 | ovr_Initialize(&initParams); | ||
| 297 | |||
| 298 | #ifdef Q_OS_WIN | ||
| 299 | |||
| 300 | ovrResult res = ovrHmd_Create(0, &_ovrHmd); | ||
| 301 | #ifdef DEBUG | ||
| 302 | if (!OVR_SUCCESS(res)) { | ||
| 303 | res = ovrHmd_CreateDebug(ovrHmd_DK2, &_ovrHmd); | ||
| 304 | Q_ASSERT(OVR_SUCCESS(res)); | ||
| 305 | } | ||
| 306 | #endif | ||
| 307 | |||
| 308 | #else | ||
| 309 | |||
| 310 | _ovrHmd = ovrHmd_Create(0); | ||
| 311 | #ifdef DEBUG | ||
| 312 | if (!_ovrHmd) { | ||
| 313 | _ovrHmd = ovrHmd_CreateDebug(ovrHmd_DK2); | ||
| 314 | } | ||
| 315 | #endif | ||
| 316 | |||
| 317 | #endif | ||
| 318 | |||
| 319 | if (!_ovrHmd) { | ||
| 216 | _isConnected = false; | 320 | _isConnected = false; |
| 217 | 321 | ||
| 218 | // we're definitely not in "VR mode" so tell the menu that | 322 | // we're definitely not in "VR mode" so tell the menu that |
| 219 | Menu::getInstance()->getActionForOption(MenuOption::EnableVRMode)->setChecked(false); | 323 | Menu::getInstance()->getActionForOption(MenuOption::EnableVRMode)->setChecked(false); |
| 324 | ovr_Shutdown(); | ||
| 325 | return; | ||
| 326 | } | ||
| 327 | |||
| 328 | _calibrationState = UNCALIBRATED; | ||
| 329 | if (!_isConnected) { | ||
| 330 | UserActivityLogger::getInstance().connectedDevice("hmd", "oculus"); | ||
| 331 | } | ||
| 332 | _isConnected = true; | ||
| 333 | |||
| 334 | for_each_eye([&](ovrEyeType eye) { | ||
| 335 | _eyeFov[eye] = _ovrHmd->DefaultEyeFov[eye]; | ||
| 336 | _eyeProjection[eye] = toGlm(ovrMatrix4f_Projection(_eyeFov[eye], | ||
| 337 | DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded)); | ||
| 338 | ovrEyeRenderDesc erd = ovrHmd_GetRenderDesc(_ovrHmd, eye, _eyeFov[eye]); | ||
| 339 | _eyeOffsets[eye] = erd.HmdToEyeViewOffset; | ||
| 340 | }); | ||
| 341 | ovrFovPort combinedFov = _ovrHmd->MaxEyeFov[0]; | ||
| 342 | combinedFov.RightTan = _ovrHmd->MaxEyeFov[1].RightTan; | ||
| 343 | _combinedProjection = toGlm(ovrMatrix4f_Projection(combinedFov, | ||
| 344 | DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded)); | ||
| 345 | |||
| 346 | _recommendedTexSize = ovrHmd_GetFovTextureSize(_ovrHmd, ovrEye_Left, _eyeFov[ovrEye_Left], 1.0f); | ||
| 347 | _renderTargetSize = { _recommendedTexSize.w * 2, _recommendedTexSize.h }; | ||
| 348 | |||
| 349 | #ifdef Q_OS_WIN | ||
| 350 | |||
| 351 | _mirrorFbo = new MirrorFramebufferWrapper(_ovrHmd); | ||
| 352 | _swapFbo = new SwapFramebufferWrapper(_ovrHmd); | ||
| 353 | _swapFbo->Init(toGlm(_renderTargetSize)); | ||
| 354 | _sceneLayer.ColorTexture[0] = _swapFbo->color; | ||
| 355 | _sceneLayer.ColorTexture[1] = nullptr; | ||
| 356 | _sceneLayer.Viewport[0].Pos = { 0, 0 }; | ||
| 357 | _sceneLayer.Viewport[0].Size = _recommendedTexSize; | ||
| 358 | _sceneLayer.Viewport[1].Pos = { _recommendedTexSize.w, 0 }; | ||
| 359 | _sceneLayer.Viewport[1].Size = _recommendedTexSize; | ||
| 360 | _sceneLayer.Header.Type = ovrLayerType_EyeFov; | ||
| 361 | _sceneLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft; | ||
| 362 | for_each_eye([&](ovrEyeType eye) { | ||
| 363 | _eyeViewports[eye] = _sceneLayer.Viewport[eye]; | ||
| 364 | _sceneLayer.Fov[eye] = _eyeFov[eye]; | ||
| 365 | }); | ||
| 366 | |||
| 367 | |||
| 368 | |||
| 369 | #else | ||
| 370 | _outputWindow = new GlWindow(shareContext); | ||
| 371 | _outputWindow->show(); | ||
| 372 | // _outputWindow->setFlags(Qt::FramelessWindowHint ); | ||
| 373 | // _outputWindow->resize(_ovrHmd->Resolution.w, _ovrHmd->Resolution.h); | ||
| 374 | // _outputWindow->setPosition(_ovrHmd->WindowsPos.x, _ovrHmd->WindowsPos.y); | ||
| 375 | ivec2 desiredPosition = toGlm(_ovrHmd->WindowsPos); | ||
| 376 | foreach(QScreen* screen, qGuiApp->screens()) { | ||
| 377 | ivec2 screenPosition = toGlm(screen->geometry().topLeft()); | ||
| 378 | if (screenPosition == desiredPosition) { | ||
| 379 | _outputWindow->setScreen(screen); | ||
| 380 | break; | ||
| 381 | } | ||
| 382 | } | ||
| 383 | _outputWindow->showFullScreen(); | ||
| 384 | _outputWindow->makeCurrent(); | ||
| 385 | |||
| 386 | ovrGLConfig cfg; | ||
| 387 | memset(&cfg, 0, sizeof(cfg)); | ||
| 388 | cfg.OGL.Header.API = ovrRenderAPI_OpenGL; | ||
| 389 | cfg.OGL.Header.BackBufferSize = _ovrHmd->Resolution; | ||
| 390 | cfg.OGL.Header.Multisample = 0; | ||
| 391 | |||
| 392 | int distortionCaps = 0 | ||
| 393 | | ovrDistortionCap_Vignette | ||
| 394 | | ovrDistortionCap_Overdrive | ||
| 395 | | ovrDistortionCap_TimeWarp; | ||
| 396 | |||
| 397 | int configResult = ovrHmd_ConfigureRendering(_ovrHmd, &cfg.Config, | ||
| 398 | distortionCaps, _eyeFov, _eyeRenderDesc); | ||
| 399 | assert(configResult); | ||
| 400 | _outputWindow->doneCurrent(); | ||
| 401 | |||
| 402 | for_each_eye([&](ovrEyeType eye) { | ||
| 403 | //Get texture size | ||
| 404 | _eyeTextures[eye].Header.API = ovrRenderAPI_OpenGL; | ||
| 405 | _eyeTextures[eye].Header.TextureSize = _renderTargetSize; | ||
| 406 | _eyeTextures[eye].Header.RenderViewport.Pos = { 0, 0 }; | ||
| 407 | _eyeTextures[eye].Header.RenderViewport.Size = _renderTargetSize; | ||
| 408 | _eyeTextures[eye].Header.RenderViewport.Size.w /= 2; | ||
| 409 | }); | ||
| 410 | _eyeTextures[ovrEye_Right].Header.RenderViewport.Pos.x = _recommendedTexSize.w; | ||
| 411 | for_each_eye([&](ovrEyeType eye) { | ||
| 412 | _eyeViewports[eye] = _eyeTextures[eye].Header.RenderViewport; | ||
| 413 | }); | ||
| 414 | #endif | ||
| 415 | |||
| 416 | ovrHmd_SetEnabledCaps(_ovrHmd, | ||
| 417 | ovrHmdCap_LowPersistence | ovrHmdCap_DynamicPrediction); | ||
| 418 | |||
| 419 | ovrHmd_ConfigureTracking(_ovrHmd, | ||
| 420 | ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, | ||
| 421 | ovrTrackingCap_Orientation); | ||
| 422 | |||
| 423 | if (!_camera) { | ||
| 424 | _camera = new Camera; | ||
| 425 | configureCamera(*_camera); // no need to use screen dimensions; they're ignored | ||
| 220 | } | 426 | } |
| 221 | } | 427 | } |
| 222 | 428 | ||
| 223 | //Disconnects and deallocates the OR | 429 | //Disconnects and deallocates the OR |
| 224 | void OculusManager::disconnect() { | 430 | void OculusManager::disconnect() { |
| 225 | if (_isConnected) { | 431 | if (_isConnected) { |
| 432 | |||
| 433 | #ifdef Q_OS_WIN | ||
| 434 | if (_swapFbo) { | ||
| 435 | delete _swapFbo; | ||
| 436 | _swapFbo = nullptr; | ||
| 437 | } | ||
| 438 | |||
| 439 | if (_mirrorFbo) { | ||
| 440 | delete _mirrorFbo; | ||
| 441 | _mirrorFbo = nullptr; | ||
| 442 | } | ||
| 443 | #else | ||
| 444 | _outputWindow->showNormal(); | ||
| 445 | _outputWindow->deleteLater(); | ||
| 446 | _outputWindow = nullptr; | ||
| 447 | #endif | ||
| 448 | |||
| 449 | if (_ovrHmd) { | ||
| 450 | ovrHmd_Destroy(_ovrHmd); | ||
| 451 | _ovrHmd = nullptr; | ||
| 452 | } | ||
| 453 | ovr_Shutdown(); | ||
| 454 | |||
| 226 | _isConnected = false; | 455 | _isConnected = false; |
| 227 | // Prepare to potentially have to dismiss the HSW again | 456 | // Prepare to potentially have to dismiss the HSW again |
| 228 | // if the user re-enables VR | 457 | // if the user re-enables VR |
| 229 | _hswDismissed = false; | 458 | _hswDismissed = false; |
| 230 | #ifndef OVR_DIRECT_MODE | ||
| 231 | shutdownSdk(); | ||
| 232 | #endif | ||
| 233 | |||
| 234 | #ifdef OVR_CLIENT_DISTORTION | ||
| 235 | //Free the distortion mesh data | ||
| 236 | for (int i = 0; i < ovrEye_Count; i++) { | ||
| 237 | if (_vertices[i] != 0) { | ||
| 238 | glDeleteBuffers(1, &(_vertices[i])); | ||
| 239 | _vertices[i] = 0; | ||
| 240 | } | ||
| 241 | if (_indices[i] != 0) { | ||
| 242 | glDeleteBuffers(1, &(_indices[i])); | ||
| 243 | _indices[i] = 0; | ||
| 244 | } | ||
| 245 | } | ||
| 246 | #endif | ||
| 247 | } | 459 | } |
| 248 | } | 460 | } |
| 249 | 461 | ||
| 250 | void OculusManager::positionCalibrationBillboard(Text3DOverlay* billboard) { | 462 | void positionCalibrationBillboard(Text3DOverlay* billboard) { |
| 251 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | 463 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); |
| 252 | glm::quat headOrientation = myAvatar->getHeadOrientation(); | 464 | glm::quat headOrientation = myAvatar->getHeadOrientation(); |
| 253 | headOrientation.x = 0; | 465 | headOrientation.x = 0; |
| @@ -258,7 +470,7 @@ void OculusManager::positionCalibrationBillboard(Text3DOverlay* billboard) { · 4 unmodified lines | |||
| 258 | billboard->setRotation(headOrientation); | 470 | billboard->setRotation(headOrientation); |
| 259 | } | 471 | } |
| 260 | 472 | ||
| 261 | void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { | 473 | void calibrate(const glm::vec3& position, const glm::quat& orientation) { |
| 262 | static QString instructionMessage = "Hold still to calibrate"; | 474 | static QString instructionMessage = "Hold still to calibrate"; |
| 263 | static QString progressMessage; | 475 | static QString progressMessage; |
| 264 | static Text3DOverlay* billboard; | 476 | static Text3DOverlay* billboard; |
| @@ -346,7 +558,7 @@ void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { · 81 unmodified lines | |||
| 346 | break; | 558 | break; |
| 347 | default: | 559 | default: |
| 348 | break; | 560 | break; |
| 349 | 561 | ||
| 350 | } | 562 | } |
| 351 | } | 563 | } |
| 352 | 564 | ||
| @@ -363,64 +575,9 @@ void OculusManager::abandonCalibration() { · 10 unmodified lines | |||
| 363 | } | 575 | } |
| 364 | } | 576 | } |
| 365 | 577 | ||
| 366 | #ifdef OVR_CLIENT_DISTORTION | ||
| 367 | void OculusManager::generateDistortionMesh() { | ||
| 368 | |||
| 369 | //Check if we already have the distortion mesh | ||
| 370 | if (_vertices[0] != 0) { | ||
| 371 | printf("WARNING: Tried to generate Oculus distortion mesh twice without freeing the VBOs."); | ||
| 372 | return; | ||
| 373 | } | ||
| 374 | |||
| 375 | for (int eyeNum = 0; eyeNum < ovrEye_Count; eyeNum++) { | ||
| 376 | // Allocate and generate distortion mesh vertices | ||
| 377 | ovrDistortionMesh meshData; | ||
| 378 | ovrHmd_CreateDistortionMesh(_ovrHmd, _eyeRenderDesc[eyeNum].Eye, _eyeRenderDesc[eyeNum].Fov, _ovrHmd->DistortionCaps, &meshData); | ||
| 379 | |||
| 380 | // Parse the vertex data and create a render ready vertex buffer | ||
| 381 | DistortionVertex* pVBVerts = new DistortionVertex[meshData.VertexCount]; | ||
| 382 | _meshSize[eyeNum] = meshData.IndexCount; | ||
| 383 | |||
| 384 | // Convert the oculus vertex data to the DistortionVertex format. | ||
| 385 | DistortionVertex* v = pVBVerts; | ||
| 386 | ovrDistortionVertex* ov = meshData.pVertexData; | ||
| 387 | for (unsigned int vertNum = 0; vertNum < meshData.VertexCount; vertNum++) { | ||
| 388 | v->pos.x = ov->ScreenPosNDC.x; | ||
| 389 | v->pos.y = ov->ScreenPosNDC.y; | ||
| 390 | v->texR.x = ov->TanEyeAnglesR.x; | ||
| 391 | v->texR.y = ov->TanEyeAnglesR.y; | ||
| 392 | v->texG.x = ov->TanEyeAnglesG.x; | ||
| 393 | v->texG.y = ov->TanEyeAnglesG.y; | ||
| 394 | v->texB.x = ov->TanEyeAnglesB.x; | ||
| 395 | v->texB.y = ov->TanEyeAnglesB.y; | ||
| 396 | v->color.r = v->color.g = v->color.b = (GLubyte)(ov->VignetteFactor * 255.99f); | ||
| 397 | v->color.a = (GLubyte)(ov->TimeWarpFactor * 255.99f); | ||
| 398 | v++; | ||
| 399 | ov++; | ||
| 400 | } | ||
| 401 | |||
| 402 | //vertices | ||
| 403 | glGenBuffers(1, &(_vertices[eyeNum])); | ||
| 404 | glBindBuffer(GL_ARRAY_BUFFER, _vertices[eyeNum]); | ||
| 405 | glBufferData(GL_ARRAY_BUFFER, sizeof(DistortionVertex) * meshData.VertexCount, pVBVerts, GL_STATIC_DRAW); | ||
| 406 | glBindBuffer(GL_ARRAY_BUFFER, 0); | ||
| 407 | |||
| 408 | //indices | ||
| 409 | glGenBuffers(1, &(_indices[eyeNum])); | ||
| 410 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indices[eyeNum]); | ||
| 411 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short) * meshData.IndexCount, meshData.pIndexData, GL_STATIC_DRAW); | ||
| 412 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); | ||
| 413 | |||
| 414 | //Now that we have the VBOs we can get rid of the mesh data | ||
| 415 | delete [] pVBVerts; | ||
| 416 | ovrHmd_DestroyDistortionMesh(&meshData); | ||
| 417 | } | ||
| 418 | |||
| 419 | } | ||
| 420 | #endif | ||
| 421 | 578 | ||
| 422 | bool OculusManager::isConnected() { | 579 | bool OculusManager::isConnected() { |
| 423 | return _isConnected && Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode); | 580 | return _isConnected; |
| 424 | } | 581 | } |
| 425 | 582 | ||
| 426 | //Begins the frame timing for oculus prediction purposes | 583 | //Begins the frame timing for oculus prediction purposes |
| @@ -428,10 +585,6 @@ void OculusManager::beginFrameTiming() { · 1 unmodified line | |||
| 428 | if (_frameTimingActive) { | 585 | if (_frameTimingActive) { |
| 429 | printf("WARNING: Called OculusManager::beginFrameTiming() twice in a row, need to call OculusManager::endFrameTiming()."); | 586 | printf("WARNING: Called OculusManager::beginFrameTiming() twice in a row, need to call OculusManager::endFrameTiming()."); |
| 430 | } | 587 | } |
| 431 | |||
| 432 | #ifdef OVR_CLIENT_DISTORTION | ||
| 433 | _hmdFrameTiming = ovrHmd_BeginFrameTiming(_ovrHmd, _frameIndex); | ||
| 434 | #endif | ||
| 435 | _frameTimingActive = true; | 588 | _frameTimingActive = true; |
| 436 | } | 589 | } |
| 437 | 590 | ||
| @@ -441,27 +594,18 @@ bool OculusManager::allowSwap() { · 3 unmodified lines | |||
| 441 | 594 | ||
| 442 | //Ends frame timing | 595 | //Ends frame timing |
| 443 | void OculusManager::endFrameTiming() { | 596 | void OculusManager::endFrameTiming() { |
| 444 | #ifdef OVR_CLIENT_DISTORTION | ||
| 445 | ovrHmd_EndFrameTiming(_ovrHmd); | ||
| 446 | #endif | ||
| 447 | _frameIndex++; | 597 | _frameIndex++; |
| 448 | _frameTimingActive = false; | 598 | _frameTimingActive = false; |
| 449 | } | 599 | } |
| 450 | 600 | ||
| 451 | //Sets the camera FoV and aspect ratio | 601 | //Sets the camera FoV and aspect ratio |
| 452 | void OculusManager::configureCamera(Camera& camera) { | 602 | void OculusManager::configureCamera(Camera& camera) { |
| 453 | ovrFovPort fov; | ||
| 454 | if (_activeEye == ovrEye_Count) { | 603 | if (_activeEye == ovrEye_Count) { |
| 455 | // When not rendering, provide a FOV encompasing both eyes | 604 | // When not rendering, provide a FOV encompasing both eyes |
| 456 | fov = _eyeFov[0]; | 605 | camera.setProjection(_combinedProjection); |
| 457 | fov.RightTan = _eyeFov[1].RightTan; | 606 | return; |
| 458 | } else { | 607 | } |
| 459 | // When rendering, provide the exact FOV | 608 | camera.setProjection(_eyeProjection[_activeEye]); |
| 460 | fov = _eyeFov[_activeEye]; | ||
| 461 | } | ||
| 462 | // Convert the FOV to the correct projection matrix | ||
| 463 | glm::mat4 projection = toGlm(ovrMatrix4f_Projection(fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded)); | ||
| 464 | camera.setProjection(projection); | ||
| 465 | } | 609 | } |
| 466 | 610 | ||
| 467 | //Displays everything for the oculus, frame timing must be active | 611 | //Displays everything for the oculus, frame timing must be active |
| @@ -473,40 +617,9 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const · 5 unmodified lines | |||
| 473 | assert(oldFrameIndex == -1 || (unsigned int)oldFrameIndex == _frameIndex - 1); | 617 | assert(oldFrameIndex == -1 || (unsigned int)oldFrameIndex == _frameIndex - 1); |
| 474 | oldFrameIndex = _frameIndex; | 618 | oldFrameIndex = _frameIndex; |
| 475 | #endif | 619 | #endif |
| 476 | |||
| 477 | // Every so often do some additional timing calculations and debug output | ||
| 478 | bool debugFrame = 0 == _frameIndex % 400; | ||
| 479 | 620 | ||
| 480 | #if 0 | 621 | #ifndef Q_OS_WIN |
| 481 | // Try to measure the amount of time taken to do the distortion | 622 | |
| 482 | // (does not seem to work on OSX with SDK based distortion) | ||
| 483 | // FIXME can't use a static object here, because it will cause a crash when the | ||
| 484 | // query attempts deconstruct after the GL context is gone. | ||
| 485 | static bool timerActive = false; | ||
| 486 | static QOpenGLTimerQuery timerQuery; | ||
| 487 | if (!timerQuery.isCreated()) { | ||
| 488 | timerQuery.create(); | ||
| 489 | } | ||
| 490 | |||
| 491 | if (timerActive && timerQuery.isResultAvailable()) { | ||
| 492 | auto result = timerQuery.waitForResult(); | ||
| 493 | if (result) { qCDebug(interfaceapp) << "Distortion took " << result << "ns"; }; | ||
| 494 | timerActive = false; | ||
| 495 | } | ||
| 496 | #endif | ||
| 497 | |||
| 498 | #ifdef OVR_DIRECT_MODE | ||
| 499 | static bool attached = false; | ||
| 500 | if (!attached) { | ||
| 501 | attached = true; | ||
| 502 | void * nativeWindowHandle = (void*)(size_t)glCanvas->effectiveWinId(); | ||
| 503 | if (nullptr != nativeWindowHandle) { | ||
| 504 | ovrHmd_AttachToWindow(_ovrHmd, nativeWindowHandle, nullptr, nullptr); | ||
| 505 | } | ||
| 506 | } | ||
| 507 | #endif | ||
| 508 | |||
| 509 | #ifndef OVR_CLIENT_DISTORTION | ||
| 510 | // FIXME: we need a better way of responding to the HSW. In particular | 623 | // FIXME: we need a better way of responding to the HSW. In particular |
| 511 | // we need to ensure that it's only displayed once per session, rather than | 624 | // we need to ensure that it's only displayed once per session, rather than |
| 512 | // every time the user toggles VR mode, and we need to hook it up to actual | 625 | // every time the user toggles VR mode, and we need to hook it up to actual |
| @@ -522,7 +635,7 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const · 9 unmodified lines | |||
| 522 | } | 635 | } |
| 523 | } | 636 | } |
| 524 | #endif | 637 | #endif |
| 525 | 638 | ||
| 526 | 639 | ||
| 527 | //beginFrameTiming must be called before display | 640 | //beginFrameTiming must be called before display |
| 528 | if (!_frameTimingActive) { | 641 | if (!_frameTimingActive) { |
| @@ -545,14 +658,14 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const · 16 unmodified lines | |||
| 545 | 658 | ||
| 546 | glMatrixMode(GL_MODELVIEW); | 659 | glMatrixMode(GL_MODELVIEW); |
| 547 | glPushMatrix(); | 660 | glPushMatrix(); |
| 548 | 661 | ||
| 549 | glm::quat orientation; | 662 | glm::quat orientation; |
| 550 | glm::vec3 trackerPosition; | 663 | glm::vec3 trackerPosition; |
| 551 | auto deviceSize = qApp->getDeviceSize(); | 664 | auto deviceSize = qApp->getDeviceSize(); |
| 552 | 665 | ||
| 553 | ovrTrackingState ts = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds()); | 666 | ovrTrackingState ts = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds()); |
| 554 | ovrVector3f ovrHeadPosition = ts.HeadPose.ThePose.Position; | 667 | ovrVector3f ovrHeadPosition = ts.HeadPose.ThePose.Position; |
| 555 | 668 | ||
| 556 | trackerPosition = glm::vec3(ovrHeadPosition.x, ovrHeadPosition.y, ovrHeadPosition.z); | 669 | trackerPosition = glm::vec3(ovrHeadPosition.x, ovrHeadPosition.y, ovrHeadPosition.z); |
| 557 | 670 | ||
| 558 | if (_calibrationState != CALIBRATED) { | 671 | if (_calibrationState != CALIBRATED) { |
| @@ -562,11 +675,11 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const · 3 unmodified lines | |||
| 562 | } | 675 | } |
| 563 | 676 | ||
| 564 | trackerPosition = bodyOrientation * trackerPosition; | 677 | trackerPosition = bodyOrientation * trackerPosition; |
| 565 | static ovrVector3f eyeOffsets[2] = { { 0, 0, 0 }, { 0, 0, 0 } }; | ||
| 566 | ovrPosef eyePoses[ovrEye_Count]; | 678 | ovrPosef eyePoses[ovrEye_Count]; |
| 567 | ovrHmd_GetEyePoses(_ovrHmd, _frameIndex, eyeOffsets, eyePoses, nullptr); | 679 | ovrHmd_GetEyePoses(_ovrHmd, _frameIndex, _eyeOffsets, eyePoses, nullptr); |
| 680 | #ifndef Q_OS_WIN | ||
| 568 | ovrHmd_BeginFrame(_ovrHmd, _frameIndex); | 681 | ovrHmd_BeginFrame(_ovrHmd, _frameIndex); |
| 569 | static ovrPosef eyeRenderPose[ovrEye_Count]; | 682 | #endif |
| 570 | //Render each eye into an fbo | 683 | //Render each eye into an fbo |
| 571 | for_each_eye(_ovrHmd, [&](ovrEyeType eye){ | 684 | for_each_eye(_ovrHmd, [&](ovrEyeType eye){ |
| 572 | // If we're in eye-per-frame mode, only render one eye | 685 | // If we're in eye-per-frame mode, only render one eye |
| @@ -576,29 +689,17 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const · 3 unmodified lines | |||
| 576 | return; | 689 | return; |
| 577 | } | 690 | } |
| 578 | _lastEyeRendered = _activeEye = eye; | 691 | _lastEyeRendered = _activeEye = eye; |
| 579 | eyeRenderPose[eye] = eyePoses[eye]; | 692 | _eyeRenderPoses[eye] = eyePoses[eye]; |
| 580 | // Set the camera rotation for this eye | 693 | // Set the camera rotation for this eye |
| 581 | orientation.x = eyeRenderPose[eye].Orientation.x; | ||
| 582 | orientation.y = eyeRenderPose[eye].Orientation.y; | ||
| 583 | orientation.z = eyeRenderPose[eye].Orientation.z; | ||
| 584 | orientation.w = eyeRenderPose[eye].Orientation.w; | ||
| 585 | |||
| 586 | // Update the application camera with the latest HMD position | ||
| 587 | whichCamera.setHmdPosition(trackerPosition); | ||
| 588 | whichCamera.setHmdRotation(orientation); | ||
| 589 | |||
| 590 | 694 | ||
| 695 | vec3 eyePosition = toGlm(_eyeRenderPoses[eye].Position); | ||
| 696 | eyePosition = whichCamera.getRotation() * eyePosition; | ||
| 697 | quat eyeRotation = toGlm(_eyeRenderPoses[eye].Orientation); | ||
| 698 | |||
| 591 | // Update our camera to what the application camera is doing | 699 | // Update our camera to what the application camera is doing |
| 592 | _camera->setRotation(whichCamera.getRotation()); | 700 | _camera->setRotation(whichCamera.getRotation() * eyeRotation); |
| 593 | _camera->setPosition(whichCamera.getPosition()); | 701 | _camera->setPosition(whichCamera.getPosition() + eyePosition); |
| 594 | configureCamera(*_camera); | 702 | configureCamera(*_camera); |
| 595 | |||
| 596 | // Store the latest left and right eye render locations for things that need to know | ||
| 597 | glm::vec3 thisEyePosition = position + trackerPosition + | ||
| 598 | (bodyOrientation * glm::quat(orientation.x, orientation.y, orientation.z, orientation.w) * | ||
| 599 | glm::vec3(_eyeRenderDesc[eye].HmdToEyeViewOffset.x, _eyeRenderDesc[eye].HmdToEyeViewOffset.y, _eyeRenderDesc[eye].HmdToEyeViewOffset.z)); | ||
| 600 | |||
| 601 | _eyePositions[eye] = thisEyePosition; | ||
| 602 | _camera->update(1.0f / Application::getInstance()->getFps()); | 703 | _camera->update(1.0f / Application::getInstance()->getFps()); |
| 603 | 704 | ||
| 604 | glMatrixMode(GL_PROJECTION); | 705 | glMatrixMode(GL_PROJECTION); |
| @@ -607,15 +708,14 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const · 2 unmodified lines | |||
| 607 | glMatrixMode(GL_MODELVIEW); | 708 | glMatrixMode(GL_MODELVIEW); |
| 608 | glLoadIdentity(); | 709 | glLoadIdentity(); |
| 609 | 710 | ||
| 610 | ovrRecti & vp = _eyeTextures[eye].Header.RenderViewport; | 711 | ovrRecti & vp = _eyeViewports[eye]; |
| 611 | vp.Size.h = _recommendedTexSize.h * _offscreenRenderScale; | 712 | vp.Size.h = _recommendedTexSize.h * _offscreenRenderScale; |
| 612 | vp.Size.w = _recommendedTexSize.w * _offscreenRenderScale; | 713 | vp.Size.w = _recommendedTexSize.w * _offscreenRenderScale; |
| 613 | |||
| 614 | glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h); | 714 | glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h); |
| 615 | 715 | ||
| 616 | renderArgs->_renderSide = RenderArgs::MONO; | 716 | renderArgs->_renderSide = RenderArgs::MONO; |
| 617 | qApp->displaySide(renderArgs, *_camera, false); | 717 | qApp->displaySide(renderArgs, *_camera); |
| 618 | qApp->getApplicationOverlay().displayOverlayTextureHmd(*_camera); | 718 | qApp->getApplicationCompositor().displayOverlayTextureHmd(renderArgs, eye); |
| 619 | }); | 719 | }); |
| 620 | _activeEye = ovrEye_Count; | 720 | _activeEye = ovrEye_Count; |
| 621 | 721 | ||
| @@ -628,7 +728,7 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const · 6 unmodified lines | |||
| 628 | glViewport(0, 0, _renderTargetSize.w, _renderTargetSize.h); | 728 | glViewport(0, 0, _renderTargetSize.w, _renderTargetSize.h); |
| 629 | finalFbo = DependencyManager::get<GlowEffect>()->render(renderArgs); | 729 | finalFbo = DependencyManager::get<GlowEffect>()->render(renderArgs); |
| 630 | } else { | 730 | } else { |
| 631 | finalFbo = DependencyManager::get<TextureCache>()->getPrimaryFramebuffer(); | 731 | finalFbo = DependencyManager::get<TextureCache>()->getPrimaryFramebuffer(); |
| 632 | glBindFramebuffer(GL_FRAMEBUFFER, 0); | 732 | glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| 633 | } | 733 | } |
| 634 | 734 | ||
| @@ -638,141 +738,73 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const · 3 unmodified lines | |||
| 638 | // restore our normal viewport | 738 | // restore our normal viewport |
| 639 | glViewport(0, 0, deviceSize.width(), deviceSize.height()); | 739 | glViewport(0, 0, deviceSize.width(), deviceSize.height()); |
| 640 | 740 | ||
| 641 | #if 0 | 741 | #ifdef Q_OS_WIN |
| 642 | if (debugFrame && !timerActive) { | 742 | auto srcFboSize = finalFbo->getSize(); |
| 643 | timerQuery.begin(); | ||
| 644 | } | ||
| 645 | #endif | ||
| 646 | 743 | ||
| 647 | #ifdef OVR_CLIENT_DISTORTION | ||
| 648 | |||
| 649 | //Wait till time-warp to reduce latency | ||
| 650 | ovr_WaitTillTime(_hmdFrameTiming.TimewarpPointSeconds); | ||
| 651 | 744 | ||
| 652 | #ifdef DEBUG_RENDER_WITHOUT_DISTORTION | 745 | // Blit to the oculus provided texture |
| 653 | auto fboSize = finalFbo->getSize(); | ||
| 654 | glBindFramebuffer(GL_READ_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo)); | 746 | glBindFramebuffer(GL_READ_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo)); |
| 747 | _swapFbo->Bound(oglplus::Framebuffer::Target::Draw, [&] { | ||
| 748 | glBlitFramebuffer( | ||
| 749 | 0, 0, srcFboSize.x, srcFboSize.y, | ||
| 750 | 0, 0, _swapFbo->size.x, _swapFbo->size.y, | ||
| 751 | GL_COLOR_BUFFER_BIT, GL_NEAREST); | ||
| 752 | }); | ||
| 753 | |||
| 754 | // Blit to the onscreen window | ||
| 755 | auto destWindowSize = qApp->getDeviceSize(); | ||
| 655 | glBlitFramebuffer( | 756 | glBlitFramebuffer( |
| 656 | 0, 0, fboSize.x, fboSize.y, | 757 | 0, 0, srcFboSize.x, srcFboSize.y, |
| 657 | 0, 0, deviceSize.width(), deviceSize.height(), | 758 | 0, 0, destWindowSize.width(), destWindowSize.height(), |
| 658 | GL_COLOR_BUFFER_BIT, GL_NEAREST); | 759 | GL_COLOR_BUFFER_BIT, GL_NEAREST); |
| 760 | glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); | ||
| 761 | |||
| 762 | // Submit the frame to the Oculus SDK for timewarp and distortion | ||
| 763 | for_each_eye([&](ovrEyeType eye) { | ||
| 764 | _sceneLayer.RenderPose[eye] = _eyeRenderPoses[eye]; | ||
| 765 | }); | ||
| 766 | auto header = &_sceneLayer.Header; | ||
| 767 | ovrResult res = ovrHmd_SubmitFrame(_ovrHmd, _frameIndex, nullptr, &header, 1); | ||
| 768 | Q_ASSERT(OVR_SUCCESS(res)); | ||
| 769 | _swapFbo->Increment(); | ||
| 659 | #else | 770 | #else |
| 660 | //Clear the color buffer to ensure that there isnt any residual color | 771 | GLsync syncObject = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); |
| 661 | //Left over from when OR was not connected. | 772 | glFlush(); |
| 662 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 663 | glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(finalFbo->getRenderBuffer(0))); | ||
| 664 | //Renders the distorted mesh onto the screen | ||
| 665 | renderDistortionMesh(eyeRenderPose); | ||
| 666 | glBindTexture(GL_TEXTURE_2D, 0); | ||
| 667 | #endif | ||
| 668 | glCanvas->swapBuffers(); | ||
| 669 | 773 | ||
| 670 | #else | ||
| 671 | 774 | ||
| 775 | _outputWindow->makeCurrent(); | ||
| 776 | // force the compositing context to wait for the texture | ||
| 777 | // rendering to complete before it starts the distortion rendering, | ||
| 778 | // but without triggering a CPU/GPU synchronization | ||
| 779 | glWaitSync(syncObject, 0, GL_TIMEOUT_IGNORED); | ||
| 780 | glDeleteSync(syncObject); | ||
| 781 | |||
| 782 | GLuint textureId = gpu::GLBackend::getTextureID(finalFbo->getRenderBuffer(0)); | ||
| 672 | for_each_eye([&](ovrEyeType eye) { | 783 | for_each_eye([&](ovrEyeType eye) { |
| 673 | ovrGLTexture & glEyeTexture = reinterpret_cast<ovrGLTexture&>(_eyeTextures[eye]); | 784 | ovrGLTexture & glEyeTexture = reinterpret_cast<ovrGLTexture&>(_eyeTextures[eye]); |
| 674 | glEyeTexture.OGL.TexId = finalFbo->texture(); | 785 | glEyeTexture.OGL.TexId = textureId; |
| 675 | |||
| 676 | }); | ||
| 677 | 786 | ||
| 678 | ovrHmd_EndFrame(_ovrHmd, eyeRenderPose, _eyeTextures); | 787 | }); |
| 679 | 788 | ||
| 789 | // restore our normal viewport | ||
| 790 | ovrHmd_EndFrame(_ovrHmd, _eyeRenderPoses, _eyeTextures); | ||
| 791 | glCanvas->makeCurrent(); | ||
| 680 | #endif | 792 | #endif |
| 681 | 793 | ||
| 682 | #if 0 | ||
| 683 | if (debugFrame && !timerActive) { | ||
| 684 | timerQuery.end(); | ||
| 685 | timerActive = true; | ||
| 686 | } | ||
| 687 | #endif | ||
| 688 | 794 | ||
| 689 | // No DK2, no message. | 795 | // in order to account account for changes in the pick ray caused by head movement |
| 796 | // we need to force a mouse move event on every frame (perhaps we could change this | ||
| 797 | // to based on the head moving a minimum distance from the last position in which we | ||
| 798 | // sent?) | ||
| 690 | { | 799 | { |
| 691 | float latencies[5] = {}; | 800 | QMouseEvent mouseEvent(QEvent::MouseMove, glCanvas->mapFromGlobal(QCursor::pos()), |
| 692 | if (debugFrame && ovrHmd_GetFloatArray(_ovrHmd, "DK2Latency", latencies, 5) == 5) | 801 | Qt::NoButton, Qt::NoButton, 0); |
| 693 | { | 802 | qApp->mouseMoveEvent(&mouseEvent, 0); |
| 694 | bool nonZero = false; | ||
| 695 | for (int i = 0; i < 5; ++i) | ||
| 696 | { | ||
| 697 | nonZero |= (latencies[i] != 0.f); | ||
| 698 | } | ||
| 699 | |||
| 700 | if (nonZero) | ||
| 701 | { | ||
| 702 | qCDebug(interfaceapp) << QString().sprintf("M2P Latency: Ren: %4.2fms TWrp: %4.2fms PostPresent: %4.2fms Err: %4.2fms %4.2fms", | ||
| 703 | latencies[0], latencies[1], latencies[2], latencies[3], latencies[4]); | ||
| 704 | } | ||
| 705 | } | ||
| 706 | } | 803 | } |
| 707 | |||
| 708 | } | ||
| 709 | |||
| 710 | #ifdef OVR_CLIENT_DISTORTION | ||
| 711 | void OculusManager::renderDistortionMesh(ovrPosef eyeRenderPose[ovrEye_Count]) { | ||
| 712 | 804 | ||
| 713 | glLoadIdentity(); | ||
| 714 | auto deviceSize = qApp->getDeviceSize(); | ||
| 715 | glOrtho(0, deviceSize.width(), 0, deviceSize.height(), -1.0, 1.0); | ||
| 716 | |||
| 717 | glDisable(GL_DEPTH_TEST); | ||
| 718 | |||
| 719 | glDisable(GL_BLEND); | ||
| 720 | _program.bind(); | ||
| 721 | _program.setUniformValue(_textureLocation, 0); | ||
| 722 | |||
| 723 | _program.enableAttributeArray(_positionAttributeLocation); | ||
| 724 | _program.enableAttributeArray(_colorAttributeLocation); | ||
| 725 | _program.enableAttributeArray(_texCoord0AttributeLocation); | ||
| 726 | _program.enableAttributeArray(_texCoord1AttributeLocation); | ||
| 727 | _program.enableAttributeArray(_texCoord2AttributeLocation); | ||
| 728 | |||
| 729 | //Render the distortion meshes for each eye | ||
| 730 | for (int eyeNum = 0; eyeNum < ovrEye_Count; eyeNum++) { | ||
| 731 | |||
| 732 | ovrHmd_GetRenderScaleAndOffset(_eyeRenderDesc[eyeNum].Fov, _renderTargetSize, _eyeTextures[eyeNum].Header.RenderViewport, | ||
| 733 | _UVScaleOffset[eyeNum]); | ||
| 734 | |||
| 735 | GLfloat uvScale[2] = { _UVScaleOffset[eyeNum][0].x, _UVScaleOffset[eyeNum][0].y }; | ||
| 736 | _program.setUniformValueArray(_eyeToSourceUVScaleLocation, uvScale, 1, 2); | ||
| 737 | GLfloat uvOffset[2] = { _UVScaleOffset[eyeNum][1].x, 1.0f - _UVScaleOffset[eyeNum][1].y }; | ||
| 738 | _program.setUniformValueArray(_eyeToSourceUVOffsetLocation, uvOffset, 1, 2); | ||
| 739 | |||
| 740 | ovrMatrix4f timeWarpMatrices[2]; | ||
| 741 | glm::mat4 transposeMatrices[2]; | ||
| 742 | //Grabs the timewarp matrices to be used in the shader | ||
| 743 | ovrHmd_GetEyeTimewarpMatrices(_ovrHmd, (ovrEyeType)eyeNum, eyeRenderPose[eyeNum], timeWarpMatrices); | ||
| 744 | //Have to transpose the matrices before using them | ||
| 745 | transposeMatrices[0] = glm::transpose(toGlm(timeWarpMatrices[0])); | ||
| 746 | transposeMatrices[1] = glm::transpose(toGlm(timeWarpMatrices[1])); | ||
| 747 | 805 | ||
| 748 | glUniformMatrix4fv(_eyeRotationStartLocation, 1, GL_FALSE, (GLfloat *)&transposeMatrices[0][0][0]); | ||
| 749 | glUniformMatrix4fv(_eyeRotationEndLocation, 1, GL_FALSE, (GLfloat *)&transposeMatrices[1][0][0]); | ||
| 750 | 806 | ||
| 751 | glBindBuffer(GL_ARRAY_BUFFER, _vertices[eyeNum]); | ||
| 752 | |||
| 753 | //Set vertex attribute pointers | ||
| 754 | glVertexAttribPointer(_positionAttributeLocation, 2, GL_FLOAT, GL_FALSE, sizeof(DistortionVertex), (void *)0); | ||
| 755 | glVertexAttribPointer(_texCoord0AttributeLocation, 2, GL_FLOAT, GL_FALSE, sizeof(DistortionVertex), (void *)8); | ||
| 756 | glVertexAttribPointer(_texCoord1AttributeLocation, 2, GL_FLOAT, GL_FALSE, sizeof(DistortionVertex), (void *)16); | ||
| 757 | glVertexAttribPointer(_texCoord2AttributeLocation, 2, GL_FLOAT, GL_FALSE, sizeof(DistortionVertex), (void *)24); | ||
| 758 | glVertexAttribPointer(_colorAttributeLocation, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(DistortionVertex), (void *)32); | ||
| 759 | |||
| 760 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indices[eyeNum]); | ||
| 761 | glDrawElements(GL_TRIANGLES, _meshSize[eyeNum], GL_UNSIGNED_SHORT, 0); | ||
| 762 | } | ||
| 763 | |||
| 764 | _program.disableAttributeArray(_positionAttributeLocation); | ||
| 765 | _program.disableAttributeArray(_colorAttributeLocation); | ||
| 766 | _program.disableAttributeArray(_texCoord0AttributeLocation); | ||
| 767 | _program.disableAttributeArray(_texCoord1AttributeLocation); | ||
| 768 | _program.disableAttributeArray(_texCoord2AttributeLocation); | ||
| 769 | |||
| 770 | glEnable(GL_BLEND); | ||
| 771 | glEnable(GL_DEPTH_TEST); | ||
| 772 | _program.release(); | ||
| 773 | glBindBuffer(GL_ARRAY_BUFFER, 0); | ||
| 774 | } | 807 | } |
| 775 | #endif | ||
| 776 | 808 | ||
| 777 | //Tries to reconnect to the sensors | 809 | //Tries to reconnect to the sensors |
| 778 | void OculusManager::reset() { | 810 | void OculusManager::reset() { |
| @@ -781,21 +813,6 @@ void OculusManager::reset() { · 2 unmodified lines | |||
| 781 | } | 813 | } |
| 782 | } | 814 | } |
| 783 | 815 | ||
| 784 | //Gets the current predicted angles from the oculus sensors | ||
| 785 | void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) { | ||
| 786 | ovrTrackingState ts = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds()); | ||
| 787 | if (ts.StatusFlags & (ovrStatus_OrientationTracked | ovrStatus_PositionTracked)) { | ||
| 788 | glm::vec3 euler = glm::eulerAngles(toGlm(ts.HeadPose.ThePose.Orientation)); | ||
| 789 | yaw = euler.y; | ||
| 790 | pitch = euler.x; | ||
| 791 | roll = euler.z; | ||
| 792 | } else { | ||
| 793 | yaw = 0.0f; | ||
| 794 | pitch = 0.0f; | ||
| 795 | roll = 0.0f; | ||
| 796 | } | ||
| 797 | } | ||
| 798 | |||
| 799 | glm::vec3 OculusManager::getRelativePosition() { | 816 | glm::vec3 OculusManager::getRelativePosition() { |
| 800 | ovrTrackingState trackingState = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds()); | 817 | ovrTrackingState trackingState = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds()); |
| 801 | return toGlm(trackingState.HeadPose.ThePose.Position); | 818 | return toGlm(trackingState.HeadPose.ThePose.Position); |
| @@ -826,6 +843,9 @@ void OculusManager::overrideOffAxisFrustum(float& left, float& right, float& bot · 24 unmodified lines | |||
| 826 | } | 843 | } |
| 827 | 844 | ||
| 828 | int OculusManager::getHMDScreen() { | 845 | int OculusManager::getHMDScreen() { |
| 846 | #ifdef Q_OS_WIN | ||
| 847 | return -1; | ||
| 848 | #else | ||
| 829 | int hmdScreenIndex = -1; // unknown | 849 | int hmdScreenIndex = -1; // unknown |
| 830 | // TODO: it might be smarter to handle multiple HMDs connected in this case. but for now, | 850 | // TODO: it might be smarter to handle multiple HMDs connected in this case. but for now, |
| 831 | // we will simply assume the initialization code that set up _ovrHmd picked the best hmd | 851 | // we will simply assume the initialization code that set up _ovrHmd picked the best hmd |
| @@ -844,7 +864,7 @@ int OculusManager::getHMDScreen() { · 12 unmodified lines | |||
| 844 | const int SIMILAR_NAMES = 10; | 864 | const int SIMILAR_NAMES = 10; |
| 845 | const int EXACT_LOCATION_MATCH = 50; | 865 | const int EXACT_LOCATION_MATCH = 50; |
| 846 | const int EXACT_RESOLUTION_MATCH = 25; | 866 | const int EXACT_RESOLUTION_MATCH = 25; |
| 847 | 867 | ||
| 848 | int bestMatchScore = 0; | 868 | int bestMatchScore = 0; |
| 849 | 869 | ||
| 850 | // look at the display list and see if we can find the best match | 870 | // look at the display list and see if we can find the best match |
| @@ -853,7 +873,7 @@ int OculusManager::getHMDScreen() { · 2 unmodified lines | |||
| 853 | foreach (QScreen* screen, QGuiApplication::screens()) { | 873 | foreach (QScreen* screen, QGuiApplication::screens()) { |
| 854 | QString screenName = screen->name(); | 874 | QString screenName = screen->name(); |
| 855 | QRect screenRect = desktop->screenGeometry(screenNumber); | 875 | QRect screenRect = desktop->screenGeometry(screenNumber); |
| 856 | 876 | ||
| 857 | int screenScore = 0; | 877 | int screenScore = 0; |
| 858 | if (screenName == productNameFromOVR) { | 878 | if (screenName == productNameFromOVR) { |
| 859 | screenScore += EXACT_NAME_MATCH; | 879 | screenScore += EXACT_NAME_MATCH; |
| @@ -876,5 +896,18 @@ int OculusManager::getHMDScreen() { · 16 unmodified lines | |||
| 876 | } | 896 | } |
| 877 | } | 897 | } |
| 878 | return hmdScreenIndex; | 898 | return hmdScreenIndex; |
| 899 | #endif | ||
| 900 | } | ||
| 901 | |||
| 902 | mat4 OculusManager::getEyeProjection(int eye) { | ||
| 903 | return _eyeProjection[eye]; | ||
| 879 | } | 904 | } |
| 880 | 905 | ||
| 906 | mat4 OculusManager::getEyePose(int eye) { | ||
| 907 | return toGlm(_eyeRenderPoses[eye]); | ||
| 908 | } | ||
| 909 | |||
| 910 | mat4 OculusManager::getHeadPose() { | ||
| 911 | ovrTrackingState ts = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds()); | ||
| 912 | return toGlm(ts.HeadPose.ThePose); | ||
| 913 | } | ||
| @@ -13,8 +13,6 @@ · 12 unmodified lines | |||
| 13 | #ifndef hifi_OculusManager_h | 13 | #ifndef hifi_OculusManager_h |
| 14 | #define hifi_OculusManager_h | 14 | #define hifi_OculusManager_h |
| 15 | 15 | ||
| 16 | #include <OVR_CAPI.h> | ||
| 17 | |||
| 18 | #include <ProgramObject.h> | 16 | #include <ProgramObject.h> |
| 19 | #include <glm/glm.hpp> | 17 | #include <glm/glm.hpp> |
| 20 | #include <glm/gtc/matrix_transform.hpp> | 18 | #include <glm/gtc/matrix_transform.hpp> |
| @@ -22,39 +20,13 @@ · 1 unmodified line | |||
| 22 | 20 | ||
| 23 | #include "RenderArgs.h" | 21 | #include "RenderArgs.h" |
| 24 | 22 | ||
| 23 | class QOpenGLContext; | ||
| 25 | class Camera; | 24 | class Camera; |
| 26 | class PalmData; | ||
| 27 | class Text3DOverlay; | ||
| 28 | |||
| 29 | // Uncomment this to enable client side distortion. NOT recommended since | ||
| 30 | // the Oculus SDK will ideally provide the best practices for distortion in | ||
| 31 | // in terms of performance and quality, and by using it we will get updated | ||
| 32 | // best practices for free with new runtime releases. | ||
| 33 | #define OVR_CLIENT_DISTORTION 1 | ||
| 34 | |||
| 35 | |||
| 36 | // Direct HMD mode is currently only supported on windows and some linux systems will | ||
| 37 | // misbehave if we try to enable the Oculus SDK at all, so isolate support for Direct | ||
| 38 | // mode only to windows for now | ||
| 39 | #ifdef Q_OS_WIN | ||
| 40 | // On Win32 platforms, enabling Direct HMD requires that the SDK be | ||
| 41 | // initialized before the GL context is set up, but this breaks v-sync | ||
| 42 | // for any application that has a Direct mode enable Rift connected | ||
| 43 | // but is not rendering to it. For the time being I'm setting this as | ||
| 44 | // a macro enabled mechanism which changes where the SDK is initialized. | ||
| 45 | // To enable Direct HMD mode, you can un-comment this, but with the | ||
| 46 | // caveat that it will break v-sync in NON-VR mode if you have an Oculus | ||
| 47 | // Rift connect and in Direct mode | ||
| 48 | #define OVR_DIRECT_MODE 1 | ||
| 49 | #endif | ||
| 50 | |||
| 51 | 25 | ||
| 52 | /// Handles interaction with the Oculus Rift. | 26 | /// Handles interaction with the Oculus Rift. |
| 53 | class OculusManager { | 27 | class OculusManager { |
| 54 | public: | 28 | public: |
| 55 | static void init(); | 29 | static void connect(QOpenGLContext* shareContext); |
| 56 | static void deinit(); | ||
| 57 | static void connect(); | ||
| 58 | static void disconnect(); | 30 | static void disconnect(); |
| 59 | static bool isConnected(); | 31 | static bool isConnected(); |
| 60 | static void recalibrate(); | 32 | static void recalibrate(); |
| @@ -66,10 +38,6 @@ public: · 5 unmodified lines | |||
| 66 | static void display(QGLWidget * glCanvas, RenderArgs* renderArgs, const glm::quat &bodyOrientation, const glm::vec3 &position, Camera& whichCamera); | 38 | static void display(QGLWidget * glCanvas, RenderArgs* renderArgs, const glm::quat &bodyOrientation, const glm::vec3 &position, Camera& whichCamera); |
| 67 | static void reset(); | 39 | static void reset(); |
| 68 | 40 | ||
| 69 | /// param \yaw[out] yaw in radians | ||
| 70 | /// param \pitch[out] pitch in radians | ||
| 71 | /// param \roll[out] roll in radians | ||
| 72 | static void getEulerAngles(float& yaw, float& pitch, float& roll); | ||
| 73 | static glm::vec3 getRelativePosition(); | 41 | static glm::vec3 getRelativePosition(); |
| 74 | static glm::quat getOrientation(); | 42 | static glm::quat getOrientation(); |
| 75 | static QSize getRenderTargetSize(); | 43 | static QSize getRenderTargetSize(); |
| @@ -77,145 +45,14 @@ public: · 1 unmodified line | |||
| 77 | static void overrideOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal, | 45 | static void overrideOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal, |
| 78 | float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane); | 46 | float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane); |
| 79 | 47 | ||
| 80 | static glm::vec3 getLeftEyePosition() { return _eyePositions[ovrEye_Left]; } | 48 | static glm::vec3 getLeftEyePosition(); |
| 81 | static glm::vec3 getRightEyePosition() { return _eyePositions[ovrEye_Right]; } | 49 | static glm::vec3 getRightEyePosition(); |
| 82 | 50 | ||
| 83 | static int getHMDScreen(); | 51 | static int getHMDScreen(); |
| 84 | |||
| 85 | private: | ||
| 86 | static void initSdk(); | ||
| 87 | static void shutdownSdk(); | ||
| 88 | #ifdef OVR_CLIENT_DISTORTION | ||
| 89 | static void generateDistortionMesh(); | ||
| 90 | static void renderDistortionMesh(ovrPosef eyeRenderPose[ovrEye_Count]); | ||
| 91 | struct DistortionVertex { | ||
| 92 | glm::vec2 pos; | ||
| 93 | glm::vec2 texR; | ||
| 94 | glm::vec2 texG; | ||
| 95 | glm::vec2 texB; | ||
| 96 | struct { | ||
| 97 | GLubyte r; | ||
| 98 | GLubyte g; | ||
| 99 | GLubyte b; | ||
| 100 | GLubyte a; | ||
| 101 | } color; | ||
| 102 | }; | ||
| 103 | 52 | ||
| 104 | static ProgramObject _program; | 53 | static glm::mat4 getEyeProjection(int eye); |
| 105 | //Uniforms | 54 | static glm::mat4 getEyePose(int eye); |
| 106 | static int _textureLocation; | 55 | static glm::mat4 getHeadPose(); |
| 107 | static int _eyeToSourceUVScaleLocation; | ||
| 108 | static int _eyeToSourceUVOffsetLocation; | ||
| 109 | static int _eyeRotationStartLocation; | ||
| 110 | static int _eyeRotationEndLocation; | ||
| 111 | //Attributes | ||
| 112 | static int _positionAttributeLocation; | ||
| 113 | static int _colorAttributeLocation; | ||
| 114 | static int _texCoord0AttributeLocation; | ||
| 115 | static int _texCoord1AttributeLocation; | ||
| 116 | static int _texCoord2AttributeLocation; | ||
| 117 | static ovrVector2f _UVScaleOffset[ovrEye_Count][2]; | ||
| 118 | static GLuint _vertices[ovrEye_Count]; | ||
| 119 | static GLuint _indices[ovrEye_Count]; | ||
| 120 | static GLsizei _meshSize[ovrEye_Count]; | ||
| 121 | static ovrFrameTiming _hmdFrameTiming; | ||
| 122 | static bool _programInitialized; | ||
| 123 | #endif | ||
| 124 | |||
| 125 | static ovrTexture _eyeTextures[ovrEye_Count]; | ||
| 126 | static bool _isConnected; | ||
| 127 | static glm::vec3 _eyePositions[ovrEye_Count]; | ||
| 128 | static ovrHmd _ovrHmd; | ||
| 129 | static ovrFovPort _eyeFov[ovrEye_Count]; | ||
| 130 | static ovrVector3f _eyeOffset[ovrEye_Count]; | ||
| 131 | static glm::mat4 _eyeProjection[ovrEye_Count]; | ||
| 132 | static ovrEyeRenderDesc _eyeRenderDesc[ovrEye_Count]; | ||
| 133 | static ovrSizei _renderTargetSize; | ||
| 134 | static unsigned int _frameIndex; | ||
| 135 | static bool _frameTimingActive; | ||
| 136 | static Camera* _camera; | ||
| 137 | static ovrEyeType _activeEye; | ||
| 138 | static bool _hswDismissed; | ||
| 139 | |||
| 140 | static void calibrate(const glm::vec3 position, const glm::quat orientation); | ||
| 141 | enum CalibrationState { | ||
| 142 | UNCALIBRATED, | ||
| 143 | WAITING_FOR_DELTA, | ||
| 144 | WAITING_FOR_ZERO, | ||
| 145 | WAITING_FOR_ZERO_HELD, | ||
| 146 | CALIBRATED | ||
| 147 | }; | ||
| 148 | static void positionCalibrationBillboard(Text3DOverlay* message); | ||
| 149 | static float CALIBRATION_DELTA_MINIMUM_LENGTH; | ||
| 150 | static float CALIBRATION_DELTA_MINIMUM_ANGLE; | ||
| 151 | static float CALIBRATION_ZERO_MAXIMUM_LENGTH; | ||
| 152 | static float CALIBRATION_ZERO_MAXIMUM_ANGLE; | ||
| 153 | static quint64 CALIBRATION_ZERO_HOLD_TIME; | ||
| 154 | static float CALIBRATION_MESSAGE_DISTANCE; | ||
| 155 | static CalibrationState _calibrationState; | ||
| 156 | static glm::vec3 _calibrationPosition; | ||
| 157 | static glm::quat _calibrationOrientation; | ||
| 158 | static quint64 _calibrationStartTime; | ||
| 159 | static int _calibrationMessage; | ||
| 160 | // TODO drop this variable and use the existing 'Developer | Render | Scale Resolution' value | ||
| 161 | static ovrSizei _recommendedTexSize; | ||
| 162 | static float _offscreenRenderScale; | ||
| 163 | static bool _eyePerFrameMode; | ||
| 164 | static ovrEyeType _lastEyeRendered; | ||
| 165 | }; | 56 | }; |
| 166 | 57 | ||
| 167 | |||
| 168 | inline glm::mat4 toGlm(const ovrMatrix4f & om) { | ||
| 169 | return glm::transpose(glm::make_mat4(&om.M[0][0])); | ||
| 170 | } | ||
| 171 | |||
| 172 | inline glm::mat4 toGlm(const ovrFovPort & fovport, float nearPlane = 0.01f, float farPlane = 10000.0f) { | ||
| 173 | return toGlm(ovrMatrix4f_Projection(fovport, nearPlane, farPlane, true)); | ||
| 174 | } | ||
| 175 | |||
| 176 | inline glm::vec3 toGlm(const ovrVector3f & ov) { | ||
| 177 | return glm::make_vec3(&ov.x); | ||
| 178 | } | ||
| 179 | |||
| 180 | inline glm::vec2 toGlm(const ovrVector2f & ov) { | ||
| 181 | return glm::make_vec2(&ov.x); | ||
| 182 | } | ||
| 183 | |||
| 184 | inline glm::uvec2 toGlm(const ovrSizei & ov) { | ||
| 185 | return glm::uvec2(ov.w, ov.h); | ||
| 186 | } | ||
| 187 | |||
| 188 | inline glm::quat toGlm(const ovrQuatf & oq) { | ||
| 189 | return glm::make_quat(&oq.x); | ||
| 190 | } | ||
| 191 | |||
| 192 | inline glm::mat4 toGlm(const ovrPosef & op) { | ||
| 193 | glm::mat4 orientation = glm::mat4_cast(toGlm(op.Orientation)); | ||
| 194 | glm::mat4 translation = glm::translate(glm::mat4(), toGlm(op.Position)); | ||
| 195 | return translation * orientation; | ||
| 196 | } | ||
| 197 | |||
| 198 | inline ovrMatrix4f ovrFromGlm(const glm::mat4 & m) { | ||
| 199 | ovrMatrix4f result; | ||
| 200 | glm::mat4 transposed(glm::transpose(m)); | ||
| 201 | memcpy(result.M, &(transposed[0][0]), sizeof(float) * 16); | ||
| 202 | return result; | ||
| 203 | } | ||
| 204 | |||
| 205 | inline ovrVector3f ovrFromGlm(const glm::vec3 & v) { | ||
| 206 | return{ v.x, v.y, v.z }; | ||
| 207 | } | ||
| 208 | |||
| 209 | inline ovrVector2f ovrFromGlm(const glm::vec2 & v) { | ||
| 210 | return{ v.x, v.y }; | ||
| 211 | } | ||
| 212 | |||
| 213 | inline ovrSizei ovrFromGlm(const glm::uvec2 & v) { | ||
| 214 | return{ (int)v.x, (int)v.y }; | ||
| 215 | } | ||
| 216 | |||
| 217 | inline ovrQuatf ovrFromGlm(const glm::quat & q) { | ||
| 218 | return{ q.x, q.y, q.z, q.w }; | ||
| 219 | } | ||
| 220 | |||
| 221 | #endif // hifi_OculusManager_h | 58 | #endif // hifi_OculusManager_h |
| @@ -74,9 +74,11 @@ SDL2Manager* SDL2Manager::getInstance() { · 73 unmodified lines | |||
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | void SDL2Manager::focusOutEvent() { | 76 | void SDL2Manager::focusOutEvent() { |
| 77 | #ifdef HAVE_SDL2 | ||
| 77 | for (auto joystick : _openJoysticks) { | 78 | for (auto joystick : _openJoysticks) { |
| 78 | joystick->focusOutEvent(); | 79 | joystick->focusOutEvent(); |
| 79 | } | 80 | } |
| 81 | #endif | ||
| 80 | } | 82 | } |
| 81 | 83 | ||
| 82 | void SDL2Manager::update() { | 84 | void SDL2Manager::update() { |
| @@ -36,6 +36,8 @@ const float NECK_X = 0.25f; // meters · 35 unmodified lines | |||
| 36 | const float NECK_Y = 0.3f; // meters | 36 | const float NECK_Y = 0.3f; // meters |
| 37 | const float NECK_Z = 0.3f; // meters | 37 | const float NECK_Z = 0.3f; // meters |
| 38 | 38 | ||
| 39 | const float CONTROLLER_THRESHOLD = 0.35f; | ||
| 40 | |||
| 39 | #ifdef __APPLE__ | 41 | #ifdef __APPLE__ |
| 40 | typedef int (*SixenseBaseFunction)(); | 42 | typedef int (*SixenseBaseFunction)(); |
| 41 | typedef int (*SixenseTakeIntFunction)(int); | 43 | typedef int (*SixenseTakeIntFunction)(int); |
| @@ -71,25 +73,25 @@ SixenseManager::SixenseManager() : · 29 unmodified lines | |||
| 71 | 73 | ||
| 72 | SixenseManager::~SixenseManager() { | 74 | SixenseManager::~SixenseManager() { |
| 73 | #ifdef HAVE_SIXENSE_ | 75 | #ifdef HAVE_SIXENSE_ |
| 74 | 76 | ||
| 75 | if (_isInitialized) { | 77 | if (_isInitialized) { |
| 76 | #ifdef __APPLE__ | 78 | #ifdef __APPLE__ |
| 77 | SixenseBaseFunction sixenseExit = (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseExit"); | 79 | SixenseBaseFunction sixenseExit = (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseExit"); |
| 78 | #endif | 80 | #endif |
| 79 | 81 | ||
| 80 | sixenseExit(); | 82 | sixenseExit(); |
| 81 | } | 83 | } |
| 82 | 84 | ||
| 83 | #ifdef __APPLE__ | 85 | #ifdef __APPLE__ |
| 84 | delete _sixenseLibrary; | 86 | delete _sixenseLibrary; |
| 85 | #endif | 87 | #endif |
| 86 | 88 | ||
| 87 | #endif | 89 | #endif |
| 88 | } | 90 | } |
| 89 | 91 | ||
| 90 | void SixenseManager::initialize() { | 92 | void SixenseManager::initialize() { |
| 91 | #ifdef HAVE_SIXENSE | 93 | #ifdef HAVE_SIXENSE |
| 92 | 94 | ||
| 93 | if (!_isInitialized) { | 95 | if (!_isInitialized) { |
| 94 | _lowVelocityFilter = false; | 96 | _lowVelocityFilter = false; |
| 95 | _controllersAtBase = true; | 97 | _controllersAtBase = true; |
| @@ -97,22 +99,22 @@ void SixenseManager::initialize() { · 1 unmodified line | |||
| 97 | // By default we assume the _neckBase (in orb frame) is as high above the orb | 99 | // By default we assume the _neckBase (in orb frame) is as high above the orb |
| 98 | // as the "torso" is below it. | 100 | // as the "torso" is below it. |
| 99 | _neckBase = glm::vec3(NECK_X, -NECK_Y, NECK_Z); | 101 | _neckBase = glm::vec3(NECK_X, -NECK_Y, NECK_Z); |
| 100 | 102 | ||
| 101 | #ifdef __APPLE__ | 103 | #ifdef __APPLE__ |
| 102 | 104 | ||
| 103 | if (!_sixenseLibrary) { | 105 | if (!_sixenseLibrary) { |
| 104 | 106 | ||
| 105 | #ifdef SIXENSE_LIB_FILENAME | 107 | #ifdef SIXENSE_LIB_FILENAME |
| 106 | _sixenseLibrary = new QLibrary(SIXENSE_LIB_FILENAME); | 108 | _sixenseLibrary = new QLibrary(SIXENSE_LIB_FILENAME); |
| 107 | #else | 109 | #else |
| 108 | const QString SIXENSE_LIBRARY_NAME = "libsixense_x64"; | 110 | const QString SIXENSE_LIBRARY_NAME = "libsixense_x64"; |
| 109 | QString frameworkSixenseLibrary = QCoreApplication::applicationDirPath() + "/../Frameworks/" | 111 | QString frameworkSixenseLibrary = QCoreApplication::applicationDirPath() + "/../Frameworks/" |
| 110 | + SIXENSE_LIBRARY_NAME; | 112 | + SIXENSE_LIBRARY_NAME; |
| 111 | 113 | ||
| 112 | _sixenseLibrary = new QLibrary(frameworkSixenseLibrary); | 114 | _sixenseLibrary = new QLibrary(frameworkSixenseLibrary); |
| 113 | #endif | 115 | #endif |
| 114 | } | 116 | } |
| 115 | 117 | ||
| 116 | if (_sixenseLibrary->load()){ | 118 | if (_sixenseLibrary->load()){ |
| 117 | qCDebug(interfaceapp) << "Loaded sixense library for hydra support -" << _sixenseLibrary->fileName(); | 119 | qCDebug(interfaceapp) << "Loaded sixense library for hydra support -" << _sixenseLibrary->fileName(); |
| 118 | } else { | 120 | } else { |
| @@ -120,14 +122,14 @@ void SixenseManager::initialize() { · 1 unmodified line | |||
| 120 | << "Continuing without hydra support."; | 122 | << "Continuing without hydra support."; |
| 121 | return; | 123 | return; |
| 122 | } | 124 | } |
| 123 | 125 | ||
| 124 | SixenseBaseFunction sixenseInit = (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseInit"); | 126 | SixenseBaseFunction sixenseInit = (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseInit"); |
| 125 | #endif | 127 | #endif |
| 126 | sixenseInit(); | 128 | sixenseInit(); |
| 127 | 129 | ||
| 128 | _isInitialized = true; | 130 | _isInitialized = true; |
| 129 | } | 131 | } |
| 130 | 132 | ||
| 131 | #endif | 133 | #endif |
| 132 | } | 134 | } |
| 133 | 135 | ||
| @@ -138,7 +140,7 @@ void SixenseManager::setFilter(bool filter) { · 4 unmodified lines | |||
| 138 | #ifdef __APPLE__ | 140 | #ifdef __APPLE__ |
| 139 | SixenseTakeIntFunction sixenseSetFilterEnabled = (SixenseTakeIntFunction) _sixenseLibrary->resolve("sixenseSetFilterEnabled"); | 141 | SixenseTakeIntFunction sixenseSetFilterEnabled = (SixenseTakeIntFunction) _sixenseLibrary->resolve("sixenseSetFilterEnabled"); |
| 140 | #endif | 142 | #endif |
| 141 | 143 | ||
| 142 | if (filter) { | 144 | if (filter) { |
| 143 | sixenseSetFilterEnabled(1); | 145 | sixenseSetFilterEnabled(1); |
| 144 | } else { | 146 | } else { |
| @@ -158,7 +160,7 @@ void SixenseManager::update(float deltaTime) { · 13 unmodified lines | |||
| 158 | SixenseBaseFunction sixenseGetNumActiveControllers = | 160 | SixenseBaseFunction sixenseGetNumActiveControllers = |
| 159 | (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers"); | 161 | (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers"); |
| 160 | #endif | 162 | #endif |
| 161 | 163 | ||
| 162 | if (sixenseGetNumActiveControllers() == 0) { | 164 | if (sixenseGetNumActiveControllers() == 0) { |
| 163 | _hydrasConnected = false; | 165 | _hydrasConnected = false; |
| 164 | if (_deviceID != 0) { | 166 | if (_deviceID != 0) { |
| @@ -173,7 +175,7 @@ void SixenseManager::update(float deltaTime) { · 8 unmodified lines | |||
| 173 | } | 175 | } |
| 174 | return; | 176 | return; |
| 175 | } | 177 | } |
| 176 | 178 | ||
| 177 | PerformanceTimer perfTimer("sixense"); | 179 | PerformanceTimer perfTimer("sixense"); |
| 178 | if (!_hydrasConnected) { | 180 | if (!_hydrasConnected) { |
| 179 | _hydrasConnected = true; | 181 | _hydrasConnected = true; |
| @@ -181,21 +183,21 @@ void SixenseManager::update(float deltaTime) { · 1 unmodified line | |||
| 181 | getInstance().assignDefaultInputMapping(*Application::getUserInputMapper()); | 183 | getInstance().assignDefaultInputMapping(*Application::getUserInputMapper()); |
| 182 | UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra"); | 184 | UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra"); |
| 183 | } | 185 | } |
| 184 | 186 | ||
| 185 | #ifdef __APPLE__ | 187 | #ifdef __APPLE__ |
| 186 | SixenseBaseFunction sixenseGetMaxControllers = | 188 | SixenseBaseFunction sixenseGetMaxControllers = |
| 187 | (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetMaxControllers"); | 189 | (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetMaxControllers"); |
| 188 | #endif | 190 | #endif |
| 189 | 191 | ||
| 190 | int maxControllers = sixenseGetMaxControllers(); | 192 | int maxControllers = sixenseGetMaxControllers(); |
| 191 | 193 | ||
| 192 | // we only support two controllers | 194 | // we only support two controllers |
| 193 | sixenseControllerData controllers[2]; | 195 | sixenseControllerData controllers[2]; |
| 194 | 196 | ||
| 195 | #ifdef __APPLE__ | 197 | #ifdef __APPLE__ |
| 196 | SixenseTakeIntFunction sixenseIsControllerEnabled = | 198 | SixenseTakeIntFunction sixenseIsControllerEnabled = |
| 197 | (SixenseTakeIntFunction) _sixenseLibrary->resolve("sixenseIsControllerEnabled"); | 199 | (SixenseTakeIntFunction) _sixenseLibrary->resolve("sixenseIsControllerEnabled"); |
| 198 | 200 | ||
| 199 | SixenseTakeIntAndSixenseControllerData sixenseGetNewestData = | 201 | SixenseTakeIntAndSixenseControllerData sixenseGetNewestData = |
| 200 | (SixenseTakeIntAndSixenseControllerData) _sixenseLibrary->resolve("sixenseGetNewestData"); | 202 | (SixenseTakeIntAndSixenseControllerData) _sixenseLibrary->resolve("sixenseGetNewestData"); |
| 201 | #endif | 203 | #endif |
| @@ -208,9 +210,9 @@ void SixenseManager::update(float deltaTime) { · 6 unmodified lines | |||
| 208 | sixenseControllerData* data = controllers + numActiveControllers; | 210 | sixenseControllerData* data = controllers + numActiveControllers; |
| 209 | ++numActiveControllers; | 211 | ++numActiveControllers; |
| 210 | sixenseGetNewestData(i, data); | 212 | sixenseGetNewestData(i, data); |
| 211 | 213 | ||
| 212 | // Set palm position and normal based on Hydra position/orientation | 214 | // Set palm position and normal based on Hydra position/orientation |
| 213 | 215 | ||
| 214 | // Either find a palm matching the sixense controller, or make a new one | 216 | // Either find a palm matching the sixense controller, or make a new one |
| 215 | PalmData* palm; | 217 | PalmData* palm; |
| 216 | bool foundHand = false; | 218 | bool foundHand = false; |
| @@ -229,47 +231,47 @@ void SixenseManager::update(float deltaTime) { · 12 unmodified lines | |||
| 229 | _prevPalms[numActiveControllers - 1] = palm; | 231 | _prevPalms[numActiveControllers - 1] = palm; |
| 230 | qCDebug(interfaceapp, "Found new Sixense controller, ID %i", data->controller_index); | 232 | qCDebug(interfaceapp, "Found new Sixense controller, ID %i", data->controller_index); |
| 231 | } | 233 | } |
| 232 | 234 | ||
| 233 | // Disable the hands (and return to default pose) if both controllers are at base station | 235 | // Disable the hands (and return to default pose) if both controllers are at base station |
| 234 | if (foundHand) { | 236 | if (foundHand) { |
| 235 | palm->setActive(!_controllersAtBase); | 237 | palm->setActive(!_controllersAtBase); |
| 236 | } else { | 238 | } else { |
| 237 | palm->setActive(false); // if this isn't a Sixsense ID palm, always make it inactive | 239 | palm->setActive(false); // if this isn't a Sixsense ID palm, always make it inactive |
| 238 | } | 240 | } |
| 239 | 241 | ||
| 240 | // Read controller buttons and joystick into the hand | 242 | // Read controller buttons and joystick into the hand |
| 241 | palm->setControllerButtons(data->buttons); | 243 | palm->setControllerButtons(data->buttons); |
| 242 | palm->setTrigger(data->trigger); | 244 | palm->setTrigger(data->trigger); |
| 243 | palm->setJoystick(data->joystick_x, data->joystick_y); | 245 | palm->setJoystick(data->joystick_x, data->joystick_y); |
| 244 | 246 | ||
| 245 | handleButtonEvent(data->buttons, numActiveControllers - 1); | 247 | handleButtonEvent(data->buttons, numActiveControllers - 1); |
| 246 | handleAxisEvent(data->joystick_x, data->joystick_y, data->trigger, numActiveControllers - 1); | 248 | handleAxisEvent(data->joystick_x, data->joystick_y, data->trigger, numActiveControllers - 1); |
| 247 | 249 | ||
| 248 | // Emulate the mouse so we can use scripts | 250 | // Emulate the mouse so we can use scripts |
| 249 | if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput) && !_controllersAtBase) { | 251 | if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput) && !_controllersAtBase) { |
| 250 | emulateMouse(palm, numActiveControllers - 1); | 252 | emulateMouse(palm, numActiveControllers - 1); |
| 251 | } | 253 | } |
| 252 | 254 | ||
| 253 | // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters. | 255 | // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters. |
| 254 | glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]); | 256 | glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]); |
| 255 | position *= METERS_PER_MILLIMETER; | 257 | position *= METERS_PER_MILLIMETER; |
| 256 | 258 | ||
| 257 | // Check to see if this hand/controller is on the base | 259 | // Check to see if this hand/controller is on the base |
| 258 | const float CONTROLLER_AT_BASE_DISTANCE = 0.075f; | 260 | const float CONTROLLER_AT_BASE_DISTANCE = 0.075f; |
| 259 | if (glm::length(position) < CONTROLLER_AT_BASE_DISTANCE) { | 261 | if (glm::length(position) < CONTROLLER_AT_BASE_DISTANCE) { |
| 260 | numControllersAtBase++; | 262 | numControllersAtBase++; |
| 261 | } | 263 | } |
| 262 | 264 | ||
| 263 | // Transform the measured position into body frame. | 265 | // Transform the measured position into body frame. |
| 264 | glm::vec3 neck = _neckBase; | 266 | glm::vec3 neck = _neckBase; |
| 265 | // Zeroing y component of the "neck" effectively raises the measured position a little bit. | 267 | // Zeroing y component of the "neck" effectively raises the measured position a little bit. |
| 266 | neck.y = 0.0f; | 268 | neck.y = 0.0f; |
| 267 | position = _orbRotation * (position - neck); | 269 | position = _orbRotation * (position - neck); |
| 268 | 270 | ||
| 269 | // Rotation of Palm | 271 | // Rotation of Palm |
| 270 | glm::quat rotation(data->rot_quat[3], -data->rot_quat[0], data->rot_quat[1], -data->rot_quat[2]); | 272 | glm::quat rotation(data->rot_quat[3], -data->rot_quat[0], data->rot_quat[1], -data->rot_quat[2]); |
| 271 | rotation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)) * _orbRotation * rotation; | 273 | rotation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)) * _orbRotation * rotation; |
| 272 | 274 | ||
| 273 | // Compute current velocity from position change | 275 | // Compute current velocity from position change |
| 274 | glm::vec3 rawVelocity; | 276 | glm::vec3 rawVelocity; |
| 275 | if (deltaTime > 0.0f) { | 277 | if (deltaTime > 0.0f) { |
| @@ -278,11 +280,11 @@ void SixenseManager::update(float deltaTime) { · 2 unmodified lines | |||
| 278 | rawVelocity = glm::vec3(0.0f); | 280 | rawVelocity = glm::vec3(0.0f); |
| 279 | } | 281 | } |
| 280 | palm->setRawVelocity(rawVelocity); // meters/sec | 282 | palm->setRawVelocity(rawVelocity); // meters/sec |
| 281 | 283 | ||
| 282 | // adjustment for hydra controllers fit into hands | 284 | // adjustment for hydra controllers fit into hands |
| 283 | float sign = (i == 0) ? -1.0f : 1.0f; | 285 | float sign = (i == 0) ? -1.0f : 1.0f; |
| 284 | rotation *= glm::angleAxis(sign * PI/4.0f, glm::vec3(0.0f, 0.0f, 1.0f)); | 286 | rotation *= glm::angleAxis(sign * PI/4.0f, glm::vec3(0.0f, 0.0f, 1.0f)); |
| 285 | 287 | ||
| 286 | // Angular Velocity of Palm | 288 | // Angular Velocity of Palm |
| 287 | glm::quat deltaRotation = rotation * glm::inverse(palm->getRawRotation()); | 289 | glm::quat deltaRotation = rotation * glm::inverse(palm->getRawRotation()); |
| 288 | glm::vec3 angularVelocity(0.0f); | 290 | glm::vec3 angularVelocity(0.0f); |
| @@ -294,7 +296,7 @@ void SixenseManager::update(float deltaTime) { · 5 unmodified lines | |||
| 294 | } else { | 296 | } else { |
| 295 | palm->setRawAngularVelocity(glm::vec3(0.0f)); | 297 | palm->setRawAngularVelocity(glm::vec3(0.0f)); |
| 296 | } | 298 | } |
| 297 | 299 | ||
| 298 | if (_lowVelocityFilter) { | 300 | if (_lowVelocityFilter) { |
| 299 | // Use a velocity sensitive filter to damp small motions and preserve large ones with | 301 | // Use a velocity sensitive filter to damp small motions and preserve large ones with |
| 300 | // no latency. | 302 | // no latency. |
| @@ -307,7 +309,7 @@ void SixenseManager::update(float deltaTime) { · 6 unmodified lines | |||
| 307 | palm->setRawPosition(position); | 309 | palm->setRawPosition(position); |
| 308 | palm->setRawRotation(rotation); | 310 | palm->setRawRotation(rotation); |
| 309 | } | 311 | } |
| 310 | 312 | ||
| 311 | // Store the one fingertip in the palm structure so we can track velocity | 313 | // Store the one fingertip in the palm structure so we can track velocity |
| 312 | const float FINGER_LENGTH = 0.3f; // meters | 314 | const float FINGER_LENGTH = 0.3f; // meters |
| 313 | const glm::vec3 FINGER_VECTOR(0.0f, 0.0f, FINGER_LENGTH); | 315 | const glm::vec3 FINGER_VECTOR(0.0f, 0.0f, FINGER_LENGTH); |
| @@ -320,19 +322,25 @@ void SixenseManager::update(float deltaTime) { · 6 unmodified lines | |||
| 320 | } | 322 | } |
| 321 | palm->setTipPosition(newTipPosition); | 323 | palm->setTipPosition(newTipPosition); |
| 322 | } | 324 | } |
| 323 | 325 | ||
| 324 | if (numActiveControllers == 2) { | 326 | if (numActiveControllers == 2) { |
| 325 | updateCalibration(controllers); | 327 | updateCalibration(controllers); |
| 326 | } | 328 | } |
| 327 | _controllersAtBase = (numControllersAtBase == 2); | 329 | _controllersAtBase = (numControllersAtBase == 2); |
| 328 | } | 330 | } |
| 331 | |||
| 332 | for (auto axisState : _axisStateMap) { | ||
| 333 | if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) { | ||
| 334 | _axisStateMap[axisState.first] = 0.0f; | ||
| 335 | } | ||
| 336 | } | ||
| 329 | #endif // HAVE_SIXENSE | 337 | #endif // HAVE_SIXENSE |
| 330 | } | 338 | } |
| 331 | 339 | ||
| 332 | //Constants for getCursorPixelRangeMultiplier() | 340 | //Constants for getCursorPixelRangeMultiplier() |
| 333 | const float MIN_PIXEL_RANGE_MULT = 0.4f; | 341 | const float MIN_PIXEL_RANGE_MULT = 0.4f; |
| 334 | const float MAX_PIXEL_RANGE_MULT = 2.0f; | 342 | const float MAX_PIXEL_RANGE_MULT = 2.0f; |
| 335 | const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01; | 343 | const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01f; |
| 336 | 344 | ||
| 337 | //Returns a multiplier to be applied to the cursor range for the controllers | 345 | //Returns a multiplier to be applied to the cursor range for the controllers |
| 338 | float SixenseManager::getCursorPixelRangeMult() const { | 346 | float SixenseManager::getCursorPixelRangeMult() const { |
| @@ -421,7 +429,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) · 82 unmodified lines | |||
| 421 | _calibrationState = CALIBRATION_STATE_X; | 429 | _calibrationState = CALIBRATION_STATE_X; |
| 422 | } | 430 | } |
| 423 | return; | 431 | return; |
| 424 | } | 432 | } |
| 425 | 433 | ||
| 426 | quint64 now = usecTimestampNow() + LOCK_DURATION; | 434 | quint64 now = usecTimestampNow() + LOCK_DURATION; |
| 427 | // these are weighted running averages | 435 | // these are weighted running averages |
| @@ -431,7 +439,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) · 3 unmodified lines | |||
| 431 | if (_calibrationState == CALIBRATION_STATE_X) { | 439 | if (_calibrationState == CALIBRATION_STATE_X) { |
| 432 | // compute new sliding average | 440 | // compute new sliding average |
| 433 | float distance = glm::distance(_averageLeft, _averageRight); | 441 | float distance = glm::distance(_averageLeft, _averageRight); |
| 434 | if (fabs(distance - _lastDistance) > MAXIMUM_NOISE_LEVEL) { | 442 | if (fabsf(distance - _lastDistance) > MAXIMUM_NOISE_LEVEL) { |
| 435 | // distance is increasing so acquire the data and push the expiry out | 443 | // distance is increasing so acquire the data and push the expiry out |
| 436 | _reachLeft = _averageLeft; | 444 | _reachLeft = _averageLeft; |
| 437 | _reachRight = _averageRight; | 445 | _reachRight = _averageRight; |
| @@ -450,7 +458,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) · 12 unmodified lines | |||
| 450 | glm::vec3 torso = 0.5f * (_reachLeft + _reachRight); | 458 | glm::vec3 torso = 0.5f * (_reachLeft + _reachRight); |
| 451 | glm::vec3 averagePosition = 0.5f * (_averageLeft + _averageRight); | 459 | glm::vec3 averagePosition = 0.5f * (_averageLeft + _averageRight); |
| 452 | float distance = (averagePosition - torso).y; | 460 | float distance = (averagePosition - torso).y; |
| 453 | if (fabs(distance) > fabs(_lastDistance) + MAXIMUM_NOISE_LEVEL) { | 461 | if (fabsf(distance) > fabsf(_lastDistance) + MAXIMUM_NOISE_LEVEL) { |
| 454 | // distance is increasing so acquire the data and push the expiry out | 462 | // distance is increasing so acquire the data and push the expiry out |
| 455 | _reachUp = averagePosition; | 463 | _reachUp = averagePosition; |
| 456 | _lastDistance = distance; | 464 | _lastDistance = distance; |
| @@ -481,11 +489,11 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) · 24 unmodified lines | |||
| 481 | _lastDistance = distance; | 489 | _lastDistance = distance; |
| 482 | _lockExpiry = now + LOCK_DURATION; | 490 | _lockExpiry = now + LOCK_DURATION; |
| 483 | } else if (now > _lockExpiry) { | 491 | } else if (now > _lockExpiry) { |
| 484 | if (fabs(_lastDistance) > 0.05f * MINIMUM_ARM_REACH) { | 492 | if (fabsf(_lastDistance) > 0.05f * MINIMUM_ARM_REACH) { |
| 485 | // lock has expired so clamp the data and move on | 493 | // lock has expired so clamp the data and move on |
| 486 | _calibrationState = CALIBRATION_STATE_COMPLETE; | 494 | _calibrationState = CALIBRATION_STATE_COMPLETE; |
| 487 | qCDebug(interfaceapp, "success: sixense calibration: forward"); | 495 | qCDebug(interfaceapp, "success: sixense calibration: forward"); |
| 488 | // TODO: it is theoretically possible to detect that the controllers have been | 496 | // TODO: it is theoretically possible to detect that the controllers have been |
| 489 | // accidentally switched (left hand is holding right controller) and to swap the order. | 497 | // accidentally switched (left hand is holding right controller) and to swap the order. |
| 490 | } | 498 | } |
| 491 | } | 499 | } |
| @@ -496,7 +504,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) · 4 unmodified lines | |||
| 496 | void SixenseManager::emulateMouse(PalmData* palm, int index) { | 504 | void SixenseManager::emulateMouse(PalmData* palm, int index) { |
| 497 | MyAvatar* avatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | 505 | MyAvatar* avatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); |
| 498 | QPoint pos; | 506 | QPoint pos; |
| 499 | 507 | ||
| 500 | Qt::MouseButton bumperButton; | 508 | Qt::MouseButton bumperButton; |
| 501 | Qt::MouseButton triggerButton; | 509 | Qt::MouseButton triggerButton; |
| 502 | 510 | ||
| @@ -510,16 +518,16 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { · 7 unmodified lines | |||
| 510 | triggerButton = Qt::LeftButton; | 518 | triggerButton = Qt::LeftButton; |
| 511 | } | 519 | } |
| 512 | 520 | ||
| 513 | if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers) | 521 | if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers) |
| 514 | || Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) { | 522 | || Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) { |
| 515 | pos = qApp->getApplicationOverlay().getPalmClickLocation(palm); | 523 | pos = qApp->getApplicationCompositor().getPalmClickLocation(palm); |
| 516 | } else { | 524 | } else { |
| 517 | // Get directon relative to avatar orientation | 525 | // Get directon relative to avatar orientation |
| 518 | glm::vec3 direction = glm::inverse(avatar->getOrientation()) * palm->getFingerDirection(); | 526 | glm::vec3 direction = glm::inverse(avatar->getOrientation()) * palm->getFingerDirection(); |
| 519 | 527 | ||
| 520 | // Get the angles, scaled between (-0.5,0.5) | 528 | // Get the angles, scaled between (-0.5,0.5) |
| 521 | float xAngle = (atan2(direction.z, direction.x) + M_PI_2); | 529 | float xAngle = (atan2(direction.z, direction.x) + M_PI_2); |
| 522 | float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); | 530 | float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)M_PI_2)); |
| 523 | auto canvasSize = qApp->getCanvasSize(); | 531 | auto canvasSize = qApp->getCanvasSize(); |
| 524 | // Get the pixel range over which the xAngle and yAngle are scaled | 532 | // Get the pixel range over which the xAngle and yAngle are scaled |
| 525 | float cursorRange = canvasSize.x * getCursorPixelRangeMult(); | 533 | float cursorRange = canvasSize.x * getCursorPixelRangeMult(); |
| @@ -562,11 +570,11 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { · 36 unmodified lines | |||
| 562 | if (!_bumperPressed[(int)(!index)]) { | 570 | if (!_bumperPressed[(int)(!index)]) { |
| 563 | qApp->mouseMoveEvent(&mouseEvent, deviceID); | 571 | qApp->mouseMoveEvent(&mouseEvent, deviceID); |
| 564 | } | 572 | } |
| 565 | } | 573 | } |
| 566 | } | 574 | } |
| 567 | _oldX[index] = pos.x(); | 575 | _oldX[index] = pos.x(); |
| 568 | _oldY[index] = pos.y(); | 576 | _oldY[index] = pos.y(); |
| 569 | 577 | ||
| 570 | 578 | ||
| 571 | //We need separate coordinates for clicks, since we need to check if | 579 | //We need separate coordinates for clicks, since we need to check if |
| 572 | //a magnification window was clicked on | 580 | //a magnification window was clicked on |
| @@ -580,7 +588,7 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { · 7 unmodified lines | |||
| 580 | if (palm->getControllerButtons() & BUTTON_FWD) { | 588 | if (palm->getControllerButtons() & BUTTON_FWD) { |
| 581 | if (!_bumperPressed[index]) { | 589 | if (!_bumperPressed[index]) { |
| 582 | _bumperPressed[index] = true; | 590 | _bumperPressed[index] = true; |
| 583 | 591 | ||
| 584 | QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, bumperButton, bumperButton, 0); | 592 | QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, bumperButton, bumperButton, 0); |
| 585 | 593 | ||
| 586 | qApp->mousePressEvent(&mouseEvent, deviceID); | 594 | qApp->mousePressEvent(&mouseEvent, deviceID); |
| @@ -12,6 +12,7 @@ · 11 unmodified lines | |||
| 12 | #include "InterfaceConfig.h" | 12 | #include "InterfaceConfig.h" |
| 13 | 13 | ||
| 14 | #include <glm/glm.hpp> | 14 | #include <glm/glm.hpp> |
| 15 | #include <glm/gtc/type_ptr.hpp> | ||
| 15 | 16 | ||
| 16 | #include <GlowEffect.h> | 17 | #include <GlowEffect.h> |
| 17 | #include "gpu/GLBackend.h" | 18 | #include "gpu/GLBackend.h" |
| @@ -106,21 +107,20 @@ void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) { · 88 unmodified lines | |||
| 106 | _activeEye = &eye; | 107 | _activeEye = &eye; |
| 107 | glViewport(portalX, portalY, portalW, portalH); | 108 | glViewport(portalX, portalY, portalW, portalH); |
| 108 | glScissor(portalX, portalY, portalW, portalH); | 109 | glScissor(portalX, portalY, portalW, portalH); |
| 110 | |||
| 111 | glm::mat4 projection = glm::frustum<float>(eye.left, eye.right, eye.bottom, eye.top, nearZ, farZ); | ||
| 112 | float fov = atan(1.0f / projection[1][1]); | ||
| 113 | projection = glm::translate(projection, vec3(eye.modelTranslation, 0, 0)); | ||
| 114 | eyeCamera.setProjection(projection); | ||
| 115 | |||
| 109 | glMatrixMode(GL_PROJECTION); | 116 | glMatrixMode(GL_PROJECTION); |
| 110 | glLoadIdentity(); // reset projection matrix | 117 | glLoadIdentity(); // reset projection matrix |
| 111 | glFrustum(eye.left, eye.right, eye.bottom, eye.top, nearZ, farZ); // set left view frustum | 118 | glLoadMatrixf(glm::value_ptr(projection)); |
| 112 | GLfloat p[4][4]; | ||
| 113 | // Really? | ||
| 114 | glGetFloatv(GL_PROJECTION_MATRIX, &(p[0][0])); | ||
| 115 | float cotangent = p[1][1]; | ||
| 116 | GLfloat fov = atan(1.0f / cotangent); | ||
| 117 | glTranslatef(eye.modelTranslation, 0.0, 0.0); // translate to cancel parallax | ||
| 118 | |||
| 119 | glMatrixMode(GL_MODELVIEW); | 119 | glMatrixMode(GL_MODELVIEW); |
| 120 | glLoadIdentity(); | 120 | glLoadIdentity(); |
| 121 | renderArgs->_renderSide = RenderArgs::MONO; | 121 | renderArgs->_renderSide = RenderArgs::MONO; |
| 122 | qApp->displaySide(renderArgs, eyeCamera, false); | 122 | qApp->displaySide(renderArgs, eyeCamera, false); |
| 123 | qApp->getApplicationOverlay().displayOverlayTextureStereo(whichCamera, _aspect, fov); | 123 | qApp->getApplicationCompositor().displayOverlayTexture(renderArgs); |
| 124 | _activeEye = NULL; | 124 | _activeEye = NULL; |
| 125 | }, [&]{ | 125 | }, [&]{ |
| 126 | // render right side view | 126 | // render right side view |
| @@ -23,7 +23,7 @@ · 22 unmodified lines | |||
| 23 | #ifdef Q_OS_WIN | 23 | #ifdef Q_OS_WIN |
| 24 | static BOOL CALLBACK enumWindowsCallback(HWND hWnd, LPARAM lParam) { | 24 | static BOOL CALLBACK enumWindowsCallback(HWND hWnd, LPARAM lParam) { |
| 25 | const UINT TIMEOUT = 200; // ms | 25 | const UINT TIMEOUT = 200; // ms |
| 26 | DWORD response; | 26 | DWORD_PTR response; |
| 27 | LRESULT result = SendMessageTimeout(hWnd, UWM_IDENTIFY_INSTANCES, 0, 0, SMTO_BLOCK | SMTO_ABORTIFHUNG, TIMEOUT, &response); | 27 | LRESULT result = SendMessageTimeout(hWnd, UWM_IDENTIFY_INSTANCES, 0, 0, SMTO_BLOCK | SMTO_ABORTIFHUNG, TIMEOUT, &response); |
| 28 | if (result == 0) { // Timeout; continue search. | 28 | if (result == 0) { // Timeout; continue search. |
| 29 | return TRUE; | 29 | return TRUE; |
| @@ -463,7 +463,7 @@ void ControllerScriptingInterface::resetDevice(unsigned int device) { · 462 unmodified lines | |||
| 463 | } | 463 | } |
| 464 | 464 | ||
| 465 | int ControllerScriptingInterface::findDevice(QString name) { | 465 | int ControllerScriptingInterface::findDevice(QString name) { |
| 466 | Application::getUserInputMapper()->findDevice(name); | 466 | return Application::getUserInputMapper()->findDevice(name); |
| 467 | } | 467 | } |
| 468 | 468 | ||
| 469 | InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) : | 469 | InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) : |
| @@ -65,6 +65,7 @@ public: · 64 unmodified lines | |||
| 65 | 65 | ||
| 66 | void emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseMoveEvent(MouseEvent(*event, deviceID)); } | 66 | void emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseMoveEvent(MouseEvent(*event, deviceID)); } |
| 67 | void emitMousePressEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mousePressEvent(MouseEvent(*event, deviceID)); } | 67 | void emitMousePressEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mousePressEvent(MouseEvent(*event, deviceID)); } |
| 68 | void emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseDoublePressEvent(MouseEvent(*event, deviceID)); } | ||
| 68 | void emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseReleaseEvent(MouseEvent(*event, deviceID)); } | 69 | void emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseReleaseEvent(MouseEvent(*event, deviceID)); } |
| 69 | 70 | ||
| 70 | void emitTouchBeginEvent(const TouchEvent& event) { emit touchBeginEvent(event); } | 71 | void emitTouchBeginEvent(const TouchEvent& event) { emit touchBeginEvent(event); } |
| @@ -25,9 +25,9 @@ bool HMDScriptingInterface::getHUDLookAtPosition3D(glm::vec3& result) const { · 24 unmodified lines | |||
| 25 | 25 | ||
| 26 | glm::vec3 direction = orientation * glm::vec3(0.0f, 0.0f, -1.0f); | 26 | glm::vec3 direction = orientation * glm::vec3(0.0f, 0.0f, -1.0f); |
| 27 | 27 | ||
| 28 | ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay(); | 28 | const auto& compositor = Application::getInstance()->getApplicationCompositor(); |
| 29 | 29 | ||
| 30 | return applicationOverlay.calculateRayUICollisionPoint(position, direction, result); | 30 | return compositor.calculateRayUICollisionPoint(position, direction, result); |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) { | 33 | QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) { |
| @@ -40,7 +40,7 @@ QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* conte · 6 unmodified lines | |||
| 40 | glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * (hudIntersection - sphereCenter); | 40 | glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * (hudIntersection - sphereCenter); |
| 41 | glm::quat rotation = ::rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), direction); | 41 | glm::quat rotation = ::rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), direction); |
| 42 | glm::vec3 eulers = ::safeEulerAngles(rotation); | 42 | glm::vec3 eulers = ::safeEulerAngles(rotation); |
| 43 | return qScriptValueFromValue<glm::vec2>(engine, Application::getInstance()->getApplicationOverlay() | 43 | return qScriptValueFromValue<glm::vec2>(engine, Application::getInstance()->getApplicationCompositor() |
| 44 | .sphericalToOverlay(glm::vec2(eulers.y, -eulers.x))); | 44 | .sphericalToOverlay(glm::vec2(eulers.y, -eulers.x))); |
| 45 | } | 45 | } |
| 46 | return QScriptValue::NullValue; | 46 | return QScriptValue::NullValue; |
| @@ -27,11 +27,11 @@ public: · 26 unmodified lines | |||
| 27 | static QScriptValue getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine); | 27 | static QScriptValue getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine); |
| 28 | 28 | ||
| 29 | public slots: | 29 | public slots: |
| 30 | void toggleMagnifier() { Application::getInstance()->getApplicationOverlay().toggleMagnifier(); }; | 30 | void toggleMagnifier() { Application::getInstance()->getApplicationCompositor().toggleMagnifier(); }; |
| 31 | 31 | ||
| 32 | private: | 32 | private: |
| 33 | HMDScriptingInterface() {}; | 33 | HMDScriptingInterface() {}; |
| 34 | bool getMagnifier() const { return Application::getInstance()->getApplicationOverlay().hasMagnifier(); }; | 34 | bool getMagnifier() const { return Application::getInstance()->getApplicationCompositor().hasMagnifier(); }; |
| 35 | bool isHMDMode() const { return Application::getInstance()->isHMDMode(); } | 35 | bool isHMDMode() const { return Application::getInstance()->isHMDMode(); } |
| 36 | 36 | ||
| 37 | bool getHUDLookAtPosition3D(glm::vec3& result) const; | 37 | bool getHUDLookAtPosition3D(glm::vec3& result) const; |
| @@ -11,6 +11,7 @@ · 10 unmodified lines | |||
| 11 | // | 11 | // |
| 12 | 12 | ||
| 13 | #include "starfield/renderer/Renderer.h" | 13 | #include "starfield/renderer/Renderer.h" |
| 14 | #include "Application.h" | ||
| 14 | 15 | ||
| 15 | using namespace starfield; | 16 | using namespace starfield; |
| 16 | 17 | ||
| @@ -52,6 +53,10 @@ void Renderer::render(float perspective, float aspect, mat4 const& orientation, · 35 unmodified lines | |||
| 52 | matrix[3][1] = 0.0f; | 53 | matrix[3][1] = 0.0f; |
| 53 | matrix[3][2] = 0.0f; | 54 | matrix[3][2] = 0.0f; |
| 54 | 55 | ||
| 56 | glMatrixMode(GL_PROJECTION); | ||
| 57 | glPushMatrix(); | ||
| 58 | glLoadMatrixf(glm::value_ptr(qApp->getDisplayViewFrustum()->getProjection())); | ||
| 59 | glMatrixMode(GL_MODELVIEW); | ||
| 55 | // extract local z vector | 60 | // extract local z vector |
| 56 | vec3 ahead = vec3(matrix[2]); | 61 | vec3 ahead = vec3(matrix[2]); |
| 57 | 62 | ||
| @@ -74,6 +79,10 @@ void Renderer::render(float perspective, float aspect, mat4 const& orientation, · 16 unmodified lines | |||
| 74 | floodFill(cursor, TileSelection(*this, _tileArray, _tileArray + _tiling.getTileCount(), (TileSelection::Cursor*) _batchCountArray)); | 79 | floodFill(cursor, TileSelection(*this, _tileArray, _tileArray + _tiling.getTileCount(), (TileSelection::Cursor*) _batchCountArray)); |
| 75 | 80 | ||
| 76 | this->glBatch(glm::value_ptr(matrix), prepareBatch((unsigned*) _batchOffs, _outIndexPos), alpha); | 81 | this->glBatch(glm::value_ptr(matrix), prepareBatch((unsigned*) _batchOffs, _outIndexPos), alpha); |
| 82 | glMatrixMode(GL_PROJECTION); | ||
| 83 | glPopMatrix(); | ||
| 84 | glMatrixMode(GL_MODELVIEW); | ||
| 85 | |||
| 77 | } | 86 | } |
| 78 | 87 | ||
| 79 | // renderer construction | 88 | // renderer construction |
| @@ -22,6 +22,20 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare · 21 unmodified lines | |||
| 22 | connect(addressManager.data(), &AddressManager::lookupResultIsOffline, this, &AddressBarDialog::displayAddressOfflineMessage); | 22 | connect(addressManager.data(), &AddressManager::lookupResultIsOffline, this, &AddressBarDialog::displayAddressOfflineMessage); |
| 23 | connect(addressManager.data(), &AddressManager::lookupResultIsNotFound, this, &AddressBarDialog::displayAddressNotFoundMessage); | 23 | connect(addressManager.data(), &AddressManager::lookupResultIsNotFound, this, &AddressBarDialog::displayAddressNotFoundMessage); |
| 24 | connect(addressManager.data(), &AddressManager::lookupResultsFinished, this, &AddressBarDialog::hide); | 24 | connect(addressManager.data(), &AddressManager::lookupResultsFinished, this, &AddressBarDialog::hide); |
| 25 | connect(addressManager.data(), &AddressManager::goBackPossible, this, [this] (bool isPossible) { | ||
| 26 | if (isPossible != _backEnabled) { | ||
| 27 | _backEnabled = isPossible; | ||
| 28 | emit backEnabledChanged(); | ||
| 29 | } | ||
| 30 | }); | ||
| 31 | connect(addressManager.data(), &AddressManager::goForwardPossible, this, [this] (bool isPossible) { | ||
| 32 | if (isPossible != _forwardEnabled) { | ||
| 33 | _forwardEnabled = isPossible; | ||
| 34 | emit forwardEnabledChanged(); | ||
| 35 | } | ||
| 36 | }); | ||
| 37 | _backEnabled = !(DependencyManager::get<AddressManager>()->getBackStack().isEmpty()); | ||
| 38 | _forwardEnabled = !(DependencyManager::get<AddressManager>()->getForwardStack().isEmpty()); | ||
| 25 | } | 39 | } |
| 26 | 40 | ||
| 27 | void AddressBarDialog::hide() { | 41 | void AddressBarDialog::hide() { |
| @@ -35,6 +49,16 @@ void AddressBarDialog::loadAddress(const QString& address) { · 7 unmodified lines | |||
| 35 | } | 49 | } |
| 36 | } | 50 | } |
| 37 | 51 | ||
| 52 | void AddressBarDialog::loadBack() { | ||
| 53 | qDebug() << "Called LoadBack"; | ||
| 54 | DependencyManager::get<AddressManager>()->goBack(); | ||
| 55 | } | ||
| 56 | |||
| 57 | void AddressBarDialog::loadForward() { | ||
| 58 | qDebug() << "Called LoadForward"; | ||
| 59 | DependencyManager::get<AddressManager>()->goForward(); | ||
| 60 | } | ||
| 61 | |||
| 38 | void AddressBarDialog::displayAddressOfflineMessage() { | 62 | void AddressBarDialog::displayAddressOfflineMessage() { |
| 39 | OffscreenUi::error("That user or place is currently offline"); | 63 | OffscreenUi::error("That user or place is currently offline"); |
| 40 | } | 64 | } |
| @@ -18,9 +18,17 @@ class AddressBarDialog : public OffscreenQmlDialog · 17 unmodified lines | |||
| 18 | { | 18 | { |
| 19 | Q_OBJECT | 19 | Q_OBJECT |
| 20 | HIFI_QML_DECL | 20 | HIFI_QML_DECL |
| 21 | Q_PROPERTY(bool backEnabled READ backEnabled NOTIFY backEnabledChanged) | ||
| 22 | Q_PROPERTY(bool forwardEnabled READ forwardEnabled NOTIFY forwardEnabledChanged) | ||
| 21 | 23 | ||
| 22 | public: | 24 | public: |
| 23 | AddressBarDialog(QQuickItem* parent = nullptr); | 25 | AddressBarDialog(QQuickItem* parent = nullptr); |
| 26 | bool backEnabled() { return _backEnabled; } | ||
| 27 | bool forwardEnabled() { return _forwardEnabled; } | ||
| 28 | |||
| 29 | signals: | ||
| 30 | void backEnabledChanged(); | ||
| 31 | void forwardEnabledChanged(); | ||
| 24 | 32 | ||
| 25 | protected: | 33 | protected: |
| 26 | void displayAddressOfflineMessage(); | 34 | void displayAddressOfflineMessage(); |
| @@ -28,6 +36,11 @@ protected: · 1 unmodified line | |||
| 28 | void hide(); | 36 | void hide(); |
| 29 | 37 | ||
| 30 | Q_INVOKABLE void loadAddress(const QString& address); | 38 | Q_INVOKABLE void loadAddress(const QString& address); |
| 39 | Q_INVOKABLE void loadBack(); | ||
| 40 | Q_INVOKABLE void loadForward(); | ||
| 41 | |||
| 42 | bool _backEnabled; | ||
| 43 | bool _forwardEnabled; | ||
| 31 | }; | 44 | }; |
| 32 | 45 | ||
| 33 | #endif | 46 | #endif |
| @@ -0,0 +1,761 @@ | |||
| 1 | // | ||
| 2 | // ApplicationCompositor.cpp | ||
| 3 | // interface/src/ui/overlays | ||
| 4 | // | ||
| 5 | // Created by Benjamin Arnold on 5/27/14. | ||
| 6 | // Copyright 2014 High Fidelity, Inc. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | |||
| 12 | #include "InterfaceConfig.h" | ||
| 13 | |||
| 14 | #include "ApplicationCompositor.h" | ||
| 15 | |||
| 16 | #include <glm/gtc/type_ptr.hpp> | ||
| 17 | |||
| 18 | #include <avatar/AvatarManager.h> | ||
| 19 | #include <gpu/GLBackend.h> | ||
| 20 | #include <CursorManager.h> | ||
| 21 | #include <Tooltip.h> | ||
| 22 | |||
| 23 | #include "Application.h" | ||
| 24 | |||
| 25 | |||
| 26 | // Used to animate the magnification windows | ||
| 27 | static const float MAG_SPEED = 0.08f; | ||
| 28 | |||
| 29 | static const quint64 MSECS_TO_USECS = 1000ULL; | ||
| 30 | static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS; | ||
| 31 | |||
| 32 | static const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; | ||
| 33 | static const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f }; | ||
| 34 | static const float reticleSize = TWO_PI / 100.0f; | ||
| 35 | |||
| 36 | static const float CONNECTION_STATUS_BORDER_COLOR[] = { 1.0f, 0.0f, 0.0f }; | ||
| 37 | static const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f; | ||
| 38 | |||
| 39 | static const float CURSOR_PIXEL_SIZE = 32.0f; | ||
| 40 | static const float MOUSE_PITCH_RANGE = 1.0f * PI; | ||
| 41 | static const float MOUSE_YAW_RANGE = 0.5f * TWO_PI; | ||
| 42 | static const glm::vec2 MOUSE_RANGE(MOUSE_YAW_RANGE, MOUSE_PITCH_RANGE); | ||
| 43 | |||
| 44 | static gpu::BufferPointer _hemiVertices; | ||
| 45 | static gpu::BufferPointer _hemiIndices; | ||
| 46 | static int _hemiIndexCount{ 0 }; | ||
| 47 | EntityItemID ApplicationCompositor::_noItemId; | ||
| 48 | static QString _tooltipId; | ||
| 49 | |||
| 50 | // Return a point's cartesian coordinates on a sphere from pitch and yaw | ||
| 51 | glm::vec3 getPoint(float yaw, float pitch) { | ||
| 52 | return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)), | ||
| 53 | glm::sin(-pitch), | ||
| 54 | glm::cos(-pitch) * (-glm::cos(yaw))); | ||
| 55 | } | ||
| 56 | |||
| 57 | //Checks if the given ray intersects the sphere at the origin. result will store a multiplier that should | ||
| 58 | //be multiplied by dir and added to origin to get the location of the collision | ||
| 59 | bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r, float* result) | ||
| 60 | { | ||
| 61 | //Source: http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection | ||
| 62 | |||
| 63 | //Compute A, B and C coefficients | ||
| 64 | float a = glm::dot(dir, dir); | ||
| 65 | float b = 2 * glm::dot(dir, origin); | ||
| 66 | float c = glm::dot(origin, origin) - (r * r); | ||
| 67 | |||
| 68 | //Find discriminant | ||
| 69 | float disc = b * b - 4 * a * c; | ||
| 70 | |||
| 71 | // if discriminant is negative there are no real roots, so return | ||
| 72 | // false as ray misses sphere | ||
| 73 | if (disc < 0) { | ||
| 74 | return false; | ||
| 75 | } | ||
| 76 | |||
| 77 | // compute q as described above | ||
| 78 | float distSqrt = sqrtf(disc); | ||
| 79 | float q; | ||
| 80 | if (b < 0) { | ||
| 81 | q = (-b - distSqrt) / 2.0f; | ||
| 82 | } else { | ||
| 83 | q = (-b + distSqrt) / 2.0f; | ||
| 84 | } | ||
| 85 | |||
| 86 | // compute t0 and t1 | ||
| 87 | float t0 = q / a; | ||
| 88 | float t1 = c / q; | ||
| 89 | |||
| 90 | // make sure t0 is smaller than t1 | ||
| 91 | if (t0 > t1) { | ||
| 92 | // if t0 is bigger than t1 swap them around | ||
| 93 | float temp = t0; | ||
| 94 | t0 = t1; | ||
| 95 | t1 = temp; | ||
| 96 | } | ||
| 97 | |||
| 98 | // if t1 is less than zero, the object is in the ray's negative direction | ||
| 99 | // and consequently the ray misses the sphere | ||
| 100 | if (t1 < 0) { | ||
| 101 | return false; | ||
| 102 | } | ||
| 103 | |||
| 104 | // if t0 is less than zero, the intersection point is at t1 | ||
| 105 | if (t0 < 0) { | ||
| 106 | *result = t1; | ||
| 107 | return true; | ||
| 108 | } else { // else the intersection point is at t0 | ||
| 109 | *result = t0; | ||
| 110 | return true; | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | ApplicationCompositor::ApplicationCompositor() { | ||
| 115 | memset(_reticleActive, 0, sizeof(_reticleActive)); | ||
| 116 | memset(_magActive, 0, sizeof(_reticleActive)); | ||
| 117 | memset(_magSizeMult, 0, sizeof(_magSizeMult)); | ||
| 118 | |||
| 119 | auto geometryCache = DependencyManager::get<GeometryCache>(); | ||
| 120 | |||
| 121 | _reticleQuad = geometryCache->allocateID(); | ||
| 122 | _magnifierQuad = geometryCache->allocateID(); | ||
| 123 | _magnifierBorder = geometryCache->allocateID(); | ||
| 124 | |||
| 125 | auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); | ||
| 126 | connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) { | ||
| 127 | if (_hoverItemId != entityItemID) { | ||
| 128 | _hoverItemId = entityItemID; | ||
| 129 | _hoverItemEnterUsecs = usecTimestampNow(); | ||
| 130 | auto properties = entityScriptingInterface->getEntityProperties(_hoverItemId); | ||
| 131 | _hoverItemHref = properties.getHref(); | ||
| 132 | auto cursor = Cursor::Manager::instance().getCursor(); | ||
| 133 | if (!_hoverItemHref.isEmpty()) { | ||
| 134 | cursor->setIcon(Cursor::Icon::LINK); | ||
| 135 | } else { | ||
| 136 | cursor->setIcon(Cursor::Icon::DEFAULT); | ||
| 137 | } | ||
| 138 | } | ||
| 139 | }); | ||
| 140 | |||
| 141 | connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) { | ||
| 142 | if (_hoverItemId == entityItemID) { | ||
| 143 | _hoverItemId = _noItemId; | ||
| 144 | _hoverItemHref.clear(); | ||
| 145 | auto cursor = Cursor::Manager::instance().getCursor(); | ||
| 146 | cursor->setIcon(Cursor::Icon::DEFAULT); | ||
| 147 | if (!_tooltipId.isEmpty()) { | ||
| 148 | qDebug() << "Closing tooltip " << _tooltipId; | ||
| 149 | Tooltip::closeTip(_tooltipId); | ||
| 150 | _tooltipId.clear(); | ||
| 151 | } | ||
| 152 | } | ||
| 153 | }); | ||
| 154 | } | ||
| 155 | |||
| 156 | ApplicationCompositor::~ApplicationCompositor() { | ||
| 157 | } | ||
| 158 | |||
| 159 | |||
| 160 | void ApplicationCompositor::bindCursorTexture(gpu::Batch& batch, uint8_t cursorIndex) { | ||
| 161 | auto& cursorManager = Cursor::Manager::instance(); | ||
| 162 | auto cursor = cursorManager.getCursor(cursorIndex); | ||
| 163 | auto iconId = cursor->getIcon(); | ||
| 164 | if (!_cursors.count(iconId)) { | ||
| 165 | auto iconPath = cursorManager.getIconImage(cursor->getIcon()); | ||
| 166 | _cursors[iconId] = DependencyManager::get<TextureCache>()-> | ||
| 167 | getImageTexture(iconPath); | ||
| 168 | } | ||
| 169 | batch.setUniformTexture(0, _cursors[iconId]); | ||
| 170 | } | ||
| 171 | |||
| 172 | // Draws the FBO texture for the screen | ||
| 173 | void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) { | ||
| 174 | if (_alpha == 0.0f) { | ||
| 175 | return; | ||
| 176 | } | ||
| 177 | |||
| 178 | GLuint texture = qApp->getApplicationOverlay().getOverlayTexture(); | ||
| 179 | if (!texture) { | ||
| 180 | return; | ||
| 181 | } | ||
| 182 | |||
| 183 | updateTooltips(); | ||
| 184 | |||
| 185 | vec2 canvasSize = qApp->getCanvasSize(); | ||
| 186 | |||
| 187 | //Handle fading and deactivation/activation of UI | ||
| 188 | gpu::Batch batch; | ||
| 189 | |||
| 190 | renderArgs->_context->syncCache(); | ||
| 191 | auto geometryCache = DependencyManager::get<GeometryCache>(); | ||
| 192 | |||
| 193 | geometryCache->useSimpleDrawPipeline(batch); | ||
| 194 | batch.setModelTransform(Transform()); | ||
| 195 | batch.setViewTransform(Transform()); | ||
| 196 | batch.setProjectionTransform(mat4()); | ||
| 197 | batch._glBindTexture(GL_TEXTURE_2D, texture); | ||
| 198 | batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||
| 199 | batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||
| 200 | geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha)); | ||
| 201 | |||
| 202 | // Doesn't actually render | ||
| 203 | renderPointers(batch); | ||
| 204 | |||
| 205 | //draw the mouse pointer | ||
| 206 | // Get the mouse coordinates and convert to NDC [-1, 1] | ||
| 207 | vec2 mousePosition = toNormalizedDeviceScale(vec2(qApp->getMouse()), canvasSize); | ||
| 208 | // Invert the Y axis | ||
| 209 | mousePosition.y *= -1.0f; | ||
| 210 | |||
| 211 | Transform model; | ||
| 212 | model.setTranslation(vec3(mousePosition, 0)); | ||
| 213 | vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize; | ||
| 214 | model.setScale(vec3(mouseSize, 1.0f)); | ||
| 215 | batch.setModelTransform(model); | ||
| 216 | bindCursorTexture(batch); | ||
| 217 | vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; | ||
| 218 | geometryCache->renderUnitQuad(batch, vec4(1)); | ||
| 219 | renderArgs->_context->render(batch); | ||
| 220 | } | ||
| 221 | |||
| 222 | |||
| 223 | vec2 getPolarCoordinates(const PalmData& palm) { | ||
| 224 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | ||
| 225 | auto avatarOrientation = myAvatar->getOrientation(); | ||
| 226 | auto eyePos = myAvatar->getDefaultEyePosition(); | ||
| 227 | glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm); | ||
| 228 | // Direction of the tip relative to the eye | ||
| 229 | glm::vec3 tipDirection = tip - eyePos; | ||
| 230 | // orient into avatar space | ||
| 231 | tipDirection = glm::inverse(avatarOrientation) * tipDirection; | ||
| 232 | // Normalize for trig functions | ||
| 233 | tipDirection = glm::normalize(tipDirection); | ||
| 234 | // Convert to polar coordinates | ||
| 235 | glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y)); | ||
| 236 | return polar; | ||
| 237 | } | ||
| 238 | |||
| 239 | // Draws the FBO texture for Oculus rift. | ||
| 240 | void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int eye) { | ||
| 241 | if (_alpha == 0.0f) { | ||
| 242 | return; | ||
| 243 | } | ||
| 244 | |||
| 245 | GLuint texture = qApp->getApplicationOverlay().getOverlayTexture(); | ||
| 246 | if (!texture) { | ||
| 247 | return; | ||
| 248 | } | ||
| 249 | |||
| 250 | updateTooltips(); | ||
| 251 | |||
| 252 | vec2 canvasSize = qApp->getCanvasSize(); | ||
| 253 | _textureAspectRatio = aspect(canvasSize); | ||
| 254 | |||
| 255 | renderArgs->_context->syncCache(); | ||
| 256 | auto geometryCache = DependencyManager::get<GeometryCache>(); | ||
| 257 | |||
| 258 | gpu::Batch batch; | ||
| 259 | geometryCache->useSimpleDrawPipeline(batch); | ||
| 260 | batch._glDisable(GL_DEPTH_TEST); | ||
| 261 | batch._glDisable(GL_CULL_FACE); | ||
| 262 | batch._glBindTexture(GL_TEXTURE_2D, texture); | ||
| 263 | batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||
| 264 | batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||
| 265 | batch.setViewTransform(Transform()); | ||
| 266 | batch.setProjectionTransform(qApp->getEyeProjection(eye)); | ||
| 267 | |||
| 268 | mat4 eyePose = qApp->getEyePose(eye); | ||
| 269 | glm::mat4 overlayXfm = glm::inverse(eyePose); | ||
| 270 | |||
| 271 | #ifdef DEBUG_OVERLAY | ||
| 272 | { | ||
| 273 | batch.setModelTransform(glm::translate(mat4(), vec3(0, 0, -2))); | ||
| 274 | geometryCache->renderUnitQuad(batch, glm::vec4(1)); | ||
| 275 | } | ||
| 276 | #else | ||
| 277 | { | ||
| 278 | batch.setModelTransform(overlayXfm); | ||
| 279 | drawSphereSection(batch); | ||
| 280 | } | ||
| 281 | #endif | ||
| 282 | |||
| 283 | // Doesn't actually render | ||
| 284 | renderPointers(batch); | ||
| 285 | vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize); | ||
| 286 | |||
| 287 | bindCursorTexture(batch); | ||
| 288 | |||
| 289 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | ||
| 290 | //Controller Pointers | ||
| 291 | for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) { | ||
| 292 | PalmData& palm = myAvatar->getHand()->getPalms()[i]; | ||
| 293 | if (palm.isActive()) { | ||
| 294 | glm::vec2 polar = getPolarCoordinates(palm); | ||
| 295 | // Convert to quaternion | ||
| 296 | mat4 pointerXfm = glm::mat4_cast(quat(vec3(polar.y, -polar.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); | ||
| 297 | mat4 reticleXfm = overlayXfm * pointerXfm; | ||
| 298 | reticleXfm = glm::scale(reticleXfm, reticleScale); | ||
| 299 | batch.setModelTransform(reticleXfm); | ||
| 300 | // Render reticle at location | ||
| 301 | geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | //Mouse Pointer | ||
| 306 | if (_reticleActive[MOUSE]) { | ||
| 307 | glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(), | ||
| 308 | _reticlePosition[MOUSE].y())); | ||
| 309 | mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); | ||
| 310 | mat4 reticleXfm = overlayXfm * pointerXfm; | ||
| 311 | reticleXfm = glm::scale(reticleXfm, reticleScale); | ||
| 312 | batch.setModelTransform(reticleXfm); | ||
| 313 | geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); | ||
| 314 | } | ||
| 315 | |||
| 316 | renderArgs->_context->render(batch); | ||
| 317 | } | ||
| 318 | |||
| 319 | |||
| 320 | void ApplicationCompositor::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const { | ||
| 321 | cursorPos *= qApp->getCanvasSize(); | ||
| 322 | const glm::vec2 projection = screenToSpherical(cursorPos); | ||
| 323 | // The overlay space orientation of the mouse coordinates | ||
| 324 | const glm::quat orientation(glm::vec3(-projection.y, projection.x, 0.0f)); | ||
| 325 | // FIXME We now have the direction of the ray FROM THE DEFAULT HEAD POSE. | ||
| 326 | // Now we need to account for the actual camera position relative to the overlay | ||
| 327 | glm::vec3 overlaySpaceDirection = glm::normalize(orientation * IDENTITY_FRONT); | ||
| 328 | |||
| 329 | |||
| 330 | // We need the RAW camera orientation and position, because this is what the overlay is | ||
| 331 | // rendered relative to | ||
| 332 | const glm::vec3 overlayPosition = qApp->getCamera()->getPosition(); | ||
| 333 | const glm::quat overlayOrientation = qApp->getCamera()->getRotation(); | ||
| 334 | |||
| 335 | // Intersection UI overlay space | ||
| 336 | glm::vec3 worldSpaceDirection = overlayOrientation * overlaySpaceDirection; | ||
| 337 | glm::vec3 worldSpaceIntersection = (glm::normalize(worldSpaceDirection) * _oculusUIRadius) + overlayPosition; | ||
| 338 | glm::vec3 worldSpaceHeadPosition = (overlayOrientation * glm::vec3(qApp->getHeadPose()[3])) + overlayPosition; | ||
| 339 | |||
| 340 | // Intersection in world space | ||
| 341 | origin = worldSpaceHeadPosition; | ||
| 342 | direction = glm::normalize(worldSpaceIntersection - worldSpaceHeadPosition); | ||
| 343 | } | ||
| 344 | |||
| 345 | //Caculate the click location using one of the sixense controllers. Scale is not applied | ||
| 346 | QPoint ApplicationCompositor::getPalmClickLocation(const PalmData *palm) const { | ||
| 347 | QPoint rv; | ||
| 348 | auto canvasSize = qApp->getCanvasSize(); | ||
| 349 | if (qApp->isHMDMode()) { | ||
| 350 | glm::vec2 polar = getPolarCoordinates(*palm); | ||
| 351 | glm::vec2 point = sphericalToScreen(-polar); | ||
| 352 | rv.rx() = point.x; | ||
| 353 | rv.ry() = point.y; | ||
| 354 | } else { | ||
| 355 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | ||
| 356 | glm::dmat4 projection; | ||
| 357 | qApp->getProjectionMatrix(&projection); | ||
| 358 | glm::quat invOrientation = glm::inverse(myAvatar->getOrientation()); | ||
| 359 | glm::vec3 eyePos = myAvatar->getDefaultEyePosition(); | ||
| 360 | glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm); | ||
| 361 | glm::vec3 tipPos = invOrientation * (tip - eyePos); | ||
| 362 | |||
| 363 | glm::vec4 clipSpacePos = glm::vec4(projection * glm::dvec4(tipPos, 1.0)); | ||
| 364 | glm::vec3 ndcSpacePos; | ||
| 365 | if (clipSpacePos.w != 0) { | ||
| 366 | ndcSpacePos = glm::vec3(clipSpacePos) / clipSpacePos.w; | ||
| 367 | } | ||
| 368 | |||
| 369 | rv.setX(((ndcSpacePos.x + 1.0) / 2.0) * canvasSize.x); | ||
| 370 | rv.setY((1.0 - ((ndcSpacePos.y + 1.0) / 2.0)) * canvasSize.y); | ||
| 371 | } | ||
| 372 | return rv; | ||
| 373 | } | ||
| 374 | |||
| 375 | //Finds the collision point of a world space ray | ||
| 376 | bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const { | ||
| 377 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | ||
| 378 | |||
| 379 | glm::quat inverseOrientation = glm::inverse(myAvatar->getOrientation()); | ||
| 380 | |||
| 381 | glm::vec3 relativePosition = inverseOrientation * (position - myAvatar->getDefaultEyePosition()); | ||
| 382 | glm::vec3 relativeDirection = glm::normalize(inverseOrientation * direction); | ||
| 383 | |||
| 384 | float t; | ||
| 385 | if (raySphereIntersect(relativeDirection, relativePosition, _oculusUIRadius * myAvatar->getScale(), &t)){ | ||
| 386 | result = position + direction * t; | ||
| 387 | return true; | ||
| 388 | } | ||
| 389 | |||
| 390 | return false; | ||
| 391 | } | ||
| 392 | |||
| 393 | //Renders optional pointers | ||
| 394 | void ApplicationCompositor::renderPointers(gpu::Batch& batch) { | ||
| 395 | if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) { | ||
| 396 | //If we are in oculus, render reticle later | ||
| 397 | QPoint position = QPoint(qApp->getTrueMouseX(), qApp->getTrueMouseY()); | ||
| 398 | _reticlePosition[MOUSE] = position; | ||
| 399 | _reticleActive[MOUSE] = true; | ||
| 400 | _magActive[MOUSE] = _magnifier; | ||
| 401 | _reticleActive[LEFT_CONTROLLER] = false; | ||
| 402 | _reticleActive[RIGHT_CONTROLLER] = false; | ||
| 403 | } else if (qApp->getLastMouseMoveWasSimulated() && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { | ||
| 404 | //only render controller pointer if we aren't already rendering a mouse pointer | ||
| 405 | _reticleActive[MOUSE] = false; | ||
| 406 | _magActive[MOUSE] = false; | ||
| 407 | renderControllerPointers(batch); | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | |||
| 412 | void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { | ||
| 413 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | ||
| 414 | |||
| 415 | //Static variables used for storing controller state | ||
| 416 | static quint64 pressedTime[NUMBER_OF_RETICLES] = { 0ULL, 0ULL, 0ULL }; | ||
| 417 | static bool isPressed[NUMBER_OF_RETICLES] = { false, false, false }; | ||
| 418 | static bool stateWhenPressed[NUMBER_OF_RETICLES] = { false, false, false }; | ||
| 419 | |||
| 420 | const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData(); | ||
| 421 | |||
| 422 | for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) { | ||
| 423 | const int index = palmIndex - 1; | ||
| 424 | |||
| 425 | const PalmData* palmData = NULL; | ||
| 426 | |||
| 427 | if (palmIndex >= handData->getPalms().size()) { | ||
| 428 | return; | ||
| 429 | } | ||
| 430 | |||
| 431 | if (handData->getPalms()[palmIndex].isActive()) { | ||
| 432 | palmData = &handData->getPalms()[palmIndex]; | ||
| 433 | } else { | ||
| 434 | continue; | ||
| 435 | } | ||
| 436 | |||
| 437 | int controllerButtons = palmData->getControllerButtons(); | ||
| 438 | |||
| 439 | //Check for if we should toggle or drag the magnification window | ||
| 440 | if (controllerButtons & BUTTON_3) { | ||
| 441 | if (isPressed[index] == false) { | ||
| 442 | //We are now dragging the window | ||
| 443 | isPressed[index] = true; | ||
| 444 | //set the pressed time in us | ||
| 445 | pressedTime[index] = usecTimestampNow(); | ||
| 446 | stateWhenPressed[index] = _magActive[index]; | ||
| 447 | } | ||
| 448 | } else if (isPressed[index]) { | ||
| 449 | isPressed[index] = false; | ||
| 450 | //If the button was only pressed for < 250 ms | ||
| 451 | //then disable it. | ||
| 452 | |||
| 453 | const int MAX_BUTTON_PRESS_TIME = 250 * MSECS_TO_USECS; | ||
| 454 | if (usecTimestampNow() < pressedTime[index] + MAX_BUTTON_PRESS_TIME) { | ||
| 455 | _magActive[index] = !stateWhenPressed[index]; | ||
| 456 | } | ||
| 457 | } | ||
| 458 | |||
| 459 | //if we have the oculus, we should make the cursor smaller since it will be | ||
| 460 | //magnified | ||
| 461 | if (qApp->isHMDMode()) { | ||
| 462 | |||
| 463 | QPoint point = getPalmClickLocation(palmData); | ||
| 464 | |||
| 465 | _reticlePosition[index] = point; | ||
| 466 | |||
| 467 | //When button 2 is pressed we drag the mag window | ||
| 468 | if (isPressed[index]) { | ||
| 469 | _magActive[index] = true; | ||
| 470 | } | ||
| 471 | |||
| 472 | // If oculus is enabled, we draw the crosshairs later | ||
| 473 | continue; | ||
| 474 | } | ||
| 475 | |||
| 476 | auto canvasSize = qApp->getCanvasSize(); | ||
| 477 | int mouseX, mouseY; | ||
| 478 | if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { | ||
| 479 | QPoint res = getPalmClickLocation(palmData); | ||
| 480 | mouseX = res.x(); | ||
| 481 | mouseY = res.y(); | ||
| 482 | } else { | ||
| 483 | // Get directon relative to avatar orientation | ||
| 484 | glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); | ||
| 485 | |||
| 486 | // Get the angles, scaled between (-0.5,0.5) | ||
| 487 | float xAngle = (atan2(direction.z, direction.x) + M_PI_2); | ||
| 488 | float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); | ||
| 489 | |||
| 490 | // Get the pixel range over which the xAngle and yAngle are scaled | ||
| 491 | float cursorRange = canvasSize.x * SixenseManager::getInstance().getCursorPixelRangeMult(); | ||
| 492 | |||
| 493 | mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle); | ||
| 494 | mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle); | ||
| 495 | } | ||
| 496 | |||
| 497 | //If the cursor is out of the screen then don't render it | ||
| 498 | if (mouseX < 0 || mouseX >= (int)canvasSize.x || mouseY < 0 || mouseY >= (int)canvasSize.y) { | ||
| 499 | _reticleActive[index] = false; | ||
| 500 | continue; | ||
| 501 | } | ||
| 502 | _reticleActive[index] = true; | ||
| 503 | |||
| 504 | |||
| 505 | const float reticleSize = 40.0f; | ||
| 506 | |||
| 507 | mouseX -= reticleSize / 2.0f; | ||
| 508 | mouseY += reticleSize / 2.0f; | ||
| 509 | |||
| 510 | |||
| 511 | glm::vec2 topLeft(mouseX, mouseY); | ||
| 512 | glm::vec2 bottomRight(mouseX + reticleSize, mouseY - reticleSize); | ||
| 513 | glm::vec2 texCoordTopLeft(0.0f, 0.0f); | ||
| 514 | glm::vec2 texCoordBottomRight(1.0f, 1.0f); | ||
| 515 | |||
| 516 | DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, | ||
| 517 | glm::vec4(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f)); | ||
| 518 | |||
| 519 | } | ||
| 520 | } | ||
| 521 | |||
| 522 | //Renders a small magnification of the currently bound texture at the coordinates | ||
| 523 | void ApplicationCompositor::renderMagnifier(gpu::Batch& batch, const glm::vec2& magPos, float sizeMult, bool showBorder) { | ||
| 524 | if (!_magnifier) { | ||
| 525 | return; | ||
| 526 | } | ||
| 527 | auto canvasSize = qApp->getCanvasSize(); | ||
| 528 | |||
| 529 | const int widgetWidth = canvasSize.x; | ||
| 530 | const int widgetHeight = canvasSize.y; | ||
| 531 | |||
| 532 | const float halfWidth = (MAGNIFY_WIDTH / _textureAspectRatio) * sizeMult / 2.0f; | ||
| 533 | const float halfHeight = MAGNIFY_HEIGHT * sizeMult / 2.0f; | ||
| 534 | // Magnification Texture Coordinates | ||
| 535 | const float magnifyULeft = (magPos.x - halfWidth) / (float)widgetWidth; | ||
| 536 | const float magnifyURight = (magPos.x + halfWidth) / (float)widgetWidth; | ||
| 537 | const float magnifyVTop = 1.0f - (magPos.y - halfHeight) / (float)widgetHeight; | ||
| 538 | const float magnifyVBottom = 1.0f - (magPos.y + halfHeight) / (float)widgetHeight; | ||
| 539 | |||
| 540 | const float newHalfWidth = halfWidth * MAGNIFY_MULT; | ||
| 541 | const float newHalfHeight = halfHeight * MAGNIFY_MULT; | ||
| 542 | //Get yaw / pitch value for the corners | ||
| 543 | const glm::vec2 topLeftYawPitch = overlayToSpherical(glm::vec2(magPos.x - newHalfWidth, | ||
| 544 | magPos.y - newHalfHeight)); | ||
| 545 | const glm::vec2 bottomRightYawPitch = overlayToSpherical(glm::vec2(magPos.x + newHalfWidth, | ||
| 546 | magPos.y + newHalfHeight)); | ||
| 547 | |||
| 548 | const glm::vec3 bottomLeft = getPoint(topLeftYawPitch.x, bottomRightYawPitch.y); | ||
| 549 | const glm::vec3 bottomRight = getPoint(bottomRightYawPitch.x, bottomRightYawPitch.y); | ||
| 550 | const glm::vec3 topLeft = getPoint(topLeftYawPitch.x, topLeftYawPitch.y); | ||
| 551 | const glm::vec3 topRight = getPoint(bottomRightYawPitch.x, topLeftYawPitch.y); | ||
| 552 | |||
| 553 | auto geometryCache = DependencyManager::get<GeometryCache>(); | ||
| 554 | |||
| 555 | if (bottomLeft != _previousMagnifierBottomLeft || bottomRight != _previousMagnifierBottomRight | ||
| 556 | || topLeft != _previousMagnifierTopLeft || topRight != _previousMagnifierTopRight) { | ||
| 557 | QVector<glm::vec3> border; | ||
| 558 | border << topLeft; | ||
| 559 | border << bottomLeft; | ||
| 560 | border << bottomRight; | ||
| 561 | border << topRight; | ||
| 562 | border << topLeft; | ||
| 563 | geometryCache->updateVertices(_magnifierBorder, border, glm::vec4(1.0f, 0.0f, 0.0f, _alpha)); | ||
| 564 | |||
| 565 | _previousMagnifierBottomLeft = bottomLeft; | ||
| 566 | _previousMagnifierBottomRight = bottomRight; | ||
| 567 | _previousMagnifierTopLeft = topLeft; | ||
| 568 | _previousMagnifierTopRight = topRight; | ||
| 569 | } | ||
| 570 | |||
| 571 | glPushMatrix(); { | ||
| 572 | if (showBorder) { | ||
| 573 | glDisable(GL_TEXTURE_2D); | ||
| 574 | glLineWidth(1.0f); | ||
| 575 | //Outer Line | ||
| 576 | geometryCache->renderVertices(gpu::LINE_STRIP, _magnifierBorder); | ||
| 577 | glEnable(GL_TEXTURE_2D); | ||
| 578 | } | ||
| 579 | glm::vec4 magnifierColor = { 1.0f, 1.0f, 1.0f, _alpha }; | ||
| 580 | |||
| 581 | DependencyManager::get<GeometryCache>()->renderQuad(bottomLeft, bottomRight, topRight, topLeft, | ||
| 582 | glm::vec2(magnifyULeft, magnifyVBottom), | ||
| 583 | glm::vec2(magnifyURight, magnifyVBottom), | ||
| 584 | glm::vec2(magnifyURight, magnifyVTop), | ||
| 585 | glm::vec2(magnifyULeft, magnifyVTop), | ||
| 586 | magnifierColor, _magnifierQuad); | ||
| 587 | |||
| 588 | } glPopMatrix(); | ||
| 589 | } | ||
| 590 | |||
| 591 | void ApplicationCompositor::buildHemiVertices( | ||
| 592 | const float fov, const float aspectRatio, const int slices, const int stacks) { | ||
| 593 | static float textureFOV = 0.0f, textureAspectRatio = 1.0f; | ||
| 594 | if (textureFOV == fov && textureAspectRatio == aspectRatio) { | ||
| 595 | return; | ||
| 596 | } | ||
| 597 | |||
| 598 | textureFOV = fov; | ||
| 599 | textureAspectRatio = aspectRatio; | ||
| 600 | |||
| 601 | auto geometryCache = DependencyManager::get<GeometryCache>(); | ||
| 602 | |||
| 603 | _hemiVertices = gpu::BufferPointer(new gpu::Buffer()); | ||
| 604 | _hemiIndices = gpu::BufferPointer(new gpu::Buffer()); | ||
| 605 | |||
| 606 | |||
| 607 | if (fov >= PI) { | ||
| 608 | qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues"; | ||
| 609 | } | ||
| 610 | |||
| 611 | //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm | ||
| 612 | |||
| 613 | vec3 pos; | ||
| 614 | vec2 uv; | ||
| 615 | // Compute vertices positions and texture UV coordinate | ||
| 616 | // Create and write to buffer | ||
| 617 | for (int i = 0; i < stacks; i++) { | ||
| 618 | uv.y = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f | ||
| 619 | // abs(theta) <= fov / 2.0f | ||
| 620 | float pitch = -fov * (uv.y - 0.5f); | ||
| 621 | for (int j = 0; j < slices; j++) { | ||
| 622 | uv.x = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f | ||
| 623 | // abs(phi) <= fov * aspectRatio / 2.0f | ||
| 624 | float yaw = -fov * aspectRatio * (uv.x - 0.5f); | ||
| 625 | pos = getPoint(yaw, pitch); | ||
| 626 | static const vec4 color(1); | ||
| 627 | _hemiVertices->append(sizeof(pos), (gpu::Byte*)&pos); | ||
| 628 | _hemiVertices->append(sizeof(vec2), (gpu::Byte*)&uv); | ||
| 629 | _hemiVertices->append(sizeof(vec4), (gpu::Byte*)&color); | ||
| 630 | } | ||
| 631 | } | ||
| 632 | |||
| 633 | // Compute number of indices needed | ||
| 634 | static const int VERTEX_PER_TRANGLE = 3; | ||
| 635 | static const int TRIANGLE_PER_RECTANGLE = 2; | ||
| 636 | int numberOfRectangles = (slices - 1) * (stacks - 1); | ||
| 637 | _hemiIndexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE; | ||
| 638 | |||
| 639 | // Compute indices order | ||
| 640 | std::vector<GLushort> indices; | ||
| 641 | for (int i = 0; i < stacks - 1; i++) { | ||
| 642 | for (int j = 0; j < slices - 1; j++) { | ||
| 643 | GLushort bottomLeftIndex = i * slices + j; | ||
| 644 | GLushort bottomRightIndex = bottomLeftIndex + 1; | ||
| 645 | GLushort topLeftIndex = bottomLeftIndex + slices; | ||
| 646 | GLushort topRightIndex = topLeftIndex + 1; | ||
| 647 | // FIXME make a z-order curve for better vertex cache locality | ||
| 648 | indices.push_back(topLeftIndex); | ||
| 649 | indices.push_back(bottomLeftIndex); | ||
| 650 | indices.push_back(topRightIndex); | ||
| 651 | |||
| 652 | indices.push_back(topRightIndex); | ||
| 653 | indices.push_back(bottomLeftIndex); | ||
| 654 | indices.push_back(bottomRightIndex); | ||
| 655 | } | ||
| 656 | } | ||
| 657 | _hemiIndices->append(sizeof(GLushort) * indices.size(), (gpu::Byte*)&indices[0]); | ||
| 658 | } | ||
| 659 | |||
| 660 | |||
| 661 | void ApplicationCompositor::drawSphereSection(gpu::Batch& batch) { | ||
| 662 | buildHemiVertices(_textureFov, _textureAspectRatio, 80, 80); | ||
| 663 | static const int VERTEX_DATA_SLOT = 0; | ||
| 664 | static const int TEXTURE_DATA_SLOT = 1; | ||
| 665 | static const int COLOR_DATA_SLOT = 2; | ||
| 666 | gpu::Stream::FormatPointer streamFormat(new gpu::Stream::Format()); // 1 for everyone | ||
| 667 | streamFormat->setAttribute(gpu::Stream::POSITION, VERTEX_DATA_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); | ||
| 668 | streamFormat->setAttribute(gpu::Stream::TEXCOORD, TEXTURE_DATA_SLOT, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); | ||
| 669 | streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_DATA_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA)); | ||
| 670 | batch.setInputFormat(streamFormat); | ||
| 671 | |||
| 672 | static const int VERTEX_STRIDE = sizeof(vec3) + sizeof(vec2) + sizeof(vec4); | ||
| 673 | gpu::BufferView posView(_hemiVertices, 0, _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::POSITION)._element); | ||
| 674 | gpu::BufferView uvView(_hemiVertices, sizeof(vec3), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::TEXCOORD)._element); | ||
| 675 | gpu::BufferView colView(_hemiVertices, sizeof(vec3) + sizeof(vec2), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element); | ||
| 676 | batch.setInputBuffer(VERTEX_DATA_SLOT, posView); | ||
| 677 | batch.setInputBuffer(TEXTURE_DATA_SLOT, uvView); | ||
| 678 | batch.setInputBuffer(COLOR_DATA_SLOT, colView); | ||
| 679 | batch.setIndexBuffer(gpu::UINT16, _hemiIndices, 0); | ||
| 680 | batch.drawIndexed(gpu::TRIANGLES, _hemiIndexCount); | ||
| 681 | } | ||
| 682 | |||
| 683 | glm::vec2 ApplicationCompositor::directionToSpherical(const glm::vec3& direction) { | ||
| 684 | glm::vec2 result; | ||
| 685 | // Compute yaw | ||
| 686 | glm::vec3 normalProjection = glm::normalize(glm::vec3(direction.x, 0.0f, direction.z)); | ||
| 687 | result.x = glm::acos(glm::dot(IDENTITY_FRONT, normalProjection)); | ||
| 688 | if (glm::dot(IDENTITY_RIGHT, normalProjection) > 0.0f) { | ||
| 689 | result.x = -glm::abs(result.x); | ||
| 690 | } else { | ||
| 691 | result.x = glm::abs(result.x); | ||
| 692 | } | ||
| 693 | // Compute pitch | ||
| 694 | result.y = angleBetween(IDENTITY_UP, direction) - PI_OVER_TWO; | ||
| 695 | |||
| 696 | return result; | ||
| 697 | } | ||
| 698 | |||
| 699 | glm::vec3 ApplicationCompositor::sphericalToDirection(const glm::vec2& sphericalPos) { | ||
| 700 | glm::quat rotation(glm::vec3(sphericalPos.y, sphericalPos.x, 0.0f)); | ||
| 701 | return rotation * IDENTITY_FRONT; | ||
| 702 | } | ||
| 703 | |||
| 704 | glm::vec2 ApplicationCompositor::screenToSpherical(const glm::vec2& screenPos) { | ||
| 705 | auto screenSize = qApp->getCanvasSize(); | ||
| 706 | glm::vec2 result; | ||
| 707 | result.x = -(screenPos.x / screenSize.x - 0.5f); | ||
| 708 | result.y = (screenPos.y / screenSize.y - 0.5f); | ||
| 709 | result.x *= MOUSE_YAW_RANGE; | ||
| 710 | result.y *= MOUSE_PITCH_RANGE; | ||
| 711 | |||
| 712 | return result; | ||
| 713 | } | ||
| 714 | |||
| 715 | glm::vec2 ApplicationCompositor::sphericalToScreen(const glm::vec2& sphericalPos) { | ||
| 716 | glm::vec2 result = sphericalPos; | ||
| 717 | result.x *= -1.0; | ||
| 718 | result /= MOUSE_RANGE; | ||
| 719 | result += 0.5f; | ||
| 720 | result *= qApp->getCanvasSize(); | ||
| 721 | return result; | ||
| 722 | } | ||
| 723 | |||
| 724 | glm::vec2 ApplicationCompositor::sphericalToOverlay(const glm::vec2& sphericalPos) const { | ||
| 725 | glm::vec2 result = sphericalPos; | ||
| 726 | result.x *= -1.0; | ||
| 727 | result /= _textureFov; | ||
| 728 | result.x /= _textureAspectRatio; | ||
| 729 | result += 0.5f; | ||
| 730 | result *= qApp->getCanvasSize(); | ||
| 731 | return result; | ||
| 732 | } | ||
| 733 | |||
| 734 | glm::vec2 ApplicationCompositor::overlayToSpherical(const glm::vec2& overlayPos) const { | ||
| 735 | glm::vec2 result = overlayPos; | ||
| 736 | result /= qApp->getCanvasSize(); | ||
| 737 | result -= 0.5f; | ||
| 738 | result *= _textureFov; | ||
| 739 | result.x *= _textureAspectRatio; | ||
| 740 | result.x *= -1.0f; | ||
| 741 | return result; | ||
| 742 | } | ||
| 743 | |||
| 744 | glm::vec2 ApplicationCompositor::screenToOverlay(const glm::vec2& screenPos) const { | ||
| 745 | return sphericalToOverlay(screenToSpherical(screenPos)); | ||
| 746 | } | ||
| 747 | |||
| 748 | glm::vec2 ApplicationCompositor::overlayToScreen(const glm::vec2& overlayPos) const { | ||
| 749 | return sphericalToScreen(overlayToSpherical(overlayPos)); | ||
| 750 | } | ||
| 751 | |||
| 752 | void ApplicationCompositor::updateTooltips() { | ||
| 753 | if (_hoverItemId != _noItemId) { | ||
| 754 | quint64 hoverDuration = usecTimestampNow() - _hoverItemEnterUsecs; | ||
| 755 | if (_hoverItemEnterUsecs != UINT64_MAX && !_hoverItemHref.isEmpty() && hoverDuration > TOOLTIP_DELAY) { | ||
| 756 | // TODO Enable and position the tooltip | ||
| 757 | _hoverItemEnterUsecs = UINT64_MAX; | ||
| 758 | _tooltipId = Tooltip::showTip("URL: " + _hoverItemHref); | ||
| 759 | } | ||
| 760 | } | ||
| 761 | } | ||
| @@ -0,0 +1,120 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis Arnold on 2015/06/13 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #ifndef hifi_ApplicationCompositor_h | ||
| 10 | #define hifi_ApplicationCompositor_h | ||
| 11 | |||
| 12 | #include <QObject> | ||
| 13 | #include <cstdint> | ||
| 14 | |||
| 15 | #include <EntityItemID.h> | ||
| 16 | #include <GeometryCache.h> | ||
| 17 | #include <GLMHelpers.h> | ||
| 18 | #include <gpu/Batch.h> | ||
| 19 | #include <gpu/Texture.h> | ||
| 20 | |||
| 21 | class Camera; | ||
| 22 | class PalmData; | ||
| 23 | class RenderArgs; | ||
| 24 | |||
| 25 | const float MAGNIFY_WIDTH = 220.0f; | ||
| 26 | const float MAGNIFY_HEIGHT = 100.0f; | ||
| 27 | const float MAGNIFY_MULT = 2.0f; | ||
| 28 | |||
| 29 | const float DEFAULT_HMD_UI_ANGULAR_SIZE = 72.0f; | ||
| 30 | |||
| 31 | // Handles the drawing of the overlays to the screen | ||
| 32 | // TODO, move divide up the rendering, displaying and input handling | ||
| 33 | // facilities of this class | ||
| 34 | class ApplicationCompositor : public QObject { | ||
| 35 | Q_OBJECT | ||
| 36 | public: | ||
| 37 | ApplicationCompositor(); | ||
| 38 | ~ApplicationCompositor(); | ||
| 39 | |||
| 40 | void displayOverlayTexture(RenderArgs* renderArgs); | ||
| 41 | void displayOverlayTextureHmd(RenderArgs* renderArgs, int eye); | ||
| 42 | |||
| 43 | QPoint getPalmClickLocation(const PalmData *palm) const; | ||
| 44 | bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const; | ||
| 45 | |||
| 46 | bool hasMagnifier() const { return _magnifier; } | ||
| 47 | void toggleMagnifier() { _magnifier = !_magnifier; } | ||
| 48 | |||
| 49 | float getHmdUIAngularSize() const { return _hmdUIAngularSize; } | ||
| 50 | void setHmdUIAngularSize(float hmdUIAngularSize) { _hmdUIAngularSize = hmdUIAngularSize; } | ||
| 51 | |||
| 52 | // Converter from one frame of reference to another. | ||
| 53 | // Frame of reference: | ||
| 54 | // Direction: Ray that represents the spherical values | ||
| 55 | // Screen: Position on the screen (x,y) | ||
| 56 | // Spherical: Pitch and yaw that gives the position on the sphere we project on (yaw,pitch) | ||
| 57 | // Overlay: Position on the overlay (x,y) | ||
| 58 | // (x,y) in Overlay are similar than (x,y) in Screen except they can be outside of the bound of te screen. | ||
| 59 | // This allows for picking outside of the screen projection in 3D. | ||
| 60 | glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const; | ||
| 61 | glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const; | ||
| 62 | glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const; | ||
| 63 | glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const; | ||
| 64 | void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const; | ||
| 65 | GLuint getOverlayTexture() const; | ||
| 66 | |||
| 67 | static glm::vec2 directionToSpherical(const glm::vec3 & direction); | ||
| 68 | static glm::vec3 sphericalToDirection(const glm::vec2 & sphericalPos); | ||
| 69 | static glm::vec2 screenToSpherical(const glm::vec2 & screenPos); | ||
| 70 | static glm::vec2 sphericalToScreen(const glm::vec2 & sphericalPos); | ||
| 71 | |||
| 72 | private: | ||
| 73 | void displayOverlayTextureStereo(RenderArgs* renderArgs, float aspectRatio, float fov); | ||
| 74 | void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0); | ||
| 75 | void buildHemiVertices(const float fov, const float aspectRatio, const int slices, const int stacks); | ||
| 76 | void drawSphereSection(gpu::Batch& batch); | ||
| 77 | void updateTooltips(); | ||
| 78 | |||
| 79 | void renderPointers(gpu::Batch& batch); | ||
| 80 | void renderMagnifier(gpu::Batch& batch, const glm::vec2& magPos, float sizeMult, bool showBorder); | ||
| 81 | void renderControllerPointers(gpu::Batch& batch); | ||
| 82 | void renderPointersOculus(gpu::Batch& batch); | ||
| 83 | |||
| 84 | // Support for hovering and tooltips | ||
| 85 | static EntityItemID _noItemId; | ||
| 86 | EntityItemID _hoverItemId{ _noItemId }; | ||
| 87 | QString _hoverItemHref; | ||
| 88 | quint64 _hoverItemEnterUsecs{ 0 }; | ||
| 89 | |||
| 90 | float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE; | ||
| 91 | float _textureFov{ glm::radians(DEFAULT_HMD_UI_ANGULAR_SIZE) }; | ||
| 92 | float _textureAspectRatio{ 1.0f }; | ||
| 93 | int _hemiVerticesID{ GeometryCache::UNKNOWN_ID }; | ||
| 94 | |||
| 95 | enum Reticles { MOUSE, LEFT_CONTROLLER, RIGHT_CONTROLLER, NUMBER_OF_RETICLES }; | ||
| 96 | bool _reticleActive[NUMBER_OF_RETICLES]; | ||
| 97 | QPoint _reticlePosition[NUMBER_OF_RETICLES]; | ||
| 98 | bool _magActive[NUMBER_OF_RETICLES]; | ||
| 99 | float _magSizeMult[NUMBER_OF_RETICLES]; | ||
| 100 | bool _magnifier{ true }; | ||
| 101 | |||
| 102 | float _alpha{ 1.0f }; | ||
| 103 | float _oculusUIRadius{ 1.0f }; | ||
| 104 | |||
| 105 | QMap<uint16_t, gpu::TexturePointer> _cursors; | ||
| 106 | |||
| 107 | int _reticleQuad; | ||
| 108 | int _magnifierQuad; | ||
| 109 | int _magnifierBorder; | ||
| 110 | |||
| 111 | int _previousBorderWidth{ -1 }; | ||
| 112 | int _previousBorderHeight{ -1 }; | ||
| 113 | |||
| 114 | glm::vec3 _previousMagnifierBottomLeft; | ||
| 115 | glm::vec3 _previousMagnifierBottomRight; | ||
| 116 | glm::vec3 _previousMagnifierTopLeft; | ||
| 117 | glm::vec3 _previousMagnifierTopRight; | ||
| 118 | }; | ||
| 119 | |||
| 120 | #endif // hifi_ApplicationCompositor_h | ||
| @@ -14,166 +14,58 @@ · 13 unmodified lines | |||
| 14 | #include <QOpenGLFramebufferObject> | 14 | #include <QOpenGLFramebufferObject> |
| 15 | #include <QOpenGLTexture> | 15 | #include <QOpenGLTexture> |
| 16 | 16 | ||
| 17 | #include <glm/gtc/type_ptr.hpp> | ||
| 18 | |||
| 17 | #include <avatar/AvatarManager.h> | 19 | #include <avatar/AvatarManager.h> |
| 20 | #include <DeferredLightingEffect.h> | ||
| 18 | #include <GLMHelpers.h> | 21 | #include <GLMHelpers.h> |
| 19 | #include <PathUtils.h> | ||
| 20 | #include <gpu/GLBackend.h> | 22 | #include <gpu/GLBackend.h> |
| 23 | #include <gpu/GLBackendShared.h> | ||
| 21 | #include <GLMHelpers.h> | 24 | #include <GLMHelpers.h> |
| 22 | #include <PerfStat.h> | ||
| 23 | #include <OffscreenUi.h> | 25 | #include <OffscreenUi.h> |
| 26 | #include <CursorManager.h> | ||
| 27 | #include <PerfStat.h> | ||
| 24 | 28 | ||
| 25 | #include "AudioClient.h" | 29 | #include "AudioClient.h" |
| 26 | #include "audio/AudioIOStatsRenderer.h" | 30 | #include "audio/AudioIOStatsRenderer.h" |
| 27 | #include "audio/AudioScope.h" | 31 | #include "audio/AudioScope.h" |
| 28 | #include "audio/AudioToolBox.h" | ||
| 29 | #include "Application.h" | 32 | #include "Application.h" |
| 30 | #include "ApplicationOverlay.h" | 33 | #include "ApplicationOverlay.h" |
| 31 | #include "devices/CameraToolBox.h" | ||
| 32 | 34 | ||
| 33 | #include "Util.h" | 35 | #include "Util.h" |
| 34 | #include "ui/Stats.h" | 36 | #include "ui/Stats.h" |
| 35 | 37 | #include "ui/AvatarInputs.h" | |
| 36 | // Used to animate the magnification windows | ||
| 37 | const float MAG_SPEED = 0.08f; | ||
| 38 | |||
| 39 | const quint64 MSECS_TO_USECS = 1000ULL; | ||
| 40 | 38 | ||
| 41 | const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; | 39 | const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; |
| 42 | const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f }; | 40 | const int AUDIO_METER_GAP = 5; |
| 43 | const float reticleSize = TWO_PI / 100.0f; | 41 | const int MUTE_ICON_PADDING = 10; |
| 44 | 42 | const vec4 CONNECTION_STATUS_BORDER_COLOR{ 1.0f, 0.0f, 0.0f, 0.8f }; | |
| 45 | |||
| 46 | const float CONNECTION_STATUS_BORDER_COLOR[] = { 1.0f, 0.0f, 0.0f }; | ||
| 47 | const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f; | 43 | const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f; |
| 44 | static const float ORTHO_NEAR_CLIP = -10000; | ||
| 45 | static const float ORTHO_FAR_CLIP = 10000; | ||
| 48 | 46 | ||
| 49 | static const float MOUSE_PITCH_RANGE = 1.0f * PI; | 47 | // TODO move somewhere useful |
| 50 | static const float MOUSE_YAW_RANGE = 0.5f * TWO_PI; | 48 | static void fboViewport(QOpenGLFramebufferObject* fbo) { |
| 51 | static const glm::vec2 MOUSE_RANGE(MOUSE_YAW_RANGE, MOUSE_PITCH_RANGE); | 49 | auto size = fbo->size(); |
| 52 | 50 | glViewport(0, 0, size.width(), size.height()); | |
| 53 | // Return a point's cartesian coordinates on a sphere from pitch and yaw | ||
| 54 | glm::vec3 getPoint(float yaw, float pitch) { | ||
| 55 | return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)), | ||
| 56 | glm::sin(-pitch), | ||
| 57 | glm::cos(-pitch) * (-glm::cos(yaw))); | ||
| 58 | } | 51 | } |
| 59 | 52 | ||
| 60 | //Checks if the given ray intersects the sphere at the origin. result will store a multiplier that should | 53 | ApplicationOverlay::ApplicationOverlay() |
| 61 | //be multiplied by dir and added to origin to get the location of the collision | ||
| 62 | bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r, float* result) | ||
| 63 | { | 54 | { |
| 64 | //Source: http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection | ||
| 65 | |||
| 66 | //Compute A, B and C coefficients | ||
| 67 | float a = glm::dot(dir, dir); | ||
| 68 | float b = 2 * glm::dot(dir, origin); | ||
| 69 | float c = glm::dot(origin, origin) - (r * r); | ||
| 70 | |||
| 71 | //Find discriminant | ||
| 72 | float disc = b * b - 4 * a * c; | ||
| 73 | |||
| 74 | // if discriminant is negative there are no real roots, so return | ||
| 75 | // false as ray misses sphere | ||
| 76 | if (disc < 0) { | ||
| 77 | return false; | ||
| 78 | } | ||
| 79 | |||
| 80 | // compute q as described above | ||
| 81 | float distSqrt = sqrtf(disc); | ||
| 82 | float q; | ||
| 83 | if (b < 0) { | ||
| 84 | q = (-b - distSqrt) / 2.0; | ||
| 85 | } else { | ||
| 86 | q = (-b + distSqrt) / 2.0; | ||
| 87 | } | ||
| 88 | |||
| 89 | // compute t0 and t1 | ||
| 90 | float t0 = q / a; | ||
| 91 | float t1 = c / q; | ||
| 92 | |||
| 93 | // make sure t0 is smaller than t1 | ||
| 94 | if (t0 > t1) { | ||
| 95 | // if t0 is bigger than t1 swap them around | ||
| 96 | float temp = t0; | ||
| 97 | t0 = t1; | ||
| 98 | t1 = temp; | ||
| 99 | } | ||
| 100 | |||
| 101 | // if t1 is less than zero, the object is in the ray's negative direction | ||
| 102 | // and consequently the ray misses the sphere | ||
| 103 | if (t1 < 0) { | ||
| 104 | return false; | ||
| 105 | } | ||
| 106 | |||
| 107 | // if t0 is less than zero, the intersection point is at t1 | ||
| 108 | if (t0 < 0) { | ||
| 109 | *result = t1; | ||
| 110 | return true; | ||
| 111 | } else { // else the intersection point is at t0 | ||
| 112 | *result = t0; | ||
| 113 | return true; | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | void ApplicationOverlay::renderReticle(glm::quat orientation, float alpha) { | ||
| 118 | glPushMatrix(); { | ||
| 119 | glm::vec3 axis = glm::axis(orientation); | ||
| 120 | glRotatef(glm::degrees(glm::angle(orientation)), axis.x, axis.y, axis.z); | ||
| 121 | glm::vec3 topLeft = getPoint(reticleSize / 2.0f, -reticleSize / 2.0f); | ||
| 122 | glm::vec3 topRight = getPoint(-reticleSize / 2.0f, -reticleSize / 2.0f); | ||
| 123 | glm::vec3 bottomLeft = getPoint(reticleSize / 2.0f, reticleSize / 2.0f); | ||
| 124 | glm::vec3 bottomRight = getPoint(-reticleSize / 2.0f, reticleSize / 2.0f); | ||
| 125 | |||
| 126 | // TODO: this version of renderQuad() needs to take a color | ||
| 127 | glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], alpha }; | ||
| 128 | |||
| 129 | |||
| 130 | |||
| 131 | DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomLeft, bottomRight, topRight, | ||
| 132 | glm::vec2(0.0f, 0.0f), glm::vec2(1.0f, 0.0f), | ||
| 133 | glm::vec2(1.0f, 1.0f), glm::vec2(0.0f, 1.0f), | ||
| 134 | reticleColor, _reticleQuad); | ||
| 135 | } glPopMatrix(); | ||
| 136 | } | ||
| 137 | |||
| 138 | ApplicationOverlay::ApplicationOverlay() : | ||
| 139 | _textureFov(glm::radians(DEFAULT_HMD_UI_ANGULAR_SIZE)), | ||
| 140 | _textureAspectRatio(1.0f), | ||
| 141 | _lastMouseMove(0), | ||
| 142 | _magnifier(true), | ||
| 143 | _alpha(1.0f), | ||
| 144 | _oculusUIRadius(1.0f), | ||
| 145 | _trailingAudioLoudness(0.0f), | ||
| 146 | _previousBorderWidth(-1), | ||
| 147 | _previousBorderHeight(-1), | ||
| 148 | _previousMagnifierBottomLeft(), | ||
| 149 | _previousMagnifierBottomRight(), | ||
| 150 | _previousMagnifierTopLeft(), | ||
| 151 | _previousMagnifierTopRight() | ||
| 152 | { | ||
| 153 | memset(_reticleActive, 0, sizeof(_reticleActive)); | ||
| 154 | memset(_magActive, 0, sizeof(_reticleActive)); | ||
| 155 | memset(_magSizeMult, 0, sizeof(_magSizeMult)); | ||
| 156 | |||
| 157 | auto geometryCache = DependencyManager::get<GeometryCache>(); | 55 | auto geometryCache = DependencyManager::get<GeometryCache>(); |
| 158 | |||
| 159 | _reticleQuad = geometryCache->allocateID(); | ||
| 160 | _magnifierQuad = geometryCache->allocateID(); | ||
| 161 | _audioRedQuad = geometryCache->allocateID(); | ||
| 162 | _audioGreenQuad = geometryCache->allocateID(); | ||
| 163 | _audioBlueQuad = geometryCache->allocateID(); | ||
| 164 | _domainStatusBorder = geometryCache->allocateID(); | 56 | _domainStatusBorder = geometryCache->allocateID(); |
| 165 | _magnifierBorder = geometryCache->allocateID(); | 57 | _magnifierBorder = geometryCache->allocateID(); |
| 166 | 58 | ||
| 167 | // Once we move UI rendering and screen rendering to different | 59 | // Once we move UI rendering and screen rendering to different |
| 168 | // threads, we need to use a sync object to deteremine when | 60 | // threads, we need to use a sync object to deteremine when |
| 169 | // the current UI texture is no longer being read from, and only | 61 | // the current UI texture is no longer being read from, and only |
| 170 | // then release it back to the UI for re-use | 62 | // then release it back to the UI for re-use |
| 171 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); | 63 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); |
| 172 | connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [&](GLuint textureId) { | 64 | connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [&](GLuint textureId) { |
| 173 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); | 65 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); |
| 174 | offscreenUi->lockTexture(textureId); | 66 | offscreenUi->lockTexture(textureId); |
| 175 | assert(!glGetError()); | 67 | assert(!glGetError()); |
| 176 | std::swap(_newUiTexture, textureId); | 68 | std::swap(_uiTexture, textureId); |
| 177 | if (textureId) { | 69 | if (textureId) { |
| 178 | offscreenUi->releaseTexture(textureId); | 70 | offscreenUi->releaseTexture(textureId); |
| 179 | } | 71 | } |
| @@ -185,846 +77,81 @@ ApplicationOverlay::~ApplicationOverlay() { · 5 unmodified lines | |||
| 185 | 77 | ||
| 186 | // Renders the overlays either to a texture or to the screen | 78 | // Renders the overlays either to a texture or to the screen |
| 187 | void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { | 79 | void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { |
| 80 | CHECK_GL_ERROR(); | ||
| 188 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); | 81 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); |
| 189 | Overlays& overlays = qApp->getOverlays(); | ||
| 190 | |||
| 191 | _textureFov = glm::radians(_hmdUIAngularSize); | ||
| 192 | glm::vec2 size = qApp->getCanvasSize(); | ||
| 193 | _textureAspectRatio = aspect(size); | ||
| 194 | 82 | ||
| 195 | //Handle fading and deactivation/activation of UI | 83 | // TODO move to Application::idle()? |
| 196 | 84 | Stats::getInstance()->updateStats(); | |
| 197 | // Render 2D overlay | 85 | AvatarInputs::getInstance()->update(); |
| 198 | glMatrixMode(GL_PROJECTION); | ||
| 199 | glDisable(GL_DEPTH_TEST); | ||
| 200 | glDisable(GL_LIGHTING); | ||
| 201 | glEnable(GL_BLEND); | ||
| 202 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||
| 203 | _overlays.buildFramebufferObject(); | ||
| 204 | _overlays.bind(); | ||
| 205 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | ||
| 206 | glViewport(0, 0, size.x, size.y); | ||
| 207 | 86 | ||
| 208 | glPushMatrix(); { | 87 | buildFramebufferObject(); |
| 209 | const float NEAR_CLIP = -10000; | ||
| 210 | const float FAR_CLIP = 10000; | ||
| 211 | glLoadIdentity(); | ||
| 212 | glOrtho(0, size.x, size.y, 0, NEAR_CLIP, FAR_CLIP); | ||
| 213 | 88 | ||
| 214 | glMatrixMode(GL_MODELVIEW); | 89 | // Execute the batch into our framebuffer |
| 215 | 90 | _overlayFramebuffer->bind(); | |
| 216 | renderAudioMeter(); | 91 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| 217 | renderCameraToggle(); | 92 | fboViewport(_overlayFramebuffer); |
| 218 | |||
| 219 | renderStatsAndLogs(); | ||
| 220 | |||
| 221 | // give external parties a change to hook in | ||
| 222 | emit qApp->renderingOverlay(); | ||
| 223 | |||
| 224 | overlays.renderHUD(renderArgs); | ||
| 225 | |||
| 226 | renderPointers(); | ||
| 227 | |||
| 228 | renderDomainConnectionStatusBorder(); | ||
| 229 | |||
| 230 | glMatrixMode(GL_PROJECTION); | ||
| 231 | } glPopMatrix(); | ||
| 232 | |||
| 233 | glMatrixMode(GL_MODELVIEW); | ||
| 234 | glEnable(GL_DEPTH_TEST); | ||
| 235 | glEnable(GL_LIGHTING); | ||
| 236 | glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); | ||
| 237 | |||
| 238 | _overlays.release(); | ||
| 239 | } | ||
| 240 | 93 | ||
| 241 | // A quick and dirty solution for compositing the old overlay | 94 | // Now render the overlay components together into a single texture |
| 242 | // texture with the new one | 95 | renderOverlays(renderArgs); |
| 243 | template <typename F> | 96 | renderStatsAndLogs(renderArgs); |
| 244 | void with_each_texture(GLuint firstPassTexture, GLuint secondPassTexture, F f) { | 97 | renderDomainConnectionStatusBorder(renderArgs); |
| 245 | glEnable(GL_TEXTURE_2D); | 98 | renderQmlUi(renderArgs); |
| 246 | glActiveTexture(GL_TEXTURE0); | 99 | _overlayFramebuffer->release(); |
| 247 | if (firstPassTexture) { | 100 | CHECK_GL_ERROR(); |
| 248 | glBindTexture(GL_TEXTURE_2D, firstPassTexture); | ||
| 249 | f(); | ||
| 250 | } | ||
| 251 | if (secondPassTexture) { | ||
| 252 | glBindTexture(GL_TEXTURE_2D, secondPassTexture); | ||
| 253 | f(); | ||
| 254 | } | ||
| 255 | glBindTexture(GL_TEXTURE_2D, 0); | ||
| 256 | glDisable(GL_TEXTURE_2D); | ||
| 257 | } | 101 | } |
| 258 | 102 | ||
| 259 | // Draws the FBO texture for the screen | 103 | void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) { |
| 260 | void ApplicationOverlay::displayOverlayTexture() { | 104 | if (_uiTexture) { |
| 261 | if (_alpha == 0.0f) { | 105 | gpu::Batch batch; |
| 262 | return; | 106 | auto geometryCache = DependencyManager::get<GeometryCache>(); |
| 107 | geometryCache->useSimpleDrawPipeline(batch); | ||
| 108 | batch.setProjectionTransform(mat4()); | ||
| 109 | batch.setModelTransform(mat4()); | ||
| 110 | batch._glBindTexture(GL_TEXTURE_2D, _uiTexture); | ||
| 111 | geometryCache->renderUnitQuad(batch, glm::vec4(1)); | ||
| 112 | renderArgs->_context->syncCache(); | ||
| 113 | renderArgs->_context->render(batch); | ||
| 263 | } | 114 | } |
| 264 | glMatrixMode(GL_PROJECTION); | ||
| 265 | glPushMatrix(); { | ||
| 266 | glLoadIdentity(); | ||
| 267 | glDisable(GL_DEPTH_TEST); | ||
| 268 | glDisable(GL_LIGHTING); | ||
| 269 | glEnable(GL_BLEND); | ||
| 270 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||
| 271 | glViewport(0, 0, qApp->getDeviceSize().width(), qApp->getDeviceSize().height()); | ||
| 272 | |||
| 273 | static const glm::vec2 topLeft(-1, 1); | ||
| 274 | static const glm::vec2 bottomRight(1, -1); | ||
| 275 | static const glm::vec2 texCoordTopLeft(0.0f, 1.0f); | ||
| 276 | static const glm::vec2 texCoordBottomRight(1.0f, 0.0f); | ||
| 277 | with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { | ||
| 278 | DependencyManager::get<GeometryCache>()->renderQuad( | ||
| 279 | topLeft, bottomRight, | ||
| 280 | texCoordTopLeft, texCoordBottomRight, | ||
| 281 | glm::vec4(1.0f, 1.0f, 1.0f, _alpha)); | ||
| 282 | }); | ||
| 283 | |||
| 284 | if (!_crosshairTexture) { | ||
| 285 | _crosshairTexture = DependencyManager::get<TextureCache>()-> | ||
| 286 | getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); | ||
| 287 | } | ||
| 288 | |||
| 289 | //draw the mouse pointer | ||
| 290 | glm::vec2 canvasSize = qApp->getCanvasSize(); | ||
| 291 | glm::vec2 mouseSize = 32.0f / canvasSize; | ||
| 292 | auto mouseTopLeft = topLeft * mouseSize; | ||
| 293 | auto mouseBottomRight = bottomRight * mouseSize; | ||
| 294 | vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY()); | ||
| 295 | mousePosition /= canvasSize; | ||
| 296 | mousePosition *= 2.0f; | ||
| 297 | mousePosition -= 1.0f; | ||
| 298 | mousePosition.y *= -1.0f; | ||
| 299 | |||
| 300 | glEnable(GL_TEXTURE_2D); | ||
| 301 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||
| 302 | glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); | ||
| 303 | glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; | ||
| 304 | DependencyManager::get<GeometryCache>()->renderQuad( | ||
| 305 | mouseTopLeft + mousePosition, mouseBottomRight + mousePosition, | ||
| 306 | texCoordTopLeft, texCoordBottomRight, | ||
| 307 | reticleColor); | ||
| 308 | glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); | ||
| 309 | glDisable(GL_TEXTURE_2D); | ||
| 310 | } glPopMatrix(); | ||
| 311 | } | 115 | } |
| 312 | 116 | ||
| 313 | // Draws the FBO texture for Oculus rift. | 117 | void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) { |
| 314 | void ApplicationOverlay::displayOverlayTextureHmd(Camera& whichCamera) { | 118 | glm::vec2 size = qApp->getCanvasSize(); |
| 315 | if (_alpha == 0.0f) { | ||
| 316 | return; | ||
| 317 | } | ||
| 318 | |||
| 319 | glEnable(GL_BLEND); | ||
| 320 | glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); | ||
| 321 | glEnable(GL_DEPTH_TEST); | ||
| 322 | glDepthMask(GL_TRUE); | ||
| 323 | glDisable(GL_LIGHTING); | ||
| 324 | glEnable(GL_ALPHA_TEST); | ||
| 325 | glAlphaFunc(GL_GREATER, 0.01f); | ||
| 326 | |||
| 327 | 119 | ||
| 328 | //Update and draw the magnifiers | 120 | mat4 legacyProjection = glm::ortho<float>(0, size.x, size.y, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP); |
| 329 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | 121 | glMatrixMode(GL_PROJECTION); |
| 330 | const glm::quat& orientation = myAvatar->getOrientation(); | 122 | glPushMatrix(); |
| 331 | // Always display the HMD overlay relative to the camera position but | 123 | glLoadMatrixf(glm::value_ptr(legacyProjection)); |
| 332 | // remove the HMD pose offset. This results in an overlay that sticks with you | ||
| 333 | // even in third person mode, but isn't drawn at a fixed distance. | ||
| 334 | glm::vec3 position = whichCamera.getPosition(); | ||
| 335 | position -= qApp->getCamera()->getHmdPosition(); | ||
| 336 | const float scale = myAvatar->getScale() * _oculusUIRadius; | ||
| 337 | |||
| 338 | // glm::vec3 eyeOffset = setEyeOffsetPosition; | ||
| 339 | glMatrixMode(GL_MODELVIEW); | 124 | glMatrixMode(GL_MODELVIEW); |
| 340 | glPushMatrix(); { | ||
| 341 | glTranslatef(position.x, position.y, position.z); | ||
| 342 | glm::mat4 rotation = glm::toMat4(orientation); | ||
| 343 | glMultMatrixf(&rotation[0][0]); | ||
| 344 | glScalef(scale, scale, scale); | ||
| 345 | for (int i = 0; i < NUMBER_OF_RETICLES; i++) { | ||
| 346 | |||
| 347 | if (_magActive[i]) { | ||
| 348 | _magSizeMult[i] += MAG_SPEED; | ||
| 349 | if (_magSizeMult[i] > 1.0f) { | ||
| 350 | _magSizeMult[i] = 1.0f; | ||
| 351 | } | ||
| 352 | } else { | ||
| 353 | _magSizeMult[i] -= MAG_SPEED; | ||
| 354 | if (_magSizeMult[i] < 0.0f) { | ||
| 355 | _magSizeMult[i] = 0.0f; | ||
| 356 | } | ||
| 357 | } | ||
| 358 | |||
| 359 | if (_magSizeMult[i] > 0.0f) { | ||
| 360 | //Render magnifier, but dont show border for mouse magnifier | ||
| 361 | glm::vec2 projection = screenToOverlay(glm::vec2(_reticlePosition[MOUSE].x(), | ||
| 362 | _reticlePosition[MOUSE].y())); | ||
| 363 | with_each_texture(_overlays.getTexture(), 0, [&] { | ||
| 364 | renderMagnifier(projection, _magSizeMult[i], i != MOUSE); | ||
| 365 | }); | ||
| 366 | } | ||
| 367 | } | ||
| 368 | |||
| 369 | glDepthMask(GL_FALSE); | ||
| 370 | glDisable(GL_ALPHA_TEST); | ||
| 371 | |||
| 372 | static float textureFOV = 0.0f, textureAspectRatio = 1.0f; | ||
| 373 | if (textureFOV != _textureFov || | ||
| 374 | textureAspectRatio != _textureAspectRatio) { | ||
| 375 | textureFOV = _textureFov; | ||
| 376 | textureAspectRatio = _textureAspectRatio; | ||
| 377 | |||
| 378 | _overlays.buildVBO(_textureFov, _textureAspectRatio, 80, 80); | ||
| 379 | } | ||
| 380 | 125 | ||
| 381 | with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { | ||
| 382 | _overlays.render(); | ||
| 383 | }); | ||
| 384 | |||
| 385 | if (!Application::getInstance()->isMouseHidden()) { | ||
| 386 | renderPointersOculus(); | ||
| 387 | } | ||
| 388 | glDepthMask(GL_TRUE); | ||
| 389 | glDisable(GL_TEXTURE_2D); | ||
| 390 | |||
| 391 | glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); | ||
| 392 | glEnable(GL_LIGHTING); | ||
| 393 | } glPopMatrix(); | ||
| 394 | } | ||
| 395 | |||
| 396 | // Draws the FBO texture for 3DTV. | ||
| 397 | void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov) { | ||
| 398 | if (_alpha == 0.0f) { | ||
| 399 | return; | ||
| 400 | } | ||
| 401 | |||
| 402 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | ||
| 403 | const glm::vec3& viewMatrixTranslation = qApp->getViewMatrixTranslation(); | ||
| 404 | |||
| 405 | glEnable(GL_BLEND); | ||
| 406 | glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); | ||
| 407 | glEnable(GL_DEPTH_TEST); | ||
| 408 | glDisable(GL_LIGHTING); | ||
| 409 | |||
| 410 | glMatrixMode(GL_MODELVIEW); | ||
| 411 | |||
| 412 | glPushMatrix(); | ||
| 413 | glLoadIdentity(); | ||
| 414 | // Transform to world space | ||
| 415 | glm::quat rotation = whichCamera.getRotation(); | ||
| 416 | glm::vec3 axis2 = glm::axis(rotation); | ||
| 417 | glRotatef(-glm::degrees(glm::angle(rotation)), axis2.x, axis2.y, axis2.z); | ||
| 418 | glTranslatef(viewMatrixTranslation.x, viewMatrixTranslation.y, viewMatrixTranslation.z); | ||
| 419 | |||
| 420 | // Translate to the front of the camera | ||
| 421 | glm::vec3 pos = whichCamera.getPosition(); | ||
| 422 | glm::quat rot = myAvatar->getOrientation(); | ||
| 423 | glm::vec3 axis = glm::axis(rot); | ||
| 424 | |||
| 425 | glTranslatef(pos.x, pos.y, pos.z); | ||
| 426 | glRotatef(glm::degrees(glm::angle(rot)), axis.x, axis.y, axis.z); | ||
| 427 | |||
| 428 | glm::vec4 overlayColor = {1.0f, 1.0f, 1.0f, _alpha}; | ||
| 429 | |||
| 430 | //Render | ||
| 431 | const GLfloat distance = 1.0f; | ||
| 432 | |||
| 433 | const GLfloat halfQuadHeight = distance * tan(fov); | ||
| 434 | const GLfloat halfQuadWidth = halfQuadHeight * aspectRatio; | ||
| 435 | const GLfloat quadWidth = halfQuadWidth * 2.0f; | ||
| 436 | const GLfloat quadHeight = halfQuadHeight * 2.0f; | ||
| 437 | |||
| 438 | GLfloat x = -halfQuadWidth; | ||
| 439 | GLfloat y = -halfQuadHeight; | ||
| 440 | glDisable(GL_DEPTH_TEST); | 126 | glDisable(GL_DEPTH_TEST); |
| 441 | 127 | glDisable(GL_LIGHTING); | |
| 442 | with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { | ||
| 443 | DependencyManager::get<GeometryCache>()->renderQuad(glm::vec3(x, y + quadHeight, -distance), | ||
| 444 | glm::vec3(x + quadWidth, y + quadHeight, -distance), | ||
| 445 | glm::vec3(x + quadWidth, y, -distance), | ||
| 446 | glm::vec3(x, y, -distance), | ||
| 447 | glm::vec2(0.0f, 1.0f), glm::vec2(1.0f, 1.0f), | ||
| 448 | glm::vec2(1.0f, 0.0f), glm::vec2(0.0f, 0.0f), | ||
| 449 | overlayColor); | ||
| 450 | }); | ||
| 451 | |||
| 452 | if (!_crosshairTexture) { | ||
| 453 | _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + | ||
| 454 | "images/sixense-reticle.png"); | ||
| 455 | } | ||
| 456 | |||
| 457 | //draw the mouse pointer | ||
| 458 | glEnable(GL_BLEND); | 128 | glEnable(GL_BLEND); |
| 459 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | 129 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
| 460 | glEnable(GL_TEXTURE_2D); | ||
| 461 | glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); | ||
| 462 | glm::vec2 canvasSize = qApp->getCanvasSize(); | ||
| 463 | const float reticleSize = 40.0f / canvasSize.x * quadWidth; | ||
| 464 | x -= reticleSize / 2.0f; | ||
| 465 | y += reticleSize / 2.0f; | ||
| 466 | const float mouseX = (qApp->getMouseX() / (float)canvasSize.x) * quadWidth; | ||
| 467 | const float mouseY = (1.0 - (qApp->getMouseY() / (float)canvasSize.y)) * quadHeight; | ||
| 468 | |||
| 469 | glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; | ||
| 470 | 130 | ||
| 471 | DependencyManager::get<GeometryCache>()->renderQuad(glm::vec3(x + mouseX, y + mouseY, -distance), | 131 | // give external parties a change to hook in |
| 472 | glm::vec3(x + mouseX + reticleSize, y + mouseY, -distance), | 132 | emit qApp->renderingOverlay(); |
| 473 | glm::vec3(x + mouseX + reticleSize, y + mouseY - reticleSize, -distance), | 133 | qApp->getOverlays().renderHUD(renderArgs); |
| 474 | glm::vec3(x + mouseX, y + mouseY - reticleSize, -distance), | ||
| 475 | glm::vec2(0.0f, 0.0f), glm::vec2(1.0f, 0.0f), | ||
| 476 | glm::vec2(1.0f, 1.0f), glm::vec2(0.0f, 1.0f), | ||
| 477 | reticleColor, _reticleQuad); | ||
| 478 | 134 | ||
| 479 | glEnable(GL_DEPTH_TEST); | 135 | glMatrixMode(GL_PROJECTION); |
| 480 | |||
| 481 | glPopMatrix(); | 136 | glPopMatrix(); |
| 482 | |||
| 483 | glDepthMask(GL_TRUE); | ||
| 484 | glBindTexture(GL_TEXTURE_2D, 0); | ||
| 485 | glDisable(GL_TEXTURE_2D); | ||
| 486 | |||
| 487 | glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); | ||
| 488 | glEnable(GL_LIGHTING); | ||
| 489 | } | ||
| 490 | |||
| 491 | void ApplicationOverlay::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const { | ||
| 492 | cursorPos *= qApp->getCanvasSize(); | ||
| 493 | const glm::vec2 projection = screenToSpherical(cursorPos); | ||
| 494 | // The overlay space orientation of the mouse coordinates | ||
| 495 | const glm::quat orientation(glm::vec3(-projection.y, projection.x, 0.0f)); | ||
| 496 | // FIXME We now have the direction of the ray FROM THE DEFAULT HEAD POSE. | ||
| 497 | // Now we need to account for the actual camera position relative to the overlay | ||
| 498 | glm::vec3 overlaySpaceDirection = glm::normalize(orientation * IDENTITY_FRONT); | ||
| 499 | |||
| 500 | |||
| 501 | const glm::vec3& hmdPosition = qApp->getCamera()->getHmdPosition(); | ||
| 502 | const glm::quat& hmdOrientation = qApp->getCamera()->getHmdRotation(); | ||
| 503 | |||
| 504 | // We need the RAW camera orientation and position, because this is what the overlay is | ||
| 505 | // rendered relative to | ||
| 506 | const glm::vec3 overlayPosition = qApp->getCamera()->getPosition() - hmdPosition; | ||
| 507 | const glm::quat overlayOrientation = qApp->getCamera()->getRotation() * glm::inverse(hmdOrientation); | ||
| 508 | |||
| 509 | // Intersection UI overlay space | ||
| 510 | glm::vec3 worldSpaceDirection = overlayOrientation * overlaySpaceDirection; | ||
| 511 | glm::vec3 intersectionWithUi = glm::normalize(worldSpaceDirection) * _oculusUIRadius; | ||
| 512 | intersectionWithUi += overlayPosition; | ||
| 513 | |||
| 514 | // Intersection in world space | ||
| 515 | origin = overlayPosition + hmdPosition; | ||
| 516 | direction = glm::normalize(intersectionWithUi - origin); | ||
| 517 | } | ||
| 518 | |||
| 519 | glm::vec2 getPolarCoordinates(const PalmData& palm) { | ||
| 520 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | ||
| 521 | auto avatarOrientation = myAvatar->getOrientation(); | ||
| 522 | auto eyePos = myAvatar->getDefaultEyePosition(); | ||
| 523 | glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm); | ||
| 524 | // Direction of the tip relative to the eye | ||
| 525 | glm::vec3 tipDirection = tip - eyePos; | ||
| 526 | // orient into avatar space | ||
| 527 | tipDirection = glm::inverse(avatarOrientation) * tipDirection; | ||
| 528 | // Normalize for trig functions | ||
| 529 | tipDirection = glm::normalize(tipDirection); | ||
| 530 | // Convert to polar coordinates | ||
| 531 | glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y)); | ||
| 532 | return polar; | ||
| 533 | } | ||
| 534 | |||
| 535 | //Caculate the click location using one of the sixense controllers. Scale is not applied | ||
| 536 | QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const { | ||
| 537 | QPoint rv; | ||
| 538 | auto canvasSize = qApp->getCanvasSize(); | ||
| 539 | if (qApp->isHMDMode()) { | ||
| 540 | glm::vec2 polar = getPolarCoordinates(*palm); | ||
| 541 | glm::vec2 point = sphericalToScreen(-polar); | ||
| 542 | rv.rx() = point.x; | ||
| 543 | rv.ry() = point.y; | ||
| 544 | } else { | ||
| 545 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | ||
| 546 | glm::dmat4 projection; | ||
| 547 | qApp->getProjectionMatrix(&projection); | ||
| 548 | glm::quat invOrientation = glm::inverse(myAvatar->getOrientation()); | ||
| 549 | glm::vec3 eyePos = myAvatar->getDefaultEyePosition(); | ||
| 550 | glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm); | ||
| 551 | glm::vec3 tipPos = invOrientation * (tip - eyePos); | ||
| 552 | |||
| 553 | glm::vec4 clipSpacePos = glm::vec4(projection * glm::dvec4(tipPos, 1.0)); | ||
| 554 | glm::vec3 ndcSpacePos; | ||
| 555 | if (clipSpacePos.w != 0) { | ||
| 556 | ndcSpacePos = glm::vec3(clipSpacePos) / clipSpacePos.w; | ||
| 557 | } | ||
| 558 | |||
| 559 | rv.setX(((ndcSpacePos.x + 1.0) / 2.0) * canvasSize.x); | ||
| 560 | rv.setY((1.0 - ((ndcSpacePos.y + 1.0) / 2.0)) * canvasSize.y); | ||
| 561 | } | ||
| 562 | return rv; | ||
| 563 | } | ||
| 564 | |||
| 565 | //Finds the collision point of a world space ray | ||
| 566 | bool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const { | ||
| 567 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | ||
| 568 | |||
| 569 | glm::quat inverseOrientation = glm::inverse(myAvatar->getOrientation()); | ||
| 570 | |||
| 571 | glm::vec3 relativePosition = inverseOrientation * (position - myAvatar->getDefaultEyePosition()); | ||
| 572 | glm::vec3 relativeDirection = glm::normalize(inverseOrientation * direction); | ||
| 573 | |||
| 574 | float t; | ||
| 575 | if (raySphereIntersect(relativeDirection, relativePosition, _oculusUIRadius * myAvatar->getScale(), &t)){ | ||
| 576 | result = position + direction * t; | ||
| 577 | return true; | ||
| 578 | } | ||
| 579 | |||
| 580 | return false; | ||
| 581 | } | ||
| 582 | |||
| 583 | //Renders optional pointers | ||
| 584 | void ApplicationOverlay::renderPointers() { | ||
| 585 | //lazily load crosshair texture | ||
| 586 | if (_crosshairTexture == 0) { | ||
| 587 | _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); | ||
| 588 | } | ||
| 589 | glEnable(GL_TEXTURE_2D); | ||
| 590 | glEnable(GL_BLEND); | ||
| 591 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||
| 592 | |||
| 593 | //glActiveTexture(GL_TEXTURE0); | ||
| 594 | //bindCursorTexture(); | ||
| 595 | |||
| 596 | if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) { | ||
| 597 | //If we are in oculus, render reticle later | ||
| 598 | if (_lastMouseMove == 0) { | ||
| 599 | _lastMouseMove = usecTimestampNow(); | ||
| 600 | } | ||
| 601 | QPoint position = QPoint(qApp->getTrueMouseX(), qApp->getTrueMouseY()); | ||
| 602 | |||
| 603 | static const int MAX_IDLE_TIME = 3; | ||
| 604 | if (_reticlePosition[MOUSE] != position) { | ||
| 605 | _lastMouseMove = usecTimestampNow(); | ||
| 606 | } else if (usecTimestampNow() - _lastMouseMove > MAX_IDLE_TIME * USECS_PER_SECOND) { | ||
| 607 | //float pitch = 0.0f, yaw = 0.0f, roll = 0.0f; // radians | ||
| 608 | //OculusManager::getEulerAngles(yaw, pitch, roll); | ||
| 609 | glm::quat orientation = qApp->getHeadOrientation(); // (glm::vec3(pitch, yaw, roll)); | ||
| 610 | glm::vec3 result; | ||
| 611 | |||
| 612 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | ||
| 613 | if (calculateRayUICollisionPoint(myAvatar->getEyePosition(), | ||
| 614 | myAvatar->getOrientation() * orientation * IDENTITY_FRONT, | ||
| 615 | result)) { | ||
| 616 | glm::vec3 lookAtDirection = glm::inverse(myAvatar->getOrientation()) * (result - myAvatar->getDefaultEyePosition()); | ||
| 617 | glm::vec2 spericalPos = directionToSpherical(glm::normalize(lookAtDirection)); | ||
| 618 | glm::vec2 screenPos = sphericalToScreen(spericalPos); | ||
| 619 | position = QPoint(screenPos.x, screenPos.y); | ||
| 620 | // FIXME | ||
| 621 | //glCanvas->cursor().setPos(glCanvas->mapToGlobal(position)); | ||
| 622 | } else { | ||
| 623 | qDebug() << "No collision point"; | ||
| 624 | } | ||
| 625 | } | ||
| 626 | |||
| 627 | _reticlePosition[MOUSE] = position; | ||
| 628 | _reticleActive[MOUSE] = true; | ||
| 629 | _magActive[MOUSE] = _magnifier; | ||
| 630 | _reticleActive[LEFT_CONTROLLER] = false; | ||
| 631 | _reticleActive[RIGHT_CONTROLLER] = false; | ||
| 632 | } else if (qApp->getLastMouseMoveWasSimulated() && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { | ||
| 633 | _lastMouseMove = 0; | ||
| 634 | //only render controller pointer if we aren't already rendering a mouse pointer | ||
| 635 | _reticleActive[MOUSE] = false; | ||
| 636 | _magActive[MOUSE] = false; | ||
| 637 | renderControllerPointers(); | ||
| 638 | } | ||
| 639 | //glBindTexture(GL_TEXTURE_2D, 0); | ||
| 640 | //glDisable(GL_TEXTURE_2D); | ||
| 641 | } | ||
| 642 | |||
| 643 | void ApplicationOverlay::renderControllerPointers() { | ||
| 644 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | ||
| 645 | |||
| 646 | //Static variables used for storing controller state | ||
| 647 | static quint64 pressedTime[NUMBER_OF_RETICLES] = { 0ULL, 0ULL, 0ULL }; | ||
| 648 | static bool isPressed[NUMBER_OF_RETICLES] = { false, false, false }; | ||
| 649 | static bool stateWhenPressed[NUMBER_OF_RETICLES] = { false, false, false }; | ||
| 650 | |||
| 651 | const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData(); | ||
| 652 | |||
| 653 | for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) { | ||
| 654 | const int index = palmIndex - 1; | ||
| 655 | |||
| 656 | const PalmData* palmData = NULL; | ||
| 657 | |||
| 658 | if (palmIndex >= handData->getPalms().size()) { | ||
| 659 | return; | ||
| 660 | } | ||
| 661 | |||
| 662 | if (handData->getPalms()[palmIndex].isActive()) { | ||
| 663 | palmData = &handData->getPalms()[palmIndex]; | ||
| 664 | } else { | ||
| 665 | continue; | ||
| 666 | } | ||
| 667 | |||
| 668 | int controllerButtons = palmData->getControllerButtons(); | ||
| 669 | |||
| 670 | //Check for if we should toggle or drag the magnification window | ||
| 671 | if (controllerButtons & BUTTON_3) { | ||
| 672 | if (isPressed[index] == false) { | ||
| 673 | //We are now dragging the window | ||
| 674 | isPressed[index] = true; | ||
| 675 | //set the pressed time in us | ||
| 676 | pressedTime[index] = usecTimestampNow(); | ||
| 677 | stateWhenPressed[index] = _magActive[index]; | ||
| 678 | } | ||
| 679 | } else if (isPressed[index]) { | ||
| 680 | isPressed[index] = false; | ||
| 681 | //If the button was only pressed for < 250 ms | ||
| 682 | //then disable it. | ||
| 683 | |||
| 684 | const int MAX_BUTTON_PRESS_TIME = 250 * MSECS_TO_USECS; | ||
| 685 | if (usecTimestampNow() < pressedTime[index] + MAX_BUTTON_PRESS_TIME) { | ||
| 686 | _magActive[index] = !stateWhenPressed[index]; | ||
| 687 | } | ||
| 688 | } | ||
| 689 | |||
| 690 | //if we have the oculus, we should make the cursor smaller since it will be | ||
| 691 | //magnified | ||
| 692 | if (qApp->isHMDMode()) { | ||
| 693 | |||
| 694 | QPoint point = getPalmClickLocation(palmData); | ||
| 695 | |||
| 696 | _reticlePosition[index] = point; | ||
| 697 | |||
| 698 | //When button 2 is pressed we drag the mag window | ||
| 699 | if (isPressed[index]) { | ||
| 700 | _magActive[index] = true; | ||
| 701 | } | ||
| 702 | |||
| 703 | // If oculus is enabled, we draw the crosshairs later | ||
| 704 | continue; | ||
| 705 | } | ||
| 706 | |||
| 707 | auto canvasSize = qApp->getCanvasSize(); | ||
| 708 | int mouseX, mouseY; | ||
| 709 | if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { | ||
| 710 | QPoint res = getPalmClickLocation(palmData); | ||
| 711 | mouseX = res.x(); | ||
| 712 | mouseY = res.y(); | ||
| 713 | } else { | ||
| 714 | // Get directon relative to avatar orientation | ||
| 715 | glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); | ||
| 716 | |||
| 717 | // Get the angles, scaled between (-0.5,0.5) | ||
| 718 | float xAngle = (atan2(direction.z, direction.x) + M_PI_2); | ||
| 719 | float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); | ||
| 720 | |||
| 721 | // Get the pixel range over which the xAngle and yAngle are scaled | ||
| 722 | float cursorRange = canvasSize.x * SixenseManager::getInstance().getCursorPixelRangeMult(); | ||
| 723 | |||
| 724 | mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle); | ||
| 725 | mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle); | ||
| 726 | } | ||
| 727 | |||
| 728 | //If the cursor is out of the screen then don't render it | ||
| 729 | if (mouseX < 0 || mouseX >= (int)canvasSize.x || mouseY < 0 || mouseY >= (int)canvasSize.y) { | ||
| 730 | _reticleActive[index] = false; | ||
| 731 | continue; | ||
| 732 | } | ||
| 733 | _reticleActive[index] = true; | ||
| 734 | |||
| 735 | |||
| 736 | const float reticleSize = 40.0f; | ||
| 737 | |||
| 738 | mouseX -= reticleSize / 2.0f; | ||
| 739 | mouseY += reticleSize / 2.0f; | ||
| 740 | |||
| 741 | |||
| 742 | glm::vec2 topLeft(mouseX, mouseY); | ||
| 743 | glm::vec2 bottomRight(mouseX + reticleSize, mouseY - reticleSize); | ||
| 744 | glm::vec2 texCoordTopLeft(0.0f, 0.0f); | ||
| 745 | glm::vec2 texCoordBottomRight(1.0f, 1.0f); | ||
| 746 | |||
| 747 | DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, | ||
| 748 | glm::vec4(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f)); | ||
| 749 | |||
| 750 | } | ||
| 751 | } | ||
| 752 | |||
| 753 | void ApplicationOverlay::renderPointersOculus() { | ||
| 754 | |||
| 755 | glEnable(GL_BLEND); | ||
| 756 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||
| 757 | glEnable(GL_TEXTURE_2D); | ||
| 758 | |||
| 759 | glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); | ||
| 760 | glDisable(GL_DEPTH_TEST); | ||
| 761 | |||
| 762 | glMatrixMode(GL_MODELVIEW); | 137 | glMatrixMode(GL_MODELVIEW); |
| 763 | |||
| 764 | //Controller Pointers | ||
| 765 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | ||
| 766 | for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) { | ||
| 767 | PalmData& palm = myAvatar->getHand()->getPalms()[i]; | ||
| 768 | if (palm.isActive()) { | ||
| 769 | glm::vec2 polar = getPolarCoordinates(palm); | ||
| 770 | // Convert to quaternion | ||
| 771 | glm::quat orientation = glm::quat(glm::vec3(polar.y, -polar.x, 0.0f)); | ||
| 772 | // Render reticle at location | ||
| 773 | renderReticle(orientation, _alpha); | ||
| 774 | } | ||
| 775 | } | ||
| 776 | |||
| 777 | //Mouse Pointer | ||
| 778 | if (_reticleActive[MOUSE]) { | ||
| 779 | glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(), | ||
| 780 | _reticlePosition[MOUSE].y())); | ||
| 781 | glm::quat orientation(glm::vec3(-projection.y, projection.x, 0.0f)); | ||
| 782 | renderReticle(orientation, _alpha); | ||
| 783 | } | ||
| 784 | 138 | ||
| 785 | glEnable(GL_DEPTH_TEST); | 139 | renderArgs->_context->syncCache(); |
| 786 | glDisable(GL_TEXTURE_2D); | 140 | fboViewport(_overlayFramebuffer); |
| 787 | glDisable(GL_BLEND); | ||
| 788 | } | 141 | } |
| 789 | 142 | ||
| 790 | //Renders a small magnification of the currently bound texture at the coordinates | 143 | void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) { |
| 791 | void ApplicationOverlay::renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder) { | ||
| 792 | if (!_magnifier) { | ||
| 793 | return; | ||
| 794 | } | ||
| 795 | auto canvasSize = qApp->getCanvasSize(); | ||
| 796 | |||
| 797 | const int widgetWidth = canvasSize.x; | ||
| 798 | const int widgetHeight = canvasSize.y; | ||
| 799 | |||
| 800 | const float halfWidth = (MAGNIFY_WIDTH / _textureAspectRatio) * sizeMult / 2.0f; | ||
| 801 | const float halfHeight = MAGNIFY_HEIGHT * sizeMult / 2.0f; | ||
| 802 | // Magnification Texture Coordinates | ||
| 803 | const float magnifyULeft = (magPos.x - halfWidth) / (float)widgetWidth; | ||
| 804 | const float magnifyURight = (magPos.x + halfWidth) / (float)widgetWidth; | ||
| 805 | const float magnifyVTop = 1.0f - (magPos.y - halfHeight) / (float)widgetHeight; | ||
| 806 | const float magnifyVBottom = 1.0f - (magPos.y + halfHeight) / (float)widgetHeight; | ||
| 807 | |||
| 808 | const float newHalfWidth = halfWidth * MAGNIFY_MULT; | ||
| 809 | const float newHalfHeight = halfHeight * MAGNIFY_MULT; | ||
| 810 | //Get yaw / pitch value for the corners | ||
| 811 | const glm::vec2 topLeftYawPitch = overlayToSpherical(glm::vec2(magPos.x - newHalfWidth, | ||
| 812 | magPos.y - newHalfHeight)); | ||
| 813 | const glm::vec2 bottomRightYawPitch = overlayToSpherical(glm::vec2(magPos.x + newHalfWidth, | ||
| 814 | magPos.y + newHalfHeight)); | ||
| 815 | |||
| 816 | const glm::vec3 bottomLeft = getPoint(topLeftYawPitch.x, bottomRightYawPitch.y); | ||
| 817 | const glm::vec3 bottomRight = getPoint(bottomRightYawPitch.x, bottomRightYawPitch.y); | ||
| 818 | const glm::vec3 topLeft = getPoint(topLeftYawPitch.x, topLeftYawPitch.y); | ||
| 819 | const glm::vec3 topRight = getPoint(bottomRightYawPitch.x, topLeftYawPitch.y); | ||
| 820 | |||
| 821 | auto geometryCache = DependencyManager::get<GeometryCache>(); | ||
| 822 | |||
| 823 | if (bottomLeft != _previousMagnifierBottomLeft || bottomRight != _previousMagnifierBottomRight | ||
| 824 | || topLeft != _previousMagnifierTopLeft || topRight != _previousMagnifierTopRight) { | ||
| 825 | QVector<glm::vec3> border; | ||
| 826 | border << topLeft; | ||
| 827 | border << bottomLeft; | ||
| 828 | border << bottomRight; | ||
| 829 | border << topRight; | ||
| 830 | border << topLeft; | ||
| 831 | geometryCache->updateVertices(_magnifierBorder, border, glm::vec4(1.0f, 0.0f, 0.0f, _alpha)); | ||
| 832 | |||
| 833 | _previousMagnifierBottomLeft = bottomLeft; | ||
| 834 | _previousMagnifierBottomRight = bottomRight; | ||
| 835 | _previousMagnifierTopLeft = topLeft; | ||
| 836 | _previousMagnifierTopRight = topRight; | ||
| 837 | } | ||
| 838 | |||
| 839 | glPushMatrix(); { | ||
| 840 | if (showBorder) { | ||
| 841 | glDisable(GL_TEXTURE_2D); | ||
| 842 | glLineWidth(1.0f); | ||
| 843 | //Outer Line | ||
| 844 | geometryCache->renderVertices(gpu::LINE_STRIP, _magnifierBorder); | ||
| 845 | glEnable(GL_TEXTURE_2D); | ||
| 846 | } | ||
| 847 | glm::vec4 magnifierColor = { 1.0f, 1.0f, 1.0f, _alpha }; | ||
| 848 | |||
| 849 | DependencyManager::get<GeometryCache>()->renderQuad(bottomLeft, bottomRight, topRight, topLeft, | ||
| 850 | glm::vec2(magnifyULeft, magnifyVBottom), | ||
| 851 | glm::vec2(magnifyURight, magnifyVBottom), | ||
| 852 | glm::vec2(magnifyURight, magnifyVTop), | ||
| 853 | glm::vec2(magnifyULeft, magnifyVTop), | ||
| 854 | magnifierColor, _magnifierQuad); | ||
| 855 | |||
| 856 | } glPopMatrix(); | ||
| 857 | } | 144 | } |
| 858 | 145 | ||
| 859 | const int AUDIO_METER_GAP = 5; | 146 | void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) { |
| 860 | const int MUTE_ICON_PADDING = 10; | ||
| 861 | |||
| 862 | void ApplicationOverlay::renderCameraToggle() { | ||
| 863 | if (Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)) { | ||
| 864 | return; | ||
| 865 | } | ||
| 866 | |||
| 867 | int audioMeterY; | ||
| 868 | bool smallMirrorVisible = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !qApp->isHMDMode(); | ||
| 869 | bool boxed = smallMirrorVisible && | ||
| 870 | !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror); | ||
| 871 | if (boxed) { | ||
| 872 | audioMeterY = MIRROR_VIEW_HEIGHT + AUDIO_METER_GAP + MUTE_ICON_PADDING; | ||
| 873 | } else { | ||
| 874 | audioMeterY = AUDIO_METER_GAP + MUTE_ICON_PADDING; | ||
| 875 | } | ||
| 876 | |||
| 877 | DependencyManager::get<CameraToolBox>()->render(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, boxed); | ||
| 878 | } | 147 | } |
| 879 | 148 | ||
| 880 | void ApplicationOverlay::renderAudioMeter() { | 149 | void ApplicationOverlay::renderStatsAndLogs(RenderArgs* renderArgs) { |
| 881 | auto audio = DependencyManager::get<AudioClient>(); | ||
| 882 | |||
| 883 | // Audio VU Meter and Mute Icon | ||
| 884 | const int MUTE_ICON_SIZE = 24; | ||
| 885 | const int AUDIO_METER_HEIGHT = 8; | ||
| 886 | const int INTER_ICON_GAP = 2; | ||
| 887 | |||
| 888 | int cameraSpace = 0; | ||
| 889 | int audioMeterWidth = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - MUTE_ICON_PADDING; | ||
| 890 | int audioMeterScaleWidth = audioMeterWidth - 2; | ||
| 891 | int audioMeterX = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_GAP; | ||
| 892 | if (!Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)) { | ||
| 893 | cameraSpace = MUTE_ICON_SIZE + INTER_ICON_GAP; | ||
| 894 | audioMeterWidth -= cameraSpace; | ||
| 895 | audioMeterScaleWidth -= cameraSpace; | ||
| 896 | audioMeterX += cameraSpace; | ||
| 897 | } | ||
| 898 | |||
| 899 | int audioMeterY; | ||
| 900 | bool smallMirrorVisible = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !qApp->isHMDMode(); | ||
| 901 | bool boxed = smallMirrorVisible && | ||
| 902 | !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror); | ||
| 903 | if (boxed) { | ||
| 904 | audioMeterY = MIRROR_VIEW_HEIGHT + AUDIO_METER_GAP + MUTE_ICON_PADDING; | ||
| 905 | } else { | ||
| 906 | audioMeterY = AUDIO_METER_GAP + MUTE_ICON_PADDING; | ||
| 907 | } | ||
| 908 | |||
| 909 | const glm::vec4 AUDIO_METER_BLUE = { 0.0, 0.0, 1.0, 1.0 }; | ||
| 910 | const glm::vec4 AUDIO_METER_GREEN = { 0.0, 1.0, 0.0, 1.0 }; | ||
| 911 | const glm::vec4 AUDIO_METER_RED = { 1.0, 0.0, 0.0, 1.0 }; | ||
| 912 | const float CLIPPING_INDICATOR_TIME = 1.0f; | ||
| 913 | const float AUDIO_METER_AVERAGING = 0.5; | ||
| 914 | const float LOG2 = log(2.0f); | ||
| 915 | const float METER_LOUDNESS_SCALE = 2.8f / 5.0f; | ||
| 916 | const float LOG2_LOUDNESS_FLOOR = 11.0f; | ||
| 917 | float audioGreenStart = 0.25f * audioMeterScaleWidth; | ||
| 918 | float audioRedStart = 0.8f * audioMeterScaleWidth; | ||
| 919 | float audioLevel = 0.0f; | ||
| 920 | float loudness = audio->getLastInputLoudness() + 1.0f; | ||
| 921 | |||
| 922 | _trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.0f - AUDIO_METER_AVERAGING) * loudness; | ||
| 923 | float log2loudness = log(_trailingAudioLoudness) / LOG2; | ||
| 924 | |||
| 925 | if (log2loudness <= LOG2_LOUDNESS_FLOOR) { | ||
| 926 | audioLevel = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE * audioMeterScaleWidth; | ||
| 927 | } else { | ||
| 928 | audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.0f)) * METER_LOUDNESS_SCALE * audioMeterScaleWidth; | ||
| 929 | } | ||
| 930 | if (audioLevel > audioMeterScaleWidth) { | ||
| 931 | audioLevel = audioMeterScaleWidth; | ||
| 932 | } | ||
| 933 | bool isClipping = ((audio->getTimeSinceLastClip() > 0.0f) && (audio->getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)); | ||
| 934 | |||
| 935 | DependencyManager::get<AudioToolBox>()->render(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, cameraSpace, boxed); | ||
| 936 | |||
| 937 | auto canvasSize = qApp->getCanvasSize(); | ||
| 938 | DependencyManager::get<AudioScope>()->render(canvasSize.x, canvasSize.y); | ||
| 939 | DependencyManager::get<AudioIOStatsRenderer>()->render(WHITE_TEXT, canvasSize.x, canvasSize.y); | ||
| 940 | |||
| 941 | audioMeterY += AUDIO_METER_HEIGHT; | ||
| 942 | |||
| 943 | // Draw audio meter background Quad | ||
| 944 | DependencyManager::get<GeometryCache>()->renderQuad(audioMeterX, audioMeterY, audioMeterWidth, AUDIO_METER_HEIGHT, | ||
| 945 | glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); | ||
| 946 | |||
| 947 | if (audioLevel > audioRedStart) { | ||
| 948 | glm::vec4 quadColor; | ||
| 949 | if (!isClipping) { | ||
| 950 | quadColor = AUDIO_METER_RED; | ||
| 951 | } else { | ||
| 952 | quadColor = glm::vec4(1, 1, 1, 1); | ||
| 953 | } | ||
| 954 | // Draw Red Quad | ||
| 955 | DependencyManager::get<GeometryCache>()->renderQuad(audioMeterX + audioRedStart, | ||
| 956 | audioMeterY, | ||
| 957 | audioLevel - audioRedStart, | ||
| 958 | AUDIO_METER_HEIGHT, quadColor, | ||
| 959 | _audioRedQuad); | ||
| 960 | |||
| 961 | audioLevel = audioRedStart; | ||
| 962 | } | ||
| 963 | |||
| 964 | if (audioLevel > audioGreenStart) { | ||
| 965 | glm::vec4 quadColor; | ||
| 966 | if (!isClipping) { | ||
| 967 | quadColor = AUDIO_METER_GREEN; | ||
| 968 | } else { | ||
| 969 | quadColor = glm::vec4(1, 1, 1, 1); | ||
| 970 | } | ||
| 971 | // Draw Green Quad | ||
| 972 | DependencyManager::get<GeometryCache>()->renderQuad(audioMeterX + audioGreenStart, | ||
| 973 | audioMeterY, | ||
| 974 | audioLevel - audioGreenStart, | ||
| 975 | AUDIO_METER_HEIGHT, quadColor, | ||
| 976 | _audioGreenQuad); | ||
| 977 | |||
| 978 | audioLevel = audioGreenStart; | ||
| 979 | } | ||
| 980 | |||
| 981 | if (audioLevel >= 0) { | ||
| 982 | glm::vec4 quadColor; | ||
| 983 | if (!isClipping) { | ||
| 984 | quadColor = AUDIO_METER_BLUE; | ||
| 985 | } else { | ||
| 986 | quadColor = glm::vec4(1, 1, 1, 1); | ||
| 987 | } | ||
| 988 | // Draw Blue (low level) quad | ||
| 989 | DependencyManager::get<GeometryCache>()->renderQuad(audioMeterX, | ||
| 990 | audioMeterY, | ||
| 991 | audioLevel, AUDIO_METER_HEIGHT, quadColor, | ||
| 992 | _audioBlueQuad); | ||
| 993 | } | ||
| 994 | } | ||
| 995 | |||
| 996 | void ApplicationOverlay::renderStatsAndLogs() { | ||
| 997 | Application* application = Application::getInstance(); | ||
| 998 | QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>(); | ||
| 999 | |||
| 1000 | const OctreePacketProcessor& octreePacketProcessor = application->getOctreePacketProcessor(); | ||
| 1001 | NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay(); | ||
| 1002 | |||
| 1003 | // Display stats and log text onscreen | 150 | // Display stats and log text onscreen |
| 1004 | glLineWidth(1.0f); | ||
| 1005 | glPointSize(1.0f); | ||
| 1006 | 151 | ||
| 1007 | // Determine whether to compute timing details | 152 | // Determine whether to compute timing details |
| 1008 | bool shouldDisplayTimingDetail = Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) && | ||
| 1009 | Menu::getInstance()->isOptionChecked(MenuOption::Stats) && | ||
| 1010 | Stats::getInstance()->isExpanded(); | ||
| 1011 | if (shouldDisplayTimingDetail != PerformanceTimer::isActive()) { | ||
| 1012 | PerformanceTimer::setActive(shouldDisplayTimingDetail); | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { | ||
| 1016 | // let's set horizontal offset to give stats some margin to mirror | ||
| 1017 | int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; | ||
| 1018 | int voxelPacketsToProcess = octreePacketProcessor.packetsToProcessCount(); | ||
| 1019 | // Onscreen text about position, servers, etc | ||
| 1020 | Stats::getInstance()->display(WHITE_TEXT, horizontalOffset, application->getFps(), | ||
| 1021 | bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond(), | ||
| 1022 | bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond(), | ||
| 1023 | bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond(), | ||
| 1024 | bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond(), | ||
| 1025 | voxelPacketsToProcess); | ||
| 1026 | } | ||
| 1027 | 153 | ||
| 154 | /* | ||
| 1028 | // Show on-screen msec timer | 155 | // Show on-screen msec timer |
| 1029 | if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) { | 156 | if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) { |
| 1030 | auto canvasSize = qApp->getCanvasSize(); | 157 | auto canvasSize = qApp->getCanvasSize(); |
| @@ -1036,159 +163,77 @@ void ApplicationOverlay::renderStatsAndLogs() { · 5 unmodified lines | |||
| 1036 | drawText(canvasSize.x - 100, canvasSize.y - timerBottom, | 163 | drawText(canvasSize.x - 100, canvasSize.y - timerBottom, |
| 1037 | 0.30f, 0.0f, 0, frameTimer.toUtf8().constData(), WHITE_TEXT); | 164 | 0.30f, 0.0f, 0, frameTimer.toUtf8().constData(), WHITE_TEXT); |
| 1038 | } | 165 | } |
| 166 | |||
| 167 | glPointSize(1.0f); | ||
| 168 | glDisable(GL_DEPTH_TEST); | ||
| 169 | glDisable(GL_LIGHTING); | ||
| 170 | glEnable(GL_BLEND); | ||
| 171 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||
| 172 | NodeBounds& nodeBoundsDisplay = qApp->getNodeBoundsDisplay(); | ||
| 1039 | nodeBoundsDisplay.drawOverlay(); | 173 | nodeBoundsDisplay.drawOverlay(); |
| 174 | glEnable(GL_DEPTH_TEST); | ||
| 175 | glEnable(GL_LIGHTING); | ||
| 176 | glEnable(GL_BLEND); | ||
| 177 | glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); | ||
| 178 | fboViewport(_overlayFramebuffer); | ||
| 179 | */ | ||
| 1040 | } | 180 | } |
| 1041 | 181 | ||
| 1042 | void ApplicationOverlay::renderDomainConnectionStatusBorder() { | 182 | void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderArgs) { |
| 183 | auto geometryCache = DependencyManager::get<GeometryCache>(); | ||
| 184 | static std::once_flag once; | ||
| 185 | std::call_once(once, [&] { | ||
| 186 | QVector<vec2> points; | ||
| 187 | static const float B = 0.99f; | ||
| 188 | points.push_back(vec2(-B)); | ||
| 189 | points.push_back(vec2(B, -B)); | ||
| 190 | points.push_back(vec2(B)); | ||
| 191 | points.push_back(vec2(-B, B)); | ||
| 192 | points.push_back(vec2(-B)); | ||
| 193 | geometryCache->updateVertices(_domainStatusBorder, points, CONNECTION_STATUS_BORDER_COLOR); | ||
| 194 | }); | ||
| 1043 | auto nodeList = DependencyManager::get<NodeList>(); | 195 | auto nodeList = DependencyManager::get<NodeList>(); |
| 1044 | |||
| 1045 | if (nodeList && !nodeList->getDomainHandler().isConnected()) { | 196 | if (nodeList && !nodeList->getDomainHandler().isConnected()) { |
| 197 | gpu::Batch batch; | ||
| 1046 | auto geometryCache = DependencyManager::get<GeometryCache>(); | 198 | auto geometryCache = DependencyManager::get<GeometryCache>(); |
| 1047 | auto canvasSize = qApp->getCanvasSize(); | 199 | geometryCache->useSimpleDrawPipeline(batch); |
| 1048 | if ((int)canvasSize.x != _previousBorderWidth || (int)canvasSize.y != _previousBorderHeight) { | 200 | batch.setProjectionTransform(mat4()); |
| 1049 | glm::vec4 color(CONNECTION_STATUS_BORDER_COLOR[0], | 201 | batch.setModelTransform(mat4()); |
| 1050 | CONNECTION_STATUS_BORDER_COLOR[1], | 202 | batch.setUniformTexture(0, DependencyManager::get<TextureCache>()->getWhiteTexture()); |
| 1051 | CONNECTION_STATUS_BORDER_COLOR[2], 1.0f); | 203 | batch._glLineWidth(CONNECTION_STATUS_BORDER_LINE_WIDTH); |
| 1052 | |||
| 1053 | QVector<glm::vec2> border; | ||
| 1054 | border << glm::vec2(0, 0); | ||
| 1055 | border << glm::vec2(0, canvasSize.y); | ||
| 1056 | border << glm::vec2(canvasSize.x, canvasSize.y); | ||
| 1057 | border << glm::vec2(canvasSize.x, 0); | ||
| 1058 | border << glm::vec2(0, 0); | ||
| 1059 | geometryCache->updateVertices(_domainStatusBorder, border, color); | ||
| 1060 | _previousBorderWidth = canvasSize.x; | ||
| 1061 | _previousBorderHeight = canvasSize.y; | ||
| 1062 | } | ||
| 1063 | |||
| 1064 | glLineWidth(CONNECTION_STATUS_BORDER_LINE_WIDTH); | ||
| 1065 | |||
| 1066 | geometryCache->renderVertices(gpu::LINE_STRIP, _domainStatusBorder); | ||
| 1067 | } | ||
| 1068 | } | ||
| 1069 | 204 | ||
| 1070 | ApplicationOverlay::TexturedHemisphere::TexturedHemisphere() : | 205 | // TODO animate the disconnect border for some excitement while not connected? |
| 1071 | _vertices(0), | 206 | //double usecs = usecTimestampNow(); |
| 1072 | _indices(0), | 207 | //double secs = usecs / 1000000.0; |
| 1073 | _framebufferObject(NULL), | 208 | //float scaleAmount = 1.0f + (0.01f * sin(secs * 5.0f)); |
| 1074 | _vbo(0, 0) { | 209 | //batch.setModelTransform(glm::scale(mat4(), vec3(scaleAmount))); |
| 1075 | } | ||
| 1076 | |||
| 1077 | ApplicationOverlay::TexturedHemisphere::~TexturedHemisphere() { | ||
| 1078 | cleanupVBO(); | ||
| 1079 | if (_framebufferObject != NULL) { | ||
| 1080 | delete _framebufferObject; | ||
| 1081 | } | ||
| 1082 | } | ||
| 1083 | |||
| 1084 | void ApplicationOverlay::TexturedHemisphere::bind() { | ||
| 1085 | _framebufferObject->bind(); | ||
| 1086 | } | ||
| 1087 | |||
| 1088 | void ApplicationOverlay::TexturedHemisphere::release() { | ||
| 1089 | _framebufferObject->release(); | ||
| 1090 | } | ||
| 1091 | 210 | ||
| 1092 | void ApplicationOverlay::TexturedHemisphere::buildVBO(const float fov, | 211 | geometryCache->renderVertices(batch, gpu::LINE_STRIP, _domainStatusBorder); |
| 1093 | const float aspectRatio, | 212 | renderArgs->_context->syncCache(); |
| 1094 | const int slices, | 213 | renderArgs->_context->render(batch); |
| 1095 | const int stacks) { | ||
| 1096 | if (fov >= PI) { | ||
| 1097 | qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues"; | ||
| 1098 | } | ||
| 1099 | // Cleanup old VBO if necessary | ||
| 1100 | cleanupVBO(); | ||
| 1101 | |||
| 1102 | //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm | ||
| 1103 | |||
| 1104 | // Compute number of vertices needed | ||
| 1105 | _vertices = slices * stacks; | ||
| 1106 | |||
| 1107 | // Compute vertices positions and texture UV coordinate | ||
| 1108 | TextureVertex* vertexData = new TextureVertex[_vertices]; | ||
| 1109 | TextureVertex* vertexPtr = &vertexData[0]; | ||
| 1110 | for (int i = 0; i < stacks; i++) { | ||
| 1111 | float stacksRatio = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f | ||
| 1112 | // abs(theta) <= fov / 2.0f | ||
| 1113 | float pitch = -fov * (stacksRatio - 0.5f); | ||
| 1114 | |||
| 1115 | for (int j = 0; j < slices; j++) { | ||
| 1116 | float slicesRatio = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f | ||
| 1117 | // abs(phi) <= fov * aspectRatio / 2.0f | ||
| 1118 | float yaw = -fov * aspectRatio * (slicesRatio - 0.5f); | ||
| 1119 | |||
| 1120 | vertexPtr->position = getPoint(yaw, pitch); | ||
| 1121 | vertexPtr->uv.x = slicesRatio; | ||
| 1122 | vertexPtr->uv.y = stacksRatio; | ||
| 1123 | vertexPtr++; | ||
| 1124 | } | ||
| 1125 | } | ||
| 1126 | // Create and write to buffer | ||
| 1127 | glGenBuffers(1, &_vbo.first); | ||
| 1128 | glBindBuffer(GL_ARRAY_BUFFER, _vbo.first); | ||
| 1129 | static const int BYTES_PER_VERTEX = sizeof(TextureVertex); | ||
| 1130 | glBufferData(GL_ARRAY_BUFFER, _vertices * BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW); | ||
| 1131 | delete[] vertexData; | ||
| 1132 | |||
| 1133 | |||
| 1134 | // Compute number of indices needed | ||
| 1135 | static const int VERTEX_PER_TRANGLE = 3; | ||
| 1136 | static const int TRIANGLE_PER_RECTANGLE = 2; | ||
| 1137 | int numberOfRectangles = (slices - 1) * (stacks - 1); | ||
| 1138 | _indices = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE; | ||
| 1139 | |||
| 1140 | // Compute indices order | ||
| 1141 | GLushort* indexData = new GLushort[_indices]; | ||
| 1142 | GLushort* indexPtr = indexData; | ||
| 1143 | for (int i = 0; i < stacks - 1; i++) { | ||
| 1144 | for (int j = 0; j < slices - 1; j++) { | ||
| 1145 | GLushort bottomLeftIndex = i * slices + j; | ||
| 1146 | GLushort bottomRightIndex = bottomLeftIndex + 1; | ||
| 1147 | GLushort topLeftIndex = bottomLeftIndex + slices; | ||
| 1148 | GLushort topRightIndex = topLeftIndex + 1; | ||
| 1149 | |||
| 1150 | *(indexPtr++) = topLeftIndex; | ||
| 1151 | *(indexPtr++) = bottomLeftIndex; | ||
| 1152 | *(indexPtr++) = topRightIndex; | ||
| 1153 | |||
| 1154 | *(indexPtr++) = topRightIndex; | ||
| 1155 | *(indexPtr++) = bottomLeftIndex; | ||
| 1156 | *(indexPtr++) = bottomRightIndex; | ||
| 1157 | } | ||
| 1158 | } | 214 | } |
| 1159 | // Create and write to buffer | ||
| 1160 | glGenBuffers(1, &_vbo.second); | ||
| 1161 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo.second); | ||
| 1162 | static const int BYTES_PER_INDEX = sizeof(GLushort); | ||
| 1163 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indices * BYTES_PER_INDEX, indexData, GL_STATIC_DRAW); | ||
| 1164 | delete[] indexData; | ||
| 1165 | } | 215 | } |
| 1166 | 216 | ||
| 1167 | void ApplicationOverlay::TexturedHemisphere::cleanupVBO() { | 217 | GLuint ApplicationOverlay::getOverlayTexture() { |
| 1168 | if (_vbo.first != 0) { | 218 | if (!_overlayFramebuffer) { |
| 1169 | glDeleteBuffers(1, &_vbo.first); | 219 | return 0; |
| 1170 | _vbo.first = 0; | ||
| 1171 | } | ||
| 1172 | if (_vbo.second != 0) { | ||
| 1173 | glDeleteBuffers(1, &_vbo.second); | ||
| 1174 | _vbo.second = 0; | ||
| 1175 | } | 220 | } |
| 221 | return _overlayFramebuffer->texture(); | ||
| 1176 | } | 222 | } |
| 1177 | 223 | ||
| 1178 | void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() { | 224 | void ApplicationOverlay::buildFramebufferObject() { |
| 1179 | auto canvasSize = qApp->getCanvasSize(); | 225 | QSize fboSize = qApp->getDeviceSize(); |
| 1180 | QSize fboSize = QSize(canvasSize.x, canvasSize.y); | 226 | if (_overlayFramebuffer && fboSize == _overlayFramebuffer->size()) { |
| 1181 | if (_framebufferObject != NULL && fboSize == _framebufferObject->size()) { | 227 | // Already built |
| 1182 | // Already build | ||
| 1183 | return; | 228 | return; |
| 1184 | } | 229 | } |
| 1185 | 230 | ||
| 1186 | if (_framebufferObject != NULL) { | 231 | if (_overlayFramebuffer) { |
| 1187 | delete _framebufferObject; | 232 | delete _overlayFramebuffer; |
| 1188 | } | 233 | } |
| 1189 | 234 | ||
| 1190 | _framebufferObject = new QOpenGLFramebufferObject(fboSize, QOpenGLFramebufferObject::Depth); | 235 | _overlayFramebuffer = new QOpenGLFramebufferObject(fboSize, QOpenGLFramebufferObject::Depth); |
| 1191 | glBindTexture(GL_TEXTURE_2D, getTexture()); | 236 | glBindTexture(GL_TEXTURE_2D, getOverlayTexture()); |
| 1192 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | 237 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| 1193 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | 238 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| 1194 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); | 239 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); |
| @@ -1197,104 +242,3 @@ void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() { · 2 unmodified lines | |||
| 1197 | glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); | 242 | glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); |
| 1198 | glBindTexture(GL_TEXTURE_2D, 0); | 243 | glBindTexture(GL_TEXTURE_2D, 0); |
| 1199 | } | 244 | } |
| 1200 | |||
| 1201 | //Renders a hemisphere with texture coordinates. | ||
| 1202 | void ApplicationOverlay::TexturedHemisphere::render() { | ||
| 1203 | if (_framebufferObject == NULL || _vbo.first == 0 || _vbo.second == 0) { | ||
| 1204 | qDebug() << "TexturedHemisphere::render(): Incorrect initialisation"; | ||
| 1205 | return; | ||
| 1206 | } | ||
| 1207 | |||
| 1208 | glBindBuffer(GL_ARRAY_BUFFER, _vbo.first); | ||
| 1209 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo.second); | ||
| 1210 | |||
| 1211 | glEnableClientState(GL_VERTEX_ARRAY); | ||
| 1212 | glEnableClientState(GL_TEXTURE_COORD_ARRAY); | ||
| 1213 | |||
| 1214 | static const int STRIDE = sizeof(TextureVertex); | ||
| 1215 | static const void* VERTEX_POINTER = 0; | ||
| 1216 | static const void* TEX_COORD_POINTER = (void*)sizeof(glm::vec3); | ||
| 1217 | glVertexPointer(3, GL_FLOAT, STRIDE, VERTEX_POINTER); | ||
| 1218 | glTexCoordPointer(2, GL_FLOAT, STRIDE, TEX_COORD_POINTER); | ||
| 1219 | |||
| 1220 | glDrawRangeElements(GL_TRIANGLES, 0, _vertices - 1, _indices, GL_UNSIGNED_SHORT, 0); | ||
| 1221 | |||
| 1222 | glDisableClientState(GL_VERTEX_ARRAY); | ||
| 1223 | glDisableClientState(GL_TEXTURE_COORD_ARRAY); | ||
| 1224 | |||
| 1225 | glBindBuffer(GL_ARRAY_BUFFER, 0); | ||
| 1226 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); | ||
| 1227 | } | ||
| 1228 | |||
| 1229 | GLuint ApplicationOverlay::TexturedHemisphere::getTexture() { | ||
| 1230 | return _framebufferObject->texture(); | ||
| 1231 | } | ||
| 1232 | |||
| 1233 | glm::vec2 ApplicationOverlay::directionToSpherical(const glm::vec3& direction) { | ||
| 1234 | glm::vec2 result; | ||
| 1235 | // Compute yaw | ||
| 1236 | glm::vec3 normalProjection = glm::normalize(glm::vec3(direction.x, 0.0f, direction.z)); | ||
| 1237 | result.x = glm::acos(glm::dot(IDENTITY_FRONT, normalProjection)); | ||
| 1238 | if (glm::dot(IDENTITY_RIGHT, normalProjection) > 0.0f) { | ||
| 1239 | result.x = -glm::abs(result.x); | ||
| 1240 | } else { | ||
| 1241 | result.x = glm::abs(result.x); | ||
| 1242 | } | ||
| 1243 | // Compute pitch | ||
| 1244 | result.y = angleBetween(IDENTITY_UP, direction) - PI_OVER_TWO; | ||
| 1245 | |||
| 1246 | return result; | ||
| 1247 | } | ||
| 1248 | |||
| 1249 | glm::vec3 ApplicationOverlay::sphericalToDirection(const glm::vec2& sphericalPos) { | ||
| 1250 | glm::quat rotation(glm::vec3(sphericalPos.y, sphericalPos.x, 0.0f)); | ||
| 1251 | return rotation * IDENTITY_FRONT; | ||
| 1252 | } | ||
| 1253 | |||
| 1254 | glm::vec2 ApplicationOverlay::screenToSpherical(const glm::vec2& screenPos) { | ||
| 1255 | auto screenSize = qApp->getCanvasSize(); | ||
| 1256 | glm::vec2 result; | ||
| 1257 | result.x = -(screenPos.x / screenSize.x - 0.5f); | ||
| 1258 | result.y = (screenPos.y / screenSize.y - 0.5f); | ||
| 1259 | result.x *= MOUSE_YAW_RANGE; | ||
| 1260 | result.y *= MOUSE_PITCH_RANGE; | ||
| 1261 | |||
| 1262 | return result; | ||
| 1263 | } | ||
| 1264 | |||
| 1265 | glm::vec2 ApplicationOverlay::sphericalToScreen(const glm::vec2& sphericalPos) { | ||
| 1266 | glm::vec2 result = sphericalPos; | ||
| 1267 | result.x *= -1.0; | ||
| 1268 | result /= MOUSE_RANGE; | ||
| 1269 | result += 0.5f; | ||
| 1270 | result *= qApp->getCanvasSize(); | ||
| 1271 | return result; | ||
| 1272 | } | ||
| 1273 | |||
| 1274 | glm::vec2 ApplicationOverlay::sphericalToOverlay(const glm::vec2& sphericalPos) const { | ||
| 1275 | glm::vec2 result = sphericalPos; | ||
| 1276 | result.x *= -1.0; | ||
| 1277 | result /= _textureFov; | ||
| 1278 | result.x /= _textureAspectRatio; | ||
| 1279 | result += 0.5f; | ||
| 1280 | result *= qApp->getCanvasSize(); | ||
| 1281 | return result; | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | glm::vec2 ApplicationOverlay::overlayToSpherical(const glm::vec2& overlayPos) const { | ||
| 1285 | glm::vec2 result = overlayPos; | ||
| 1286 | result /= qApp->getCanvasSize(); | ||
| 1287 | result -= 0.5f; | ||
| 1288 | result *= _textureFov; | ||
| 1289 | result.x *= _textureAspectRatio; | ||
| 1290 | result.x *= -1.0f; | ||
| 1291 | return result; | ||
| 1292 | } | ||
| 1293 | |||
| 1294 | glm::vec2 ApplicationOverlay::screenToOverlay(const glm::vec2& screenPos) const { | ||
| 1295 | return sphericalToOverlay(screenToSpherical(screenPos)); | ||
| 1296 | } | ||
| 1297 | |||
| 1298 | glm::vec2 ApplicationOverlay::overlayToScreen(const glm::vec2& overlayPos) const { | ||
| 1299 | return sphericalToScreen(overlayToSpherical(overlayPos)); | ||
| 1300 | } | ||
| @@ -13,16 +13,8 @@ · 12 unmodified lines | |||
| 13 | #define hifi_ApplicationOverlay_h | 13 | #define hifi_ApplicationOverlay_h |
| 14 | 14 | ||
| 15 | #include <gpu/Texture.h> | 15 | #include <gpu/Texture.h> |
| 16 | class Camera; | ||
| 17 | class Overlays; | ||
| 18 | class QOpenGLFramebufferObject; | 16 | class QOpenGLFramebufferObject; |
| 19 | 17 | ||
| 20 | const float MAGNIFY_WIDTH = 220.0f; | ||
| 21 | const float MAGNIFY_HEIGHT = 100.0f; | ||
| 22 | const float MAGNIFY_MULT = 2.0f; | ||
| 23 | |||
| 24 | const float DEFAULT_HMD_UI_ANGULAR_SIZE = 72.0f; | ||
| 25 | |||
| 26 | // Handles the drawing of the overlays to the screen | 18 | // Handles the drawing of the overlays to the screen |
| 27 | // TODO, move divide up the rendering, displaying and input handling | 19 | // TODO, move divide up the rendering, displaying and input handling |
| 28 | // facilities of this class | 20 | // facilities of this class |
| @@ -33,122 +25,26 @@ public: · 4 unmodified lines | |||
| 33 | ~ApplicationOverlay(); | 25 | ~ApplicationOverlay(); |
| 34 | 26 | ||
| 35 | void renderOverlay(RenderArgs* renderArgs); | 27 | void renderOverlay(RenderArgs* renderArgs); |
| 36 | void displayOverlayTexture(); | 28 | GLuint getOverlayTexture(); |
| 37 | void displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov); | ||
| 38 | void displayOverlayTextureHmd(Camera& whichCamera); | ||
| 39 | |||
| 40 | QPoint getPalmClickLocation(const PalmData *palm) const; | ||
| 41 | bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const; | ||
| 42 | |||
| 43 | bool hasMagnifier() const { return _magnifier; } | ||
| 44 | void toggleMagnifier() { _magnifier = !_magnifier; } | ||
| 45 | 29 | ||
| 46 | float getHmdUIAngularSize() const { return _hmdUIAngularSize; } | ||
| 47 | void setHmdUIAngularSize(float hmdUIAngularSize) { _hmdUIAngularSize = hmdUIAngularSize; } | ||
| 48 | |||
| 49 | // Converter from one frame of reference to another. | ||
| 50 | // Frame of reference: | ||
| 51 | // Direction: Ray that represents the spherical values | ||
| 52 | // Screen: Position on the screen (x,y) | ||
| 53 | // Spherical: Pitch and yaw that gives the position on the sphere we project on (yaw,pitch) | ||
| 54 | // Overlay: Position on the overlay (x,y) | ||
| 55 | // (x,y) in Overlay are similar than (x,y) in Screen except they can be outside of the bound of te screen. | ||
| 56 | // This allows for picking outside of the screen projection in 3D. | ||
| 57 | glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const; | ||
| 58 | glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const; | ||
| 59 | glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const; | ||
| 60 | glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const; | ||
| 61 | void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const; | ||
| 62 | |||
| 63 | static glm::vec2 directionToSpherical(const glm::vec3 & direction); | ||
| 64 | static glm::vec3 sphericalToDirection(const glm::vec2 & sphericalPos); | ||
| 65 | static glm::vec2 screenToSpherical(const glm::vec2 & screenPos); | ||
| 66 | static glm::vec2 sphericalToScreen(const glm::vec2 & sphericalPos); | ||
| 67 | |||
| 68 | private: | 30 | private: |
| 69 | // Interleaved vertex data | 31 | void renderStatsAndLogs(RenderArgs* renderArgs); |
| 70 | struct TextureVertex { | 32 | void renderDomainConnectionStatusBorder(RenderArgs* renderArgs); |
| 71 | glm::vec3 position; | 33 | void renderRearViewToFbo(RenderArgs* renderArgs); |
| 72 | glm::vec2 uv; | 34 | void renderRearView(RenderArgs* renderArgs); |
| 73 | }; | 35 | void renderQmlUi(RenderArgs* renderArgs); |
| 74 | 36 | void renderOverlays(RenderArgs* renderArgs); | |
| 75 | typedef QPair<GLuint, GLuint> VerticesIndices; | 37 | void buildFramebufferObject(); |
| 76 | class TexturedHemisphere { | 38 | |
| 77 | public: | 39 | float _alpha{ 1.0f }; |
| 78 | TexturedHemisphere(); | 40 | float _trailingAudioLoudness{ 0.0f }; |
| 79 | ~TexturedHemisphere(); | 41 | GLuint _uiTexture{ 0 }; |
| 80 | |||
| 81 | void bind(); | ||
| 82 | void release(); | ||
| 83 | GLuint getTexture(); | ||
| 84 | |||
| 85 | void buildFramebufferObject(); | ||
| 86 | void buildVBO(const float fov, const float aspectRatio, const int slices, const int stacks); | ||
| 87 | void render(); | ||
| 88 | |||
| 89 | private: | ||
| 90 | void cleanupVBO(); | ||
| 91 | |||
| 92 | GLuint _vertices; | ||
| 93 | GLuint _indices; | ||
| 94 | QOpenGLFramebufferObject* _framebufferObject; | ||
| 95 | VerticesIndices _vbo; | ||
| 96 | }; | ||
| 97 | |||
| 98 | float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE; | ||
| 99 | void renderReticle(glm::quat orientation, float alpha); | ||
| 100 | void renderPointers();; | ||
| 101 | void renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder); | ||
| 102 | |||
| 103 | void renderControllerPointers(); | ||
| 104 | void renderPointersOculus(); | ||
| 105 | |||
| 106 | void renderAudioMeter(); | ||
| 107 | void renderCameraToggle(); | ||
| 108 | void renderStatsAndLogs(); | ||
| 109 | void renderDomainConnectionStatusBorder(); | ||
| 110 | void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0); | ||
| 111 | 42 | ||
| 112 | TexturedHemisphere _overlays; | ||
| 113 | |||
| 114 | float _textureFov; | ||
| 115 | float _textureAspectRatio; | ||
| 116 | |||
| 117 | enum Reticles { MOUSE, LEFT_CONTROLLER, RIGHT_CONTROLLER, NUMBER_OF_RETICLES }; | ||
| 118 | bool _reticleActive[NUMBER_OF_RETICLES]; | ||
| 119 | QPoint _reticlePosition[NUMBER_OF_RETICLES]; | ||
| 120 | bool _magActive[NUMBER_OF_RETICLES]; | ||
| 121 | float _magSizeMult[NUMBER_OF_RETICLES]; | ||
| 122 | quint64 _lastMouseMove; | ||
| 123 | bool _magnifier; | ||
| 124 | |||
| 125 | float _alpha = 1.0f; | ||
| 126 | float _oculusUIRadius; | ||
| 127 | float _trailingAudioLoudness; | ||
| 128 | |||
| 129 | gpu::TexturePointer _crosshairTexture; | ||
| 130 | |||
| 131 | |||
| 132 | QMap<uint16_t, gpu::TexturePointer> _cursors; | ||
| 133 | |||
| 134 | GLuint _newUiTexture{ 0 }; | ||
| 135 | |||
| 136 | int _reticleQuad; | ||
| 137 | int _magnifierQuad; | ||
| 138 | int _audioRedQuad; | ||
| 139 | int _audioGreenQuad; | ||
| 140 | int _audioBlueQuad; | ||
| 141 | int _domainStatusBorder; | 43 | int _domainStatusBorder; |
| 142 | int _magnifierBorder; | 44 | int _magnifierBorder; |
| 143 | 45 | ||
| 144 | int _previousBorderWidth; | 46 | ivec2 _previousBorderSize{ -1 }; |
| 145 | int _previousBorderHeight; | 47 | QOpenGLFramebufferObject* _overlayFramebuffer{ nullptr }; |
| 146 | |||
| 147 | glm::vec3 _previousMagnifierBottomLeft; | ||
| 148 | glm::vec3 _previousMagnifierBottomRight; | ||
| 149 | glm::vec3 _previousMagnifierTopLeft; | ||
| 150 | glm::vec3 _previousMagnifierTopRight; | ||
| 151 | |||
| 152 | }; | 48 | }; |
| 153 | 49 | ||
| 154 | #endif // hifi_ApplicationOverlay_h | 50 | #endif // hifi_ApplicationOverlay_h |
| @@ -0,0 +1,134 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/06/19 | ||
| 3 | // Copyright 2013 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | |||
| 10 | #include "Application.h" | ||
| 11 | #include "AvatarInputs.h" | ||
| 12 | |||
| 13 | #include <SettingHandle.h> | ||
| 14 | |||
| 15 | #include "Menu.h" | ||
| 16 | #include "devices/FaceTracker.h" | ||
| 17 | |||
| 18 | HIFI_QML_DEF(AvatarInputs) | ||
| 19 | |||
| 20 | |||
| 21 | static AvatarInputs* INSTANCE{ nullptr }; | ||
| 22 | static const char SETTINGS_GROUP_NAME[] = "Rear View Tools"; | ||
| 23 | static const char ZOOM_LEVEL_SETTINGS[] = "ZoomLevel"; | ||
| 24 | |||
| 25 | static Setting::Handle<int> rearViewZoomLevel(QStringList() << SETTINGS_GROUP_NAME << ZOOM_LEVEL_SETTINGS, 0); | ||
| 26 | |||
| 27 | AvatarInputs* AvatarInputs::getInstance() { | ||
| 28 | if (!INSTANCE) { | ||
| 29 | AvatarInputs::registerType(); | ||
| 30 | AvatarInputs::show(); | ||
| 31 | Q_ASSERT(INSTANCE); | ||
| 32 | } | ||
| 33 | return INSTANCE; | ||
| 34 | } | ||
| 35 | |||
| 36 | AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) { | ||
| 37 | INSTANCE = this; | ||
| 38 | int zoomSetting = rearViewZoomLevel.get(); | ||
| 39 | _mirrorZoomed = zoomSetting == 0; | ||
| 40 | } | ||
| 41 | |||
| 42 | #define AI_UPDATE(name, src) \ | ||
| 43 | { \ | ||
| 44 | auto val = src; \ | ||
| 45 | if (_##name != val) { \ | ||
| 46 | _##name = val; \ | ||
| 47 | emit name##Changed(); \ | ||
| 48 | } \ | ||
| 49 | } | ||
| 50 | |||
| 51 | #define AI_UPDATE_FLOAT(name, src, epsilon) \ | ||
| 52 | { \ | ||
| 53 | float val = src; \ | ||
| 54 | if (fabs(_##name - val) >= epsilon) { \ | ||
| 55 | _##name = val; \ | ||
| 56 | emit name##Changed(); \ | ||
| 57 | } \ | ||
| 58 | } | ||
| 59 | |||
| 60 | void AvatarInputs::update() { | ||
| 61 | if (!Menu::getInstance()) { | ||
| 62 | return; | ||
| 63 | } | ||
| 64 | AI_UPDATE(mirrorVisible, Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !qApp->isHMDMode() | ||
| 65 | && !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)); | ||
| 66 | AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)); | ||
| 67 | AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking)); | ||
| 68 | |||
| 69 | auto audioIO = DependencyManager::get<AudioClient>(); | ||
| 70 | const float CLIPPING_INDICATOR_TIME = 1.0f; | ||
| 71 | const float AUDIO_METER_AVERAGING = 0.5; | ||
| 72 | const float LOG2 = log(2.0f); | ||
| 73 | const float METER_LOUDNESS_SCALE = 2.8f / 5.0f; | ||
| 74 | const float LOG2_LOUDNESS_FLOOR = 11.0f; | ||
| 75 | float audioLevel = 0.0f; | ||
| 76 | auto audio = DependencyManager::get<AudioClient>(); | ||
| 77 | float loudness = audio->getLastInputLoudness() + 1.0f; | ||
| 78 | |||
| 79 | _trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.0f - AUDIO_METER_AVERAGING) * loudness; | ||
| 80 | |||
| 81 | float log2loudness = logf(_trailingAudioLoudness) / LOG2; | ||
| 82 | |||
| 83 | if (log2loudness <= LOG2_LOUDNESS_FLOOR) { | ||
| 84 | audioLevel = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE; | ||
| 85 | } else { | ||
| 86 | audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.0f)) * METER_LOUDNESS_SCALE; | ||
| 87 | } | ||
| 88 | if (audioLevel > 1.0) { | ||
| 89 | audioLevel = 1.0; | ||
| 90 | } | ||
| 91 | AI_UPDATE_FLOAT(audioLevel, audioLevel, 0.01); | ||
| 92 | AI_UPDATE(audioClipping, ((audioIO->getTimeSinceLastClip() > 0.0f) && (audioIO->getTimeSinceLastClip() < 1.0f))); | ||
| 93 | AI_UPDATE(audioMuted, audioIO->isMuted()); | ||
| 94 | |||
| 95 | //// Make muted icon pulsate | ||
| 96 | //static const float PULSE_MIN = 0.4f; | ||
| 97 | //static const float PULSE_MAX = 1.0f; | ||
| 98 | //static const float PULSE_FREQUENCY = 1.0f; // in Hz | ||
| 99 | //qint64 now = usecTimestampNow(); | ||
| 100 | //if (now - _iconPulseTimeReference > (qint64)USECS_PER_SECOND) { | ||
| 101 | // // Prevents t from getting too big, which would diminish glm::cos precision | ||
| 102 | // _iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND); | ||
| 103 | //} | ||
| 104 | //float t = (float)(now - _iconPulseTimeReference) / (float)USECS_PER_SECOND; | ||
| 105 | //float pulseFactor = (glm::cos(t * PULSE_FREQUENCY * 2.0f * PI) + 1.0f) / 2.0f; | ||
| 106 | //iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor; | ||
| 107 | } | ||
| 108 | |||
| 109 | void AvatarInputs::toggleCameraMute() { | ||
| 110 | FaceTracker* faceTracker = Application::getInstance()->getSelectedFaceTracker(); | ||
| 111 | if (faceTracker) { | ||
| 112 | faceTracker->toggleMute(); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | void AvatarInputs::toggleAudioMute() { | ||
| 117 | DependencyManager::get<AudioClient>()->toggleMute(); | ||
| 118 | } | ||
| 119 | |||
| 120 | void AvatarInputs::resetSensors() { | ||
| 121 | qApp->resetSensors(); | ||
| 122 | } | ||
| 123 | |||
| 124 | void AvatarInputs::toggleZoom() { | ||
| 125 | _mirrorZoomed = !_mirrorZoomed; | ||
| 126 | rearViewZoomLevel.set(_mirrorZoomed ? 0 : 1); | ||
| 127 | emit mirrorZoomedChanged(); | ||
| 128 | } | ||
| 129 | |||
| 130 | void AvatarInputs::closeMirror() { | ||
| 131 | if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { | ||
| 132 | Menu::getInstance()->triggerOption(MenuOption::Mirror); | ||
| 133 | } | ||
| 134 | } | ||
| @@ -0,0 +1,59 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/06/19 | ||
| 3 | // Copyright 2013 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #ifndef hifi_AvatarInputs_h | ||
| 10 | #define hifi_AvatarInputs_h | ||
| 11 | |||
| 12 | #include <QQuickItem> | ||
| 13 | #include <OffscreenUi.h> | ||
| 14 | |||
| 15 | #define AI_PROPERTY(type, name, initialValue) \ | ||
| 16 | Q_PROPERTY(type name READ name NOTIFY name##Changed) \ | ||
| 17 | public: \ | ||
| 18 | type name() { return _##name; }; \ | ||
| 19 | private: \ | ||
| 20 | type _##name{ initialValue }; | ||
| 21 | |||
| 22 | class AvatarInputs : public QQuickItem { | ||
| 23 | Q_OBJECT | ||
| 24 | HIFI_QML_DECL | ||
| 25 | |||
| 26 | AI_PROPERTY(bool, cameraEnabled, false) | ||
| 27 | AI_PROPERTY(bool, cameraMuted, false) | ||
| 28 | AI_PROPERTY(bool, audioMuted, false) | ||
| 29 | AI_PROPERTY(bool, audioClipping, false) | ||
| 30 | AI_PROPERTY(float, audioLevel, 0) | ||
| 31 | AI_PROPERTY(bool, mirrorVisible, false) | ||
| 32 | AI_PROPERTY(bool, mirrorZoomed, true) | ||
| 33 | |||
| 34 | public: | ||
| 35 | static AvatarInputs* getInstance(); | ||
| 36 | AvatarInputs(QQuickItem* parent = nullptr); | ||
| 37 | void update(); | ||
| 38 | |||
| 39 | signals: | ||
| 40 | void cameraEnabledChanged(); | ||
| 41 | void cameraMutedChanged(); | ||
| 42 | void audioMutedChanged(); | ||
| 43 | void audioClippingChanged(); | ||
| 44 | void audioLevelChanged(); | ||
| 45 | void mirrorVisibleChanged(); | ||
| 46 | void mirrorZoomedChanged(); | ||
| 47 | |||
| 48 | protected: | ||
| 49 | Q_INVOKABLE void resetSensors(); | ||
| 50 | Q_INVOKABLE void toggleCameraMute(); | ||
| 51 | Q_INVOKABLE void toggleAudioMute(); | ||
| 52 | Q_INVOKABLE void toggleZoom(); | ||
| 53 | Q_INVOKABLE void closeMirror(); | ||
| 54 | |||
| 55 | private: | ||
| 56 | float _trailingAudioLoudness{ 0 }; | ||
| 57 | }; | ||
| 58 | |||
| 59 | #endif // hifi_AvatarInputs_h | ||
| @@ -38,8 +38,17 @@ · 37 unmodified lines | |||
| 38 | ** | 38 | ** |
| 39 | ****************************************************************************/ | 39 | ****************************************************************************/ |
| 40 | 40 | ||
| 41 | #ifdef __GNUC__ | ||
| 42 | #pragma GCC diagnostic push | ||
| 43 | #pragma GCC diagnostic ignored "-Wdouble-promotion" | ||
| 44 | #endif | ||
| 45 | |||
| 41 | #include <QtWidgets> | 46 | #include <QtWidgets> |
| 42 | 47 | ||
| 48 | #ifdef __GNUC__ | ||
| 49 | #pragma GCC diagnostic pop | ||
| 50 | #endif | ||
| 51 | |||
| 43 | #include "FlowLayout.h" | 52 | #include "FlowLayout.h" |
| 44 | //! [1] | 53 | //! [1] |
| 45 | FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) | 54 | FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) |
| @@ -56,8 +56,6 @@ HMDToolsDialog::HMDToolsDialog(QWidget* parent) : · 55 unmodified lines | |||
| 56 | 56 | ||
| 57 | this->QDialog::setLayout(form); | 57 | this->QDialog::setLayout(form); |
| 58 | 58 | ||
| 59 | _wasMoved = false; | ||
| 60 | _previousRect = Application::getInstance()->getWindow()->rect(); | ||
| 61 | Application::getInstance()->getWindow()->activateWindow(); | 59 | Application::getInstance()->getWindow()->activateWindow(); |
| 62 | 60 | ||
| 63 | // watch for our application window moving screens. If it does we want to update our screen details | 61 | // watch for our application window moving screens. If it does we want to update our screen details |
| @@ -136,24 +134,6 @@ void HMDToolsDialog::enterHDMMode() { · 72 unmodified lines | |||
| 136 | if (!_inHDMMode) { | 134 | if (!_inHDMMode) { |
| 137 | _switchModeButton->setText("Leave HMD Mode"); | 135 | _switchModeButton->setText("Leave HMD Mode"); |
| 138 | _debugDetails->setText(getDebugDetails()); | 136 | _debugDetails->setText(getDebugDetails()); |
| 139 | |||
| 140 | _hmdScreenNumber = OculusManager::getHMDScreen(); | ||
| 141 | |||
| 142 | if (_hmdScreenNumber >= 0) { | ||
| 143 | QWindow* mainWindow = Application::getInstance()->getWindow()->windowHandle(); | ||
| 144 | _hmdScreen = QGuiApplication::screens()[_hmdScreenNumber]; | ||
| 145 | |||
| 146 | _previousRect = Application::getInstance()->getWindow()->rect(); | ||
| 147 | _previousRect = QRect(mainWindow->mapToGlobal(_previousRect.topLeft()), | ||
| 148 | mainWindow->mapToGlobal(_previousRect.bottomRight())); | ||
| 149 | _previousScreen = mainWindow->screen(); | ||
| 150 | QRect rect = QApplication::desktop()->screenGeometry(_hmdScreenNumber); | ||
| 151 | mainWindow->setScreen(_hmdScreen); | ||
| 152 | mainWindow->setGeometry(rect); | ||
| 153 | |||
| 154 | _wasMoved = true; | ||
| 155 | } | ||
| 156 | |||
| 157 | 137 | ||
| 158 | // if we're on a single screen setup, then hide our tools window when entering HMD mode | 138 | // if we're on a single screen setup, then hide our tools window when entering HMD mode |
| 159 | if (QApplication::desktop()->screenCount() == 1) { | 139 | if (QApplication::desktop()->screenCount() == 1) { |
| @@ -161,58 +141,21 @@ void HMDToolsDialog::enterHDMMode() { · 1 unmodified line | |||
| 161 | } | 141 | } |
| 162 | 142 | ||
| 163 | Application::getInstance()->setEnableVRMode(true); | 143 | Application::getInstance()->setEnableVRMode(true); |
| 164 | 144 | ||
| 165 | const int SLIGHT_DELAY = 500; | ||
| 166 | // If we go to fullscreen immediately, it ends up on the primary monitor, | ||
| 167 | // even though we've already moved the window. By adding this delay, the | ||
| 168 | // fullscreen target screen ends up correct. | ||
| 169 | QTimer::singleShot(SLIGHT_DELAY, this, [&]{ | ||
| 170 | Application::getInstance()->setFullscreen(true); | ||
| 171 | activateWindowAfterEnterMode(); | ||
| 172 | }); | ||
| 173 | |||
| 174 | _inHDMMode = true; | 145 | _inHDMMode = true; |
| 175 | } | 146 | } |
| 176 | } | 147 | } |
| 177 | 148 | ||
| 178 | void HMDToolsDialog::activateWindowAfterEnterMode() { | ||
| 179 | Application::getInstance()->getWindow()->activateWindow(); | ||
| 180 | |||
| 181 | // center the cursor on the main application window | ||
| 182 | centerCursorOnWidget(Application::getInstance()->getWindow()); | ||
| 183 | } | ||
| 184 | |||
| 185 | void HMDToolsDialog::leaveHDMMode() { | 149 | void HMDToolsDialog::leaveHDMMode() { |
| 186 | if (_inHDMMode) { | 150 | if (_inHDMMode) { |
| 187 | _switchModeButton->setText("Enter HMD Mode"); | 151 | _switchModeButton->setText("Enter HMD Mode"); |
| 188 | _debugDetails->setText(getDebugDetails()); | 152 | _debugDetails->setText(getDebugDetails()); |
| 189 | |||
| 190 | Application::getInstance()->setEnableVRMode(false); | 153 | Application::getInstance()->setEnableVRMode(false); |
| 191 | Application::getInstance()->setFullscreen(false); | ||
| 192 | Application::getInstance()->getWindow()->activateWindow(); | 154 | Application::getInstance()->getWindow()->activateWindow(); |
| 193 | |||
| 194 | if (_wasMoved) { | ||
| 195 | QWindow* mainWindow = Application::getInstance()->getWindow()->windowHandle(); | ||
| 196 | mainWindow->setScreen(_previousScreen); | ||
| 197 | mainWindow->setGeometry(_previousRect); | ||
| 198 | |||
| 199 | const int SLIGHT_DELAY = 1500; | ||
| 200 | QTimer::singleShot(SLIGHT_DELAY, this, SLOT(moveWindowAfterLeaveMode())); | ||
| 201 | } | ||
| 202 | _wasMoved = false; | ||
| 203 | _inHDMMode = false; | 155 | _inHDMMode = false; |
| 204 | } | 156 | } |
| 205 | } | 157 | } |
| 206 | 158 | ||
| 207 | void HMDToolsDialog::moveWindowAfterLeaveMode() { | ||
| 208 | QWindow* mainWindow = Application::getInstance()->getWindow()->windowHandle(); | ||
| 209 | mainWindow->setScreen(_previousScreen); | ||
| 210 | mainWindow->setGeometry(_previousRect); | ||
| 211 | Application::getInstance()->getWindow()->activateWindow(); | ||
| 212 | Application::getInstance()->resetSensors(); | ||
| 213 | } | ||
| 214 | |||
| 215 | |||
| 216 | void HMDToolsDialog::reject() { | 159 | void HMDToolsDialog::reject() { |
| 217 | // Just regularly close upon ESC | 160 | // Just regularly close upon ESC |
| 218 | close(); | 161 | close(); |
| @@ -244,13 +187,15 @@ void HMDToolsDialog::hideEvent(QHideEvent* event) { · 25 unmodified lines | |||
| 244 | 187 | ||
| 245 | void HMDToolsDialog::aboutToQuit() { | 188 | void HMDToolsDialog::aboutToQuit() { |
| 246 | if (_inHDMMode) { | 189 | if (_inHDMMode) { |
| 190 | // FIXME this is ineffective because it doesn't trigger the menu to | ||
| 191 | // save the fact that VR Mode is not checked. | ||
| 247 | leaveHDMMode(); | 192 | leaveHDMMode(); |
| 248 | } | 193 | } |
| 249 | } | 194 | } |
| 250 | 195 | ||
| 251 | void HMDToolsDialog::screenCountChanged(int newCount) { | 196 | void HMDToolsDialog::screenCountChanged(int newCount) { |
| 252 | if (!OculusManager::isConnected()) { | 197 | if (!OculusManager::isConnected()) { |
| 253 | OculusManager::connect(); | 198 | //OculusManager::connect(); |
| 254 | } | 199 | } |
| 255 | int hmdScreenNumber = OculusManager::getHMDScreen(); | 200 | int hmdScreenNumber = OculusManager::getHMDScreen(); |
| 256 | 201 | ||
| @@ -35,8 +35,6 @@ signals: · 34 unmodified lines | |||
| 35 | public slots: | 35 | public slots: |
| 36 | void reject(); | 36 | void reject(); |
| 37 | void switchModeClicked(bool checked); | 37 | void switchModeClicked(bool checked); |
| 38 | void activateWindowAfterEnterMode(); | ||
| 39 | void moveWindowAfterLeaveMode(); | ||
| 40 | void applicationWindowScreenChanged(QScreen* screen); | 38 | void applicationWindowScreenChanged(QScreen* screen); |
| 41 | void aboutToQuit(); | 39 | void aboutToQuit(); |
| 42 | void screenCountChanged(int newCount); | 40 | void screenCountChanged(int newCount); |
| @@ -51,8 +49,6 @@ private: · 8 unmodified lines | |||
| 51 | void enterHDMMode(); | 49 | void enterHDMMode(); |
| 52 | void leaveHDMMode(); | 50 | void leaveHDMMode(); |
| 53 | 51 | ||
| 54 | bool _wasMoved; | ||
| 55 | QRect _previousRect; | ||
| 56 | QScreen* _previousScreen; | 52 | QScreen* _previousScreen; |
| 57 | QScreen* _hmdScreen; | 53 | QScreen* _hmdScreen; |
| 58 | int _hmdScreenNumber; | 54 | int _hmdScreenNumber; |
| @@ -11,10 +11,19 @@ · 10 unmodified lines | |||
| 11 | 11 | ||
| 12 | #include "InterfaceConfig.h" | 12 | #include "InterfaceConfig.h" |
| 13 | 13 | ||
| 14 | #ifdef __GNUC__ | ||
| 15 | #pragma GCC diagnostic push | ||
| 16 | #pragma GCC diagnostic ignored "-Wdouble-promotion" | ||
| 17 | #endif | ||
| 18 | |||
| 14 | #include <QDesktopWidget> | 19 | #include <QDesktopWidget> |
| 15 | #include <QTextBlock> | 20 | #include <QTextBlock> |
| 16 | #include <QtGui> | 21 | #include <QtGui> |
| 17 | 22 | ||
| 23 | #ifdef __GNUC__ | ||
| 24 | #pragma GCC diagnostic pop | ||
| 25 | #endif | ||
| 26 | |||
| 18 | #include <PathUtils.h> | 27 | #include <PathUtils.h> |
| 19 | #include <SharedUtil.h> | 28 | #include <SharedUtil.h> |
| 20 | 29 | ||
| @@ -175,7 +175,7 @@ void PreferencesDialog::loadPreferences() { · 174 unmodified lines | |||
| 175 | 175 | ||
| 176 | ui.maxOctreePPSSpin->setValue(qApp->getMaxOctreePacketsPerSecond()); | 176 | ui.maxOctreePPSSpin->setValue(qApp->getMaxOctreePacketsPerSecond()); |
| 177 | 177 | ||
| 178 | ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationOverlay().getHmdUIAngularSize()); | 178 | ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationCompositor().getHmdUIAngularSize()); |
| 179 | 179 | ||
| 180 | SixenseManager& sixense = SixenseManager::getInstance(); | 180 | SixenseManager& sixense = SixenseManager::getInstance(); |
| 181 | ui.sixenseReticleMoveSpeedSpin->setValue(sixense.getReticleMoveSpeed()); | 181 | ui.sixenseReticleMoveSpeedSpin->setValue(sixense.getReticleMoveSpeed()); |
| @@ -239,7 +239,7 @@ void PreferencesDialog::savePreferences() { · 57 unmodified lines | |||
| 239 | 239 | ||
| 240 | qApp->setMaxOctreePacketsPerSecond(ui.maxOctreePPSSpin->value()); | 240 | qApp->setMaxOctreePacketsPerSecond(ui.maxOctreePPSSpin->value()); |
| 241 | 241 | ||
| 242 | qApp->getApplicationOverlay().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value()); | 242 | qApp->getApplicationCompositor().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value()); |
| 243 | 243 | ||
| 244 | SixenseManager& sixense = SixenseManager::getInstance(); | 244 | SixenseManager& sixense = SixenseManager::getInstance(); |
| 245 | sixense.setReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value()); | 245 | sixense.setReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value()); |
| @@ -1,134 +0,0 @@ | |||
| 1 | // | ||
| 2 | // RearMirrorTools.cpp | ||
| 3 | // interface/src/ui | ||
| 4 | // | ||
| 5 | // Created by Stojce Slavkovski on 10/23/2013. | ||
| 6 | // Copyright 2013 High Fidelity, Inc. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | |||
| 12 | #include "InterfaceConfig.h" | ||
| 13 | |||
| 14 | #include <QMouseEvent> | ||
| 15 | |||
| 16 | #include <PathUtils.h> | ||
| 17 | #include <SharedUtil.h> | ||
| 18 | #include <gpu/GLBackend.h> | ||
| 19 | |||
| 20 | #include "Application.h" | ||
| 21 | #include "RearMirrorTools.h" | ||
| 22 | #include "Util.h" | ||
| 23 | |||
| 24 | const int ICON_SIZE = 24; | ||
| 25 | const int ICON_PADDING = 5; | ||
| 26 | |||
| 27 | const char SETTINGS_GROUP_NAME[] = "Rear View Tools"; | ||
| 28 | const char ZOOM_LEVEL_SETTINGS[] = "ZoomLevel"; | ||
| 29 | Setting::Handle<int> RearMirrorTools::rearViewZoomLevel(QStringList() << SETTINGS_GROUP_NAME << ZOOM_LEVEL_SETTINGS, | ||
| 30 | ZoomLevel::HEAD); | ||
| 31 | |||
| 32 | RearMirrorTools::RearMirrorTools(QRect& bounds) : | ||
| 33 | _bounds(bounds), | ||
| 34 | _windowed(false), | ||
| 35 | _fullScreen(false) | ||
| 36 | { | ||
| 37 | _closeTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/close.svg"); | ||
| 38 | |||
| 39 | _zoomHeadTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/plus.svg"); | ||
| 40 | _zoomBodyTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/minus.svg"); | ||
| 41 | |||
| 42 | _shrinkIconRect = QRect(ICON_PADDING, ICON_PADDING, ICON_SIZE, ICON_SIZE); | ||
| 43 | _closeIconRect = QRect(_bounds.left() + ICON_PADDING, _bounds.top() + ICON_PADDING, ICON_SIZE, ICON_SIZE); | ||
| 44 | _resetIconRect = QRect(_bounds.width() - ICON_SIZE - ICON_PADDING, _bounds.top() + ICON_PADDING, ICON_SIZE, ICON_SIZE); | ||
| 45 | _bodyZoomIconRect = QRect(_bounds.width() - ICON_SIZE - ICON_PADDING, _bounds.bottom() - ICON_PADDING - ICON_SIZE, ICON_SIZE, ICON_SIZE); | ||
| 46 | _headZoomIconRect = QRect(_bounds.left() + ICON_PADDING, _bounds.bottom() - ICON_PADDING - ICON_SIZE, ICON_SIZE, ICON_SIZE); | ||
| 47 | } | ||
| 48 | |||
| 49 | void RearMirrorTools::render(RenderArgs* renderArgs, bool fullScreen, const QPoint & mousePosition) { | ||
| 50 | if (fullScreen) { | ||
| 51 | _fullScreen = true; | ||
| 52 | displayIcon(QRect(QPoint(), qApp->getDeviceSize()), _shrinkIconRect, _closeTexture); | ||
| 53 | } else { | ||
| 54 | // render rear view tools if mouse is in the bounds | ||
| 55 | _windowed = _bounds.contains(mousePosition); | ||
| 56 | if (_windowed) { | ||
| 57 | displayIcon(_bounds, _closeIconRect, _closeTexture); | ||
| 58 | |||
| 59 | ZoomLevel zoomLevel = (ZoomLevel)rearViewZoomLevel.get(); | ||
| 60 | displayIcon(_bounds, _headZoomIconRect, _zoomHeadTexture, zoomLevel == HEAD); | ||
| 61 | displayIcon(_bounds, _bodyZoomIconRect, _zoomBodyTexture, zoomLevel == BODY); | ||
| 62 | } | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | bool RearMirrorTools::mousePressEvent(int x, int y) { | ||
| 67 | if (_windowed) { | ||
| 68 | if (_closeIconRect.contains(x, y)) { | ||
| 69 | _windowed = false; | ||
| 70 | emit closeView(); | ||
| 71 | return true; | ||
| 72 | } | ||
| 73 | |||
| 74 | if (_headZoomIconRect.contains(x, y)) { | ||
| 75 | rearViewZoomLevel.set(HEAD); | ||
| 76 | return true; | ||
| 77 | } | ||
| 78 | |||
| 79 | if (_bodyZoomIconRect.contains(x, y)) { | ||
| 80 | rearViewZoomLevel.set(BODY); | ||
| 81 | return true; | ||
| 82 | } | ||
| 83 | |||
| 84 | if (_bounds.contains(x, y)) { | ||
| 85 | _windowed = false; | ||
| 86 | emit restoreView(); | ||
| 87 | return true; | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | if (_fullScreen) { | ||
| 92 | if (_shrinkIconRect.contains(x, y)) { | ||
| 93 | _fullScreen = false; | ||
| 94 | emit shrinkView(); | ||
| 95 | return true; | ||
| 96 | } | ||
| 97 | } | ||
| 98 | return false; | ||
| 99 | } | ||
| 100 | |||
| 101 | void RearMirrorTools::displayIcon(QRect bounds, QRect iconBounds, const gpu::TexturePointer& texture, bool selected) { | ||
| 102 | |||
| 103 | glMatrixMode(GL_PROJECTION); | ||
| 104 | glPushMatrix(); | ||
| 105 | glLoadIdentity(); | ||
| 106 | |||
| 107 | glOrtho(bounds.left(), bounds.right(), bounds.bottom(), bounds.top(), -1.0, 1.0); | ||
| 108 | glDisable(GL_DEPTH_TEST); | ||
| 109 | glDisable(GL_LIGHTING); | ||
| 110 | glEnable(GL_TEXTURE_2D); | ||
| 111 | |||
| 112 | glm::vec4 quadColor; | ||
| 113 | if (selected) { | ||
| 114 | quadColor = glm::vec4(.5f, .5f, .5f, 1.0f); | ||
| 115 | } else { | ||
| 116 | quadColor = glm::vec4(1, 1, 1, 1); | ||
| 117 | } | ||
| 118 | |||
| 119 | glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(texture)); | ||
| 120 | |||
| 121 | glm::vec2 topLeft(iconBounds.left(), iconBounds.top()); | ||
| 122 | glm::vec2 bottomRight(iconBounds.right(), iconBounds.bottom()); | ||
| 123 | static const glm::vec2 texCoordTopLeft(0.0f, 1.0f); | ||
| 124 | static const glm::vec2 texCoordBottomRight(1.0f, 0.0f); | ||
| 125 | |||
| 126 | DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor); | ||
| 127 | |||
| 128 | glPopMatrix(); | ||
| 129 | |||
| 130 | glMatrixMode(GL_MODELVIEW); | ||
| 131 | |||
| 132 | glBindTexture(GL_TEXTURE_2D, 0); | ||
| 133 | glDisable(GL_TEXTURE_2D); | ||
| 134 | } | ||
| @@ -1,56 +0,0 @@ | |||
| 1 | // | ||
| 2 | // RearMirrorTools.h | ||
| 3 | // interface/src/ui | ||
| 4 | // | ||
| 5 | // Created by Stojce Slavkovski on 10/23/2013. | ||
| 6 | // Copyright 2013 High Fidelity, Inc. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | |||
| 12 | #ifndef hifi_RearMirrorTools_h | ||
| 13 | #define hifi_RearMirrorTools_h | ||
| 14 | |||
| 15 | #include <gpu/Texture.h> | ||
| 16 | #include <SettingHandle.h> | ||
| 17 | |||
| 18 | enum ZoomLevel { | ||
| 19 | HEAD = 0, | ||
| 20 | BODY = 1 | ||
| 21 | }; | ||
| 22 | |||
| 23 | class RearMirrorTools : public QObject { | ||
| 24 | Q_OBJECT | ||
| 25 | public: | ||
| 26 | RearMirrorTools(QRect& bounds); | ||
| 27 | void render(RenderArgs* renderArgs, bool fullScreen, const QPoint & mousePos); | ||
| 28 | bool mousePressEvent(int x, int y); | ||
| 29 | |||
| 30 | static Setting::Handle<int> rearViewZoomLevel; | ||
| 31 | |||
| 32 | signals: | ||
| 33 | void closeView(); | ||
| 34 | void shrinkView(); | ||
| 35 | void resetView(); | ||
| 36 | void restoreView(); | ||
| 37 | |||
| 38 | private: | ||
| 39 | QRect _bounds; | ||
| 40 | gpu::TexturePointer _closeTexture; | ||
| 41 | gpu::TexturePointer _zoomBodyTexture; | ||
| 42 | gpu::TexturePointer _zoomHeadTexture; | ||
| 43 | |||
| 44 | QRect _closeIconRect; | ||
| 45 | QRect _resetIconRect; | ||
| 46 | QRect _shrinkIconRect; | ||
| 47 | QRect _headZoomIconRect; | ||
| 48 | QRect _bodyZoomIconRect; | ||
| 49 | |||
| 50 | bool _windowed; | ||
| 51 | bool _fullScreen; | ||
| 52 | |||
| 53 | void displayIcon(QRect bounds, QRect iconBounds, const gpu::TexturePointer& texture, bool selected = false); | ||
| 54 | }; | ||
| 55 | |||
| 56 | #endif // hifi_RearMirrorTools_h | ||
| @@ -1,17 +1,17 @@ | |||
| 1 | // | 1 | // |
| 2 | // Stats.cpp | 2 | // Created by Bradley Austin Davis 2015/06/17 |
| 3 | // interface/src/ui | ||
| 4 | // | ||
| 5 | // Created by Lucas Crisman on 22/03/14. | ||
| 6 | // Copyright 2013 High Fidelity, Inc. | 3 | // Copyright 2013 High Fidelity, Inc. |
| 7 | // | 4 | // |
| 8 | // Distributed under the Apache License, Version 2.0. | 5 | // Distributed under the Apache License, Version 2.0. |
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 10 | // | 7 | // |
| 11 | 8 | ||
| 12 | #include <sstream> | 9 | #include <gpu/GPUConfig.h> |
| 13 | 10 | ||
| 14 | #include <stdlib.h> | 11 | #include "Stats.h" |
| 12 | |||
| 13 | #include <sstream> | ||
| 14 | #include <QFontDatabase> | ||
| 15 | 15 | ||
| 16 | #include <glm/glm.hpp> | 16 | #include <glm/glm.hpp> |
| 17 | #include <glm/gtx/component_wise.hpp> | 17 | #include <glm/gtx/component_wise.hpp> |
| @@ -25,147 +25,31 @@ · 7 unmodified lines | |||
| 25 | #include <LODManager.h> | 25 | #include <LODManager.h> |
| 26 | #include <PerfStat.h> | 26 | #include <PerfStat.h> |
| 27 | 27 | ||
| 28 | #include "Stats.h" | ||
| 29 | #include "BandwidthRecorder.h" | 28 | #include "BandwidthRecorder.h" |
| 30 | #include "InterfaceConfig.h" | 29 | #include "InterfaceConfig.h" |
| 31 | #include "Menu.h" | 30 | #include "Menu.h" |
| 32 | #include "Util.h" | 31 | #include "Util.h" |
| 33 | #include "SequenceNumberStats.h" | 32 | #include "SequenceNumberStats.h" |
| 34 | 33 | ||
| 35 | using namespace std; | 34 | HIFI_QML_DEF(Stats) |
| 36 | 35 | ||
| 37 | const int STATS_PELS_PER_LINE = 16; | 36 | using namespace std; |
| 38 | const int STATS_PELS_INITIALOFFSET = 12; | ||
| 39 | 37 | ||
| 40 | const int STATS_GENERAL_MIN_WIDTH = 165; | 38 | static Stats* INSTANCE{ nullptr }; |
| 41 | const int STATS_PING_MIN_WIDTH = 190; | ||
| 42 | const int STATS_GEO_MIN_WIDTH = 240; | ||
| 43 | const int STATS_OCTREE_MIN_WIDTH = 410; | ||
| 44 | 39 | ||
| 45 | Stats* Stats::getInstance() { | 40 | Stats* Stats::getInstance() { |
| 46 | static Stats stats; | 41 | if (!INSTANCE) { |
| 47 | return &stats; | 42 | Stats::registerType(); |
| 48 | } | 43 | Stats::show(); |
| 49 | 44 | Q_ASSERT(INSTANCE); | |
| 50 | Stats::Stats(): | ||
| 51 | _expanded(false), | ||
| 52 | _recentMaxPackets(0), | ||
| 53 | _resetRecentMaxPacketsSoon(true), | ||
| 54 | _generalStatsWidth(STATS_GENERAL_MIN_WIDTH), | ||
| 55 | _pingStatsWidth(STATS_PING_MIN_WIDTH), | ||
| 56 | _geoStatsWidth(STATS_GEO_MIN_WIDTH), | ||
| 57 | _octreeStatsWidth(STATS_OCTREE_MIN_WIDTH), | ||
| 58 | _lastHorizontalOffset(0) | ||
| 59 | { | ||
| 60 | auto canvasSize = Application::getInstance()->getCanvasSize(); | ||
| 61 | resetWidth(canvasSize.x, 0); | ||
| 62 | } | ||
| 63 | |||
| 64 | void Stats::toggleExpanded() { | ||
| 65 | _expanded = !_expanded; | ||
| 66 | } | ||
| 67 | |||
| 68 | // called on mouse click release | ||
| 69 | // check for clicks over stats in order to expand or contract them | ||
| 70 | void Stats::checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseDragStartedY, int horizontalOffset) { | ||
| 71 | auto canvasSize = Application::getInstance()->getCanvasSize(); | ||
| 72 | |||
| 73 | if (0 != glm::compMax(glm::abs(glm::ivec2(mouseX - mouseDragStartedX, mouseY - mouseDragStartedY)))) { | ||
| 74 | // not worried about dragging on stats | ||
| 75 | return; | ||
| 76 | } | ||
| 77 | |||
| 78 | int statsHeight = 0, | ||
| 79 | statsWidth = 0, | ||
| 80 | statsX = 0, | ||
| 81 | statsY = 0, | ||
| 82 | lines = 0; | ||
| 83 | |||
| 84 | statsX = horizontalOffset; | ||
| 85 | |||
| 86 | // top-left stats click | ||
| 87 | lines = _expanded ? 5 : 3; | ||
| 88 | statsHeight = lines * STATS_PELS_PER_LINE + 10; | ||
| 89 | if (mouseX > statsX && mouseX < statsX + _generalStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) { | ||
| 90 | toggleExpanded(); | ||
| 91 | return; | ||
| 92 | } | ||
| 93 | statsX += _generalStatsWidth; | ||
| 94 | |||
| 95 | // ping stats click | ||
| 96 | if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { | ||
| 97 | lines = _expanded ? 4 : 3; | ||
| 98 | statsHeight = lines * STATS_PELS_PER_LINE + 10; | ||
| 99 | if (mouseX > statsX && mouseX < statsX + _pingStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) { | ||
| 100 | toggleExpanded(); | ||
| 101 | return; | ||
| 102 | } | ||
| 103 | statsX += _pingStatsWidth; | ||
| 104 | } | ||
| 105 | |||
| 106 | // geo stats panel click | ||
| 107 | lines = _expanded ? 4 : 3; | ||
| 108 | statsHeight = lines * STATS_PELS_PER_LINE + 10; | ||
| 109 | if (mouseX > statsX && mouseX < statsX + _geoStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) { | ||
| 110 | toggleExpanded(); | ||
| 111 | return; | ||
| 112 | } | ||
| 113 | statsX += _geoStatsWidth; | ||
| 114 | |||
| 115 | // top-right stats click | ||
| 116 | lines = _expanded ? 11 : 3; | ||
| 117 | statsHeight = lines * STATS_PELS_PER_LINE + 10; | ||
| 118 | statsWidth = canvasSize.x - statsX; | ||
| 119 | if (mouseX > statsX && mouseX < statsX + statsWidth && mouseY > statsY && mouseY < statsY + statsHeight) { | ||
| 120 | toggleExpanded(); | ||
| 121 | return; | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | void Stats::resetWidth(int width, int horizontalOffset) { | ||
| 126 | auto canvasSize = Application::getInstance()->getCanvasSize(); | ||
| 127 | int extraSpace = canvasSize.x - horizontalOffset - 2 | ||
| 128 | - STATS_GENERAL_MIN_WIDTH | ||
| 129 | - (Menu::getInstance()->isOptionChecked(MenuOption::TestPing) ? STATS_PING_MIN_WIDTH -1 : 0) | ||
| 130 | - STATS_GEO_MIN_WIDTH | ||
| 131 | - STATS_OCTREE_MIN_WIDTH; | ||
| 132 | |||
| 133 | int panels = 4; | ||
| 134 | |||
| 135 | _generalStatsWidth = STATS_GENERAL_MIN_WIDTH; | ||
| 136 | if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { | ||
| 137 | _pingStatsWidth = STATS_PING_MIN_WIDTH; | ||
| 138 | } else { | ||
| 139 | _pingStatsWidth = 0; | ||
| 140 | panels = 3; | ||
| 141 | } | ||
| 142 | _geoStatsWidth = STATS_GEO_MIN_WIDTH; | ||
| 143 | _octreeStatsWidth = STATS_OCTREE_MIN_WIDTH; | ||
| 144 | |||
| 145 | if (extraSpace > panels) { | ||
| 146 | _generalStatsWidth += (int) extraSpace / panels; | ||
| 147 | if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { | ||
| 148 | _pingStatsWidth += (int) extraSpace / panels; | ||
| 149 | } | ||
| 150 | _geoStatsWidth += (int) extraSpace / panels; | ||
| 151 | _octreeStatsWidth += canvasSize.x - | ||
| 152 | (_generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3); | ||
| 153 | } | 45 | } |
| 46 | return INSTANCE; | ||
| 154 | } | 47 | } |
| 155 | 48 | ||
| 156 | 49 | Stats::Stats(QQuickItem* parent) : QQuickItem(parent) { | |
| 157 | // translucent background box that makes stats more readable | 50 | INSTANCE = this; |
| 158 | void Stats::drawBackground(unsigned int rgba, int x, int y, int width, int height) { | 51 | const QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont); |
| 159 | glm::vec4 color(((rgba >> 24) & 0xff) / 255.0f, | 52 | _monospaceFont = font.family(); |
| 160 | ((rgba >> 16) & 0xff) / 255.0f, | ||
| 161 | ((rgba >> 8) & 0xff) / 255.0f, | ||
| 162 | (rgba & 0xff) / 255.0f); | ||
| 163 | |||
| 164 | // FIX ME: is this correct? It seems to work to fix textures bleeding into us... | ||
| 165 | glBindTexture(GL_TEXTURE_2D, 0); | ||
| 166 | glDisable(GL_TEXTURE_2D); | ||
| 167 | |||
| 168 | DependencyManager::get<GeometryCache>()->renderQuad(x, y, width, height, color); | ||
| 169 | } | 53 | } |
| 170 | 54 | ||
| 171 | bool Stats::includeTimingRecord(const QString& name) { | 55 | bool Stats::includeTimingRecord(const QString& name) { |
| @@ -190,161 +74,70 @@ bool Stats::includeTimingRecord(const QString& name) { · 18 unmodified lines | |||
| 190 | return false; | 74 | return false; |
| 191 | } | 75 | } |
| 192 | 76 | ||
| 193 | // display expanded or contracted stats | 77 | #define STAT_UPDATE(name, src) \ |
| 194 | void Stats::display( | 78 | { \ |
| 195 | const float* color, | 79 | auto val = src; \ |
| 196 | int horizontalOffset, | 80 | if (_##name != val) { \ |
| 197 | float fps, | 81 | _##name = val; \ |
| 198 | int inPacketsPerSecond, | 82 | emit name##Changed(); \ |
| 199 | int outPacketsPerSecond, | 83 | } \ |
| 200 | int inKbitsPerSecond, | ||
| 201 | int outKbitsPerSecond, | ||
| 202 | int voxelPacketsToProcess) | ||
| 203 | { | ||
| 204 | auto canvasSize = Application::getInstance()->getCanvasSize(); | ||
| 205 | |||
| 206 | unsigned int backgroundColor = 0x33333399; | ||
| 207 | int verticalOffset = 0, lines = 0; | ||
| 208 | float scale = 0.10f; | ||
| 209 | float rotation = 0.0f; | ||
| 210 | int font = 2; | ||
| 211 | |||
| 212 | QLocale locale(QLocale::English); | ||
| 213 | std::stringstream octreeStats; | ||
| 214 | |||
| 215 | QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>(); | ||
| 216 | |||
| 217 | if (_lastHorizontalOffset != horizontalOffset) { | ||
| 218 | resetWidth(canvasSize.x, horizontalOffset); | ||
| 219 | _lastHorizontalOffset = horizontalOffset; | ||
| 220 | } | 84 | } |
| 221 | 85 | ||
| 222 | glPointSize(1.0f); | 86 | #define STAT_UPDATE_FLOAT(name, src, epsilon) \ |
| 223 | 87 | { \ | |
| 224 | // we need to take one avatar out so we don't include ourselves | 88 | float val = src; \ |
| 225 | int totalAvatars = DependencyManager::get<AvatarManager>()->size() - 1; | 89 | if (fabs(_##name - val) >= epsilon) { \ |
| 226 | int totalServers = DependencyManager::get<NodeList>()->size(); | 90 | _##name = val; \ |
| 227 | 91 | emit name##Changed(); \ | |
| 228 | lines = 5; | 92 | } \ |
| 229 | int columnOneWidth = _generalStatsWidth; | ||
| 230 | |||
| 231 | bool performanceTimerIsActive = PerformanceTimer::isActive(); | ||
| 232 | bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails); | ||
| 233 | if (displayPerf && performanceTimerIsActive) { | ||
| 234 | PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up | ||
| 235 | |||
| 236 | columnOneWidth = _generalStatsWidth + _pingStatsWidth + _geoStatsWidth; // 3 columns wide... | ||
| 237 | // we will also include room for 1 line per timing record and a header of 4 lines | ||
| 238 | lines += 4; | ||
| 239 | |||
| 240 | const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords(); | ||
| 241 | QMapIterator<QString, PerformanceTimerRecord> i(allRecords); | ||
| 242 | bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen); | ||
| 243 | int statsLines = 0; | ||
| 244 | while (i.hasNext()) { | ||
| 245 | i.next(); | ||
| 246 | if (includeTimingRecord(i.key())) { | ||
| 247 | lines++; | ||
| 248 | statsLines++; | ||
| 249 | if (onlyDisplayTopTen && statsLines == 10) { | ||
| 250 | break; | ||
| 251 | } | ||
| 252 | } | ||
| 253 | } | ||
| 254 | } | 93 | } |
| 255 | |||
| 256 | drawBackground(backgroundColor, horizontalOffset, 0, columnOneWidth, (lines + 1) * STATS_PELS_PER_LINE); | ||
| 257 | horizontalOffset += 5; | ||
| 258 | |||
| 259 | int columnOneHorizontalOffset = horizontalOffset; | ||
| 260 | |||
| 261 | QString serverNodes = QString("Servers: %1").arg(totalServers); | ||
| 262 | QString avatarNodes = QString("Avatars: %1").arg(totalAvatars); | ||
| 263 | QString framesPerSecond = QString("Framerate: %1 FPS").arg(fps, 3, 'f', 0); | ||
| 264 | |||
| 265 | verticalOffset = STATS_PELS_INITIALOFFSET; // first one is offset by less than a line | ||
| 266 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, serverNodes.toUtf8().constData(), color); | ||
| 267 | verticalOffset += STATS_PELS_PER_LINE; | ||
| 268 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarNodes.toUtf8().constData(), color); | ||
| 269 | verticalOffset += STATS_PELS_PER_LINE; | ||
| 270 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, framesPerSecond.toUtf8().constData(), color); | ||
| 271 | |||
| 272 | QString packetsPerSecondString = QString("Packets In/Out: %1/%2").arg(inPacketsPerSecond).arg(outPacketsPerSecond); | ||
| 273 | QString averageMegabitsPerSecond = QString("Mbps In/Out: %1/%2"). | ||
| 274 | arg((float)inKbitsPerSecond * 1.0f / 1000.0f). | ||
| 275 | arg((float)outKbitsPerSecond * 1.0f / 1000.0f); | ||
| 276 | |||
| 277 | verticalOffset += STATS_PELS_PER_LINE; | ||
| 278 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, packetsPerSecondString.toUtf8().constData(), color); | ||
| 279 | verticalOffset += STATS_PELS_PER_LINE; | ||
| 280 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, averageMegabitsPerSecond.toUtf8().constData(), color); | ||
| 281 | 94 | ||
| 282 | |||
| 283 | // TODO: the display of these timing details should all be moved to JavaScript | ||
| 284 | if (displayPerf && performanceTimerIsActive) { | ||
| 285 | bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen); | ||
| 286 | // Timing details... | ||
| 287 | verticalOffset += STATS_PELS_PER_LINE * 4; // skip 3 lines to be under the other columns | ||
| 288 | drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, | ||
| 289 | "-------------------------------------------------------- Function " | ||
| 290 | "------------------------------------------------------- --msecs- -calls--", color); | ||
| 291 | |||
| 292 | // First iterate all the records, and for the ones that should be included, insert them into | ||
| 293 | // a new Map sorted by average time... | ||
| 294 | QMap<float, QString> sortedRecords; | ||
| 295 | const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords(); | ||
| 296 | QMapIterator<QString, PerformanceTimerRecord> i(allRecords); | ||
| 297 | 95 | ||
| 298 | while (i.hasNext()) { | 96 | void Stats::updateStats() { |
| 299 | i.next(); | 97 | if (!Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { |
| 300 | if (includeTimingRecord(i.key())) { | 98 | if (isVisible()) { |
| 301 | float averageTime = (float)i.value().getMovingAverage() / (float)USECS_PER_MSEC; | 99 | setVisible(false); |
| 302 | sortedRecords.insertMulti(averageTime, i.key()); | ||
| 303 | } | ||
| 304 | } | 100 | } |
| 305 | 101 | return; | |
| 306 | int linesDisplayed = 0; | 102 | } else { |
| 307 | QMapIterator<float, QString> j(sortedRecords); | 103 | if (!isVisible()) { |
| 308 | j.toBack(); | 104 | setVisible(true); |
| 309 | while (j.hasPrevious()) { | ||
| 310 | j.previous(); | ||
| 311 | QChar noBreakingSpace = QChar::Nbsp; | ||
| 312 | QString functionName = j.value(); | ||
| 313 | const PerformanceTimerRecord& record = allRecords.value(functionName); | ||
| 314 | |||
| 315 | QString perfLine = QString("%1: %2 [%3]"). | ||
| 316 | arg(QString(qPrintable(functionName)), 120, noBreakingSpace). | ||
| 317 | arg((float)record.getMovingAverage() / (float)USECS_PER_MSEC, 8, 'f', 3, noBreakingSpace). | ||
| 318 | arg((int)record.getCount(), 6, 10, noBreakingSpace); | ||
| 319 | |||
| 320 | verticalOffset += STATS_PELS_PER_LINE; | ||
| 321 | drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, perfLine.toUtf8().constData(), color); | ||
| 322 | linesDisplayed++; | ||
| 323 | if (onlyDisplayTopTen && linesDisplayed == 10) { | ||
| 324 | break; | ||
| 325 | } | ||
| 326 | } | 105 | } |
| 327 | } | 106 | } |
| 328 | 107 | ||
| 108 | bool shouldDisplayTimingDetail = Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) && | ||
| 109 | Menu::getInstance()->isOptionChecked(MenuOption::Stats) && isExpanded(); | ||
| 110 | if (shouldDisplayTimingDetail != PerformanceTimer::isActive()) { | ||
| 111 | PerformanceTimer::setActive(shouldDisplayTimingDetail); | ||
| 112 | } | ||
| 329 | 113 | ||
| 330 | verticalOffset = STATS_PELS_INITIALOFFSET; | ||
| 331 | horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + 1; | ||
| 332 | 114 | ||
| 333 | if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { | 115 | auto nodeList = DependencyManager::get<NodeList>(); |
| 334 | int pingAudio = -1, pingAvatar = -1, pingVoxel = -1, pingOctreeMax = -1; | 116 | auto avatarManager = DependencyManager::get<AvatarManager>(); |
| 117 | // we need to take one avatar out so we don't include ourselves | ||
| 118 | STAT_UPDATE(avatarCount, avatarManager->size() - 1); | ||
| 119 | STAT_UPDATE(serverCount, nodeList->size()); | ||
| 120 | STAT_UPDATE(framerate, (int)qApp->getFps()); | ||
| 121 | |||
| 122 | auto bandwidthRecorder = DependencyManager::get<BandwidthRecorder>(); | ||
| 123 | STAT_UPDATE(packetInCount, bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond()); | ||
| 124 | STAT_UPDATE(packetOutCount, bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond()); | ||
| 125 | STAT_UPDATE_FLOAT(mbpsIn, (float)bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond() / 1000.0f, 0.01f); | ||
| 126 | STAT_UPDATE_FLOAT(mbpsOut, (float)bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond() / 1000.0f, 0.01f); | ||
| 335 | 127 | ||
| 336 | auto nodeList = DependencyManager::get<NodeList>(); | 128 | // Second column: ping |
| 129 | if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { | ||
| 337 | SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); | 130 | SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); |
| 338 | SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); | 131 | SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); |
| 132 | STAT_UPDATE(audioPing, audioMixerNode ? audioMixerNode->getPingMs() : -1); | ||
| 133 | STAT_UPDATE(avatarPing, avatarMixerNode ? avatarMixerNode->getPingMs() : -1); | ||
| 339 | 134 | ||
| 340 | pingAudio = audioMixerNode ? audioMixerNode->getPingMs() : -1; | 135 | //// Now handle voxel servers, since there could be more than one, we average their ping times |
| 341 | pingAvatar = avatarMixerNode ? avatarMixerNode->getPingMs() : -1; | ||
| 342 | |||
| 343 | // Now handle voxel servers, since there could be more than one, we average their ping times | ||
| 344 | unsigned long totalPingOctree = 0; | 136 | unsigned long totalPingOctree = 0; |
| 345 | int octreeServerCount = 0; | 137 | int octreeServerCount = 0; |
| 346 | 138 | int pingOctreeMax = 0; | |
| 347 | nodeList->eachNode([&totalPingOctree, &pingOctreeMax, &octreeServerCount](const SharedNodePointer& node){ | 139 | int pingVoxel; |
| 140 | nodeList->eachNode([&](const SharedNodePointer& node) { | ||
| 348 | // TODO: this should also support entities | 141 | // TODO: this should also support entities |
| 349 | if (node->getType() == NodeType::EntityServer) { | 142 | if (node->getType() == NodeType::EntityServer) { |
| 350 | totalPingOctree += node->getPingMs(); | 143 | totalPingOctree += node->getPingMs(); |
| @@ -359,168 +152,85 @@ void Stats::display( · 8 unmodified lines | |||
| 359 | pingVoxel = totalPingOctree / octreeServerCount; | 152 | pingVoxel = totalPingOctree / octreeServerCount; |
| 360 | } | 153 | } |
| 361 | 154 | ||
| 362 | lines = _expanded ? 4 : 3; | 155 | //STAT_UPDATE(entitiesPing, pingVoxel); |
| 363 | 156 | //if (_expanded) { | |
| 364 | // only draw our background if column one didn't draw a wide background | 157 | // QString voxelMaxPing; |
| 365 | if (columnOneWidth == _generalStatsWidth) { | 158 | // if (pingVoxel >= 0) { // Average is only meaningful if pingVoxel is valid. |
| 366 | drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, (lines + 1) * STATS_PELS_PER_LINE); | 159 | // voxelMaxPing = QString("Voxel max ping: %1").arg(pingOctreeMax); |
| 367 | } | 160 | // } else { |
| 368 | horizontalOffset += 5; | 161 | // voxelMaxPing = QString("Voxel max ping: --"); |
| 369 | 162 | // } | |
| 370 | 163 | } else { | |
| 371 | QString audioPing; | 164 | // -2 causes the QML to hide the ping column |
| 372 | if (pingAudio >= 0) { | 165 | STAT_UPDATE(audioPing, -2); |
| 373 | audioPing = QString("Audio ping: %1").arg(pingAudio); | ||
| 374 | } else { | ||
| 375 | audioPing = QString("Audio ping: --"); | ||
| 376 | } | ||
| 377 | |||
| 378 | QString avatarPing; | ||
| 379 | if (pingAvatar >= 0) { | ||
| 380 | avatarPing = QString("Avatar ping: %1").arg(pingAvatar); | ||
| 381 | } else { | ||
| 382 | avatarPing = QString("Avatar ping: --"); | ||
| 383 | } | ||
| 384 | |||
| 385 | QString voxelAvgPing; | ||
| 386 | if (pingVoxel >= 0) { | ||
| 387 | voxelAvgPing = QString("Entities avg ping: %1").arg(pingVoxel); | ||
| 388 | } else { | ||
| 389 | voxelAvgPing = QString("Entities avg ping: --"); | ||
| 390 | } | ||
| 391 | |||
| 392 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, audioPing.toUtf8().constData(), color); | ||
| 393 | verticalOffset += STATS_PELS_PER_LINE; | ||
| 394 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarPing.toUtf8().constData(), color); | ||
| 395 | verticalOffset += STATS_PELS_PER_LINE; | ||
| 396 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelAvgPing.toUtf8().constData(), color); | ||
| 397 | |||
| 398 | if (_expanded) { | ||
| 399 | QString voxelMaxPing; | ||
| 400 | if (pingVoxel >= 0) { // Average is only meaningful if pingVoxel is valid. | ||
| 401 | voxelMaxPing = QString("Voxel max ping: %1").arg(pingOctreeMax); | ||
| 402 | } else { | ||
| 403 | voxelMaxPing = QString("Voxel max ping: --"); | ||
| 404 | } | ||
| 405 | |||
| 406 | verticalOffset += STATS_PELS_PER_LINE; | ||
| 407 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelMaxPing.toUtf8().constData(), color); | ||
| 408 | } | ||
| 409 | |||
| 410 | verticalOffset = STATS_PELS_INITIALOFFSET; | ||
| 411 | horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + 2; | ||
| 412 | } | 166 | } |
| 413 | 167 | ||
| 414 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | ||
| 415 | glm::vec3 avatarPos = myAvatar->getPosition(); | ||
| 416 | |||
| 417 | lines = _expanded ? 7 : 3; | ||
| 418 | |||
| 419 | if (columnOneWidth == _generalStatsWidth) { | ||
| 420 | drawBackground(backgroundColor, horizontalOffset, 0, _geoStatsWidth, (lines + 1) * STATS_PELS_PER_LINE); | ||
| 421 | } | ||
| 422 | horizontalOffset += 5; | ||
| 423 | |||
| 424 | QString avatarPosition = QString("Position: %1, %2, %3"). | ||
| 425 | arg(avatarPos.x, -1, 'f', 1). | ||
| 426 | arg(avatarPos.y, -1, 'f', 1). | ||
| 427 | arg(avatarPos.z, -1, 'f', 1); | ||
| 428 | QString avatarVelocity = QString("Velocity: %1").arg(glm::length(myAvatar->getVelocity()), -1, 'f', 1); | ||
| 429 | QString avatarBodyYaw = QString("Yaw: %1").arg(myAvatar->getBodyYaw(), -1, 'f', 1); | ||
| 430 | QString avatarMixerStats; | ||
| 431 | |||
| 432 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarPosition.toUtf8().constData(), color); | ||
| 433 | verticalOffset += STATS_PELS_PER_LINE; | ||
| 434 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarVelocity.toUtf8().constData(), color); | ||
| 435 | verticalOffset += STATS_PELS_PER_LINE; | ||
| 436 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarBodyYaw.toUtf8().constData(), color); | ||
| 437 | 168 | ||
| 169 | // Third column, avatar stats | ||
| 170 | MyAvatar* myAvatar = avatarManager->getMyAvatar(); | ||
| 171 | glm::vec3 avatarPos = myAvatar->getPosition(); | ||
| 172 | STAT_UPDATE(position, QVector3D(avatarPos.x, avatarPos.y, avatarPos.z)); | ||
| 173 | STAT_UPDATE_FLOAT(velocity, glm::length(myAvatar->getVelocity()), 0.1f); | ||
| 174 | STAT_UPDATE_FLOAT(yaw, myAvatar->getBodyYaw(), 0.1f); | ||
| 438 | if (_expanded) { | 175 | if (_expanded) { |
| 439 | SharedNodePointer avatarMixer = DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::AvatarMixer); | 176 | SharedNodePointer avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer); |
| 440 | if (avatarMixer) { | 177 | if (avatarMixer) { |
| 441 | avatarMixerStats = QString("Avatar Mixer: %1 kbps, %2 pps"). | 178 | STAT_UPDATE(avatarMixerKbps, roundf( |
| 442 | arg(roundf(bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) + | 179 | bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AvatarMixer) + |
| 443 | bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer))). | 180 | bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AvatarMixer))); |
| 444 | arg(roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) + | 181 | STAT_UPDATE(avatarMixerPps, roundf( |
| 445 | bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer))); | 182 | bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AvatarMixer) + |
| 183 | bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AvatarMixer))); | ||
| 446 | } else { | 184 | } else { |
| 447 | avatarMixerStats = QString("No Avatar Mixer"); | 185 | STAT_UPDATE(avatarMixerKbps, -1); |
| 186 | STAT_UPDATE(avatarMixerPps, -1); | ||
| 448 | } | 187 | } |
| 449 | 188 | SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); | |
| 450 | verticalOffset += STATS_PELS_PER_LINE; | 189 | if (audioMixerNode) { |
| 451 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarMixerStats.toUtf8().constData(), color); | 190 | STAT_UPDATE(audioMixerKbps, roundf( |
| 452 | 191 | bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) + | |
| 453 | stringstream downloads; | 192 | bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer))); |
| 454 | downloads << "Downloads: "; | 193 | STAT_UPDATE(audioMixerPps, roundf( |
| 455 | foreach (Resource* resource, ResourceCache::getLoadingRequests()) { | 194 | bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) + |
| 456 | downloads << (int)(resource->getProgress() * 100.0f) << "% "; | 195 | bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer))); |
| 196 | } else { | ||
| 197 | STAT_UPDATE(audioMixerKbps, -1); | ||
| 198 | STAT_UPDATE(audioMixerPps, -1); | ||
| 457 | } | 199 | } |
| 458 | downloads << "(" << ResourceCache::getPendingRequestCount() << " pending)"; | ||
| 459 | |||
| 460 | verticalOffset += STATS_PELS_PER_LINE; | ||
| 461 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, downloads.str().c_str(), color); | ||
| 462 | } | ||
| 463 | 200 | ||
| 464 | verticalOffset = STATS_PELS_INITIALOFFSET; | 201 | STAT_UPDATE(downloads, ResourceCache::getLoadingRequests().size()); |
| 465 | horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3; | 202 | STAT_UPDATE(downloadsPending, ResourceCache::getPendingRequestCount()); |
| 466 | 203 | // TODO fix to match original behavior | |
| 467 | lines = _expanded ? 10 : 3; | 204 | //stringstream downloads; |
| 468 | 205 | //downloads << "Downloads: "; | |
| 469 | drawBackground(backgroundColor, horizontalOffset, 0, canvasSize.x - horizontalOffset, | 206 | //foreach(Resource* resource, ) { |
| 470 | (lines + 1) * STATS_PELS_PER_LINE); | 207 | // downloads << (int)(resource->getProgress() * 100.0f) << "% "; |
| 471 | horizontalOffset += 5; | 208 | //} |
| 472 | 209 | //downloads << "(" << << " pending)"; | |
| 473 | // Model/Entity render details | 210 | } // expanded avatar column |
| 474 | octreeStats.str(""); | 211 | |
| 475 | octreeStats << "Triangles: " << _renderDetails._trianglesRendered | 212 | // Fourth column, octree stats |
| 476 | << " / Quads:" << _renderDetails._quadsRendered | ||
| 477 | << " / Material Switches:" << _renderDetails._materialSwitches; | ||
| 478 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); | ||
| 479 | |||
| 480 | if (_expanded) { | ||
| 481 | octreeStats.str(""); | ||
| 482 | octreeStats << " Mesh Parts Rendered Opaque: " << _renderDetails._opaque._rendered | ||
| 483 | << " / Translucent:" << _renderDetails._translucent._rendered; | ||
| 484 | verticalOffset += STATS_PELS_PER_LINE; | ||
| 485 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); | ||
| 486 | |||
| 487 | octreeStats.str(""); | ||
| 488 | octreeStats << " Opaque considered: " << _renderDetails._opaque._considered | ||
| 489 | << " / Out of view:" << _renderDetails._opaque._outOfView | ||
| 490 | << " / Too small:" << _renderDetails._opaque._tooSmall; | ||
| 491 | verticalOffset += STATS_PELS_PER_LINE; | ||
| 492 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); | ||
| 493 | |||
| 494 | octreeStats.str(""); | ||
| 495 | octreeStats << " Translucent considered: " << _renderDetails._translucent._considered | ||
| 496 | << " / Out of view:" << _renderDetails._translucent._outOfView | ||
| 497 | << " / Too small:" << _renderDetails._translucent._tooSmall; | ||
| 498 | verticalOffset += STATS_PELS_PER_LINE; | ||
| 499 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); | ||
| 500 | } | ||
| 501 | |||
| 502 | // iterate all the current voxel stats, and list their sending modes, and total voxel counts | ||
| 503 | std::stringstream sendingMode(""); | ||
| 504 | sendingMode << "Octree Sending Mode: ["; | ||
| 505 | int serverCount = 0; | 213 | int serverCount = 0; |
| 506 | int movingServerCount = 0; | 214 | int movingServerCount = 0; |
| 507 | unsigned long totalNodes = 0; | 215 | unsigned long totalNodes = 0; |
| 508 | unsigned long totalInternal = 0; | 216 | unsigned long totalInternal = 0; |
| 509 | unsigned long totalLeaves = 0; | 217 | unsigned long totalLeaves = 0; |
| 218 | std::stringstream sendingModeStream(""); | ||
| 219 | sendingModeStream << "["; | ||
| 510 | NodeToOctreeSceneStats* octreeServerSceneStats = Application::getInstance()->getOcteeSceneStats(); | 220 | NodeToOctreeSceneStats* octreeServerSceneStats = Application::getInstance()->getOcteeSceneStats(); |
| 511 | for(NodeToOctreeSceneStatsIterator i = octreeServerSceneStats->begin(); i != octreeServerSceneStats->end(); i++) { | 221 | for (NodeToOctreeSceneStatsIterator i = octreeServerSceneStats->begin(); i != octreeServerSceneStats->end(); i++) { |
| 512 | //const QUuid& uuid = i->first; | 222 | //const QUuid& uuid = i->first; |
| 513 | OctreeSceneStats& stats = i->second; | 223 | OctreeSceneStats& stats = i->second; |
| 514 | serverCount++; | 224 | serverCount++; |
| 515 | if (_expanded) { | 225 | if (_expanded) { |
| 516 | if (serverCount > 1) { | 226 | if (serverCount > 1) { |
| 517 | sendingMode << ","; | 227 | sendingModeStream << ","; |
| 518 | } | 228 | } |
| 519 | if (stats.isMoving()) { | 229 | if (stats.isMoving()) { |
| 520 | sendingMode << "M"; | 230 | sendingModeStream << "M"; |
| 521 | movingServerCount++; | 231 | movingServerCount++; |
| 522 | } else { | 232 | } else { |
| 523 | sendingMode << "S"; | 233 | sendingModeStream << "S"; |
| 524 | } | 234 | } |
| 525 | } | 235 | } |
| 526 | 236 | ||
| @@ -528,32 +238,35 @@ void Stats::display( · 1 unmodified line | |||
| 528 | totalNodes += stats.getTotalElements(); | 238 | totalNodes += stats.getTotalElements(); |
| 529 | if (_expanded) { | 239 | if (_expanded) { |
| 530 | totalInternal += stats.getTotalInternal(); | 240 | totalInternal += stats.getTotalInternal(); |
| 531 | totalLeaves += stats.getTotalLeaves(); | 241 | totalLeaves += stats.getTotalLeaves(); |
| 532 | } | 242 | } |
| 533 | } | 243 | } |
| 534 | if (_expanded) { | 244 | if (_expanded) { |
| 535 | if (serverCount == 0) { | 245 | if (serverCount == 0) { |
| 536 | sendingMode << "---"; | 246 | sendingModeStream << "---"; |
| 537 | } | 247 | } |
| 538 | sendingMode << "] " << serverCount << " servers"; | 248 | sendingModeStream << "] " << serverCount << " servers"; |
| 539 | if (movingServerCount > 0) { | 249 | if (movingServerCount > 0) { |
| 540 | sendingMode << " <SCENE NOT STABLE>"; | 250 | sendingModeStream << " <SCENE NOT STABLE>"; |
| 541 | } else { | 251 | } else { |
| 542 | sendingMode << " <SCENE STABLE>"; | 252 | sendingModeStream << " <SCENE STABLE>"; |
| 543 | } | 253 | } |
| 544 | verticalOffset += STATS_PELS_PER_LINE; | 254 | QString sendingModeResult = sendingModeStream.str().c_str(); |
| 545 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)sendingMode.str().c_str(), color); | 255 | STAT_UPDATE(sendingMode, sendingModeResult); |
| 546 | } | 256 | } |
| 547 | 257 | ||
| 548 | // Incoming packets | 258 | // Incoming packets |
| 259 | QLocale locale(QLocale::English); | ||
| 260 | auto voxelPacketsToProcess = qApp->getOctreePacketProcessor().packetsToProcessCount(); | ||
| 549 | if (_expanded) { | 261 | if (_expanded) { |
| 550 | octreeStats.str(""); | 262 | std::stringstream octreeStats; |
| 551 | QString packetsString = locale.toString((int)voxelPacketsToProcess); | 263 | QString packetsString = locale.toString((int)voxelPacketsToProcess); |
| 552 | QString maxString = locale.toString((int)_recentMaxPackets); | 264 | QString maxString = locale.toString((int)_recentMaxPackets); |
| 553 | octreeStats << "Octree Packets to Process: " << qPrintable(packetsString) | 265 | octreeStats << "Octree Packets to Process: " << qPrintable(packetsString) |
| 554 | << " [Recent Max: " << qPrintable(maxString) << "]"; | 266 | << " [Recent Max: " << qPrintable(maxString) << "]"; |
| 555 | verticalOffset += STATS_PELS_PER_LINE; | 267 | QString str = octreeStats.str().c_str(); |
| 556 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); | 268 | STAT_UPDATE(packetStats, str); |
| 269 | // drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); | ||
| 557 | } | 270 | } |
| 558 | 271 | ||
| 559 | if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) { | 272 | if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) { |
| @@ -562,60 +275,102 @@ void Stats::display( · 2 unmodified lines | |||
| 562 | } | 275 | } |
| 563 | if (voxelPacketsToProcess == 0) { | 276 | if (voxelPacketsToProcess == 0) { |
| 564 | _resetRecentMaxPacketsSoon = true; | 277 | _resetRecentMaxPacketsSoon = true; |
| 565 | } else { | 278 | } else if (voxelPacketsToProcess > _recentMaxPackets) { |
| 566 | if (voxelPacketsToProcess > _recentMaxPackets) { | 279 | _recentMaxPackets = voxelPacketsToProcess; |
| 567 | _recentMaxPackets = voxelPacketsToProcess; | ||
| 568 | } | ||
| 569 | } | 280 | } |
| 570 | 281 | ||
| 571 | QString serversTotalString = locale.toString((uint)totalNodes); // consider adding: .rightJustified(10, ' '); | ||
| 572 | unsigned long localTotal = OctreeElement::getNodeCount(); | ||
| 573 | QString localTotalString = locale.toString((uint)localTotal); // consider adding: .rightJustified(10, ' '); | ||
| 574 | |||
| 575 | // Server Octree Elements | 282 | // Server Octree Elements |
| 576 | if (!_expanded) { | 283 | STAT_UPDATE(serverElements, totalNodes); |
| 577 | octreeStats.str(""); | 284 | STAT_UPDATE(localElements, OctreeElement::getNodeCount()); |
| 578 | octreeStats << "Octree Elements Server: " << qPrintable(serversTotalString) | ||
| 579 | << " Local:" << qPrintable(localTotalString); | ||
| 580 | verticalOffset += STATS_PELS_PER_LINE; | ||
| 581 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); | ||
| 582 | } | ||
| 583 | 285 | ||
| 584 | if (_expanded) { | 286 | if (_expanded) { |
| 585 | octreeStats.str(""); | 287 | STAT_UPDATE(serverInternal, totalInternal); |
| 586 | octreeStats << "Octree Elements -"; | 288 | STAT_UPDATE(serverLeaves, totalLeaves); |
| 587 | verticalOffset += STATS_PELS_PER_LINE; | 289 | // Local Voxels |
| 588 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); | 290 | STAT_UPDATE(localInternal, OctreeElement::getInternalNodeCount()); |
| 291 | STAT_UPDATE(localLeaves, OctreeElement::getLeafNodeCount()); | ||
| 292 | // LOD Details | ||
| 293 | STAT_UPDATE(lodStatus, "You can see " + DependencyManager::get<LODManager>()->getLODFeedbackText()); | ||
| 294 | } | ||
| 589 | 295 | ||
| 590 | QString serversInternalString = locale.toString((uint)totalInternal); | 296 | bool performanceTimerIsActive = PerformanceTimer::isActive(); |
| 591 | QString serversLeavesString = locale.toString((uint)totalLeaves); | 297 | bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails); |
| 298 | if (displayPerf && performanceTimerIsActive) { | ||
| 299 | if (!_timingExpanded) { | ||
| 300 | _timingExpanded = true; | ||
| 301 | emit timingExpandedChanged(); | ||
| 302 | } | ||
| 303 | PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up | ||
| 592 | 304 | ||
| 593 | octreeStats.str(""); | 305 | // we will also include room for 1 line per timing record and a header of 4 lines |
| 594 | octreeStats << " Server: " << qPrintable(serversTotalString) << | 306 | // Timing details... |
| 595 | " Internal: " << qPrintable(serversInternalString) << | ||
| 596 | " Leaves: " << qPrintable(serversLeavesString); | ||
| 597 | verticalOffset += STATS_PELS_PER_LINE; | ||
| 598 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); | ||
| 599 | 307 | ||
| 600 | // Local Voxels | 308 | // First iterate all the records, and for the ones that should be included, insert them into |
| 601 | unsigned long localInternal = OctreeElement::getInternalNodeCount(); | 309 | // a new Map sorted by average time... |
| 602 | unsigned long localLeaves = OctreeElement::getLeafNodeCount(); | 310 | bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen); |
| 603 | QString localInternalString = locale.toString((uint)localInternal); | 311 | QMap<float, QString> sortedRecords; |
| 604 | QString localLeavesString = locale.toString((uint)localLeaves); | 312 | const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords(); |
| 605 | 313 | QMapIterator<QString, PerformanceTimerRecord> i(allRecords); | |
| 606 | octreeStats.str(""); | 314 | |
| 607 | octreeStats << " Local: " << qPrintable(serversTotalString) << | 315 | while (i.hasNext()) { |
| 608 | " Internal: " << qPrintable(localInternalString) << | 316 | i.next(); |
| 609 | " Leaves: " << qPrintable(localLeavesString) << ""; | 317 | if (includeTimingRecord(i.key())) { |
| 610 | verticalOffset += STATS_PELS_PER_LINE; | 318 | float averageTime = (float)i.value().getMovingAverage() / (float)USECS_PER_MSEC; |
| 611 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); | 319 | sortedRecords.insertMulti(averageTime, i.key()); |
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | int linesDisplayed = 0; | ||
| 324 | QMapIterator<float, QString> j(sortedRecords); | ||
| 325 | j.toBack(); | ||
| 326 | QString perfLines; | ||
| 327 | while (j.hasPrevious()) { | ||
| 328 | j.previous(); | ||
| 329 | static const QChar noBreakingSpace = QChar::Nbsp; | ||
| 330 | QString functionName = j.value(); | ||
| 331 | const PerformanceTimerRecord& record = allRecords.value(functionName); | ||
| 332 | perfLines += QString("%1: %2 [%3]\n"). | ||
| 333 | arg(QString(qPrintable(functionName)), 90, noBreakingSpace). | ||
| 334 | arg((float)record.getMovingAverage() / (float)USECS_PER_MSEC, 8, 'f', 3, noBreakingSpace). | ||
| 335 | arg((int)record.getCount(), 6, 10, noBreakingSpace); | ||
| 336 | linesDisplayed++; | ||
| 337 | if (onlyDisplayTopTen && linesDisplayed == 10) { | ||
| 338 | break; | ||
| 339 | } | ||
| 340 | } | ||
| 341 | _timingStats = perfLines; | ||
| 342 | emit timingStatsChanged(); | ||
| 343 | } else if (_timingExpanded) { | ||
| 344 | _timingExpanded = false; | ||
| 345 | emit timingExpandedChanged(); | ||
| 612 | } | 346 | } |
| 347 | } | ||
| 348 | |||
| 349 | void Stats::setRenderDetails(const RenderDetails& details) { | ||
| 350 | STAT_UPDATE(triangles, details._trianglesRendered); | ||
| 351 | STAT_UPDATE(quads, details._quadsRendered); | ||
| 352 | STAT_UPDATE(materialSwitches, details._materialSwitches); | ||
| 353 | if (_expanded) { | ||
| 354 | STAT_UPDATE(meshOpaque, details._opaque._rendered); | ||
| 355 | STAT_UPDATE(meshTranslucent, details._opaque._rendered); | ||
| 356 | STAT_UPDATE(opaqueConsidered, details._opaque._considered); | ||
| 357 | STAT_UPDATE(opaqueOutOfView, details._opaque._outOfView); | ||
| 358 | STAT_UPDATE(opaqueTooSmall, details._opaque._tooSmall); | ||
| 359 | STAT_UPDATE(translucentConsidered, details._translucent._considered); | ||
| 360 | STAT_UPDATE(translucentOutOfView, details._translucent._outOfView); | ||
| 361 | STAT_UPDATE(translucentTooSmall, details._translucent._tooSmall); | ||
| 362 | } | ||
| 363 | } | ||
| 364 | |||
| 365 | |||
| 366 | /* | ||
| 367 | // display expanded or contracted stats | ||
| 368 | void Stats::display( | ||
| 369 | int voxelPacketsToProcess) | ||
| 370 | { | ||
| 371 | // iterate all the current voxel stats, and list their sending modes, and total voxel counts | ||
| 613 | 372 | ||
| 614 | // LOD Details | ||
| 615 | octreeStats.str(""); | ||
| 616 | QString displayLODDetails = DependencyManager::get<LODManager>()->getLODFeedbackText(); | ||
| 617 | octreeStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed()); | ||
| 618 | verticalOffset += STATS_PELS_PER_LINE; | ||
| 619 | drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); | ||
| 620 | } | 373 | } |
| 621 | 374 | ||
| 375 | |||
| 376 | */ | ||
| @@ -1,8 +1,5 @@ | |||
| 1 | // | 1 | // |
| 2 | // Stats.h | 2 | // Created by Bradley Austin Davis 2015/06/17 |
| 3 | // interface/src/ui | ||
| 4 | // | ||
| 5 | // Created by Lucas Crisman on 22/03/14. | ||
| 6 | // Copyright 2013 High Fidelity, Inc. | 3 | // Copyright 2013 High Fidelity, Inc. |
| 7 | // | 4 | // |
| 8 | // Distributed under the Apache License, Version 2.0. | 5 | // Distributed under the Apache License, Version 2.0. |
| @@ -13,47 +10,139 @@ · 4 unmodified lines | |||
| 13 | #define hifi_Stats_h | 10 | #define hifi_Stats_h |
| 14 | 11 | ||
| 15 | #include <QObject> | 12 | #include <QObject> |
| 13 | #include <QQuickItem> | ||
| 14 | #include <QVector3D> | ||
| 16 | 15 | ||
| 16 | #include <OffscreenUi.h> | ||
| 17 | #include <RenderArgs.h> | 17 | #include <RenderArgs.h> |
| 18 | 18 | ||
| 19 | class Stats: public QObject { | 19 | #define STATS_PROPERTY(type, name, initialValue) \ |
| 20 | Q_PROPERTY(type name READ name NOTIFY name##Changed) \ | ||
| 21 | public: \ | ||
| 22 | type name() { return _##name; }; \ | ||
| 23 | private: \ | ||
| 24 | type _##name{ initialValue }; | ||
| 25 | |||
| 26 | |||
| 27 | class Stats : public QQuickItem { | ||
| 20 | Q_OBJECT | 28 | Q_OBJECT |
| 29 | HIFI_QML_DECL | ||
| 30 | Q_PROPERTY(bool expanded READ isExpanded WRITE setExpanded NOTIFY expandedChanged) | ||
| 31 | Q_PROPERTY(bool timingExpanded READ isTimingExpanded NOTIFY timingExpandedChanged) | ||
| 32 | Q_PROPERTY(QString monospaceFont READ monospaceFont) | ||
| 33 | |||
| 34 | STATS_PROPERTY(int, serverCount, 0) | ||
| 35 | STATS_PROPERTY(int, framerate, 0) | ||
| 36 | STATS_PROPERTY(int, avatarCount, 0) | ||
| 37 | STATS_PROPERTY(int, packetInCount, 0) | ||
| 38 | STATS_PROPERTY(int, packetOutCount, 0) | ||
| 39 | STATS_PROPERTY(float, mbpsIn, 0) | ||
| 40 | STATS_PROPERTY(float, mbpsOut, 0) | ||
| 41 | STATS_PROPERTY(int, audioPing, 0) | ||
| 42 | STATS_PROPERTY(int, avatarPing, 0) | ||
| 43 | STATS_PROPERTY(int, entitiesPing, 0) | ||
| 44 | STATS_PROPERTY(QVector3D, position, QVector3D(0, 0, 0) ) | ||
| 45 | STATS_PROPERTY(float, velocity, 0) | ||
| 46 | STATS_PROPERTY(float, yaw, 0) | ||
| 47 | STATS_PROPERTY(int, avatarMixerKbps, 0) | ||
| 48 | STATS_PROPERTY(int, avatarMixerPps, 0) | ||
| 49 | STATS_PROPERTY(int, audioMixerKbps, 0) | ||
| 50 | STATS_PROPERTY(int, audioMixerPps, 0) | ||
| 51 | STATS_PROPERTY(int, downloads, 0) | ||
| 52 | STATS_PROPERTY(int, downloadsPending, 0) | ||
| 53 | STATS_PROPERTY(int, triangles, 0) | ||
| 54 | STATS_PROPERTY(int, quads, 0) | ||
| 55 | STATS_PROPERTY(int, materialSwitches, 0) | ||
| 56 | STATS_PROPERTY(int, meshOpaque, 0) | ||
| 57 | STATS_PROPERTY(int, meshTranslucent, 0) | ||
| 58 | STATS_PROPERTY(int, opaqueConsidered, 0) | ||
| 59 | STATS_PROPERTY(int, opaqueOutOfView, 0) | ||
| 60 | STATS_PROPERTY(int, opaqueTooSmall, 0) | ||
| 61 | STATS_PROPERTY(int, translucentConsidered, 0) | ||
| 62 | STATS_PROPERTY(int, translucentOutOfView, 0) | ||
| 63 | STATS_PROPERTY(int, translucentTooSmall, 0) | ||
| 64 | STATS_PROPERTY(QString, sendingMode, QString()) | ||
| 65 | STATS_PROPERTY(QString, packetStats, QString()) | ||
| 66 | STATS_PROPERTY(QString, lodStatus, QString()) | ||
| 67 | STATS_PROPERTY(QString, timingStats, QString()) | ||
| 68 | STATS_PROPERTY(int, serverElements, 0) | ||
| 69 | STATS_PROPERTY(int, serverInternal, 0) | ||
| 70 | STATS_PROPERTY(int, serverLeaves, 0) | ||
| 71 | STATS_PROPERTY(int, localElements, 0) | ||
| 72 | STATS_PROPERTY(int, localInternal, 0) | ||
| 73 | STATS_PROPERTY(int, localLeaves, 0) | ||
| 21 | 74 | ||
| 22 | public: | 75 | public: |
| 23 | static Stats* getInstance(); | 76 | static Stats* getInstance(); |
| 24 | 77 | ||
| 25 | Stats(); | 78 | Stats(QQuickItem* parent = nullptr); |
| 26 | |||
| 27 | static void drawBackground(unsigned int rgba, int x, int y, int width, int height); | ||
| 28 | |||
| 29 | void toggleExpanded(); | ||
| 30 | bool isExpanded() { return _expanded; } | ||
| 31 | |||
| 32 | void checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseDragStartedY, int horizontalOffset); | ||
| 33 | void resetWidth(int width, int horizontalOffset); | ||
| 34 | void display(const float* color, int horizontalOffset, float fps, int inPacketsPerSecond, int outPacketsPerSecond, | ||
| 35 | int inKbitsPerSecond, int outKbitsPerSecond, int voxelPacketsToProcess); | ||
| 36 | bool includeTimingRecord(const QString& name); | 79 | bool includeTimingRecord(const QString& name); |
| 37 | 80 | void setRenderDetails(const RenderDetails& details); | |
| 38 | void setRenderDetails(const RenderDetails& details) { _renderDetails = details; } | 81 | const QString& monospaceFont() { |
| 39 | 82 | return _monospaceFont; | |
| 40 | private: | 83 | } |
| 41 | static Stats* _sharedInstance; | 84 | void updateStats(); |
| 42 | 85 | ||
| 43 | bool _expanded; | 86 | bool isExpanded() { return _expanded; } |
| 87 | bool isTimingExpanded() { return _timingExpanded; } | ||
| 44 | 88 | ||
| 45 | int _recentMaxPackets; // recent max incoming voxel packets to process | 89 | void setExpanded(bool expanded) { |
| 46 | bool _resetRecentMaxPacketsSoon; | 90 | if (_expanded != expanded) { |
| 91 | _expanded = expanded; | ||
| 92 | emit expandedChanged(); | ||
| 93 | } | ||
| 94 | } | ||
| 47 | 95 | ||
| 48 | int _generalStatsWidth; | 96 | signals: |
| 49 | int _bandwidthStatsWidth; | 97 | void expandedChanged(); |
| 50 | int _pingStatsWidth; | 98 | void timingExpandedChanged(); |
| 51 | int _geoStatsWidth; | 99 | void serverCountChanged(); |
| 52 | int _octreeStatsWidth; | 100 | void framerateChanged(); |
| 101 | void avatarCountChanged(); | ||
| 102 | void packetInCountChanged(); | ||
| 103 | void packetOutCountChanged(); | ||
| 104 | void mbpsInChanged(); | ||
| 105 | void mbpsOutChanged(); | ||
| 106 | void audioPingChanged(); | ||
| 107 | void avatarPingChanged(); | ||
| 108 | void entitiesPingChanged(); | ||
| 109 | void positionChanged(); | ||
| 110 | void velocityChanged(); | ||
| 111 | void yawChanged(); | ||
| 112 | void avatarMixerKbpsChanged(); | ||
| 113 | void avatarMixerPpsChanged(); | ||
| 114 | void audioMixerKbpsChanged(); | ||
| 115 | void audioMixerPpsChanged(); | ||
| 116 | void downloadsChanged(); | ||
| 117 | void downloadsPendingChanged(); | ||
| 118 | void trianglesChanged(); | ||
| 119 | void quadsChanged(); | ||
| 120 | void materialSwitchesChanged(); | ||
| 121 | void meshOpaqueChanged(); | ||
| 122 | void meshTranslucentChanged(); | ||
| 123 | void opaqueConsideredChanged(); | ||
| 124 | void opaqueOutOfViewChanged(); | ||
| 125 | void opaqueTooSmallChanged(); | ||
| 126 | void translucentConsideredChanged(); | ||
| 127 | void translucentOutOfViewChanged(); | ||
| 128 | void translucentTooSmallChanged(); | ||
| 129 | void sendingModeChanged(); | ||
| 130 | void packetStatsChanged(); | ||
| 131 | void lodStatusChanged(); | ||
| 132 | void serverElementsChanged(); | ||
| 133 | void serverInternalChanged(); | ||
| 134 | void serverLeavesChanged(); | ||
| 135 | void localElementsChanged(); | ||
| 136 | void localInternalChanged(); | ||
| 137 | void localLeavesChanged(); | ||
| 138 | void timingStatsChanged(); | ||
| 53 | 139 | ||
| 54 | int _lastHorizontalOffset; | 140 | private: |
| 55 | 141 | int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process | |
| 56 | RenderDetails _renderDetails; | 142 | bool _resetRecentMaxPacketsSoon{ true }; |
| 143 | bool _expanded{ false }; | ||
| 144 | bool _timingExpanded{ false }; | ||
| 145 | QString _monospaceFont; | ||
| 57 | }; | 146 | }; |
| 58 | 147 | ||
| 59 | #endif // hifi_Stats_h | 148 | #endif // hifi_Stats_h |
| @@ -53,7 +53,7 @@ void UserInputMapper::resetDevice(uint16 deviceID) { · 52 unmodified lines | |||
| 53 | 53 | ||
| 54 | int UserInputMapper::findDevice(QString name) { | 54 | int UserInputMapper::findDevice(QString name) { |
| 55 | for (auto device : _registeredDevices) { | 55 | for (auto device : _registeredDevices) { |
| 56 | if (device.second->_name == name) { | 56 | if (device.second->_name.split(" (")[0] == name) { |
| 57 | return device.first; | 57 | return device.first; |
| 58 | } | 58 | } |
| 59 | } | 59 | } |
| @@ -11,6 +11,7 @@ · 10 unmodified lines | |||
| 11 | // include this before QGLWidget, which includes an earlier version of OpenGL | 11 | // include this before QGLWidget, which includes an earlier version of OpenGL |
| 12 | #include "InterfaceConfig.h" | 12 | #include "InterfaceConfig.h" |
| 13 | 13 | ||
| 14 | #include <DeferredLightingEffect.h> | ||
| 14 | #include <GeometryCache.h> | 15 | #include <GeometryCache.h> |
| 15 | #include <GlowEffect.h> | 16 | #include <GlowEffect.h> |
| 16 | #include <SharedUtil.h> | 17 | #include <SharedUtil.h> |
| @@ -75,20 +76,20 @@ void Circle3DOverlay::render(RenderArgs* args) { · 58 unmodified lines | |||
| 75 | 76 | ||
| 76 | float alpha = getAlpha(); | 77 | float alpha = getAlpha(); |
| 77 | 78 | ||
| 78 | if (alpha == 0.0) { | 79 | if (alpha == 0.0f) { |
| 79 | return; // do nothing if our alpha is 0, we're not visible | 80 | return; // do nothing if our alpha is 0, we're not visible |
| 80 | } | 81 | } |
| 81 | 82 | ||
| 82 | // Create the circle in the coordinates origin | 83 | // Create the circle in the coordinates origin |
| 83 | float outerRadius = getOuterRadius(); | 84 | float outerRadius = getOuterRadius(); |
| 84 | float innerRadius = getInnerRadius(); // only used in solid case | 85 | float innerRadius = getInnerRadius(); // only used in solid case |
| 85 | float startAt = getStartAt(); | 86 | float startAt = getStartAt(); |
| 86 | float endAt = getEndAt(); | 87 | float endAt = getEndAt(); |
| 87 | 88 | ||
| 88 | bool geometryChanged = (startAt != _lastStartAt || endAt != _lastEndAt || | 89 | bool geometryChanged = (startAt != _lastStartAt || endAt != _lastEndAt || |
| 89 | innerRadius != _lastInnerRadius || outerRadius != _lastOuterRadius); | 90 | innerRadius != _lastInnerRadius || outerRadius != _lastOuterRadius); |
| 90 | 91 | ||
| 91 | 92 | ||
| 92 | const float FULL_CIRCLE = 360.0f; | 93 | const float FULL_CIRCLE = 360.0f; |
| 93 | const float SLICES = 180.0f; // The amount of segment to create the circle | 94 | const float SLICES = 180.0f; // The amount of segment to create the circle |
| 94 | const float SLICE_ANGLE = FULL_CIRCLE / SLICES; | 95 | const float SLICE_ANGLE = FULL_CIRCLE / SLICES; |
| @@ -101,195 +102,178 @@ void Circle3DOverlay::render(RenderArgs* args) { · 6 unmodified lines | |||
| 101 | bool colorChanged = colorX.red != _lastColor.red || colorX.green != _lastColor.green || colorX.blue != _lastColor.blue; | 102 | bool colorChanged = colorX.red != _lastColor.red || colorX.green != _lastColor.green || colorX.blue != _lastColor.blue; |
| 102 | _lastColor = colorX; | 103 | _lastColor = colorX; |
| 103 | 104 | ||
| 104 | glDisable(GL_LIGHTING); | 105 | auto geometryCache = DependencyManager::get<GeometryCache>(); |
| 105 | |||
| 106 | glm::vec3 position = getPosition(); | ||
| 107 | glm::vec3 center = getCenter(); | ||
| 108 | glm::vec2 dimensions = getDimensions(); | ||
| 109 | glm::quat rotation = getRotation(); | ||
| 110 | |||
| 111 | float glowLevel = getGlowLevel(); | ||
| 112 | Glower* glower = NULL; | ||
| 113 | if (glowLevel > 0.0f) { | ||
| 114 | glower = new Glower(glowLevel); | ||
| 115 | } | ||
| 116 | |||
| 117 | glPushMatrix(); | ||
| 118 | glTranslatef(position.x, position.y, position.z); | ||
| 119 | glm::vec3 axis = glm::axis(rotation); | ||
| 120 | glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); | ||
| 121 | glPushMatrix(); | ||
| 122 | glm::vec3 positionToCenter = center - position; | ||
| 123 | glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); | ||
| 124 | glScalef(dimensions.x / 2.0f, dimensions.y / 2.0f, 1.0f); | ||
| 125 | 106 | ||
| 126 | glLineWidth(_lineWidth); | 107 | Transform transform; |
| 127 | 108 | transform.setTranslation(getCenter()); | |
| 128 | auto geometryCache = DependencyManager::get<GeometryCache>(); | 109 | transform.setRotation(getRotation()); |
| 110 | transform.setScale(glm::vec3(getDimensions(), 0.01f)); | ||
| 111 | |||
| 112 | |||
| 113 | auto& batch = *args->_batch; | ||
| 114 | batch._glLineWidth(_lineWidth); | ||
| 115 | batch.setModelTransform(transform); | ||
| 116 | DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, false, false); | ||
| 117 | |||
| 118 | // for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise | ||
| 119 | // we just draw a line... | ||
| 120 | if (getIsSolid()) { | ||
| 121 | if (_quadVerticesID == GeometryCache::UNKNOWN_ID) { | ||
| 122 | _quadVerticesID = geometryCache->allocateID(); | ||
| 123 | } | ||
| 124 | |||
| 125 | if (geometryChanged || colorChanged) { | ||
| 129 | 126 | ||
| 130 | // for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise | 127 | QVector<glm::vec2> points; |
| 131 | // we just draw a line... | 128 | |
| 132 | if (getIsSolid()) { | 129 | float angle = startAt; |
| 133 | if (_quadVerticesID == GeometryCache::UNKNOWN_ID) { | 130 | float angleInRadians = glm::radians(angle); |
| 134 | _quadVerticesID = geometryCache->allocateID(); | 131 | glm::vec2 firstInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius); |
| 135 | } | 132 | glm::vec2 firstOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius); |
| 133 | |||
| 134 | points << firstInnerPoint << firstOuterPoint; | ||
| 135 | |||
| 136 | while (angle < endAt) { | ||
| 137 | angleInRadians = glm::radians(angle); | ||
| 138 | glm::vec2 thisInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius); | ||
| 139 | glm::vec2 thisOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius); | ||
| 136 | 140 | ||
| 137 | if (geometryChanged || colorChanged) { | 141 | points << thisOuterPoint << thisInnerPoint; |
| 138 | |||
| 139 | QVector<glm::vec2> points; | ||
| 140 | |||
| 141 | float angle = startAt; | ||
| 142 | float angleInRadians = glm::radians(angle); | ||
| 143 | glm::vec2 firstInnerPoint(cos(angleInRadians) * innerRadius, sin(angleInRadians) * innerRadius); | ||
| 144 | glm::vec2 firstOuterPoint(cos(angleInRadians) * outerRadius, sin(angleInRadians) * outerRadius); | ||
| 145 | |||
| 146 | points << firstInnerPoint << firstOuterPoint; | ||
| 147 | |||
| 148 | while (angle < endAt) { | ||
| 149 | angleInRadians = glm::radians(angle); | ||
| 150 | glm::vec2 thisInnerPoint(cos(angleInRadians) * innerRadius, sin(angleInRadians) * innerRadius); | ||
| 151 | glm::vec2 thisOuterPoint(cos(angleInRadians) * outerRadius, sin(angleInRadians) * outerRadius); | ||
| 152 | |||
| 153 | points << thisOuterPoint << thisInnerPoint; | ||
| 154 | 142 | ||
| 155 | angle += SLICE_ANGLE; | 143 | angle += SLICE_ANGLE; |
| 156 | } | 144 | } |
| 157 | 145 | ||
| 158 | // get the last slice portion.... | 146 | // get the last slice portion.... |
| 159 | angle = endAt; | 147 | angle = endAt; |
| 160 | angleInRadians = glm::radians(angle); | 148 | angleInRadians = glm::radians(angle); |
| 161 | glm::vec2 lastInnerPoint(cos(angleInRadians) * innerRadius, sin(angleInRadians) * innerRadius); | 149 | glm::vec2 lastInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius); |
| 162 | glm::vec2 lastOuterPoint(cos(angleInRadians) * outerRadius, sin(angleInRadians) * outerRadius); | 150 | glm::vec2 lastOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius); |
| 163 | 151 | ||
| 164 | points << lastOuterPoint << lastInnerPoint; | 152 | points << lastOuterPoint << lastInnerPoint; |
| 165 | |||
| 166 | geometryCache->updateVertices(_quadVerticesID, points, color); | ||
| 167 | } | ||
| 168 | |||
| 169 | geometryCache->renderVertices(gpu::QUAD_STRIP, _quadVerticesID); | ||
| 170 | |||
| 171 | } else { | ||
| 172 | if (_lineVerticesID == GeometryCache::UNKNOWN_ID) { | ||
| 173 | _lineVerticesID = geometryCache->allocateID(); | ||
| 174 | } | ||
| 175 | |||
| 176 | if (geometryChanged || colorChanged) { | ||
| 177 | QVector<glm::vec2> points; | ||
| 178 | |||
| 179 | float angle = startAt; | ||
| 180 | float angleInRadians = glm::radians(angle); | ||
| 181 | glm::vec2 firstPoint(cos(angleInRadians) * outerRadius, sin(angleInRadians) * outerRadius); | ||
| 182 | points << firstPoint; | ||
| 183 | |||
| 184 | while (angle < endAt) { | ||
| 185 | angle += SLICE_ANGLE; | ||
| 186 | angleInRadians = glm::radians(angle); | ||
| 187 | glm::vec2 thisPoint(cos(angleInRadians) * outerRadius, sin(angleInRadians) * outerRadius); | ||
| 188 | points << thisPoint; | ||
| 189 | |||
| 190 | if (getIsDashedLine()) { | ||
| 191 | angle += SLICE_ANGLE / 2.0f; // short gap | ||
| 192 | angleInRadians = glm::radians(angle); | ||
| 193 | glm::vec2 dashStartPoint(cos(angleInRadians) * outerRadius, sin(angleInRadians) * outerRadius); | ||
| 194 | points << dashStartPoint; | ||
| 195 | } | ||
| 196 | } | ||
| 197 | 153 | ||
| 198 | // get the last slice portion.... | 154 | geometryCache->updateVertices(_quadVerticesID, points, color); |
| 199 | angle = endAt; | 155 | } |
| 200 | angleInRadians = glm::radians(angle); | 156 | |
| 201 | glm::vec2 lastPoint(cos(angleInRadians) * outerRadius, sin(angleInRadians) * outerRadius); | 157 | geometryCache->renderVertices(batch, gpu::QUAD_STRIP, _quadVerticesID); |
| 202 | points << lastPoint; | 158 | |
| 203 | 159 | } else { | |
| 204 | geometryCache->updateVertices(_lineVerticesID, points, color); | 160 | if (_lineVerticesID == GeometryCache::UNKNOWN_ID) { |
| 205 | } | 161 | _lineVerticesID = geometryCache->allocateID(); |
| 206 | 162 | } | |
| 163 | |||
| 164 | if (geometryChanged || colorChanged) { | ||
| 165 | QVector<glm::vec2> points; | ||
| 166 | |||
| 167 | float angle = startAt; | ||
| 168 | float angleInRadians = glm::radians(angle); | ||
| 169 | glm::vec2 firstPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius); | ||
| 170 | points << firstPoint; | ||
| 171 | |||
| 172 | while (angle < endAt) { | ||
| 173 | angle += SLICE_ANGLE; | ||
| 174 | angleInRadians = glm::radians(angle); | ||
| 175 | glm::vec2 thisPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius); | ||
| 176 | points << thisPoint; | ||
| 177 | |||
| 207 | if (getIsDashedLine()) { | 178 | if (getIsDashedLine()) { |
| 208 | geometryCache->renderVertices(gpu::LINES, _lineVerticesID); | 179 | angle += SLICE_ANGLE / 2.0f; // short gap |
| 209 | } else { | 180 | angleInRadians = glm::radians(angle); |
| 210 | geometryCache->renderVertices(gpu::LINE_STRIP, _lineVerticesID); | 181 | glm::vec2 dashStartPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius); |
| 182 | points << dashStartPoint; | ||
| 211 | } | 183 | } |
| 212 | } | 184 | } |
| 213 | 185 | ||
| 214 | // draw our tick marks | 186 | // get the last slice portion.... |
| 215 | // for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise | 187 | angle = endAt; |
| 216 | // we just draw a line... | 188 | angleInRadians = glm::radians(angle); |
| 217 | if (getHasTickMarks()) { | 189 | glm::vec2 lastPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius); |
| 218 | 190 | points << lastPoint; | |
| 219 | if (_majorTicksVerticesID == GeometryCache::UNKNOWN_ID) { | 191 | |
| 220 | _majorTicksVerticesID = geometryCache->allocateID(); | 192 | geometryCache->updateVertices(_lineVerticesID, points, color); |
| 221 | } | 193 | } |
| 222 | if (_minorTicksVerticesID == GeometryCache::UNKNOWN_ID) { | 194 | |
| 223 | _minorTicksVerticesID = geometryCache->allocateID(); | 195 | if (getIsDashedLine()) { |
| 224 | } | 196 | geometryCache->renderVertices(batch, gpu::LINES, _lineVerticesID); |
| 225 | 197 | } else { | |
| 226 | if (geometryChanged) { | 198 | geometryCache->renderVertices(batch, gpu::LINE_STRIP, _lineVerticesID); |
| 227 | QVector<glm::vec2> majorPoints; | 199 | } |
| 228 | QVector<glm::vec2> minorPoints; | 200 | } |
| 229 | 201 | ||
| 230 | // draw our major tick marks | 202 | // draw our tick marks |
| 231 | if (getMajorTickMarksAngle() > 0.0f && getMajorTickMarksLength() != 0.0f) { | 203 | // for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise |
| 204 | // we just draw a line... | ||
| 205 | if (getHasTickMarks()) { | ||
| 206 | |||
| 207 | if (_majorTicksVerticesID == GeometryCache::UNKNOWN_ID) { | ||
| 208 | _majorTicksVerticesID = geometryCache->allocateID(); | ||
| 209 | } | ||
| 210 | if (_minorTicksVerticesID == GeometryCache::UNKNOWN_ID) { | ||
| 211 | _minorTicksVerticesID = geometryCache->allocateID(); | ||
| 212 | } | ||
| 213 | |||
| 214 | if (geometryChanged) { | ||
| 215 | QVector<glm::vec2> majorPoints; | ||
| 216 | QVector<glm::vec2> minorPoints; | ||
| 217 | |||
| 218 | // draw our major tick marks | ||
| 219 | if (getMajorTickMarksAngle() > 0.0f && getMajorTickMarksLength() != 0.0f) { | ||
| 232 | 220 | ||
| 233 | float tickMarkAngle = getMajorTickMarksAngle(); | 221 | float tickMarkAngle = getMajorTickMarksAngle(); |
| 234 | float angle = startAt - fmod(startAt, tickMarkAngle) + tickMarkAngle; | 222 | float angle = startAt - fmodf(startAt, tickMarkAngle) + tickMarkAngle; |
| 235 | float angleInRadians = glm::radians(angle); | 223 | float angleInRadians = glm::radians(angle); |
| 236 | float tickMarkLength = getMajorTickMarksLength(); | 224 | float tickMarkLength = getMajorTickMarksLength(); |
| 237 | float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius; | 225 | float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius; |
| 238 | float endRadius = startRadius + tickMarkLength; | 226 | float endRadius = startRadius + tickMarkLength; |
| 239 | |||
| 240 | while (angle <= endAt) { | ||
| 241 | angleInRadians = glm::radians(angle); | ||
| 242 | |||
| 243 | glm::vec2 thisPointA(cos(angleInRadians) * startRadius, sin(angleInRadians) * startRadius); | ||
| 244 | glm::vec2 thisPointB(cos(angleInRadians) * endRadius, sin(angleInRadians) * endRadius); | ||
| 245 | |||
| 246 | majorPoints << thisPointA << thisPointB; | ||
| 247 | 227 | ||
| 248 | angle += tickMarkAngle; | 228 | while (angle <= endAt) { |
| 249 | } | 229 | angleInRadians = glm::radians(angle); |
| 250 | } | 230 | |
| 251 | 231 | glm::vec2 thisPointA(cosf(angleInRadians) * startRadius, sinf(angleInRadians) * startRadius); | |
| 252 | // draw our minor tick marks | 232 | glm::vec2 thisPointB(cosf(angleInRadians) * endRadius, sinf(angleInRadians) * endRadius); |
| 253 | if (getMinorTickMarksAngle() > 0.0f && getMinorTickMarksLength() != 0.0f) { | 233 | |
| 234 | majorPoints << thisPointA << thisPointB; | ||
| 235 | |||
| 236 | angle += tickMarkAngle; | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | // draw our minor tick marks | ||
| 241 | if (getMinorTickMarksAngle() > 0.0f && getMinorTickMarksLength() != 0.0f) { | ||
| 254 | 242 | ||
| 255 | float tickMarkAngle = getMinorTickMarksAngle(); | 243 | float tickMarkAngle = getMinorTickMarksAngle(); |
| 256 | float angle = startAt - fmod(startAt, tickMarkAngle) + tickMarkAngle; | 244 | float angle = startAt - fmodf(startAt, tickMarkAngle) + tickMarkAngle; |
| 257 | float angleInRadians = glm::radians(angle); | 245 | float angleInRadians = glm::radians(angle); |
| 258 | float tickMarkLength = getMinorTickMarksLength(); | 246 | float tickMarkLength = getMinorTickMarksLength(); |
| 259 | float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius; | 247 | float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius; |
| 260 | float endRadius = startRadius + tickMarkLength; | 248 | float endRadius = startRadius + tickMarkLength; |
| 261 | |||
| 262 | while (angle <= endAt) { | ||
| 263 | angleInRadians = glm::radians(angle); | ||
| 264 | |||
| 265 | glm::vec2 thisPointA(cos(angleInRadians) * startRadius, sin(angleInRadians) * startRadius); | ||
| 266 | glm::vec2 thisPointB(cos(angleInRadians) * endRadius, sin(angleInRadians) * endRadius); | ||
| 267 | |||
| 268 | minorPoints << thisPointA << thisPointB; | ||
| 269 | 249 | ||
| 270 | angle += tickMarkAngle; | 250 | while (angle <= endAt) { |
| 271 | } | 251 | angleInRadians = glm::radians(angle); |
| 272 | } | 252 | |
| 273 | 253 | glm::vec2 thisPointA(cosf(angleInRadians) * startRadius, sinf(angleInRadians) * startRadius); | |
| 274 | xColor majorColorX = getMajorTickMarksColor(); | 254 | glm::vec2 thisPointB(cosf(angleInRadians) * endRadius, sinf(angleInRadians) * endRadius); |
| 275 | glm::vec4 majorColor(majorColorX.red / MAX_COLOR, majorColorX.green / MAX_COLOR, majorColorX.blue / MAX_COLOR, alpha); | 255 | |
| 276 | 256 | minorPoints << thisPointA << thisPointB; | |
| 277 | geometryCache->updateVertices(_majorTicksVerticesID, majorPoints, majorColor); | 257 | |
| 278 | 258 | angle += tickMarkAngle; | |
| 279 | xColor minorColorX = getMinorTickMarksColor(); | ||
| 280 | glm::vec4 minorColor(minorColorX.red / MAX_COLOR, minorColorX.green / MAX_COLOR, minorColorX.blue / MAX_COLOR, alpha); | ||
| 281 | |||
| 282 | geometryCache->updateVertices(_minorTicksVerticesID, minorPoints, minorColor); | ||
| 283 | } | 259 | } |
| 284 | |||
| 285 | geometryCache->renderVertices(gpu::LINES, _majorTicksVerticesID); | ||
| 286 | |||
| 287 | geometryCache->renderVertices(gpu::LINES, _minorTicksVerticesID); | ||
| 288 | } | 260 | } |
| 289 | 261 | ||
| 290 | 262 | xColor majorColorX = getMajorTickMarksColor(); | |
| 291 | glPopMatrix(); | 263 | glm::vec4 majorColor(majorColorX.red / MAX_COLOR, majorColorX.green / MAX_COLOR, majorColorX.blue / MAX_COLOR, alpha); |
| 292 | glPopMatrix(); | 264 | |
| 265 | geometryCache->updateVertices(_majorTicksVerticesID, majorPoints, majorColor); | ||
| 266 | |||
| 267 | xColor minorColorX = getMinorTickMarksColor(); | ||
| 268 | glm::vec4 minorColor(minorColorX.red / MAX_COLOR, minorColorX.green / MAX_COLOR, minorColorX.blue / MAX_COLOR, alpha); | ||
| 269 | |||
| 270 | geometryCache->updateVertices(_minorTicksVerticesID, minorPoints, minorColor); | ||
| 271 | } | ||
| 272 | |||
| 273 | geometryCache->renderVertices(batch, gpu::LINES, _majorTicksVerticesID); | ||
| 274 | |||
| 275 | geometryCache->renderVertices(batch, gpu::LINES, _minorTicksVerticesID); | ||
| 276 | } | ||
| 293 | 277 | ||
| 294 | if (geometryChanged) { | 278 | if (geometryChanged) { |
| 295 | _lastStartAt = startAt; | 279 | _lastStartAt = startAt; |
| @@ -297,15 +281,11 @@ void Circle3DOverlay::render(RenderArgs* args) { · 1 unmodified line | |||
| 297 | _lastInnerRadius = innerRadius; | 281 | _lastInnerRadius = innerRadius; |
| 298 | _lastOuterRadius = outerRadius; | 282 | _lastOuterRadius = outerRadius; |
| 299 | } | 283 | } |
| 300 | |||
| 301 | if (glower) { | ||
| 302 | delete glower; | ||
| 303 | } | ||
| 304 | } | 284 | } |
| 305 | 285 | ||
| 306 | void Circle3DOverlay::setProperties(const QScriptValue &properties) { | 286 | void Circle3DOverlay::setProperties(const QScriptValue &properties) { |
| 307 | Planar3DOverlay::setProperties(properties); | 287 | Planar3DOverlay::setProperties(properties); |
| 308 | 288 | ||
| 309 | QScriptValue startAt = properties.property("startAt"); | 289 | QScriptValue startAt = properties.property("startAt"); |
| 310 | if (startAt.isValid()) { | 290 | if (startAt.isValid()) { |
| 311 | setStartAt(startAt.toVariant().toFloat()); | 291 | setStartAt(startAt.toVariant().toFloat()); |
| @@ -415,7 +395,7 @@ QScriptValue Circle3DOverlay::getProperty(const QString& property) { · 103 unmodified lines | |||
| 415 | } | 395 | } |
| 416 | 396 | ||
| 417 | 397 | ||
| 418 | bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin, | 398 | bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin, |
| 419 | const glm::vec3& direction, float& distance, BoxFace& face) { | 399 | const glm::vec3& direction, float& distance, BoxFace& face) { |
| 420 | 400 | ||
| 421 | bool intersects = Planar3DOverlay::findRayIntersection(origin, direction, distance, face); | 401 | bool intersects = Planar3DOverlay::findRayIntersection(origin, direction, distance, face); |
| @@ -32,6 +32,16 @@ Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) : · 31 unmodified lines | |||
| 32 | Line3DOverlay::~Line3DOverlay() { | 32 | Line3DOverlay::~Line3DOverlay() { |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | AABox Line3DOverlay::getBounds() const { | ||
| 36 | auto start = _position + _start; | ||
| 37 | auto end = _position + _end; | ||
| 38 | |||
| 39 | auto min = glm::min(start, end); | ||
| 40 | auto max = glm::max(start, end); | ||
| 41 | |||
| 42 | return AABox(min, max - min); | ||
| 43 | } | ||
| 44 | |||
| 35 | void Line3DOverlay::render(RenderArgs* args) { | 45 | void Line3DOverlay::render(RenderArgs* args) { |
| 36 | if (!_visible) { | 46 | if (!_visible) { |
| 37 | return; // do nothing if we're not visible | 47 | return; // do nothing if we're not visible |
| @@ -21,6 +21,7 @@ public: · 20 unmodified lines | |||
| 21 | Line3DOverlay(const Line3DOverlay* line3DOverlay); | 21 | Line3DOverlay(const Line3DOverlay* line3DOverlay); |
| 22 | ~Line3DOverlay(); | 22 | ~Line3DOverlay(); |
| 23 | virtual void render(RenderArgs* args); | 23 | virtual void render(RenderArgs* args); |
| 24 | virtual AABox getBounds() const override; | ||
| 24 | 25 | ||
| 25 | // getters | 26 | // getters |
| 26 | const glm::vec3& getStart() const { return _start; } | 27 | const glm::vec3& getStart() const { return _start; } |
| @@ -258,7 +258,7 @@ void Overlays::deleteOverlay(unsigned int id) { · 257 unmodified lines | |||
| 258 | unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { | 258 | unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { |
| 259 | glm::vec2 pointCopy = point; | 259 | glm::vec2 pointCopy = point; |
| 260 | if (qApp->isHMDMode()) { | 260 | if (qApp->isHMDMode()) { |
| 261 | pointCopy = qApp->getApplicationOverlay().screenToOverlay(point); | 261 | pointCopy = qApp->getApplicationCompositor().screenToOverlay(point); |
| 262 | } | 262 | } |
| 263 | 263 | ||
| 264 | QReadLocker lock(&_lock); | 264 | QReadLocker lock(&_lock); |
| @@ -80,6 +80,7 @@ void TextOverlay::render(RenderArgs* args) { · 79 unmodified lines | |||
| 80 | 80 | ||
| 81 | glm::vec2 topLeft(left, top); | 81 | glm::vec2 topLeft(left, top); |
| 82 | glm::vec2 bottomRight(right, bottom); | 82 | glm::vec2 bottomRight(right, bottom); |
| 83 | glBindTexture(GL_TEXTURE_2D, 0); | ||
| 83 | DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, quadColor); | 84 | DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, quadColor); |
| 84 | 85 | ||
| 85 | const int leftAdjust = -1; // required to make text render relative to left edge of bounds | 86 | const int leftAdjust = -1; // required to make text render relative to left edge of bounds |