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
6666 #include <EntityScriptingInterface.h>
6767 #include <ErrorDialog.h>
6868 #include <GlowEffect.h>
69+#include <gpu/Batch.h>
70+#include <gpu/Context.h>
71+#include <gpu/GLBackend.h>
6972 #include <HFActionEvent.h>
7073 #include <HFBackEvent.h>
7174 #include <InfoView.h>
@@ -83,12 +86,14 @@ · 11 unmodified lines
8386 #include <PerfStat.h>
8487 #include <PhysicsEngine.h>
8588 #include <ProgramObject.h>
89+#include <RenderDeferredTask.h>
8690 #include <ResourceCache.h>
8791 #include <SceneScriptingInterface.h>
8892 #include <ScriptCache.h>
8993 #include <SettingHandle.h>
9094 #include <SoundCache.h>
9195 #include <TextRenderer.h>
96+#include <Tooltip.h>
9297 #include <UserActivityLogger.h>
9398 #include <UUID.h>
9499 #include <VrMenu.h>
@@ -106,11 +111,9 @@ · 11 unmodified lines
106111
107112 #include "avatar/AvatarManager.h"
108113
109−#include "audio/AudioToolBox.h"
110114 #include "audio/AudioIOStatsRenderer.h"
111115 #include "audio/AudioScope.h"
112116
113−#include "devices/CameraToolBox.h"
114117 #include "devices/DdeFaceTracker.h"
115118 #include "devices/Faceshift.h"
116119 #include "devices/Leapmotion.h"
@@ -120,12 +123,6 @@ · 3 unmodified lines
120123 #include "devices/OculusManager.h"
121124 #include "devices/TV3DManager.h"
122125
123−#include "gpu/Batch.h"
124−#include "gpu/Context.h"
125−#include "gpu/GLBackend.h"
126−
127−#include "RenderDeferredTask.h"
128−
129126 #include "scripting/AccountScriptingInterface.h"
130127 #include "scripting/AudioDeviceScriptingInterface.h"
131128 #include "scripting/ClipboardScriptingInterface.h"
@@ -141,6 +138,7 @@ · 9 unmodified lines
141138 #include "SpeechRecognizer.h"
142139 #endif
143140
141+#include "ui/AvatarInputs.h"
144142 #include "ui/DataWebDialog.h"
145143 #include "ui/DialogsManager.h"
146144 #include "ui/LoginDialog.h"
@@ -281,8 +279,6 @@ bool setupEssentials(int& argc, char** argv) { · 134 unmodified lines
281279 auto animationCache = DependencyManager::set<AnimationCache>();
282280 auto ddeFaceTracker = DependencyManager::set<DdeFaceTracker>();
283281 auto modelBlender = DependencyManager::set<ModelBlender>();
284− auto audioToolBox = DependencyManager::set<AudioToolBox>();
285− auto cameraToolBox = DependencyManager::set<CameraToolBox>();
286282 auto avatarManager = DependencyManager::set<AvatarManager>();
287283 auto lodManager = DependencyManager::set<LODManager>();
288284 auto jsConsole = DependencyManager::set<StandAloneJSConsole>();
@@ -335,8 +331,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : · 46 unmodified lines
335331 _cursorVisible(true),
336332 _lastMouseMove(usecTimestampNow()),
337333 _lastMouseMoveWasSimulated(false),
338− _touchAvgX(0.0f),
339− _touchAvgY(0.0f),
340334 _isTouchPressed(false),
341335 _mousePressed(false),
342336 _enableProcessOctreeThread(true),
@@ -814,9 +808,9 @@ void Application::initializeGL() { · 471 unmodified lines
814808 _idleLoopStdev.reset();
815809
816810 if (_justStarted) {
817− float startupTime = (float)_applicationStartupTime.elapsed() / 1000.0;
811+ float startupTime = (float)_applicationStartupTime.elapsed() / 1000.0f;
818812 _justStarted = false;
819− qCDebug(interfaceapp, "Startup time: %4.2f seconds.", startupTime);
813+ qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTime);
820814 }
821815
822816 // update before the first render
@@ -831,6 +825,7 @@ void Application::initializeUi() { · 8 unmodified lines
831825 LoginDialog::registerType();
832826 MessageDialog::registerType();
833827 VrMenu::registerType();
828+ Tooltip::registerType();
834829 UpdateDialog::registerType();
835830
836831 auto offscreenUi = DependencyManager::get<OffscreenUi>();
@@ -844,7 +839,7 @@ void Application::initializeUi() { · 7 unmodified lines
844839 VrMenu::executeQueuedLambdas();
845840 offscreenUi->setMouseTranslator([this](const QPointF& p){
846841 if (OculusManager::isConnected()) {
847− glm::vec2 pos = _applicationOverlay.screenToOverlay(toGlm(p));
842+ glm::vec2 pos = _compositor.screenToOverlay(toGlm(p));
848843 return QPointF(pos.x, pos.y);
849844 }
850845 return QPointF(p);
@@ -877,6 +872,7 @@ void Application::paintGL() { · 26 unmodified lines
877872 OculusManager::beginFrameTiming();
878873 }
879874
875+
880876 PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings));
881877 bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
882878 PerformanceWarning warn(showWarnings, "Application::paintGL()");
@@ -884,6 +880,12 @@ void Application::paintGL() { · 1 unmodified line
884880
885881 {
886882 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+ */
887889 _applicationOverlay.renderOverlay(&renderArgs);
888890 }
889891
@@ -912,13 +914,18 @@ void Application::paintGL() { · 22 unmodified lines
912914 }
913915
914916 } 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());
917917 if (OculusManager::isConnected()) {
918918 _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation());
919919 } else {
920920 _myCamera.setRotation(_myAvatar->getHead()->getOrientation());
921921 }
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+ }
922929
923930 } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
924931 _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)));
@@ -967,9 +974,7 @@ void Application::paintGL() { · 42 unmodified lines
967974 glPopMatrix();
968975
969976 renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
970− if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
971− _rearMirrorTools->render(&renderArgs, true, _glWidget->mapFromGlobal(QCursor::pos()));
972− } else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
977+ if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
973978 renderRearViewMirror(&renderArgs, _mirrorViewRect);
974979 }
975980
@@ -977,6 +982,7 @@ void Application::paintGL() { · 1 unmodified line
977982
978983 auto finalFbo = DependencyManager::get<GlowEffect>()->render(&renderArgs);
979984
985+
980986 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
981987 glBindFramebuffer(GL_READ_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo));
982988 glBlitFramebuffer(0, 0, _renderResolution.x, _renderResolution.y,
@@ -984,9 +990,10 @@ void Application::paintGL() { · 1 unmodified line
984990 GL_COLOR_BUFFER_BIT, GL_NEAREST);
985991 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
986992
987− _applicationOverlay.displayOverlayTexture();
993+ _compositor.displayOverlayTexture(&renderArgs);
988994 }
989995
996+
990997 if (!OculusManager::isConnected() || OculusManager::allowSwap()) {
991998 _glWidget->swapBuffers();
992999 }
@@ -1044,27 +1051,24 @@ void Application::resizeGL() { · 51 unmodified lines
10441051 } else {
10451052 renderSize = _glWidget->getDeviceSize() * getRenderResolutionScale();
10461053 }
1047− if (_renderResolution == toGlm(renderSize)) {
1048− return;
1049− }
10501054
1051− _renderResolution = toGlm(renderSize);
1052− DependencyManager::get<TextureCache>()->setFrameBufferSize(renderSize);
1053− resetCamerasOnResizeGL(_myCamera, _renderResolution);
1055+ if (_renderResolution != toGlm(renderSize)) {
1056+ _renderResolution = toGlm(renderSize);
1057+ DependencyManager::get<TextureCache>()->setFrameBufferSize(renderSize);
1058+ resetCamerasOnResizeGL(_myCamera, _renderResolution);
10541059
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???
10561061
1057− updateProjectionMatrix();
1058− glLoadIdentity();
1062+ updateProjectionMatrix();
1063+ glLoadIdentity();
1064+ }
10591065
10601066 auto offscreenUi = DependencyManager::get<OffscreenUi>();
1061− offscreenUi->resize(_glWidget->size());
1067+
1068+ auto canvasSize = _glWidget->size();
1069+ offscreenUi->resize(canvasSize);
10621070 _glWidget->makeCurrent();
10631071
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);
10681072 }
10691073
10701074 void Application::updateProjectionMatrix() {
@@ -1568,27 +1572,9 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { · 497 unmodified lines
15681572 _keyboardMouseDevice.mousePressEvent(event);
15691573
15701574 if (event->button() == Qt::LeftButton) {
1571− _mouseDragStartedX = getTrueMouseX();
1572− _mouseDragStartedY = getTrueMouseY();
1575+ _mouseDragStarted = getTrueMouse();
15731576 _mousePressed = true;
15741577
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−
15921578 // nobody handled this - make it an action event on the _window object
15931579 HFActionEvent actionEvent(HFActionEvent::startType(),
15941580 computePickRay(event->x(), event->y()));
@@ -1606,16 +1592,7 @@ void Application::mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceI · 11 unmodified lines
16061592 return;
16071593 }
16081594
1609− if (activeWindow() == _window) {
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− }
1595+ _controllerScriptingInterface.emitMouseDoublePressEvent(event);
16191596 }
16201597
16211598 void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
@@ -1637,13 +1614,6 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { · 15 unmodified lines
16371614 if (event->button() == Qt::LeftButton) {
16381615 _mousePressed = false;
16391616
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−
16471617 // fire an action end event
16481618 HFActionEvent actionEvent(HFActionEvent::endType(),
16491619 computePickRay(event->x(), event->y()));
@@ -1671,22 +1641,18 @@ void Application::touchUpdateEvent(QTouchEvent* event) { · 21 unmodified lines
16711641 bool validTouch = false;
16721642 if (activeWindow() == _window) {
16731643 const QList<QTouchEvent::TouchPoint>& tPoints = event->touchPoints();
1674− _touchAvgX = 0.0f;
1675− _touchAvgY = 0.0f;
1644+ _touchAvg = vec2();
16761645 int numTouches = tPoints.count();
16771646 if (numTouches > 1) {
16781647 for (int i = 0; i < numTouches; ++i) {
1679− _touchAvgX += tPoints[i].pos().x();
1680− _touchAvgY += tPoints[i].pos().y();
1648+ _touchAvg += toGlm(tPoints[i].pos());
16811649 }
1682− _touchAvgX /= (float)(numTouches);
1683− _touchAvgY /= (float)(numTouches);
1650+ _touchAvg /= (float)(numTouches);
16841651 validTouch = true;
16851652 }
16861653 }
16871654 if (!_isTouchPressed) {
1688− _touchDragStartedAvgX = _touchAvgX;
1689− _touchDragStartedAvgY = _touchAvgY;
1655+ _touchDragStartedAvg = _touchAvg;
16901656 }
16911657 _isTouchPressed = validTouch;
16921658 }
@@ -1722,8 +1688,7 @@ void Application::touchEndEvent(QTouchEvent* event) { · 29 unmodified lines
17221688 _keyboardMouseDevice.touchEndEvent(event);
17231689
17241690 // put any application specific touch behavior below here..
1725− _touchDragStartedAvgX = _touchAvgX;
1726− _touchDragStartedAvgY = _touchAvgY;
1691+ _touchDragStartedAvg = _touchAvg;
17271692 _isTouchPressed = false;
17281693
17291694 }
@@ -1872,9 +1837,9 @@ void Application::setFullscreen(bool fullscreen) { · 142 unmodified lines
18721837 Menu::getInstance()->getActionForOption(MenuOption::Fullscreen)->setChecked(fullscreen);
18731838 }
18741839
1875−// The following code block is useful on platforms that can have a visible
1876−// app menu in a fullscreen window. However the OSX mechanism hides the
1877−// application menu for fullscreen apps, so the check is not required.
1840+ // The following code block is useful on platforms that can have a visible
1841+ // app menu in a fullscreen window. However the OSX mechanism hides the
1842+ // application menu for fullscreen apps, so the check is not required.
18781843 #ifndef Q_OS_MAC
18791844 if (Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) {
18801845 if (fullscreen) {
@@ -1939,16 +1904,15 @@ void Application::setEnableVRMode(bool enableVRMode) { · 58 unmodified lines
19391904 // attempt to reconnect the Oculus manager - it's possible this was a workaround
19401905 // for the sixense crash
19411906 OculusManager::disconnect();
1942− OculusManager::connect();
1907+ OculusManager::connect(_glWidget->context()->contextHandle());
1908+ _glWidget->setFocus();
1909+ _glWidget->makeCurrent();
1910+ glClear(GL_COLOR_BUFFER_BIT);
19431911 }
19441912 OculusManager::recalibrate();
19451913 } else {
19461914 OculusManager::abandonCalibration();
1947−
1948− _mirrorCamera.setHmdPosition(glm::vec3());
1949− _mirrorCamera.setHmdRotation(glm::quat());
1950− _myCamera.setHmdPosition(glm::vec3());
1951− _myCamera.setHmdRotation(glm::quat());
1915+ OculusManager::disconnect();
19521916 }
19531917
19541918 resizeGL();
@@ -1960,44 +1924,34 @@ void Application::setLowVelocityFilter(bool lowVelocityFilter) { · 5 unmodified lines
19601924
19611925 bool Application::mouseOnScreen() const {
19621926 if (OculusManager::isConnected()) {
1963− return getMouseX() >= 0 && getMouseX() <= _glWidget->getDeviceWidth() &&
1964− getMouseY() >= 0 && getMouseY() <= _glWidget->getDeviceHeight();
1927+ ivec2 mouse = getMouse();
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+ }
19651935 }
19661936 return true;
19671937 }
19681938
1969−int Application::getMouseX() const {
1970− if (OculusManager::isConnected()) {
1971− glm::vec2 pos = _applicationOverlay.screenToOverlay(glm::vec2(getTrueMouseX(), getTrueMouseY()));
1972− return pos.x;
1973− }
1974− return getTrueMouseX();
1975−}
1976−
1977−int Application::getMouseY() const {
1939+ivec2 Application::getMouse() const {
19781940 if (OculusManager::isConnected()) {
1979− glm::vec2 pos = _applicationOverlay.screenToOverlay(glm::vec2(getTrueMouseX(), getTrueMouseY()));
1980− return pos.y;
1941+ return _compositor.screenToOverlay(getTrueMouse());
19811942 }
1982− return getTrueMouseY();
1943+ return getTrueMouse();
19831944 }
19841945
1985−int Application::getMouseDragStartedX() const {
1946+ivec2 Application::getMouseDragStarted() const {
19861947 if (OculusManager::isConnected()) {
1987− glm::vec2 pos = _applicationOverlay.screenToOverlay(glm::vec2(getTrueMouseDragStartedX(),
1988− getTrueMouseDragStartedY()));
1989− return pos.x;
1948+ return _compositor.screenToOverlay(getTrueMouseDragStarted());
19901949 }
1991− return getTrueMouseDragStartedX();
1950+ return getTrueMouseDragStarted();
19921951 }
19931952
1994−int Application::getMouseDragStartedY() const {
1995− if (OculusManager::isConnected()) {
1996− glm::vec2 pos = _applicationOverlay.screenToOverlay(glm::vec2(getTrueMouseDragStartedX(),
1997− getTrueMouseDragStartedY()));
1998− return pos.y;
1999− }
2000− return getTrueMouseDragStartedY();
1953+ivec2 Application::getTrueMouseDragStarted() const {
1954+ return _mouseDragStarted;
20011955 }
20021956
20031957 FaceTracker* Application::getActiveFaceTracker() {
@@ -2179,13 +2133,6 @@ void Application::init() { · 175 unmodified lines
21792133
21802134 _mirrorCamera.setMode(CAMERA_MODE_MIRROR);
21812135
2182− OculusManager::connect();
2183− if (OculusManager::isConnected()) {
2184− QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen),
2185− "trigger",
2186− Qt::QueuedConnection);
2187− }
2188−
21892136 TV3DManager::connect();
21902137 if (TV3DManager::isConnected()) {
21912138 QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen),
@@ -2249,13 +2196,6 @@ void Application::init() { · 57 unmodified lines
22492196 _entityClipboardRenderer.setViewFrustum(getViewFrustum());
22502197 _entityClipboardRenderer.setTree(&_entityClipboard);
22512198
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−
22592199 // initialize the GlowEffect with our widget
22602200 bool glow = Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect);
22612201 DependencyManager::get<GlowEffect>()->init(glow);
@@ -2434,6 +2374,12 @@ void Application::cameraMenuChanged() { · 172 unmodified lines
24342374 }
24352375 }
24362376
2377+void Application::rotationModeChanged() {
2378+ if (!Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) {
2379+ _myAvatar->setHeadPitch(0);
2380+ }
2381+}
2382+
24372383 void Application::updateCamera(float deltaTime) {
24382384 PerformanceTimer perfTimer("updateCamera");
24392385 bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
@@ -3155,7 +3101,7 @@ PickRay Application::computePickRay(float x, float y) const { · 715 unmodified lines
31553101 y /= size.y;
31563102 PickRay result;
31573103 if (isHMDMode()) {
3158− getApplicationOverlay().computeHmdPickRay(glm::vec2(x, y), result.origin, result.direction);
3104+ getApplicationCompositor().computeHmdPickRay(glm::vec2(x, y), result.origin, result.direction);
31593105 } else {
31603106 if (QThread::currentThread() == activeRenderingThread) {
31613107 getDisplayViewFrustum()->computePickRay(x, y, result.origin, result.direction);
@@ -3184,13 +3130,11 @@ QImage Application::renderAvatarBillboard(RenderArgs* renderArgs) { · 22 unmodified lines
31843130 renderRearViewMirror(renderArgs, QRect(0, _glWidget->getDeviceHeight() - BILLBOARD_SIZE,
31853131 BILLBOARD_SIZE, BILLBOARD_SIZE),
31863132 true);
3187−
31883133 QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32);
31893134 glReadPixels(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE, GL_BGRA, GL_UNSIGNED_BYTE, image.bits());
31903135 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
31913136
31923137 glBindFramebuffer(GL_FRAMEBUFFER, 0);
3193−
31943138 return image;
31953139 }
31963140
@@ -3386,9 +3330,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 189 unmodified lines
33863330
33873331 // flip x if in mirror mode (also requires reversing winding order for backface culling)
33883332 if (theCamera.getMode() == CAMERA_MODE_MIRROR) {
3389− glScalef(-1.0f, 1.0f, 1.0f);
3390− glFrontFace(GL_CW);
3391−
3333+ //glScalef(-1.0f, 1.0f, 1.0f);
3334+ //glFrontFace(GL_CW);
33923335 } else {
33933336 glFrontFace(GL_CCW);
33943337 }
@@ -3411,7 +3354,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 16 unmodified lines
34113354 viewTransform.setTranslation(theCamera.getPosition());
34123355 viewTransform.setRotation(rotation);
34133356 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));
34153358 }
34163359 if (renderArgs->_renderSide != RenderArgs::MONO) {
34173360 glm::mat4 invView = glm::inverse(_untranslatedViewMatrix);
@@ -3548,6 +3491,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 130 unmodified lines
35483491
35493492 renderContext._maxDrawnOpaqueItems = sceneInterface->getEngineMaxDrawnOpaqueItems();
35503493 renderContext._maxDrawnTransparentItems = sceneInterface->getEngineMaxDrawnTransparentItems();
3494+ renderContext._maxDrawnOverlay3DItems = sceneInterface->getEngineMaxDrawnOverlay3DItems();
35513495
35523496 renderArgs->_shouldRender = LODManager::shouldRender;
35533497
@@ -3564,7 +3508,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 10 unmodified lines
35643508
35653509 sceneInterface->setEngineFeedTransparentItems(engineRC->_numFeedTransparentItems);
35663510 sceneInterface->setEngineDrawnTransparentItems(engineRC->_numDrawnTransparentItems);
3567−
3511+
3512+ sceneInterface->setEngineFeedOverlay3DItems(engineRC->_numFeedOverlay3DItems);
3513+ sceneInterface->setEngineDrawnOverlay3DItems(engineRC->_numDrawnOverlay3DItems);
35683514 }
35693515 //Render the sixense lasers
35703516 if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) {
@@ -3672,8 +3618,8 @@ glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) { · 101 unmodified lines
36723618 // +-----------------------+
36733619 // -1,-1 1,-1
36743620
3675− glm::vec2 screenPoint((projectedPoint.x + 1.0) * horizontalScale,
3676− ((projectedPoint.y + 1.0) * -verticalScale) + _glWidget->getDeviceHeight());
3621+ glm::vec2 screenPoint((projectedPoint.x + 1.0f) * horizontalScale,
3622+ ((projectedPoint.y + 1.0f) * -verticalScale) + _glWidget->getDeviceHeight());
36773623
36783624 return screenPoint;
36793625 }
@@ -3691,7 +3637,7 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi · 11 unmodified lines
36913637 _mirrorCamera.setPosition(_myAvatar->getPosition() +
36923638 _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * BILLBOARD_DISTANCE * _myAvatar->getScale());
36933639
3694− } else if (RearMirrorTools::rearViewZoomLevel.get() == BODY) {
3640+ } else if (!AvatarInputs::getInstance()->mirrorZoomed()) {
36953641 _mirrorCamera.setPosition(_myAvatar->getChestPosition() +
36963642 _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale());
36973643
@@ -3726,7 +3672,7 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi · 28 unmodified lines
37263672 } else {
37273673 // if not rendering the billboard, the region is in device independent coordinates; must convert to device
37283674 QSize size = DependencyManager::get<TextureCache>()->getFrameBufferSize();
3729− float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale();
3675+ float ratio = (float)QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale();
37303676 int x = region.x() * ratio, y = region.y() * ratio, width = region.width() * ratio, height = region.height() * ratio;
37313677 glViewport(x, size.height() - y - height, width, height);
37323678 glScissor(x, size.height() - y - height, width, height);
@@ -3741,10 +3687,6 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi · 8 unmodified lines
37413687 displaySide(renderArgs, _mirrorCamera, true, billboard);
37423688 glPopMatrix();
37433689
3744− if (!billboard) {
3745− _rearMirrorTools->render(renderArgs, false, _glWidget->mapFromGlobal(QCursor::pos()));
3746− }
3747−
37483690 // reset Viewport and projection matrix
37493691 glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
37503692 glDisable(GL_SCISSOR_TEST);
@@ -3884,7 +3826,7 @@ void Application::nodeKilled(SharedNodePointer node) { · 133 unmodified lines
38843826 _entityServerJurisdictions.unlock();
38853827
38863828 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);
38883830
38893831 // Add the jurisditionDetails object to the list of "fade outs"
38903832 if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnOctreeServerChanges)) {
@@ -3970,7 +3912,8 @@ int Application::parseOctreeStats(const QByteArray& packet, const SharedNodePoin · 79 unmodified lines
39703912 jurisdiction->unlock();
39713913
39723914 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);
39743917
39753918 // Add the jurisditionDetails object to the list of "fade outs"
39763919 if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnOctreeServerChanges)) {
@@ -4750,11 +4693,9 @@ void Application::postLambdaEvent(std::function<void()> f) { · 773 unmodified lines
47504693 }
47514694
47524695 void Application::initPlugins() {
4753− OculusManager::init();
47544696 }
47554697
47564698 void Application::shutdownPlugins() {
4757− OculusManager::deinit();
47584699 }
47594700
47604701 glm::vec3 Application::getHeadPosition() const {
@@ -4774,12 +4715,8 @@ QSize Application::getDeviceSize() const { · 13 unmodified lines
47744715 return _glWidget->getDeviceSize();
47754716 }
47764717
4777−int Application::getTrueMouseX() const {
4778− return _glWidget->mapFromGlobal(QCursor::pos()).x();
4779−}
4780−
4781−int Application::getTrueMouseY() const {
4782− return _glWidget->mapFromGlobal(QCursor::pos()).y();
4718+ivec2 Application::getTrueMouse() const {
4719+ return toGlm(_glWidget->mapFromGlobal(QCursor::pos()));
47834720 }
47844721
47854722 bool Application::isThrottleRendering() const {
@@ -4809,3 +4746,25 @@ qreal Application::getDevicePixelRatio() { · 23 unmodified lines
48094746 return _window ? _window->windowHandle()->devicePixelRatio() : 1.0;
48104747 }
48114748
4749+mat4 Application::getEyeProjection(int eye) const {
4750+ if (isHMDMode()) {
4751+ return OculusManager::getEyeProjection(eye);
4752+ }
4753+
4754+ return _viewFrustum.getProjection();
4755+}
4756+
4757+mat4 Application::getEyePose(int eye) const {
4758+ if (isHMDMode()) {
4759+ return OculusManager::getEyePose(eye);
4760+ }
4761+
4762+ return mat4();
4763+}
4764+
4765+mat4 Application::getHeadPose() const {
4766+ if (isHMDMode()) {
4767+ return OculusManager::getHeadPose();
4768+ }
4769+ return mat4();
4770+}
interface/src/Application.h+37−28
@@ -62,12 +62,12 @@ · 61 unmodified lines
6262 #include "ui/ModelsBrowser.h"
6363 #include "ui/NodeBounds.h"
6464 #include "ui/OctreeStatsDialog.h"
65−#include "ui/RearMirrorTools.h"
6665 #include "ui/SnapshotShareDialog.h"
6766 #include "ui/LodToolsDialog.h"
6867 #include "ui/LogDialog.h"
6968 #include "ui/overlays/Overlays.h"
7069 #include "ui/ApplicationOverlay.h"
70+#include "ui/ApplicationCompositor.h"
7171 #include "ui/RunningScriptsWidget.h"
7272 #include "ui/ToolWindow.h"
7373 #include "ui/UserInputMapper.h"
@@ -221,15 +221,21 @@ public: · 147 unmodified lines
221221 const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; }
222222 const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
223223 bool mouseOnScreen() const;
224− int getMouseX() const;
225− int getMouseY() const;
226− glm::ivec2 getTrueMousePosition() const;
227− int getTrueMouseX() const;
228− int getTrueMouseY() const;
229− int getMouseDragStartedX() const;
230− int getMouseDragStartedY() const;
231− int getTrueMouseDragStartedX() const { return _mouseDragStartedX; }
232− int getTrueMouseDragStartedY() const { return _mouseDragStartedY; }
224+
225+ ivec2 getMouse() const;
226+ ivec2 getTrueMouse() const;
227+ ivec2 getMouseDragStarted() const;
228+ ivec2 getTrueMouseDragStarted() const;
229+
230+ // TODO get rid of these and use glm types directly
231+ int getMouseX() const { return getMouse().x; }
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; }
233239 bool getLastMouseMoveWasSimulated() const { return _lastMouseMoveWasSimulated; }
234240
235241 FaceTracker* getActiveFaceTracker();
@@ -238,6 +244,8 @@ public: · 2 unmodified lines
238244 QSystemTrayIcon* getTrayIcon() { return _trayIcon; }
239245 ApplicationOverlay& getApplicationOverlay() { return _applicationOverlay; }
240246 const ApplicationOverlay& getApplicationOverlay() const { return _applicationOverlay; }
247+ ApplicationCompositor& getApplicationCompositor() { return _compositor; }
248+ const ApplicationCompositor& getApplicationCompositor() const { return _compositor; }
241249 Overlays& getOverlays() { return _overlays; }
242250
243251 float getFps() const { return _fps; }
@@ -330,6 +338,9 @@ public: · 86 unmodified lines
330338 bool isHMDMode() const;
331339 glm::quat getHeadOrientation() const;
332340 glm::vec3 getHeadPosition() const;
341+ glm::mat4 getHeadPose() const;
342+ glm::mat4 getEyePose(int eye) const;
343+ glm::mat4 getEyeProjection(int eye) const;
333344
334345 QRect getDesirableApplicationGeometry();
335346 RunningScriptsWidget* getRunningScriptsWidget() { return _runningScriptsWidget; }
@@ -451,6 +462,8 @@ private slots: · 115 unmodified lines
451462 void setFullscreen(bool fullscreen);
452463 void setEnable3DTVMode(bool enable3DTVMode);
453464 void setEnableVRMode(bool enableVRMode);
465+
466+ void rotationModeChanged();
454467
455468 glm::vec2 getScaledScreenPoint(glm::vec2 projectedPoint);
456469
@@ -548,21 +561,17 @@ private: · 91 unmodified lines
548561
549562 OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers
550563
551− KeyboardMouseDevice _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad
552−
553− UserInputMapper _userInputMapper; // User input mapper allowing to mapp different real devices to the action channels that the application has to offer
554−
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
564+ KeyboardMouseDevice _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad
565+ UserInputMapper _userInputMapper; // User input mapper allowing to mapp different real devices to the action channels that the application has to offer
566+ MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be)
567+ Camera _myCamera; // My view onto the world
558568 Camera _mirrorCamera; // Cammera for mirror view
559569 QRect _mirrorViewRect;
560− RearMirrorTools* _rearMirrorTools;
561570
562− Setting::Handle<bool> _firstRun;
563− Setting::Handle<QString> _previousScriptLocation;
564− Setting::Handle<QString> _scriptsLocationHandle;
565− Setting::Handle<float> _fieldOfView;
571+ Setting::Handle<bool> _firstRun;
572+ Setting::Handle<QString> _previousScriptLocation;
573+ Setting::Handle<QString> _scriptsLocationHandle;
574+ Setting::Handle<float> _fieldOfView;
566575
567576 Transform _viewTransform;
568577 glm::mat4 _untranslatedViewMatrix;
@@ -580,18 +589,17 @@ private: · 11 unmodified lines
580589 Environment _environment;
581590
582591 bool _cursorVisible;
583− int _mouseDragStartedX;
584− int _mouseDragStartedY;
592+ ivec2 _mouseDragStarted;
593+
585594 quint64 _lastMouseMove;
586595 bool _lastMouseMoveWasSimulated;
587596
588597 glm::vec3 _mouseRayOrigin;
589598 glm::vec3 _mouseRayDirection;
590599
591− float _touchAvgX;
592− float _touchAvgY;
593− float _touchDragStartedAvgX;
594− float _touchDragStartedAvgY;
600+ vec2 _touchAvg;
601+ vec2 _touchDragStartedAvg;
602+
595603 bool _isTouchPressed; // true if multitouch has been pressed (clear when finished)
596604
597605 bool _mousePressed; // true if mouse has been pressed (clear when finished)
@@ -669,6 +677,7 @@ private: · 71 unmodified lines
669677
670678 Overlays _overlays;
671679 ApplicationOverlay _applicationOverlay;
680+ ApplicationCompositor _compositor;
672681 };
673682
674683 #endif // hifi_Application_h
interface/src/Camera.cpp+0−16
@@ -49,8 +49,6 @@ Camera::Camera() : · 48 unmodified lines
4949 _mode(CAMERA_MODE_THIRD_PERSON),
5050 _position(0.0f, 0.0f, 0.0f),
5151 _projection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), 16.0f/9.0f, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)),
52− _hmdPosition(),
53− _hmdRotation(),
5452 _isKeepLookingAt(false),
5553 _lookingAt(0.0f, 0.0f, 0.0f)
5654 {
@@ -74,20 +72,6 @@ void Camera::setRotation(const glm::quat& rotation) { · 17 unmodified lines
7472 }
7573 }
7674
77−void Camera::setHmdPosition(const glm::vec3& hmdPosition) {
78− _hmdPosition = hmdPosition;
79− if (_isKeepLookingAt) {
80− lookAt(_lookingAt);
81− }
82−}
83−
84−void Camera::setHmdRotation(const glm::quat& hmdRotation) {
85− _hmdRotation = hmdRotation;
86− if (_isKeepLookingAt) {
87− lookAt(_lookingAt);
88− }
89−}
90−
9175 void Camera::setMode(CameraMode mode) {
9276 _mode = mode;
9377 emit modeUpdated(modeToString(mode));
interface/src/Camera.h+4−10
@@ -45,26 +45,22 @@ public: · 44 unmodified lines
4545
4646 void setRotation(const glm::quat& rotation);
4747 void setProjection(const glm::mat4 & projection);
48− void setHmdPosition(const glm::vec3& hmdPosition);
49− void setHmdRotation(const glm::quat& hmdRotation);
5048 void setMode(CameraMode m);
5149
52− glm::quat getRotation() const { return _rotation * _hmdRotation; }
50+ glm::quat getRotation() const { return _rotation; }
5351 const glm::mat4& getProjection() const { return _projection; }
54− const glm::vec3& getHmdPosition() const { return _hmdPosition; }
55− const glm::quat& getHmdRotation() const { return _hmdRotation; }
5652 CameraMode getMode() const { return _mode; }
5753
5854 public slots:
5955 QString getModeString() const;
6056 void setModeString(const QString& mode);
6157
62− glm::vec3 getPosition() const { return _position + _hmdPosition; }
58+ glm::vec3 getPosition() const { return _position; }
6359 void setPosition(const glm::vec3& position);
6460
65− void setOrientation(const glm::quat& orientation) { setRotation(orientation); }
6661 glm::quat getOrientation() const { return getRotation(); }
67−
62+ void setOrientation(const glm::quat& orientation) { setRotation(orientation); }
63+
6864 PickRay computePickRay(float x, float y);
6965
7066 // These only work on independent cameras
@@ -86,8 +82,6 @@ private: · 15 unmodified lines
8682 glm::vec3 _position;
8783 glm::quat _rotation;
8884 glm::mat4 _projection;
89− glm::vec3 _hmdPosition;
90− glm::quat _hmdRotation;
9185 bool _isKeepLookingAt;
9286 glm::vec3 _lookingAt;
9387 };
interface/src/GLCanvas.cpp+2−2
@@ -39,11 +39,11 @@ bool GLCanvas::isThrottleRendering() const { · 38 unmodified lines
3939 }
4040
4141 int GLCanvas::getDeviceWidth() const {
42− return width() * (windowHandle() ? windowHandle()->devicePixelRatio() : 1.0f);
42+ return width() * (windowHandle() ? (float)windowHandle()->devicePixelRatio() : 1.0f);
4343 }
4444
4545 int GLCanvas::getDeviceHeight() const {
46− return height() * (windowHandle() ? windowHandle()->devicePixelRatio() : 1.0f);
46+ return height() * (windowHandle() ? (float)windowHandle()->devicePixelRatio() : 1.0f);
4747 }
4848
4949 void GLCanvas::initializeGL() {
interface/src/InterfaceActionFactory.cpp+3−3
@@ -12,7 +12,7 @@ · 11 unmodified lines
1212
1313
1414 #include <avatar/AvatarActionHold.h>
15−#include <ObjectActionPullToPoint.h>
15+#include <ObjectActionOffset.h>
1616 #include <ObjectActionSpring.h>
1717
1818 #include "InterfaceActionFactory.h"
@@ -27,8 +27,8 @@ EntityActionPointer InterfaceActionFactory::factory(EntitySimulation* simulation · 8 unmodified lines
2727 switch (type) {
2828 case ACTION_TYPE_NONE:
2929 return nullptr;
30− case ACTION_TYPE_PULL_TO_POINT:
31− action = (EntityActionPointer) new ObjectActionPullToPoint(id, ownerEntity);
30+ case ACTION_TYPE_OFFSET:
31+ action = (EntityActionPointer) new ObjectActionOffset(id, ownerEntity);
3232 break;
3333 case ACTION_TYPE_SPRING:
3434 action = (EntityActionPointer) new ObjectActionSpring(id, ownerEntity);
interface/src/LODManager.cpp+3−3
@@ -205,11 +205,11 @@ QString LODManager::getLODFeedbackText() { · 204 unmodified lines
205205 int relativeToTwentyTwenty = 20 / relativeToDefault;
206206
207207 QString result;
208− if (relativeToDefault > 1.01) {
208+ if (relativeToDefault > 1.01f) {
209209 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) {
211211 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) {
213213 result = QString("20:%1 or %2 of default distance for average vision%3").arg(relativeToTwentyTwenty).arg(relativeToDefault,0,'f',3).arg(granularityFeedback);
214214 } else {
215215 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
287287 addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror,
288288 0, //QML Qt::SHIFT | Qt::Key_H,
289289 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()));
290296
291297 addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools,
292298 #ifdef Q_OS_MAC
interface/src/Menu.h+1−0
@@ -201,6 +201,7 @@ namespace MenuOption { · 200 unmodified lines
201201 const QString FrameTimer = "Show Timer";
202202 const QString Fullscreen = "Fullscreen";
203203 const QString FullscreenMirror = "Fullscreen Mirror";
204+ const QString CenterPlayerInView = "Center Player In View";
204205 const QString GlowWhenSpeaking = "Glow When Speaking";
205206 const QString NamesAboveHeads = "Names Above Heads";
206207 const QString HMDTools = "HMD Tools";
interface/src/ModelPropertiesDialog.cpp+26−26
@@ -33,19 +33,19 @@ _basePath(basePath), · 32 unmodified lines
3333 _geometry(geometry)
3434 {
3535 setWindowTitle("Set Model Properties");
36−
36+
3737 QFormLayout* form = new QFormLayout();
3838 setLayout(form);
39−
39+
4040 form->addRow("Name:", _name = new QLineEdit());
41−
41+
4242 form->addRow("Texture Directory:", _textureDirectory = new QPushButton());
4343 connect(_textureDirectory, SIGNAL(clicked(bool)), SLOT(chooseTextureDirectory()));
44−
44+
4545 form->addRow("Scale:", _scale = new QDoubleSpinBox());
4646 _scale->setMaximum(FLT_MAX);
4747 _scale->setSingleStep(0.01);
48−
48+
4949 if (_modelType != FSTReader::ENTITY_MODEL) {
5050 if (_modelType == FSTReader::ATTACHMENT_MODEL) {
5151 QHBoxLayout* translation = new QHBoxLayout();
@@ -57,7 +57,7 @@ _geometry(geometry) · 5 unmodified lines
5757 form->addRow("Pivot Joint:", _pivotJoint = createJointBox());
5858 connect(_pivotAboutCenter, SIGNAL(toggled(bool)), SLOT(updatePivotJoint()));
5959 _pivotAboutCenter->setChecked(true);
60−
60+
6161 } else {
6262 form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox());
6363 form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox());
@@ -69,22 +69,22 @@ _geometry(geometry) · 5 unmodified lines
6969 form->addRow("Head Joint:", _headJoint = createJointBox());
7070 form->addRow("Left Hand Joint:", _leftHandJoint = createJointBox());
7171 form->addRow("Right Hand Joint:", _rightHandJoint = createJointBox());
72−
72+
7373 form->addRow("Free Joints:", _freeJoints = new QVBoxLayout());
7474 QPushButton* newFreeJoint = new QPushButton("New Free Joint");
7575 _freeJoints->addWidget(newFreeJoint);
7676 connect(newFreeJoint, SIGNAL(clicked(bool)), SLOT(createNewFreeJoint()));
7777 }
7878 }
79−
79+
8080 QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok |
8181 QDialogButtonBox::Cancel | QDialogButtonBox::Reset);
8282 connect(buttons, SIGNAL(accepted()), SLOT(accept()));
8383 connect(buttons, SIGNAL(rejected()), SLOT(reject()));
8484 connect(buttons->button(QDialogButtonBox::Reset), SIGNAL(clicked(bool)), SLOT(reset()));
85−
85+
8686 form->addRow(buttons);
87−
87+
8888 // reset to initialize the fields
8989 reset();
9090 }
@@ -100,42 +100,42 @@ QVariantHash ModelPropertiesDialog::getMapping() const { · 9 unmodified lines
100100 mapping.insert(NAME_FIELD, _name->text());
101101 mapping.insert(TEXDIR_FIELD, _textureDirectory->text());
102102 mapping.insert(SCALE_FIELD, QString::number(_scale->value()));
103−
103+
104104 // update the joint indices
105105 QVariantHash jointIndices;
106106 for (int i = 0; i < _geometry.joints.size(); i++) {
107107 jointIndices.insert(_geometry.joints.at(i).name, QString::number(i));
108108 }
109109 mapping.insert(JOINT_INDEX_FIELD, jointIndices);
110−
110+
111111 if (_modelType != FSTReader::ENTITY_MODEL) {
112112 QVariantHash joints = mapping.value(JOINT_FIELD).toHash();
113113 if (_modelType == FSTReader::ATTACHMENT_MODEL) {
114114 glm::vec3 pivot;
115115 if (_pivotAboutCenter->isChecked()) {
116116 pivot = (_geometry.meshExtents.minimum + _geometry.meshExtents.maximum) * 0.5f;
117−
117+
118118 } else if (_pivotJoint->currentIndex() != 0) {
119119 pivot = extractTranslation(_geometry.joints.at(_pivotJoint->currentIndex() - 1).transform);
120120 }
121− mapping.insert(TRANSLATION_X_FIELD, -pivot.x * _scale->value() + _translationX->value());
122− mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * _scale->value() + _translationY->value());
123− mapping.insert(TRANSLATION_Z_FIELD, -pivot.z * _scale->value() + _translationZ->value());
124−
121+ mapping.insert(TRANSLATION_X_FIELD, -pivot.x * (float)_scale->value() + (float)_translationX->value());
122+ mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * (float)_scale->value() + (float)_translationY->value());
123+ mapping.insert(TRANSLATION_Z_FIELD, -pivot.z * (float)_scale->value() + (float)_translationZ->value());
124+
125125 } else {
126126 insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText());
127127 insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText());
128128 insertJointMapping(joints, "jointNeck", _neckJoint->currentText());
129129 }
130−
131−
130+
131+
132132 if (_modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL) {
133133 insertJointMapping(joints, "jointRoot", _rootJoint->currentText());
134134 insertJointMapping(joints, "jointLean", _leanJoint->currentText());
135135 insertJointMapping(joints, "jointHead", _headJoint->currentText());
136136 insertJointMapping(joints, "jointLeftHand", _leftHandJoint->currentText());
137137 insertJointMapping(joints, "jointRightHand", _rightHandJoint->currentText());
138−
138+
139139 mapping.remove(FREE_JOINT_FIELD);
140140 for (int i = 0; i < _freeJoints->count() - 1; i++) {
141141 QComboBox* box = static_cast<QComboBox*>(_freeJoints->itemAt(i)->widget()->layout()->itemAt(0)->widget());
@@ -144,7 +144,7 @@ QVariantHash ModelPropertiesDialog::getMapping() const { · 2 unmodified lines
144144 }
145145 mapping.insert(JOINT_FIELD, joints);
146146 }
147−
147+
148148 return mapping;
149149 }
150150
@@ -156,9 +156,9 @@ void ModelPropertiesDialog::reset() { · 5 unmodified lines
156156 _name->setText(_originalMapping.value(NAME_FIELD).toString());
157157 _textureDirectory->setText(_originalMapping.value(TEXDIR_FIELD).toString());
158158 _scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble());
159−
159+
160160 QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash();
161−
161+
162162 if (_modelType != FSTReader::ENTITY_MODEL) {
163163 if (_modelType == FSTReader::ATTACHMENT_MODEL) {
164164 _translationX->setValue(_originalMapping.value(TRANSLATION_X_FIELD).toDouble());
@@ -166,20 +166,20 @@ void ModelPropertiesDialog::reset() { · 1 unmodified line
166166 _translationZ->setValue(_originalMapping.value(TRANSLATION_Z_FIELD).toDouble());
167167 _pivotAboutCenter->setChecked(true);
168168 _pivotJoint->setCurrentIndex(0);
169−
169+
170170 } else {
171171 setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString());
172172 setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString());
173173 setJointText(_neckJoint, jointHash.value("jointNeck").toString());
174174 }
175−
175+
176176 if (_modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL) {
177177 setJointText(_rootJoint, jointHash.value("jointRoot").toString());
178178 setJointText(_leanJoint, jointHash.value("jointLean").toString());
179179 setJointText(_headJoint, jointHash.value("jointHead").toString());
180180 setJointText(_leftHandJoint, jointHash.value("jointLeftHand").toString());
181181 setJointText(_rightHandJoint, jointHash.value("jointRightHand").toString());
182−
182+
183183 while (_freeJoints->count() > 1) {
184184 delete _freeJoints->itemAt(0)->widget();
185185 }
interface/src/Stars.cpp+8−10
@@ -16,13 +16,13 @@ · 15 unmodified lines
1616 #include "InterfaceConfig.h"
1717 #include "starfield/Controller.h"
1818
19−Stars::Stars() :
19+Stars::Stars() :
2020 _controller(0l), _starsLoaded(false) {
2121 _controller = new starfield::Controller;
2222 }
2323
24−Stars::~Stars() {
25− delete _controller;
24+Stars::~Stars() {
25+ delete _controller;
2626 }
2727
2828 bool Stars::generate(unsigned numStars, unsigned seed) {
@@ -30,22 +30,20 @@ bool Stars::generate(unsigned numStars, unsigned seed) { · 1 unmodified line
3030 return _starsLoaded;
3131 }
3232
33−bool Stars::setResolution(unsigned k) {
34− return _controller->setResolution(k);
33+bool Stars::setResolution(unsigned k) {
34+ return _controller->setResolution(k);
3535 }
3636
3737 void Stars::render(float fovY, float aspect, float nearZ, float alpha) {
3838 // 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);
4040 float halfDiagonal = sqrt(quadrantHeight * quadrantHeight * (1.0f + aspect * aspect));
4141
4242 // determine fov angle in respect to the diagonal
43− float fovDiagonal = atan(halfDiagonal / nearZ) * 2.0f;
43+ float fovDiagonal = atanf(halfDiagonal / nearZ) * 2.0f;
4444
4545 // 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));
4747
4848 _controller->render(fovDiagonal, aspect, glm::affineInverse(view), alpha);
4949 }
50−
51−
interface/src/UIUtil.cpp+1−1
@@ -70,6 +70,6 @@ void UIUtil::internalScaleWidgetFontSizes(QWidget* widget, float scale) { · 69 unmodified lines
7070 }
7171
7272 QFont font = widget->font();
73− font.setPointSizeF(font.pointSizeF() * scale);
73+ font.setPointSizeF(font.pointSizeF() * (double)scale);
7474 widget->setFont(font);
7575 }
interface/src/Util.cpp+25−22
@@ -41,7 +41,7 @@ void renderWorldBox(gpu::Batch& batch) { · 40 unmodified lines
4141 static const glm::vec3 green(0.0f, 1.0f, 0.0f);
4242 static const glm::vec3 blue(0.0f, 0.0f, 1.0f);
4343 static const glm::vec3 grey(0.5f, 0.5f, 0.5f);
44−
44+
4545 auto transform = Transform{};
4646 batch.setModelTransform(transform);
4747 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
4949 geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, TREE_SCALE), blue);
5050 geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, TREE_SCALE), glm::vec3(TREE_SCALE, 0.0f, TREE_SCALE), grey);
5151 geometryCache->renderLine(batch, glm::vec3(TREE_SCALE, 0.0f, TREE_SCALE), glm::vec3(TREE_SCALE, 0.0f, 0.0f), grey);
52−
52+
5353 // Draw meter markers along the 3 axis to help with measuring things
5454 const float MARKER_DISTANCE = 1.0f;
5555 const float MARKER_RADIUS = 0.05f;
56−
56+
5757 geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, red);
58−
58+
5959 transform.setTranslation(glm::vec3(MARKER_DISTANCE, 0.0f, 0.0f));
6060 batch.setModelTransform(transform);
6161 geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, red);
@@ -79,10 +79,10 @@ const glm::vec3 randVector() { · 17 unmodified lines
7979 }
8080
8181 static TextRenderer* textRenderer(int mono) {
82− static TextRenderer* monoRenderer = TextRenderer::getInstance(MONO_FONT_FAMILY);
82+ static TextRenderer* monoRenderer = TextRenderer::getInstance(MONO_FONT_FAMILY);
8383 static TextRenderer* proportionalRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY,
8484 -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,
8686 false);
8787 switch (mono) {
8888 case 1:
@@ -133,46 +133,46 @@ void runTimingTests() { · 44 unmodified lines
133133 QElapsedTimer startTime;
134134 startTime.start();
135135 float elapsedUsecs;
136−
136+
137137 float NSEC_TO_USEC = 1.0f / 1000.0f;
138138 elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
139− qCDebug(interfaceapp, "QElapsedTimer::nsecElapsed() usecs: %f", elapsedUsecs);
140−
139+ qCDebug(interfaceapp, "QElapsedTimer::nsecElapsed() usecs: %f", (double)elapsedUsecs);
140+
141141 // Test sleep functions for accuracy
142142 startTime.start();
143143 QThread::msleep(1);
144144 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));
146146
147147 startTime.start();
148148 QThread::sleep(1);
149149 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));
151151
152152 startTime.start();
153153 usleep(1);
154154 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));
156156
157157 startTime.start();
158158 usleep(10);
159159 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));
161161
162162 startTime.start();
163163 usleep(100);
164164 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));
166166
167167 startTime.start();
168168 usleep(1000);
169169 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));
171171
172172 startTime.start();
173173 usleep(15000);
174174 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));
176176
177177 // Random number generation
178178 startTime.start();
@@ -180,7 +180,8 @@ void runTimingTests() { · 1 unmodified line
180180 iResults[i] = rand();
181181 }
182182 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]);
184185
185186 // Random number generation using randFloat()
186187 startTime.start();
@@ -188,7 +189,8 @@ void runTimingTests() { · 1 unmodified line
188189 fResults[i] = randFloat();
189190 }
190191 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]));
192194
193195 free(iResults);
194196 free(fResults);
@@ -200,7 +202,7 @@ void runTimingTests() { · 5 unmodified lines
200202 fTest = powf(fTest, 0.5f);
201203 }
202204 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));
204206
205207 // Vector Math
206208 float distance;
@@ -213,19 +215,20 @@ void runTimingTests() { · 6 unmodified lines
213215 }
214216 elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
215217 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);
217219
218220 // Vec3 test
219221 glm::vec3 vecA(randVector()), vecB(randVector());
220222 float result;
221−
223+
222224 startTime.start();
223225 for (int i = 0; i < numTests; i++) {
224226 glm::vec3 temp = vecA-vecB;
225227 result = glm::dot(temp,temp);
226228 }
227229 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);
229232 }
230233
231234 bool 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
4141 if (!_isEnabled) {
4242 return;
4343 }
44−
44+
4545 const int linesWhenCentered = _shouldShowInjectedStreams ? 34 : 27;
4646 const int CENTERED_BACKGROUND_HEIGHT = STATS_HEIGHT_PER_LINE * linesWhenCentered;
47−
47+
4848 int lines = _shouldShowInjectedStreams ? _stats->getMixerInjectedStreamStatsMap().size() * 7 + 27 : 27;
4949 int statsHeight = STATS_HEIGHT_PER_LINE * lines;
50−
51−
50+
51+
5252 static const glm::vec4 backgroundColor = { 0.2f, 0.2f, 0.2f, 0.6f };
5353 int x = std::max((width - (int)STATS_WIDTH) / 2, 0);
5454 int y = std::max((height - CENTERED_BACKGROUND_HEIGHT) / 2, 0);
5555 int w = STATS_WIDTH;
5656 int h = statsHeight;
5757 DependencyManager::get<GeometryCache>()->renderQuad(x, y, w, h, backgroundColor);
58−
58+
5959 int horizontalOffset = x + 5;
6060 int verticalOffset = y;
61−
61+
6262 float scale = 0.10f;
6363 float rotation = 0.0f;
6464 int font = 2;
65−
65+
6666 char latencyStatString[512];
67−
67+
6868 float audioInputBufferLatency = 0.0f, inputRingBufferLatency = 0.0f, networkRoundtripLatency = 0.0f, mixerRingBufferLatency = 0.0f, outputRingBufferLatency = 0.0f, audioOutputBufferLatency = 0.0f;
69−
69+
7070 AudioStreamStats downstreamAudioStreamStats = _stats->getMixerDownstreamStats();
7171 SharedNodePointer audioMixerNodePointer = DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::AudioMixer);
7272 if (!audioMixerNodePointer.isNull()) {
@@ -78,48 +78,62 @@ void AudioIOStatsRenderer::render(const float* color, int width, int height) { · 5 unmodified lines
7878 audioOutputBufferLatency = _stats->getAudioOutputMsecsUnplayedStats().getWindowAverage();
7979 }
8080 float totalLatency = audioInputBufferLatency + inputRingBufferLatency + networkRoundtripLatency + mixerRingBufferLatency + outputRingBufferLatency + audioOutputBufferLatency;
81−
82− sprintf(latencyStatString, " Audio input buffer: %7.2fms - avg msecs of samples read to the input ring buffer in last 10s", audioInputBufferLatency);
81+
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);
8385 verticalOffset += STATS_HEIGHT_PER_LINE;
8486 drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color);
85−
86− sprintf(latencyStatString, " Input ring buffer: %7.2fms - avg msecs of samples in input ring buffer in last 10s", inputRingBufferLatency);
87+
88+ sprintf(latencyStatString,
89+ " Input ring buffer: %7.2fms - avg msecs of samples in input ring buffer in last 10s",
90+ (double)inputRingBufferLatency);
8791 verticalOffset += STATS_HEIGHT_PER_LINE;
8892 drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color);
89−
90− sprintf(latencyStatString, " Network to mixer: %7.2fms - half of last ping value calculated by the node list", networkRoundtripLatency / 2.0f);
93+
94+ sprintf(latencyStatString,
95+ " Network to mixer: %7.2fms - half of last ping value calculated by the node list",
96+ (double)(networkRoundtripLatency / 2.0f));
9197 verticalOffset += STATS_HEIGHT_PER_LINE;
9298 drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color);
93−
94− sprintf(latencyStatString, " AudioMixer ring buffer: %7.2fms - avg msecs of samples in audio mixer's ring buffer in last 10s", mixerRingBufferLatency);
99+
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);
95103 verticalOffset += STATS_HEIGHT_PER_LINE;
96104 drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color);
97−
98− sprintf(latencyStatString, " Network to client: %7.2fms - half of last ping value calculated by the node list", networkRoundtripLatency / 2.0f);
105+
106+ sprintf(latencyStatString,
107+ " Network to client: %7.2fms - half of last ping value calculated by the node list",
108+ (double)(networkRoundtripLatency / 2.0f));
99109 verticalOffset += STATS_HEIGHT_PER_LINE;
100110 drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color);
101−
102− sprintf(latencyStatString, " Output ring buffer: %7.2fms - avg msecs of samples in output ring buffer in last 10s", outputRingBufferLatency);
111+
112+ sprintf(latencyStatString,
113+ " Output ring buffer: %7.2fms - avg msecs of samples in output ring buffer in last 10s",
114+ (double)outputRingBufferLatency);
103115 verticalOffset += STATS_HEIGHT_PER_LINE;
104116 drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color);
105−
106− sprintf(latencyStatString, " Audio output buffer: %7.2fms - avg msecs of samples in audio output buffer in last 10s", audioOutputBufferLatency);
117+
118+ sprintf(latencyStatString,
119+ " Audio output buffer: %7.2fms - avg msecs of samples in audio output buffer in last 10s",
120+ (double)audioOutputBufferLatency);
107121 verticalOffset += STATS_HEIGHT_PER_LINE;
108122 drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color);
109−
110− sprintf(latencyStatString, " TOTAL: %7.2fms\n", totalLatency);
123+
124+ sprintf(latencyStatString, " TOTAL: %7.2fms\n", (double)totalLatency);
111125 verticalOffset += STATS_HEIGHT_PER_LINE;
112126 drawText(horizontalOffset, verticalOffset, scale, rotation, font, latencyStatString, color);
113−
114−
127+
128+
115129 verticalOffset += STATS_HEIGHT_PER_LINE; // blank line
116−
130+
117131 char clientUpstreamMicLabelString[] = "Upstream Mic Audio Packets Sent Gaps (by client):";
118132 verticalOffset += STATS_HEIGHT_PER_LINE;
119133 drawText(horizontalOffset, verticalOffset, scale, rotation, font, clientUpstreamMicLabelString, color);
120−
134+
121135 const MovingMinMaxAvg<quint64>& packetSentTimeGaps = _stats->getPacketSentTimeGaps();
122−
136+
123137 char stringBuffer[512];
124138 sprintf(stringBuffer, " Inter-packet timegaps (overall) | min: %9s, max: %9s, avg: %9s",
125139 formatUsecTime(packetSentTimeGaps.getMin()).toLatin1().data(),
@@ -127,47 +141,47 @@ void AudioIOStatsRenderer::render(const float* color, int width, int height) { · 1 unmodified line
127141 formatUsecTime(packetSentTimeGaps.getAverage()).toLatin1().data());
128142 verticalOffset += STATS_HEIGHT_PER_LINE;
129143 drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color);
130−
144+
131145 sprintf(stringBuffer, " Inter-packet timegaps (last 30s) | min: %9s, max: %9s, avg: %9s",
132146 formatUsecTime(packetSentTimeGaps.getWindowMin()).toLatin1().data(),
133147 formatUsecTime(packetSentTimeGaps.getWindowMax()).toLatin1().data(),
134148 formatUsecTime(packetSentTimeGaps.getWindowAverage()).toLatin1().data());
135149 verticalOffset += STATS_HEIGHT_PER_LINE;
136150 drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color);
137−
151+
138152 verticalOffset += STATS_HEIGHT_PER_LINE; // blank line
139−
153+
140154 char upstreamMicLabelString[] = "Upstream mic audio stats (received and reported by audio-mixer):";
141155 verticalOffset += STATS_HEIGHT_PER_LINE;
142156 drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamMicLabelString, color);
143−
157+
144158 renderAudioStreamStats(&_stats->getMixerAvatarStreamStats(), horizontalOffset, verticalOffset,
145159 scale, rotation, font, color);
146−
147−
160+
161+
148162 verticalOffset += STATS_HEIGHT_PER_LINE; // blank line
149−
163+
150164 char downstreamLabelString[] = "Downstream mixed audio stats:";
151165 verticalOffset += STATS_HEIGHT_PER_LINE;
152166 drawText(horizontalOffset, verticalOffset, scale, rotation, font, downstreamLabelString, color);
153−
167+
154168 AudioStreamStats downstreamStats = _stats->getMixerDownstreamStats();
155169 renderAudioStreamStats(&downstreamStats, horizontalOffset, verticalOffset,
156170 scale, rotation, font, color, true);
157−
158−
171+
172+
159173 if (_shouldShowInjectedStreams) {
160−
174+
161175 foreach(const AudioStreamStats& injectedStreamAudioStats, _stats->getMixerInjectedStreamStatsMap()) {
162−
176+
163177 verticalOffset += STATS_HEIGHT_PER_LINE; // blank line
164−
178+
165179 char upstreamInjectedLabelString[512];
166180 sprintf(upstreamInjectedLabelString, "Upstream injected audio stats: stream ID: %s",
167181 injectedStreamAudioStats._streamIdentifier.toString().toLatin1().data());
168182 verticalOffset += STATS_HEIGHT_PER_LINE;
169183 drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamInjectedLabelString, color);
170−
184+
171185 renderAudioStreamStats(&injectedStreamAudioStats, horizontalOffset, verticalOffset, scale, rotation, font, color);
172186 }
173187 }
@@ -175,22 +189,22 @@ void AudioIOStatsRenderer::render(const float* color, int width, int height) { · 1 unmodified line
175189
176190 void AudioIOStatsRenderer::renderAudioStreamStats(const AudioStreamStats* streamStats, int horizontalOffset, int& verticalOffset,
177191 float scale, float rotation, int font, const float* color, bool isDownstreamStats) {
178−
192+
179193 char stringBuffer[512];
180−
194+
181195 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),
183197 streamStats->_packetStreamStats._lost,
184− streamStats->_packetStreamWindowStats.getLostRate() * 100.0f,
198+ (double)(streamStats->_packetStreamWindowStats.getLostRate() * 100.0f),
185199 streamStats->_packetStreamWindowStats._lost);
186200 verticalOffset += STATS_HEIGHT_PER_LINE;
187201 drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color);
188−
202+
189203 if (isDownstreamStats) {
190204 sprintf(stringBuffer, " Ringbuffer frames | desired: %u, avg_available(10s): %u+%d, available: %u+%d",
191205 streamStats->_desiredJitterBufferFrames,
192206 streamStats->_framesAvailableAverage,
193− (int)(_stats->getAudioInputMsecsReadStats().getWindowAverage() / AudioConstants::NETWORK_FRAME_MSECS),
207+ (int)((float)_stats->getAudioInputMsecsReadStats().getWindowAverage() / AudioConstants::NETWORK_FRAME_MSECS),
194208 streamStats->_framesAvailable,
195209 (int)(_stats->getAudioOutputMsecsUnplayedStats().getCurrentIntervalLastSample()
196210 / AudioConstants::NETWORK_FRAME_MSECS));
@@ -200,10 +214,10 @@ void AudioIOStatsRenderer::renderAudioStreamStats(const AudioStreamStats* stream · 3 unmodified lines
200214 streamStats->_framesAvailableAverage,
201215 streamStats->_framesAvailable);
202216 }
203−
217+
204218 verticalOffset += STATS_HEIGHT_PER_LINE;
205219 drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color);
206−
220+
207221 sprintf(stringBuffer, " Ringbuffer stats | starves: %u, prev_starve_lasted: %u, frames_dropped: %u, overflows: %u",
208222 streamStats->_starveCount,
209223 streamStats->_consecutiveNotMixedCount,
@@ -211,18 +225,18 @@ void AudioIOStatsRenderer::renderAudioStreamStats(const AudioStreamStats* stream · 1 unmodified line
211225 streamStats->_overflowCount);
212226 verticalOffset += STATS_HEIGHT_PER_LINE;
213227 drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color);
214−
228+
215229 sprintf(stringBuffer, " Inter-packet timegaps (overall) | min: %9s, max: %9s, avg: %9s",
216230 formatUsecTime(streamStats->_timeGapMin).toLatin1().data(),
217231 formatUsecTime(streamStats->_timeGapMax).toLatin1().data(),
218232 formatUsecTime(streamStats->_timeGapAverage).toLatin1().data());
219233 verticalOffset += STATS_HEIGHT_PER_LINE;
220234 drawText(horizontalOffset, verticalOffset, scale, rotation, font, stringBuffer, color);
221−
235+
222236 sprintf(stringBuffer, " Inter-packet timegaps (last 30s) | min: %9s, max: %9s, avg: %9s",
223237 formatUsecTime(streamStats->_timeGapWindowMin).toLatin1().data(),
224238 formatUsecTime(streamStats->_timeGapWindowMax).toLatin1().data(),
225239 formatUsecTime(streamStats->_timeGapWindowAverage).toLatin1().data());
226240 verticalOffset += STATS_HEIGHT_PER_LINE;
227241 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
24−const int MUTE_ICON_SIZE = 24;
25−
26−AudioToolBox::AudioToolBox() :
27− _iconPulseTimeReference(usecTimestampNow())
28−{
29−}
30−
31−bool AudioToolBox::mousePressEvent(int x, int y) {
32− if (_iconBounds.contains(x, y)) {
33− DependencyManager::get<AudioClient>()->toggleMute();
34− return true;
35− }
36− return false;
37−}
38−
39−void AudioToolBox::render(int x, int y, int padding, bool boxed) {
40− glEnable(GL_TEXTURE_2D);
41−
42− if (!_micTexture) {
43− _micTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/mic.svg");
44− }
45− if (!_muteTexture) {
46− _muteTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/mic-mute.svg");
47− }
48− if (_boxTexture) {
49− _boxTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/audio-box.svg");
50− }
51−
52− auto audioIO = DependencyManager::get<AudioClient>();
53−
54− if (boxed) {
55− bool isClipping = ((audioIO->getTimeSinceLastClip() > 0.0f) && (audioIO->getTimeSinceLastClip() < 1.0f));
56− const int BOX_LEFT_PADDING = 5;
57− const int BOX_TOP_PADDING = 10;
58− const int BOX_WIDTH = 266;
59− const int BOX_HEIGHT = 44;
60−
61− QRect boxBounds = QRect(x - BOX_LEFT_PADDING, y - BOX_TOP_PADDING, BOX_WIDTH, BOX_HEIGHT);
62− glm::vec4 quadColor;
63−
64− if (isClipping) {
65− quadColor = glm::vec4(1.0f, 0.0f, 0.0f, 1.0f);
66− } else {
67− quadColor = glm::vec4(0.41f, 0.41f, 0.41f, 1.0f);
68− }
69− glm::vec2 topLeft(boxBounds.left(), boxBounds.top());
70− glm::vec2 bottomRight(boxBounds.right(), boxBounds.bottom());
71− static const glm::vec2 texCoordTopLeft(1,1);
72− static const glm::vec2 texCoordBottomRight(0, 0);
73− glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_boxTexture));
74− DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor);
75− }
76−
77− float iconColor = 1.0f;
78−
79− _iconBounds = QRect(x + padding, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE);
80− if (!audioIO->isMuted()) {
81− glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_micTexture));
82− iconColor = 1.0f;
83− } else {
84− glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_muteTexture));
85−
86− // Make muted icon pulsate
87− static const float PULSE_MIN = 0.4f;
88− static const float PULSE_MAX = 1.0f;
89− static const float PULSE_FREQUENCY = 1.0f; // in Hz
90− qint64 now = usecTimestampNow();
91− if (now - _iconPulseTimeReference > (qint64)USECS_PER_SECOND) {
92− // Prevents t from getting too big, which would diminish glm::cos precision
93− _iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND);
94− }
95− float t = (float)(now - _iconPulseTimeReference) / (float)USECS_PER_SECOND;
96− float pulseFactor = (glm::cos(t * PULSE_FREQUENCY * 2.0f * PI) + 1.0f) / 2.0f;
97− iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor;
98− }
99−
100− glm::vec4 quadColor(iconColor, iconColor, iconColor, 1.0f);
101−
102− glm::vec2 topLeft(_iconBounds.left(), _iconBounds.top());
103− glm::vec2 bottomRight(_iconBounds.right(), _iconBounds.bottom());
104− glm::vec2 texCoordTopLeft(1,1);
105− glm::vec2 texCoordBottomRight(0,0);
106−
107− if (_boxQuadID == GeometryCache::UNKNOWN_ID) {
108− _boxQuadID = DependencyManager::get<GeometryCache>()->allocateID();
109− }
110−
111− DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor, _boxQuadID);
112− glBindTexture(GL_TEXTURE_2D, 0);
113− glDisable(GL_TEXTURE_2D);
114−}
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−
19−class AudioToolBox : public Dependency {
20− SINGLETON_DEPENDENCY
21−public:
22− void render(int x, int y, int padding, bool boxed);
23−
24− bool mousePressEvent(int x, int y);
25−protected:
26− AudioToolBox();
27−private:
28− gpu::TexturePointer _micTexture;
29− gpu::TexturePointer _muteTexture;
30− gpu::TexturePointer _boxTexture;
31− int _boxQuadID = GeometryCache::UNKNOWN_ID;
32− QRect _iconBounds;
33− qint64 _iconPulseTimeReference = 0;
34−};
35−
36−#endif // hifi_AudioToolBox_h
interface/src/avatar/Avatar.cpp+110−121
@@ -57,7 +57,7 @@ const float CHAT_MESSAGE_SCALE = 0.0015f; · 56 unmodified lines
5757 const float CHAT_MESSAGE_HEIGHT = 0.1f;
5858 const float DISPLAYNAME_FADE_TIME = 0.5f;
5959 const float DISPLAYNAME_FADE_FACTOR = pow(0.01f, 1.0f / DISPLAYNAME_FADE_TIME);
60−const float DISPLAYNAME_ALPHA = 0.95f;
60+const float DISPLAYNAME_ALPHA = 1.0f;
6161 const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f;
6262
6363 namespace render {
@@ -107,6 +107,7 @@ Avatar::Avatar() : · 43 unmodified lines
107107 }
108108
109109 Avatar::~Avatar() {
110+ assert(_motionState == nullptr);
110111 for(auto attachment : _unusedAttachments) {
111112 delete attachment;
112113 }
@@ -280,9 +281,9 @@ enum TextRendererType { · 167 unmodified lines
280281 };
281282
282283 static TextRenderer3D* textRenderer(TextRendererType type) {
283− static TextRenderer3D* chatRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, 24, -1,
284+ static TextRenderer3D* chatRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, -1,
284285 false, TextRenderer3D::SHADOW_EFFECT);
285− static TextRenderer3D* displayNameRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, 12);
286+ static TextRenderer3D* displayNameRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY);
286287
287288 switch(type) {
288289 case CHAT:
@@ -323,7 +324,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo · 34 unmodified lines
323324 _referential->update();
324325 }
325326
326− auto batch = renderArgs->_batch;
327+ auto& batch = *renderArgs->_batch;
327328
328329 if (postLighting &&
329330 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
354355 Transform pointerTransform;
355356 pointerTransform.setTranslation(position);
356357 pointerTransform.setRotation(rotation);
357− batch->setModelTransform(pointerTransform);
358− deferredLighting->bindSimpleProgram(*batch);
359− geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor);
358+ batch.setModelTransform(pointerTransform);
359+ deferredLighting->bindSimpleProgram(batch);
360+ geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor);
360361 }
361362 }
362363
@@ -377,9 +378,9 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo · 14 unmodified lines
377378 Transform pointerTransform;
378379 pointerTransform.setTranslation(position);
379380 pointerTransform.setRotation(rotation);
380− batch->setModelTransform(pointerTransform);
381− deferredLighting->bindSimpleProgram(*batch);
382− geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor);
381+ batch.setModelTransform(pointerTransform);
382+ deferredLighting->bindSimpleProgram(batch);
383+ geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor);
383384 }
384385 }
385386 }
@@ -464,8 +465,8 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo · 78 unmodified lines
464465 }
465466 Transform transform;
466467 transform.setTranslation(position);
467− batch->setModelTransform(transform);
468− DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(*batch, LOOK_AT_INDICATOR_RADIUS
468+ batch.setModelTransform(transform);
469+ DependencyManager::get<DeferredLightingEffect>()->renderSolidSphere(batch, LOOK_AT_INDICATOR_RADIUS
469470 , 15, 15, LOOK_AT_INDICATOR_COLOR);
470471 }
471472 }
@@ -492,14 +493,14 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo · 20 unmodified lines
492493 Transform transform;
493494 transform.setTranslation(_position);
494495 transform.setScale(height);
495− batch->setModelTransform(transform);
496+ batch.setModelTransform(transform);
496497
497498 if (_voiceSphereID == GeometryCache::UNKNOWN_ID) {
498499 _voiceSphereID = DependencyManager::get<GeometryCache>()->allocateID();
499500 }
500501
501− DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch);
502− DependencyManager::get<GeometryCache>()->renderSphere(*batch, sphereRadius, 15, 15,
502+ DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch);
503+ DependencyManager::get<GeometryCache>()->renderSphere(batch, sphereRadius, 15, 15,
503504 glm::vec4(SPHERE_COLOR[0], SPHERE_COLOR[1], SPHERE_COLOR[2], 1.0f - angle / MAX_SPHERE_ANGLE), true,
504505 _voiceSphereID);
505506 }
@@ -507,14 +508,12 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo · 1 unmodified line
507508 }
508509
509510 const float DISPLAYNAME_DISTANCE = 20.0f;
510− setShowDisplayName(renderArgs->_renderMode == RenderArgs::NORMAL_RENDER_MODE && distanceToTarget < DISPLAYNAME_DISTANCE);
511+ setShowDisplayName(distanceToTarget < DISPLAYNAME_DISTANCE);
511512
512− if (renderArgs->_renderMode != RenderArgs::NORMAL_RENDER_MODE || (isMyAvatar() &&
513− Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON)) {
514− return;
513+ auto cameraMode = Application::getInstance()->getCamera()->getMode();
514+ if (!isMyAvatar() || cameraMode != CAMERA_MODE_FIRST_PERSON) {
515+ renderDisplayName(batch, *renderArgs->_viewFrustum);
515516 }
516−
517− renderDisplayName(renderArgs);
518517 }
519518
520519 glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
@@ -654,8 +653,8 @@ float Avatar::getBillboardSize() const { · 133 unmodified lines
654653 return _scale * BILLBOARD_DISTANCE * tanf(glm::radians(BILLBOARD_FIELD_OF_VIEW / 2.0f));
655654 }
656655
657−glm::vec3 Avatar::getDisplayNamePosition() {
658− glm::vec3 namePosition;
656+glm::vec3 Avatar::getDisplayNamePosition() const {
657+ glm::vec3 namePosition(0.0f);
659658 if (getSkeletonModel().getNeckPosition(namePosition)) {
660659 namePosition += getBodyUpDirection() * getHeadHeight() * 1.1f;
661660 } else {
@@ -665,81 +664,73 @@ glm::vec3 Avatar::getDisplayNamePosition() { · 3 unmodified lines
665664 return namePosition;
666665 }
667666
668−float Avatar::calculateDisplayNameScaleFactor(const glm::vec3& textPosition, bool inHMD) {
669−
670− // We need to compute the scale factor such as the text remains with fixed size respect to window coordinates
671− // We project a unit vector and check the difference in screen coordinates, to check which is the
672− // correction scale needed
673− // save the matrices for later scale correction factor
674− // The up vector must be relative to the rotation current rotation matrix:
675− // we set the identity
667+Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, float fontSize) const {
668+ Transform result;
669+ // We assume textPosition is whithin the frustum
670+ glm::vec3 textPosition = getDisplayNamePosition();
671+
672+ // Compute viewProjection matrix
673+ glm::mat4 projMat, viewMat;
674+ Transform view;
675+ frustum.evalProjectionMatrix(projMat);
676+ frustum.evalViewTransform(view);
677+ glm::mat4 viewProj = projMat * view.getInverseMatrix(viewMat);
678+
679+ // Used to determine correct scale
676680 glm::vec3 testPoint0 = textPosition;
677− glm::vec3 testPoint1 = textPosition + (Application::getInstance()->getCamera()->getRotation() * IDENTITY_UP);
678−
679− double textWindowHeight;
680−
681+ glm::vec3 testPoint1 = testPoint0 + glm::normalize(frustum.getUp());
682+ // testPoints projections
683+ glm::vec4 p0 = viewProj * glm::vec4(testPoint0, 1.0);
684+ glm::vec4 p1 = viewProj * glm::vec4(testPoint1, 1.0);
685+
686+ // TODO REMOVE vvv
681687 GLint viewportMatrix[4];
682688 glGetIntegerv(GL_VIEWPORT, viewportMatrix);
683689 glm::dmat4 modelViewMatrix;
684− float windowSizeX = viewportMatrix[2] - viewportMatrix[0];
685690 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;
686720
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;
711721 }
712722
713−void Avatar::renderDisplayName(RenderArgs* renderArgs) {
714− auto batch = renderArgs->_batch;
715−
723+void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum) const {
716724 bool shouldShowReceiveStats = DependencyManager::get<AvatarManager>()->shouldShowReceiveStats() && !isMyAvatar();
717725
726+ // If we have nothing to draw, or it's tottaly transparent, return
718727 if ((_displayName.isEmpty() && !shouldShowReceiveStats) || _displayNameAlpha == 0.0f) {
719728 return;
720729 }
721−
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);
730+ auto renderer = textRenderer(DISPLAYNAME);
738731
739732 // optionally render timing stats for this avatar with the display name
740733 QString renderedDisplayName = _displayName;
741− QRect nameDynamicRect = _displayNameBoundingRect;
742−
743734 if (shouldShowReceiveStats) {
744735 float kilobitsPerSecond = getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT;
745736
@@ -747,42 +738,43 @@ void Avatar::renderDisplayName(RenderArgs* renderArgs) { · 1 unmodified line
747738 if (!renderedDisplayName.isEmpty()) {
748739 statsFormat.prepend(" - ");
749740 }
750−
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);
741+ renderedDisplayName += statsFormat.arg(QString::number(kilobitsPerSecond, 'f', 2)).arg(getReceiveRate());
756742 }
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;
771743
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
772760 glm::vec4 textColor(0.93f, 0.93f, 0.93f, _displayNameAlpha);
773761 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());
775766
767+ // Render background slightly behind to avoid z-fighting
776768 auto backgroundTransform = textTransform;
777− backgroundTransform.postTranslate(glm::vec3(0.0f, 0.0f, -0.001f));
778− batch->setModelTransform(backgroundTransform);
779− DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch);
780− DependencyManager::get<GeometryCache>()->renderBevelCornersRect(*batch, left, bottom, right - left, top - bottom, 3,
781− backgroundColor);
769+ backgroundTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_BEHIND));
770+ batch.setModelTransform(backgroundTransform);
771+ DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch);
772+ DependencyManager::get<GeometryCache>()->renderBevelCornersRect(batch, left, bottom, width, height,
773+ bevelDistance, backgroundColor);
774+ // Render actual name
782775 QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit();
783−
784− batch->setModelTransform(textTransform);
785− textRenderer(DISPLAYNAME)->draw(*batch, text_x, -text_y, nameUTF8.data(), textColor);
776+ batch.setModelTransform(textTransform);
777+ renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor);
786778 }
787779
788780 bool Avatar::findRayIntersection(RayIntersectionInfo& intersection) const {
@@ -987,13 +979,6 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) { · 198 unmodified lines
987979 }
988980 }
989981
990−void Avatar::setDisplayName(const QString& displayName) {
991− AvatarData::setDisplayName(displayName);
992− // FIXME is this a sufficient replacement for tightBoundingRect?
993− glm::vec2 extent = textRenderer(DISPLAYNAME)->computeExtent(displayName);
994− _displayNameBoundingRect = QRect(0, 0, (int)extent.x, (int)extent.y);
995−}
996−
997982 void Avatar::setBillboard(const QByteArray& billboard) {
998983 AvatarData::setBillboard(billboard);
999984
@@ -1092,7 +1077,12 @@ float Avatar::getSkeletonHeight() const { · 92 unmodified lines
10921077 float Avatar::getHeadHeight() const {
10931078 Extents extents = getHead()->getFaceModel().getMeshExtents();
10941079 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+ }
10961086 }
10971087
10981088 extents = _skeletonModel.getMeshExtents();
@@ -1116,7 +1106,7 @@ void Avatar::setShowDisplayName(bool showDisplayName) { · 17 unmodified lines
11161106 }
11171107
11181108 // For myAvatar, the alpha update is not done (called in simulate for other avatars)
1119− if (DependencyManager::get<AvatarManager>()->getMyAvatar() == this) {
1109+ if (isMyAvatar()) {
11201110 if (showDisplayName) {
11211111 _displayNameAlpha = DISPLAYNAME_ALPHA;
11221112 } else {
@@ -1129,7 +1119,6 @@ void Avatar::setShowDisplayName(bool showDisplayName) { · 6 unmodified lines
11291119 } else {
11301120 _displayNameTargetAlpha = 0.0f;
11311121 }
1132−
11331122 }
11341123
11351124 // virtual
interface/src/avatar/Avatar.h+5−5
@@ -98,6 +98,7 @@ public: · 97 unmodified lines
9898 //getters
9999 bool isInitialized() const { return _initialized; }
100100 SkeletonModel& getSkeletonModel() { return _skeletonModel; }
101+ const SkeletonModel& getSkeletonModel() const { return _skeletonModel; }
101102 const QVector<Model*>& getAttachmentModels() const { return _attachmentModels; }
102103 glm::vec3 getChestPosition() const;
103104 float getScale() const { return _scale; }
@@ -131,7 +132,7 @@ public: · 27 unmodified lines
131132 /// \return whether or not the plane penetrated
132133 bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions);
133134
134− virtual bool isMyAvatar() { return false; }
135+ virtual bool isMyAvatar() const { return false; }
135136
136137 virtual QVector<glm::quat> getJointRotations() const;
137138 virtual glm::quat getJointRotation(int index) const;
@@ -141,7 +142,6 @@ public: · 3 unmodified lines
141142 virtual void setFaceModelURL(const QUrl& faceModelURL);
142143 virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
143144 virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
144− virtual void setDisplayName(const QString& displayName);
145145 virtual void setBillboard(const QByteArray& billboard);
146146
147147 void setShowDisplayName(bool showDisplayName);
@@ -232,10 +232,10 @@ protected: · 84 unmodified lines
232232 float getSkeletonHeight() const;
233233 float getHeadHeight() const;
234234 float getPelvisFloatingHeight() const;
235− glm::vec3 getDisplayNamePosition();
235+ glm::vec3 getDisplayNamePosition() const;
236236
237− float calculateDisplayNameScaleFactor(const glm::vec3& textPosition, bool inHMD);
238− void renderDisplayName(RenderArgs* renderArgs);
237+ Transform calculateDisplayNameTransform(const ViewFrustum& frustum, float fontSize) const;
238+ void renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum) const;
239239 virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, bool postLighting, float glowLevel = 0.0f);
240240 virtual bool shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const;
241241 virtual void fixupModelsInScene();
interface/src/avatar/AvatarActionHold.cpp+10−1
@@ -29,6 +29,12 @@ AvatarActionHold::~AvatarActionHold() { · 28 unmodified lines
2929
3030 void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
3131 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+
3238 glm::vec3 palmPosition;
3339 if (_hand == "right") {
3440 palmPosition = myAvatar->getRightPalmPosition();
@@ -40,8 +46,11 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { · 5 unmodified lines
4046 auto offset = rotation * _relativePosition;
4147 auto position = palmPosition + offset;
4248 rotation *= _relativeRotation;
49+ unlock();
4350
44− lockForWrite();
51+ if (!tryLockForWrite()) {
52+ return;
53+ }
4554 _positionalTarget = position;
4655 _rotationalTarget = rotation;
4756 unlock();
interface/src/avatar/AvatarManager.cpp+17−5
@@ -13,8 +13,18 @@ · 12 unmodified lines
1313
1414 #include <QScriptEngine>
1515
16+#ifdef __GNUC__
17+#pragma GCC diagnostic push
18+#pragma GCC diagnostic ignored "-Wdouble-promotion"
19+#endif
20+
1621 #include <glm/gtx/string_cast.hpp>
1722
23+#ifdef __GNUC__
24+#pragma GCC diagnostic pop
25+#endif
26+
27+
1828 #include <GlowEffect.h>
1929 #include <PerfStat.h>
2030 #include <RegisteredMetaTypes.h>
@@ -104,6 +114,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { · 83 unmodified lines
104114 // DO NOT update or fade out uninitialized Avatars
105115 ++avatarIterator;
106116 } else if (avatar->shouldDie()) {
117+ removeAvatarMotionState(avatar);
107118 _avatarFades.push_back(avatarIterator.value());
108119 avatarIterator = _avatarHash.erase(avatarIterator);
109120 } else {
@@ -153,12 +164,13 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe · 43 unmodified lines
153164 }
154165
155166 // protected
156−void AvatarManager::removeAvatarMotionState(Avatar* avatar) {
157− AvatarMotionState* motionState= avatar->_motionState;
167+void AvatarManager::removeAvatarMotionState(AvatarSharedPointer avatar) {
168+ auto rawPointer = std::static_pointer_cast<Avatar>(avatar);
169+ AvatarMotionState* motionState= rawPointer->_motionState;
158170 if (motionState) {
159171 // clean up physics stuff
160172 motionState->clearObjectBackPointer();
161− avatar->_motionState = nullptr;
173+ rawPointer->_motionState = nullptr;
162174 _avatarMotionStates.remove(motionState);
163175 _motionStatesToAdd.remove(motionState);
164176 _motionStatesToDelete.push_back(motionState);
@@ -171,7 +183,7 @@ void AvatarManager::removeAvatar(const QUuid& sessionUUID) { · 6 unmodified lines
171183 if (avatarIterator != _avatarHash.end()) {
172184 std::shared_ptr<Avatar> avatar = std::dynamic_pointer_cast<Avatar>(avatarIterator.value());
173185 if (avatar != _myAvatar && avatar->isInitialized()) {
174− removeAvatarMotionState(avatar.get());
186+ removeAvatarMotionState(avatar);
175187 _avatarFades.push_back(avatarIterator.value());
176188 _avatarHash.erase(avatarIterator);
177189 }
@@ -187,7 +199,7 @@ void AvatarManager::clearOtherAvatars() { · 9 unmodified lines
187199 // don't remove myAvatar or uninitialized avatars from the list
188200 ++avatarIterator;
189201 } else {
190− removeAvatarMotionState(avatar.get());
202+ removeAvatarMotionState(avatar);
191203 _avatarFades.push_back(avatarIterator.value());
192204 avatarIterator = _avatarHash.erase(avatarIterator);
193205 }
interface/src/avatar/AvatarManager.h+1−1
@@ -73,7 +73,7 @@ private: · 72 unmodified lines
7373 // virtual overrides
7474 virtual AvatarSharedPointer newSharedAvatar();
7575 virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer);
76− void removeAvatarMotionState(Avatar* avatar);
76+ void removeAvatarMotionState(AvatarSharedPointer avatar);
7777 virtual void removeAvatar(const QUuid& sessionUUID);
7878
7979 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
7777 const float AUDIO_LONG_TERM_AVERAGING_SECS = 30.0f;
7878 _averageLoudness = glm::mix(_averageLoudness, _audioLoudness, glm::min(deltaTime / AUDIO_AVERAGING_SECS, 1.0f));
7979
80− if (_longTermAverageLoudness == -1.0) {
80+ if (_longTermAverageLoudness == -1.0f) {
8181 _longTermAverageLoudness = _averageLoudness;
8282 } else {
8383 _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
124124
125125 if (!(_isFaceTrackerConnected || billboard)) {
126126 // Update eye saccades
127− const float AVERAGE_MICROSACCADE_INTERVAL = 0.50f;
128− const float AVERAGE_SACCADE_INTERVAL = 4.0f;
127+ const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f;
128+ const float AVERAGE_SACCADE_INTERVAL = 6.0f;
129129 const float MICROSACCADE_MAGNITUDE = 0.002f;
130130 const float SACCADE_MAGNITUDE = 0.04f;
131−
131+ const float MAXIMUM_SACCADE_SPEED = 0.8f;
132+
132133 if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) {
133134 _saccadeTarget = MICROSACCADE_MAGNITUDE * randVector();
134135 } else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) {
135136 _saccadeTarget = SACCADE_MAGNITUDE * randVector();
136137 }
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+
139146 // Detect transition from talking to not; force blink after that and a delay
140147 bool forceBlink = false;
141148 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
5959 const float MAX_BOOST_SPEED = 0.5f * MAX_WALKING_SPEED; // keyboard motor gets additive boost below this speed
6060 const float MIN_AVATAR_SPEED = 0.05f; // speed is set to zero below this
6161
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
6363 // to properly follow avatar size.
6464 float MAX_AVATAR_SPEED = 300.0f;
65−float MAX_KEYBOARD_MOTOR_SPEED = MAX_AVATAR_SPEED;
65+float MAX_KEYBOARD_MOTOR_SPEED = MAX_AVATAR_SPEED;
6666 float DEFAULT_KEYBOARD_MOTOR_TIMESCALE = 0.25f;
6767 float MIN_SCRIPTED_MOTOR_TIMESCALE = 0.005f;
6868 float DEFAULT_SCRIPTED_MOTOR_TIMESCALE = 1.0e6f;
@@ -146,7 +146,7 @@ void MyAvatar::update(float deltaTime) { · 77 unmodified lines
146146 if (_referential) {
147147 _referential->update();
148148 }
149−
149+
150150 Head* head = getHead();
151151 head->relaxLean(deltaTime);
152152 updateFromTrackers(deltaTime);
@@ -163,12 +163,12 @@ void MyAvatar::update(float deltaTime) { · 10 unmodified lines
163163
164164 void MyAvatar::simulate(float deltaTime) {
165165 PerformanceTimer perfTimer("simulate");
166−
166+
167167 // Play back recording
168168 if (_player && _player->isPlaying()) {
169169 _player->play();
170170 }
171−
171+
172172 if (_scale != _targetScale) {
173173 float scale = (1.0f - SMOOTHING_RATIO) * _scale + SMOOTHING_RATIO * _targetScale;
174174 setScale(scale);
@@ -179,7 +179,7 @@ void MyAvatar::simulate(float deltaTime) { · 4 unmodified lines
179179 updateOrientation(deltaTime);
180180 updatePosition(deltaTime);
181181 }
182−
182+
183183 {
184184 PerformanceTimer perfTimer("hand");
185185 // update avatar skeleton and simulate hand and head
@@ -222,12 +222,12 @@ void MyAvatar::simulate(float deltaTime) { · 36 unmodified lines
222222 head->setScale(_scale);
223223 head->simulate(deltaTime, true);
224224 }
225−
225+
226226 // Record avatars movements.
227227 if (_recorder && _recorder->isRecording()) {
228228 _recorder->record();
229229 }
230−
230+
231231 // consider updating our billboard
232232 maybeUpdateBillboard();
233233 }
@@ -235,18 +235,18 @@ void MyAvatar::simulate(float deltaTime) { · 1 unmodified line
235235 // Update avatar head rotation with sensor data
236236 void MyAvatar::updateFromTrackers(float deltaTime) {
237237 glm::vec3 estimatedPosition, estimatedRotation;
238−
238+
239239 bool inHmd = qApp->isHMDMode();
240−
240+
241241 if (isPlaying() && inHmd) {
242242 return;
243243 }
244244
245245 if (inHmd) {
246− estimatedPosition = qApp->getHeadPosition();
246+ estimatedPosition = qApp->getHeadPosition();
247247 estimatedPosition.x *= -1.0f;
248248 _trackedHeadPosition = estimatedPosition;
249−
249+
250250 const float OCULUS_LEAN_SCALE = 0.05f;
251251 estimatedPosition /= OCULUS_LEAN_SCALE;
252252 } else {
@@ -257,7 +257,7 @@ void MyAvatar::updateFromTrackers(float deltaTime) { · 4 unmodified lines
257257 estimatedRotation = glm::degrees(safeEulerAngles(tracker->getHeadRotation()));
258258 }
259259 }
260−
260+
261261 // Rotate the body if the head is turned beyond the screen
262262 if (Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)) {
263263 const float TRACKER_YAW_TURN_SENSITIVITY = 0.5f;
@@ -317,7 +317,7 @@ void MyAvatar::renderDebugBodyPoints() { · 53 unmodified lines
317317 glm::vec3 headPosition(getHead()->getEyePosition());
318318 float torsoToHead = glm::length(headPosition - torsoPosition);
319319 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());
321321
322322 // Torso Sphere
323323 position = torsoPosition;
@@ -433,7 +433,7 @@ void MyAvatar::startRecording() { · 109 unmodified lines
433433 auto audioClient = DependencyManager::get<AudioClient>();
434434 connect(audioClient.data(), &AudioClient::inputReceived, _recorder.data(),
435435 &Recorder::recordAudio, Qt::BlockingQueuedConnection);
436−
436+
437437 _recorder->startRecording();
438438 }
439439
@@ -449,7 +449,7 @@ void MyAvatar::stopRecording() { · 9 unmodified lines
449449 // stop grabbing audio from the AudioClient
450450 auto audioClient = DependencyManager::get<AudioClient>();
451451 disconnect(audioClient.data(), 0, _recorder.data(), 0);
452−
452+
453453 _recorder->stopRecording();
454454 }
455455 }
@@ -481,7 +481,7 @@ void MyAvatar::loadLastRecording() { · 25 unmodified lines
481481 if (!_player) {
482482 _player = PlayerPointer(new Player(this));
483483 }
484−
484+
485485 _player->loadRecording(_recorder->getRecording());
486486 }
487487
@@ -573,7 +573,7 @@ AnimationDetails MyAvatar::getAnimationDetailsByRole(const QString& role) { · 85 unmodified lines
573573 AnimationDetails result;
574574 if (QThread::currentThread() != thread()) {
575575 QMetaObject::invokeMethod(this, "getAnimationDetailsByRole", Qt::BlockingQueuedConnection,
576− Q_RETURN_ARG(AnimationDetails, result),
576+ Q_RETURN_ARG(AnimationDetails, result),
577577 Q_ARG(const QString&, role));
578578 return result;
579579 }
@@ -590,7 +590,7 @@ AnimationDetails MyAvatar::getAnimationDetails(const QString& url) { · 10 unmodified lines
590590 AnimationDetails result;
591591 if (QThread::currentThread() != thread()) {
592592 QMetaObject::invokeMethod(this, "getAnimationDetails", Qt::BlockingQueuedConnection,
593− Q_RETURN_ARG(AnimationDetails, result),
593+ Q_RETURN_ARG(AnimationDetails, result),
594594 Q_ARG(const QString&, url));
595595 return result;
596596 }
@@ -638,7 +638,7 @@ void MyAvatar::saveData() { · 41 unmodified lines
638638 settings.setValue("scale", attachment.scale);
639639 }
640640 settings.endArray();
641−
641+
642642 settings.beginWriteArray("animationHandles");
643643 for (int i = 0; i < _animationHandles.size(); i++) {
644644 settings.setArrayIndex(i);
@@ -655,7 +655,7 @@ void MyAvatar::saveData() { · 10 unmodified lines
655655 settings.setValue("maskedJoints", pointer->getMaskedJoints());
656656 }
657657 settings.endArray();
658−
658+
659659 settings.setValue("displayName", _displayName);
660660
661661 settings.endGroup();
@@ -681,7 +681,7 @@ void MyAvatar::loadData() { · 19 unmodified lines
681681 _targetScale = loadSetting(settings, "scale", 1.0f);
682682 setScale(_scale);
683683
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
685685 // for their avatar, So we need to attempt to detect this old case and set our new preferences accordingly. If
686686 // the head URL is empty, then we will assume they are using a full url...
687687 bool isOldSettings = !(settings.contains("useFullAvatar") || settings.contains("fullAvatarURL"));
@@ -693,7 +693,7 @@ void MyAvatar::loadData() { · 5 unmodified lines
693693 _headModelName = settings.value("headModelName", DEFAULT_HEAD_MODEL_NAME).toString();
694694 _bodyModelName = settings.value("bodyModelName", DEFAULT_BODY_MODEL_NAME).toString();
695695 _fullAvatarModelName = settings.value("fullAvatarModelName", DEFAULT_FULL_AVATAR_MODEL_NAME).toString();
696−
696+
697697 if (isOldSettings) {
698698 bool assumeFullAvatar = _headURLFromPreferences.isEmpty();
699699 _useFullAvatar = assumeFullAvatar;
@@ -719,14 +719,14 @@ void MyAvatar::loadData() { · 19 unmodified lines
719719 QVariantHash bodyFST = FSTReader::downloadMapping(_skeletonURLFromPreferences.toString());
720720 _bodyModelName = bodyFST["name"].toString();
721721 }
722−
722+
723723 if (_headURLFromPreferences == DEFAULT_HEAD_MODEL_URL) {
724724 _headModelName = DEFAULT_HEAD_MODEL_NAME;
725725 } else {
726726 QVariantHash headFST = FSTReader::downloadMapping(_headURLFromPreferences.toString());
727727 _headModelName = headFST["name"].toString();
728728 }
729−
729+
730730 _fullAvatarModelName = "Default";
731731 }
732732 }
@@ -736,7 +736,7 @@ void MyAvatar::loadData() { · 3 unmodified lines
736736 } else {
737737 useHeadAndBodyURLs(_headURLFromPreferences, _skeletonURLFromPreferences, _headModelName, _bodyModelName);
738738 }
739−
739+
740740 QVector<AttachmentData> attachmentData;
741741 int attachmentCount = settings.beginReadArray("attachmentData");
742742 for (int i = 0; i < attachmentCount; i++) {
@@ -757,7 +757,7 @@ void MyAvatar::loadData() { · 14 unmodified lines
757757 }
758758 settings.endArray();
759759 setAttachmentData(attachmentData);
760−
760+
761761 int animationCount = settings.beginReadArray("animationHandles");
762762 while (_animationHandles.size() > animationCount) {
763763 _animationHandles.takeLast()->stop();
@@ -780,7 +780,7 @@ void MyAvatar::loadData() { · 16 unmodified lines
780780 handle->setStartAutomatically(settings.value("startAutomatically", true).toBool());
781781 }
782782 settings.endArray();
783−
783+
784784 setDisplayName(settings.value("displayName").toString());
785785
786786 settings.endGroup();
@@ -792,7 +792,7 @@ void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const { · 5 unmodified lines
792792 settings.beginGroup(_skeletonModel.getURL().toString());
793793 settings.beginGroup(attachment.modelURL.toString());
794794 settings.setValue("jointName", attachment.jointName);
795−
795+
796796 settings.beginGroup(attachment.jointName);
797797 settings.setValue("translation_x", attachment.translation.x);
798798 settings.setValue("translation_y", attachment.translation.y);
@@ -802,7 +802,7 @@ void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const { · 3 unmodified lines
802802 settings.setValue("rotation_y", eulers.y);
803803 settings.setValue("rotation_z", eulers.z);
804804 settings.setValue("scale", attachment.scale);
805−
805+
806806 settings.endGroup();
807807 settings.endGroup();
808808 settings.endGroup();
@@ -814,7 +814,7 @@ AttachmentData MyAvatar::loadAttachmentData(const QUrl& modelURL, const QString& · 5 unmodified lines
814814 settings.beginGroup("savedAttachmentData");
815815 settings.beginGroup(_skeletonModel.getURL().toString());
816816 settings.beginGroup(modelURL.toString());
817−
817+
818818 AttachmentData attachment;
819819 attachment.modelURL = modelURL;
820820 if (jointName.isEmpty()) {
@@ -836,18 +836,18 @@ AttachmentData MyAvatar::loadAttachmentData(const QUrl& modelURL, const QString& · 15 unmodified lines
836836 } else {
837837 attachment = AttachmentData();
838838 }
839−
839+
840840 settings.endGroup();
841841 settings.endGroup();
842842 settings.endGroup();
843843 settings.endGroup();
844−
844+
845845 return attachment;
846846 }
847847
848848 int MyAvatar::parseDataAtOffset(const QByteArray& packet, int offset) {
849849 qCDebug(interfaceapp) << "Error: ignoring update packet for MyAvatar"
850− << " packetLength = " << packet.size()
850+ << " packetLength = " << packet.size()
851851 << " offset = " << offset;
852852 // this packet is just bad, so we pretend that we unpacked it ALL
853853 return packet.size() - offset;
@@ -865,14 +865,14 @@ void MyAvatar::updateLookAtTargetAvatar() { · 11 unmodified lines
865865 //
866866 _lookAtTargetAvatar.reset();
867867 _targetAvatarPosition = glm::vec3(0.0f);
868−
868+
869869 glm::vec3 lookForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FRONT;
870870 glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition();
871−
871+
872872 float smallestAngleTo = glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES) / 2.0f;
873873 const float KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR = 1.3f;
874874 const float GREATEST_LOOKING_AT_DISTANCE = 10.0f;
875−
875+
876876 int howManyLookingAtMe = 0;
877877 foreach (const AvatarSharedPointer& avatarPointer, DependencyManager::get<AvatarManager>()->getAvatarHash()) {
878878 Avatar* avatar = static_cast<Avatar*>(avatarPointer.get());
@@ -1004,13 +1004,13 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN · 125 unmodified lines
10041004 }
10051005
10061006 _useFullAvatar = true;
1007−
1007+
10081008 if (_fullAvatarURLFromPreferences != fullAvatarURL) {
10091009 _fullAvatarURLFromPreferences = fullAvatarURL;
10101010 if (modelName.isEmpty()) {
10111011 QVariantHash fullAvatarFST = FSTReader::downloadMapping(_fullAvatarURLFromPreferences.toString());
10121012 _fullAvatarModelName = fullAvatarFST["name"].toString();
1013− } else {
1013+ } else {
10141014 _fullAvatarModelName = modelName;
10151015 }
10161016 }
@@ -1051,7 +1051,7 @@ void MyAvatar::useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL, cons · 34 unmodified lines
10511051 if (headName.isEmpty()) {
10521052 QVariantHash headFST = FSTReader::downloadMapping(_headURLFromPreferences.toString());
10531053 _headModelName = headFST["name"].toString();
1054− } else {
1054+ } else {
10551055 _headModelName = headName;
10561056 }
10571057 }
@@ -1061,7 +1061,7 @@ void MyAvatar::useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL, cons · 3 unmodified lines
10611061 if (bodyName.isEmpty()) {
10621062 QVariantHash bodyFST = FSTReader::downloadMapping(_skeletonURLFromPreferences.toString());
10631063 _bodyModelName = bodyFST["name"].toString();
1064− } else {
1064+ } else {
10651065 _bodyModelName = bodyName;
10661066 }
10671067 }
@@ -1092,7 +1092,7 @@ void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) · 24 unmodified lines
10921092 glm::vec3 MyAvatar::getSkeletonPosition() const {
10931093 CameraMode mode = Application::getInstance()->getCamera()->getMode();
10941094 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
10961096 // to get the skeleton offset contribution in the world-frame.
10971097 const glm::quat FLIP = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
10981098 glm::vec3 skeletonOffset = _skeletonOffset;
@@ -1135,9 +1135,9 @@ void MyAvatar::setScriptedMotorVelocity(const glm::vec3& velocity) { · 36 unmodified lines
11351135 }
11361136
11371137 void MyAvatar::setScriptedMotorTimescale(float timescale) {
1138− // we clamp the timescale on the large side (instead of just the low side) to prevent
1138+ // we clamp the timescale on the large side (instead of just the low side) to prevent
11391139 // 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,
11411141 DEFAULT_SCRIPTED_MOTOR_TIMESCALE);
11421142 }
11431143
@@ -1155,14 +1155,14 @@ void MyAvatar::clearScriptableSettings() { · 11 unmodified lines
11551155 clearJointAnimationPriorities();
11561156 _scriptedMotorVelocity = glm::vec3(0.0f);
11571157 _scriptedMotorTimescale = DEFAULT_SCRIPTED_MOTOR_TIMESCALE;
1158−}
1158+}
11591159
11601160 void MyAvatar::attach(const QString& modelURL, const QString& jointName, const glm::vec3& translation,
11611161 const glm::quat& rotation, float scale, bool allowDuplicates, bool useSaved) {
1162− if (QThread::currentThread() != thread()) {
1162+ if (QThread::currentThread() != thread()) {
11631163 Avatar::attach(modelURL, jointName, translation, rotation, scale, allowDuplicates, useSaved);
11641164 return;
1165− }
1165+ }
11661166 if (useSaved) {
11671167 AttachmentData attachment = loadAttachmentData(modelURL, jointName);
11681168 if (attachment.isValid()) {
@@ -1199,7 +1199,7 @@ const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f; · 30 unmodified lines
11991199
12001200 bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& cameraPosition) const {
12011201 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) ||
12031203 (glm::length(cameraPosition - head->getEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE * _scale);
12041204 }
12051205
@@ -1230,10 +1230,10 @@ void MyAvatar::updateOrientation(float deltaTime) { · 24 unmodified lines
12301230
12311231 //Invert yaw and roll when in mirror mode
12321232 if (Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_MIRROR) {
1233− YAW(euler) *= -1.0;
1234− ROLL(euler) *= -1.0;
1233+ YAW(euler) *= -1.0f;
1234+ ROLL(euler) *= -1.0f;
12351235 }
1236−
1236+
12371237 Head* head = getHead();
12381238 head->setBaseYaw(YAW(euler));
12391239 head->setBasePitch(PITCH(euler));
@@ -1246,9 +1246,9 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe · 6 unmodified lines
12461246 return localVelocity;
12471247 }
12481248 // compute motor efficiency
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,
1251− // and a long timescale makes it weak. The value of timescale to use depends
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,
1251+ // and a long timescale makes it weak. The value of timescale to use depends
12521252 // on what the motor is doing:
12531253 //
12541254 // (1) braking --> short timescale (aggressive motor assertion)
@@ -1259,8 +1259,8 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe · 4 unmodified lines
12591259 float MIN_KEYBOARD_BRAKE_SPEED = 0.3f;
12601260 float timescale = MAX_KEYBOARD_MOTOR_TIMESCALE;
12611261 bool isThrust = (glm::length2(_thrust) > EPSILON);
1262− if (_isPushing || isThrust ||
1263− (_scriptedMotorTimescale < MAX_KEYBOARD_MOTOR_TIMESCALE &&
1262+ if (_isPushing || isThrust ||
1263+ (_scriptedMotorTimescale < MAX_KEYBOARD_MOTOR_TIMESCALE &&
12641264 _motionBehaviors | AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED)) {
12651265 // we don't want to brake if something is pushing the avatar around
12661266 timescale = _keyboardMotorTimescale;
@@ -1277,18 +1277,18 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe · 10 unmodified lines
12771277 float motorEfficiency = glm::clamp(deltaTime / timescale, 0.0f, 1.0f);
12781278
12791279 glm::vec3 newLocalVelocity = localVelocity;
1280− float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) +
1281− (fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT])) +
1280+ float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) +
1281+ (fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT])) +
12821282 fabsf(_driveKeys[UP] - _driveKeys[DOWN]);
12831283 if (keyboardInput) {
12841284 // Compute keyboard input
12851285 glm::vec3 front = (_driveKeys[FWD] - _driveKeys[BACK]) * IDENTITY_FRONT;
12861286 glm::vec3 right = (_driveKeys[RIGHT] - _driveKeys[LEFT]) * IDENTITY_RIGHT;
12871287 glm::vec3 up = (_driveKeys[UP] - _driveKeys[DOWN]) * IDENTITY_UP;
1288−
1288+
12891289 glm::vec3 direction = front + right + up;
12901290 float directionLength = glm::length(direction);
1291−
1291+
12921292 // Compute motor magnitude
12931293 if (directionLength > EPSILON) {
12941294 direction /= directionLength;
@@ -1318,7 +1318,7 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe · 23 unmodified lines
13181318 motorEfficiency = glm::clamp(deltaTime / WALK_ACCELERATION_TIMESCALE, 0.0f, 1.0f);
13191319 }
13201320 _isPushing = true;
1321− }
1321+ }
13221322 newLocalVelocity = localVelocity + motorEfficiency * (_keyboardMotorVelocity - localVelocity);
13231323 } else {
13241324 _keyboardMotorVelocity = glm::vec3(0.0f);
@@ -1382,13 +1382,13 @@ void MyAvatar::updatePosition(float deltaTime) { · 57 unmodified lines
13821382 _targetVelocity *= MAX_AVATAR_SPEED / speed;
13831383 speed = MAX_AVATAR_SPEED;
13841384 }
1385−
1385+
13861386 if (speed > MIN_AVATAR_SPEED && !_characterController.isEnabled()) {
13871387 // update position ourselves
13881388 applyPositionDelta(deltaTime * _targetVelocity);
13891389 measureMotionDerivatives(deltaTime);
13901390 } // else physics will move avatar later
1391−
1391+
13921392 // update _moving flag based on speed
13931393 const float MOVING_SPEED_THRESHOLD = 0.01f;
13941394 _moving = speed > MOVING_SPEED_THRESHOLD;
@@ -1405,7 +1405,7 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float · 10 unmodified lines
14051405 float xzDistance = sqrt(positionBA.x * positionBA.x + positionBA.z * positionBA.z);
14061406 if (xzDistance < (radiusA + radiusB)) {
14071407 float yDistance = fabs(positionBA.y);
1408− float halfHeights = 0.5 * (heightA + heightB);
1408+ float halfHeights = 0.5f * (heightA + heightB);
14091409 if (yDistance < halfHeights) {
14101410 // cylinders collide
14111411 if (xzDistance > 0.0f) {
@@ -1448,60 +1448,60 @@ void MyAvatar::maybeUpdateBillboard() { · 36 unmodified lines
14481448 }
14491449 gpu::Context context(new gpu::GLBackend());
14501450 RenderArgs renderArgs(&context);
1451− QImage image = Application::getInstance()->renderAvatarBillboard(&renderArgs);
1451+ QImage image = qApp->renderAvatarBillboard(&renderArgs);
14521452 _billboard.clear();
14531453 QBuffer buffer(&_billboard);
14541454 buffer.open(QIODevice::WriteOnly);
14551455 image.save(&buffer, "PNG");
14561456 _billboardValid = true;
1457−
1457+
14581458 sendBillboardPacket();
14591459 }
14601460
14611461 void MyAvatar::increaseSize() {
14621462 if ((1.0f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) {
14631463 _targetScale *= (1.0f + SCALING_RATIO);
1464− qCDebug(interfaceapp, "Changed scale to %f", _targetScale);
1464+ qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale);
14651465 }
14661466 }
14671467
14681468 void MyAvatar::decreaseSize() {
14691469 if (MIN_AVATAR_SCALE < (1.0f - SCALING_RATIO) * _targetScale) {
14701470 _targetScale *= (1.0f - SCALING_RATIO);
1471− qCDebug(interfaceapp, "Changed scale to %f", _targetScale);
1471+ qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale);
14721472 }
14731473 }
14741474
14751475 void MyAvatar::resetSize() {
14761476 _targetScale = 1.0f;
1477− qCDebug(interfaceapp, "Reseted scale to %f", _targetScale);
1477+ qCDebug(interfaceapp, "Reseted scale to %f", (double)_targetScale);
14781478 }
14791479
14801480 void MyAvatar::goToLocation(const glm::vec3& newPosition,
14811481 bool hasOrientation, const glm::quat& newOrientation,
14821482 bool shouldFaceLocation) {
1483−
1483+
14841484 qCDebug(interfaceapp).nospace() << "MyAvatar goToLocation - moving to " << newPosition.x << ", "
14851485 << newPosition.y << ", " << newPosition.z;
1486−
1486+
14871487 glm::vec3 shiftedPosition = newPosition;
1488−
1488+
14891489 if (hasOrientation) {
14901490 qCDebug(interfaceapp).nospace() << "MyAvatar goToLocation - new orientation is "
14911491 << newOrientation.x << ", " << newOrientation.y << ", " << newOrientation.z << ", " << newOrientation.w;
1492−
1492+
14931493 // orient the user to face the target
14941494 glm::quat quatOrientation = newOrientation;
1495−
1495+
14961496 if (shouldFaceLocation) {
1497−
1497+
14981498 quatOrientation = newOrientation * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
1499−
1499+
15001500 // move the user a couple units away
15011501 const float DISTANCE_TO_USER = 2.0f;
15021502 shiftedPosition = newPosition - quatOrientation * IDENTITY_FRONT * DISTANCE_TO_USER;
15031503 }
1504−
1504+
15051505 setOrientation(quatOrientation);
15061506 }
15071507
@@ -1547,7 +1547,6 @@ void MyAvatar::renderLaserPointers() { · 39 unmodified lines
15471547
15481548 //Gets the tip position for the laser pointer
15491549 glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) {
1550− const ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay();
15511550 glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition());
15521551
15531552 glm::vec3 position = palm->getPosition();
@@ -1556,7 +1555,8 @@ glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) { · 2 unmodified lines
15561555
15571556
15581557 glm::vec3 result;
1559− if (applicationOverlay.calculateRayUICollisionPoint(position, direction, result)) {
1558+ const auto& compositor = qApp->getApplicationCompositor();
1559+ if (compositor.calculateRayUICollisionPoint(position, direction, result)) {
15601560 return result;
15611561 }
15621562
@@ -1569,8 +1569,8 @@ void MyAvatar::clearDriveKeys() { · 6 unmodified lines
15691569 }
15701570 }
15711571
1572−void MyAvatar::relayDriveKeysToCharacterController() {
1572+void MyAvatar::relayDriveKeysToCharacterController() {
15731573 if (_driveKeys[UP] > 0.0f) {
1574− _characterController.jump();
1574+ _characterController.jump();
15751575 }
15761576 }
interface/src/avatar/MyAvatar.h+1−1
@@ -90,7 +90,7 @@ public: · 89 unmodified lines
9090
9191 void relayDriveKeysToCharacterController();
9292
93− bool isMyAvatar() { return true; }
93+ bool isMyAvatar() const { return true; }
9494
9595 bool isLookingAtLeftEye();
9696
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−
23−CameraToolBox::CameraToolBox() :
24− _iconPulseTimeReference(usecTimestampNow()),
25− _doubleClickTimer(NULL)
26−{
27−}
28−
29−CameraToolBox::~CameraToolBox() {
30− if (_doubleClickTimer) {
31− _doubleClickTimer->stop();
32− delete _doubleClickTimer;
33− }
34−}
35−
36−bool CameraToolBox::mousePressEvent(int x, int y) {
37− if (_iconBounds.contains(x, y)) {
38− if (!_doubleClickTimer) {
39− // Toggle mute after waiting to check that it's not a double-click.
40− const int DOUBLE_CLICK_WAIT = 200; // ms
41− _doubleClickTimer = new QTimer(this);
42− connect(_doubleClickTimer, SIGNAL(timeout()), this, SLOT(toggleMute()));
43− _doubleClickTimer->setSingleShot(true);
44− _doubleClickTimer->setInterval(DOUBLE_CLICK_WAIT);
45− _doubleClickTimer->start();
46− }
47− return true;
48− }
49− return false;
50−}
51−
52−bool CameraToolBox::mouseDoublePressEvent(int x, int y) {
53− if (_iconBounds.contains(x, y)) {
54− if (_doubleClickTimer) {
55− _doubleClickTimer->stop();
56− delete _doubleClickTimer;
57− _doubleClickTimer = NULL;
58− }
59− Application::getInstance()->resetSensors();
60− return true;
61− }
62− return false;
63−}
64−
65−void CameraToolBox::toggleMute() {
66− delete _doubleClickTimer;
67− _doubleClickTimer = NULL;
68−
69− FaceTracker* faceTracker = Application::getInstance()->getSelectedFaceTracker();
70− if (faceTracker) {
71− faceTracker->toggleMute();
72− }
73−}
74−
75−void CameraToolBox::render(int x, int y, bool boxed) {
76− glEnable(GL_TEXTURE_2D);
77−
78− if (!_enabledTexture) {
79− _enabledTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/face.svg");
80− }
81− if (!_mutedTexture) {
82− _mutedTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/face-mute.svg");
83− }
84−
85− const int MUTE_ICON_SIZE = 24;
86− _iconBounds = QRect(x, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE);
87− float iconColor = 1.0f;
88− if (!Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking)) {
89− glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_enabledTexture));
90− } else {
91− glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_mutedTexture));
92−
93− // Make muted icon pulsate
94− static const float PULSE_MIN = 0.4f;
95− static const float PULSE_MAX = 1.0f;
96− static const float PULSE_FREQUENCY = 1.0f; // in Hz
97− qint64 now = usecTimestampNow();
98− if (now - _iconPulseTimeReference > (qint64)USECS_PER_SECOND) {
99− // Prevents t from getting too big, which would diminish glm::cos precision
100− _iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND);
101− }
102− float t = (float)(now - _iconPulseTimeReference) / (float)USECS_PER_SECOND;
103− float pulseFactor = (glm::cos(t * PULSE_FREQUENCY * 2.0f * PI) + 1.0f) / 2.0f;
104− iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor;
105− }
106−
107− glm::vec4 quadColor(iconColor, iconColor, iconColor, 1.0f);
108−
109− glm::vec2 topLeft(_iconBounds.left(), _iconBounds.top());
110− glm::vec2 bottomRight(_iconBounds.right(), _iconBounds.bottom());
111− glm::vec2 texCoordTopLeft(1,1);
112− glm::vec2 texCoordBottomRight(0,0);
113−
114− if (_boxQuadID == GeometryCache::UNKNOWN_ID) {
115− _boxQuadID = DependencyManager::get<GeometryCache>()->allocateID();
116− }
117−
118− DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor, _boxQuadID);
119−
120− glDisable(GL_TEXTURE_2D);
121−}
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−
20−class CameraToolBox : public QObject, public Dependency {
21− Q_OBJECT
22− SINGLETON_DEPENDENCY
23−
24−public:
25− void render(int x, int y, bool boxed);
26− bool mousePressEvent(int x, int y);
27− bool mouseDoublePressEvent(int x, int y);
28−
29−protected:
30− CameraToolBox();
31− ~CameraToolBox();
32−
33−private slots:
34− void toggleMute();
35−
36−private:
37− gpu::TexturePointer _enabledTexture;
38− gpu::TexturePointer _mutedTexture;
39− int _boxQuadID = GeometryCache::UNKNOWN_ID;
40− QRect _iconBounds;
41− qint64 _iconPulseTimeReference = 0;
42− QTimer* _doubleClickTimer;
43−};
44−
45−#endif // hifi_CameraToolBox_h
interface/src/devices/DdeFaceTracker.cpp+6−5
@@ -415,7 +415,7 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { · 414 unmodified lines
415415 float browUp = _coefficients[_browUpCenterIndex];
416416 if (isFiltering) {
417417 const float BROW_VELOCITY_FILTER_STRENGTH = 0.5f;
418− float velocity = fabs(browUp - _lastBrowUp) / _averageMessageTime;
418+ float velocity = fabsf(browUp - _lastBrowUp) / _averageMessageTime;
419419 float velocityFilter = glm::clamp(velocity * BROW_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f);
420420 _filteredBrowUp = velocityFilter * browUp + (1.0f - velocityFilter) * _filteredBrowUp;
421421 _lastBrowUp = browUp;
@@ -438,11 +438,12 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { · 16 unmodified lines
438438
439439 // Velocity filter EyeBlink values
440440 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] };
442443 if (isFiltering) {
443444 const float BLINK_VELOCITY_FILTER_STRENGTH = 0.3f;
444445 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;
446447 float velocityFilter = glm::clamp(velocity * BLINK_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f);
447448 _filteredEyeBlinks[i] = velocityFilter * eyeBlinks[i] + (1.0f - velocityFilter) * _filteredEyeBlinks[i];
448449 _lastEyeBlinks[i] = eyeBlinks[i];
@@ -479,9 +480,9 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) { · 30 unmodified lines
479480 if (_eyeStates[i] == EYE_CLOSING) {
480481 // Close eyelid until it's fully closed
481482 float closingValue = _lastEyeCoefficients[i] + EYELID_MOVEMENT_RATE * _averageMessageTime;
482− if (closingValue >= 1.0) {
483+ if (closingValue >= 1.0f) {
483484 _eyeStates[i] = EYE_CLOSED;
484− eyeCoefficients[i] = 1.0;
485+ eyeCoefficients[i] = 1.0f;
485486 } else {
486487 eyeCoefficients[i] = closingValue;
487488 }
interface/src/devices/OculusManager.cpp+469−436
@@ -11,13 +11,13 @@ · 10 unmodified lines
1111 //
1212
1313 #include "InterfaceConfig.h"
14−
1514 #include "OculusManager.h"
1615 #include "ui/overlays/Text3DOverlay.h"
1716
17+#include <CursorManager.h>
1818 #include <QDesktopWidget>
1919 #include <QGuiApplication>
20−#include <QScreen>
20+#include <QScreen>
2121 #include <QOpenGLTimerQuery>
2222
2323 #include <glm/glm.hpp>
@@ -25,6 +25,9 @@ · 1 unmodified line
2525 #include <avatar/AvatarManager.h>
2626 #include <avatar/MyAvatar.h>
2727 #include <GlowEffect.h>
28+#include <GlWindow.h>
29+#include <gpu/GLBackend.h>
30+#include <OglplusHelpers.h>
2831 #include <PathUtils.h>
2932 #include <SharedUtil.h>
3033 #include <UserActivityLogger.h>
@@ -34,8 +37,6 @@ · 3 unmodified lines
3437 #include "InterfaceLogging.h"
3538 #include "Application.h"
3639
37−#include <gpu/GLBackend.h>
38−
3940 template <typename Function>
4041 void for_each_eye(Function function) {
4142 for (ovrEyeType eye = ovrEyeType::ovrEye_Left;
@@ -52,202 +53,413 @@ void for_each_eye(const ovrHmd & hmd, Function function) { · 10 unmodified lines
5253 function(eye);
5354 }
5455 }
56+enum CalibrationState {
57+ UNCALIBRATED,
58+ WAITING_FOR_DELTA,
59+ WAITING_FOR_ZERO,
60+ WAITING_FOR_ZERO_HELD,
61+ CALIBRATED
62+};
63+
64+inline glm::mat4 toGlm(const ovrMatrix4f & om) {
65+ return glm::transpose(glm::make_mat4(&om.M[0][0]));
66+}
5567
56−#ifdef OVR_CLIENT_DISTORTION
57−ProgramObject OculusManager::_program;
58−int OculusManager::_textureLocation;
59−int OculusManager::_eyeToSourceUVScaleLocation;
60−int OculusManager::_eyeToSourceUVOffsetLocation;
61−int OculusManager::_eyeRotationStartLocation;
62−int OculusManager::_eyeRotationEndLocation;
63−int OculusManager::_positionAttributeLocation;
64−int OculusManager::_colorAttributeLocation;
65−int OculusManager::_texCoord0AttributeLocation;
66−int OculusManager::_texCoord1AttributeLocation;
67−int OculusManager::_texCoord2AttributeLocation;
68−ovrVector2f OculusManager::_UVScaleOffset[ovrEye_Count][2];
69−GLuint OculusManager::_vertices[ovrEye_Count] = { 0, 0 };
70−GLuint OculusManager::_indices[ovrEye_Count] = { 0, 0 };
71−GLsizei OculusManager::_meshSize[ovrEye_Count] = { 0, 0 };
72−ovrFrameTiming OculusManager::_hmdFrameTiming;
73−bool OculusManager::_programInitialized = false;
74−#endif
68+inline glm::mat4 toGlm(const ovrFovPort & fovport, float nearPlane = 0.01f, float farPlane = 10000.0f) {
69+ return toGlm(ovrMatrix4f_Projection(fovport, nearPlane, farPlane, true));
70+}
7571
76−ovrTexture OculusManager::_eyeTextures[ovrEye_Count];
77−bool OculusManager::_isConnected = false;
78−ovrHmd OculusManager::_ovrHmd;
79−ovrFovPort OculusManager::_eyeFov[ovrEye_Count];
80−ovrVector3f OculusManager::_eyeOffset[ovrEye_Count];
81−ovrEyeRenderDesc OculusManager::_eyeRenderDesc[ovrEye_Count];
82−ovrSizei OculusManager::_renderTargetSize;
83−glm::mat4 OculusManager::_eyeProjection[ovrEye_Count];
84−unsigned int OculusManager::_frameIndex = 0;
85−bool OculusManager::_frameTimingActive = false;
86−Camera* OculusManager::_camera = NULL;
87−ovrEyeType OculusManager::_activeEye = ovrEye_Count;
88−bool OculusManager::_hswDismissed = false;
89−
90−float OculusManager::CALIBRATION_DELTA_MINIMUM_LENGTH = 0.02f;
91−float OculusManager::CALIBRATION_DELTA_MINIMUM_ANGLE = 5.0f * RADIANS_PER_DEGREE;
92−float OculusManager::CALIBRATION_ZERO_MAXIMUM_LENGTH = 0.01f;
93−float OculusManager::CALIBRATION_ZERO_MAXIMUM_ANGLE = 2.0f * RADIANS_PER_DEGREE;
94−quint64 OculusManager::CALIBRATION_ZERO_HOLD_TIME = 3000000; // usec
95−float OculusManager::CALIBRATION_MESSAGE_DISTANCE = 2.5f;
96−OculusManager::CalibrationState OculusManager::_calibrationState;
97−glm::vec3 OculusManager::_calibrationPosition;
98−glm::quat OculusManager::_calibrationOrientation;
99−quint64 OculusManager::_calibrationStartTime;
100−int OculusManager::_calibrationMessage = 0;
101−glm::vec3 OculusManager::_eyePositions[ovrEye_Count];
102−// TODO expose this as a developer toggle
103−bool OculusManager::_eyePerFrameMode = false;
104−ovrEyeType OculusManager::_lastEyeRendered = ovrEye_Count;
105−ovrSizei OculusManager::_recommendedTexSize = { 0, 0 };
106−float OculusManager::_offscreenRenderScale = 1.0;
72+inline glm::vec3 toGlm(const ovrVector3f & ov) {
73+ return glm::make_vec3(&ov.x);
74+}
10775
76+inline glm::vec2 toGlm(const ovrVector2f & ov) {
77+ return glm::make_vec2(&ov.x);
78+}
10879
109−void OculusManager::initSdk() {
110− ovr_Initialize();
111− _ovrHmd = ovrHmd_Create(0);
112− if (!_ovrHmd) {
113− _ovrHmd = ovrHmd_CreateDebug(ovrHmd_DK2);
114− }
80+inline glm::ivec2 toGlm(const ovrVector2i & ov) {
81+ return glm::ivec2(ov.x, ov.y);
11582 }
11683
117−void OculusManager::shutdownSdk() {
118− if (_ovrHmd) {
119− ovrHmd_Destroy(_ovrHmd);
120− _ovrHmd = nullptr;
121− ovr_Shutdown();
122− }
84+inline glm::uvec2 toGlm(const ovrSizei & ov) {
85+ return glm::uvec2(ov.w, ov.h);
12386 }
12487
125−void OculusManager::init() {
126−#ifdef OVR_DIRECT_MODE
127− initSdk();
128−#endif
88+inline glm::quat toGlm(const ovrQuatf & oq) {
89+ return glm::make_quat(&oq.x);
12990 }
13091
131−void OculusManager::deinit() {
132−#ifdef OVR_DIRECT_MODE
133− shutdownSdk();
134−#endif
92+inline glm::mat4 toGlm(const ovrPosef & op) {
93+ glm::mat4 orientation = glm::mat4_cast(toGlm(op.Orientation));
94+ glm::mat4 translation = glm::translate(glm::mat4(), toGlm(op.Position));
95+ return translation * orientation;
13596 }
13697
137−void OculusManager::connect() {
138−#ifndef OVR_DIRECT_MODE
139− initSdk();
140−#endif
141− _calibrationState = UNCALIBRATED;
142− qCDebug(interfaceapp) << "Oculus SDK" << OVR_VERSION_STRING;
143− if (_ovrHmd) {
144− if (!_isConnected) {
145− UserActivityLogger::getInstance().connectedDevice("hmd", "oculus");
98+inline ovrMatrix4f ovrFromGlm(const glm::mat4 & m) {
99+ ovrMatrix4f result;
100+ glm::mat4 transposed(glm::transpose(m));
101+ memcpy(result.M, &(transposed[0][0]), sizeof(float) * 16);
102+ return result;
103+}
104+
105+inline ovrVector3f ovrFromGlm(const glm::vec3 & v) {
106+ return{ v.x, v.y, v.z };
107+}
108+
109+inline ovrVector2f ovrFromGlm(const glm::vec2 & v) {
110+ return{ v.x, v.y };
111+}
112+
113+inline ovrSizei ovrFromGlm(const glm::uvec2 & v) {
114+ return{ (int)v.x, (int)v.y };
115+}
116+
117+inline ovrQuatf ovrFromGlm(const glm::quat & q) {
118+ return{ q.x, q.y, q.z, q.w };
119+}
120+
121+
122+
123+#ifdef Q_OS_WIN
124+
125+// A base class for FBO wrappers that need to use the Oculus C
126+// API to manage textures via ovrHmd_CreateSwapTextureSetGL,
127+// ovrHmd_CreateMirrorTextureGL, etc
128+template <typename C>
129+struct RiftFramebufferWrapper : public FramebufferWrapper<C, char> {
130+ ovrHmd hmd;
131+ RiftFramebufferWrapper(const ovrHmd & hmd) : hmd(hmd) {
132+ color = 0;
133+ depth = 0;
134+ };
135+
136+ void Resize(const uvec2 & size) {
137+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oglplus::GetName(fbo));
138+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
139+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
140+ this->size = size;
141+ initColor();
142+ initDone();
143+ }
144+
145+protected:
146+ virtual void initDepth() override final {
147+ }
148+};
149+
150+// A wrapper for constructing and using a swap texture set,
151+// where each frame you draw to a texture via the FBO,
152+// then submit it and increment to the next texture.
153+// The Oculus SDK manages the creation and destruction of
154+// the textures
155+struct SwapFramebufferWrapper : public RiftFramebufferWrapper<ovrSwapTextureSet*> {
156+ SwapFramebufferWrapper(const ovrHmd & hmd)
157+ : RiftFramebufferWrapper(hmd) {
158+ }
159+
160+ ~SwapFramebufferWrapper() {
161+ if (color) {
162+ ovrHmd_DestroySwapTextureSet(hmd, color);
163+ color = nullptr;
146164 }
147− _isConnected = true;
148−
149− for_each_eye([&](ovrEyeType eye) {
150− _eyeFov[eye] = _ovrHmd->DefaultEyeFov[eye];
151− });
152−
153− ovrGLConfig cfg;
154− memset(&cfg, 0, sizeof(cfg));
155− cfg.OGL.Header.API = ovrRenderAPI_OpenGL;
156− cfg.OGL.Header.BackBufferSize = _ovrHmd->Resolution;
157− cfg.OGL.Header.Multisample = 1;
158−
159− int distortionCaps = 0
160− | ovrDistortionCap_Vignette
161− | ovrDistortionCap_Overdrive
162− | ovrDistortionCap_TimeWarp;
163−
164− int configResult = ovrHmd_ConfigureRendering(_ovrHmd, &cfg.Config,
165− distortionCaps, _eyeFov, _eyeRenderDesc);
166− assert(configResult);
167−
168−
169− _recommendedTexSize = ovrHmd_GetFovTextureSize(_ovrHmd, ovrEye_Left, _eyeFov[ovrEye_Left], 1.0f);
170− _renderTargetSize = { _recommendedTexSize.w * 2, _recommendedTexSize.h };
171− for_each_eye([&](ovrEyeType eye) {
172− //Get texture size
173− _eyeTextures[eye].Header.API = ovrRenderAPI_OpenGL;
174− _eyeTextures[eye].Header.TextureSize = _renderTargetSize;
175− _eyeTextures[eye].Header.RenderViewport.Pos = { 0, 0 };
176− });
177− _eyeTextures[ovrEye_Right].Header.RenderViewport.Pos.x = _recommendedTexSize.w;
178−
179− ovrHmd_SetEnabledCaps(_ovrHmd, ovrHmdCap_LowPersistence | ovrHmdCap_DynamicPrediction);
180−
181− ovrHmd_ConfigureTracking(_ovrHmd, ovrTrackingCap_Orientation | ovrTrackingCap_Position |
182− ovrTrackingCap_MagYawCorrection,
183− ovrTrackingCap_Orientation);
184−
185− if (!_camera) {
186− _camera = new Camera;
187− configureCamera(*_camera); // no need to use screen dimensions; they're ignored
165+ }
166+
167+ void Increment() {
168+ ++color->CurrentIndex;
169+ color->CurrentIndex %= color->TextureCount;
170+ }
171+
172+protected:
173+ virtual void initColor() override {
174+ if (color) {
175+ ovrHmd_DestroySwapTextureSet(hmd, color);
176+ color = nullptr;
177+ }
178+
179+ ovrResult result = ovrHmd_CreateSwapTextureSetGL(hmd, GL_RGBA, size.x, size.y, &color);
180+ Q_ASSERT(OVR_SUCCESS(result));
181+
182+ for (int i = 0; i < color->TextureCount; ++i) {
183+ ovrGLTexture& ovrTex = (ovrGLTexture&)color->Textures[i];
184+ glBindTexture(GL_TEXTURE_2D, ovrTex.OGL.TexId);
185+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
186+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
187+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
188+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
189+ }
190+ glBindTexture(GL_TEXTURE_2D, 0);
191+ }
192+
193+ virtual void initDone() override {
194+ }
195+
196+ virtual void onBind(oglplus::Framebuffer::Target target) override {
197+ ovrGLTexture& tex = (ovrGLTexture&)(color->Textures[color->CurrentIndex]);
198+ glFramebufferTexture2D(toEnum(target), GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex.OGL.TexId, 0);
199+ }
200+
201+ virtual void onUnbind(oglplus::Framebuffer::Target target) override {
202+ glFramebufferTexture2D(toEnum(target), GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
203+ }
204+};
205+
206+
207+// We use a FBO to wrap the mirror texture because it makes it easier to
208+// render to the screen via glBlitFramebuffer
209+struct MirrorFramebufferWrapper : public RiftFramebufferWrapper<ovrGLTexture*> {
210+ MirrorFramebufferWrapper(const ovrHmd & hmd)
211+ : RiftFramebufferWrapper(hmd) {
212+ }
213+
214+ virtual ~MirrorFramebufferWrapper() {
215+ if (color) {
216+ ovrHmd_DestroyMirrorTexture(hmd, (ovrTexture*)color);
217+ color = nullptr;
188218 }
189−#ifdef OVR_CLIENT_DISTORTION
190− if (!_programInitialized) {
191− // Shader program
192− _programInitialized = true;
193− _program.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + "shaders/oculus.vert");
194− _program.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + "shaders/oculus.frag");
195− _program.link();
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");
219+ }
220+
221+private:
222+ void initColor() override {
223+ if (color) {
224+ ovrHmd_DestroyMirrorTexture(hmd, (ovrTexture*)color);
225+ color = nullptr;
210226 }
227+ ovrResult result = ovrHmd_CreateMirrorTextureGL(hmd, GL_RGBA, size.x, size.y, (ovrTexture**)&color);
228+ Q_ASSERT(OVR_SUCCESS(result));
229+ }
230+
231+ void initDone() override {
232+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oglplus::GetName(fbo));
233+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color->OGL.TexId, 0);
234+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
235+ }
236+};
237+
238+static SwapFramebufferWrapper* _swapFbo{ nullptr };
239+static MirrorFramebufferWrapper* _mirrorFbo{ nullptr };
240+static ovrLayerEyeFov _sceneLayer;
241+
242+#else
243+
244+static ovrTexture _eyeTextures[ovrEye_Count];
245+static GlWindow* _outputWindow{ nullptr };
211246
212− //Generate the distortion VBOs
213− generateDistortionMesh();
214247 #endif
215− } else {
248+
249+static bool _isConnected = false;
250+static ovrHmd _ovrHmd;
251+static ovrFovPort _eyeFov[ovrEye_Count];
252+static ovrVector3f _eyeOffset[ovrEye_Count];
253+static ovrEyeRenderDesc _eyeRenderDesc[ovrEye_Count];
254+static ovrSizei _renderTargetSize;
255+static glm::mat4 _eyeProjection[ovrEye_Count];
256+static unsigned int _frameIndex = 0;
257+static bool _frameTimingActive = false;
258+static Camera* _camera = NULL;
259+static ovrEyeType _activeEye = ovrEye_Count;
260+static bool _hswDismissed = false;
261+
262+static const float CALIBRATION_DELTA_MINIMUM_LENGTH = 0.02f;
263+static const float CALIBRATION_DELTA_MINIMUM_ANGLE = 5.0f * RADIANS_PER_DEGREE;
264+static const float CALIBRATION_ZERO_MAXIMUM_LENGTH = 0.01f;
265+static const float CALIBRATION_ZERO_MAXIMUM_ANGLE = 2.0f * RADIANS_PER_DEGREE;
266+static const quint64 CALIBRATION_ZERO_HOLD_TIME = 3000000; // usec
267+static const float CALIBRATION_MESSAGE_DISTANCE = 2.5f;
268+static CalibrationState _calibrationState;
269+static glm::vec3 _calibrationPosition;
270+static glm::quat _calibrationOrientation;
271+static quint64 _calibrationStartTime;
272+static int _calibrationMessage = 0;
273+static glm::vec3 _eyePositions[ovrEye_Count];
274+// TODO expose this as a developer toggle
275+static bool _eyePerFrameMode = false;
276+static ovrEyeType _lastEyeRendered = ovrEye_Count;
277+static ovrSizei _recommendedTexSize = { 0, 0 };
278+static float _offscreenRenderScale = 1.0;
279+static glm::mat4 _combinedProjection;
280+static ovrPosef _eyeRenderPoses[ovrEye_Count];
281+static ovrRecti _eyeViewports[ovrEye_Count];
282+static ovrVector3f _eyeOffsets[ovrEye_Count];
283+
284+glm::vec3 OculusManager::getLeftEyePosition() { return _eyePositions[ovrEye_Left]; }
285+glm::vec3 OculusManager::getRightEyePosition() { return _eyePositions[ovrEye_Right]; }
286+
287+void OculusManager::connect(QOpenGLContext* shareContext) {
288+ qCDebug(interfaceapp) << "Oculus SDK" << OVR_VERSION_STRING;
289+
290+ ovrInitParams initParams; memset(&initParams, 0, sizeof(initParams));
291+
292+#ifdef DEBUG
293+ initParams.Flags |= ovrInit_Debug;
294+#endif
295+
296+ ovr_Initialize(&initParams);
297+
298+#ifdef Q_OS_WIN
299+
300+ ovrResult res = ovrHmd_Create(0, &_ovrHmd);
301+#ifdef DEBUG
302+ if (!OVR_SUCCESS(res)) {
303+ res = ovrHmd_CreateDebug(ovrHmd_DK2, &_ovrHmd);
304+ Q_ASSERT(OVR_SUCCESS(res));
305+ }
306+#endif
307+
308+#else
309+
310+ _ovrHmd = ovrHmd_Create(0);
311+#ifdef DEBUG
312+ if (!_ovrHmd) {
313+ _ovrHmd = ovrHmd_CreateDebug(ovrHmd_DK2);
314+ }
315+#endif
316+
317+#endif
318+
319+ if (!_ovrHmd) {
216320 _isConnected = false;
217−
321+
218322 // we're definitely not in "VR mode" so tell the menu that
219323 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
220426 }
221427 }
222428
223429 //Disconnects and deallocates the OR
224430 void OculusManager::disconnect() {
225431 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+
226455 _isConnected = false;
227456 // Prepare to potentially have to dismiss the HSW again
228457 // if the user re-enables VR
229458 _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
247459 }
248460 }
249461
250−void OculusManager::positionCalibrationBillboard(Text3DOverlay* billboard) {
462+void positionCalibrationBillboard(Text3DOverlay* billboard) {
251463 MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
252464 glm::quat headOrientation = myAvatar->getHeadOrientation();
253465 headOrientation.x = 0;
@@ -258,7 +470,7 @@ void OculusManager::positionCalibrationBillboard(Text3DOverlay* billboard) { · 4 unmodified lines
258470 billboard->setRotation(headOrientation);
259471 }
260472
261−void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) {
473+void calibrate(const glm::vec3& position, const glm::quat& orientation) {
262474 static QString instructionMessage = "Hold still to calibrate";
263475 static QString progressMessage;
264476 static Text3DOverlay* billboard;
@@ -346,7 +558,7 @@ void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { · 81 unmodified lines
346558 break;
347559 default:
348560 break;
349−
561+
350562 }
351563 }
352564
@@ -363,64 +575,9 @@ void OculusManager::abandonCalibration() { · 10 unmodified lines
363575 }
364576 }
365577
366−#ifdef OVR_CLIENT_DISTORTION
367−void OculusManager::generateDistortionMesh() {
368−
369− //Check if we already have the distortion mesh
370− if (_vertices[0] != 0) {
371− printf("WARNING: Tried to generate Oculus distortion mesh twice without freeing the VBOs.");
372− return;
373− }
374−
375− for (int eyeNum = 0; eyeNum < ovrEye_Count; eyeNum++) {
376− // Allocate and generate distortion mesh vertices
377− ovrDistortionMesh meshData;
378− ovrHmd_CreateDistortionMesh(_ovrHmd, _eyeRenderDesc[eyeNum].Eye, _eyeRenderDesc[eyeNum].Fov, _ovrHmd->DistortionCaps, &meshData);
379−
380− // Parse the vertex data and create a render ready vertex buffer
381− DistortionVertex* pVBVerts = new DistortionVertex[meshData.VertexCount];
382− _meshSize[eyeNum] = meshData.IndexCount;
383−
384− // Convert the oculus vertex data to the DistortionVertex format.
385− DistortionVertex* v = pVBVerts;
386− ovrDistortionVertex* ov = meshData.pVertexData;
387− for (unsigned int vertNum = 0; vertNum < meshData.VertexCount; vertNum++) {
388− v->pos.x = ov->ScreenPosNDC.x;
389− v->pos.y = ov->ScreenPosNDC.y;
390− v->texR.x = ov->TanEyeAnglesR.x;
391− v->texR.y = ov->TanEyeAnglesR.y;
392− v->texG.x = ov->TanEyeAnglesG.x;
393− v->texG.y = ov->TanEyeAnglesG.y;
394− v->texB.x = ov->TanEyeAnglesB.x;
395− v->texB.y = ov->TanEyeAnglesB.y;
396− v->color.r = v->color.g = v->color.b = (GLubyte)(ov->VignetteFactor * 255.99f);
397− v->color.a = (GLubyte)(ov->TimeWarpFactor * 255.99f);
398− v++;
399− ov++;
400− }
401−
402− //vertices
403− glGenBuffers(1, &(_vertices[eyeNum]));
404− glBindBuffer(GL_ARRAY_BUFFER, _vertices[eyeNum]);
405− glBufferData(GL_ARRAY_BUFFER, sizeof(DistortionVertex) * meshData.VertexCount, pVBVerts, GL_STATIC_DRAW);
406− glBindBuffer(GL_ARRAY_BUFFER, 0);
407−
408− //indices
409− glGenBuffers(1, &(_indices[eyeNum]));
410− glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indices[eyeNum]);
411− glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short) * meshData.IndexCount, meshData.pIndexData, GL_STATIC_DRAW);
412− glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
413−
414− //Now that we have the VBOs we can get rid of the mesh data
415− delete [] pVBVerts;
416− ovrHmd_DestroyDistortionMesh(&meshData);
417− }
418−
419−}
420−#endif
421578
422579 bool OculusManager::isConnected() {
423− return _isConnected && Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode);
580+ return _isConnected;
424581 }
425582
426583 //Begins the frame timing for oculus prediction purposes
@@ -428,10 +585,6 @@ void OculusManager::beginFrameTiming() { · 1 unmodified line
428585 if (_frameTimingActive) {
429586 printf("WARNING: Called OculusManager::beginFrameTiming() twice in a row, need to call OculusManager::endFrameTiming().");
430587 }
431−
432−#ifdef OVR_CLIENT_DISTORTION
433− _hmdFrameTiming = ovrHmd_BeginFrameTiming(_ovrHmd, _frameIndex);
434−#endif
435588 _frameTimingActive = true;
436589 }
437590
@@ -441,27 +594,18 @@ bool OculusManager::allowSwap() { · 3 unmodified lines
441594
442595 //Ends frame timing
443596 void OculusManager::endFrameTiming() {
444−#ifdef OVR_CLIENT_DISTORTION
445− ovrHmd_EndFrameTiming(_ovrHmd);
446−#endif
447597 _frameIndex++;
448598 _frameTimingActive = false;
449599 }
450600
451601 //Sets the camera FoV and aspect ratio
452602 void OculusManager::configureCamera(Camera& camera) {
453− ovrFovPort fov;
454603 if (_activeEye == ovrEye_Count) {
455604 // When not rendering, provide a FOV encompasing both eyes
456− fov = _eyeFov[0];
457− fov.RightTan = _eyeFov[1].RightTan;
458− } else {
459− // When rendering, provide the exact FOV
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);
605+ camera.setProjection(_combinedProjection);
606+ return;
607+ }
608+ camera.setProjection(_eyeProjection[_activeEye]);
465609 }
466610
467611 //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
473617 assert(oldFrameIndex == -1 || (unsigned int)oldFrameIndex == _frameIndex - 1);
474618 oldFrameIndex = _frameIndex;
475619 #endif
476−
477− // Every so often do some additional timing calculations and debug output
478− bool debugFrame = 0 == _frameIndex % 400;
479620
480−#if 0
481− // Try to measure the amount of time taken to do the distortion
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
621+#ifndef Q_OS_WIN
622+
510623 // FIXME: we need a better way of responding to the HSW. In particular
511624 // we need to ensure that it's only displayed once per session, rather than
512625 // 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
522635 }
523636 }
524637 #endif
525−
638+
526639
527640 //beginFrameTiming must be called before display
528641 if (!_frameTimingActive) {
@@ -545,14 +658,14 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const · 16 unmodified lines
545658
546659 glMatrixMode(GL_MODELVIEW);
547660 glPushMatrix();
548−
661+
549662 glm::quat orientation;
550663 glm::vec3 trackerPosition;
551664 auto deviceSize = qApp->getDeviceSize();
552665
553666 ovrTrackingState ts = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds());
554667 ovrVector3f ovrHeadPosition = ts.HeadPose.ThePose.Position;
555−
668+
556669 trackerPosition = glm::vec3(ovrHeadPosition.x, ovrHeadPosition.y, ovrHeadPosition.z);
557670
558671 if (_calibrationState != CALIBRATED) {
@@ -562,11 +675,11 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const · 3 unmodified lines
562675 }
563676
564677 trackerPosition = bodyOrientation * trackerPosition;
565− static ovrVector3f eyeOffsets[2] = { { 0, 0, 0 }, { 0, 0, 0 } };
566678 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
568681 ovrHmd_BeginFrame(_ovrHmd, _frameIndex);
569− static ovrPosef eyeRenderPose[ovrEye_Count];
682+#endif
570683 //Render each eye into an fbo
571684 for_each_eye(_ovrHmd, [&](ovrEyeType eye){
572685 // 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
576689 return;
577690 }
578691 _lastEyeRendered = _activeEye = eye;
579− eyeRenderPose[eye] = eyePoses[eye];
692+ _eyeRenderPoses[eye] = eyePoses[eye];
580693 // 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−
590694
695+ vec3 eyePosition = toGlm(_eyeRenderPoses[eye].Position);
696+ eyePosition = whichCamera.getRotation() * eyePosition;
697+ quat eyeRotation = toGlm(_eyeRenderPoses[eye].Orientation);
698+
591699 // Update our camera to what the application camera is doing
592− _camera->setRotation(whichCamera.getRotation());
593− _camera->setPosition(whichCamera.getPosition());
700+ _camera->setRotation(whichCamera.getRotation() * eyeRotation);
701+ _camera->setPosition(whichCamera.getPosition() + eyePosition);
594702 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;
602703 _camera->update(1.0f / Application::getInstance()->getFps());
603704
604705 glMatrixMode(GL_PROJECTION);
@@ -607,15 +708,14 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const · 2 unmodified lines
607708 glMatrixMode(GL_MODELVIEW);
608709 glLoadIdentity();
609710
610− ovrRecti & vp = _eyeTextures[eye].Header.RenderViewport;
711+ ovrRecti & vp = _eyeViewports[eye];
611712 vp.Size.h = _recommendedTexSize.h * _offscreenRenderScale;
612713 vp.Size.w = _recommendedTexSize.w * _offscreenRenderScale;
613−
614714 glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h);
615715
616716 renderArgs->_renderSide = RenderArgs::MONO;
617− qApp->displaySide(renderArgs, *_camera, false);
618− qApp->getApplicationOverlay().displayOverlayTextureHmd(*_camera);
717+ qApp->displaySide(renderArgs, *_camera);
718+ qApp->getApplicationCompositor().displayOverlayTextureHmd(renderArgs, eye);
619719 });
620720 _activeEye = ovrEye_Count;
621721
@@ -628,7 +728,7 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const · 6 unmodified lines
628728 glViewport(0, 0, _renderTargetSize.w, _renderTargetSize.h);
629729 finalFbo = DependencyManager::get<GlowEffect>()->render(renderArgs);
630730 } else {
631− finalFbo = DependencyManager::get<TextureCache>()->getPrimaryFramebuffer();
731+ finalFbo = DependencyManager::get<TextureCache>()->getPrimaryFramebuffer();
632732 glBindFramebuffer(GL_FRAMEBUFFER, 0);
633733 }
634734
@@ -638,141 +738,73 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const · 3 unmodified lines
638738 // restore our normal viewport
639739 glViewport(0, 0, deviceSize.width(), deviceSize.height());
640740
641−#if 0
642− if (debugFrame && !timerActive) {
643− timerQuery.begin();
644− }
645−#endif
741+#ifdef Q_OS_WIN
742+ auto srcFboSize = finalFbo->getSize();
646743
647−#ifdef OVR_CLIENT_DISTORTION
648−
649− //Wait till time-warp to reduce latency
650− ovr_WaitTillTime(_hmdFrameTiming.TimewarpPointSeconds);
651744
652−#ifdef DEBUG_RENDER_WITHOUT_DISTORTION
653− auto fboSize = finalFbo->getSize();
745+ // Blit to the oculus provided texture
654746 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();
655756 glBlitFramebuffer(
656− 0, 0, fboSize.x, fboSize.y,
657− 0, 0, deviceSize.width(), deviceSize.height(),
757+ 0, 0, srcFboSize.x, srcFboSize.y,
758+ 0, 0, destWindowSize.width(), destWindowSize.height(),
658759 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();
659770 #else
660− //Clear the color buffer to ensure that there isnt any residual color
661− //Left over from when OR was not connected.
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();
771+ GLsync syncObject = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
772+ glFlush();
669773
670−#else
671774
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));
672783 for_each_eye([&](ovrEyeType eye) {
673784 ovrGLTexture & glEyeTexture = reinterpret_cast<ovrGLTexture&>(_eyeTextures[eye]);
674− glEyeTexture.OGL.TexId = finalFbo->texture();
675−
676− });
785+ glEyeTexture.OGL.TexId = textureId;
677786
678− ovrHmd_EndFrame(_ovrHmd, eyeRenderPose, _eyeTextures);
787+ });
679788
789+ // restore our normal viewport
790+ ovrHmd_EndFrame(_ovrHmd, _eyeRenderPoses, _eyeTextures);
791+ glCanvas->makeCurrent();
680792 #endif
681793
682−#if 0
683− if (debugFrame && !timerActive) {
684− timerQuery.end();
685− timerActive = true;
686− }
687−#endif
688794
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?)
690799 {
691− float latencies[5] = {};
692− if (debugFrame && ovrHmd_GetFloatArray(_ovrHmd, "DK2Latency", latencies, 5) == 5)
693− {
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− }
800+ QMouseEvent mouseEvent(QEvent::MouseMove, glCanvas->mapFromGlobal(QCursor::pos()),
801+ Qt::NoButton, Qt::NoButton, 0);
802+ qApp->mouseMoveEvent(&mouseEvent, 0);
706803 }
707−
708−}
709−
710−#ifdef OVR_CLIENT_DISTORTION
711−void OculusManager::renderDistortionMesh(ovrPosef eyeRenderPose[ovrEye_Count]) {
712804
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]));
747805
748− glUniformMatrix4fv(_eyeRotationStartLocation, 1, GL_FALSE, (GLfloat *)&transposeMatrices[0][0][0]);
749− glUniformMatrix4fv(_eyeRotationEndLocation, 1, GL_FALSE, (GLfloat *)&transposeMatrices[1][0][0]);
750806
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);
774807 }
775−#endif
776808
777809 //Tries to reconnect to the sensors
778810 void OculusManager::reset() {
@@ -781,21 +813,6 @@ void OculusManager::reset() { · 2 unmodified lines
781813 }
782814 }
783815
784−//Gets the current predicted angles from the oculus sensors
785−void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) {
786− ovrTrackingState ts = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds());
787− if (ts.StatusFlags & (ovrStatus_OrientationTracked | ovrStatus_PositionTracked)) {
788− glm::vec3 euler = glm::eulerAngles(toGlm(ts.HeadPose.ThePose.Orientation));
789− yaw = euler.y;
790− pitch = euler.x;
791− roll = euler.z;
792− } else {
793− yaw = 0.0f;
794− pitch = 0.0f;
795− roll = 0.0f;
796− }
797−}
798−
799816 glm::vec3 OculusManager::getRelativePosition() {
800817 ovrTrackingState trackingState = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds());
801818 return toGlm(trackingState.HeadPose.ThePose.Position);
@@ -826,6 +843,9 @@ void OculusManager::overrideOffAxisFrustum(float& left, float& right, float& bot · 24 unmodified lines
826843 }
827844
828845 int OculusManager::getHMDScreen() {
846+#ifdef Q_OS_WIN
847+ return -1;
848+#else
829849 int hmdScreenIndex = -1; // unknown
830850 // TODO: it might be smarter to handle multiple HMDs connected in this case. but for now,
831851 // 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
844864 const int SIMILAR_NAMES = 10;
845865 const int EXACT_LOCATION_MATCH = 50;
846866 const int EXACT_RESOLUTION_MATCH = 25;
847−
867+
848868 int bestMatchScore = 0;
849869
850870 // look at the display list and see if we can find the best match
@@ -853,7 +873,7 @@ int OculusManager::getHMDScreen() { · 2 unmodified lines
853873 foreach (QScreen* screen, QGuiApplication::screens()) {
854874 QString screenName = screen->name();
855875 QRect screenRect = desktop->screenGeometry(screenNumber);
856−
876+
857877 int screenScore = 0;
858878 if (screenName == productNameFromOVR) {
859879 screenScore += EXACT_NAME_MATCH;
@@ -876,5 +896,18 @@ int OculusManager::getHMDScreen() { · 16 unmodified lines
876896 }
877897 }
878898 return hmdScreenIndex;
899+#endif
900+}
901+
902+mat4 OculusManager::getEyeProjection(int eye) {
903+ return _eyeProjection[eye];
879904 }
880905
906+mat4 OculusManager::getEyePose(int eye) {
907+ return toGlm(_eyeRenderPoses[eye]);
908+}
909+
910+mat4 OculusManager::getHeadPose() {
911+ ovrTrackingState ts = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds());
912+ return toGlm(ts.HeadPose.ThePose);
913+}
interface/src/devices/OculusManager.h+7−170
@@ -13,8 +13,6 @@ · 12 unmodified lines
1313 #ifndef hifi_OculusManager_h
1414 #define hifi_OculusManager_h
1515
16−#include <OVR_CAPI.h>
17−
1816 #include <ProgramObject.h>
1917 #include <glm/glm.hpp>
2018 #include <glm/gtc/matrix_transform.hpp>
@@ -22,39 +20,13 @@ · 1 unmodified line
2220
2321 #include "RenderArgs.h"
2422
23+class QOpenGLContext;
2524 class Camera;
26−class PalmData;
27−class Text3DOverlay;
28−
29−// Uncomment this to enable client side distortion. NOT recommended since
30−// the Oculus SDK will ideally provide the best practices for distortion in
31−// in terms of performance and quality, and by using it we will get updated
32−// best practices for free with new runtime releases.
33−#define OVR_CLIENT_DISTORTION 1
34−
35−
36−// Direct HMD mode is currently only supported on windows and some linux systems will
37−// misbehave if we try to enable the Oculus SDK at all, so isolate support for Direct
38−// mode only to windows for now
39−#ifdef Q_OS_WIN
40−// On Win32 platforms, enabling Direct HMD requires that the SDK be
41−// initialized before the GL context is set up, but this breaks v-sync
42−// for any application that has a Direct mode enable Rift connected
43−// but is not rendering to it. For the time being I'm setting this as
44−// a macro enabled mechanism which changes where the SDK is initialized.
45−// To enable Direct HMD mode, you can un-comment this, but with the
46−// caveat that it will break v-sync in NON-VR mode if you have an Oculus
47−// Rift connect and in Direct mode
48−#define OVR_DIRECT_MODE 1
49−#endif
50−
5125
5226 /// Handles interaction with the Oculus Rift.
5327 class OculusManager {
5428 public:
55− static void init();
56− static void deinit();
57− static void connect();
29+ static void connect(QOpenGLContext* shareContext);
5830 static void disconnect();
5931 static bool isConnected();
6032 static void recalibrate();
@@ -66,10 +38,6 @@ public: · 5 unmodified lines
6638 static void display(QGLWidget * glCanvas, RenderArgs* renderArgs, const glm::quat &bodyOrientation, const glm::vec3 &position, Camera& whichCamera);
6739 static void reset();
6840
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);
7341 static glm::vec3 getRelativePosition();
7442 static glm::quat getOrientation();
7543 static QSize getRenderTargetSize();
@@ -77,145 +45,14 @@ public: · 1 unmodified line
7745 static void overrideOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
7846 float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane);
7947
80− static glm::vec3 getLeftEyePosition() { return _eyePositions[ovrEye_Left]; }
81− static glm::vec3 getRightEyePosition() { return _eyePositions[ovrEye_Right]; }
48+ static glm::vec3 getLeftEyePosition();
49+ static glm::vec3 getRightEyePosition();
8250
8351 static int getHMDScreen();
84−
85−private:
86− static void initSdk();
87− static void shutdownSdk();
88−#ifdef OVR_CLIENT_DISTORTION
89− static void generateDistortionMesh();
90− static void renderDistortionMesh(ovrPosef eyeRenderPose[ovrEye_Count]);
91− struct DistortionVertex {
92− glm::vec2 pos;
93− glm::vec2 texR;
94− glm::vec2 texG;
95− glm::vec2 texB;
96− struct {
97− GLubyte r;
98− GLubyte g;
99− GLubyte b;
100− GLubyte a;
101− } color;
102− };
10352
104− static ProgramObject _program;
105− //Uniforms
106− static int _textureLocation;
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;
53+ static glm::mat4 getEyeProjection(int eye);
54+ static glm::mat4 getEyePose(int eye);
55+ static glm::mat4 getHeadPose();
16556 };
16657
167−
168−inline glm::mat4 toGlm(const ovrMatrix4f & om) {
169− return glm::transpose(glm::make_mat4(&om.M[0][0]));
170−}
171−
172−inline glm::mat4 toGlm(const ovrFovPort & fovport, float nearPlane = 0.01f, float farPlane = 10000.0f) {
173− return toGlm(ovrMatrix4f_Projection(fovport, nearPlane, farPlane, true));
174−}
175−
176−inline glm::vec3 toGlm(const ovrVector3f & ov) {
177− return glm::make_vec3(&ov.x);
178−}
179−
180−inline glm::vec2 toGlm(const ovrVector2f & ov) {
181− return glm::make_vec2(&ov.x);
182−}
183−
184−inline glm::uvec2 toGlm(const ovrSizei & ov) {
185− return glm::uvec2(ov.w, ov.h);
186−}
187−
188−inline glm::quat toGlm(const ovrQuatf & oq) {
189− return glm::make_quat(&oq.x);
190−}
191−
192−inline glm::mat4 toGlm(const ovrPosef & op) {
193− glm::mat4 orientation = glm::mat4_cast(toGlm(op.Orientation));
194− glm::mat4 translation = glm::translate(glm::mat4(), toGlm(op.Position));
195− return translation * orientation;
196−}
197−
198−inline ovrMatrix4f ovrFromGlm(const glm::mat4 & m) {
199− ovrMatrix4f result;
200− glm::mat4 transposed(glm::transpose(m));
201− memcpy(result.M, &(transposed[0][0]), sizeof(float) * 16);
202− return result;
203−}
204−
205−inline ovrVector3f ovrFromGlm(const glm::vec3 & v) {
206− return{ v.x, v.y, v.z };
207−}
208−
209−inline ovrVector2f ovrFromGlm(const glm::vec2 & v) {
210− return{ v.x, v.y };
211−}
212−
213−inline ovrSizei ovrFromGlm(const glm::uvec2 & v) {
214− return{ (int)v.x, (int)v.y };
215−}
216−
217−inline ovrQuatf ovrFromGlm(const glm::quat & q) {
218− return{ q.x, q.y, q.z, q.w };
219−}
220−
22158 #endif // hifi_OculusManager_h
interface/src/devices/SDL2Manager.cpp+2−0
@@ -74,9 +74,11 @@ SDL2Manager* SDL2Manager::getInstance() { · 73 unmodified lines
7474 }
7575
7676 void SDL2Manager::focusOutEvent() {
77+#ifdef HAVE_SDL2
7778 for (auto joystick : _openJoysticks) {
7879 joystick->focusOutEvent();
7980 }
81+#endif
8082 }
8183
8284 void SDL2Manager::update() {
interface/src/devices/SixenseManager.cpp+58−50
@@ -36,6 +36,8 @@ const float NECK_X = 0.25f; // meters · 35 unmodified lines
3636 const float NECK_Y = 0.3f; // meters
3737 const float NECK_Z = 0.3f; // meters
3838
39+const float CONTROLLER_THRESHOLD = 0.35f;
40+
3941 #ifdef __APPLE__
4042 typedef int (*SixenseBaseFunction)();
4143 typedef int (*SixenseTakeIntFunction)(int);
@@ -71,25 +73,25 @@ SixenseManager::SixenseManager() : · 29 unmodified lines
7173
7274 SixenseManager::~SixenseManager() {
7375 #ifdef HAVE_SIXENSE_
74−
76+
7577 if (_isInitialized) {
7678 #ifdef __APPLE__
7779 SixenseBaseFunction sixenseExit = (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseExit");
7880 #endif
79−
81+
8082 sixenseExit();
8183 }
82−
84+
8385 #ifdef __APPLE__
8486 delete _sixenseLibrary;
8587 #endif
86−
88+
8789 #endif
8890 }
8991
9092 void SixenseManager::initialize() {
9193 #ifdef HAVE_SIXENSE
92−
94+
9395 if (!_isInitialized) {
9496 _lowVelocityFilter = false;
9597 _controllersAtBase = true;
@@ -97,22 +99,22 @@ void SixenseManager::initialize() { · 1 unmodified line
9799 // By default we assume the _neckBase (in orb frame) is as high above the orb
98100 // as the "torso" is below it.
99101 _neckBase = glm::vec3(NECK_X, -NECK_Y, NECK_Z);
100−
102+
101103 #ifdef __APPLE__
102−
104+
103105 if (!_sixenseLibrary) {
104−
106+
105107 #ifdef SIXENSE_LIB_FILENAME
106108 _sixenseLibrary = new QLibrary(SIXENSE_LIB_FILENAME);
107109 #else
108110 const QString SIXENSE_LIBRARY_NAME = "libsixense_x64";
109111 QString frameworkSixenseLibrary = QCoreApplication::applicationDirPath() + "/../Frameworks/"
110112 + SIXENSE_LIBRARY_NAME;
111−
113+
112114 _sixenseLibrary = new QLibrary(frameworkSixenseLibrary);
113115 #endif
114116 }
115−
117+
116118 if (_sixenseLibrary->load()){
117119 qCDebug(interfaceapp) << "Loaded sixense library for hydra support -" << _sixenseLibrary->fileName();
118120 } else {
@@ -120,14 +122,14 @@ void SixenseManager::initialize() { · 1 unmodified line
120122 << "Continuing without hydra support.";
121123 return;
122124 }
123−
125+
124126 SixenseBaseFunction sixenseInit = (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseInit");
125127 #endif
126128 sixenseInit();
127−
129+
128130 _isInitialized = true;
129131 }
130−
132+
131133 #endif
132134 }
133135
@@ -138,7 +140,7 @@ void SixenseManager::setFilter(bool filter) { · 4 unmodified lines
138140 #ifdef __APPLE__
139141 SixenseTakeIntFunction sixenseSetFilterEnabled = (SixenseTakeIntFunction) _sixenseLibrary->resolve("sixenseSetFilterEnabled");
140142 #endif
141−
143+
142144 if (filter) {
143145 sixenseSetFilterEnabled(1);
144146 } else {
@@ -158,7 +160,7 @@ void SixenseManager::update(float deltaTime) { · 13 unmodified lines
158160 SixenseBaseFunction sixenseGetNumActiveControllers =
159161 (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers");
160162 #endif
161−
163+
162164 if (sixenseGetNumActiveControllers() == 0) {
163165 _hydrasConnected = false;
164166 if (_deviceID != 0) {
@@ -173,7 +175,7 @@ void SixenseManager::update(float deltaTime) { · 8 unmodified lines
173175 }
174176 return;
175177 }
176−
178+
177179 PerformanceTimer perfTimer("sixense");
178180 if (!_hydrasConnected) {
179181 _hydrasConnected = true;
@@ -181,21 +183,21 @@ void SixenseManager::update(float deltaTime) { · 1 unmodified line
181183 getInstance().assignDefaultInputMapping(*Application::getUserInputMapper());
182184 UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra");
183185 }
184−
186+
185187 #ifdef __APPLE__
186188 SixenseBaseFunction sixenseGetMaxControllers =
187189 (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetMaxControllers");
188190 #endif
189−
191+
190192 int maxControllers = sixenseGetMaxControllers();
191−
193+
192194 // we only support two controllers
193195 sixenseControllerData controllers[2];
194−
196+
195197 #ifdef __APPLE__
196198 SixenseTakeIntFunction sixenseIsControllerEnabled =
197199 (SixenseTakeIntFunction) _sixenseLibrary->resolve("sixenseIsControllerEnabled");
198−
200+
199201 SixenseTakeIntAndSixenseControllerData sixenseGetNewestData =
200202 (SixenseTakeIntAndSixenseControllerData) _sixenseLibrary->resolve("sixenseGetNewestData");
201203 #endif
@@ -208,9 +210,9 @@ void SixenseManager::update(float deltaTime) { · 6 unmodified lines
208210 sixenseControllerData* data = controllers + numActiveControllers;
209211 ++numActiveControllers;
210212 sixenseGetNewestData(i, data);
211−
213+
212214 // Set palm position and normal based on Hydra position/orientation
213−
215+
214216 // Either find a palm matching the sixense controller, or make a new one
215217 PalmData* palm;
216218 bool foundHand = false;
@@ -229,47 +231,47 @@ void SixenseManager::update(float deltaTime) { · 12 unmodified lines
229231 _prevPalms[numActiveControllers - 1] = palm;
230232 qCDebug(interfaceapp, "Found new Sixense controller, ID %i", data->controller_index);
231233 }
232−
234+
233235 // Disable the hands (and return to default pose) if both controllers are at base station
234236 if (foundHand) {
235237 palm->setActive(!_controllersAtBase);
236238 } else {
237239 palm->setActive(false); // if this isn't a Sixsense ID palm, always make it inactive
238240 }
239−
241+
240242 // Read controller buttons and joystick into the hand
241243 palm->setControllerButtons(data->buttons);
242244 palm->setTrigger(data->trigger);
243245 palm->setJoystick(data->joystick_x, data->joystick_y);
244−
246+
245247 handleButtonEvent(data->buttons, numActiveControllers - 1);
246248 handleAxisEvent(data->joystick_x, data->joystick_y, data->trigger, numActiveControllers - 1);
247−
249+
248250 // Emulate the mouse so we can use scripts
249251 if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput) && !_controllersAtBase) {
250252 emulateMouse(palm, numActiveControllers - 1);
251253 }
252−
254+
253255 // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters.
254256 glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]);
255257 position *= METERS_PER_MILLIMETER;
256−
258+
257259 // Check to see if this hand/controller is on the base
258260 const float CONTROLLER_AT_BASE_DISTANCE = 0.075f;
259261 if (glm::length(position) < CONTROLLER_AT_BASE_DISTANCE) {
260262 numControllersAtBase++;
261263 }
262−
264+
263265 // Transform the measured position into body frame.
264266 glm::vec3 neck = _neckBase;
265267 // Zeroing y component of the "neck" effectively raises the measured position a little bit.
266268 neck.y = 0.0f;
267269 position = _orbRotation * (position - neck);
268−
270+
269271 // Rotation of Palm
270272 glm::quat rotation(data->rot_quat[3], -data->rot_quat[0], data->rot_quat[1], -data->rot_quat[2]);
271273 rotation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)) * _orbRotation * rotation;
272−
274+
273275 // Compute current velocity from position change
274276 glm::vec3 rawVelocity;
275277 if (deltaTime > 0.0f) {
@@ -278,11 +280,11 @@ void SixenseManager::update(float deltaTime) { · 2 unmodified lines
278280 rawVelocity = glm::vec3(0.0f);
279281 }
280282 palm->setRawVelocity(rawVelocity); // meters/sec
281−
283+
282284 // adjustment for hydra controllers fit into hands
283285 float sign = (i == 0) ? -1.0f : 1.0f;
284286 rotation *= glm::angleAxis(sign * PI/4.0f, glm::vec3(0.0f, 0.0f, 1.0f));
285−
287+
286288 // Angular Velocity of Palm
287289 glm::quat deltaRotation = rotation * glm::inverse(palm->getRawRotation());
288290 glm::vec3 angularVelocity(0.0f);
@@ -294,7 +296,7 @@ void SixenseManager::update(float deltaTime) { · 5 unmodified lines
294296 } else {
295297 palm->setRawAngularVelocity(glm::vec3(0.0f));
296298 }
297−
299+
298300 if (_lowVelocityFilter) {
299301 // Use a velocity sensitive filter to damp small motions and preserve large ones with
300302 // no latency.
@@ -307,7 +309,7 @@ void SixenseManager::update(float deltaTime) { · 6 unmodified lines
307309 palm->setRawPosition(position);
308310 palm->setRawRotation(rotation);
309311 }
310−
312+
311313 // Store the one fingertip in the palm structure so we can track velocity
312314 const float FINGER_LENGTH = 0.3f; // meters
313315 const glm::vec3 FINGER_VECTOR(0.0f, 0.0f, FINGER_LENGTH);
@@ -320,19 +322,25 @@ void SixenseManager::update(float deltaTime) { · 6 unmodified lines
320322 }
321323 palm->setTipPosition(newTipPosition);
322324 }
323−
325+
324326 if (numActiveControllers == 2) {
325327 updateCalibration(controllers);
326328 }
327329 _controllersAtBase = (numControllersAtBase == 2);
328330 }
331+
332+ for (auto axisState : _axisStateMap) {
333+ if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) {
334+ _axisStateMap[axisState.first] = 0.0f;
335+ }
336+ }
329337 #endif // HAVE_SIXENSE
330338 }
331339
332340 //Constants for getCursorPixelRangeMultiplier()
333341 const float MIN_PIXEL_RANGE_MULT = 0.4f;
334342 const float MAX_PIXEL_RANGE_MULT = 2.0f;
335−const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01;
343+const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01f;
336344
337345 //Returns a multiplier to be applied to the cursor range for the controllers
338346 float SixenseManager::getCursorPixelRangeMult() const {
@@ -421,7 +429,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) · 82 unmodified lines
421429 _calibrationState = CALIBRATION_STATE_X;
422430 }
423431 return;
424− }
432+ }
425433
426434 quint64 now = usecTimestampNow() + LOCK_DURATION;
427435 // these are weighted running averages
@@ -431,7 +439,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) · 3 unmodified lines
431439 if (_calibrationState == CALIBRATION_STATE_X) {
432440 // compute new sliding average
433441 float distance = glm::distance(_averageLeft, _averageRight);
434− if (fabs(distance - _lastDistance) > MAXIMUM_NOISE_LEVEL) {
442+ if (fabsf(distance - _lastDistance) > MAXIMUM_NOISE_LEVEL) {
435443 // distance is increasing so acquire the data and push the expiry out
436444 _reachLeft = _averageLeft;
437445 _reachRight = _averageRight;
@@ -450,7 +458,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) · 12 unmodified lines
450458 glm::vec3 torso = 0.5f * (_reachLeft + _reachRight);
451459 glm::vec3 averagePosition = 0.5f * (_averageLeft + _averageRight);
452460 float distance = (averagePosition - torso).y;
453− if (fabs(distance) > fabs(_lastDistance) + MAXIMUM_NOISE_LEVEL) {
461+ if (fabsf(distance) > fabsf(_lastDistance) + MAXIMUM_NOISE_LEVEL) {
454462 // distance is increasing so acquire the data and push the expiry out
455463 _reachUp = averagePosition;
456464 _lastDistance = distance;
@@ -481,11 +489,11 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) · 24 unmodified lines
481489 _lastDistance = distance;
482490 _lockExpiry = now + LOCK_DURATION;
483491 } else if (now > _lockExpiry) {
484− if (fabs(_lastDistance) > 0.05f * MINIMUM_ARM_REACH) {
492+ if (fabsf(_lastDistance) > 0.05f * MINIMUM_ARM_REACH) {
485493 // lock has expired so clamp the data and move on
486494 _calibrationState = CALIBRATION_STATE_COMPLETE;
487495 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
489497 // accidentally switched (left hand is holding right controller) and to swap the order.
490498 }
491499 }
@@ -496,7 +504,7 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) · 4 unmodified lines
496504 void SixenseManager::emulateMouse(PalmData* palm, int index) {
497505 MyAvatar* avatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
498506 QPoint pos;
499−
507+
500508 Qt::MouseButton bumperButton;
501509 Qt::MouseButton triggerButton;
502510
@@ -510,16 +518,16 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { · 7 unmodified lines
510518 triggerButton = Qt::LeftButton;
511519 }
512520
513− if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)
521+ if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)
514522 || Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) {
515− pos = qApp->getApplicationOverlay().getPalmClickLocation(palm);
523+ pos = qApp->getApplicationCompositor().getPalmClickLocation(palm);
516524 } else {
517525 // Get directon relative to avatar orientation
518526 glm::vec3 direction = glm::inverse(avatar->getOrientation()) * palm->getFingerDirection();
519527
520528 // Get the angles, scaled between (-0.5,0.5)
521529 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));
523531 auto canvasSize = qApp->getCanvasSize();
524532 // Get the pixel range over which the xAngle and yAngle are scaled
525533 float cursorRange = canvasSize.x * getCursorPixelRangeMult();
@@ -562,11 +570,11 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { · 36 unmodified lines
562570 if (!_bumperPressed[(int)(!index)]) {
563571 qApp->mouseMoveEvent(&mouseEvent, deviceID);
564572 }
565− }
573+ }
566574 }
567575 _oldX[index] = pos.x();
568576 _oldY[index] = pos.y();
569−
577+
570578
571579 //We need separate coordinates for clicks, since we need to check if
572580 //a magnification window was clicked on
@@ -580,7 +588,7 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { · 7 unmodified lines
580588 if (palm->getControllerButtons() & BUTTON_FWD) {
581589 if (!_bumperPressed[index]) {
582590 _bumperPressed[index] = true;
583−
591+
584592 QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, bumperButton, bumperButton, 0);
585593
586594 qApp->mousePressEvent(&mouseEvent, deviceID);
interface/src/devices/TV3DManager.cpp+9−9
@@ -12,6 +12,7 @@ · 11 unmodified lines
1212 #include "InterfaceConfig.h"
1313
1414 #include <glm/glm.hpp>
15+#include <glm/gtc/type_ptr.hpp>
1516
1617 #include <GlowEffect.h>
1718 #include "gpu/GLBackend.h"
@@ -106,21 +107,20 @@ void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) { · 88 unmodified lines
106107 _activeEye = &eye;
107108 glViewport(portalX, portalY, portalW, portalH);
108109 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+
109116 glMatrixMode(GL_PROJECTION);
110117 glLoadIdentity(); // reset projection matrix
111− glFrustum(eye.left, eye.right, eye.bottom, eye.top, nearZ, farZ); // set left view frustum
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−
118+ glLoadMatrixf(glm::value_ptr(projection));
119119 glMatrixMode(GL_MODELVIEW);
120120 glLoadIdentity();
121121 renderArgs->_renderSide = RenderArgs::MONO;
122122 qApp->displaySide(renderArgs, eyeCamera, false);
123− qApp->getApplicationOverlay().displayOverlayTextureStereo(whichCamera, _aspect, fov);
123+ qApp->getApplicationCompositor().displayOverlayTexture(renderArgs);
124124 _activeEye = NULL;
125125 }, [&]{
126126 // render right side view
interface/src/main.cpp+1−1
@@ -23,7 +23,7 @@ · 22 unmodified lines
2323 #ifdef Q_OS_WIN
2424 static BOOL CALLBACK enumWindowsCallback(HWND hWnd, LPARAM lParam) {
2525 const UINT TIMEOUT = 200; // ms
26− DWORD response;
26+ DWORD_PTR response;
2727 LRESULT result = SendMessageTimeout(hWnd, UWM_IDENTIFY_INSTANCES, 0, 0, SMTO_BLOCK | SMTO_ABORTIFHUNG, TIMEOUT, &response);
2828 if (result == 0) { // Timeout; continue search.
2929 return TRUE;
interface/src/scripting/ControllerScriptingInterface.cpp+1−1
@@ -463,7 +463,7 @@ void ControllerScriptingInterface::resetDevice(unsigned int device) { · 462 unmodified lines
463463 }
464464
465465 int ControllerScriptingInterface::findDevice(QString name) {
466− Application::getUserInputMapper()->findDevice(name);
466+ return Application::getUserInputMapper()->findDevice(name);
467467 }
468468
469469 InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) :
interface/src/scripting/ControllerScriptingInterface.h+1−0
@@ -65,6 +65,7 @@ public: · 64 unmodified lines
6565
6666 void emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseMoveEvent(MouseEvent(*event, deviceID)); }
6767 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)); }
6869 void emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseReleaseEvent(MouseEvent(*event, deviceID)); }
6970
7071 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
2525
2626 glm::vec3 direction = orientation * glm::vec3(0.0f, 0.0f, -1.0f);
2727
28− ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay();
28+ const auto& compositor = Application::getInstance()->getApplicationCompositor();
2929
30− return applicationOverlay.calculateRayUICollisionPoint(position, direction, result);
30+ return compositor.calculateRayUICollisionPoint(position, direction, result);
3131 }
3232
3333 QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) {
@@ -40,7 +40,7 @@ QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* conte · 6 unmodified lines
4040 glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * (hudIntersection - sphereCenter);
4141 glm::quat rotation = ::rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), direction);
4242 glm::vec3 eulers = ::safeEulerAngles(rotation);
43− return qScriptValueFromValue<glm::vec2>(engine, Application::getInstance()->getApplicationOverlay()
43+ return qScriptValueFromValue<glm::vec2>(engine, Application::getInstance()->getApplicationCompositor()
4444 .sphericalToOverlay(glm::vec2(eulers.y, -eulers.x)));
4545 }
4646 return QScriptValue::NullValue;
interface/src/scripting/HMDScriptingInterface.h+2−2
@@ -27,11 +27,11 @@ public: · 26 unmodified lines
2727 static QScriptValue getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine);
2828
2929 public slots:
30− void toggleMagnifier() { Application::getInstance()->getApplicationOverlay().toggleMagnifier(); };
30+ void toggleMagnifier() { Application::getInstance()->getApplicationCompositor().toggleMagnifier(); };
3131
3232 private:
3333 HMDScriptingInterface() {};
34− bool getMagnifier() const { return Application::getInstance()->getApplicationOverlay().hasMagnifier(); };
34+ bool getMagnifier() const { return Application::getInstance()->getApplicationCompositor().hasMagnifier(); };
3535 bool isHMDMode() const { return Application::getInstance()->isHMDMode(); }
3636
3737 bool getHUDLookAtPosition3D(glm::vec3& result) const;
interface/src/starfield/renderer/Renderer.cpp+9−0
@@ -11,6 +11,7 @@ · 10 unmodified lines
1111 //
1212
1313 #include "starfield/renderer/Renderer.h"
14+#include "Application.h"
1415
1516 using namespace starfield;
1617
@@ -52,6 +53,10 @@ void Renderer::render(float perspective, float aspect, mat4 const& orientation, · 35 unmodified lines
5253 matrix[3][1] = 0.0f;
5354 matrix[3][2] = 0.0f;
5455
56+ glMatrixMode(GL_PROJECTION);
57+ glPushMatrix();
58+ glLoadMatrixf(glm::value_ptr(qApp->getDisplayViewFrustum()->getProjection()));
59+ glMatrixMode(GL_MODELVIEW);
5560 // extract local z vector
5661 vec3 ahead = vec3(matrix[2]);
5762
@@ -74,6 +79,10 @@ void Renderer::render(float perspective, float aspect, mat4 const& orientation, · 16 unmodified lines
7479 floodFill(cursor, TileSelection(*this, _tileArray, _tileArray + _tiling.getTileCount(), (TileSelection::Cursor*) _batchCountArray));
7580
7681 this->glBatch(glm::value_ptr(matrix), prepareBatch((unsigned*) _batchOffs, _outIndexPos), alpha);
82+ glMatrixMode(GL_PROJECTION);
83+ glPopMatrix();
84+ glMatrixMode(GL_MODELVIEW);
85+
7786 }
7887
7988 // renderer construction
interface/src/ui/AddressBarDialog.cpp+24−0
@@ -22,6 +22,20 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare · 21 unmodified lines
2222 connect(addressManager.data(), &AddressManager::lookupResultIsOffline, this, &AddressBarDialog::displayAddressOfflineMessage);
2323 connect(addressManager.data(), &AddressManager::lookupResultIsNotFound, this, &AddressBarDialog::displayAddressNotFoundMessage);
2424 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());
2539 }
2640
2741 void AddressBarDialog::hide() {
@@ -35,6 +49,16 @@ void AddressBarDialog::loadAddress(const QString& address) { · 7 unmodified lines
3549 }
3650 }
3751
52+void AddressBarDialog::loadBack() {
53+ qDebug() << "Called LoadBack";
54+ DependencyManager::get<AddressManager>()->goBack();
55+}
56+
57+void AddressBarDialog::loadForward() {
58+ qDebug() << "Called LoadForward";
59+ DependencyManager::get<AddressManager>()->goForward();
60+}
61+
3862 void AddressBarDialog::displayAddressOfflineMessage() {
3963 OffscreenUi::error("That user or place is currently offline");
4064 }
interface/src/ui/AddressBarDialog.h+13−0
@@ -18,9 +18,17 @@ class AddressBarDialog : public OffscreenQmlDialog · 17 unmodified lines
1818 {
1919 Q_OBJECT
2020 HIFI_QML_DECL
21+ Q_PROPERTY(bool backEnabled READ backEnabled NOTIFY backEnabledChanged)
22+ Q_PROPERTY(bool forwardEnabled READ forwardEnabled NOTIFY forwardEnabledChanged)
2123
2224 public:
2325 AddressBarDialog(QQuickItem* parent = nullptr);
26+ bool backEnabled() { return _backEnabled; }
27+ bool forwardEnabled() { return _forwardEnabled; }
28+
29+signals:
30+ void backEnabledChanged();
31+ void forwardEnabledChanged();
2432
2533 protected:
2634 void displayAddressOfflineMessage();
@@ -28,6 +36,11 @@ protected: · 1 unmodified line
2836 void hide();
2937
3038 Q_INVOKABLE void loadAddress(const QString& address);
39+ Q_INVOKABLE void loadBack();
40+ Q_INVOKABLE void loadForward();
41+
42+ bool _backEnabled;
43+ bool _forwardEnabled;
3144 };
3245
3346 #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
27+static const float MAG_SPEED = 0.08f;
28+
29+static const quint64 MSECS_TO_USECS = 1000ULL;
30+static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS;
31+
32+static const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };
33+static const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f };
34+static const float reticleSize = TWO_PI / 100.0f;
35+
36+static const float CONNECTION_STATUS_BORDER_COLOR[] = { 1.0f, 0.0f, 0.0f };
37+static const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f;
38+
39+static const float CURSOR_PIXEL_SIZE = 32.0f;
40+static const float MOUSE_PITCH_RANGE = 1.0f * PI;
41+static const float MOUSE_YAW_RANGE = 0.5f * TWO_PI;
42+static const glm::vec2 MOUSE_RANGE(MOUSE_YAW_RANGE, MOUSE_PITCH_RANGE);
43+
44+static gpu::BufferPointer _hemiVertices;
45+static gpu::BufferPointer _hemiIndices;
46+static int _hemiIndexCount{ 0 };
47+EntityItemID ApplicationCompositor::_noItemId;
48+static QString _tooltipId;
49+
50+// Return a point's cartesian coordinates on a sphere from pitch and yaw
51+glm::vec3 getPoint(float yaw, float pitch) {
52+ return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)),
53+ glm::sin(-pitch),
54+ glm::cos(-pitch) * (-glm::cos(yaw)));
55+}
56+
57+//Checks if the given ray intersects the sphere at the origin. result will store a multiplier that should
58+//be multiplied by dir and added to origin to get the location of the collision
59+bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r, float* result)
60+{
61+ //Source: http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection
62+
63+ //Compute A, B and C coefficients
64+ float a = glm::dot(dir, dir);
65+ float b = 2 * glm::dot(dir, origin);
66+ float c = glm::dot(origin, origin) - (r * r);
67+
68+ //Find discriminant
69+ float disc = b * b - 4 * a * c;
70+
71+ // if discriminant is negative there are no real roots, so return
72+ // false as ray misses sphere
73+ if (disc < 0) {
74+ return false;
75+ }
76+
77+ // compute q as described above
78+ float distSqrt = sqrtf(disc);
79+ float q;
80+ if (b < 0) {
81+ q = (-b - distSqrt) / 2.0f;
82+ } else {
83+ q = (-b + distSqrt) / 2.0f;
84+ }
85+
86+ // compute t0 and t1
87+ float t0 = q / a;
88+ float t1 = c / q;
89+
90+ // make sure t0 is smaller than t1
91+ if (t0 > t1) {
92+ // if t0 is bigger than t1 swap them around
93+ float temp = t0;
94+ t0 = t1;
95+ t1 = temp;
96+ }
97+
98+ // if t1 is less than zero, the object is in the ray's negative direction
99+ // and consequently the ray misses the sphere
100+ if (t1 < 0) {
101+ return false;
102+ }
103+
104+ // if t0 is less than zero, the intersection point is at t1
105+ if (t0 < 0) {
106+ *result = t1;
107+ return true;
108+ } else { // else the intersection point is at t0
109+ *result = t0;
110+ return true;
111+ }
112+}
113+
114+ApplicationCompositor::ApplicationCompositor() {
115+ memset(_reticleActive, 0, sizeof(_reticleActive));
116+ memset(_magActive, 0, sizeof(_reticleActive));
117+ memset(_magSizeMult, 0, sizeof(_magSizeMult));
118+
119+ auto geometryCache = DependencyManager::get<GeometryCache>();
120+
121+ _reticleQuad = geometryCache->allocateID();
122+ _magnifierQuad = geometryCache->allocateID();
123+ _magnifierBorder = geometryCache->allocateID();
124+
125+ auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
126+ connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) {
127+ if (_hoverItemId != entityItemID) {
128+ _hoverItemId = entityItemID;
129+ _hoverItemEnterUsecs = usecTimestampNow();
130+ auto properties = entityScriptingInterface->getEntityProperties(_hoverItemId);
131+ _hoverItemHref = properties.getHref();
132+ auto cursor = Cursor::Manager::instance().getCursor();
133+ if (!_hoverItemHref.isEmpty()) {
134+ cursor->setIcon(Cursor::Icon::LINK);
135+ } else {
136+ cursor->setIcon(Cursor::Icon::DEFAULT);
137+ }
138+ }
139+ });
140+
141+ connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) {
142+ if (_hoverItemId == entityItemID) {
143+ _hoverItemId = _noItemId;
144+ _hoverItemHref.clear();
145+ auto cursor = Cursor::Manager::instance().getCursor();
146+ cursor->setIcon(Cursor::Icon::DEFAULT);
147+ if (!_tooltipId.isEmpty()) {
148+ qDebug() << "Closing tooltip " << _tooltipId;
149+ Tooltip::closeTip(_tooltipId);
150+ _tooltipId.clear();
151+ }
152+ }
153+ });
154+}
155+
156+ApplicationCompositor::~ApplicationCompositor() {
157+}
158+
159+
160+void ApplicationCompositor::bindCursorTexture(gpu::Batch& batch, uint8_t cursorIndex) {
161+ auto& cursorManager = Cursor::Manager::instance();
162+ auto cursor = cursorManager.getCursor(cursorIndex);
163+ auto iconId = cursor->getIcon();
164+ if (!_cursors.count(iconId)) {
165+ auto iconPath = cursorManager.getIconImage(cursor->getIcon());
166+ _cursors[iconId] = DependencyManager::get<TextureCache>()->
167+ getImageTexture(iconPath);
168+ }
169+ batch.setUniformTexture(0, _cursors[iconId]);
170+}
171+
172+// Draws the FBO texture for the screen
173+void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {
174+ if (_alpha == 0.0f) {
175+ return;
176+ }
177+
178+ GLuint texture = qApp->getApplicationOverlay().getOverlayTexture();
179+ if (!texture) {
180+ return;
181+ }
182+
183+ updateTooltips();
184+
185+ vec2 canvasSize = qApp->getCanvasSize();
186+
187+ //Handle fading and deactivation/activation of UI
188+ gpu::Batch batch;
189+
190+ renderArgs->_context->syncCache();
191+ auto geometryCache = DependencyManager::get<GeometryCache>();
192+
193+ geometryCache->useSimpleDrawPipeline(batch);
194+ batch.setModelTransform(Transform());
195+ batch.setViewTransform(Transform());
196+ batch.setProjectionTransform(mat4());
197+ batch._glBindTexture(GL_TEXTURE_2D, texture);
198+ batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
199+ batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
200+ geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha));
201+
202+ // Doesn't actually render
203+ renderPointers(batch);
204+
205+ //draw the mouse pointer
206+ // Get the mouse coordinates and convert to NDC [-1, 1]
207+ vec2 mousePosition = toNormalizedDeviceScale(vec2(qApp->getMouse()), canvasSize);
208+ // Invert the Y axis
209+ mousePosition.y *= -1.0f;
210+
211+ Transform model;
212+ model.setTranslation(vec3(mousePosition, 0));
213+ vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize;
214+ model.setScale(vec3(mouseSize, 1.0f));
215+ batch.setModelTransform(model);
216+ bindCursorTexture(batch);
217+ vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f };
218+ geometryCache->renderUnitQuad(batch, vec4(1));
219+ renderArgs->_context->render(batch);
220+}
221+
222+
223+vec2 getPolarCoordinates(const PalmData& palm) {
224+ MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
225+ auto avatarOrientation = myAvatar->getOrientation();
226+ auto eyePos = myAvatar->getDefaultEyePosition();
227+ glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm);
228+ // Direction of the tip relative to the eye
229+ glm::vec3 tipDirection = tip - eyePos;
230+ // orient into avatar space
231+ tipDirection = glm::inverse(avatarOrientation) * tipDirection;
232+ // Normalize for trig functions
233+ tipDirection = glm::normalize(tipDirection);
234+ // Convert to polar coordinates
235+ glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y));
236+ return polar;
237+}
238+
239+// Draws the FBO texture for Oculus rift.
240+void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int eye) {
241+ if (_alpha == 0.0f) {
242+ return;
243+ }
244+
245+ GLuint texture = qApp->getApplicationOverlay().getOverlayTexture();
246+ if (!texture) {
247+ return;
248+ }
249+
250+ updateTooltips();
251+
252+ vec2 canvasSize = qApp->getCanvasSize();
253+ _textureAspectRatio = aspect(canvasSize);
254+
255+ renderArgs->_context->syncCache();
256+ auto geometryCache = DependencyManager::get<GeometryCache>();
257+
258+ gpu::Batch batch;
259+ geometryCache->useSimpleDrawPipeline(batch);
260+ batch._glDisable(GL_DEPTH_TEST);
261+ batch._glDisable(GL_CULL_FACE);
262+ batch._glBindTexture(GL_TEXTURE_2D, texture);
263+ batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
264+ batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
265+ batch.setViewTransform(Transform());
266+ batch.setProjectionTransform(qApp->getEyeProjection(eye));
267+
268+ mat4 eyePose = qApp->getEyePose(eye);
269+ glm::mat4 overlayXfm = glm::inverse(eyePose);
270+
271+#ifdef DEBUG_OVERLAY
272+ {
273+ batch.setModelTransform(glm::translate(mat4(), vec3(0, 0, -2)));
274+ geometryCache->renderUnitQuad(batch, glm::vec4(1));
275+ }
276+#else
277+ {
278+ batch.setModelTransform(overlayXfm);
279+ drawSphereSection(batch);
280+ }
281+#endif
282+
283+ // Doesn't actually render
284+ renderPointers(batch);
285+ vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize);
286+
287+ bindCursorTexture(batch);
288+
289+ MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
290+ //Controller Pointers
291+ for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) {
292+ PalmData& palm = myAvatar->getHand()->getPalms()[i];
293+ if (palm.isActive()) {
294+ glm::vec2 polar = getPolarCoordinates(palm);
295+ // Convert to quaternion
296+ mat4 pointerXfm = glm::mat4_cast(quat(vec3(polar.y, -polar.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
297+ mat4 reticleXfm = overlayXfm * pointerXfm;
298+ reticleXfm = glm::scale(reticleXfm, reticleScale);
299+ batch.setModelTransform(reticleXfm);
300+ // Render reticle at location
301+ geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
302+ }
303+ }
304+
305+ //Mouse Pointer
306+ if (_reticleActive[MOUSE]) {
307+ glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(),
308+ _reticlePosition[MOUSE].y()));
309+ mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
310+ mat4 reticleXfm = overlayXfm * pointerXfm;
311+ reticleXfm = glm::scale(reticleXfm, reticleScale);
312+ batch.setModelTransform(reticleXfm);
313+ geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
314+ }
315+
316+ renderArgs->_context->render(batch);
317+}
318+
319+
320+void ApplicationCompositor::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const {
321+ cursorPos *= qApp->getCanvasSize();
322+ const glm::vec2 projection = screenToSpherical(cursorPos);
323+ // The overlay space orientation of the mouse coordinates
324+ const glm::quat orientation(glm::vec3(-projection.y, projection.x, 0.0f));
325+ // FIXME We now have the direction of the ray FROM THE DEFAULT HEAD POSE.
326+ // Now we need to account for the actual camera position relative to the overlay
327+ glm::vec3 overlaySpaceDirection = glm::normalize(orientation * IDENTITY_FRONT);
328+
329+
330+ // We need the RAW camera orientation and position, because this is what the overlay is
331+ // rendered relative to
332+ const glm::vec3 overlayPosition = qApp->getCamera()->getPosition();
333+ const glm::quat overlayOrientation = qApp->getCamera()->getRotation();
334+
335+ // Intersection UI overlay space
336+ glm::vec3 worldSpaceDirection = overlayOrientation * overlaySpaceDirection;
337+ glm::vec3 worldSpaceIntersection = (glm::normalize(worldSpaceDirection) * _oculusUIRadius) + overlayPosition;
338+ glm::vec3 worldSpaceHeadPosition = (overlayOrientation * glm::vec3(qApp->getHeadPose()[3])) + overlayPosition;
339+
340+ // Intersection in world space
341+ origin = worldSpaceHeadPosition;
342+ direction = glm::normalize(worldSpaceIntersection - worldSpaceHeadPosition);
343+}
344+
345+//Caculate the click location using one of the sixense controllers. Scale is not applied
346+QPoint ApplicationCompositor::getPalmClickLocation(const PalmData *palm) const {
347+ QPoint rv;
348+ auto canvasSize = qApp->getCanvasSize();
349+ if (qApp->isHMDMode()) {
350+ glm::vec2 polar = getPolarCoordinates(*palm);
351+ glm::vec2 point = sphericalToScreen(-polar);
352+ rv.rx() = point.x;
353+ rv.ry() = point.y;
354+ } else {
355+ MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
356+ glm::dmat4 projection;
357+ qApp->getProjectionMatrix(&projection);
358+ glm::quat invOrientation = glm::inverse(myAvatar->getOrientation());
359+ glm::vec3 eyePos = myAvatar->getDefaultEyePosition();
360+ glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm);
361+ glm::vec3 tipPos = invOrientation * (tip - eyePos);
362+
363+ glm::vec4 clipSpacePos = glm::vec4(projection * glm::dvec4(tipPos, 1.0));
364+ glm::vec3 ndcSpacePos;
365+ if (clipSpacePos.w != 0) {
366+ ndcSpacePos = glm::vec3(clipSpacePos) / clipSpacePos.w;
367+ }
368+
369+ rv.setX(((ndcSpacePos.x + 1.0) / 2.0) * canvasSize.x);
370+ rv.setY((1.0 - ((ndcSpacePos.y + 1.0) / 2.0)) * canvasSize.y);
371+ }
372+ return rv;
373+}
374+
375+//Finds the collision point of a world space ray
376+bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const {
377+ MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
378+
379+ glm::quat inverseOrientation = glm::inverse(myAvatar->getOrientation());
380+
381+ glm::vec3 relativePosition = inverseOrientation * (position - myAvatar->getDefaultEyePosition());
382+ glm::vec3 relativeDirection = glm::normalize(inverseOrientation * direction);
383+
384+ float t;
385+ if (raySphereIntersect(relativeDirection, relativePosition, _oculusUIRadius * myAvatar->getScale(), &t)){
386+ result = position + direction * t;
387+ return true;
388+ }
389+
390+ return false;
391+}
392+
393+//Renders optional pointers
394+void ApplicationCompositor::renderPointers(gpu::Batch& batch) {
395+ if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) {
396+ //If we are in oculus, render reticle later
397+ QPoint position = QPoint(qApp->getTrueMouseX(), qApp->getTrueMouseY());
398+ _reticlePosition[MOUSE] = position;
399+ _reticleActive[MOUSE] = true;
400+ _magActive[MOUSE] = _magnifier;
401+ _reticleActive[LEFT_CONTROLLER] = false;
402+ _reticleActive[RIGHT_CONTROLLER] = false;
403+ } else if (qApp->getLastMouseMoveWasSimulated() && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) {
404+ //only render controller pointer if we aren't already rendering a mouse pointer
405+ _reticleActive[MOUSE] = false;
406+ _magActive[MOUSE] = false;
407+ renderControllerPointers(batch);
408+ }
409+}
410+
411+
412+void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) {
413+ MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
414+
415+ //Static variables used for storing controller state
416+ static quint64 pressedTime[NUMBER_OF_RETICLES] = { 0ULL, 0ULL, 0ULL };
417+ static bool isPressed[NUMBER_OF_RETICLES] = { false, false, false };
418+ static bool stateWhenPressed[NUMBER_OF_RETICLES] = { false, false, false };
419+
420+ const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData();
421+
422+ for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) {
423+ const int index = palmIndex - 1;
424+
425+ const PalmData* palmData = NULL;
426+
427+ if (palmIndex >= handData->getPalms().size()) {
428+ return;
429+ }
430+
431+ if (handData->getPalms()[palmIndex].isActive()) {
432+ palmData = &handData->getPalms()[palmIndex];
433+ } else {
434+ continue;
435+ }
436+
437+ int controllerButtons = palmData->getControllerButtons();
438+
439+ //Check for if we should toggle or drag the magnification window
440+ if (controllerButtons & BUTTON_3) {
441+ if (isPressed[index] == false) {
442+ //We are now dragging the window
443+ isPressed[index] = true;
444+ //set the pressed time in us
445+ pressedTime[index] = usecTimestampNow();
446+ stateWhenPressed[index] = _magActive[index];
447+ }
448+ } else if (isPressed[index]) {
449+ isPressed[index] = false;
450+ //If the button was only pressed for < 250 ms
451+ //then disable it.
452+
453+ const int MAX_BUTTON_PRESS_TIME = 250 * MSECS_TO_USECS;
454+ if (usecTimestampNow() < pressedTime[index] + MAX_BUTTON_PRESS_TIME) {
455+ _magActive[index] = !stateWhenPressed[index];
456+ }
457+ }
458+
459+ //if we have the oculus, we should make the cursor smaller since it will be
460+ //magnified
461+ if (qApp->isHMDMode()) {
462+
463+ QPoint point = getPalmClickLocation(palmData);
464+
465+ _reticlePosition[index] = point;
466+
467+ //When button 2 is pressed we drag the mag window
468+ if (isPressed[index]) {
469+ _magActive[index] = true;
470+ }
471+
472+ // If oculus is enabled, we draw the crosshairs later
473+ continue;
474+ }
475+
476+ auto canvasSize = qApp->getCanvasSize();
477+ int mouseX, mouseY;
478+ if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) {
479+ QPoint res = getPalmClickLocation(palmData);
480+ mouseX = res.x();
481+ mouseY = res.y();
482+ } else {
483+ // Get directon relative to avatar orientation
484+ glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection();
485+
486+ // Get the angles, scaled between (-0.5,0.5)
487+ float xAngle = (atan2(direction.z, direction.x) + M_PI_2);
488+ float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2));
489+
490+ // Get the pixel range over which the xAngle and yAngle are scaled
491+ float cursorRange = canvasSize.x * SixenseManager::getInstance().getCursorPixelRangeMult();
492+
493+ mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle);
494+ mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle);
495+ }
496+
497+ //If the cursor is out of the screen then don't render it
498+ if (mouseX < 0 || mouseX >= (int)canvasSize.x || mouseY < 0 || mouseY >= (int)canvasSize.y) {
499+ _reticleActive[index] = false;
500+ continue;
501+ }
502+ _reticleActive[index] = true;
503+
504+
505+ const float reticleSize = 40.0f;
506+
507+ mouseX -= reticleSize / 2.0f;
508+ mouseY += reticleSize / 2.0f;
509+
510+
511+ glm::vec2 topLeft(mouseX, mouseY);
512+ glm::vec2 bottomRight(mouseX + reticleSize, mouseY - reticleSize);
513+ glm::vec2 texCoordTopLeft(0.0f, 0.0f);
514+ glm::vec2 texCoordBottomRight(1.0f, 1.0f);
515+
516+ DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
517+ glm::vec4(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f));
518+
519+ }
520+}
521+
522+//Renders a small magnification of the currently bound texture at the coordinates
523+void ApplicationCompositor::renderMagnifier(gpu::Batch& batch, const glm::vec2& magPos, float sizeMult, bool showBorder) {
524+ if (!_magnifier) {
525+ return;
526+ }
527+ auto canvasSize = qApp->getCanvasSize();
528+
529+ const int widgetWidth = canvasSize.x;
530+ const int widgetHeight = canvasSize.y;
531+
532+ const float halfWidth = (MAGNIFY_WIDTH / _textureAspectRatio) * sizeMult / 2.0f;
533+ const float halfHeight = MAGNIFY_HEIGHT * sizeMult / 2.0f;
534+ // Magnification Texture Coordinates
535+ const float magnifyULeft = (magPos.x - halfWidth) / (float)widgetWidth;
536+ const float magnifyURight = (magPos.x + halfWidth) / (float)widgetWidth;
537+ const float magnifyVTop = 1.0f - (magPos.y - halfHeight) / (float)widgetHeight;
538+ const float magnifyVBottom = 1.0f - (magPos.y + halfHeight) / (float)widgetHeight;
539+
540+ const float newHalfWidth = halfWidth * MAGNIFY_MULT;
541+ const float newHalfHeight = halfHeight * MAGNIFY_MULT;
542+ //Get yaw / pitch value for the corners
543+ const glm::vec2 topLeftYawPitch = overlayToSpherical(glm::vec2(magPos.x - newHalfWidth,
544+ magPos.y - newHalfHeight));
545+ const glm::vec2 bottomRightYawPitch = overlayToSpherical(glm::vec2(magPos.x + newHalfWidth,
546+ magPos.y + newHalfHeight));
547+
548+ const glm::vec3 bottomLeft = getPoint(topLeftYawPitch.x, bottomRightYawPitch.y);
549+ const glm::vec3 bottomRight = getPoint(bottomRightYawPitch.x, bottomRightYawPitch.y);
550+ const glm::vec3 topLeft = getPoint(topLeftYawPitch.x, topLeftYawPitch.y);
551+ const glm::vec3 topRight = getPoint(bottomRightYawPitch.x, topLeftYawPitch.y);
552+
553+ auto geometryCache = DependencyManager::get<GeometryCache>();
554+
555+ if (bottomLeft != _previousMagnifierBottomLeft || bottomRight != _previousMagnifierBottomRight
556+ || topLeft != _previousMagnifierTopLeft || topRight != _previousMagnifierTopRight) {
557+ QVector<glm::vec3> border;
558+ border << topLeft;
559+ border << bottomLeft;
560+ border << bottomRight;
561+ border << topRight;
562+ border << topLeft;
563+ geometryCache->updateVertices(_magnifierBorder, border, glm::vec4(1.0f, 0.0f, 0.0f, _alpha));
564+
565+ _previousMagnifierBottomLeft = bottomLeft;
566+ _previousMagnifierBottomRight = bottomRight;
567+ _previousMagnifierTopLeft = topLeft;
568+ _previousMagnifierTopRight = topRight;
569+ }
570+
571+ glPushMatrix(); {
572+ if (showBorder) {
573+ glDisable(GL_TEXTURE_2D);
574+ glLineWidth(1.0f);
575+ //Outer Line
576+ geometryCache->renderVertices(gpu::LINE_STRIP, _magnifierBorder);
577+ glEnable(GL_TEXTURE_2D);
578+ }
579+ glm::vec4 magnifierColor = { 1.0f, 1.0f, 1.0f, _alpha };
580+
581+ DependencyManager::get<GeometryCache>()->renderQuad(bottomLeft, bottomRight, topRight, topLeft,
582+ glm::vec2(magnifyULeft, magnifyVBottom),
583+ glm::vec2(magnifyURight, magnifyVBottom),
584+ glm::vec2(magnifyURight, magnifyVTop),
585+ glm::vec2(magnifyULeft, magnifyVTop),
586+ magnifierColor, _magnifierQuad);
587+
588+ } glPopMatrix();
589+}
590+
591+void ApplicationCompositor::buildHemiVertices(
592+ const float fov, const float aspectRatio, const int slices, const int stacks) {
593+ static float textureFOV = 0.0f, textureAspectRatio = 1.0f;
594+ if (textureFOV == fov && textureAspectRatio == aspectRatio) {
595+ return;
596+ }
597+
598+ textureFOV = fov;
599+ textureAspectRatio = aspectRatio;
600+
601+ auto geometryCache = DependencyManager::get<GeometryCache>();
602+
603+ _hemiVertices = gpu::BufferPointer(new gpu::Buffer());
604+ _hemiIndices = gpu::BufferPointer(new gpu::Buffer());
605+
606+
607+ if (fov >= PI) {
608+ qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues";
609+ }
610+
611+ //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm
612+
613+ vec3 pos;
614+ vec2 uv;
615+ // Compute vertices positions and texture UV coordinate
616+ // Create and write to buffer
617+ for (int i = 0; i < stacks; i++) {
618+ uv.y = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f
619+ // abs(theta) <= fov / 2.0f
620+ float pitch = -fov * (uv.y - 0.5f);
621+ for (int j = 0; j < slices; j++) {
622+ uv.x = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f
623+ // abs(phi) <= fov * aspectRatio / 2.0f
624+ float yaw = -fov * aspectRatio * (uv.x - 0.5f);
625+ pos = getPoint(yaw, pitch);
626+ static const vec4 color(1);
627+ _hemiVertices->append(sizeof(pos), (gpu::Byte*)&pos);
628+ _hemiVertices->append(sizeof(vec2), (gpu::Byte*)&uv);
629+ _hemiVertices->append(sizeof(vec4), (gpu::Byte*)&color);
630+ }
631+ }
632+
633+ // Compute number of indices needed
634+ static const int VERTEX_PER_TRANGLE = 3;
635+ static const int TRIANGLE_PER_RECTANGLE = 2;
636+ int numberOfRectangles = (slices - 1) * (stacks - 1);
637+ _hemiIndexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE;
638+
639+ // Compute indices order
640+ std::vector<GLushort> indices;
641+ for (int i = 0; i < stacks - 1; i++) {
642+ for (int j = 0; j < slices - 1; j++) {
643+ GLushort bottomLeftIndex = i * slices + j;
644+ GLushort bottomRightIndex = bottomLeftIndex + 1;
645+ GLushort topLeftIndex = bottomLeftIndex + slices;
646+ GLushort topRightIndex = topLeftIndex + 1;
647+ // FIXME make a z-order curve for better vertex cache locality
648+ indices.push_back(topLeftIndex);
649+ indices.push_back(bottomLeftIndex);
650+ indices.push_back(topRightIndex);
651+
652+ indices.push_back(topRightIndex);
653+ indices.push_back(bottomLeftIndex);
654+ indices.push_back(bottomRightIndex);
655+ }
656+ }
657+ _hemiIndices->append(sizeof(GLushort) * indices.size(), (gpu::Byte*)&indices[0]);
658+}
659+
660+
661+void ApplicationCompositor::drawSphereSection(gpu::Batch& batch) {
662+ buildHemiVertices(_textureFov, _textureAspectRatio, 80, 80);
663+ static const int VERTEX_DATA_SLOT = 0;
664+ static const int TEXTURE_DATA_SLOT = 1;
665+ static const int COLOR_DATA_SLOT = 2;
666+ gpu::Stream::FormatPointer streamFormat(new gpu::Stream::Format()); // 1 for everyone
667+ streamFormat->setAttribute(gpu::Stream::POSITION, VERTEX_DATA_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
668+ streamFormat->setAttribute(gpu::Stream::TEXCOORD, TEXTURE_DATA_SLOT, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV));
669+ streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_DATA_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA));
670+ batch.setInputFormat(streamFormat);
671+
672+ static const int VERTEX_STRIDE = sizeof(vec3) + sizeof(vec2) + sizeof(vec4);
673+ gpu::BufferView posView(_hemiVertices, 0, _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::POSITION)._element);
674+ gpu::BufferView uvView(_hemiVertices, sizeof(vec3), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::TEXCOORD)._element);
675+ gpu::BufferView colView(_hemiVertices, sizeof(vec3) + sizeof(vec2), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element);
676+ batch.setInputBuffer(VERTEX_DATA_SLOT, posView);
677+ batch.setInputBuffer(TEXTURE_DATA_SLOT, uvView);
678+ batch.setInputBuffer(COLOR_DATA_SLOT, colView);
679+ batch.setIndexBuffer(gpu::UINT16, _hemiIndices, 0);
680+ batch.drawIndexed(gpu::TRIANGLES, _hemiIndexCount);
681+}
682+
683+glm::vec2 ApplicationCompositor::directionToSpherical(const glm::vec3& direction) {
684+ glm::vec2 result;
685+ // Compute yaw
686+ glm::vec3 normalProjection = glm::normalize(glm::vec3(direction.x, 0.0f, direction.z));
687+ result.x = glm::acos(glm::dot(IDENTITY_FRONT, normalProjection));
688+ if (glm::dot(IDENTITY_RIGHT, normalProjection) > 0.0f) {
689+ result.x = -glm::abs(result.x);
690+ } else {
691+ result.x = glm::abs(result.x);
692+ }
693+ // Compute pitch
694+ result.y = angleBetween(IDENTITY_UP, direction) - PI_OVER_TWO;
695+
696+ return result;
697+}
698+
699+glm::vec3 ApplicationCompositor::sphericalToDirection(const glm::vec2& sphericalPos) {
700+ glm::quat rotation(glm::vec3(sphericalPos.y, sphericalPos.x, 0.0f));
701+ return rotation * IDENTITY_FRONT;
702+}
703+
704+glm::vec2 ApplicationCompositor::screenToSpherical(const glm::vec2& screenPos) {
705+ auto screenSize = qApp->getCanvasSize();
706+ glm::vec2 result;
707+ result.x = -(screenPos.x / screenSize.x - 0.5f);
708+ result.y = (screenPos.y / screenSize.y - 0.5f);
709+ result.x *= MOUSE_YAW_RANGE;
710+ result.y *= MOUSE_PITCH_RANGE;
711+
712+ return result;
713+}
714+
715+glm::vec2 ApplicationCompositor::sphericalToScreen(const glm::vec2& sphericalPos) {
716+ glm::vec2 result = sphericalPos;
717+ result.x *= -1.0;
718+ result /= MOUSE_RANGE;
719+ result += 0.5f;
720+ result *= qApp->getCanvasSize();
721+ return result;
722+}
723+
724+glm::vec2 ApplicationCompositor::sphericalToOverlay(const glm::vec2& sphericalPos) const {
725+ glm::vec2 result = sphericalPos;
726+ result.x *= -1.0;
727+ result /= _textureFov;
728+ result.x /= _textureAspectRatio;
729+ result += 0.5f;
730+ result *= qApp->getCanvasSize();
731+ return result;
732+}
733+
734+glm::vec2 ApplicationCompositor::overlayToSpherical(const glm::vec2& overlayPos) const {
735+ glm::vec2 result = overlayPos;
736+ result /= qApp->getCanvasSize();
737+ result -= 0.5f;
738+ result *= _textureFov;
739+ result.x *= _textureAspectRatio;
740+ result.x *= -1.0f;
741+ return result;
742+}
743+
744+glm::vec2 ApplicationCompositor::screenToOverlay(const glm::vec2& screenPos) const {
745+ return sphericalToOverlay(screenToSpherical(screenPos));
746+}
747+
748+glm::vec2 ApplicationCompositor::overlayToScreen(const glm::vec2& overlayPos) const {
749+ return sphericalToScreen(overlayToSpherical(overlayPos));
750+}
751+
752+void ApplicationCompositor::updateTooltips() {
753+ if (_hoverItemId != _noItemId) {
754+ quint64 hoverDuration = usecTimestampNow() - _hoverItemEnterUsecs;
755+ if (_hoverItemEnterUsecs != UINT64_MAX && !_hoverItemHref.isEmpty() && hoverDuration > TOOLTIP_DELAY) {
756+ // TODO Enable and position the tooltip
757+ _hoverItemEnterUsecs = UINT64_MAX;
758+ _tooltipId = Tooltip::showTip("URL: " + _hoverItemHref);
759+ }
760+ }
761+}
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+
21+class Camera;
22+class PalmData;
23+class RenderArgs;
24+
25+const float MAGNIFY_WIDTH = 220.0f;
26+const float MAGNIFY_HEIGHT = 100.0f;
27+const float MAGNIFY_MULT = 2.0f;
28+
29+const float DEFAULT_HMD_UI_ANGULAR_SIZE = 72.0f;
30+
31+// Handles the drawing of the overlays to the screen
32+// TODO, move divide up the rendering, displaying and input handling
33+// facilities of this class
34+class ApplicationCompositor : public QObject {
35+ Q_OBJECT
36+public:
37+ ApplicationCompositor();
38+ ~ApplicationCompositor();
39+
40+ void displayOverlayTexture(RenderArgs* renderArgs);
41+ void displayOverlayTextureHmd(RenderArgs* renderArgs, int eye);
42+
43+ QPoint getPalmClickLocation(const PalmData *palm) const;
44+ bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const;
45+
46+ bool hasMagnifier() const { return _magnifier; }
47+ void toggleMagnifier() { _magnifier = !_magnifier; }
48+
49+ float getHmdUIAngularSize() const { return _hmdUIAngularSize; }
50+ void setHmdUIAngularSize(float hmdUIAngularSize) { _hmdUIAngularSize = hmdUIAngularSize; }
51+
52+ // Converter from one frame of reference to another.
53+ // Frame of reference:
54+ // Direction: Ray that represents the spherical values
55+ // Screen: Position on the screen (x,y)
56+ // Spherical: Pitch and yaw that gives the position on the sphere we project on (yaw,pitch)
57+ // Overlay: Position on the overlay (x,y)
58+ // (x,y) in Overlay are similar than (x,y) in Screen except they can be outside of the bound of te screen.
59+ // This allows for picking outside of the screen projection in 3D.
60+ glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const;
61+ glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const;
62+ glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const;
63+ glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const;
64+ void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const;
65+ GLuint getOverlayTexture() const;
66+
67+ static glm::vec2 directionToSpherical(const glm::vec3 & direction);
68+ static glm::vec3 sphericalToDirection(const glm::vec2 & sphericalPos);
69+ static glm::vec2 screenToSpherical(const glm::vec2 & screenPos);
70+ static glm::vec2 sphericalToScreen(const glm::vec2 & sphericalPos);
71+
72+private:
73+ void displayOverlayTextureStereo(RenderArgs* renderArgs, float aspectRatio, float fov);
74+ void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0);
75+ void buildHemiVertices(const float fov, const float aspectRatio, const int slices, const int stacks);
76+ void drawSphereSection(gpu::Batch& batch);
77+ void updateTooltips();
78+
79+ void renderPointers(gpu::Batch& batch);
80+ void renderMagnifier(gpu::Batch& batch, const glm::vec2& magPos, float sizeMult, bool showBorder);
81+ void renderControllerPointers(gpu::Batch& batch);
82+ void renderPointersOculus(gpu::Batch& batch);
83+
84+ // Support for hovering and tooltips
85+ static EntityItemID _noItemId;
86+ EntityItemID _hoverItemId{ _noItemId };
87+ QString _hoverItemHref;
88+ quint64 _hoverItemEnterUsecs{ 0 };
89+
90+ float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE;
91+ float _textureFov{ glm::radians(DEFAULT_HMD_UI_ANGULAR_SIZE) };
92+ float _textureAspectRatio{ 1.0f };
93+ int _hemiVerticesID{ GeometryCache::UNKNOWN_ID };
94+
95+ enum Reticles { MOUSE, LEFT_CONTROLLER, RIGHT_CONTROLLER, NUMBER_OF_RETICLES };
96+ bool _reticleActive[NUMBER_OF_RETICLES];
97+ QPoint _reticlePosition[NUMBER_OF_RETICLES];
98+ bool _magActive[NUMBER_OF_RETICLES];
99+ float _magSizeMult[NUMBER_OF_RETICLES];
100+ bool _magnifier{ true };
101+
102+ float _alpha{ 1.0f };
103+ float _oculusUIRadius{ 1.0f };
104+
105+ QMap<uint16_t, gpu::TexturePointer> _cursors;
106+
107+ int _reticleQuad;
108+ int _magnifierQuad;
109+ int _magnifierBorder;
110+
111+ int _previousBorderWidth{ -1 };
112+ int _previousBorderHeight{ -1 };
113+
114+ glm::vec3 _previousMagnifierBottomLeft;
115+ glm::vec3 _previousMagnifierBottomRight;
116+ glm::vec3 _previousMagnifierTopLeft;
117+ glm::vec3 _previousMagnifierTopRight;
118+};
119+
120+#endif // hifi_ApplicationCompositor_h
interface/src/ui/ApplicationOverlay.cpp+116−1172
@@ -14,166 +14,58 @@ · 13 unmodified lines
1414 #include <QOpenGLFramebufferObject>
1515 #include <QOpenGLTexture>
1616
17+#include <glm/gtc/type_ptr.hpp>
18+
1719 #include <avatar/AvatarManager.h>
20+#include <DeferredLightingEffect.h>
1821 #include <GLMHelpers.h>
19−#include <PathUtils.h>
2022 #include <gpu/GLBackend.h>
23+#include <gpu/GLBackendShared.h>
2124 #include <GLMHelpers.h>
22−#include <PerfStat.h>
2325 #include <OffscreenUi.h>
26+#include <CursorManager.h>
27+#include <PerfStat.h>
2428
2529 #include "AudioClient.h"
2630 #include "audio/AudioIOStatsRenderer.h"
2731 #include "audio/AudioScope.h"
28−#include "audio/AudioToolBox.h"
2932 #include "Application.h"
3033 #include "ApplicationOverlay.h"
31−#include "devices/CameraToolBox.h"
3234
3335 #include "Util.h"
3436 #include "ui/Stats.h"
35−
36−// Used to animate the magnification windows
37−const float MAG_SPEED = 0.08f;
38−
39−const quint64 MSECS_TO_USECS = 1000ULL;
37+#include "ui/AvatarInputs.h"
4038
4139 const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f };
42−const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f };
43−const float reticleSize = TWO_PI / 100.0f;
44−
45−
46−const float CONNECTION_STATUS_BORDER_COLOR[] = { 1.0f, 0.0f, 0.0f };
40+const int AUDIO_METER_GAP = 5;
41+const int MUTE_ICON_PADDING = 10;
42+const vec4 CONNECTION_STATUS_BORDER_COLOR{ 1.0f, 0.0f, 0.0f, 0.8f };
4743 const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f;
44+static const float ORTHO_NEAR_CLIP = -10000;
45+static const float ORTHO_FAR_CLIP = 10000;
4846
49−static const float MOUSE_PITCH_RANGE = 1.0f * PI;
50−static const float MOUSE_YAW_RANGE = 0.5f * TWO_PI;
51−static const glm::vec2 MOUSE_RANGE(MOUSE_YAW_RANGE, MOUSE_PITCH_RANGE);
52−
53−// Return a point's cartesian coordinates on a sphere from pitch and yaw
54−glm::vec3 getPoint(float yaw, float pitch) {
55− return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)),
56− glm::sin(-pitch),
57− glm::cos(-pitch) * (-glm::cos(yaw)));
47+// TODO move somewhere useful
48+static void fboViewport(QOpenGLFramebufferObject* fbo) {
49+ auto size = fbo->size();
50+ glViewport(0, 0, size.width(), size.height());
5851 }
5952
60−//Checks if the given ray intersects the sphere at the origin. result will store a multiplier that should
61−//be multiplied by dir and added to origin to get the location of the collision
62−bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r, float* result)
53+ApplicationOverlay::ApplicationOverlay()
6354 {
64− //Source: http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection
65−
66− //Compute A, B and C coefficients
67− float a = glm::dot(dir, dir);
68− float b = 2 * glm::dot(dir, origin);
69− float c = glm::dot(origin, origin) - (r * r);
70−
71− //Find discriminant
72− float disc = b * b - 4 * a * c;
73−
74− // if discriminant is negative there are no real roots, so return
75− // false as ray misses sphere
76− if (disc < 0) {
77− return false;
78− }
79−
80− // compute q as described above
81− float distSqrt = sqrtf(disc);
82− float q;
83− if (b < 0) {
84− q = (-b - distSqrt) / 2.0;
85− } else {
86− q = (-b + distSqrt) / 2.0;
87− }
88−
89− // compute t0 and t1
90− float t0 = q / a;
91− float t1 = c / q;
92−
93− // make sure t0 is smaller than t1
94− if (t0 > t1) {
95− // if t0 is bigger than t1 swap them around
96− float temp = t0;
97− t0 = t1;
98− t1 = temp;
99− }
100−
101− // if t1 is less than zero, the object is in the ray's negative direction
102− // and consequently the ray misses the sphere
103− if (t1 < 0) {
104− return false;
105− }
106−
107− // if t0 is less than zero, the intersection point is at t1
108− if (t0 < 0) {
109− *result = t1;
110− return true;
111− } else { // else the intersection point is at t0
112− *result = t0;
113− return true;
114− }
115−}
116−
117−void ApplicationOverlay::renderReticle(glm::quat orientation, float alpha) {
118− glPushMatrix(); {
119− glm::vec3 axis = glm::axis(orientation);
120− glRotatef(glm::degrees(glm::angle(orientation)), axis.x, axis.y, axis.z);
121− glm::vec3 topLeft = getPoint(reticleSize / 2.0f, -reticleSize / 2.0f);
122− glm::vec3 topRight = getPoint(-reticleSize / 2.0f, -reticleSize / 2.0f);
123− glm::vec3 bottomLeft = getPoint(reticleSize / 2.0f, reticleSize / 2.0f);
124− glm::vec3 bottomRight = getPoint(-reticleSize / 2.0f, reticleSize / 2.0f);
125−
126− // TODO: this version of renderQuad() needs to take a color
127− glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], alpha };
128−
129−
130−
131− DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomLeft, bottomRight, topRight,
132− glm::vec2(0.0f, 0.0f), glm::vec2(1.0f, 0.0f),
133− glm::vec2(1.0f, 1.0f), glm::vec2(0.0f, 1.0f),
134− reticleColor, _reticleQuad);
135− } glPopMatrix();
136−}
137−
138−ApplicationOverlay::ApplicationOverlay() :
139− _textureFov(glm::radians(DEFAULT_HMD_UI_ANGULAR_SIZE)),
140− _textureAspectRatio(1.0f),
141− _lastMouseMove(0),
142− _magnifier(true),
143− _alpha(1.0f),
144− _oculusUIRadius(1.0f),
145− _trailingAudioLoudness(0.0f),
146− _previousBorderWidth(-1),
147− _previousBorderHeight(-1),
148− _previousMagnifierBottomLeft(),
149− _previousMagnifierBottomRight(),
150− _previousMagnifierTopLeft(),
151− _previousMagnifierTopRight()
152−{
153− memset(_reticleActive, 0, sizeof(_reticleActive));
154− memset(_magActive, 0, sizeof(_reticleActive));
155− memset(_magSizeMult, 0, sizeof(_magSizeMult));
156−
15755 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();
16456 _domainStatusBorder = geometryCache->allocateID();
16557 _magnifierBorder = geometryCache->allocateID();
166−
58+
16759 // Once we move UI rendering and screen rendering to different
16860 // 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
17062 // then release it back to the UI for re-use
17163 auto offscreenUi = DependencyManager::get<OffscreenUi>();
17264 connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [&](GLuint textureId) {
17365 auto offscreenUi = DependencyManager::get<OffscreenUi>();
17466 offscreenUi->lockTexture(textureId);
17567 assert(!glGetError());
176− std::swap(_newUiTexture, textureId);
68+ std::swap(_uiTexture, textureId);
17769 if (textureId) {
17870 offscreenUi->releaseTexture(textureId);
17971 }
@@ -185,846 +77,81 @@ ApplicationOverlay::~ApplicationOverlay() { · 5 unmodified lines
18577
18678 // Renders the overlays either to a texture or to the screen
18779 void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
80+ CHECK_GL_ERROR();
18881 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);
19482
195− //Handle fading and deactivation/activation of UI
196−
197− // Render 2D overlay
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);
83+ // TODO move to Application::idle()?
84+ Stats::getInstance()->updateStats();
85+ AvatarInputs::getInstance()->update();
20786
208− glPushMatrix(); {
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);
87+ buildFramebufferObject();
21388
214− glMatrixMode(GL_MODELVIEW);
215−
216− renderAudioMeter();
217− renderCameraToggle();
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−}
89+ // Execute the batch into our framebuffer
90+ _overlayFramebuffer->bind();
91+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
92+ fboViewport(_overlayFramebuffer);
24093
241−// A quick and dirty solution for compositing the old overlay
242−// texture with the new one
243−template <typename F>
244−void with_each_texture(GLuint firstPassTexture, GLuint secondPassTexture, F f) {
245− glEnable(GL_TEXTURE_2D);
246− glActiveTexture(GL_TEXTURE0);
247− if (firstPassTexture) {
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);
94+ // Now render the overlay components together into a single texture
95+ renderOverlays(renderArgs);
96+ renderStatsAndLogs(renderArgs);
97+ renderDomainConnectionStatusBorder(renderArgs);
98+ renderQmlUi(renderArgs);
99+ _overlayFramebuffer->release();
100+ CHECK_GL_ERROR();
257101 }
258102
259−// Draws the FBO texture for the screen
260−void ApplicationOverlay::displayOverlayTexture() {
261− if (_alpha == 0.0f) {
262− return;
103+void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
104+ if (_uiTexture) {
105+ gpu::Batch batch;
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);
263114 }
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();
311115 }
312116
313−// Draws the FBO texture for Oculus rift.
314−void ApplicationOverlay::displayOverlayTextureHmd(Camera& whichCamera) {
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−
117+void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) {
118+ glm::vec2 size = qApp->getCanvasSize();
327119
328− //Update and draw the magnifiers
329− MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
330− const glm::quat& orientation = myAvatar->getOrientation();
331− // Always display the HMD overlay relative to the camera position but
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;
120+ mat4 legacyProjection = glm::ortho<float>(0, size.x, size.y, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP);
121+ glMatrixMode(GL_PROJECTION);
122+ glPushMatrix();
123+ glLoadMatrixf(glm::value_ptr(legacyProjection));
339124 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− }
380125
381− with_each_texture(_overlays.getTexture(), _newUiTexture, [&] {
382− _overlays.render();
383− });
384−
385− if (!Application::getInstance()->isMouseHidden()) {
386− renderPointersOculus();
387− }
388− glDepthMask(GL_TRUE);
389− glDisable(GL_TEXTURE_2D);
390−
391− glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
392− glEnable(GL_LIGHTING);
393− } glPopMatrix();
394−}
395−
396−// Draws the FBO texture for 3DTV.
397−void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov) {
398− if (_alpha == 0.0f) {
399− return;
400− }
401−
402− MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
403− const glm::vec3& viewMatrixTranslation = qApp->getViewMatrixTranslation();
404−
405− glEnable(GL_BLEND);
406− glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
407− glEnable(GL_DEPTH_TEST);
408− glDisable(GL_LIGHTING);
409−
410− glMatrixMode(GL_MODELVIEW);
411−
412− glPushMatrix();
413− glLoadIdentity();
414− // Transform to world space
415− glm::quat rotation = whichCamera.getRotation();
416− glm::vec3 axis2 = glm::axis(rotation);
417− glRotatef(-glm::degrees(glm::angle(rotation)), axis2.x, axis2.y, axis2.z);
418− glTranslatef(viewMatrixTranslation.x, viewMatrixTranslation.y, viewMatrixTranslation.z);
419−
420− // Translate to the front of the camera
421− glm::vec3 pos = whichCamera.getPosition();
422− glm::quat rot = myAvatar->getOrientation();
423− glm::vec3 axis = glm::axis(rot);
424−
425− glTranslatef(pos.x, pos.y, pos.z);
426− glRotatef(glm::degrees(glm::angle(rot)), axis.x, axis.y, axis.z);
427−
428− glm::vec4 overlayColor = {1.0f, 1.0f, 1.0f, _alpha};
429−
430− //Render
431− const GLfloat distance = 1.0f;
432−
433− const GLfloat halfQuadHeight = distance * tan(fov);
434− const GLfloat halfQuadWidth = halfQuadHeight * aspectRatio;
435− const GLfloat quadWidth = halfQuadWidth * 2.0f;
436− const GLfloat quadHeight = halfQuadHeight * 2.0f;
437−
438− GLfloat x = -halfQuadWidth;
439− GLfloat y = -halfQuadHeight;
440126 glDisable(GL_DEPTH_TEST);
441−
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
127+ glDisable(GL_LIGHTING);
458128 glEnable(GL_BLEND);
459129 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 };
470130
471− DependencyManager::get<GeometryCache>()->renderQuad(glm::vec3(x + mouseX, y + mouseY, -distance),
472− glm::vec3(x + mouseX + reticleSize, y + mouseY, -distance),
473− glm::vec3(x + mouseX + reticleSize, y + mouseY - reticleSize, -distance),
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);
131+ // give external parties a change to hook in
132+ emit qApp->renderingOverlay();
133+ qApp->getOverlays().renderHUD(renderArgs);
478134
479− glEnable(GL_DEPTH_TEST);
480−
135+ glMatrixMode(GL_PROJECTION);
481136 glPopMatrix();
482−
483− glDepthMask(GL_TRUE);
484− glBindTexture(GL_TEXTURE_2D, 0);
485− glDisable(GL_TEXTURE_2D);
486−
487− glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
488− glEnable(GL_LIGHTING);
489−}
490−
491−void ApplicationOverlay::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const {
492− cursorPos *= qApp->getCanvasSize();
493− const glm::vec2 projection = screenToSpherical(cursorPos);
494− // The overlay space orientation of the mouse coordinates
495− const glm::quat orientation(glm::vec3(-projection.y, projection.x, 0.0f));
496− // FIXME We now have the direction of the ray FROM THE DEFAULT HEAD POSE.
497− // Now we need to account for the actual camera position relative to the overlay
498− glm::vec3 overlaySpaceDirection = glm::normalize(orientation * IDENTITY_FRONT);
499−
500−
501− const glm::vec3& hmdPosition = qApp->getCamera()->getHmdPosition();
502− const glm::quat& hmdOrientation = qApp->getCamera()->getHmdRotation();
503−
504− // We need the RAW camera orientation and position, because this is what the overlay is
505− // rendered relative to
506− const glm::vec3 overlayPosition = qApp->getCamera()->getPosition() - hmdPosition;
507− const glm::quat overlayOrientation = qApp->getCamera()->getRotation() * glm::inverse(hmdOrientation);
508−
509− // Intersection UI overlay space
510− glm::vec3 worldSpaceDirection = overlayOrientation * overlaySpaceDirection;
511− glm::vec3 intersectionWithUi = glm::normalize(worldSpaceDirection) * _oculusUIRadius;
512− intersectionWithUi += overlayPosition;
513−
514− // Intersection in world space
515− origin = overlayPosition + hmdPosition;
516− direction = glm::normalize(intersectionWithUi - origin);
517−}
518−
519−glm::vec2 getPolarCoordinates(const PalmData& palm) {
520− MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
521− auto avatarOrientation = myAvatar->getOrientation();
522− auto eyePos = myAvatar->getDefaultEyePosition();
523− glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm);
524− // Direction of the tip relative to the eye
525− glm::vec3 tipDirection = tip - eyePos;
526− // orient into avatar space
527− tipDirection = glm::inverse(avatarOrientation) * tipDirection;
528− // Normalize for trig functions
529− tipDirection = glm::normalize(tipDirection);
530− // Convert to polar coordinates
531− glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y));
532− return polar;
533−}
534−
535−//Caculate the click location using one of the sixense controllers. Scale is not applied
536−QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const {
537− QPoint rv;
538− auto canvasSize = qApp->getCanvasSize();
539− if (qApp->isHMDMode()) {
540− glm::vec2 polar = getPolarCoordinates(*palm);
541− glm::vec2 point = sphericalToScreen(-polar);
542− rv.rx() = point.x;
543− rv.ry() = point.y;
544− } else {
545− MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
546− glm::dmat4 projection;
547− qApp->getProjectionMatrix(&projection);
548− glm::quat invOrientation = glm::inverse(myAvatar->getOrientation());
549− glm::vec3 eyePos = myAvatar->getDefaultEyePosition();
550− glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm);
551− glm::vec3 tipPos = invOrientation * (tip - eyePos);
552−
553− glm::vec4 clipSpacePos = glm::vec4(projection * glm::dvec4(tipPos, 1.0));
554− glm::vec3 ndcSpacePos;
555− if (clipSpacePos.w != 0) {
556− ndcSpacePos = glm::vec3(clipSpacePos) / clipSpacePos.w;
557− }
558−
559− rv.setX(((ndcSpacePos.x + 1.0) / 2.0) * canvasSize.x);
560− rv.setY((1.0 - ((ndcSpacePos.y + 1.0) / 2.0)) * canvasSize.y);
561− }
562− return rv;
563−}
564−
565−//Finds the collision point of a world space ray
566−bool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const {
567− MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
568−
569− glm::quat inverseOrientation = glm::inverse(myAvatar->getOrientation());
570−
571− glm::vec3 relativePosition = inverseOrientation * (position - myAvatar->getDefaultEyePosition());
572− glm::vec3 relativeDirection = glm::normalize(inverseOrientation * direction);
573−
574− float t;
575− if (raySphereIntersect(relativeDirection, relativePosition, _oculusUIRadius * myAvatar->getScale(), &t)){
576− result = position + direction * t;
577− return true;
578− }
579−
580− return false;
581−}
582−
583−//Renders optional pointers
584−void ApplicationOverlay::renderPointers() {
585− //lazily load crosshair texture
586− if (_crosshairTexture == 0) {
587− _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png");
588− }
589− glEnable(GL_TEXTURE_2D);
590− glEnable(GL_BLEND);
591− glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
592−
593− //glActiveTexture(GL_TEXTURE0);
594− //bindCursorTexture();
595−
596− if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) {
597− //If we are in oculus, render reticle later
598− if (_lastMouseMove == 0) {
599− _lastMouseMove = usecTimestampNow();
600− }
601− QPoint position = QPoint(qApp->getTrueMouseX(), qApp->getTrueMouseY());
602−
603− static const int MAX_IDLE_TIME = 3;
604− if (_reticlePosition[MOUSE] != position) {
605− _lastMouseMove = usecTimestampNow();
606− } else if (usecTimestampNow() - _lastMouseMove > MAX_IDLE_TIME * USECS_PER_SECOND) {
607− //float pitch = 0.0f, yaw = 0.0f, roll = 0.0f; // radians
608− //OculusManager::getEulerAngles(yaw, pitch, roll);
609− glm::quat orientation = qApp->getHeadOrientation(); // (glm::vec3(pitch, yaw, roll));
610− glm::vec3 result;
611−
612− MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
613− if (calculateRayUICollisionPoint(myAvatar->getEyePosition(),
614− myAvatar->getOrientation() * orientation * IDENTITY_FRONT,
615− result)) {
616− glm::vec3 lookAtDirection = glm::inverse(myAvatar->getOrientation()) * (result - myAvatar->getDefaultEyePosition());
617− glm::vec2 spericalPos = directionToSpherical(glm::normalize(lookAtDirection));
618− glm::vec2 screenPos = sphericalToScreen(spericalPos);
619− position = QPoint(screenPos.x, screenPos.y);
620− // FIXME
621− //glCanvas->cursor().setPos(glCanvas->mapToGlobal(position));
622− } else {
623− qDebug() << "No collision point";
624− }
625− }
626−
627− _reticlePosition[MOUSE] = position;
628− _reticleActive[MOUSE] = true;
629− _magActive[MOUSE] = _magnifier;
630− _reticleActive[LEFT_CONTROLLER] = false;
631− _reticleActive[RIGHT_CONTROLLER] = false;
632− } else if (qApp->getLastMouseMoveWasSimulated() && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) {
633− _lastMouseMove = 0;
634− //only render controller pointer if we aren't already rendering a mouse pointer
635− _reticleActive[MOUSE] = false;
636− _magActive[MOUSE] = false;
637− renderControllerPointers();
638− }
639− //glBindTexture(GL_TEXTURE_2D, 0);
640− //glDisable(GL_TEXTURE_2D);
641−}
642−
643−void ApplicationOverlay::renderControllerPointers() {
644− MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
645−
646− //Static variables used for storing controller state
647− static quint64 pressedTime[NUMBER_OF_RETICLES] = { 0ULL, 0ULL, 0ULL };
648− static bool isPressed[NUMBER_OF_RETICLES] = { false, false, false };
649− static bool stateWhenPressed[NUMBER_OF_RETICLES] = { false, false, false };
650−
651− const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData();
652−
653− for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) {
654− const int index = palmIndex - 1;
655−
656− const PalmData* palmData = NULL;
657−
658− if (palmIndex >= handData->getPalms().size()) {
659− return;
660− }
661−
662− if (handData->getPalms()[palmIndex].isActive()) {
663− palmData = &handData->getPalms()[palmIndex];
664− } else {
665− continue;
666− }
667−
668− int controllerButtons = palmData->getControllerButtons();
669−
670− //Check for if we should toggle or drag the magnification window
671− if (controllerButtons & BUTTON_3) {
672− if (isPressed[index] == false) {
673− //We are now dragging the window
674− isPressed[index] = true;
675− //set the pressed time in us
676− pressedTime[index] = usecTimestampNow();
677− stateWhenPressed[index] = _magActive[index];
678− }
679− } else if (isPressed[index]) {
680− isPressed[index] = false;
681− //If the button was only pressed for < 250 ms
682− //then disable it.
683−
684− const int MAX_BUTTON_PRESS_TIME = 250 * MSECS_TO_USECS;
685− if (usecTimestampNow() < pressedTime[index] + MAX_BUTTON_PRESS_TIME) {
686− _magActive[index] = !stateWhenPressed[index];
687− }
688− }
689−
690− //if we have the oculus, we should make the cursor smaller since it will be
691− //magnified
692− if (qApp->isHMDMode()) {
693−
694− QPoint point = getPalmClickLocation(palmData);
695−
696− _reticlePosition[index] = point;
697−
698− //When button 2 is pressed we drag the mag window
699− if (isPressed[index]) {
700− _magActive[index] = true;
701− }
702−
703− // If oculus is enabled, we draw the crosshairs later
704− continue;
705− }
706−
707− auto canvasSize = qApp->getCanvasSize();
708− int mouseX, mouseY;
709− if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) {
710− QPoint res = getPalmClickLocation(palmData);
711− mouseX = res.x();
712− mouseY = res.y();
713− } else {
714− // Get directon relative to avatar orientation
715− glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection();
716−
717− // Get the angles, scaled between (-0.5,0.5)
718− float xAngle = (atan2(direction.z, direction.x) + M_PI_2);
719− float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2));
720−
721− // Get the pixel range over which the xAngle and yAngle are scaled
722− float cursorRange = canvasSize.x * SixenseManager::getInstance().getCursorPixelRangeMult();
723−
724− mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle);
725− mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle);
726− }
727−
728− //If the cursor is out of the screen then don't render it
729− if (mouseX < 0 || mouseX >= (int)canvasSize.x || mouseY < 0 || mouseY >= (int)canvasSize.y) {
730− _reticleActive[index] = false;
731− continue;
732− }
733− _reticleActive[index] = true;
734−
735−
736− const float reticleSize = 40.0f;
737−
738− mouseX -= reticleSize / 2.0f;
739− mouseY += reticleSize / 2.0f;
740−
741−
742− glm::vec2 topLeft(mouseX, mouseY);
743− glm::vec2 bottomRight(mouseX + reticleSize, mouseY - reticleSize);
744− glm::vec2 texCoordTopLeft(0.0f, 0.0f);
745− glm::vec2 texCoordBottomRight(1.0f, 1.0f);
746−
747− DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
748− glm::vec4(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f));
749−
750− }
751−}
752−
753−void ApplicationOverlay::renderPointersOculus() {
754−
755− glEnable(GL_BLEND);
756− glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
757− glEnable(GL_TEXTURE_2D);
758−
759− glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture));
760− glDisable(GL_DEPTH_TEST);
761−
762137 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− }
784138
785− glEnable(GL_DEPTH_TEST);
786− glDisable(GL_TEXTURE_2D);
787− glDisable(GL_BLEND);
139+ renderArgs->_context->syncCache();
140+ fboViewport(_overlayFramebuffer);
788141 }
789142
790−//Renders a small magnification of the currently bound texture at the coordinates
791−void ApplicationOverlay::renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder) {
792− if (!_magnifier) {
793− return;
794− }
795− auto canvasSize = qApp->getCanvasSize();
796−
797− const int widgetWidth = canvasSize.x;
798− const int widgetHeight = canvasSize.y;
799−
800− const float halfWidth = (MAGNIFY_WIDTH / _textureAspectRatio) * sizeMult / 2.0f;
801− const float halfHeight = MAGNIFY_HEIGHT * sizeMult / 2.0f;
802− // Magnification Texture Coordinates
803− const float magnifyULeft = (magPos.x - halfWidth) / (float)widgetWidth;
804− const float magnifyURight = (magPos.x + halfWidth) / (float)widgetWidth;
805− const float magnifyVTop = 1.0f - (magPos.y - halfHeight) / (float)widgetHeight;
806− const float magnifyVBottom = 1.0f - (magPos.y + halfHeight) / (float)widgetHeight;
807−
808− const float newHalfWidth = halfWidth * MAGNIFY_MULT;
809− const float newHalfHeight = halfHeight * MAGNIFY_MULT;
810− //Get yaw / pitch value for the corners
811− const glm::vec2 topLeftYawPitch = overlayToSpherical(glm::vec2(magPos.x - newHalfWidth,
812− magPos.y - newHalfHeight));
813− const glm::vec2 bottomRightYawPitch = overlayToSpherical(glm::vec2(magPos.x + newHalfWidth,
814− magPos.y + newHalfHeight));
815−
816− const glm::vec3 bottomLeft = getPoint(topLeftYawPitch.x, bottomRightYawPitch.y);
817− const glm::vec3 bottomRight = getPoint(bottomRightYawPitch.x, bottomRightYawPitch.y);
818− const glm::vec3 topLeft = getPoint(topLeftYawPitch.x, topLeftYawPitch.y);
819− const glm::vec3 topRight = getPoint(bottomRightYawPitch.x, topLeftYawPitch.y);
820−
821− auto geometryCache = DependencyManager::get<GeometryCache>();
822−
823− if (bottomLeft != _previousMagnifierBottomLeft || bottomRight != _previousMagnifierBottomRight
824− || topLeft != _previousMagnifierTopLeft || topRight != _previousMagnifierTopRight) {
825− QVector<glm::vec3> border;
826− border << topLeft;
827− border << bottomLeft;
828− border << bottomRight;
829− border << topRight;
830− border << topLeft;
831− geometryCache->updateVertices(_magnifierBorder, border, glm::vec4(1.0f, 0.0f, 0.0f, _alpha));
832−
833− _previousMagnifierBottomLeft = bottomLeft;
834− _previousMagnifierBottomRight = bottomRight;
835− _previousMagnifierTopLeft = topLeft;
836− _previousMagnifierTopRight = topRight;
837− }
838−
839− glPushMatrix(); {
840− if (showBorder) {
841− glDisable(GL_TEXTURE_2D);
842− glLineWidth(1.0f);
843− //Outer Line
844− geometryCache->renderVertices(gpu::LINE_STRIP, _magnifierBorder);
845− glEnable(GL_TEXTURE_2D);
846− }
847− glm::vec4 magnifierColor = { 1.0f, 1.0f, 1.0f, _alpha };
848−
849− DependencyManager::get<GeometryCache>()->renderQuad(bottomLeft, bottomRight, topRight, topLeft,
850− glm::vec2(magnifyULeft, magnifyVBottom),
851− glm::vec2(magnifyURight, magnifyVBottom),
852− glm::vec2(magnifyURight, magnifyVTop),
853− glm::vec2(magnifyULeft, magnifyVTop),
854− magnifierColor, _magnifierQuad);
855−
856− } glPopMatrix();
143+void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) {
857144 }
858145
859−const int AUDIO_METER_GAP = 5;
860−const int MUTE_ICON_PADDING = 10;
861−
862−void ApplicationOverlay::renderCameraToggle() {
863− if (Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)) {
864− return;
865− }
866−
867− int audioMeterY;
868− bool smallMirrorVisible = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !qApp->isHMDMode();
869− bool boxed = smallMirrorVisible &&
870− !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror);
871− if (boxed) {
872− audioMeterY = MIRROR_VIEW_HEIGHT + AUDIO_METER_GAP + MUTE_ICON_PADDING;
873− } else {
874− audioMeterY = AUDIO_METER_GAP + MUTE_ICON_PADDING;
875− }
876−
877− DependencyManager::get<CameraToolBox>()->render(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, boxed);
146+void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) {
878147 }
879148
880−void ApplicationOverlay::renderAudioMeter() {
881− auto audio = DependencyManager::get<AudioClient>();
882−
883− // Audio VU Meter and Mute Icon
884− const int MUTE_ICON_SIZE = 24;
885− const int AUDIO_METER_HEIGHT = 8;
886− const int INTER_ICON_GAP = 2;
887−
888− int cameraSpace = 0;
889− int audioMeterWidth = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - MUTE_ICON_PADDING;
890− int audioMeterScaleWidth = audioMeterWidth - 2;
891− int audioMeterX = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_GAP;
892− if (!Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)) {
893− cameraSpace = MUTE_ICON_SIZE + INTER_ICON_GAP;
894− audioMeterWidth -= cameraSpace;
895− audioMeterScaleWidth -= cameraSpace;
896− audioMeterX += cameraSpace;
897− }
898−
899− int audioMeterY;
900− bool smallMirrorVisible = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !qApp->isHMDMode();
901− bool boxed = smallMirrorVisible &&
902− !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror);
903− if (boxed) {
904− audioMeterY = MIRROR_VIEW_HEIGHT + AUDIO_METER_GAP + MUTE_ICON_PADDING;
905− } else {
906− audioMeterY = AUDIO_METER_GAP + MUTE_ICON_PADDING;
907− }
908−
909− const glm::vec4 AUDIO_METER_BLUE = { 0.0, 0.0, 1.0, 1.0 };
910− const glm::vec4 AUDIO_METER_GREEN = { 0.0, 1.0, 0.0, 1.0 };
911− const glm::vec4 AUDIO_METER_RED = { 1.0, 0.0, 0.0, 1.0 };
912− const float CLIPPING_INDICATOR_TIME = 1.0f;
913− const float AUDIO_METER_AVERAGING = 0.5;
914− const float LOG2 = log(2.0f);
915− const float METER_LOUDNESS_SCALE = 2.8f / 5.0f;
916− const float LOG2_LOUDNESS_FLOOR = 11.0f;
917− float audioGreenStart = 0.25f * audioMeterScaleWidth;
918− float audioRedStart = 0.8f * audioMeterScaleWidth;
919− float audioLevel = 0.0f;
920− float loudness = audio->getLastInputLoudness() + 1.0f;
921−
922− _trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.0f - AUDIO_METER_AVERAGING) * loudness;
923− float log2loudness = log(_trailingAudioLoudness) / LOG2;
924−
925− if (log2loudness <= LOG2_LOUDNESS_FLOOR) {
926− audioLevel = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE * audioMeterScaleWidth;
927− } else {
928− audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.0f)) * METER_LOUDNESS_SCALE * audioMeterScaleWidth;
929− }
930− if (audioLevel > audioMeterScaleWidth) {
931− audioLevel = audioMeterScaleWidth;
932− }
933− bool isClipping = ((audio->getTimeSinceLastClip() > 0.0f) && (audio->getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME));
934−
935− DependencyManager::get<AudioToolBox>()->render(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, cameraSpace, boxed);
936−
937− auto canvasSize = qApp->getCanvasSize();
938− DependencyManager::get<AudioScope>()->render(canvasSize.x, canvasSize.y);
939− DependencyManager::get<AudioIOStatsRenderer>()->render(WHITE_TEXT, canvasSize.x, canvasSize.y);
940−
941− audioMeterY += AUDIO_METER_HEIGHT;
942−
943− // Draw audio meter background Quad
944− DependencyManager::get<GeometryCache>()->renderQuad(audioMeterX, audioMeterY, audioMeterWidth, AUDIO_METER_HEIGHT,
945− glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
946−
947− if (audioLevel > audioRedStart) {
948− glm::vec4 quadColor;
949− if (!isClipping) {
950− quadColor = AUDIO_METER_RED;
951− } else {
952− quadColor = glm::vec4(1, 1, 1, 1);
953− }
954− // Draw Red Quad
955− DependencyManager::get<GeometryCache>()->renderQuad(audioMeterX + audioRedStart,
956− audioMeterY,
957− audioLevel - audioRedStart,
958− AUDIO_METER_HEIGHT, quadColor,
959− _audioRedQuad);
960−
961− audioLevel = audioRedStart;
962− }
963−
964− if (audioLevel > audioGreenStart) {
965− glm::vec4 quadColor;
966− if (!isClipping) {
967− quadColor = AUDIO_METER_GREEN;
968− } else {
969− quadColor = glm::vec4(1, 1, 1, 1);
970− }
971− // Draw Green Quad
972− DependencyManager::get<GeometryCache>()->renderQuad(audioMeterX + audioGreenStart,
973− audioMeterY,
974− audioLevel - audioGreenStart,
975− AUDIO_METER_HEIGHT, quadColor,
976− _audioGreenQuad);
977−
978− audioLevel = audioGreenStart;
979− }
980−
981− if (audioLevel >= 0) {
982− glm::vec4 quadColor;
983− if (!isClipping) {
984− quadColor = AUDIO_METER_BLUE;
985− } else {
986− quadColor = glm::vec4(1, 1, 1, 1);
987− }
988− // Draw Blue (low level) quad
989− DependencyManager::get<GeometryCache>()->renderQuad(audioMeterX,
990− audioMeterY,
991− audioLevel, AUDIO_METER_HEIGHT, quadColor,
992− _audioBlueQuad);
993− }
994−}
995−
996−void ApplicationOverlay::renderStatsAndLogs() {
997− Application* application = Application::getInstance();
998− QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
999−
1000− const OctreePacketProcessor& octreePacketProcessor = application->getOctreePacketProcessor();
1001− NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay();
1002−
149+void ApplicationOverlay::renderStatsAndLogs(RenderArgs* renderArgs) {
1003150 // Display stats and log text onscreen
1004− glLineWidth(1.0f);
1005− glPointSize(1.0f);
1006151
1007152 // 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− }
1027153
154+ /*
1028155 // Show on-screen msec timer
1029156 if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) {
1030157 auto canvasSize = qApp->getCanvasSize();
@@ -1036,159 +163,77 @@ void ApplicationOverlay::renderStatsAndLogs() { · 5 unmodified lines
1036163 drawText(canvasSize.x - 100, canvasSize.y - timerBottom,
1037164 0.30f, 0.0f, 0, frameTimer.toUtf8().constData(), WHITE_TEXT);
1038165 }
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();
1039173 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+ */
1040180 }
1041181
1042−void ApplicationOverlay::renderDomainConnectionStatusBorder() {
182+void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderArgs) {
183+ auto geometryCache = DependencyManager::get<GeometryCache>();
184+ static std::once_flag once;
185+ std::call_once(once, [&] {
186+ QVector<vec2> points;
187+ static const float B = 0.99f;
188+ points.push_back(vec2(-B));
189+ points.push_back(vec2(B, -B));
190+ points.push_back(vec2(B));
191+ points.push_back(vec2(-B, B));
192+ points.push_back(vec2(-B));
193+ geometryCache->updateVertices(_domainStatusBorder, points, CONNECTION_STATUS_BORDER_COLOR);
194+ });
1043195 auto nodeList = DependencyManager::get<NodeList>();
1044−
1045196 if (nodeList && !nodeList->getDomainHandler().isConnected()) {
197+ gpu::Batch batch;
1046198 auto geometryCache = DependencyManager::get<GeometryCache>();
1047− auto canvasSize = qApp->getCanvasSize();
1048− if ((int)canvasSize.x != _previousBorderWidth || (int)canvasSize.y != _previousBorderHeight) {
1049− glm::vec4 color(CONNECTION_STATUS_BORDER_COLOR[0],
1050− CONNECTION_STATUS_BORDER_COLOR[1],
1051− CONNECTION_STATUS_BORDER_COLOR[2], 1.0f);
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−}
199+ geometryCache->useSimpleDrawPipeline(batch);
200+ batch.setProjectionTransform(mat4());
201+ batch.setModelTransform(mat4());
202+ batch.setUniformTexture(0, DependencyManager::get<TextureCache>()->getWhiteTexture());
203+ batch._glLineWidth(CONNECTION_STATUS_BORDER_LINE_WIDTH);
1069204
1070−ApplicationOverlay::TexturedHemisphere::TexturedHemisphere() :
1071− _vertices(0),
1072− _indices(0),
1073− _framebufferObject(NULL),
1074− _vbo(0, 0) {
1075−}
1076−
1077−ApplicationOverlay::TexturedHemisphere::~TexturedHemisphere() {
1078− cleanupVBO();
1079− if (_framebufferObject != NULL) {
1080− delete _framebufferObject;
1081− }
1082−}
1083−
1084−void ApplicationOverlay::TexturedHemisphere::bind() {
1085− _framebufferObject->bind();
1086−}
1087−
1088−void ApplicationOverlay::TexturedHemisphere::release() {
1089− _framebufferObject->release();
1090−}
205+ // TODO animate the disconnect border for some excitement while not connected?
206+ //double usecs = usecTimestampNow();
207+ //double secs = usecs / 1000000.0;
208+ //float scaleAmount = 1.0f + (0.01f * sin(secs * 5.0f));
209+ //batch.setModelTransform(glm::scale(mat4(), vec3(scaleAmount)));
1091210
1092−void ApplicationOverlay::TexturedHemisphere::buildVBO(const float fov,
1093− const float aspectRatio,
1094− const int slices,
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− }
211+ geometryCache->renderVertices(batch, gpu::LINE_STRIP, _domainStatusBorder);
212+ renderArgs->_context->syncCache();
213+ renderArgs->_context->render(batch);
1158214 }
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;
1165215 }
1166216
1167−void ApplicationOverlay::TexturedHemisphere::cleanupVBO() {
1168− if (_vbo.first != 0) {
1169− glDeleteBuffers(1, &_vbo.first);
1170− _vbo.first = 0;
1171− }
1172− if (_vbo.second != 0) {
1173− glDeleteBuffers(1, &_vbo.second);
1174− _vbo.second = 0;
217+GLuint ApplicationOverlay::getOverlayTexture() {
218+ if (!_overlayFramebuffer) {
219+ return 0;
1175220 }
221+ return _overlayFramebuffer->texture();
1176222 }
1177223
1178−void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() {
1179− auto canvasSize = qApp->getCanvasSize();
1180− QSize fboSize = QSize(canvasSize.x, canvasSize.y);
1181− if (_framebufferObject != NULL && fboSize == _framebufferObject->size()) {
1182− // Already build
224+void ApplicationOverlay::buildFramebufferObject() {
225+ QSize fboSize = qApp->getDeviceSize();
226+ if (_overlayFramebuffer && fboSize == _overlayFramebuffer->size()) {
227+ // Already built
1183228 return;
1184229 }
1185230
1186− if (_framebufferObject != NULL) {
1187− delete _framebufferObject;
231+ if (_overlayFramebuffer) {
232+ delete _overlayFramebuffer;
1188233 }
1189234
1190− _framebufferObject = new QOpenGLFramebufferObject(fboSize, QOpenGLFramebufferObject::Depth);
1191− glBindTexture(GL_TEXTURE_2D, getTexture());
235+ _overlayFramebuffer = new QOpenGLFramebufferObject(fboSize, QOpenGLFramebufferObject::Depth);
236+ glBindTexture(GL_TEXTURE_2D, getOverlayTexture());
1192237 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1193238 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1194239 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
@@ -1197,104 +242,3 @@ void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() { · 2 unmodified lines
1197242 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
1198243 glBindTexture(GL_TEXTURE_2D, 0);
1199244 }
1200−
1201−//Renders a hemisphere with texture coordinates.
1202−void ApplicationOverlay::TexturedHemisphere::render() {
1203− if (_framebufferObject == NULL || _vbo.first == 0 || _vbo.second == 0) {
1204− qDebug() << "TexturedHemisphere::render(): Incorrect initialisation";
1205− return;
1206− }
1207−
1208− glBindBuffer(GL_ARRAY_BUFFER, _vbo.first);
1209− glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo.second);
1210−
1211− glEnableClientState(GL_VERTEX_ARRAY);
1212− glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1213−
1214− static const int STRIDE = sizeof(TextureVertex);
1215− static const void* VERTEX_POINTER = 0;
1216− static const void* TEX_COORD_POINTER = (void*)sizeof(glm::vec3);
1217− glVertexPointer(3, GL_FLOAT, STRIDE, VERTEX_POINTER);
1218− glTexCoordPointer(2, GL_FLOAT, STRIDE, TEX_COORD_POINTER);
1219−
1220− glDrawRangeElements(GL_TRIANGLES, 0, _vertices - 1, _indices, GL_UNSIGNED_SHORT, 0);
1221−
1222− glDisableClientState(GL_VERTEX_ARRAY);
1223− glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1224−
1225− glBindBuffer(GL_ARRAY_BUFFER, 0);
1226− glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1227−}
1228−
1229−GLuint ApplicationOverlay::TexturedHemisphere::getTexture() {
1230− return _framebufferObject->texture();
1231−}
1232−
1233−glm::vec2 ApplicationOverlay::directionToSpherical(const glm::vec3& direction) {
1234− glm::vec2 result;
1235− // Compute yaw
1236− glm::vec3 normalProjection = glm::normalize(glm::vec3(direction.x, 0.0f, direction.z));
1237− result.x = glm::acos(glm::dot(IDENTITY_FRONT, normalProjection));
1238− if (glm::dot(IDENTITY_RIGHT, normalProjection) > 0.0f) {
1239− result.x = -glm::abs(result.x);
1240− } else {
1241− result.x = glm::abs(result.x);
1242− }
1243− // Compute pitch
1244− result.y = angleBetween(IDENTITY_UP, direction) - PI_OVER_TWO;
1245−
1246− return result;
1247−}
1248−
1249−glm::vec3 ApplicationOverlay::sphericalToDirection(const glm::vec2& sphericalPos) {
1250− glm::quat rotation(glm::vec3(sphericalPos.y, sphericalPos.x, 0.0f));
1251− return rotation * IDENTITY_FRONT;
1252−}
1253−
1254−glm::vec2 ApplicationOverlay::screenToSpherical(const glm::vec2& screenPos) {
1255− auto screenSize = qApp->getCanvasSize();
1256− glm::vec2 result;
1257− result.x = -(screenPos.x / screenSize.x - 0.5f);
1258− result.y = (screenPos.y / screenSize.y - 0.5f);
1259− result.x *= MOUSE_YAW_RANGE;
1260− result.y *= MOUSE_PITCH_RANGE;
1261−
1262− return result;
1263−}
1264−
1265−glm::vec2 ApplicationOverlay::sphericalToScreen(const glm::vec2& sphericalPos) {
1266− glm::vec2 result = sphericalPos;
1267− result.x *= -1.0;
1268− result /= MOUSE_RANGE;
1269− result += 0.5f;
1270− result *= qApp->getCanvasSize();
1271− return result;
1272−}
1273−
1274−glm::vec2 ApplicationOverlay::sphericalToOverlay(const glm::vec2& sphericalPos) const {
1275− glm::vec2 result = sphericalPos;
1276− result.x *= -1.0;
1277− result /= _textureFov;
1278− result.x /= _textureAspectRatio;
1279− result += 0.5f;
1280− result *= qApp->getCanvasSize();
1281− return result;
1282−}
1283−
1284−glm::vec2 ApplicationOverlay::overlayToSpherical(const glm::vec2& overlayPos) const {
1285− glm::vec2 result = overlayPos;
1286− result /= qApp->getCanvasSize();
1287− result -= 0.5f;
1288− result *= _textureFov;
1289− result.x *= _textureAspectRatio;
1290− result.x *= -1.0f;
1291− return result;
1292−}
1293−
1294−glm::vec2 ApplicationOverlay::screenToOverlay(const glm::vec2& screenPos) const {
1295− return sphericalToOverlay(screenToSpherical(screenPos));
1296−}
1297−
1298−glm::vec2 ApplicationOverlay::overlayToScreen(const glm::vec2& overlayPos) const {
1299− return sphericalToScreen(overlayToSpherical(overlayPos));
1300−}
interface/src/ui/ApplicationOverlay.h+14−118
@@ -13,16 +13,8 @@ · 12 unmodified lines
1313 #define hifi_ApplicationOverlay_h
1414
1515 #include <gpu/Texture.h>
16−class Camera;
17−class Overlays;
1816 class QOpenGLFramebufferObject;
1917
20−const float MAGNIFY_WIDTH = 220.0f;
21−const float MAGNIFY_HEIGHT = 100.0f;
22−const float MAGNIFY_MULT = 2.0f;
23−
24−const float DEFAULT_HMD_UI_ANGULAR_SIZE = 72.0f;
25−
2618 // Handles the drawing of the overlays to the screen
2719 // TODO, move divide up the rendering, displaying and input handling
2820 // facilities of this class
@@ -33,122 +25,26 @@ public: · 4 unmodified lines
3325 ~ApplicationOverlay();
3426
3527 void renderOverlay(RenderArgs* renderArgs);
36− void displayOverlayTexture();
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; }
28+ GLuint getOverlayTexture();
4529
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−
6830 private:
69− // Interleaved vertex data
70− struct TextureVertex {
71− glm::vec3 position;
72− glm::vec2 uv;
73− };
74−
75− typedef QPair<GLuint, GLuint> VerticesIndices;
76− class TexturedHemisphere {
77− public:
78− TexturedHemisphere();
79− ~TexturedHemisphere();
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);
31+ void renderStatsAndLogs(RenderArgs* renderArgs);
32+ void renderDomainConnectionStatusBorder(RenderArgs* renderArgs);
33+ void renderRearViewToFbo(RenderArgs* renderArgs);
34+ void renderRearView(RenderArgs* renderArgs);
35+ void renderQmlUi(RenderArgs* renderArgs);
36+ void renderOverlays(RenderArgs* renderArgs);
37+ void buildFramebufferObject();
38+
39+ float _alpha{ 1.0f };
40+ float _trailingAudioLoudness{ 0.0f };
41+ GLuint _uiTexture{ 0 };
11142
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;
14143 int _domainStatusBorder;
14244 int _magnifierBorder;
14345
144− int _previousBorderWidth;
145− int _previousBorderHeight;
146−
147− glm::vec3 _previousMagnifierBottomLeft;
148− glm::vec3 _previousMagnifierBottomRight;
149− glm::vec3 _previousMagnifierTopLeft;
150− glm::vec3 _previousMagnifierTopRight;
151−
46+ ivec2 _previousBorderSize{ -1 };
47+ QOpenGLFramebufferObject* _overlayFramebuffer{ nullptr };
15248 };
15349
15450 #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+
18+HIFI_QML_DEF(AvatarInputs)
19+
20+
21+static AvatarInputs* INSTANCE{ nullptr };
22+static const char SETTINGS_GROUP_NAME[] = "Rear View Tools";
23+static const char ZOOM_LEVEL_SETTINGS[] = "ZoomLevel";
24+
25+static Setting::Handle<int> rearViewZoomLevel(QStringList() << SETTINGS_GROUP_NAME << ZOOM_LEVEL_SETTINGS, 0);
26+
27+AvatarInputs* AvatarInputs::getInstance() {
28+ if (!INSTANCE) {
29+ AvatarInputs::registerType();
30+ AvatarInputs::show();
31+ Q_ASSERT(INSTANCE);
32+ }
33+ return INSTANCE;
34+}
35+
36+AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) {
37+ INSTANCE = this;
38+ int zoomSetting = rearViewZoomLevel.get();
39+ _mirrorZoomed = zoomSetting == 0;
40+}
41+
42+#define AI_UPDATE(name, src) \
43+ { \
44+ auto val = src; \
45+ if (_##name != val) { \
46+ _##name = val; \
47+ emit name##Changed(); \
48+ } \
49+ }
50+
51+#define AI_UPDATE_FLOAT(name, src, epsilon) \
52+ { \
53+ float val = src; \
54+ if (fabs(_##name - val) >= epsilon) { \
55+ _##name = val; \
56+ emit name##Changed(); \
57+ } \
58+ }
59+
60+void AvatarInputs::update() {
61+ if (!Menu::getInstance()) {
62+ return;
63+ }
64+ AI_UPDATE(mirrorVisible, Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !qApp->isHMDMode()
65+ && !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror));
66+ AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking));
67+ AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking));
68+
69+ auto audioIO = DependencyManager::get<AudioClient>();
70+ const float CLIPPING_INDICATOR_TIME = 1.0f;
71+ const float AUDIO_METER_AVERAGING = 0.5;
72+ const float LOG2 = log(2.0f);
73+ const float METER_LOUDNESS_SCALE = 2.8f / 5.0f;
74+ const float LOG2_LOUDNESS_FLOOR = 11.0f;
75+ float audioLevel = 0.0f;
76+ auto audio = DependencyManager::get<AudioClient>();
77+ float loudness = audio->getLastInputLoudness() + 1.0f;
78+
79+ _trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.0f - AUDIO_METER_AVERAGING) * loudness;
80+
81+ float log2loudness = logf(_trailingAudioLoudness) / LOG2;
82+
83+ if (log2loudness <= LOG2_LOUDNESS_FLOOR) {
84+ audioLevel = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE;
85+ } else {
86+ audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.0f)) * METER_LOUDNESS_SCALE;
87+ }
88+ if (audioLevel > 1.0) {
89+ audioLevel = 1.0;
90+ }
91+ AI_UPDATE_FLOAT(audioLevel, audioLevel, 0.01);
92+ AI_UPDATE(audioClipping, ((audioIO->getTimeSinceLastClip() > 0.0f) && (audioIO->getTimeSinceLastClip() < 1.0f)));
93+ AI_UPDATE(audioMuted, audioIO->isMuted());
94+
95+ //// Make muted icon pulsate
96+ //static const float PULSE_MIN = 0.4f;
97+ //static const float PULSE_MAX = 1.0f;
98+ //static const float PULSE_FREQUENCY = 1.0f; // in Hz
99+ //qint64 now = usecTimestampNow();
100+ //if (now - _iconPulseTimeReference > (qint64)USECS_PER_SECOND) {
101+ // // Prevents t from getting too big, which would diminish glm::cos precision
102+ // _iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND);
103+ //}
104+ //float t = (float)(now - _iconPulseTimeReference) / (float)USECS_PER_SECOND;
105+ //float pulseFactor = (glm::cos(t * PULSE_FREQUENCY * 2.0f * PI) + 1.0f) / 2.0f;
106+ //iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor;
107+}
108+
109+void AvatarInputs::toggleCameraMute() {
110+ FaceTracker* faceTracker = Application::getInstance()->getSelectedFaceTracker();
111+ if (faceTracker) {
112+ faceTracker->toggleMute();
113+ }
114+}
115+
116+void AvatarInputs::toggleAudioMute() {
117+ DependencyManager::get<AudioClient>()->toggleMute();
118+}
119+
120+void AvatarInputs::resetSensors() {
121+ qApp->resetSensors();
122+}
123+
124+void AvatarInputs::toggleZoom() {
125+ _mirrorZoomed = !_mirrorZoomed;
126+ rearViewZoomLevel.set(_mirrorZoomed ? 0 : 1);
127+ emit mirrorZoomedChanged();
128+}
129+
130+void AvatarInputs::closeMirror() {
131+ if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
132+ Menu::getInstance()->triggerOption(MenuOption::Mirror);
133+ }
134+}
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) \
17+public: \
18+ type name() { return _##name; }; \
19+private: \
20+ type _##name{ initialValue };
21+
22+class AvatarInputs : public QQuickItem {
23+ Q_OBJECT
24+ HIFI_QML_DECL
25+
26+ AI_PROPERTY(bool, cameraEnabled, false)
27+ AI_PROPERTY(bool, cameraMuted, false)
28+ AI_PROPERTY(bool, audioMuted, false)
29+ AI_PROPERTY(bool, audioClipping, false)
30+ AI_PROPERTY(float, audioLevel, 0)
31+ AI_PROPERTY(bool, mirrorVisible, false)
32+ AI_PROPERTY(bool, mirrorZoomed, true)
33+
34+public:
35+ static AvatarInputs* getInstance();
36+ AvatarInputs(QQuickItem* parent = nullptr);
37+ void update();
38+
39+signals:
40+ void cameraEnabledChanged();
41+ void cameraMutedChanged();
42+ void audioMutedChanged();
43+ void audioClippingChanged();
44+ void audioLevelChanged();
45+ void mirrorVisibleChanged();
46+ void mirrorZoomedChanged();
47+
48+protected:
49+ Q_INVOKABLE void resetSensors();
50+ Q_INVOKABLE void toggleCameraMute();
51+ Q_INVOKABLE void toggleAudioMute();
52+ Q_INVOKABLE void toggleZoom();
53+ Q_INVOKABLE void closeMirror();
54+
55+private:
56+ float _trailingAudioLoudness{ 0 };
57+};
58+
59+#endif // hifi_AvatarInputs_h
interface/src/ui/FlowLayout.cpp+9−0
@@ -38,8 +38,17 @@ · 37 unmodified lines
3838 **
3939 ****************************************************************************/
4040
41+#ifdef __GNUC__
42+#pragma GCC diagnostic push
43+#pragma GCC diagnostic ignored "-Wdouble-promotion"
44+#endif
45+
4146 #include <QtWidgets>
4247
48+#ifdef __GNUC__
49+#pragma GCC diagnostic pop
50+#endif
51+
4352 #include "FlowLayout.h"
4453 //! [1]
4554 FlowLayout::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
5656
5757 this->QDialog::setLayout(form);
5858
59− _wasMoved = false;
60− _previousRect = Application::getInstance()->getWindow()->rect();
6159 Application::getInstance()->getWindow()->activateWindow();
6260
6361 // 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
136134 if (!_inHDMMode) {
137135 _switchModeButton->setText("Leave HMD Mode");
138136 _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−
157137
158138 // if we're on a single screen setup, then hide our tools window when entering HMD mode
159139 if (QApplication::desktop()->screenCount() == 1) {
@@ -161,58 +141,21 @@ void HMDToolsDialog::enterHDMMode() { · 1 unmodified line
161141 }
162142
163143 Application::getInstance()->setEnableVRMode(true);
164−
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−
144+
174145 _inHDMMode = true;
175146 }
176147 }
177148
178−void HMDToolsDialog::activateWindowAfterEnterMode() {
179− Application::getInstance()->getWindow()->activateWindow();
180−
181− // center the cursor on the main application window
182− centerCursorOnWidget(Application::getInstance()->getWindow());
183−}
184−
185149 void HMDToolsDialog::leaveHDMMode() {
186150 if (_inHDMMode) {
187151 _switchModeButton->setText("Enter HMD Mode");
188152 _debugDetails->setText(getDebugDetails());
189−
190153 Application::getInstance()->setEnableVRMode(false);
191− Application::getInstance()->setFullscreen(false);
192154 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;
203155 _inHDMMode = false;
204156 }
205157 }
206158
207−void HMDToolsDialog::moveWindowAfterLeaveMode() {
208− QWindow* mainWindow = Application::getInstance()->getWindow()->windowHandle();
209− mainWindow->setScreen(_previousScreen);
210− mainWindow->setGeometry(_previousRect);
211− Application::getInstance()->getWindow()->activateWindow();
212− Application::getInstance()->resetSensors();
213−}
214−
215−
216159 void HMDToolsDialog::reject() {
217160 // Just regularly close upon ESC
218161 close();
@@ -244,13 +187,15 @@ void HMDToolsDialog::hideEvent(QHideEvent* event) { · 25 unmodified lines
244187
245188 void HMDToolsDialog::aboutToQuit() {
246189 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.
247192 leaveHDMMode();
248193 }
249194 }
250195
251196 void HMDToolsDialog::screenCountChanged(int newCount) {
252197 if (!OculusManager::isConnected()) {
253− OculusManager::connect();
198+ //OculusManager::connect();
254199 }
255200 int hmdScreenNumber = OculusManager::getHMDScreen();
256201
interface/src/ui/HMDToolsDialog.h+0−4
@@ -35,8 +35,6 @@ signals: · 34 unmodified lines
3535 public slots:
3636 void reject();
3737 void switchModeClicked(bool checked);
38− void activateWindowAfterEnterMode();
39− void moveWindowAfterLeaveMode();
4038 void applicationWindowScreenChanged(QScreen* screen);
4139 void aboutToQuit();
4240 void screenCountChanged(int newCount);
@@ -51,8 +49,6 @@ private: · 8 unmodified lines
5149 void enterHDMMode();
5250 void leaveHDMMode();
5351
54− bool _wasMoved;
55− QRect _previousRect;
5652 QScreen* _previousScreen;
5753 QScreen* _hmdScreen;
5854 int _hmdScreenNumber;
interface/src/ui/LogDialog.cpp+9−0
@@ -11,10 +11,19 @@ · 10 unmodified lines
1111
1212 #include "InterfaceConfig.h"
1313
14+#ifdef __GNUC__
15+#pragma GCC diagnostic push
16+#pragma GCC diagnostic ignored "-Wdouble-promotion"
17+#endif
18+
1419 #include <QDesktopWidget>
1520 #include <QTextBlock>
1621 #include <QtGui>
1722
23+#ifdef __GNUC__
24+#pragma GCC diagnostic pop
25+#endif
26+
1827 #include <PathUtils.h>
1928 #include <SharedUtil.h>
2029
interface/src/ui/PreferencesDialog.cpp+2−2
@@ -175,7 +175,7 @@ void PreferencesDialog::loadPreferences() { · 174 unmodified lines
175175
176176 ui.maxOctreePPSSpin->setValue(qApp->getMaxOctreePacketsPerSecond());
177177
178− ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationOverlay().getHmdUIAngularSize());
178+ ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationCompositor().getHmdUIAngularSize());
179179
180180 SixenseManager& sixense = SixenseManager::getInstance();
181181 ui.sixenseReticleMoveSpeedSpin->setValue(sixense.getReticleMoveSpeed());
@@ -239,7 +239,7 @@ void PreferencesDialog::savePreferences() { · 57 unmodified lines
239239
240240 qApp->setMaxOctreePacketsPerSecond(ui.maxOctreePPSSpin->value());
241241
242− qApp->getApplicationOverlay().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value());
242+ qApp->getApplicationCompositor().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value());
243243
244244 SixenseManager& sixense = SixenseManager::getInstance();
245245 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−
24−const int ICON_SIZE = 24;
25−const int ICON_PADDING = 5;
26−
27−const char SETTINGS_GROUP_NAME[] = "Rear View Tools";
28−const char ZOOM_LEVEL_SETTINGS[] = "ZoomLevel";
29−Setting::Handle<int> RearMirrorTools::rearViewZoomLevel(QStringList() << SETTINGS_GROUP_NAME << ZOOM_LEVEL_SETTINGS,
30− ZoomLevel::HEAD);
31−
32−RearMirrorTools::RearMirrorTools(QRect& bounds) :
33− _bounds(bounds),
34− _windowed(false),
35− _fullScreen(false)
36−{
37− _closeTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/close.svg");
38−
39− _zoomHeadTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/plus.svg");
40− _zoomBodyTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/minus.svg");
41−
42− _shrinkIconRect = QRect(ICON_PADDING, ICON_PADDING, ICON_SIZE, ICON_SIZE);
43− _closeIconRect = QRect(_bounds.left() + ICON_PADDING, _bounds.top() + ICON_PADDING, ICON_SIZE, ICON_SIZE);
44− _resetIconRect = QRect(_bounds.width() - ICON_SIZE - ICON_PADDING, _bounds.top() + ICON_PADDING, ICON_SIZE, ICON_SIZE);
45− _bodyZoomIconRect = QRect(_bounds.width() - ICON_SIZE - ICON_PADDING, _bounds.bottom() - ICON_PADDING - ICON_SIZE, ICON_SIZE, ICON_SIZE);
46− _headZoomIconRect = QRect(_bounds.left() + ICON_PADDING, _bounds.bottom() - ICON_PADDING - ICON_SIZE, ICON_SIZE, ICON_SIZE);
47−}
48−
49−void RearMirrorTools::render(RenderArgs* renderArgs, bool fullScreen, const QPoint & mousePosition) {
50− if (fullScreen) {
51− _fullScreen = true;
52− displayIcon(QRect(QPoint(), qApp->getDeviceSize()), _shrinkIconRect, _closeTexture);
53− } else {
54− // render rear view tools if mouse is in the bounds
55− _windowed = _bounds.contains(mousePosition);
56− if (_windowed) {
57− displayIcon(_bounds, _closeIconRect, _closeTexture);
58−
59− ZoomLevel zoomLevel = (ZoomLevel)rearViewZoomLevel.get();
60− displayIcon(_bounds, _headZoomIconRect, _zoomHeadTexture, zoomLevel == HEAD);
61− displayIcon(_bounds, _bodyZoomIconRect, _zoomBodyTexture, zoomLevel == BODY);
62− }
63− }
64−}
65−
66−bool RearMirrorTools::mousePressEvent(int x, int y) {
67− if (_windowed) {
68− if (_closeIconRect.contains(x, y)) {
69− _windowed = false;
70− emit closeView();
71− return true;
72− }
73−
74− if (_headZoomIconRect.contains(x, y)) {
75− rearViewZoomLevel.set(HEAD);
76− return true;
77− }
78−
79− if (_bodyZoomIconRect.contains(x, y)) {
80− rearViewZoomLevel.set(BODY);
81− return true;
82− }
83−
84− if (_bounds.contains(x, y)) {
85− _windowed = false;
86− emit restoreView();
87− return true;
88− }
89− }
90−
91− if (_fullScreen) {
92− if (_shrinkIconRect.contains(x, y)) {
93− _fullScreen = false;
94− emit shrinkView();
95− return true;
96− }
97− }
98− return false;
99−}
100−
101−void RearMirrorTools::displayIcon(QRect bounds, QRect iconBounds, const gpu::TexturePointer& texture, bool selected) {
102−
103− glMatrixMode(GL_PROJECTION);
104− glPushMatrix();
105− glLoadIdentity();
106−
107− glOrtho(bounds.left(), bounds.right(), bounds.bottom(), bounds.top(), -1.0, 1.0);
108− glDisable(GL_DEPTH_TEST);
109− glDisable(GL_LIGHTING);
110− glEnable(GL_TEXTURE_2D);
111−
112− glm::vec4 quadColor;
113− if (selected) {
114− quadColor = glm::vec4(.5f, .5f, .5f, 1.0f);
115− } else {
116− quadColor = glm::vec4(1, 1, 1, 1);
117− }
118−
119− glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(texture));
120−
121− glm::vec2 topLeft(iconBounds.left(), iconBounds.top());
122− glm::vec2 bottomRight(iconBounds.right(), iconBounds.bottom());
123− static const glm::vec2 texCoordTopLeft(0.0f, 1.0f);
124− static const glm::vec2 texCoordBottomRight(1.0f, 0.0f);
125−
126− DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, quadColor);
127−
128− glPopMatrix();
129−
130− glMatrixMode(GL_MODELVIEW);
131−
132− glBindTexture(GL_TEXTURE_2D, 0);
133− glDisable(GL_TEXTURE_2D);
134−}
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−
18−enum ZoomLevel {
19− HEAD = 0,
20− BODY = 1
21−};
22−
23−class RearMirrorTools : public QObject {
24− Q_OBJECT
25−public:
26− RearMirrorTools(QRect& bounds);
27− void render(RenderArgs* renderArgs, bool fullScreen, const QPoint & mousePos);
28− bool mousePressEvent(int x, int y);
29−
30− static Setting::Handle<int> rearViewZoomLevel;
31−
32−signals:
33− void closeView();
34− void shrinkView();
35− void resetView();
36− void restoreView();
37−
38−private:
39− QRect _bounds;
40− gpu::TexturePointer _closeTexture;
41− gpu::TexturePointer _zoomBodyTexture;
42− gpu::TexturePointer _zoomHeadTexture;
43−
44− QRect _closeIconRect;
45− QRect _resetIconRect;
46− QRect _shrinkIconRect;
47− QRect _headZoomIconRect;
48− QRect _bodyZoomIconRect;
49−
50− bool _windowed;
51− bool _fullScreen;
52−
53− void displayIcon(QRect bounds, QRect iconBounds, const gpu::TexturePointer& texture, bool selected = false);
54−};
55−
56−#endif // hifi_RearMirrorTools_h
interface/src/ui/Stats.cpp+221−466
@@ -1,17 +1,17 @@
11 //
2−// Stats.cpp
3−// interface/src/ui
4−//
5−// Created by Lucas Crisman on 22/03/14.
2+// Created by Bradley Austin Davis 2015/06/17
63 // Copyright 2013 High Fidelity, Inc.
74 //
85 // Distributed under the Apache License, Version 2.0.
96 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
107 //
118
12−#include <sstream>
9+#include <gpu/GPUConfig.h>
1310
14−#include <stdlib.h>
11+#include "Stats.h"
12+
13+#include <sstream>
14+#include <QFontDatabase>
1515
1616 #include <glm/glm.hpp>
1717 #include <glm/gtx/component_wise.hpp>
@@ -25,147 +25,31 @@ · 7 unmodified lines
2525 #include <LODManager.h>
2626 #include <PerfStat.h>
2727
28−#include "Stats.h"
2928 #include "BandwidthRecorder.h"
3029 #include "InterfaceConfig.h"
3130 #include "Menu.h"
3231 #include "Util.h"
3332 #include "SequenceNumberStats.h"
3433
35−using namespace std;
34+HIFI_QML_DEF(Stats)
3635
37−const int STATS_PELS_PER_LINE = 16;
38−const int STATS_PELS_INITIALOFFSET = 12;
36+using namespace std;
3937
40−const int STATS_GENERAL_MIN_WIDTH = 165;
41−const int STATS_PING_MIN_WIDTH = 190;
42−const int STATS_GEO_MIN_WIDTH = 240;
43−const int STATS_OCTREE_MIN_WIDTH = 410;
38+static Stats* INSTANCE{ nullptr };
4439
4540 Stats* Stats::getInstance() {
46− static Stats stats;
47− return &stats;
48−}
49−
50−Stats::Stats():
51− _expanded(false),
52− _recentMaxPackets(0),
53− _resetRecentMaxPacketsSoon(true),
54− _generalStatsWidth(STATS_GENERAL_MIN_WIDTH),
55− _pingStatsWidth(STATS_PING_MIN_WIDTH),
56− _geoStatsWidth(STATS_GEO_MIN_WIDTH),
57− _octreeStatsWidth(STATS_OCTREE_MIN_WIDTH),
58− _lastHorizontalOffset(0)
59−{
60− auto canvasSize = Application::getInstance()->getCanvasSize();
61− resetWidth(canvasSize.x, 0);
62−}
63−
64−void Stats::toggleExpanded() {
65− _expanded = !_expanded;
66−}
67−
68−// called on mouse click release
69−// check for clicks over stats in order to expand or contract them
70−void Stats::checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseDragStartedY, int horizontalOffset) {
71− auto canvasSize = Application::getInstance()->getCanvasSize();
72−
73− if (0 != glm::compMax(glm::abs(glm::ivec2(mouseX - mouseDragStartedX, mouseY - mouseDragStartedY)))) {
74− // not worried about dragging on stats
75− return;
76− }
77−
78− int statsHeight = 0,
79− statsWidth = 0,
80− statsX = 0,
81− statsY = 0,
82− lines = 0;
83−
84− statsX = horizontalOffset;
85−
86− // top-left stats click
87− lines = _expanded ? 5 : 3;
88− statsHeight = lines * STATS_PELS_PER_LINE + 10;
89− if (mouseX > statsX && mouseX < statsX + _generalStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) {
90− toggleExpanded();
91− return;
92− }
93− statsX += _generalStatsWidth;
94−
95− // ping stats click
96− if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
97− lines = _expanded ? 4 : 3;
98− statsHeight = lines * STATS_PELS_PER_LINE + 10;
99− if (mouseX > statsX && mouseX < statsX + _pingStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) {
100− toggleExpanded();
101− return;
102− }
103− statsX += _pingStatsWidth;
104− }
105−
106− // geo stats panel click
107− lines = _expanded ? 4 : 3;
108− statsHeight = lines * STATS_PELS_PER_LINE + 10;
109− if (mouseX > statsX && mouseX < statsX + _geoStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) {
110− toggleExpanded();
111− return;
112− }
113− statsX += _geoStatsWidth;
114−
115− // top-right stats click
116− lines = _expanded ? 11 : 3;
117− statsHeight = lines * STATS_PELS_PER_LINE + 10;
118− statsWidth = canvasSize.x - statsX;
119− if (mouseX > statsX && mouseX < statsX + statsWidth && mouseY > statsY && mouseY < statsY + statsHeight) {
120− toggleExpanded();
121− return;
122− }
123−}
124−
125−void Stats::resetWidth(int width, int horizontalOffset) {
126− auto canvasSize = Application::getInstance()->getCanvasSize();
127− int extraSpace = canvasSize.x - horizontalOffset - 2
128− - STATS_GENERAL_MIN_WIDTH
129− - (Menu::getInstance()->isOptionChecked(MenuOption::TestPing) ? STATS_PING_MIN_WIDTH -1 : 0)
130− - STATS_GEO_MIN_WIDTH
131− - STATS_OCTREE_MIN_WIDTH;
132−
133− int panels = 4;
134−
135− _generalStatsWidth = STATS_GENERAL_MIN_WIDTH;
136− if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
137− _pingStatsWidth = STATS_PING_MIN_WIDTH;
138− } else {
139− _pingStatsWidth = 0;
140− panels = 3;
141− }
142− _geoStatsWidth = STATS_GEO_MIN_WIDTH;
143− _octreeStatsWidth = STATS_OCTREE_MIN_WIDTH;
144−
145− if (extraSpace > panels) {
146− _generalStatsWidth += (int) extraSpace / panels;
147− if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
148− _pingStatsWidth += (int) extraSpace / panels;
149− }
150− _geoStatsWidth += (int) extraSpace / panels;
151− _octreeStatsWidth += canvasSize.x -
152− (_generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3);
41+ if (!INSTANCE) {
42+ Stats::registerType();
43+ Stats::show();
44+ Q_ASSERT(INSTANCE);
15345 }
46+ return INSTANCE;
15447 }
15548
156−
157−// translucent background box that makes stats more readable
158−void Stats::drawBackground(unsigned int rgba, int x, int y, int width, int height) {
159− glm::vec4 color(((rgba >> 24) & 0xff) / 255.0f,
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);
49+Stats::Stats(QQuickItem* parent) : QQuickItem(parent) {
50+ INSTANCE = this;
51+ const QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
52+ _monospaceFont = font.family();
16953 }
17054
17155 bool Stats::includeTimingRecord(const QString& name) {
@@ -190,161 +74,70 @@ bool Stats::includeTimingRecord(const QString& name) { · 18 unmodified lines
19074 return false;
19175 }
19276
193−// display expanded or contracted stats
194−void Stats::display(
195− const float* color,
196− int horizontalOffset,
197− float fps,
198− int inPacketsPerSecond,
199− int outPacketsPerSecond,
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;
77+#define STAT_UPDATE(name, src) \
78+ { \
79+ auto val = src; \
80+ if (_##name != val) { \
81+ _##name = val; \
82+ emit name##Changed(); \
83+ } \
22084 }
22185
222− glPointSize(1.0f);
223−
224− // we need to take one avatar out so we don't include ourselves
225− int totalAvatars = DependencyManager::get<AvatarManager>()->size() - 1;
226− int totalServers = DependencyManager::get<NodeList>()->size();
227−
228− lines = 5;
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− }
86+#define STAT_UPDATE_FLOAT(name, src, epsilon) \
87+ { \
88+ float val = src; \
89+ if (fabs(_##name - val) >= epsilon) { \
90+ _##name = val; \
91+ emit name##Changed(); \
92+ } \
25493 }
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);
28194
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);
29795
298− while (i.hasNext()) {
299− i.next();
300− if (includeTimingRecord(i.key())) {
301− float averageTime = (float)i.value().getMovingAverage() / (float)USECS_PER_MSEC;
302− sortedRecords.insertMulti(averageTime, i.key());
303− }
96+void Stats::updateStats() {
97+ if (!Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
98+ if (isVisible()) {
99+ setVisible(false);
304100 }
305−
306− int linesDisplayed = 0;
307− QMapIterator<float, QString> j(sortedRecords);
308− j.toBack();
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− }
101+ return;
102+ } else {
103+ if (!isVisible()) {
104+ setVisible(true);
326105 }
327106 }
328107
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+ }
329113
330− verticalOffset = STATS_PELS_INITIALOFFSET;
331− horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + 1;
332114
333− if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
334− int pingAudio = -1, pingAvatar = -1, pingVoxel = -1, pingOctreeMax = -1;
115+ auto nodeList = DependencyManager::get<NodeList>();
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);
335127
336− auto nodeList = DependencyManager::get<NodeList>();
128+ // Second column: ping
129+ if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
337130 SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
338131 SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer);
132+ STAT_UPDATE(audioPing, audioMixerNode ? audioMixerNode->getPingMs() : -1);
133+ STAT_UPDATE(avatarPing, avatarMixerNode ? avatarMixerNode->getPingMs() : -1);
339134
340− pingAudio = audioMixerNode ? audioMixerNode->getPingMs() : -1;
341− pingAvatar = avatarMixerNode ? avatarMixerNode->getPingMs() : -1;
342−
343− // Now handle voxel servers, since there could be more than one, we average their ping times
135+ //// Now handle voxel servers, since there could be more than one, we average their ping times
344136 unsigned long totalPingOctree = 0;
345137 int octreeServerCount = 0;
346−
347− nodeList->eachNode([&totalPingOctree, &pingOctreeMax, &octreeServerCount](const SharedNodePointer& node){
138+ int pingOctreeMax = 0;
139+ int pingVoxel;
140+ nodeList->eachNode([&](const SharedNodePointer& node) {
348141 // TODO: this should also support entities
349142 if (node->getType() == NodeType::EntityServer) {
350143 totalPingOctree += node->getPingMs();
@@ -359,168 +152,85 @@ void Stats::display( · 8 unmodified lines
359152 pingVoxel = totalPingOctree / octreeServerCount;
360153 }
361154
362− lines = _expanded ? 4 : 3;
363−
364− // only draw our background if column one didn't draw a wide background
365− if (columnOneWidth == _generalStatsWidth) {
366− drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, (lines + 1) * STATS_PELS_PER_LINE);
367− }
368− horizontalOffset += 5;
369−
370−
371− QString audioPing;
372− if (pingAudio >= 0) {
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;
155+ //STAT_UPDATE(entitiesPing, pingVoxel);
156+ //if (_expanded) {
157+ // QString voxelMaxPing;
158+ // if (pingVoxel >= 0) { // Average is only meaningful if pingVoxel is valid.
159+ // voxelMaxPing = QString("Voxel max ping: %1").arg(pingOctreeMax);
160+ // } else {
161+ // voxelMaxPing = QString("Voxel max ping: --");
162+ // }
163+ } else {
164+ // -2 causes the QML to hide the ping column
165+ STAT_UPDATE(audioPing, -2);
412166 }
413167
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);
437168
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);
438175 if (_expanded) {
439− SharedNodePointer avatarMixer = DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::AvatarMixer);
176+ SharedNodePointer avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer);
440177 if (avatarMixer) {
441− avatarMixerStats = QString("Avatar Mixer: %1 kbps, %2 pps").
442− arg(roundf(bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) +
443− bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer))).
444− arg(roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) +
445− bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer)));
178+ STAT_UPDATE(avatarMixerKbps, roundf(
179+ bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AvatarMixer) +
180+ bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AvatarMixer)));
181+ STAT_UPDATE(avatarMixerPps, roundf(
182+ bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AvatarMixer) +
183+ bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AvatarMixer)));
446184 } else {
447− avatarMixerStats = QString("No Avatar Mixer");
185+ STAT_UPDATE(avatarMixerKbps, -1);
186+ STAT_UPDATE(avatarMixerPps, -1);
448187 }
449−
450− verticalOffset += STATS_PELS_PER_LINE;
451− drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarMixerStats.toUtf8().constData(), color);
452−
453− stringstream downloads;
454− downloads << "Downloads: ";
455− foreach (Resource* resource, ResourceCache::getLoadingRequests()) {
456− downloads << (int)(resource->getProgress() * 100.0f) << "% ";
188+ SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
189+ if (audioMixerNode) {
190+ STAT_UPDATE(audioMixerKbps, roundf(
191+ bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) +
192+ bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer)));
193+ STAT_UPDATE(audioMixerPps, roundf(
194+ bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) +
195+ bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer)));
196+ } else {
197+ STAT_UPDATE(audioMixerKbps, -1);
198+ STAT_UPDATE(audioMixerPps, -1);
457199 }
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− }
463200
464− verticalOffset = STATS_PELS_INITIALOFFSET;
465− horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3;
466−
467− lines = _expanded ? 10 : 3;
468−
469− drawBackground(backgroundColor, horizontalOffset, 0, canvasSize.x - horizontalOffset,
470− (lines + 1) * STATS_PELS_PER_LINE);
471− horizontalOffset += 5;
472−
473− // Model/Entity render details
474− octreeStats.str("");
475− octreeStats << "Triangles: " << _renderDetails._trianglesRendered
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: [";
201+ STAT_UPDATE(downloads, ResourceCache::getLoadingRequests().size());
202+ STAT_UPDATE(downloadsPending, ResourceCache::getPendingRequestCount());
203+ // TODO fix to match original behavior
204+ //stringstream downloads;
205+ //downloads << "Downloads: ";
206+ //foreach(Resource* resource, ) {
207+ // downloads << (int)(resource->getProgress() * 100.0f) << "% ";
208+ //}
209+ //downloads << "(" << << " pending)";
210+ } // expanded avatar column
211+
212+ // Fourth column, octree stats
505213 int serverCount = 0;
506214 int movingServerCount = 0;
507215 unsigned long totalNodes = 0;
508216 unsigned long totalInternal = 0;
509217 unsigned long totalLeaves = 0;
218+ std::stringstream sendingModeStream("");
219+ sendingModeStream << "[";
510220 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++) {
512222 //const QUuid& uuid = i->first;
513223 OctreeSceneStats& stats = i->second;
514224 serverCount++;
515225 if (_expanded) {
516226 if (serverCount > 1) {
517− sendingMode << ",";
227+ sendingModeStream << ",";
518228 }
519229 if (stats.isMoving()) {
520− sendingMode << "M";
230+ sendingModeStream << "M";
521231 movingServerCount++;
522232 } else {
523− sendingMode << "S";
233+ sendingModeStream << "S";
524234 }
525235 }
526236
@@ -528,32 +238,35 @@ void Stats::display( · 1 unmodified line
528238 totalNodes += stats.getTotalElements();
529239 if (_expanded) {
530240 totalInternal += stats.getTotalInternal();
531− totalLeaves += stats.getTotalLeaves();
241+ totalLeaves += stats.getTotalLeaves();
532242 }
533243 }
534244 if (_expanded) {
535245 if (serverCount == 0) {
536− sendingMode << "---";
246+ sendingModeStream << "---";
537247 }
538− sendingMode << "] " << serverCount << " servers";
248+ sendingModeStream << "] " << serverCount << " servers";
539249 if (movingServerCount > 0) {
540− sendingMode << " <SCENE NOT STABLE>";
250+ sendingModeStream << " <SCENE NOT STABLE>";
541251 } else {
542− sendingMode << " <SCENE STABLE>";
252+ sendingModeStream << " <SCENE STABLE>";
543253 }
544− verticalOffset += STATS_PELS_PER_LINE;
545− drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)sendingMode.str().c_str(), color);
254+ QString sendingModeResult = sendingModeStream.str().c_str();
255+ STAT_UPDATE(sendingMode, sendingModeResult);
546256 }
547257
548258 // Incoming packets
259+ QLocale locale(QLocale::English);
260+ auto voxelPacketsToProcess = qApp->getOctreePacketProcessor().packetsToProcessCount();
549261 if (_expanded) {
550− octreeStats.str("");
262+ std::stringstream octreeStats;
551263 QString packetsString = locale.toString((int)voxelPacketsToProcess);
552264 QString maxString = locale.toString((int)_recentMaxPackets);
553265 octreeStats << "Octree Packets to Process: " << qPrintable(packetsString)
554− << " [Recent Max: " << qPrintable(maxString) << "]";
555− verticalOffset += STATS_PELS_PER_LINE;
556− drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
266+ << " [Recent Max: " << qPrintable(maxString) << "]";
267+ QString str = octreeStats.str().c_str();
268+ STAT_UPDATE(packetStats, str);
269+ // drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
557270 }
558271
559272 if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) {
@@ -562,60 +275,102 @@ void Stats::display( · 2 unmodified lines
562275 }
563276 if (voxelPacketsToProcess == 0) {
564277 _resetRecentMaxPacketsSoon = true;
565− } else {
566− if (voxelPacketsToProcess > _recentMaxPackets) {
567− _recentMaxPackets = voxelPacketsToProcess;
568− }
278+ } else if (voxelPacketsToProcess > _recentMaxPackets) {
279+ _recentMaxPackets = voxelPacketsToProcess;
569280 }
570281
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−
575282 // Server Octree Elements
576− if (!_expanded) {
577− octreeStats.str("");
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− }
283+ STAT_UPDATE(serverElements, totalNodes);
284+ STAT_UPDATE(localElements, OctreeElement::getNodeCount());
583285
584286 if (_expanded) {
585− octreeStats.str("");
586− octreeStats << "Octree Elements -";
587− verticalOffset += STATS_PELS_PER_LINE;
588− drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
287+ STAT_UPDATE(serverInternal, totalInternal);
288+ STAT_UPDATE(serverLeaves, totalLeaves);
289+ // Local Voxels
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+ }
589295
590− QString serversInternalString = locale.toString((uint)totalInternal);
591− QString serversLeavesString = locale.toString((uint)totalLeaves);
296+ bool performanceTimerIsActive = PerformanceTimer::isActive();
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
592304
593− octreeStats.str("");
594− octreeStats << " Server: " << qPrintable(serversTotalString) <<
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);
305+ // we will also include room for 1 line per timing record and a header of 4 lines
306+ // Timing details...
599307
600− // Local Voxels
601− unsigned long localInternal = OctreeElement::getInternalNodeCount();
602− unsigned long localLeaves = OctreeElement::getLeafNodeCount();
603− QString localInternalString = locale.toString((uint)localInternal);
604− QString localLeavesString = locale.toString((uint)localLeaves);
605−
606− octreeStats.str("");
607− octreeStats << " Local: " << qPrintable(serversTotalString) <<
608− " Internal: " << qPrintable(localInternalString) <<
609− " Leaves: " << qPrintable(localLeavesString) << "";
610− verticalOffset += STATS_PELS_PER_LINE;
611− drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color);
308+ // First iterate all the records, and for the ones that should be included, insert them into
309+ // a new Map sorted by average time...
310+ bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen);
311+ QMap<float, QString> sortedRecords;
312+ const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
313+ QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
314+
315+ while (i.hasNext()) {
316+ i.next();
317+ if (includeTimingRecord(i.key())) {
318+ float averageTime = (float)i.value().getMovingAverage() / (float)USECS_PER_MSEC;
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();
612346 }
347+}
348+
349+void Stats::setRenderDetails(const RenderDetails& details) {
350+ STAT_UPDATE(triangles, details._trianglesRendered);
351+ STAT_UPDATE(quads, details._quadsRendered);
352+ STAT_UPDATE(materialSwitches, details._materialSwitches);
353+ if (_expanded) {
354+ STAT_UPDATE(meshOpaque, details._opaque._rendered);
355+ STAT_UPDATE(meshTranslucent, details._opaque._rendered);
356+ STAT_UPDATE(opaqueConsidered, details._opaque._considered);
357+ STAT_UPDATE(opaqueOutOfView, details._opaque._outOfView);
358+ STAT_UPDATE(opaqueTooSmall, details._opaque._tooSmall);
359+ STAT_UPDATE(translucentConsidered, details._translucent._considered);
360+ STAT_UPDATE(translucentOutOfView, details._translucent._outOfView);
361+ STAT_UPDATE(translucentTooSmall, details._translucent._tooSmall);
362+ }
363+}
364+
365+
366+/*
367+// display expanded or contracted stats
368+void Stats::display(
369+ int voxelPacketsToProcess)
370+{
371+ // iterate all the current voxel stats, and list their sending modes, and total voxel counts
613372
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);
620373 }
621374
375+
376+*/
interface/src/ui/Stats.h+121−32
@@ -1,8 +1,5 @@
11 //
2−// Stats.h
3−// interface/src/ui
4−//
5−// Created by Lucas Crisman on 22/03/14.
2+// Created by Bradley Austin Davis 2015/06/17
63 // Copyright 2013 High Fidelity, Inc.
74 //
85 // Distributed under the Apache License, Version 2.0.
@@ -13,47 +10,139 @@ · 4 unmodified lines
1310 #define hifi_Stats_h
1411
1512 #include <QObject>
13+#include <QQuickItem>
14+#include <QVector3D>
1615
16+#include <OffscreenUi.h>
1717 #include <RenderArgs.h>
1818
19−class Stats: public QObject {
19+#define STATS_PROPERTY(type, name, initialValue) \
20+ Q_PROPERTY(type name READ name NOTIFY name##Changed) \
21+public: \
22+ type name() { return _##name; }; \
23+private: \
24+ type _##name{ initialValue };
25+
26+
27+class Stats : public QQuickItem {
2028 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)
2174
2275 public:
2376 static Stats* getInstance();
2477
25− Stats();
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);
78+ Stats(QQuickItem* parent = nullptr);
3679 bool includeTimingRecord(const QString& name);
37−
38− void setRenderDetails(const RenderDetails& details) { _renderDetails = details; }
39−
40−private:
41− static Stats* _sharedInstance;
80+ void setRenderDetails(const RenderDetails& details);
81+ const QString& monospaceFont() {
82+ return _monospaceFont;
83+ }
84+ void updateStats();
4285
43− bool _expanded;
86+ bool isExpanded() { return _expanded; }
87+ bool isTimingExpanded() { return _timingExpanded; }
4488
45− int _recentMaxPackets; // recent max incoming voxel packets to process
46− bool _resetRecentMaxPacketsSoon;
89+ void setExpanded(bool expanded) {
90+ if (_expanded != expanded) {
91+ _expanded = expanded;
92+ emit expandedChanged();
93+ }
94+ }
4795
48− int _generalStatsWidth;
49− int _bandwidthStatsWidth;
50− int _pingStatsWidth;
51− int _geoStatsWidth;
52− int _octreeStatsWidth;
96+signals:
97+ void expandedChanged();
98+ void timingExpandedChanged();
99+ void serverCountChanged();
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();
53139
54− int _lastHorizontalOffset;
55−
56− RenderDetails _renderDetails;
140+private:
141+ int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process
142+ bool _resetRecentMaxPacketsSoon{ true };
143+ bool _expanded{ false };
144+ bool _timingExpanded{ false };
145+ QString _monospaceFont;
57146 };
58147
59148 #endif // hifi_Stats_h
interface/src/ui/UserInputMapper.cpp+1−1
@@ -53,7 +53,7 @@ void UserInputMapper::resetDevice(uint16 deviceID) { · 52 unmodified lines
5353
5454 int UserInputMapper::findDevice(QString name) {
5555 for (auto device : _registeredDevices) {
56− if (device.second->_name == name) {
56+ if (device.second->_name.split(" (")[0] == name) {
5757 return device.first;
5858 }
5959 }
interface/src/ui/overlays/Circle3DOverlay.cpp+161−181
@@ -11,6 +11,7 @@ · 10 unmodified lines
1111 // include this before QGLWidget, which includes an earlier version of OpenGL
1212 #include "InterfaceConfig.h"
1313
14+#include <DeferredLightingEffect.h>
1415 #include <GeometryCache.h>
1516 #include <GlowEffect.h>
1617 #include <SharedUtil.h>
@@ -75,20 +76,20 @@ void Circle3DOverlay::render(RenderArgs* args) { · 58 unmodified lines
7576
7677 float alpha = getAlpha();
7778
78− if (alpha == 0.0) {
79+ if (alpha == 0.0f) {
7980 return; // do nothing if our alpha is 0, we're not visible
8081 }
81−
82+
8283 // Create the circle in the coordinates origin
8384 float outerRadius = getOuterRadius();
8485 float innerRadius = getInnerRadius(); // only used in solid case
8586 float startAt = getStartAt();
8687 float endAt = getEndAt();
87−
88+
8889 bool geometryChanged = (startAt != _lastStartAt || endAt != _lastEndAt ||
8990 innerRadius != _lastInnerRadius || outerRadius != _lastOuterRadius);
9091
91−
92+
9293 const float FULL_CIRCLE = 360.0f;
9394 const float SLICES = 180.0f; // The amount of segment to create the circle
9495 const float SLICE_ANGLE = FULL_CIRCLE / SLICES;
@@ -101,195 +102,178 @@ void Circle3DOverlay::render(RenderArgs* args) { · 6 unmodified lines
101102 bool colorChanged = colorX.red != _lastColor.red || colorX.green != _lastColor.green || colorX.blue != _lastColor.blue;
102103 _lastColor = colorX;
103104
104− glDisable(GL_LIGHTING);
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);
105+ auto geometryCache = DependencyManager::get<GeometryCache>();
125106
126− glLineWidth(_lineWidth);
127−
128− auto geometryCache = DependencyManager::get<GeometryCache>();
107+ Transform transform;
108+ transform.setTranslation(getCenter());
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) {
129126
130− // for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise
131− // we just draw a line...
132− if (getIsSolid()) {
133− if (_quadVerticesID == GeometryCache::UNKNOWN_ID) {
134− _quadVerticesID = geometryCache->allocateID();
135− }
127+ QVector<glm::vec2> points;
128+
129+ float angle = startAt;
130+ float angleInRadians = glm::radians(angle);
131+ glm::vec2 firstInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius);
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);
136140
137− if (geometryChanged || colorChanged) {
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;
141+ points << thisOuterPoint << thisInnerPoint;
154142
155− angle += SLICE_ANGLE;
156− }
143+ angle += SLICE_ANGLE;
144+ }
157145
158− // get the last slice portion....
159− angle = endAt;
160− angleInRadians = glm::radians(angle);
161− glm::vec2 lastInnerPoint(cos(angleInRadians) * innerRadius, sin(angleInRadians) * innerRadius);
162− glm::vec2 lastOuterPoint(cos(angleInRadians) * outerRadius, sin(angleInRadians) * outerRadius);
146+ // get the last slice portion....
147+ angle = endAt;
148+ angleInRadians = glm::radians(angle);
149+ glm::vec2 lastInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius);
150+ glm::vec2 lastOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
163151
164− 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− }
152+ points << lastOuterPoint << lastInnerPoint;
197153
198− // get the last slice portion....
199− angle = endAt;
200− angleInRadians = glm::radians(angle);
201− glm::vec2 lastPoint(cos(angleInRadians) * outerRadius, sin(angleInRadians) * outerRadius);
202− points << lastPoint;
203−
204− geometryCache->updateVertices(_lineVerticesID, points, color);
205− }
206−
154+ geometryCache->updateVertices(_quadVerticesID, points, color);
155+ }
156+
157+ geometryCache->renderVertices(batch, gpu::QUAD_STRIP, _quadVerticesID);
158+
159+ } else {
160+ if (_lineVerticesID == GeometryCache::UNKNOWN_ID) {
161+ _lineVerticesID = geometryCache->allocateID();
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+
207178 if (getIsDashedLine()) {
208− geometryCache->renderVertices(gpu::LINES, _lineVerticesID);
209− } else {
210− geometryCache->renderVertices(gpu::LINE_STRIP, _lineVerticesID);
179+ angle += SLICE_ANGLE / 2.0f; // short gap
180+ angleInRadians = glm::radians(angle);
181+ glm::vec2 dashStartPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
182+ points << dashStartPoint;
211183 }
212184 }
213185
214− // draw our tick marks
215− // for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise
216− // we just draw a line...
217− if (getHasTickMarks()) {
218−
219− if (_majorTicksVerticesID == GeometryCache::UNKNOWN_ID) {
220− _majorTicksVerticesID = geometryCache->allocateID();
221− }
222− if (_minorTicksVerticesID == GeometryCache::UNKNOWN_ID) {
223− _minorTicksVerticesID = geometryCache->allocateID();
224− }
225−
226− if (geometryChanged) {
227− QVector<glm::vec2> majorPoints;
228− QVector<glm::vec2> minorPoints;
229−
230− // draw our major tick marks
231− if (getMajorTickMarksAngle() > 0.0f && getMajorTickMarksLength() != 0.0f) {
186+ // get the last slice portion....
187+ angle = endAt;
188+ angleInRadians = glm::radians(angle);
189+ glm::vec2 lastPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
190+ points << lastPoint;
191+
192+ geometryCache->updateVertices(_lineVerticesID, points, color);
193+ }
194+
195+ if (getIsDashedLine()) {
196+ geometryCache->renderVertices(batch, gpu::LINES, _lineVerticesID);
197+ } else {
198+ geometryCache->renderVertices(batch, gpu::LINE_STRIP, _lineVerticesID);
199+ }
200+ }
201+
202+ // draw our tick marks
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) {
232220
233− float tickMarkAngle = getMajorTickMarksAngle();
234− float angle = startAt - fmod(startAt, tickMarkAngle) + tickMarkAngle;
235− float angleInRadians = glm::radians(angle);
236− float tickMarkLength = getMajorTickMarksLength();
237− float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius;
238− 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;
221+ float tickMarkAngle = getMajorTickMarksAngle();
222+ float angle = startAt - fmodf(startAt, tickMarkAngle) + tickMarkAngle;
223+ float angleInRadians = glm::radians(angle);
224+ float tickMarkLength = getMajorTickMarksLength();
225+ float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius;
226+ float endRadius = startRadius + tickMarkLength;
247227
248− angle += tickMarkAngle;
249− }
250− }
251−
252− // draw our minor tick marks
253− if (getMinorTickMarksAngle() > 0.0f && getMinorTickMarksLength() != 0.0f) {
228+ while (angle <= endAt) {
229+ angleInRadians = glm::radians(angle);
230+
231+ glm::vec2 thisPointA(cosf(angleInRadians) * startRadius, sinf(angleInRadians) * startRadius);
232+ glm::vec2 thisPointB(cosf(angleInRadians) * endRadius, sinf(angleInRadians) * endRadius);
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) {
254242
255− float tickMarkAngle = getMinorTickMarksAngle();
256− float angle = startAt - fmod(startAt, tickMarkAngle) + tickMarkAngle;
257− float angleInRadians = glm::radians(angle);
258− float tickMarkLength = getMinorTickMarksLength();
259− float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius;
260− 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;
243+ float tickMarkAngle = getMinorTickMarksAngle();
244+ float angle = startAt - fmodf(startAt, tickMarkAngle) + tickMarkAngle;
245+ float angleInRadians = glm::radians(angle);
246+ float tickMarkLength = getMinorTickMarksLength();
247+ float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius;
248+ float endRadius = startRadius + tickMarkLength;
269249
270− angle += tickMarkAngle;
271− }
272− }
273−
274− xColor majorColorX = getMajorTickMarksColor();
275− glm::vec4 majorColor(majorColorX.red / MAX_COLOR, majorColorX.green / MAX_COLOR, majorColorX.blue / MAX_COLOR, alpha);
276−
277− geometryCache->updateVertices(_majorTicksVerticesID, majorPoints, majorColor);
278−
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);
250+ while (angle <= endAt) {
251+ angleInRadians = glm::radians(angle);
252+
253+ glm::vec2 thisPointA(cosf(angleInRadians) * startRadius, sinf(angleInRadians) * startRadius);
254+ glm::vec2 thisPointB(cosf(angleInRadians) * endRadius, sinf(angleInRadians) * endRadius);
255+
256+ minorPoints << thisPointA << thisPointB;
257+
258+ angle += tickMarkAngle;
283259 }
284−
285− geometryCache->renderVertices(gpu::LINES, _majorTicksVerticesID);
286−
287− geometryCache->renderVertices(gpu::LINES, _minorTicksVerticesID);
288260 }
289261
290−
291− glPopMatrix();
292− glPopMatrix();
262+ xColor majorColorX = getMajorTickMarksColor();
263+ glm::vec4 majorColor(majorColorX.red / MAX_COLOR, majorColorX.green / MAX_COLOR, majorColorX.blue / MAX_COLOR, alpha);
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+ }
293277
294278 if (geometryChanged) {
295279 _lastStartAt = startAt;
@@ -297,15 +281,11 @@ void Circle3DOverlay::render(RenderArgs* args) { · 1 unmodified line
297281 _lastInnerRadius = innerRadius;
298282 _lastOuterRadius = outerRadius;
299283 }
300−
301− if (glower) {
302− delete glower;
303− }
304284 }
305285
306286 void Circle3DOverlay::setProperties(const QScriptValue &properties) {
307287 Planar3DOverlay::setProperties(properties);
308−
288+
309289 QScriptValue startAt = properties.property("startAt");
310290 if (startAt.isValid()) {
311291 setStartAt(startAt.toVariant().toFloat());
@@ -415,7 +395,7 @@ QScriptValue Circle3DOverlay::getProperty(const QString& property) { · 103 unmodified lines
415395 }
416396
417397
418−bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin,
398+bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin,
419399 const glm::vec3& direction, float& distance, BoxFace& face) {
420400
421401 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
3232 Line3DOverlay::~Line3DOverlay() {
3333 }
3434
35+AABox Line3DOverlay::getBounds() const {
36+ auto start = _position + _start;
37+ auto end = _position + _end;
38+
39+ auto min = glm::min(start, end);
40+ auto max = glm::max(start, end);
41+
42+ return AABox(min, max - min);
43+}
44+
3545 void Line3DOverlay::render(RenderArgs* args) {
3646 if (!_visible) {
3747 return; // do nothing if we're not visible
interface/src/ui/overlays/Line3DOverlay.h+1−0
@@ -21,6 +21,7 @@ public: · 20 unmodified lines
2121 Line3DOverlay(const Line3DOverlay* line3DOverlay);
2222 ~Line3DOverlay();
2323 virtual void render(RenderArgs* args);
24+ virtual AABox getBounds() const override;
2425
2526 // getters
2627 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
258258 unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) {
259259 glm::vec2 pointCopy = point;
260260 if (qApp->isHMDMode()) {
261− pointCopy = qApp->getApplicationOverlay().screenToOverlay(point);
261+ pointCopy = qApp->getApplicationCompositor().screenToOverlay(point);
262262 }
263263
264264 QReadLocker lock(&_lock);
interface/src/ui/overlays/TextOverlay.cpp+1−0
@@ -80,6 +80,7 @@ void TextOverlay::render(RenderArgs* args) { · 79 unmodified lines
8080
8181 glm::vec2 topLeft(left, top);
8282 glm::vec2 bottomRight(right, bottom);
83+ glBindTexture(GL_TEXTURE_2D, 0);
8384 DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, quadColor);
8485
8586 const int leftAdjust = -1; // required to make text render relative to left edge of bounds