// Draws the FBO texture for the screen void ApplicationOverlay::displayOverlayTexture(Camera& whichCamera) { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); glEnable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture()); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(0, glWidget->width(), glWidget->height(), 0); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2i(0, glWidget->height()); glTexCoord2f(1, 0); glVertex2i(glWidget->width(), glWidget->height()); glTexCoord2f(1, 1); glVertex2i(glWidget->width(), 0); glTexCoord2f(0, 1); glVertex2i(0, 0); glEnd(); glPopMatrix(); glDisable(GL_TEXTURE_2D); }
// 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()"); Application* application = Application::getInstance(); Overlays& overlays = application->getOverlays(); QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); if (renderToTexture) { getFramebufferObject()->bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Render 2D overlay glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(0, glWidget->width(), glWidget->height(), 0); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); renderAudioMeter(); if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse)) { myAvatar->renderHeadMouse(glWidget->width(), glWidget->height()); } renderStatsAndLogs(); // give external parties a change to hook in emit application->renderingOverlay(); overlays.render2D(); renderPointers(); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); if (renderToTexture) { getFramebufferObject()->release(); } }
void ApplicationOverlay::renderStatsAndLogs() { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); const OctreePacketProcessor& octreePacketProcessor = application->getOctreePacketProcessor(); BandwidthMeter* bandwidthMeter = application->getBandwidthMeter(); NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay(); // Display stats and log text onscreen glLineWidth(1.0f); glPointSize(1.0f); if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { // let's set horizontal offset to give stats some margin to mirror int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; int voxelPacketsToProcess = octreePacketProcessor.packetsToProcessCount(); // Onscreen text about position, servers, etc Stats::getInstance()->display(WHITE_TEXT, horizontalOffset, application->getFps(), application->getPacketsPerSecond(), application->getBytesPerSecond(), voxelPacketsToProcess); // Bandwidth meter if (Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth)) { Stats::drawBackground(0x33333399, glWidget->width() - 296, glWidget->height() - 68, 296, 68); bandwidthMeter->render(glWidget->width(), glWidget->height()); } } // Show on-screen msec timer if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) { char frameTimer[10]; quint64 mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5); sprintf(frameTimer, "%d\n", (int)(mSecsNow % 1000)); int timerBottom = (Menu::getInstance()->isOptionChecked(MenuOption::Stats) && Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth)) ? 80 : 20; drawText(glWidget->width() - 100, glWidget->height() - timerBottom, 0.30f, 0.0f, 0, frameTimer, WHITE_TEXT); } nodeBoundsDisplay.drawOverlay(); }
//Injecting mouse movements and clicks void SixenseManager::emulateMouse(PalmData* palm, int index) { Application* application = Application::getInstance(); MyAvatar* avatar = application->getAvatar(); QGLWidget* widget = application->getGLWidget(); QPoint pos; Qt::MouseButton bumperButton; Qt::MouseButton triggerButton; if (Menu::getInstance()->getInvertSixenseButtons()) { bumperButton = Qt::LeftButton; triggerButton = Qt::RightButton; } else { bumperButton = Qt::RightButton; triggerButton = Qt::LeftButton; } if (OculusManager::isConnected()) { pos = application->getApplicationOverlay().getOculusPalmClickLocation(palm); } else { // Get directon relative to avatar orientation glm::vec3 direction = glm::inverse(avatar->getOrientation()) * palm->getFingerDirection(); // 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)); // Get the pixel range over which the xAngle and yAngle are scaled float cursorRange = widget->width() * getCursorPixelRangeMult(); 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. if (pos.x() == INT_MAX) { if (_bumperPressed[index]) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, bumperButton, bumperButton, 0); application->mouseReleaseEvent(&mouseEvent); _bumperPressed[index] = false; } if (_triggerPressed[index]) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, triggerButton, triggerButton, 0); application->mouseReleaseEvent(&mouseEvent); _triggerPressed[index] = false; } return; } //If position has changed, emit a mouse move to the application if (pos.x() != _oldX[index] || pos.y() != _oldY[index]) { QMouseEvent mouseEvent(static_cast<QEvent::Type>(CONTROLLER_MOVE_EVENT), pos, Qt::NoButton, Qt::NoButton, 0); //Only send the mouse event if the opposite left button isnt held down. //This is specifically for edit voxels if (triggerButton == Qt::LeftButton) { if (!_triggerPressed[(int)(!index)]) { application->mouseMoveEvent(&mouseEvent); } } else { if (!_bumperPressed[(int)(!index)]) { application->mouseMoveEvent(&mouseEvent); } } } _oldX[index] = pos.x(); _oldY[index] = pos.y(); //We need separate coordinates for clicks, since we need to check if //a magnification window was clicked on int clickX = pos.x(); int clickY = pos.y(); //Checks for magnification window click application->getApplicationOverlay().getClickLocation(clickX, clickY); //Set pos to the new click location, which may be the same if no magnification window is open pos.setX(clickX); pos.setY(clickY); //Check for bumper press ( Right Click ) if (palm->getControllerButtons() & BUTTON_FWD) { if (!_bumperPressed[index]) { _bumperPressed[index] = true; QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, bumperButton, bumperButton, 0); application->mousePressEvent(&mouseEvent); } } else if (_bumperPressed[index]) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, bumperButton, bumperButton, 0); application->mouseReleaseEvent(&mouseEvent); _bumperPressed[index] = false; } //Check for trigger press ( Left Click ) if (palm->getTrigger() == 1.0f) { if (!_triggerPressed[index]) { _triggerPressed[index] = true; QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, triggerButton, triggerButton, 0); application->mousePressEvent(&mouseEvent); } } else if (_triggerPressed[index]) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, triggerButton, triggerButton, 0); application->mouseReleaseEvent(&mouseEvent); _triggerPressed[index] = false; } }
void ApplicationOverlay::renderAudioMeter() { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); Audio* audio = application->getAudio(); // Display a single screen-size quad to create an alpha blended 'collision' flash if (audio->getCollisionFlashesScreen()) { float collisionSoundMagnitude = audio->getCollisionSoundMagnitude(); const float VISIBLE_COLLISION_SOUND_MAGNITUDE = 0.5f; if (collisionSoundMagnitude > VISIBLE_COLLISION_SOUND_MAGNITUDE) { renderCollisionOverlay(glWidget->width(), glWidget->height(), audio->getCollisionSoundMagnitude()); } } // Audio VU Meter and Mute Icon const int MUTE_ICON_SIZE = 24; const int AUDIO_METER_INSET = 2; const int MUTE_ICON_PADDING = 10; const int AUDIO_METER_WIDTH = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - AUDIO_METER_INSET - MUTE_ICON_PADDING; const int AUDIO_METER_SCALE_WIDTH = AUDIO_METER_WIDTH - 2 * AUDIO_METER_INSET; const int AUDIO_METER_HEIGHT = 8; const int AUDIO_METER_GAP = 5; const int AUDIO_METER_X = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_INSET + AUDIO_METER_GAP; int audioMeterY; if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { audioMeterY = MIRROR_VIEW_HEIGHT + AUDIO_METER_GAP + MUTE_ICON_PADDING; } else { audioMeterY = AUDIO_METER_GAP + MUTE_ICON_PADDING; } const float AUDIO_METER_BLUE[] = { 0.0, 0.0, 1.0 }; const float AUDIO_METER_GREEN[] = { 0.0, 1.0, 0.0 }; const float AUDIO_METER_RED[] = { 1.0, 0.0, 0.0 }; const float AUDIO_GREEN_START = 0.25 * AUDIO_METER_SCALE_WIDTH; const float AUDIO_RED_START = 0.80 * AUDIO_METER_SCALE_WIDTH; const float CLIPPING_INDICATOR_TIME = 1.0f; const float AUDIO_METER_AVERAGING = 0.5; const float LOG2 = log(2.f); const float METER_LOUDNESS_SCALE = 2.8f / 5.f; const float LOG2_LOUDNESS_FLOOR = 11.f; float audioLevel = 0.f; float loudness = audio->getLastInputLoudness() + 1.f; _trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.f - AUDIO_METER_AVERAGING) * loudness; float log2loudness = log(_trailingAudioLoudness) / LOG2; if (log2loudness <= LOG2_LOUDNESS_FLOOR) { audioLevel = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE * AUDIO_METER_SCALE_WIDTH; } else { audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.f)) * METER_LOUDNESS_SCALE * AUDIO_METER_SCALE_WIDTH; } if (audioLevel > AUDIO_METER_SCALE_WIDTH) { audioLevel = AUDIO_METER_SCALE_WIDTH; } bool isClipping = ((audio->getTimeSinceLastClip() > 0.f) && (audio->getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)); if ((audio->getTimeSinceLastClip() > 0.f) && (audio->getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)) { const float MAX_MAGNITUDE = 0.7f; float magnitude = MAX_MAGNITUDE * (1 - audio->getTimeSinceLastClip() / CLIPPING_INDICATOR_TIME); renderCollisionOverlay(glWidget->width(), glWidget->height(), magnitude, 1.0f); } audio->renderToolBox(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, Menu::getInstance()->isOptionChecked(MenuOption::Mirror)); audio->renderScope(glWidget->width(), glWidget->height()); glBegin(GL_QUADS); if (isClipping) { glColor3f(1, 0, 0); } else { glColor3f(0.475f, 0.475f, 0.475f); } audioMeterY += AUDIO_METER_HEIGHT; glColor3f(0, 0, 0); // Draw audio meter background Quad glVertex2i(AUDIO_METER_X, audioMeterY); glVertex2i(AUDIO_METER_X + AUDIO_METER_WIDTH, audioMeterY); glVertex2i(AUDIO_METER_X + AUDIO_METER_WIDTH, audioMeterY + AUDIO_METER_HEIGHT); glVertex2i(AUDIO_METER_X, audioMeterY + AUDIO_METER_HEIGHT); if (audioLevel > AUDIO_RED_START) { if (!isClipping) { glColor3fv(AUDIO_METER_RED); } else { glColor3f(1, 1, 1); } // Draw Red Quad glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_RED_START, audioMeterY + AUDIO_METER_INSET); glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET); glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_RED_START, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); audioLevel = AUDIO_RED_START; } if (audioLevel > AUDIO_GREEN_START) { if (!isClipping) { glColor3fv(AUDIO_METER_GREEN); } else { glColor3f(1, 1, 1); } // Draw Green Quad glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_GREEN_START, audioMeterY + AUDIO_METER_INSET); glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET); glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_GREEN_START, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); audioLevel = AUDIO_GREEN_START; } // Draw Blue Quad if (!isClipping) { glColor3fv(AUDIO_METER_BLUE); } else { glColor3f(1, 1, 1); } // Draw Blue (low level) quad glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, audioMeterY + AUDIO_METER_INSET); glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET); glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); glEnd(); }
//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; 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; //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; } const float halfMagnifyHeight = magnifyHeight / 2.0f; 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; // 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(); } 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)); glEnd(); } }
void ApplicationOverlay::renderControllerPointer() { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); 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; if (palmIndex >= handData->getPalms().size()) { return; } if (handData->getPalms()[palmIndex].isActive()) { palmData = &handData->getPalms()[palmIndex]; } else { continue; } // 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); float cursorRange = glWidget->width(); int mouseX = cursorRange * xAngle; int mouseY = 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()) { continue; } 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; _mouseX[_numMagnifiers] = mouseX; _mouseY[_numMagnifiers] = mouseY; _numMagnifiers++; } mouseX -= pointerWidth / 2.0f; mouseY += pointerHeight / 2.0f; glBegin(GL_QUADS); glColor3f(0.0f, 0.0f, 1.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); glEnd(); } }
// Draws the FBO texture for Oculus rift. TODO: Draw a curved texture instead of plane. void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); 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); glEnable(GL_BLEND); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture()); glEnable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glEnable(GL_TEXTURE_2D); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); // Transform to world space glm::quat rotation = whichCamera.getRotation(); glm::vec3 axis2 = glm::axis(rotation); glRotatef(-glm::degrees(glm::angle(rotation)), axis2.x, axis2.y, axis2.z); glTranslatef(viewMatrixTranslation.x, viewMatrixTranslation.y, viewMatrixTranslation.z); // Translate to the front of the camera glm::vec3 pos = whichCamera.getPosition(); glm::quat rot = myAvatar->getOrientation(); glm::vec3 axis = glm::axis(rot); 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); float leftX, rightX, leftZ, rightZ, topZ, bottomZ; //Draw the magnifiers for (int i = 0; i < _numMagnifiers; i++) { renderMagnifier(_mouseX[i], _mouseY[i]); } 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(); } glPopMatrix(); glDepthMask(GL_TRUE); glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); glEnable(GL_LIGHTING); }
void NodeBounds::draw() { if (!(_showVoxelNodes || _showModelNodes || _showParticleNodes)) { _overlayText[0] = '\0'; return; } NodeToJurisdictionMap& voxelServerJurisdictions = Application::getInstance()->getVoxelServerJurisdictions(); NodeToJurisdictionMap& modelServerJurisdictions = Application::getInstance()->getModelServerJurisdictions(); NodeToJurisdictionMap& particleServerJurisdictions = Application::getInstance()->getParticleServerJurisdictions(); NodeToJurisdictionMap* serverJurisdictions; // Compute ray to find selected nodes later on. We can't use the pre-computed ray in Application because it centers // itself after the cursor disappears. Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); float mouseX = application->getMouseX() / (float)glWidget->width(); float mouseY = application->getMouseY() / (float)glWidget->height(); glm::vec3 mouseRayOrigin; glm::vec3 mouseRayDirection; application->getViewFrustum()->computePickRay(mouseX, mouseY, mouseRayOrigin, mouseRayDirection); // Variables to keep track of the selected node and properties to draw the cube later if needed Node* selectedNode = NULL; float selectedDistance = FLT_MAX; bool selectedIsInside = true; glm::vec3 selectedCenter; float selectedScale = 0; NodeList* nodeList = NodeList::getInstance(); foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { NodeType_t nodeType = node->getType(); if (nodeType == NodeType::VoxelServer && _showVoxelNodes) { serverJurisdictions = &voxelServerJurisdictions; } else if (nodeType == NodeType::ModelServer && _showModelNodes) { serverJurisdictions = &modelServerJurisdictions; } else if (nodeType == NodeType::ParticleServer && _showParticleNodes) { serverJurisdictions = &particleServerJurisdictions; } else { continue; } QUuid nodeUUID = node->getUUID(); if (serverJurisdictions->find(nodeUUID) != serverJurisdictions->end()) { const JurisdictionMap& map = serverJurisdictions->value(nodeUUID); unsigned char* rootCode = map.getRootOctalCode(); if (rootCode) { VoxelPositionSize rootDetails; voxelDetailsForCode(rootCode, rootDetails); glm::vec3 location(rootDetails.x, rootDetails.y, rootDetails.z); location *= (float)TREE_SCALE; AABox serverBounds(location, rootDetails.s * TREE_SCALE); glm::vec3 center = serverBounds.getVertex(BOTTOM_RIGHT_NEAR) + ((serverBounds.getVertex(TOP_LEFT_FAR) - serverBounds.getVertex(BOTTOM_RIGHT_NEAR)) / 2.0f); const float VOXEL_NODE_SCALE = 1.00f; const float MODEL_NODE_SCALE = 0.99f; const float PARTICLE_NODE_SCALE = 0.98f; float scaleFactor = rootDetails.s * TREE_SCALE; // Scale by 0.92 - 1.00 depending on the scale of the node. This allows smaller nodes to scale in // a bit and not overlap larger nodes. scaleFactor *= 0.92 + (rootDetails.s * 0.08); // Scale different node types slightly differently because it's common for them to overlap. if (nodeType == NodeType::VoxelServer) { scaleFactor *= VOXEL_NODE_SCALE; } else if (nodeType == NodeType::ModelServer) { scaleFactor *= MODEL_NODE_SCALE; } else { scaleFactor *= PARTICLE_NODE_SCALE; } float red, green, blue; getColorForNodeType(nodeType, red, green, blue); drawNodeBorder(center, scaleFactor, red, green, blue); float distance; BoxFace face; bool inside = serverBounds.contains(mouseRayOrigin); bool colliding = serverBounds.findRayIntersection(mouseRayOrigin, mouseRayDirection, distance, face); // If the camera is inside a node it will be "selected" if you don't have your cursor over another node // that you aren't inside. if (colliding && (!selectedNode || (!inside && (distance < selectedDistance || selectedIsInside)))) { selectedNode = node.data(); selectedDistance = distance; selectedIsInside = inside; selectedCenter = center; selectedScale = scaleFactor; } } } } if (selectedNode) { glPushMatrix(); glTranslatef(selectedCenter.x, selectedCenter.y, selectedCenter.z); glScalef(selectedScale, selectedScale, selectedScale); NodeType_t selectedNodeType = selectedNode->getType(); float red, green, blue; getColorForNodeType(selectedNode->getType(), red, green, blue); glColor4f(red, green, blue, 0.2); glutSolidCube(1.0); glPopMatrix(); HifiSockAddr addr = selectedNode->getPublicSocket(); QString overlay = QString("%1:%2 %3ms") .arg(addr.getAddress().toString()) .arg(addr.getPort()) .arg(selectedNode->getPingMs()) .left(MAX_OVERLAY_TEXT_LENGTH); // Ideally we'd just use a QString, but I ran into weird blinking issues using // constData() directly, as if the data was being overwritten. strcpy(_overlayText, overlay.toLocal8Bit().constData()); } else { _overlayText[0] = '\0'; } }
glm::vec2 ControllerScriptingInterface::getViewportDimensions() const { QGLWidget* widget = Application::getInstance()->getGLWidget(); return glm::vec2(widget->width(), widget->height()); }
// display expanded or contracted stats void Stats::display( const float* color, int horizontalOffset, float fps, int packetsPerSecond, int bytesPerSecond, int voxelPacketsToProcess) { QGLWidget* glWidget = Application::getInstance()->getGLWidget(); unsigned int backgroundColor = 0x33333399; int verticalOffset = 0, lines = 0; QLocale locale(QLocale::English); std::stringstream voxelStats; if (_lastHorizontalOffset != horizontalOffset) { resetWidth(glWidget->width(), horizontalOffset); _lastHorizontalOffset = horizontalOffset; } glPointSize(1.0f); // we need to take one avatar out so we don't include ourselves int totalAvatars = Application::getInstance()->getAvatarManager().size() - 1; int totalServers = NodeList::getInstance()->size(); lines = _expanded ? 5 : 3; drawBackground(backgroundColor, horizontalOffset, 0, _generalStatsWidth, lines * STATS_PELS_PER_LINE + 10); horizontalOffset += 5; char serverNodes[30]; sprintf(serverNodes, "Servers: %d", totalServers); char avatarNodes[30]; sprintf(avatarNodes, "Avatars: %d", totalAvatars); char framesPerSecond[30]; sprintf(framesPerSecond, "Framerate: %3.0f FPS", fps); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, serverNodes, color); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarNodes, color); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, framesPerSecond, color); if (_expanded) { char packetsPerSecondString[30]; sprintf(packetsPerSecondString, "Pkts/sec: %d", packetsPerSecond); char averageMegabitsPerSecond[30]; sprintf(averageMegabitsPerSecond, "Mbps: %3.2f", (float)bytesPerSecond * 8.f / 1000000.f); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, packetsPerSecondString, color); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, averageMegabitsPerSecond, color); } verticalOffset = 0; horizontalOffset = _lastHorizontalOffset + _generalStatsWidth +1; if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { int pingAudio = 0, pingAvatar = 0, pingVoxel = 0, pingVoxelMax = 0; NodeList* nodeList = NodeList::getInstance(); SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); pingAudio = audioMixerNode ? audioMixerNode->getPingMs() : 0; pingAvatar = avatarMixerNode ? avatarMixerNode->getPingMs() : 0; // Now handle voxel servers, since there could be more than one, we average their ping times unsigned long totalPingVoxel = 0; int voxelServerCount = 0; foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { if (node->getType() == NodeType::VoxelServer) { totalPingVoxel += node->getPingMs(); voxelServerCount++; if (pingVoxelMax < node->getPingMs()) { pingVoxelMax = node->getPingMs(); } } } if (voxelServerCount) { pingVoxel = totalPingVoxel/voxelServerCount; } lines = _expanded ? 4 : 3; drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, lines * STATS_PELS_PER_LINE + 10); horizontalOffset += 5; Audio* audio = Application::getInstance()->getAudio(); char audioJitter[30]; sprintf(audioJitter, "Buffer msecs %.1f", (float) (audio->getNetworkBufferLengthSamplesPerChannel() + (float) audio->getJitterBufferSamples()) / (float) audio->getNetworkSampleRate() * 1000.f); drawText(30, glWidget->height() - 22, 0.10f, 0.f, 2.f, audioJitter, color); char audioPing[30]; sprintf(audioPing, "Audio ping: %d", pingAudio); char avatarPing[30]; sprintf(avatarPing, "Avatar ping: %d", pingAvatar); char voxelAvgPing[30]; sprintf(voxelAvgPing, "Voxel avg ping: %d", pingVoxel); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, audioPing, color); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarPing, color); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, voxelAvgPing, color); if (_expanded) { char voxelMaxPing[30]; sprintf(voxelMaxPing, "Voxel max ping: %d", pingVoxelMax); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, voxelMaxPing, color); } verticalOffset = 0; horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + 2; }