From e4a0275f57fdc2455bdc4880e37a5e828dcd21ce Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 10:53:11 -0700 Subject: [PATCH 01/12] Exposed sixense and mouse options and oculus UI angle to preferences --- interface/src/Menu.cpp | 6 +- interface/src/Menu.h | 10 +- interface/src/devices/OculusManager.h | 2 + interface/src/devices/SixenseManager.cpp | 4 +- interface/src/devices/SixenseManager.h | 3 + interface/src/ui/ApplicationOverlay.cpp | 8 +- interface/src/ui/ApplicationOverlay.h | 5 +- interface/src/ui/PreferencesDialog.cpp | 13 + interface/ui/preferencesDialog.ui | 331 ++++++++++++++++++++++- 9 files changed, 367 insertions(+), 15 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 3015a638f6..644b08f03d 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -45,6 +45,7 @@ #include "ui/ModelsBrowser.h" #include "ui/LoginDialog.h" #include "ui/NodeBounds.h" +#include "devices/OculusManager.h" Menu* Menu::_instance = NULL; @@ -83,6 +84,7 @@ Menu::Menu() : _audioJitterBufferSamples(0), _bandwidthDialog(NULL), _fieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES), + _realWorldFieldOfView(DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES), _faceshiftEyeDeflection(DEFAULT_FACESHIFT_EYE_DEFLECTION), _frustumDrawMode(FRUSTUM_DRAW_MODE_ALL), _viewFrustumOffset(DEFAULT_FRUSTUM_OFFSET), @@ -91,6 +93,9 @@ Menu::Menu() : _lodToolsDialog(NULL), _maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM), _voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE), + _oculusUIAngularSize(DEFAULT_OCULUS_UI_ANGULAR_SIZE), + _sixenseReticleMoveSpeed(DEFAULT_SIXENSE_RETICLE_MOVE_SPEED), + _invertSixenseButtons(DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS), _automaticAvatarLOD(true), _avatarLODDecreaseFPS(DEFAULT_ADJUST_AVATAR_LOD_DOWN_FPS), _avatarLODIncreaseFPS(ADJUST_LOD_UP_FPS), @@ -387,7 +392,6 @@ Menu::Menu() : QMenu* sixenseOptionsMenu = developerMenu->addMenu("Sixense Options"); addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true); - addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseInvertInputButtons, 0, false); QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 41f0b41498..4d2174a448 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -90,6 +90,12 @@ public: void setFieldOfView(float fieldOfView) { _fieldOfView = fieldOfView; } float getRealWorldFieldOfView() const { return _realWorldFieldOfView; } void setRealWorldFieldOfView(float realWorldFieldOfView) { _realWorldFieldOfView = realWorldFieldOfView; } + float getOculusUIAngularSize() const { return _oculusUIAngularSize; } + void setOculusUIAngularSize(float oculusUIAngularSize) { _oculusUIAngularSize = oculusUIAngularSize; } + float getSixenseReticleMoveSpeed() const { return _sixenseReticleMoveSpeed; } + void setSixenseReticleMoveSpeed(float sixenseReticleMoveSpeed) { _sixenseReticleMoveSpeed = sixenseReticleMoveSpeed; } + bool getInvertSixenseButtons() const { return _invertSixenseButtons; } + void setInvertSixenseButtons(bool invertSixenseButtons) { _invertSixenseButtons = invertSixenseButtons; } float getFaceshiftEyeDeflection() const { return _faceshiftEyeDeflection; } void setFaceshiftEyeDeflection(float faceshiftEyeDeflection) { _faceshiftEyeDeflection = faceshiftEyeDeflection; } @@ -255,6 +261,9 @@ private: LodToolsDialog* _lodToolsDialog; int _maxVoxels; float _voxelSizeScale; + float _oculusUIAngularSize; + float _sixenseReticleMoveSpeed; + bool _invertSixenseButtons; bool _automaticAvatarLOD; float _avatarLODDecreaseFPS; float _avatarLODIncreaseFPS; @@ -400,7 +409,6 @@ namespace MenuOption { const QString SettingsExport = "Export Settings"; const QString SettingsImport = "Import Settings"; const QString SimpleShadows = "Simple"; - const QString SixenseInvertInputButtons = "Invert Sixense Mouse Input Buttons"; const QString SixenseMouseInput = "Enable Sixense Mouse Input"; const QString ShowBordersVoxelNodes = "Show Voxel Nodes"; const QString ShowBordersModelNodes = "Show Model Nodes"; diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index caff38a6b0..21b9d67f4d 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -20,6 +20,8 @@ #include "renderer/ProgramObject.h" +const float DEFAULT_OCULUS_UI_ANGULAR_SIZE = 72.0f; + class Camera; /// Handles interaction with the Oculus Rift. diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index fa902be46f..5257a777e0 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -339,7 +339,7 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { Qt::MouseButton bumperButton; Qt::MouseButton triggerButton; - if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseInvertInputButtons)) { + if (Menu::getInstance()->getInvertSixenseButtons()) { bumperButton = Qt::LeftButton; triggerButton = Qt::RightButton; } else { @@ -351,7 +351,7 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); - float cursorRange = widget->width(); + float cursorRange = widget->width() * (1.0f - Menu::getInstance()->getSixenseReticleMoveSpeed() * 0.01f) * 2.0f; pos.setX(cursorRange * xAngle); pos.setY(cursorRange * yAngle); diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index f3c5633f90..71511cd06b 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -30,6 +30,9 @@ const unsigned int BUTTON_FWD = 1U << 7; // Event type that represents moving the controller const unsigned int CONTROLLER_MOVE_EVENT = 1500U; +const float DEFAULT_SIXENSE_RETICLE_MOVE_SPEED = 1.0f; +const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = true; + /// Handles interaction with the Sixense SDK (e.g., Razer Hydra). class SixenseManager : public QObject { Q_OBJECT diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 95ac803e37..a45e42dc0b 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -33,7 +33,7 @@ ApplicationOverlay::ApplicationOverlay() : _framebufferObject(NULL), _oculusAngle(65.0f * RADIANS_PER_DEGREE), _distance(0.5f), - _textureFov(PI / 2.5f), + _textureFov(DEFAULT_OCULUS_UI_ANGULAR_SIZE * RADIANS_PER_DEGREE), _uiType(HEMISPHERE) { } @@ -50,6 +50,8 @@ const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; void ApplicationOverlay::renderOverlay(bool renderToTexture) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); + _textureFov = Menu::getInstance()->getOculusUIAngularSize() * RADIANS_PER_DEGREE; + Application* application = Application::getInstance(); Overlays& overlays = application->getOverlays(); @@ -154,8 +156,6 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { MyAvatar* myAvatar = application->getAvatar(); const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); - - // Get vertical FoV of the displayed overlay texture const float halfVerticalAngle = _oculusAngle / 2.0f; const float overlayAspectRatio = glWidget->width() / (float)glWidget->height(); @@ -338,7 +338,7 @@ void ApplicationOverlay::renderControllerPointer() { float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); - float cursorRange = glWidget->width(); + float cursorRange = glWidget->width() * (1.0f - Menu::getInstance()->getSixenseReticleMoveSpeed() * 0.01f) * 2.0f; int mouseX = cursorRange * xAngle; int mouseY = cursorRange * yAngle; diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 03a323cd5d..b6066099fa 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -31,10 +31,7 @@ public: // Getters QOpenGLFramebufferObject* getFramebufferObject(); - float getOculusAngle() const { return _oculusAngle; } - - // Setters - void setOculusAngle(float oculusAngle) { _oculusAngle = oculusAngle; } + void setUIType(UIType uiType) { _uiType = uiType; } private: diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index d5c6079d4b..5e6c6984eb 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -137,6 +137,13 @@ void PreferencesDialog::loadPreferences() { ui.maxVoxelsSpin->setValue(menuInstance->getMaxVoxels()); ui.maxVoxelsPPSSpin->setValue(menuInstance->getMaxVoxelPacketsPerSecond()); + + ui.oculusUIAngularSizeSpin->setValue(menuInstance->getOculusUIAngularSize()); + + ui.sixenseReticleMoveSpeedSpin->setValue(menuInstance->getSixenseReticleMoveSpeed()); + + ui.invertSixenseButtonsCheckBox->setChecked(menuInstance->getInvertSixenseButtons()); + } void PreferencesDialog::savePreferences() { @@ -189,6 +196,12 @@ void PreferencesDialog::savePreferences() { (float)ui.faceshiftEyeDeflectionSider->maximum()); Menu::getInstance()->setMaxVoxelPacketsPerSecond(ui.maxVoxelsPPSSpin->value()); + Menu::getInstance()->setOculusUIAngularSize(ui.oculusUIAngularSizeSpin->value()); + + Menu::getInstance()->setSixenseReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value()); + + Menu::getInstance()->setInvertSixenseButtons(ui.invertSixenseButtonsCheckBox->isChecked()); + Menu::getInstance()->setAudioJitterBufferSamples(ui.audioJitterSpin->value()); Application::getInstance()->getAudio()->setJitterBufferSamples(ui.audioJitterSpin->value()); diff --git a/interface/ui/preferencesDialog.ui b/interface/ui/preferencesDialog.ui index a1c2073ab6..f00d7c4788 100644 --- a/interface/ui/preferencesDialog.ui +++ b/interface/ui/preferencesDialog.ui @@ -154,9 +154,9 @@ color: #0e7077 0 - -204 - 494 - 1091 + -1002 + 477 + 1386 @@ -1605,6 +1605,331 @@ padding: 10px;margin-top:10px + + + + + 0 + 0 + + + + + 0 + 40 + + + + + Arial + 20 + 50 + false + + + + color: #0e7077 + + + Oculus Rift + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + 0 + + + 10 + + + 0 + + + 10 + + + + + + Arial + + + + color: rgb(51, 51, 51) + + + User Interface Angular Size + + + 15 + + + maxVoxelsSpin + + + + + + + + Arial + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 125 + 36 + + + + + Arial + + + + 30 + + + 160 + + + 1 + + + 72 + + + + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + Arial + 20 + 50 + false + + + + color: #0e7077 + + + Sixense Controllers + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + 0 + + + 10 + + + 0 + + + 10 + + + + + + Arial + + + + color: rgb(51, 51, 51) + + + Invert Mouse Buttons + + + 15 + + + maxVoxelsSpin + + + + + + + + Arial + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 32 + 0 + + + + + 0 + 0 + + + + + + + + 32 + 32 + + + + + + + + + + 0 + + + 10 + + + 0 + + + 10 + + + + + + Arial + + + + color: rgb(51, 51, 51) + + + Reticle Movement Speed + + + 15 + + + maxVoxelsSpin + + + + + + + + Arial + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 125 + 36 + + + + + Arial + + + + 100 + + + 1 + + + 50 + + + + + From 4a1307fa382ff92a25877f21474c57c42a55825d Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 10:54:50 -0700 Subject: [PATCH 02/12] Killed two warnings --- interface/src/ui/ApplicationOverlay.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index a45e42dc0b..f6afc15223 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -316,7 +316,6 @@ void ApplicationOverlay::renderControllerPointer() { MyAvatar* myAvatar = application->getAvatar(); const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); - int numberOfPalms = handData->getNumPalms(); for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) { const PalmData* palmData = NULL; @@ -431,8 +430,6 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) magnifyHeight = widgetHeight - mouseY; } - const float halfMagnifyHeight = magnifyHeight / 2.0f; - float newWidth = magnifyWidth * magnification; float newHeight = magnifyHeight * magnification; From 422db14812a20a63cb7c3a987733832253c7e883 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 11:34:57 -0700 Subject: [PATCH 03/12] Fixed some improper logic and used better defaults --- interface/src/devices/SixenseManager.cpp | 17 +++++++++++------ interface/src/devices/SixenseManager.h | 5 +++-- interface/src/ui/ApplicationOverlay.cpp | 13 ++++++------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 5257a777e0..ed55dc4c66 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -185,6 +185,11 @@ void SixenseManager::update(float deltaTime) { #endif // HAVE_SIXENSE } +float SixenseManager::getCursorPixelRangeMultiplier() const { + //scales (0,100) to (0.4,2.0) + return ((Menu::getInstance()->getSixenseReticleMoveSpeed()) * 0.008f + 0.2f) * 2.0f; +} + #ifdef HAVE_SIXENSE // the calibration sequence is: @@ -347,14 +352,14 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { triggerButton = Qt::LeftButton; } - // Get the angles, scaled between 0-1 - float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; - float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); + // Get the angles, scaled between (-0.5,0.5) + float xAngle = (atan2(direction.z, direction.x) + M_PI_2); + float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); - float cursorRange = widget->width() * (1.0f - Menu::getInstance()->getSixenseReticleMoveSpeed() * 0.01f) * 2.0f; + float cursorRange = widget->width() * getCursorPixelRangeMultiplier(); - pos.setX(cursorRange * xAngle); - pos.setY(cursorRange * yAngle); + pos.setX(widget->width() / 2.0f + cursorRange * xAngle); + pos.setY(widget->height() / 2.0f + cursorRange * yAngle); //If we are off screen then we should stop processing, and if a trigger or bumper is pressed, //we should unpress them. diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index 71511cd06b..41e061a25b 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -30,8 +30,8 @@ const unsigned int BUTTON_FWD = 1U << 7; // Event type that represents moving the controller const unsigned int CONTROLLER_MOVE_EVENT = 1500U; -const float DEFAULT_SIXENSE_RETICLE_MOVE_SPEED = 1.0f; -const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = true; +const float DEFAULT_SIXENSE_RETICLE_MOVE_SPEED = 37.5f; +const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; /// Handles interaction with the Sixense SDK (e.g., Razer Hydra). class SixenseManager : public QObject { @@ -42,6 +42,7 @@ public: ~SixenseManager(); void update(float deltaTime); + float getCursorPixelRangeMultiplier() const; public slots: diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index f6afc15223..8ba0b42910 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -333,14 +333,14 @@ void ApplicationOverlay::renderControllerPointer() { // Get directon relative to avatar orientation glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); - // Get the angles, scaled between 0-1 - float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; - float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); + // Get the angles, scaled between (-0.5,0.5) + float xAngle = (atan2(direction.z, direction.x) + M_PI_2) ; + float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); - float cursorRange = glWidget->width() * (1.0f - Menu::getInstance()->getSixenseReticleMoveSpeed() * 0.01f) * 2.0f; + float cursorRange = glWidget->width() * application->getSixenseManager()->getCursorPixelRangeMultiplier(); - int mouseX = cursorRange * xAngle; - int mouseY = cursorRange * yAngle; + int mouseX = glWidget->width() / 2.0f + cursorRange * xAngle; + int mouseY = glWidget->height() / 2.0f + cursorRange * yAngle; //If the cursor is out of the screen then don't render it if (mouseX < 0 || mouseX >= glWidget->width() || mouseY < 0 || mouseY >= glWidget->height()) { @@ -409,7 +409,6 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) const float horizontalAngle = halfVerticalAngle * 2.0f * overlayAspectRatio; const float halfHorizontalAngle = horizontalAngle / 2; - float magnifyWidth = 80.0f; float magnifyHeight = 60.0f; From 3747a5eeae79bee2b14b6bd43db226ac37316808 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 13:38:28 -0700 Subject: [PATCH 04/12] update hemisphere VBO on ui size change --- interface/src/ui/ApplicationOverlay.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 8ba0b42910..e228e39976 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -692,8 +692,11 @@ void ApplicationOverlay::renderTexturedHemisphere() { static VerticesIndices vbo(0, 0); int vertices = slices * (stacks - 1) + 1; int indices = slices * 2 * 3 * (stacks - 2) + slices * 3; - //We only generate the VBO once - if (vbo.first == 0) { + + static float oldTextureFOV = _textureFov; + //We only generate the VBO when the _textureFov changes + if (vbo.first == 0 || oldTextureFOV != _textureFov) { + oldTextureFOV = _textureFov; TextureVertex* vertexData = new TextureVertex[vertices]; TextureVertex* vertex = vertexData; for (int i = 0; i < stacks - 1; i++) { @@ -718,7 +721,9 @@ void ApplicationOverlay::renderTexturedHemisphere() { vertex->uv.y = 0.5f; vertex++; - glGenBuffers(1, &vbo.first); + if (vbo.first == 0){ + glGenBuffers(1, &vbo.first); + } glBindBuffer(GL_ARRAY_BUFFER, vbo.first); const int BYTES_PER_VERTEX = sizeof(TextureVertex); glBufferData(GL_ARRAY_BUFFER, vertices * BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW); From 230af7f5a7820ddf632c28bf2258f279440fd537 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 13:49:35 -0700 Subject: [PATCH 05/12] Stopped clamping the magnification --- interface/src/ui/ApplicationOverlay.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index e228e39976..830a3f843b 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -415,20 +415,6 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) mouseX -= magnifyWidth / 2; mouseY -= magnifyHeight / 2; - //clamp the magnification - if (mouseX < 0) { - magnifyWidth += mouseX; - mouseX = 0; - } else if (mouseX + magnifyWidth > widgetWidth) { - magnifyWidth = widgetWidth - mouseX; - } - if (mouseY < 0) { - magnifyHeight += mouseY; - mouseY = 0; - } else if (mouseY + magnifyHeight > widgetHeight) { - magnifyHeight = widgetHeight - mouseY; - } - float newWidth = magnifyWidth * magnification; float newHeight = magnifyHeight * magnification; From c593117ac5bccd39d09625298d54c596846bf411 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 15:03:09 -0700 Subject: [PATCH 06/12] New reticle for sixense and oculus mouse movement --- .../resources/images/sixense-reticle.png | Bin 0 -> 26350 bytes interface/src/ui/ApplicationOverlay.cpp | 62 ++++++++++-------- interface/src/ui/ApplicationOverlay.h | 2 + 3 files changed, 35 insertions(+), 29 deletions(-) create mode 100644 interface/resources/images/sixense-reticle.png diff --git a/interface/resources/images/sixense-reticle.png b/interface/resources/images/sixense-reticle.png new file mode 100644 index 0000000000000000000000000000000000000000..b168b25106b99693112c3b2f3a351b4a49733325 GIT binary patch literal 26350 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 int mouseY = application->getMouseY(); + + //lazily load crosshair texture + if (_crosshairTexture == 0) { + _crosshairTexture = Application::getInstance()->getGLWidget()->bindTexture(QImage(Application::resourcesPath() + "images/sixense-reticle.png")); + } + glEnable(GL_TEXTURE_2D); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, _crosshairTexture); + + + + if (OculusManager::isConnected() && application->getLastMouseMoveType() == QEvent::MouseMove) { const float pointerWidth = 10; const float pointerHeight = 10; - const float crossPad = 4; _numMagnifiers = 1; _mouseX[0] = application->getMouseX(); @@ -289,25 +304,22 @@ void ApplicationOverlay::renderPointers() { glBegin(GL_QUADS); - glColor3f(1, 0, 0); + glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); //Horizontal crosshair - glVertex2i(mouseX, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight + crossPad); - glVertex2i(mouseX, mouseY - pointerHeight + crossPad); - - //Vertical crosshair - glVertex2i(mouseX + crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY - pointerHeight); - glVertex2i(mouseX + crossPad, mouseY - pointerHeight); + glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY); + glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY); + glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight); + glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - pointerHeight); glEnd(); } else if (application->getLastMouseMoveType() == CONTROLLER_MOVE_EVENT && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { //only render controller pointer if we aren't already rendering a mouse pointer renderControllerPointer(); } + + glDisable(GL_TEXTURE_2D); + } void ApplicationOverlay::renderControllerPointer() { @@ -349,13 +361,12 @@ void ApplicationOverlay::renderControllerPointer() { float pointerWidth = 40; float pointerHeight = 40; - float crossPad = 16; + //if we have the oculus, we should make the cursor smaller since it will be //magnified if (OculusManager::isConnected()) { - pointerWidth /= 4; - pointerHeight /= 4; - crossPad /= 4; + pointerWidth /= 2; + pointerHeight /= 2; _mouseX[_numMagnifiers] = mouseX; _mouseY[_numMagnifiers] = mouseY; @@ -368,19 +379,12 @@ void ApplicationOverlay::renderControllerPointer() { glBegin(GL_QUADS); - glColor3f(0.0f, 0.0f, 1.0f); + glColor3f(0.0f, 198.0f/255.0f, 244.0f/255.0f); - //Horizontal crosshair - glVertex2i(mouseX, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight + crossPad); - glVertex2i(mouseX, mouseY - pointerHeight + crossPad); - - //Vertical crosshair - glVertex2i(mouseX + crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY - pointerHeight); - glVertex2i(mouseX + crossPad, mouseY - pointerHeight); + glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY); + glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY); + glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight); + glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - pointerHeight); glEnd(); } diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index b6066099fa..dc1d3d641f 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -59,6 +59,8 @@ private: int _mouseX[2]; int _mouseY[2]; int _numMagnifiers; + + GLuint _crosshairTexture; }; #endif // hifi_ApplicationOverlay_h \ No newline at end of file From cb1669653d771c510667caef7ee899d4640a148f Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 15:05:23 -0700 Subject: [PATCH 07/12] Mouse reticle bigger in oculus --- interface/src/ui/ApplicationOverlay.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 2b98360b02..9222cb092d 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -292,8 +292,8 @@ void ApplicationOverlay::renderPointers() { if (OculusManager::isConnected() && application->getLastMouseMoveType() == QEvent::MouseMove) { - const float pointerWidth = 10; - const float pointerHeight = 10; + const float pointerWidth = 20; + const float pointerHeight = 20; _numMagnifiers = 1; _mouseX[0] = application->getMouseX(); From 587c0e5a9d1ca50b661b8ee310b8186a895d082c Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 16:01:59 -0700 Subject: [PATCH 08/12] Render Oculus pointers separate from texture for better quality --- interface/src/ui/ApplicationOverlay.cpp | 108 +++++++++++++++++++----- interface/src/ui/ApplicationOverlay.h | 3 +- 2 files changed, 88 insertions(+), 23 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 9222cb092d..fe6baadf66 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -37,8 +37,6 @@ ApplicationOverlay::ApplicationOverlay() : _uiType(HEMISPHERE), _crosshairTexture(0) { - - } ApplicationOverlay::~ApplicationOverlay() { @@ -259,6 +257,8 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { glEnd(); } + renderControllerPointersOculus(); + glPopMatrix(); glDepthMask(GL_TRUE); @@ -289,8 +289,6 @@ void ApplicationOverlay::renderPointers() { glBindTexture(GL_TEXTURE_2D, _crosshairTexture); - - if (OculusManager::isConnected() && application->getLastMouseMoveType() == QEvent::MouseMove) { const float pointerWidth = 20; const float pointerHeight = 20; @@ -299,30 +297,36 @@ void ApplicationOverlay::renderPointers() { _mouseX[0] = application->getMouseX(); _mouseY[0] = application->getMouseY(); - mouseX -= pointerWidth / 2.0f; - mouseY += pointerHeight / 2.0f; + //If we are in oculus, render reticle later - glBegin(GL_QUADS); - glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); + if (!OculusManager::isConnected()) { - //Horizontal crosshair - glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY); - glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY); - glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight); - glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - pointerHeight); + mouseX -= pointerWidth / 2.0f; + mouseY += pointerHeight / 2.0f; - glEnd(); + glBegin(GL_QUADS); + + glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); + + //Horizontal crosshair + glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY); + glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY); + glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight); + glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - pointerHeight); + + glEnd(); + } } else if (application->getLastMouseMoveType() == CONTROLLER_MOVE_EVENT && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { //only render controller pointer if we aren't already rendering a mouse pointer - renderControllerPointer(); + renderControllerPointers(); } - + glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); } -void ApplicationOverlay::renderControllerPointer() { +void ApplicationOverlay::renderControllerPointers() { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); @@ -365,13 +369,12 @@ void ApplicationOverlay::renderControllerPointer() { //if we have the oculus, we should make the cursor smaller since it will be //magnified if (OculusManager::isConnected()) { - pointerWidth /= 2; - pointerHeight /= 2; _mouseX[_numMagnifiers] = mouseX; _mouseY[_numMagnifiers] = mouseY; _numMagnifiers++; - + //If oculus is enabled, we draw the crosshairs later + continue; } mouseX -= pointerWidth / 2.0f; @@ -390,13 +393,74 @@ void ApplicationOverlay::renderControllerPointer() { } } +void ApplicationOverlay::renderControllerPointersOculus() { + Application* application = Application::getInstance(); + QGLWidget* glWidget = application->getGLWidget(); + + const int widgetWidth = glWidget->width(); + const int widgetHeight = glWidget->height(); + + const float reticleSize = 50.0f; + + glBindTexture(GL_TEXTURE_2D, _crosshairTexture); + glDisable(GL_DEPTH_TEST); + + for (int i = 0; i < _numMagnifiers; i++) { + + float mouseX = (float)_mouseX[i]; + float mouseY = (float)_mouseY[i]; + mouseX -= reticleSize / 2; + mouseY += reticleSize / 2; + + // Get position on hemisphere using angle + if (_uiType == HEMISPHERE) { + + //Get new UV coordinates from our magnification window + float newULeft = mouseX / widgetWidth; + float newURight = (mouseX + reticleSize) / widgetWidth; + float newVBottom = 1.0 - mouseY / widgetHeight; + float newVTop = 1.0 - (mouseY - reticleSize) / widgetHeight; + + // Project our position onto the hemisphere using the UV coordinates + float lX = sin((newULeft - 0.5f) * _textureFov); + float rX = sin((newURight - 0.5f) * _textureFov); + float bY = sin((newVBottom - 0.5f) * _textureFov); + float tY = sin((newVTop - 0.5f) * _textureFov); + + float dist; + //Bottom Left + dist = sqrt(lX * lX + bY * bY); + float blZ = sqrt(1.0f - dist * dist); + //Top Left + dist = sqrt(lX * lX + tY * tY); + float tlZ = sqrt(1.0f - dist * dist); + //Bottom Right + dist = sqrt(rX * rX + bY * bY); + float brZ = sqrt(1.0f - dist * dist); + //Top Right + dist = sqrt(rX * rX + tY * tY); + float trZ = sqrt(1.0f - dist * dist); + + glBegin(GL_QUADS); + + glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); + + glTexCoord2f(0.0f, 0.0f); glVertex3f(lX, tY, -tlZ); + glTexCoord2f(1.0f, 0.0f); glVertex3f(rX, tY, -trZ); + glTexCoord2f(1.0f, 1.0f); glVertex3f(rX, bY, -brZ); + glTexCoord2f(0.0f, 1.0f); glVertex3f(lX, bY, -blZ); + + glEnd(); + } + } + glEnable(GL_DEPTH_TEST); +} + //Renders a small magnification of the currently bound texture at the coordinates void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); - MyAvatar* myAvatar = application->getAvatar(); - const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); float leftX, rightX, leftZ, rightZ, topZ, bottomZ; diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index dc1d3d641f..71f78d606d 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -44,7 +44,8 @@ private: typedef QPair VerticesIndices; void renderPointers(); - void renderControllerPointer(); + void renderControllerPointers(); + void renderControllerPointersOculus(); void renderMagnifier(int mouseX, int mouseY); void renderAudioMeter(); void renderStatsAndLogs(); From 53d4cc795a62b767f27c975267ba37de596afe7f Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 16:09:45 -0700 Subject: [PATCH 09/12] Removed unused UI types that clutter code --- interface/src/ui/ApplicationOverlay.cpp | 272 ++++++++---------------- interface/src/ui/ApplicationOverlay.h | 7 +- 2 files changed, 85 insertions(+), 194 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index fe6baadf66..2ef5665bee 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -34,7 +34,6 @@ ApplicationOverlay::ApplicationOverlay() : _oculusAngle(65.0f * RADIANS_PER_DEGREE), _distance(0.5f), _textureFov(DEFAULT_OCULUS_UI_ANGULAR_SIZE * RADIANS_PER_DEGREE), - _uiType(HEMISPHERE), _crosshairTexture(0) { } @@ -157,25 +156,6 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { MyAvatar* myAvatar = application->getAvatar(); const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); - // Get vertical FoV of the displayed overlay texture - const float halfVerticalAngle = _oculusAngle / 2.0f; - const float overlayAspectRatio = glWidget->width() / (float)glWidget->height(); - const float halfOverlayHeight = _distance * tan(halfVerticalAngle); - const float overlayHeight = halfOverlayHeight * 2.0f; - - // The more vertices, the better the curve - const int numHorizontalVertices = 20; - const int numVerticalVertices = 20; - // U texture coordinate width at each quad - const float quadTexWidth = 1.0f / (numHorizontalVertices - 1); - const float quadTexHeight = 1.0f / (numVerticalVertices - 1); - - // Get horizontal angle and angle increment from vertical angle and aspect ratio - const float horizontalAngle = halfVerticalAngle * 2.0f * overlayAspectRatio; - const float angleIncrement = horizontalAngle / (numHorizontalVertices - 1); - const float halfHorizontalAngle = horizontalAngle / 2; - - const float verticalAngleIncrement = _oculusAngle / (numVerticalVertices - 1); glActiveTexture(GL_TEXTURE0); @@ -211,8 +191,6 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.01f); - float leftX, rightX, leftZ, rightZ, topZ, bottomZ; - //Draw the magnifiers for (int i = 0; i < _numMagnifiers; i++) { renderMagnifier(_mouseX[i], _mouseY[i]); @@ -221,42 +199,8 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { glDepthMask(GL_FALSE); glDisable(GL_ALPHA_TEST); - //TODO: Remove immediate mode in favor of VBO - if (_uiType == HEMISPHERE) { - renderTexturedHemisphere(); - } else{ - glBegin(GL_QUADS); - // Place the vertices in a semicircle curve around the camera - for (int i = 0; i < numHorizontalVertices - 1; i++) { - for (int j = 0; j < numVerticalVertices - 1; j++) { - - // Calculate the X and Z coordinates from the angles and radius from camera - leftX = sin(angleIncrement * i - halfHorizontalAngle) * _distance; - rightX = sin(angleIncrement * (i + 1) - halfHorizontalAngle) * _distance; - leftZ = -cos(angleIncrement * i - halfHorizontalAngle) * _distance; - rightZ = -cos(angleIncrement * (i + 1) - halfHorizontalAngle) * _distance; - if (_uiType == 2) { - topZ = -cos((verticalAngleIncrement * (j + 1) - halfVerticalAngle) * overlayAspectRatio) * _distance; - bottomZ = -cos((verticalAngleIncrement * j - halfVerticalAngle) * overlayAspectRatio) * _distance; - } else { - topZ = -99999; - bottomZ = -99999; - } - - glTexCoord2f(quadTexWidth * i, (j + 1) * quadTexHeight); - glVertex3f(leftX, (j + 1) * quadTexHeight * overlayHeight - halfOverlayHeight, max(topZ, leftZ)); - glTexCoord2f(quadTexWidth * (i + 1), (j + 1) * quadTexHeight); - glVertex3f(rightX, (j + 1) * quadTexHeight * overlayHeight - halfOverlayHeight, max(topZ, rightZ)); - glTexCoord2f(quadTexWidth * (i + 1), j * quadTexHeight); - glVertex3f(rightX, j * quadTexHeight * overlayHeight - halfOverlayHeight, max(bottomZ, rightZ)); - glTexCoord2f(quadTexWidth * i, j * quadTexHeight); - glVertex3f(leftX, j * quadTexHeight * overlayHeight - halfOverlayHeight, max(bottomZ, leftZ)); - } - } - - glEnd(); - } - + renderTexturedHemisphere(); + renderControllerPointersOculus(); glPopMatrix(); @@ -411,106 +355,12 @@ void ApplicationOverlay::renderControllerPointersOculus() { float mouseY = (float)_mouseY[i]; mouseX -= reticleSize / 2; mouseY += reticleSize / 2; - - // Get position on hemisphere using angle - if (_uiType == HEMISPHERE) { - - //Get new UV coordinates from our magnification window - float newULeft = mouseX / widgetWidth; - float newURight = (mouseX + reticleSize) / widgetWidth; - float newVBottom = 1.0 - mouseY / widgetHeight; - float newVTop = 1.0 - (mouseY - reticleSize) / widgetHeight; - - // Project our position onto the hemisphere using the UV coordinates - float lX = sin((newULeft - 0.5f) * _textureFov); - float rX = sin((newURight - 0.5f) * _textureFov); - float bY = sin((newVBottom - 0.5f) * _textureFov); - float tY = sin((newVTop - 0.5f) * _textureFov); - - float dist; - //Bottom Left - dist = sqrt(lX * lX + bY * bY); - float blZ = sqrt(1.0f - dist * dist); - //Top Left - dist = sqrt(lX * lX + tY * tY); - float tlZ = sqrt(1.0f - dist * dist); - //Bottom Right - dist = sqrt(rX * rX + bY * bY); - float brZ = sqrt(1.0f - dist * dist); - //Top Right - dist = sqrt(rX * rX + tY * tY); - float trZ = sqrt(1.0f - dist * dist); - - glBegin(GL_QUADS); - - glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); - - glTexCoord2f(0.0f, 0.0f); glVertex3f(lX, tY, -tlZ); - glTexCoord2f(1.0f, 0.0f); glVertex3f(rX, tY, -trZ); - glTexCoord2f(1.0f, 1.0f); glVertex3f(rX, bY, -brZ); - glTexCoord2f(0.0f, 1.0f); glVertex3f(lX, bY, -blZ); - - glEnd(); - } - } - glEnable(GL_DEPTH_TEST); -} - -//Renders a small magnification of the currently bound texture at the coordinates -void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) -{ - Application* application = Application::getInstance(); - QGLWidget* glWidget = application->getGLWidget(); - - float leftX, rightX, leftZ, rightZ, topZ, bottomZ; - - const int widgetWidth = glWidget->width(); - const int widgetHeight = glWidget->height(); - const float magnification = 4.0f; - - // Get vertical FoV of the displayed overlay texture - const float halfVerticalAngle = _oculusAngle / 2.0f; - const float overlayAspectRatio = glWidget->width() / (float)glWidget->height(); - const float halfOverlayHeight = _distance * tan(halfVerticalAngle); - - // Get horizontal angle and angle increment from vertical angle and aspect ratio - const float horizontalAngle = halfVerticalAngle * 2.0f * overlayAspectRatio; - const float halfHorizontalAngle = horizontalAngle / 2; - - float magnifyWidth = 80.0f; - float magnifyHeight = 60.0f; - - mouseX -= magnifyWidth / 2; - mouseY -= magnifyHeight / 2; - - float newWidth = magnifyWidth * magnification; - float newHeight = magnifyHeight * magnification; - - // Magnification Texture Coordinates - float magnifyULeft = mouseX / (float)widgetWidth; - float magnifyURight = (mouseX + magnifyWidth) / (float)widgetWidth; - float magnifyVBottom = 1.0f - mouseY / (float)widgetHeight; - float magnifyVTop = 1.0f - (mouseY + magnifyHeight) / (float)widgetHeight; - - // Coordinates of magnification overlay - float newMouseX = (mouseX + magnifyWidth / 2) - newWidth / 2.0f; - float newMouseY = (mouseY + magnifyHeight / 2) + newHeight / 2.0f; - - // Get angle on the UI - float leftAngle = (newMouseX / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; - float rightAngle = ((newMouseX + newWidth) / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; - - float bottomAngle = (newMouseY / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; - float topAngle = ((newMouseY - newHeight) / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; - - // Get position on hemisphere using angle - if (_uiType == HEMISPHERE) { - + //Get new UV coordinates from our magnification window - float newULeft = newMouseX / widgetWidth; - float newURight = (newMouseX + newWidth) / widgetWidth; - float newVBottom = 1.0 - newMouseY / widgetHeight; - float newVTop = 1.0 - (newMouseY - newHeight) / widgetHeight; + float newULeft = mouseX / widgetWidth; + float newURight = (mouseX + reticleSize) / widgetWidth; + float newVBottom = 1.0 - mouseY / widgetHeight; + float newVTop = 1.0 - (mouseY - reticleSize) / widgetHeight; // Project our position onto the hemisphere using the UV coordinates float lX = sin((newULeft - 0.5f) * _textureFov); @@ -534,40 +384,86 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) glBegin(GL_QUADS); - glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(lX, tY, -tlZ); - glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rX, tY, -trZ); - glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rX, bY, -brZ); - glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(lX, bY, -blZ); - - glEnd(); - - } else { - leftX = sin(leftAngle) * _distance; - rightX = sin(rightAngle) * _distance; - leftZ = -cos(leftAngle) * _distance; - rightZ = -cos(rightAngle) * _distance; - if (_uiType == CURVED_SEMICIRCLE) { - topZ = -cos(topAngle * overlayAspectRatio) * _distance; - bottomZ = -cos(bottomAngle * overlayAspectRatio) * _distance; - } else { - // Dont want to use topZ or bottomZ for SEMICIRCLE - topZ = -99999; - bottomZ = -99999; - } - - float bottomY = (1.0 - newMouseY / (float)widgetHeight) * halfOverlayHeight * 2.0f - halfOverlayHeight; - float topY = bottomY + (newHeight / widgetHeight) * halfOverlayHeight * 2; - - //TODO: Remove immediate mode in favor of VBO - glBegin(GL_QUADS); - - glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(leftX, topY, max(topZ, leftZ)); - glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rightX, topY, max(topZ, rightZ)); - glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rightX, bottomY, max(bottomZ, rightZ)); - glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(leftX, bottomY, max(bottomZ, leftZ)); + glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); + + glTexCoord2f(0.0f, 0.0f); glVertex3f(lX, tY, -tlZ); + glTexCoord2f(1.0f, 0.0f); glVertex3f(rX, tY, -trZ); + glTexCoord2f(1.0f, 1.0f); glVertex3f(rX, bY, -brZ); + glTexCoord2f(0.0f, 1.0f); glVertex3f(lX, bY, -blZ); glEnd(); + } + glEnable(GL_DEPTH_TEST); +} + +//Renders a small magnification of the currently bound texture at the coordinates +void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) +{ + Application* application = Application::getInstance(); + QGLWidget* glWidget = application->getGLWidget(); + + + const int widgetWidth = glWidget->width(); + const int widgetHeight = glWidget->height(); + const float magnification = 4.0f; + + float magnifyWidth = 80.0f; + float magnifyHeight = 60.0f; + + mouseX -= magnifyWidth / 2; + mouseY -= magnifyHeight / 2; + + float newWidth = magnifyWidth * magnification; + float newHeight = magnifyHeight * magnification; + + // Magnification Texture Coordinates + float magnifyULeft = mouseX / (float)widgetWidth; + float magnifyURight = (mouseX + magnifyWidth) / (float)widgetWidth; + float magnifyVBottom = 1.0f - mouseY / (float)widgetHeight; + float magnifyVTop = 1.0f - (mouseY + magnifyHeight) / (float)widgetHeight; + + // Coordinates of magnification overlay + float newMouseX = (mouseX + magnifyWidth / 2) - newWidth / 2.0f; + float newMouseY = (mouseY + magnifyHeight / 2) + newHeight / 2.0f; + + // Get position on hemisphere using angle + + //Get new UV coordinates from our magnification window + float newULeft = newMouseX / widgetWidth; + float newURight = (newMouseX + newWidth) / widgetWidth; + float newVBottom = 1.0 - newMouseY / widgetHeight; + float newVTop = 1.0 - (newMouseY - newHeight) / widgetHeight; + + // Project our position onto the hemisphere using the UV coordinates + float lX = sin((newULeft - 0.5f) * _textureFov); + float rX = sin((newURight - 0.5f) * _textureFov); + float bY = sin((newVBottom - 0.5f) * _textureFov); + float tY = sin((newVTop - 0.5f) * _textureFov); + + float dist; + //Bottom Left + dist = sqrt(lX * lX + bY * bY); + float blZ = sqrt(1.0f - dist * dist); + //Top Left + dist = sqrt(lX * lX + tY * tY); + float tlZ = sqrt(1.0f - dist * dist); + //Bottom Right + dist = sqrt(rX * rX + bY * bY); + float brZ = sqrt(1.0f - dist * dist); + //Top Right + dist = sqrt(rX * rX + tY * tY); + float trZ = sqrt(1.0f - dist * dist); + + glBegin(GL_QUADS); + + glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(lX, tY, -tlZ); + glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rX, tY, -trZ); + glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rX, bY, -brZ); + glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(lX, bY, -blZ); + + glEnd(); + } void ApplicationOverlay::renderAudioMeter() { diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 71f78d606d..53a0125dae 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -19,8 +19,6 @@ class QOpenGLFramebufferObject; class ApplicationOverlay { public: - enum UIType { HEMISPHERE, SEMICIRCLE, CURVED_SEMICIRCLE }; - ApplicationOverlay(); ~ApplicationOverlay(); @@ -31,9 +29,7 @@ public: // Getters QOpenGLFramebufferObject* getFramebufferObject(); - - void setUIType(UIType uiType) { _uiType = uiType; } - + private: // Interleaved vertex data struct TextureVertex { @@ -56,7 +52,6 @@ private: float _oculusAngle; float _distance; float _textureFov; - UIType _uiType; int _mouseX[2]; int _mouseY[2]; int _numMagnifiers; From bef625d237a14dd12bf36c79d61518ab919bc515 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 16:13:43 -0700 Subject: [PATCH 10/12] Removed pointless Code --- interface/src/ui/ApplicationOverlay.cpp | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 2ef5665bee..13630290fd 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -234,33 +234,10 @@ void ApplicationOverlay::renderPointers() { if (OculusManager::isConnected() && application->getLastMouseMoveType() == QEvent::MouseMove) { - const float pointerWidth = 20; - const float pointerHeight = 20; - + //If we are in oculus, render reticle later _numMagnifiers = 1; _mouseX[0] = application->getMouseX(); _mouseY[0] = application->getMouseY(); - - //If we are in oculus, render reticle later - - - if (!OculusManager::isConnected()) { - - mouseX -= pointerWidth / 2.0f; - mouseY += pointerHeight / 2.0f; - - glBegin(GL_QUADS); - - glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); - - //Horizontal crosshair - glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY); - glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY); - glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight); - glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - pointerHeight); - - glEnd(); - } } else if (application->getLastMouseMoveType() == CONTROLLER_MOVE_EVENT && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { //only render controller pointer if we aren't already rendering a mouse pointer renderControllerPointers(); From 94e58e9559bff84fd60233acdf3aaa3f9b4d314f Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 16 Jun 2014 10:04:56 -0700 Subject: [PATCH 11/12] Fixed coding standard and made getCursorPixelRangeMultiplier more clear --- interface/src/devices/SixenseManager.cpp | 10 ++++++++-- interface/src/ui/ApplicationOverlay.cpp | 8 +++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index ed55dc4c66..43dadaef1c 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -185,9 +185,14 @@ void SixenseManager::update(float deltaTime) { #endif // HAVE_SIXENSE } +const float MAXIMUM_PIXEL_RANGE_MULT = 2.0f; +const float MINIMUM_PIXEL_RANGE_MULT = 0.4f; +const float RANGE_MULT = (MAXIMUM_PIXEL_RANGE_MULT - MINIMUM_PIXEL_RANGE_MULT) * 0.01; + +//Returns a multiplier to be applied to the cursor range for the controllers float SixenseManager::getCursorPixelRangeMultiplier() const { - //scales (0,100) to (0.4,2.0) - return ((Menu::getInstance()->getSixenseReticleMoveSpeed()) * 0.008f + 0.2f) * 2.0f; + //scales (0,100) to (MINIMUM_PIXEL_RANGE_MULT, MAXIMUM_PIXEL_RANGE_MULT) + return Menu::getInstance()->getSixenseReticleMoveSpeed() * RANGE_MULT + MINIMUM_PIXEL_RANGE_MULT; } #ifdef HAVE_SIXENSE @@ -356,6 +361,7 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { float xAngle = (atan2(direction.z, direction.x) + M_PI_2); float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); + // Get the pixel range over which the xAngle and yAngle are scaled float cursorRange = widget->width() * getCursorPixelRangeMultiplier(); pos.setX(widget->width() / 2.0f + cursorRange * xAngle); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 13630290fd..aedc81facc 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -46,6 +46,8 @@ ApplicationOverlay::~ApplicationOverlay() { const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; +const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f }; + // Renders the overlays either to a texture or to the screen void ApplicationOverlay::renderOverlay(bool renderToTexture) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); @@ -222,7 +224,6 @@ void ApplicationOverlay::renderPointers() { int mouseX = application->getMouseX(); int mouseY = application->getMouseY(); - //lazily load crosshair texture if (_crosshairTexture == 0) { _crosshairTexture = Application::getInstance()->getGLWidget()->bindTexture(QImage(Application::resourcesPath() + "images/sixense-reticle.png")); @@ -274,6 +275,7 @@ void ApplicationOverlay::renderControllerPointers() { float xAngle = (atan2(direction.z, direction.x) + M_PI_2) ; float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); + // Get the pixel range over which the xAngle and yAngle are scaled float cursorRange = glWidget->width() * application->getSixenseManager()->getCursorPixelRangeMultiplier(); int mouseX = glWidget->width() / 2.0f + cursorRange * xAngle; @@ -303,7 +305,7 @@ void ApplicationOverlay::renderControllerPointers() { glBegin(GL_QUADS); - glColor3f(0.0f, 198.0f/255.0f, 244.0f/255.0f); + glColor3f(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2]); glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY); glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY); @@ -361,7 +363,7 @@ void ApplicationOverlay::renderControllerPointersOculus() { glBegin(GL_QUADS); - glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); + glColor3f(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2]); glTexCoord2f(0.0f, 0.0f); glVertex3f(lX, tY, -tlZ); glTexCoord2f(1.0f, 0.0f); glVertex3f(rX, tY, -trZ); From 031fe323ea5e98b5d37167da3c983ce7960d45b8 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 16 Jun 2014 10:12:09 -0700 Subject: [PATCH 12/12] Got rid of some verbosity --- interface/src/devices/SixenseManager.cpp | 13 +++++++------ interface/src/devices/SixenseManager.h | 2 +- interface/src/ui/ApplicationOverlay.cpp | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 43dadaef1c..2c62a58800 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -185,14 +185,15 @@ void SixenseManager::update(float deltaTime) { #endif // HAVE_SIXENSE } -const float MAXIMUM_PIXEL_RANGE_MULT = 2.0f; -const float MINIMUM_PIXEL_RANGE_MULT = 0.4f; -const float RANGE_MULT = (MAXIMUM_PIXEL_RANGE_MULT - MINIMUM_PIXEL_RANGE_MULT) * 0.01; +//Constants for getCursorPixelRangeMultiplier() +const float MIN_PIXEL_RANGE_MULT = 0.4f; +const float MAX_PIXEL_RANGE_MULT = 2.0f; +const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01; //Returns a multiplier to be applied to the cursor range for the controllers -float SixenseManager::getCursorPixelRangeMultiplier() const { +float SixenseManager::getCursorPixelRangeMult() const { //scales (0,100) to (MINIMUM_PIXEL_RANGE_MULT, MAXIMUM_PIXEL_RANGE_MULT) - return Menu::getInstance()->getSixenseReticleMoveSpeed() * RANGE_MULT + MINIMUM_PIXEL_RANGE_MULT; + return Menu::getInstance()->getSixenseReticleMoveSpeed() * RANGE_MULT + MIN_PIXEL_RANGE_MULT; } #ifdef HAVE_SIXENSE @@ -362,7 +363,7 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); // Get the pixel range over which the xAngle and yAngle are scaled - float cursorRange = widget->width() * getCursorPixelRangeMultiplier(); + float cursorRange = widget->width() * getCursorPixelRangeMult(); pos.setX(widget->width() / 2.0f + cursorRange * xAngle); pos.setY(widget->height() / 2.0f + cursorRange * yAngle); diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index 41e061a25b..8803c2c006 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -42,7 +42,7 @@ public: ~SixenseManager(); void update(float deltaTime); - float getCursorPixelRangeMultiplier() const; + float getCursorPixelRangeMult() const; public slots: diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index aedc81facc..471ff20d66 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -276,7 +276,7 @@ void ApplicationOverlay::renderControllerPointers() { float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); // Get the pixel range over which the xAngle and yAngle are scaled - float cursorRange = glWidget->width() * application->getSixenseManager()->getCursorPixelRangeMultiplier(); + float cursorRange = glWidget->width() * application->getSixenseManager()->getCursorPixelRangeMult(); int mouseX = glWidget->width() / 2.0f + cursorRange * xAngle; int mouseY = glWidget->height() / 2.0f + cursorRange * yAngle;