diff --git a/examples/flockingBirds.js b/examples/flockingBirds.js index 17c60d88aa..3fa8681abe 100644 --- a/examples/flockingBirds.js +++ b/examples/flockingBirds.js @@ -51,7 +51,7 @@ var flockGravity = { x: 0, y: -1, z: 0}; var enableRandomXZThrust = false; // leading birds randomly decide to thrust in some random direction. var enableSomeBirdsLead = false; // birds randomly decide not fly toward flock, causing other birds to follow var leaders = 0; // number of birds leading -var PROBABILITY_TO_LEAD = 0.1; // probabolity a bird will choose to lead +var PROBABILITY_TO_LEAD = 0.1; // probability a bird will choose to lead var birds = new Array(); // array of bird state data diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0e1c80d3e9..3222b2461a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1007,6 +1007,12 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_At: Menu::getInstance()->goTo(); break; + case Qt::Key_B: + _applicationOverlay.setOculusAngle(_applicationOverlay.getOculusAngle() - RADIANS_PER_DEGREE); + break; + case Qt::Key_N: + _applicationOverlay.setOculusAngle(_applicationOverlay.getOculusAngle() + RADIANS_PER_DEGREE); + break; default: event->ignore(); break; @@ -1097,7 +1103,8 @@ void Application::mouseMoveEvent(QMouseEvent* event) { _lastMouseMove = usecTimestampNow(); - if (_mouseHidden) { + + if (_mouseHidden && !OculusManager::isConnected) { getGLWidget()->setCursor(Qt::ArrowCursor); _mouseHidden = false; _seenMouseMove = true; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index deb052e595..5d9cd1f1c4 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -348,7 +348,6 @@ Menu::Menu() : QMenu* avatarOptionsMenu = developerMenu->addMenu("Avatar Options"); - addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::AllowOculusCameraModeChange, 0, false); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::AvatarsReceiveShadows, 0, true); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderSkeletonCollisionShapes); @@ -375,6 +374,10 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::GlowWhenSpeaking, 0, true); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::ChatCircling, 0, false); + QMenu* oculusOptionsMenu = developerMenu->addMenu("Oculus Options"); + addCheckableActionToQMenuAndActionHash(oculusOptionsMenu, MenuOption::AllowOculusCameraModeChange, 0, false); + addCheckableActionToQMenuAndActionHash(oculusOptionsMenu, MenuOption::DisplayOculusOverlays, 0, true); + QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options"); addCheckableActionToQMenuAndActionHash(handOptionsMenu, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 020cd651d2..6bc9adef05 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -326,6 +326,7 @@ namespace MenuOption { const QString DisplayModelBounds = "Display Model Bounds"; const QString DisplayModelElementProxy = "Display Model Element Bounds"; const QString DisplayModelElementChildProxies = "Display Model Element Children"; + const QString DisplayOculusOverlays = "Display Oculus Overlays"; const QString DisplayTimingDetails = "Display Timing Details"; const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes"; const QString EchoLocalAudio = "Echo Local Audio"; diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index c8b2d0dcbc..fe8cc37168 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -86,7 +86,7 @@ void OculusManager::display(Camera& whichCamera) { // We only need to render the overlays to a texture once, then we just render the texture as a quad // PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay() applicationOverlay.renderOverlay(true); - const bool displayOverlays = false; + const bool displayOverlays = Menu::getInstance()->isOptionChecked(MenuOption::DisplayOculusOverlays); Application::getInstance()->getGlowEffect()->prepare(); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 740b310ff2..5b8560d061 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -19,7 +19,10 @@ #include "ui/Stats.h" -ApplicationOverlay::ApplicationOverlay() : _framebufferObject(NULL) { +ApplicationOverlay::ApplicationOverlay() : + _framebufferObject(NULL), + _oculusAngle(65.0f * RADIANS_PER_DEGREE), + _distance(0.5f) { } @@ -45,6 +48,10 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { BandwidthMeter* bandwidthMeter = application->getBandwidthMeter(); NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay(); + int mouseX = application->getMouseX(); + int mouseY = application->getMouseY(); + bool renderPointer = renderToTexture; + if (renderToTexture) { getFramebufferObject()->bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -220,6 +227,34 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { overlays.render2D(); + // Render a crosshair over the pointer when in Oculus + if (renderPointer) { + const float pointerWidth = 10; + const float pointerHeight = 10; + const float crossPad = 4; + + mouseX -= pointerWidth / 2.0f; + mouseY += pointerHeight / 2.0f; + + glBegin(GL_QUADS); + + glColor3f(1, 0, 0); + + //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); + + glEnd(); + } + glPopMatrix(); glMatrixMode(GL_MODELVIEW); @@ -227,7 +262,6 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { glEnable(GL_LIGHTING); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - if (renderToTexture) { getFramebufferObject()->release(); } @@ -262,6 +296,15 @@ void ApplicationOverlay::displayOverlayTexture(Camera& whichCamera) { glDisable(GL_TEXTURE_2D); } +// Fast helper functions +inline float max(float a, float b) { + return (a > b) ? a : b; +} + +inline float min(float a, float b) { + return (a < b) ? a : b; +} + // Draws the FBO texture for Oculus rift. TODO: Draw a curved texture instead of plane. void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { @@ -271,23 +314,37 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { MyAvatar* myAvatar = application->getAvatar(); const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); - // Calculates the world space width and height of the texture based on a desired FOV - const float overlayFov = whichCamera.getFieldOfView() * PI / 180.0f; - const float overlayDistance = 1; + int mouseX = application->getMouseX(); + int mouseY = application->getMouseY(); + int widgetWidth = glWidget->width(); + int widgetHeight = glWidget->height(); + float magnifyWidth = 80.0f; + float magnifyHeight = 60.0f; + const float magnification = 4.0f; + + // Get vertical FoV of the displayed overlay texture + const float halfVerticalAngle = _oculusAngle / 2.0f; + const float verticalAngle = halfVerticalAngle * 2.0f; const float overlayAspectRatio = glWidget->width() / (float)glWidget->height(); - const float overlayHeight = overlayDistance * tan(overlayFov); - const float overlayWidth = overlayHeight * overlayAspectRatio; - const float halfOverlayWidth = overlayWidth / 2; - const float halfOverlayHeight = overlayHeight / 2; + const float halfOverlayHeight = _distance * tan(halfVerticalAngle); + + // The more vertices, the better the curve + const int numHorizontalVertices = 20; + // U texture coordinate width at each quad + const float quadTexWidth = 1.0f / (numHorizontalVertices - 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; glActiveTexture(GL_TEXTURE0); - glDepthMask(GL_FALSE); + glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture()); - glDisable(GL_DEPTH_TEST); + glEnable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glEnable(GL_TEXTURE_2D); @@ -305,21 +362,102 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { glm::vec3 pos = whichCamera.getPosition(); glm::quat rot = myAvatar->getOrientation(); glm::vec3 axis = glm::axis(rot); - pos += rot * glm::vec3(0.0, 0.0, -overlayDistance); - + glTranslatef(pos.x, pos.y, pos.z); glRotatef(glm::degrees(glm::angle(rot)), axis.x, axis.y, axis.z); + glColor3f(1.0f, 1.0f, 1.0f); + + glDepthMask(GL_TRUE); + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.01f); + + //Draw the magnifying glass + + 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; + float tmp; + + // 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 halfMagnifyHeight = magnifyHeight / 2.0f; + + float leftX, rightX, leftZ, rightZ; + + // Get position on hemisphere using angle + leftX = sin(leftAngle) * _distance; + rightX = sin(rightAngle) * _distance; + leftZ = -cos(leftAngle) * _distance; + rightZ = -cos(rightAngle) * _distance; + + 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(1, 0); glVertex3f(-halfOverlayWidth, halfOverlayHeight, 0); - glTexCoord2f(0, 0); glVertex3f(halfOverlayWidth, halfOverlayHeight, 0); - glTexCoord2f(0, 1); glVertex3f(halfOverlayWidth, -halfOverlayHeight, 0); - glTexCoord2f(1, 1); glVertex3f(-halfOverlayWidth, -halfOverlayHeight, 0); + + glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(leftX, topY, leftZ); + glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rightX, topY, rightZ); + glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rightX, bottomY, rightZ); + glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(leftX, bottomY, leftZ); + + glEnd(); + + glDepthMask(GL_FALSE); + glDisable(GL_ALPHA_TEST); + + //TODO: Remove immediate mode in favor of VBO + glBegin(GL_QUADS); + // Place the vertices in a semicircle curve around the camera + for (int i = 0; i < numHorizontalVertices-1; i++) { + + // 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; + + glTexCoord2f(quadTexWidth * i, 1); glVertex3f(leftX, halfOverlayHeight, leftZ); + glTexCoord2f(quadTexWidth * (i + 1), 1); glVertex3f(rightX, halfOverlayHeight, rightZ); + glTexCoord2f(quadTexWidth * (i + 1), 0); glVertex3f(rightX, -halfOverlayHeight, rightZ); + glTexCoord2f(quadTexWidth * i, 0); glVertex3f(leftX, -halfOverlayHeight, leftZ); + } + glEnd(); glPopMatrix(); - glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index cdc4065f45..c617e03457 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -28,11 +28,17 @@ public: // Getters QOpenGLFramebufferObject* getFramebufferObject(); + float getOculusAngle() const { return _oculusAngle; } + + // Setters + void setOculusAngle(float oculusAngle) { _oculusAngle = oculusAngle; } private: QOpenGLFramebufferObject* _framebufferObject; float _trailingAudioLoudness; + float _oculusAngle; + float _distance; }; #endif // hifi_ApplicationOverlay_h \ No newline at end of file