History

Merge with master

Sam Gondelman committed 11 years ago

Commit 19ec0802 parents 778e19a 0bef506214 files · +6701 −6001Browse files

Showing the changes to interface/src (61 of 214 files). Show all

Unified · Split

61 files
  1. interface/src/Application.cpp +115 −156
  2. interface/src/Application.h +37 −28
  3. interface/src/Camera.cpp +0 −16
  4. interface/src/Camera.h +4 −10
  5. interface/src/GLCanvas.cpp +2 −2
  6. interface/src/InterfaceActionFactory.cpp +3 −3
  7. interface/src/LODManager.cpp +3 −3
  8. interface/src/Menu.cpp +6 −0
  9. interface/src/Menu.h +1 −0
  10. interface/src/ModelPropertiesDialog.cpp +26 −26
  11. interface/src/Stars.cpp +8 −10
  12. interface/src/UIUtil.cpp +1 −1
  13. interface/src/Util.cpp +25 −22
  14. interface/src/audio/AudioIOStatsRenderer.cpp +69 −55
  15. interface/src/audio/AudioToolBox.cpp +0 −114
  16. interface/src/audio/AudioToolBox.h +0 −36
  17. interface/src/avatar/Avatar.cpp +110 −121
  18. interface/src/avatar/Avatar.h +5 −5
  19. interface/src/avatar/AvatarActionHold.cpp +10 −1
  20. interface/src/avatar/AvatarManager.cpp +17 −5
  21. interface/src/avatar/AvatarManager.h +1 −1
  22. interface/src/avatar/Head.cpp +13 −6
  23. interface/src/avatar/MyAvatar.cpp +81 −81
  24. interface/src/avatar/MyAvatar.h +1 −1
  25. interface/src/devices/CameraToolBox.cpp +0 −121
  26. interface/src/devices/CameraToolBox.h +0 −45
  27. interface/src/devices/DdeFaceTracker.cpp +6 −5
  28. interface/src/devices/OculusManager.cpp +469 −436
  29. interface/src/devices/OculusManager.h +7 −170
  30. interface/src/devices/SDL2Manager.cpp +2 −0
  31. interface/src/devices/SixenseManager.cpp +58 −50
  32. interface/src/devices/TV3DManager.cpp +9 −9
  33. interface/src/main.cpp +1 −1
  34. interface/src/scripting/ControllerScriptingInterface.cpp +1 −1
  35. interface/src/scripting/ControllerScriptingInterface.h +1 −0
  36. interface/src/scripting/HMDScriptingInterface.cpp +3 −3
  37. interface/src/scripting/HMDScriptingInterface.h +2 −2
  38. interface/src/starfield/renderer/Renderer.cpp +9 −0
  39. interface/src/ui/AddressBarDialog.cpp +24 −0
  40. interface/src/ui/AddressBarDialog.h +13 −0
  41. interface/src/ui/ApplicationCompositor.cpp +761 −0
  42. interface/src/ui/ApplicationCompositor.h +120 −0
  43. interface/src/ui/ApplicationOverlay.cpp +116 −1172
  44. interface/src/ui/ApplicationOverlay.h +14 −118
  45. interface/src/ui/AvatarInputs.cpp +134 −0
  46. interface/src/ui/AvatarInputs.h +59 −0
  47. interface/src/ui/FlowLayout.cpp +9 −0
  48. interface/src/ui/HMDToolsDialog.cpp +4 −59
  49. interface/src/ui/HMDToolsDialog.h +0 −4
  50. interface/src/ui/LogDialog.cpp +9 −0
  51. interface/src/ui/PreferencesDialog.cpp +2 −2
  52. interface/src/ui/RearMirrorTools.cpp +0 −134
  53. interface/src/ui/RearMirrorTools.h +0 −56
  54. interface/src/ui/Stats.cpp +221 −466
  55. interface/src/ui/Stats.h +121 −32
  56. interface/src/ui/UserInputMapper.cpp +1 −1
  57. interface/src/ui/overlays/Circle3DOverlay.cpp +161 −181
  58. interface/src/ui/overlays/Line3DOverlay.cpp +10 −0
  59. interface/src/ui/overlays/Line3DOverlay.h +1 −0
  60. interface/src/ui/overlays/Overlays.cpp +1 −1
  61. interface/src/ui/overlays/TextOverlay.cpp +1 −0
interface/src/Application.cpp+115−156
@@ -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#endif139#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 render816 // 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
1070void Application::updateProjectionMatrix() {1074void 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 object1578 // 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
1621void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {1598void 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 event1617 // 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 visible1840 // 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 the1841 // 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_MAC1843#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 workaround1904 // attempt to reconnect the Oculus manager - it's possible this was a workaround
1940 // for the sixense crash1905 // 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
1961bool Application::mouseOnScreen() const {1925bool 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
1969int Application::getMouseX() const {1939ivec2 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
1977int 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
1985int Application::getMouseDragStartedX() const {1946ivec2 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
1994int Application::getMouseDragStartedY() const {1953ivec2 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
2003FaceTracker* Application::getActiveFaceTracker() {1957FaceTracker* 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 widget2199 // 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
2377void Application::rotationModeChanged() {
2378 if (!Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) {
2379 _myAvatar->setHeadPitch(0);
2380 }
2381}
2382
2437void Application::updateCamera(float deltaTime) {2383void 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 lasers3515 //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,-13619 // -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 device3673 // 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 matrix3690 // 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
4752void Application::initPlugins() {4695void Application::initPlugins() {
4753 OculusManager::init();
4754}4696}
4755 4697
4756void Application::shutdownPlugins() {4698void Application::shutdownPlugins() {
4757 OculusManager::deinit();
4758}4699}
4759 4700
4760glm::vec3 Application::getHeadPosition() const {4701glm::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
4777int Application::getTrueMouseX() const {4718ivec2 Application::getTrueMouse() const {
4778 return _glWidget->mapFromGlobal(QCursor::pos()).x();4719 return toGlm(_glWidget->mapFromGlobal(QCursor::pos()));
4779}
4780
4781int Application::getTrueMouseY() const {
4782 return _glWidget->mapFromGlobal(QCursor::pos()).y();
4783}4720}
4784 4721
4785bool Application::isThrottleRendering() const {4722bool 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
4749mat4 Application::getEyeProjection(int eye) const {
4750 if (isHMDMode()) {
4751 return OculusManager::getEyeProjection(eye);
4752 }
4753
4754 return _viewFrustum.getProjection();
4755}
4756
4757mat4 Application::getEyePose(int eye) const {
4758 if (isHMDMode()) {
4759 return OculusManager::getEyePose(eye);
4760 }
4761
4762 return mat4();
4763}
4764
4765mat4 Application::getHeadPose() const {
4766 if (isHMDMode()) {
4767 return OculusManager::getHeadPose();
4768 }
4769 return mat4();
4770}
interface/src/Application.h+37−28
@@ -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 servers562 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 touchpad564 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 offer566 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 view568 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_h683#endif // hifi_Application_h
interface/src/Camera.cpp+0−16
@@ -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
77void Camera::setHmdPosition(const glm::vec3& hmdPosition) {
78 _hmdPosition = hmdPosition;
79 if (_isKeepLookingAt) {
80 lookAt(_lookingAt);
81 }
82}
83
84void Camera::setHmdRotation(const glm::quat& hmdRotation) {
85 _hmdRotation = hmdRotation;
86 if (_isKeepLookingAt) {
87 lookAt(_lookingAt);
88 }
89}
90
91void Camera::setMode(CameraMode mode) {75void Camera::setMode(CameraMode mode) {
92 _mode = mode;76 _mode = mode;
93 emit modeUpdated(modeToString(mode));77 emit modeUpdated(modeToString(mode));
interface/src/Camera.h+4−10
@@ -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
58public slots:54public 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 cameras66 // 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};
interface/src/GLCanvas.cpp+2−2
@@ -39,11 +39,11 @@ bool GLCanvas::isThrottleRendering() const { · 38 unmodified lines
39}39}
40 40
41int GLCanvas::getDeviceWidth() const {41int GLCanvas::getDeviceWidth() const {
42 return width() * (windowHandle() ? windowHandle()->devicePixelRatio() : 1.0f);42 return width() * (windowHandle() ? (float)windowHandle()->devicePixelRatio() : 1.0f);
43}43}
44 44
45int GLCanvas::getDeviceHeight() const {45int GLCanvas::getDeviceHeight() const {
46 return height() * (windowHandle() ? windowHandle()->devicePixelRatio() : 1.0f);46 return height() * (windowHandle() ? (float)windowHandle()->devicePixelRatio() : 1.0f);
47}47}
48 48
49void GLCanvas::initializeGL() {49void GLCanvas::initializeGL() {
interface/src/InterfaceActionFactory.cpp+3−3
@@ -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);
interface/src/LODManager.cpp+3−3
@@ -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);
interface/src/Menu.cpp+6−0
@@ -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_MAC298#ifdef Q_OS_MAC
interface/src/Menu.h+1−0
@@ -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";
interface/src/ModelPropertiesDialog.cpp+26−26
@@ -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 fields88 // 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 indices104 // 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 }
interface/src/Stars.cpp+8−10
@@ -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
19Stars::Stars() : 19Stars::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
24Stars::~Stars() { 24Stars::~Stars() {
25 delete _controller; 25 delete _controller;
26}26}
27 27
28bool Stars::generate(unsigned numStars, unsigned seed) {28bool 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
33bool Stars::setResolution(unsigned k) { 33bool Stars::setResolution(unsigned k) {
34 return _controller->setResolution(k); 34 return _controller->setResolution(k);
35}35}
36 36
37void Stars::render(float fovY, float aspect, float nearZ, float alpha) {37void Stars::render(float fovY, float aspect, float nearZ, float alpha) {
38 // determine length of screen diagonal from quadrant height and aspect ratio38 // 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 diagonal42 // 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 stack45 // 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
interface/src/UIUtil.cpp+1−1
@@ -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}
interface/src/Util.cpp+25−22
@@ -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 things53 // 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
81static TextRenderer* textRenderer(int mono) {81static 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 accuracy141 // 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 generation177 // 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 Math207 // 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 test220 // 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
231bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNormalizedDirection,234bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNormalizedDirection,
interface/src/audio/AudioIOStatsRenderer.cpp+69−55
@@ -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 line129 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 line152 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 line162 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 line177 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
176void AudioIOStatsRenderer::renderAudioStreamStats(const AudioStreamStats* streamStats, int horizontalOffset, int& verticalOffset,190void 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}
interface/src/audio/AudioToolBox.cpp+0−114
@@ -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
24const int MUTE_ICON_SIZE = 24;
25
26AudioToolBox::AudioToolBox() :
27 _iconPulseTimeReference(usecTimestampNow())
28{
29}
30
31bool 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
39void 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}
interface/src/audio/AudioToolBox.h+0−36
@@ -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
19class AudioToolBox : public Dependency {
20 SINGLETON_DEPENDENCY
21public:
22 void render(int x, int y, int padding, bool boxed);
23
24 bool mousePressEvent(int x, int y);
25protected:
26 AudioToolBox();
27private:
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
interface/src/avatar/Avatar.cpp+110−121
@@ -57,7 +57,7 @@ const float CHAT_MESSAGE_SCALE = 0.0015f; · 56 unmodified lines
57const float CHAT_MESSAGE_HEIGHT = 0.1f;57const float CHAT_MESSAGE_HEIGHT = 0.1f;
58const float DISPLAYNAME_FADE_TIME = 0.5f;58const float DISPLAYNAME_FADE_TIME = 0.5f;
59const float DISPLAYNAME_FADE_FACTOR = pow(0.01f, 1.0f / DISPLAYNAME_FADE_TIME);59const float DISPLAYNAME_FADE_FACTOR = pow(0.01f, 1.0f / DISPLAYNAME_FADE_TIME);
60const float DISPLAYNAME_ALPHA = 0.95f;60const float DISPLAYNAME_ALPHA = 1.0f;
61const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f;61const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f;
62 62
63namespace render {63namespace render {
@@ -107,6 +107,7 @@ Avatar::Avatar() : · 43 unmodified lines
107}107}
108 108
109Avatar::~Avatar() {109Avatar::~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
282static TextRenderer3D* textRenderer(TextRendererType type) {283static 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_RADIUS469 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
520glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {519glm::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
657glm::vec3 Avatar::getDisplayNamePosition() {656glm::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
668float Avatar::calculateDisplayNameScaleFactor(const glm::vec3& textPosition, bool inHMD) {667Transform 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 coordinates669 // We assume textPosition is whithin the frustum
671 // We project a unit vector and check the difference in screen coordinates, to check which is the670 glm::vec3 textPosition = getDisplayNamePosition();
672 // correction scale needed671
673 // save the matrices for later scale correction factor672 // 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 identity674 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
713void Avatar::renderDisplayName(RenderArgs* renderArgs) {723void 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 name732 // 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
788bool Avatar::findRayIntersection(RayIntersectionInfo& intersection) const {780bool 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
990void 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
997void Avatar::setBillboard(const QByteArray& billboard) {982void 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
1092float Avatar::getHeadHeight() const {1077float 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
interface/src/avatar/Avatar.h+5−5
@@ -98,6 +98,7 @@ public: · 97 unmodified lines
98 //getters98 //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 penetrated132 /// \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();
interface/src/avatar/AvatarActionHold.cpp+10−1
@@ -29,6 +29,12 @@ AvatarActionHold::~AvatarActionHold() { · 28 unmodified lines
29 29
30void AvatarActionHold::updateActionWorker(float deltaTimeStep) {30void 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();
interface/src/avatar/AvatarManager.cpp+17−5
@@ -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 Avatars114 // 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// protected166// protected
156void AvatarManager::removeAvatarMotionState(Avatar* avatar) {167void 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 stuff171 // 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 list199 // 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 }
interface/src/avatar/AvatarManager.h+1−1
@@ -73,7 +73,7 @@ private: · 72 unmodified lines
73 // virtual overrides73 // 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;
interface/src/avatar/Head.cpp+13−6
@@ -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 saccades126 // 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 delay146 // 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;
interface/src/avatar/MyAvatar.cpp+81−81
@@ -59,10 +59,10 @@ const float MAX_WALKING_SPEED = 2.5f; // human walking speed · 58 unmodified lines
59const float MAX_BOOST_SPEED = 0.5f * MAX_WALKING_SPEED; // keyboard motor gets additive boost below this speed59const float MAX_BOOST_SPEED = 0.5f * MAX_WALKING_SPEED; // keyboard motor gets additive boost below this speed
60const float MIN_AVATAR_SPEED = 0.05f; // speed is set to zero below this60const 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.
64float MAX_AVATAR_SPEED = 300.0f;64float MAX_AVATAR_SPEED = 300.0f;
65float MAX_KEYBOARD_MOTOR_SPEED = MAX_AVATAR_SPEED; 65float MAX_KEYBOARD_MOTOR_SPEED = MAX_AVATAR_SPEED;
66float DEFAULT_KEYBOARD_MOTOR_TIMESCALE = 0.25f;66float DEFAULT_KEYBOARD_MOTOR_TIMESCALE = 0.25f;
67float MIN_SCRIPTED_MOTOR_TIMESCALE = 0.005f;67float MIN_SCRIPTED_MOTOR_TIMESCALE = 0.005f;
68float DEFAULT_SCRIPTED_MOTOR_TIMESCALE = 1.0e6f;68float 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
164void MyAvatar::simulate(float deltaTime) {164void MyAvatar::simulate(float deltaTime) {
165 PerformanceTimer perfTimer("simulate");165 PerformanceTimer perfTimer("simulate");
166 166
167 // Play back recording167 // 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 head185 // 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 billboard231 // 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 data235// Update avatar head rotation with sensor data
236void MyAvatar::updateFromTrackers(float deltaTime) {236void 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 screen261 // 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 Sphere322 // 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 AudioClient449 // 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. If685 // 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
848int MyAvatar::parseDataAtOffset(const QByteArray& packet, int offset) {848int 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 ALL852 // 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
1092glm::vec3 MyAvatar::getSkeletonPosition() const {1092glm::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
1137void MyAvatar::setScriptedMotorTimescale(float timescale) {1137void 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 velocity1139 // 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
1160void MyAvatar::attach(const QString& modelURL, const QString& jointName, const glm::vec3& translation,1160void 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
1200bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const {1200bool 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 mode1231 //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 efficiency1248 // 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 around1265 // 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 input1284 // 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 magnitude1292 // 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 ourselves1387 // update position ourselves
1388 applyPositionDelta(deltaTime * _targetVelocity);1388 applyPositionDelta(deltaTime * _targetVelocity);
1389 measureMotionDerivatives(deltaTime);1389 measureMotionDerivatives(deltaTime);
1390 } // else physics will move avatar later1390 } // else physics will move avatar later
1391 1391
1392 // update _moving flag based on speed1392 // 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 collide1410 // 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
1461void MyAvatar::increaseSize() {1461void 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
1468void MyAvatar::decreaseSize() {1468void 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
1475void MyAvatar::resetSize() {1475void 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
1480void MyAvatar::goToLocation(const glm::vec3& newPosition,1480void 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 target1493 // 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 away1500 // 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 pointer1548//Gets the tip position for the laser pointer
1549glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) {1549glm::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
1572void MyAvatar::relayDriveKeysToCharacterController() { 1572void MyAvatar::relayDriveKeysToCharacterController() {
1573 if (_driveKeys[UP] > 0.0f) {1573 if (_driveKeys[UP] > 0.0f) {
1574 _characterController.jump(); 1574 _characterController.jump();
1575 }1575 }
1576}1576}
interface/src/avatar/MyAvatar.h+1−1
@@ -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
interface/src/devices/CameraToolBox.cpp+0−121
@@ -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
23CameraToolBox::CameraToolBox() :
24 _iconPulseTimeReference(usecTimestampNow()),
25 _doubleClickTimer(NULL)
26{
27}
28
29CameraToolBox::~CameraToolBox() {
30 if (_doubleClickTimer) {
31 _doubleClickTimer->stop();
32 delete _doubleClickTimer;
33 }
34}
35
36bool 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
52bool 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
65void CameraToolBox::toggleMute() {
66 delete _doubleClickTimer;
67 _doubleClickTimer = NULL;
68
69 FaceTracker* faceTracker = Application::getInstance()->getSelectedFaceTracker();
70 if (faceTracker) {
71 faceTracker->toggleMute();
72 }
73}
74
75void 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}
interface/src/devices/CameraToolBox.h+0−45
@@ -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
20class CameraToolBox : public QObject, public Dependency {
21 Q_OBJECT
22 SINGLETON_DEPENDENCY
23
24public:
25 void render(int x, int y, bool boxed);
26 bool mousePressEvent(int x, int y);
27 bool mouseDoublePressEvent(int x, int y);
28
29protected:
30 CameraToolBox();
31 ~CameraToolBox();
32
33private slots:
34 void toggleMute();
35
36private:
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
interface/src/devices/DdeFaceTracker.cpp+6−5
@@ -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 values439 // 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 closed481 // 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 }
interface/src/devices/OculusManager.cpp+469−436
@@ -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
39template <typename Function>40template <typename Function>
40void for_each_eye(Function function) {41void 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}
56enum CalibrationState {
57 UNCALIBRATED,
58 WAITING_FOR_DELTA,
59 WAITING_FOR_ZERO,
60 WAITING_FOR_ZERO_HELD,
61 CALIBRATED
62};
63
64inline glm::mat4 toGlm(const ovrMatrix4f & om) {
65 return glm::transpose(glm::make_mat4(&om.M[0][0]));
66}
55 67
56#ifdef OVR_CLIENT_DISTORTION68inline glm::mat4 toGlm(const ovrFovPort & fovport, float nearPlane = 0.01f, float farPlane = 10000.0f) {
57ProgramObject OculusManager::_program;69 return toGlm(ovrMatrix4f_Projection(fovport, nearPlane, farPlane, true));
58int OculusManager::_textureLocation;70}
59int OculusManager::_eyeToSourceUVScaleLocation;
60int OculusManager::_eyeToSourceUVOffsetLocation;
61int OculusManager::_eyeRotationStartLocation;
62int OculusManager::_eyeRotationEndLocation;
63int OculusManager::_positionAttributeLocation;
64int OculusManager::_colorAttributeLocation;
65int OculusManager::_texCoord0AttributeLocation;
66int OculusManager::_texCoord1AttributeLocation;
67int OculusManager::_texCoord2AttributeLocation;
68ovrVector2f OculusManager::_UVScaleOffset[ovrEye_Count][2];
69GLuint OculusManager::_vertices[ovrEye_Count] = { 0, 0 };
70GLuint OculusManager::_indices[ovrEye_Count] = { 0, 0 };
71GLsizei OculusManager::_meshSize[ovrEye_Count] = { 0, 0 };
72ovrFrameTiming OculusManager::_hmdFrameTiming;
73bool OculusManager::_programInitialized = false;
74#endif
75 71
76ovrTexture OculusManager::_eyeTextures[ovrEye_Count];72inline glm::vec3 toGlm(const ovrVector3f & ov) {
77bool OculusManager::_isConnected = false;73 return glm::make_vec3(&ov.x);
78ovrHmd OculusManager::_ovrHmd;74}
79ovrFovPort OculusManager::_eyeFov[ovrEye_Count];
80ovrVector3f OculusManager::_eyeOffset[ovrEye_Count];
81ovrEyeRenderDesc OculusManager::_eyeRenderDesc[ovrEye_Count];
82ovrSizei OculusManager::_renderTargetSize;
83glm::mat4 OculusManager::_eyeProjection[ovrEye_Count];
84unsigned int OculusManager::_frameIndex = 0;
85bool OculusManager::_frameTimingActive = false;
86Camera* OculusManager::_camera = NULL;
87ovrEyeType OculusManager::_activeEye = ovrEye_Count;
88bool OculusManager::_hswDismissed = false;
89
90float OculusManager::CALIBRATION_DELTA_MINIMUM_LENGTH = 0.02f;
91float OculusManager::CALIBRATION_DELTA_MINIMUM_ANGLE = 5.0f * RADIANS_PER_DEGREE;
92float OculusManager::CALIBRATION_ZERO_MAXIMUM_LENGTH = 0.01f;
93float OculusManager::CALIBRATION_ZERO_MAXIMUM_ANGLE = 2.0f * RADIANS_PER_DEGREE;
94quint64 OculusManager::CALIBRATION_ZERO_HOLD_TIME = 3000000; // usec
95float OculusManager::CALIBRATION_MESSAGE_DISTANCE = 2.5f;
96OculusManager::CalibrationState OculusManager::_calibrationState;
97glm::vec3 OculusManager::_calibrationPosition;
98glm::quat OculusManager::_calibrationOrientation;
99quint64 OculusManager::_calibrationStartTime;
100int OculusManager::_calibrationMessage = 0;
101glm::vec3 OculusManager::_eyePositions[ovrEye_Count];
102// TODO expose this as a developer toggle
103bool OculusManager::_eyePerFrameMode = false;
104ovrEyeType OculusManager::_lastEyeRendered = ovrEye_Count;
105ovrSizei OculusManager::_recommendedTexSize = { 0, 0 };
106float OculusManager::_offscreenRenderScale = 1.0;
107 75
76inline glm::vec2 toGlm(const ovrVector2f & ov) {
77 return glm::make_vec2(&ov.x);
78}
108 79
109void OculusManager::initSdk() {80inline 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
117void OculusManager::shutdownSdk() {84inline 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
125void OculusManager::init() {88inline glm::quat toGlm(const ovrQuatf & oq) {
126#ifdef OVR_DIRECT_MODE89 return glm::make_quat(&oq.x);
127 initSdk();
128#endif
129}90}
130 91
131void OculusManager::deinit() {92inline glm::mat4 toGlm(const ovrPosef & op) {
132#ifdef OVR_DIRECT_MODE93 glm::mat4 orientation = glm::mat4_cast(toGlm(op.Orientation));
133 shutdownSdk();94 glm::mat4 translation = glm::translate(glm::mat4(), toGlm(op.Position));
134#endif95 return translation * orientation;
135}96}
136 97
137void OculusManager::connect() {98inline ovrMatrix4f ovrFromGlm(const glm::mat4 & m) {
138#ifndef OVR_DIRECT_MODE99 ovrMatrix4f result;
139 initSdk();100 glm::mat4 transposed(glm::transpose(m));
140#endif101 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) {105inline ovrVector3f ovrFromGlm(const glm::vec3 & v) {
145 UserActivityLogger::getInstance().connectedDevice("hmd", "oculus");106 return{ v.x, v.y, v.z };
107}
108
109inline ovrVector2f ovrFromGlm(const glm::vec2 & v) {
110 return{ v.x, v.y };
111}
112
113inline ovrSizei ovrFromGlm(const glm::uvec2 & v) {
114 return{ (int)v.x, (int)v.y };
115}
116
117inline 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
128template <typename C>
129struct 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
145protected:
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
155struct 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));172protected:
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 = 0177 }
160 | ovrDistortionCap_Vignette178
161 | ovrDistortionCap_Overdrive179 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 size190 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 ignored205
206
207// We use a FBO to wrap the mirror texture because it makes it easier to
208// render to the screen via glBlitFramebuffer
209struct 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_DISTORTION219 }
190 if (!_programInitialized) {220
191 // Shader program221private:
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
238static SwapFramebufferWrapper* _swapFbo{ nullptr };
239static MirrorFramebufferWrapper* _mirrorFbo{ nullptr };
240static ovrLayerEyeFov _sceneLayer;
241
242#else
243
244static ovrTexture _eyeTextures[ovrEye_Count];
245static GlWindow* _outputWindow{ nullptr };
211 246
212 //Generate the distortion VBOs
213 generateDistortionMesh();
214#endif247#endif
215 } else {248
249static bool _isConnected = false;
250static ovrHmd _ovrHmd;
251static ovrFovPort _eyeFov[ovrEye_Count];
252static ovrVector3f _eyeOffset[ovrEye_Count];
253static ovrEyeRenderDesc _eyeRenderDesc[ovrEye_Count];
254static ovrSizei _renderTargetSize;
255static glm::mat4 _eyeProjection[ovrEye_Count];
256static unsigned int _frameIndex = 0;
257static bool _frameTimingActive = false;
258static Camera* _camera = NULL;
259static ovrEyeType _activeEye = ovrEye_Count;
260static bool _hswDismissed = false;
261
262static const float CALIBRATION_DELTA_MINIMUM_LENGTH = 0.02f;
263static const float CALIBRATION_DELTA_MINIMUM_ANGLE = 5.0f * RADIANS_PER_DEGREE;
264static const float CALIBRATION_ZERO_MAXIMUM_LENGTH = 0.01f;
265static const float CALIBRATION_ZERO_MAXIMUM_ANGLE = 2.0f * RADIANS_PER_DEGREE;
266static const quint64 CALIBRATION_ZERO_HOLD_TIME = 3000000; // usec
267static const float CALIBRATION_MESSAGE_DISTANCE = 2.5f;
268static CalibrationState _calibrationState;
269static glm::vec3 _calibrationPosition;
270static glm::quat _calibrationOrientation;
271static quint64 _calibrationStartTime;
272static int _calibrationMessage = 0;
273static glm::vec3 _eyePositions[ovrEye_Count];
274// TODO expose this as a developer toggle
275static bool _eyePerFrameMode = false;
276static ovrEyeType _lastEyeRendered = ovrEye_Count;
277static ovrSizei _recommendedTexSize = { 0, 0 };
278static float _offscreenRenderScale = 1.0;
279static glm::mat4 _combinedProjection;
280static ovrPosef _eyeRenderPoses[ovrEye_Count];
281static ovrRecti _eyeViewports[ovrEye_Count];
282static ovrVector3f _eyeOffsets[ovrEye_Count];
283
284glm::vec3 OculusManager::getLeftEyePosition() { return _eyePositions[ovrEye_Left]; }
285glm::vec3 OculusManager::getRightEyePosition() { return _eyePositions[ovrEye_Right]; }
286
287void 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 that322 // 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 OR429//Disconnects and deallocates the OR
224void OculusManager::disconnect() {430void 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 again456 // Prepare to potentially have to dismiss the HSW again
228 // if the user re-enables VR457 // 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
250void OculusManager::positionCalibrationBillboard(Text3DOverlay* billboard) {462void 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
261void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) {473void 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
367void 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
422bool OculusManager::isConnected() {579bool 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 purposes583//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 timing595//Ends frame timing
443void OculusManager::endFrameTiming() {596void 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 ratio601//Sets the camera FoV and aspect ratio
452void OculusManager::configureCamera(Camera& camera) {602void 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 eyes604 // 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 FOV608 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 active611//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#endif619#endif
476
477 // Every so often do some additional timing calculations and debug output
478 bool debugFrame = 0 == _frameIndex % 400;
479 620
480#if 0621#ifndef Q_OS_WIN
481 // Try to measure the amount of time taken to do the distortion622
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 particular623 // 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 than624 // 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 actual625 // 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#endif637#endif
525 638
526 639
527 //beginFrameTiming must be called before display640 //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 fbo683 //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 eye685 // 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 eye693 // 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 doing699 // 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 viewport738 // restore our normal viewport
639 glViewport(0, 0, deviceSize.width(), deviceSize.height());739 glViewport(0, 0, deviceSize.width(), deviceSize.height());
640 740
641#if 0741#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_DISTORTION745 // 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 color771 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#endif792#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
711void 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 sensors809//Tries to reconnect to the sensors
778void OculusManager::reset() {810void 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
785void 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
799glm::vec3 OculusManager::getRelativePosition() {816glm::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
828int OculusManager::getHMDScreen() {845int OculusManager::getHMDScreen() {
846#ifdef Q_OS_WIN
847 return -1;
848#else
829 int hmdScreenIndex = -1; // unknown849 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 hmd851 // 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 match870 // 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
902mat4 OculusManager::getEyeProjection(int eye) {
903 return _eyeProjection[eye];
879}904}
880 905
906mat4 OculusManager::getEyePose(int eye) {
907 return toGlm(_eyeRenderPoses[eye]);
908}
909
910mat4 OculusManager::getHeadPose() {
911 ovrTrackingState ts = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds());
912 return toGlm(ts.HeadPose.ThePose);
913}
interface/src/devices/OculusManager.h+7−170
@@ -13,8 +13,6 @@ · 12 unmodified lines
13#ifndef hifi_OculusManager_h13#ifndef hifi_OculusManager_h
14#define hifi_OculusManager_h14#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
23class QOpenGLContext;
25class Camera;24class Camera;
26class PalmData;
27class 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.
53class OculusManager {27class OculusManager {
54public:28public:
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
85private:
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 //Uniforms54 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
168inline glm::mat4 toGlm(const ovrMatrix4f & om) {
169 return glm::transpose(glm::make_mat4(&om.M[0][0]));
170}
171
172inline 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
176inline glm::vec3 toGlm(const ovrVector3f & ov) {
177 return glm::make_vec3(&ov.x);
178}
179
180inline glm::vec2 toGlm(const ovrVector2f & ov) {
181 return glm::make_vec2(&ov.x);
182}
183
184inline glm::uvec2 toGlm(const ovrSizei & ov) {
185 return glm::uvec2(ov.w, ov.h);
186}
187
188inline glm::quat toGlm(const ovrQuatf & oq) {
189 return glm::make_quat(&oq.x);
190}
191
192inline 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
198inline 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
205inline ovrVector3f ovrFromGlm(const glm::vec3 & v) {
206 return{ v.x, v.y, v.z };
207}
208
209inline ovrVector2f ovrFromGlm(const glm::vec2 & v) {
210 return{ v.x, v.y };
211}
212
213inline ovrSizei ovrFromGlm(const glm::uvec2 & v) {
214 return{ (int)v.x, (int)v.y };
215}
216
217inline ovrQuatf ovrFromGlm(const glm::quat & q) {
218 return{ q.x, q.y, q.z, q.w };
219}
220
221#endif // hifi_OculusManager_h58#endif // hifi_OculusManager_h
interface/src/devices/SDL2Manager.cpp+2−0
@@ -74,9 +74,11 @@ SDL2Manager* SDL2Manager::getInstance() { · 73 unmodified lines
74}74}
75 75
76void SDL2Manager::focusOutEvent() {76void 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
82void SDL2Manager::update() {84void SDL2Manager::update() {
interface/src/devices/SixenseManager.cpp+58−50
@@ -36,6 +36,8 @@ const float NECK_X = 0.25f; // meters · 35 unmodified lines
36const float NECK_Y = 0.3f; // meters36const float NECK_Y = 0.3f; // meters
37const float NECK_Z = 0.3f; // meters37const float NECK_Z = 0.3f; // meters
38 38
39const float CONTROLLER_THRESHOLD = 0.35f;
40
39#ifdef __APPLE__41#ifdef __APPLE__
40typedef int (*SixenseBaseFunction)();42typedef int (*SixenseBaseFunction)();
41typedef int (*SixenseTakeIntFunction)(int);43typedef int (*SixenseTakeIntFunction)(int);
@@ -71,25 +73,25 @@ SixenseManager::SixenseManager() : · 29 unmodified lines
71 73
72SixenseManager::~SixenseManager() {74SixenseManager::~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#endif80#endif
79 81
80 sixenseExit();82 sixenseExit();
81 }83 }
82 84
83#ifdef __APPLE__85#ifdef __APPLE__
84 delete _sixenseLibrary;86 delete _sixenseLibrary;
85#endif87#endif
86 88
87#endif89#endif
88}90}
89 91
90void SixenseManager::initialize() {92void SixenseManager::initialize() {
91#ifdef HAVE_SIXENSE93#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 orb99 // 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_FILENAME107#ifdef SIXENSE_LIB_FILENAME
106 _sixenseLibrary = new QLibrary(SIXENSE_LIB_FILENAME);108 _sixenseLibrary = new QLibrary(SIXENSE_LIB_FILENAME);
107#else109#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#endif115#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#endif127#endif
126 sixenseInit();128 sixenseInit();
127 129
128 _isInitialized = true;130 _isInitialized = true;
129 }131 }
130 132
131#endif133#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#endif142#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#endif162#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#endif190#endif
189 191
190 int maxControllers = sixenseGetMaxControllers();192 int maxControllers = sixenseGetMaxControllers();
191 193
192 // we only support two controllers194 // 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#endif203#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/orientation214 // 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 one216 // 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 station235 // 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 inactive239 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 hand242 // 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 scripts250 // 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 base259 // 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 Palm271 // 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 change275 // 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/sec282 palm->setRawVelocity(rawVelocity); // meters/sec
281 283
282 // adjustment for hydra controllers fit into hands284 // 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 Palm288 // 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 with301 // 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 velocity313 // Store the one fingertip in the palm structure so we can track velocity
312 const float FINGER_LENGTH = 0.3f; // meters314 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_SIXENSE337#endif // HAVE_SIXENSE
330}338}
331 339
332//Constants for getCursorPixelRangeMultiplier()340//Constants for getCursorPixelRangeMultiplier()
333const float MIN_PIXEL_RANGE_MULT = 0.4f;341const float MIN_PIXEL_RANGE_MULT = 0.4f;
334const float MAX_PIXEL_RANGE_MULT = 2.0f;342const float MAX_PIXEL_RANGE_MULT = 2.0f;
335const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01;343const 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 controllers345//Returns a multiplier to be applied to the cursor range for the controllers
338float SixenseManager::getCursorPixelRangeMult() const {346float 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 averages435 // 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 average440 // 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 out443 // 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 out462 // 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 on493 // 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
496void SixenseManager::emulateMouse(PalmData* palm, int index) {504void 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 orientation525 // 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 scaled532 // 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 if579 //We need separate coordinates for clicks, since we need to check if
572 //a magnification window was clicked on580 //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);
interface/src/devices/TV3DManager.cpp+9−9
@@ -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 matrix117 glLoadIdentity(); // reset projection matrix
111 glFrustum(eye.left, eye.right, eye.bottom, eye.top, nearZ, farZ); // set left view frustum118 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 view126 // render right side view
interface/src/main.cpp+1−1
@@ -23,7 +23,7 @@ · 22 unmodified lines
23#ifdef Q_OS_WIN23#ifdef Q_OS_WIN
24static BOOL CALLBACK enumWindowsCallback(HWND hWnd, LPARAM lParam) {24static BOOL CALLBACK enumWindowsCallback(HWND hWnd, LPARAM lParam) {
25 const UINT TIMEOUT = 200; // ms25 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;
interface/src/scripting/ControllerScriptingInterface.cpp+1−1
@@ -463,7 +463,7 @@ void ControllerScriptingInterface::resetDevice(unsigned int device) { · 462 unmodified lines
463}463}
464 464
465int ControllerScriptingInterface::findDevice(QString name) {465int ControllerScriptingInterface::findDevice(QString name) {
466 Application::getUserInputMapper()->findDevice(name);466 return Application::getUserInputMapper()->findDevice(name);
467}467}
468 468
469InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) :469InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) :
interface/src/scripting/ControllerScriptingInterface.h+1−0
@@ -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); }
interface/src/scripting/HMDScriptingInterface.cpp+3−3
@@ -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
33QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) {33QScriptValue 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;
interface/src/scripting/HMDScriptingInterface.h+2−2
@@ -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
29public slots:29public slots:
30 void toggleMagnifier() { Application::getInstance()->getApplicationOverlay().toggleMagnifier(); };30 void toggleMagnifier() { Application::getInstance()->getApplicationCompositor().toggleMagnifier(); };
31 31
32private:32private:
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;
interface/src/starfield/renderer/Renderer.cpp+9−0
@@ -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
15using namespace starfield;16using 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 vector60 // 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 construction88// renderer construction
interface/src/ui/AddressBarDialog.cpp+24−0
@@ -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
27void AddressBarDialog::hide() {41void AddressBarDialog::hide() {
@@ -35,6 +49,16 @@ void AddressBarDialog::loadAddress(const QString& address) { · 7 unmodified lines
35 }49 }
36}50}
37 51
52void AddressBarDialog::loadBack() {
53 qDebug() << "Called LoadBack";
54 DependencyManager::get<AddressManager>()->goBack();
55}
56
57void AddressBarDialog::loadForward() {
58 qDebug() << "Called LoadForward";
59 DependencyManager::get<AddressManager>()->goForward();
60}
61
38void AddressBarDialog::displayAddressOfflineMessage() {62void AddressBarDialog::displayAddressOfflineMessage() {
39 OffscreenUi::error("That user or place is currently offline");63 OffscreenUi::error("That user or place is currently offline");
40}64}
interface/src/ui/AddressBarDialog.h+13−0
@@ -18,9 +18,17 @@ class AddressBarDialog : public OffscreenQmlDialog · 17 unmodified lines
18{18{
19 Q_OBJECT19 Q_OBJECT
20 HIFI_QML_DECL20 HIFI_QML_DECL
21 Q_PROPERTY(bool backEnabled READ backEnabled NOTIFY backEnabledChanged)
22 Q_PROPERTY(bool forwardEnabled READ forwardEnabled NOTIFY forwardEnabledChanged)
21 23
22public:24public:
23 AddressBarDialog(QQuickItem* parent = nullptr);25 AddressBarDialog(QQuickItem* parent = nullptr);
26 bool backEnabled() { return _backEnabled; }
27 bool forwardEnabled() { return _forwardEnabled; }
28
29signals:
30 void backEnabledChanged();
31 void forwardEnabledChanged();
24 32
25protected:33protected:
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#endif46#endif
interface/src/ui/ApplicationCompositor.cpp+761−0
@@ -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
27static const float MAG_SPEED = 0.08f;
28
29static const quint64 MSECS_TO_USECS = 1000ULL;
30static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS;
31
32static const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };
33static const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f };
34static const float reticleSize = TWO_PI / 100.0f;
35
36static const float CONNECTION_STATUS_BORDER_COLOR[] = { 1.0f, 0.0f, 0.0f };
37static const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f;
38
39static const float CURSOR_PIXEL_SIZE = 32.0f;
40static const float MOUSE_PITCH_RANGE = 1.0f * PI;
41static const float MOUSE_YAW_RANGE = 0.5f * TWO_PI;
42static const glm::vec2 MOUSE_RANGE(MOUSE_YAW_RANGE, MOUSE_PITCH_RANGE);
43
44static gpu::BufferPointer _hemiVertices;
45static gpu::BufferPointer _hemiIndices;
46static int _hemiIndexCount{ 0 };
47EntityItemID ApplicationCompositor::_noItemId;
48static QString _tooltipId;
49
50// Return a point's cartesian coordinates on a sphere from pitch and yaw
51glm::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
59bool 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
114ApplicationCompositor::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
156ApplicationCompositor::~ApplicationCompositor() {
157}
158
159
160void 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
173void 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
223vec2 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.
240void 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
320void 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
346QPoint 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
376bool 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
394void 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
412void 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
523void 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
591void 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
661void 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
683glm::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
699glm::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
704glm::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
715glm::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
724glm::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
734glm::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
744glm::vec2 ApplicationCompositor::screenToOverlay(const glm::vec2& screenPos) const {
745 return sphericalToOverlay(screenToSpherical(screenPos));
746}
747
748glm::vec2 ApplicationCompositor::overlayToScreen(const glm::vec2& overlayPos) const {
749 return sphericalToScreen(overlayToSpherical(overlayPos));
750}
751
752void 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}
interface/src/ui/ApplicationCompositor.h+120−0
@@ -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
21class Camera;
22class PalmData;
23class RenderArgs;
24
25const float MAGNIFY_WIDTH = 220.0f;
26const float MAGNIFY_HEIGHT = 100.0f;
27const float MAGNIFY_MULT = 2.0f;
28
29const 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
34class ApplicationCompositor : public QObject {
35 Q_OBJECT
36public:
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
72private:
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
interface/src/ui/ApplicationOverlay.cpp+116−1172
@@ -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
37const float MAG_SPEED = 0.08f;
38
39const quint64 MSECS_TO_USECS = 1000ULL;
40 38
41const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };39const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };
42const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f };40const int AUDIO_METER_GAP = 5;
43const float reticleSize = TWO_PI / 100.0f;41const int MUTE_ICON_PADDING = 10;
44 42const vec4 CONNECTION_STATUS_BORDER_COLOR{ 1.0f, 0.0f, 0.0f, 0.8f };
45
46const float CONNECTION_STATUS_BORDER_COLOR[] = { 1.0f, 0.0f, 0.0f };
47const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f;43const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f;
44static const float ORTHO_NEAR_CLIP = -10000;
45static const float ORTHO_FAR_CLIP = 10000;
48 46
49static const float MOUSE_PITCH_RANGE = 1.0f * PI;47// TODO move somewhere useful
50static const float MOUSE_YAW_RANGE = 0.5f * TWO_PI;48static void fboViewport(QOpenGLFramebufferObject* fbo) {
51static 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
54glm::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 should53ApplicationOverlay::ApplicationOverlay()
61//be multiplied by dir and added to origin to get the location of the collision
62bool 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
117void 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
138ApplicationOverlay::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 different59 // Once we move UI rendering and screen rendering to different
168 // threads, we need to use a sync object to deteremine when60 // 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-use62 // 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 screen78// Renders the overlays either to a texture or to the screen
187void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {79void 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 UI83 // TODO move to Application::idle()?
196 84 Stats::getInstance()->updateStats();
197 // Render 2D overlay85 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 one95 renderOverlays(renderArgs);
243template <typename F>96 renderStatsAndLogs(renderArgs);
244void 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 screen103void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
260void 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.117void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) {
314void 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 magnifiers120 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.
397void 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
491void 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
519glm::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
536QPoint 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
566bool 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
584void 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
643void 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
753void 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 coordinates143void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) {
791void 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
859const int AUDIO_METER_GAP = 5;146void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) {
860const int MUTE_ICON_PADDING = 10;
861
862void 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
880void ApplicationOverlay::renderAudioMeter() {149void 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
996void 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 onscreen150 // Display stats and log text onscreen
1004 glLineWidth(1.0f);
1005 glPointSize(1.0f);
1006 151
1007 // Determine whether to compute timing details152 // 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 timer155 // 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
1042void ApplicationOverlay::renderDomainConnectionStatusBorder() {182void 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
1070ApplicationOverlay::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
1077ApplicationOverlay::TexturedHemisphere::~TexturedHemisphere() {
1078 cleanupVBO();
1079 if (_framebufferObject != NULL) {
1080 delete _framebufferObject;
1081 }
1082}
1083
1084void ApplicationOverlay::TexturedHemisphere::bind() {
1085 _framebufferObject->bind();
1086}
1087
1088void ApplicationOverlay::TexturedHemisphere::release() {
1089 _framebufferObject->release();
1090}
1091 210
1092void 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
1167void ApplicationOverlay::TexturedHemisphere::cleanupVBO() {217GLuint 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
1178void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() {224void 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.
1202void 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
1229GLuint ApplicationOverlay::TexturedHemisphere::getTexture() {
1230 return _framebufferObject->texture();
1231}
1232
1233glm::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
1249glm::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
1254glm::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
1265glm::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
1274glm::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
1284glm::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
1294glm::vec2 ApplicationOverlay::screenToOverlay(const glm::vec2& screenPos) const {
1295 return sphericalToOverlay(screenToSpherical(screenPos));
1296}
1297
1298glm::vec2 ApplicationOverlay::overlayToScreen(const glm::vec2& overlayPos) const {
1299 return sphericalToScreen(overlayToSpherical(overlayPos));
1300}
interface/src/ui/ApplicationOverlay.h+14−118
@@ -13,16 +13,8 @@ · 12 unmodified lines
13#define hifi_ApplicationOverlay_h13#define hifi_ApplicationOverlay_h
14 14
15#include <gpu/Texture.h>15#include <gpu/Texture.h>
16class Camera;
17class Overlays;
18class QOpenGLFramebufferObject;16class QOpenGLFramebufferObject;
19 17
20const float MAGNIFY_WIDTH = 220.0f;
21const float MAGNIFY_HEIGHT = 100.0f;
22const float MAGNIFY_MULT = 2.0f;
23
24const float DEFAULT_HMD_UI_ANGULAR_SIZE = 72.0f;
25
26// Handles the drawing of the overlays to the screen18// Handles the drawing of the overlays to the screen
27// TODO, move divide up the rendering, displaying and input handling19// TODO, move divide up the rendering, displaying and input handling
28// facilities of this class20// 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
68private:30private:
69 // Interleaved vertex data31 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_h50#endif // hifi_ApplicationOverlay_h
interface/src/ui/AvatarInputs.cpp+134−0
@@ -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
18HIFI_QML_DEF(AvatarInputs)
19
20
21static AvatarInputs* INSTANCE{ nullptr };
22static const char SETTINGS_GROUP_NAME[] = "Rear View Tools";
23static const char ZOOM_LEVEL_SETTINGS[] = "ZoomLevel";
24
25static Setting::Handle<int> rearViewZoomLevel(QStringList() << SETTINGS_GROUP_NAME << ZOOM_LEVEL_SETTINGS, 0);
26
27AvatarInputs* AvatarInputs::getInstance() {
28 if (!INSTANCE) {
29 AvatarInputs::registerType();
30 AvatarInputs::show();
31 Q_ASSERT(INSTANCE);
32 }
33 return INSTANCE;
34}
35
36AvatarInputs::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
60void 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
109void AvatarInputs::toggleCameraMute() {
110 FaceTracker* faceTracker = Application::getInstance()->getSelectedFaceTracker();
111 if (faceTracker) {
112 faceTracker->toggleMute();
113 }
114}
115
116void AvatarInputs::toggleAudioMute() {
117 DependencyManager::get<AudioClient>()->toggleMute();
118}
119
120void AvatarInputs::resetSensors() {
121 qApp->resetSensors();
122}
123
124void AvatarInputs::toggleZoom() {
125 _mirrorZoomed = !_mirrorZoomed;
126 rearViewZoomLevel.set(_mirrorZoomed ? 0 : 1);
127 emit mirrorZoomedChanged();
128}
129
130void AvatarInputs::closeMirror() {
131 if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
132 Menu::getInstance()->triggerOption(MenuOption::Mirror);
133 }
134}
interface/src/ui/AvatarInputs.h+59−0
@@ -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) \
17public: \
18 type name() { return _##name; }; \
19private: \
20 type _##name{ initialValue };
21
22class 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
34public:
35 static AvatarInputs* getInstance();
36 AvatarInputs(QQuickItem* parent = nullptr);
37 void update();
38
39signals:
40 void cameraEnabledChanged();
41 void cameraMutedChanged();
42 void audioMutedChanged();
43 void audioClippingChanged();
44 void audioLevelChanged();
45 void mirrorVisibleChanged();
46 void mirrorZoomedChanged();
47
48protected:
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
55private:
56 float _trailingAudioLoudness{ 0 };
57};
58
59#endif // hifi_AvatarInputs_h
interface/src/ui/FlowLayout.cpp+9−0
@@ -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]
45FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)54FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
interface/src/ui/HMDToolsDialog.cpp+4−59
@@ -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 details61 // 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 mode138 // 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
178void HMDToolsDialog::activateWindowAfterEnterMode() {
179 Application::getInstance()->getWindow()->activateWindow();
180
181 // center the cursor on the main application window
182 centerCursorOnWidget(Application::getInstance()->getWindow());
183}
184
185void HMDToolsDialog::leaveHDMMode() {149void 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
207void 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
216void HMDToolsDialog::reject() {159void HMDToolsDialog::reject() {
217 // Just regularly close upon ESC160 // Just regularly close upon ESC
218 close();161 close();
@@ -244,13 +187,15 @@ void HMDToolsDialog::hideEvent(QHideEvent* event) { · 25 unmodified lines
244 187
245void HMDToolsDialog::aboutToQuit() {188void 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
251void HMDToolsDialog::screenCountChanged(int newCount) {196void 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
interface/src/ui/HMDToolsDialog.h+0−4
@@ -35,8 +35,6 @@ signals: · 34 unmodified lines
35public slots:35public 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;
interface/src/ui/LogDialog.cpp+9−0
@@ -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
interface/src/ui/PreferencesDialog.cpp+2−2
@@ -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());
interface/src/ui/RearMirrorTools.cpp+0−134
@@ -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
24const int ICON_SIZE = 24;
25const int ICON_PADDING = 5;
26
27const char SETTINGS_GROUP_NAME[] = "Rear View Tools";
28const char ZOOM_LEVEL_SETTINGS[] = "ZoomLevel";
29Setting::Handle<int> RearMirrorTools::rearViewZoomLevel(QStringList() << SETTINGS_GROUP_NAME << ZOOM_LEVEL_SETTINGS,
30 ZoomLevel::HEAD);
31
32RearMirrorTools::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
49void 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
66bool 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
101void 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}
interface/src/ui/RearMirrorTools.h+0−56
@@ -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
18enum ZoomLevel {
19 HEAD = 0,
20 BODY = 1
21};
22
23class RearMirrorTools : public QObject {
24 Q_OBJECT
25public:
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
32signals:
33 void closeView();
34 void shrinkView();
35 void resetView();
36 void restoreView();
37
38private:
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
interface/src/ui/Stats.cpp+221−466
@@ -1,17 +1,17 @@
1//1//
2// Stats.cpp2// 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.html6// 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
35using namespace std;34HIFI_QML_DEF(Stats)
36 35
37const int STATS_PELS_PER_LINE = 16;36using namespace std;
38const int STATS_PELS_INITIALOFFSET = 12;
39 37
40const int STATS_GENERAL_MIN_WIDTH = 165; 38static Stats* INSTANCE{ nullptr };
41const int STATS_PING_MIN_WIDTH = 190;
42const int STATS_GEO_MIN_WIDTH = 240;
43const int STATS_OCTREE_MIN_WIDTH = 410;
44 39
45Stats* Stats::getInstance() {40Stats* Stats::getInstance() {
46 static Stats stats;41 if (!INSTANCE) {
47 return &stats;42 Stats::registerType();
48}43 Stats::show();
49 44 Q_ASSERT(INSTANCE);
50Stats::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
64void 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
70void 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
125void 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 49Stats::Stats(QQuickItem* parent) : QQuickItem(parent) {
157// translucent background box that makes stats more readable50 INSTANCE = this;
158void 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
171bool Stats::includeTimingRecord(const QString& name) {55bool 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 stats77#define STAT_UPDATE(name, src) \
194void 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 ourselves88 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()) {96void 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 entities141 // 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 background157 // 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 details210 } // expanded avatar column
474 octreeStats.str("");211
475 octreeStats << "Triangles: " << _renderDetails._trianglesRendered212 // 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 packets258 // 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 Elements282 // 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 Voxels308 // 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
349void 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
368void 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*/
interface/src/ui/Stats.h+121−32
@@ -1,8 +1,5 @@
1//1//
2// Stats.h2// 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_h10#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
19class Stats: public QObject {19#define STATS_PROPERTY(type, name, initialValue) \
20 Q_PROPERTY(type name READ name NOTIFY name##Changed) \
21public: \
22 type name() { return _##name; }; \
23private: \
24 type _##name{ initialValue };
25
26
27class Stats : public QQuickItem {
20 Q_OBJECT28 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
22public:75public:
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;
40private: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 process89 void setExpanded(bool expanded) {
46 bool _resetRecentMaxPacketsSoon;90 if (_expanded != expanded) {
91 _expanded = expanded;
92 emit expandedChanged();
93 }
94 }
47 95
48 int _generalStatsWidth;96signals:
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;140private:
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_h148#endif // hifi_Stats_h
interface/src/ui/UserInputMapper.cpp+1−1
@@ -53,7 +53,7 @@ void UserInputMapper::resetDevice(uint16 deviceID) { · 52 unmodified lines
53 53
54int UserInputMapper::findDevice(QString name) {54int 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 }
interface/src/ui/overlays/Circle3DOverlay.cpp+161−181
@@ -11,6 +11,7 @@ · 10 unmodified lines
11// include this before QGLWidget, which includes an earlier version of OpenGL11// 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 visible80 return; // do nothing if our alpha is 0, we're not visible
80 }81 }
81 82
82 // Create the circle in the coordinates origin83 // Create the circle in the coordinates origin
83 float outerRadius = getOuterRadius();84 float outerRadius = getOuterRadius();
84 float innerRadius = getInnerRadius(); // only used in solid case85 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 circle94 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, otherwise127 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 marks186 // 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, otherwise187 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 marks202 // 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 marks232 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
306void Circle3DOverlay::setProperties(const QScriptValue &properties) {286void 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
418bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin, 398bool 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);
interface/src/ui/overlays/Line3DOverlay.cpp+10−0
@@ -32,6 +32,16 @@ Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) : · 31 unmodified lines
32Line3DOverlay::~Line3DOverlay() {32Line3DOverlay::~Line3DOverlay() {
33}33}
34 34
35AABox 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
35void Line3DOverlay::render(RenderArgs* args) {45void Line3DOverlay::render(RenderArgs* args) {
36 if (!_visible) {46 if (!_visible) {
37 return; // do nothing if we're not visible47 return; // do nothing if we're not visible
interface/src/ui/overlays/Line3DOverlay.h+1−0
@@ -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 // getters26 // getters
26 const glm::vec3& getStart() const { return _start; }27 const glm::vec3& getStart() const { return _start; }
interface/src/ui/overlays/Overlays.cpp+1−1
@@ -258,7 +258,7 @@ void Overlays::deleteOverlay(unsigned int id) { · 257 unmodified lines
258unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) {258unsigned 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);
interface/src/ui/overlays/TextOverlay.cpp+1−0
@@ -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 bounds86 const int leftAdjust = -1; // required to make text render relative to left edge of bounds