void Stats::resetWidth(int width, int horizontalOffset) { GLCanvas* glWidget = Application::getInstance()->getGLWidget(); int extraSpace = glWidget->width() - horizontalOffset -2 - STATS_GENERAL_MIN_WIDTH - (Menu::getInstance()->isOptionChecked(MenuOption::TestPing) ? STATS_PING_MIN_WIDTH -1 : 0) - STATS_GEO_MIN_WIDTH - STATS_VOXEL_MIN_WIDTH; int panels = 4; _generalStatsWidth = STATS_GENERAL_MIN_WIDTH; if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { _pingStatsWidth = STATS_PING_MIN_WIDTH; } else { _pingStatsWidth = 0; panels = 3; } _geoStatsWidth = STATS_GEO_MIN_WIDTH; _voxelStatsWidth = STATS_VOXEL_MIN_WIDTH; if (extraSpace > panels) { _generalStatsWidth += (int) extraSpace / panels; if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { _pingStatsWidth += (int) extraSpace / panels; } _geoStatsWidth += (int) extraSpace / panels; _voxelStatsWidth += glWidget->width() - (_generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3); } }
//Caculate the click location using one of the sixense controllers. Scale is not applied QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const { Application* application = Application::getInstance(); GLCanvas* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm); glm::vec3 eyePos = myAvatar->getHead()->getEyePosition(); glm::quat invOrientation = glm::inverse(myAvatar->getOrientation()); //direction of ray goes towards camera glm::vec3 dir = invOrientation * glm::normalize(application->getCamera()->getPosition() - tip); glm::vec3 tipPos = invOrientation * (tip - eyePos); QPoint rv; if (OculusManager::isConnected()) { float t; //We back the ray up by dir to ensure that it will not start inside the UI. glm::vec3 adjustedPos = tipPos - dir; //Find intersection of crosshair ray. if (raySphereIntersect(dir, adjustedPos, _oculusUIRadius * myAvatar->getScale(), &t)){ glm::vec3 collisionPos = adjustedPos + dir * t; //Normalize it in case its not a radius of 1 collisionPos = glm::normalize(collisionPos); //If we hit the back hemisphere, mark it as not a collision if (collisionPos.z > 0) { rv.setX(INT_MAX); rv.setY(INT_MAX); } else { float u = asin(collisionPos.x) / (_textureFov)+0.5f; float v = 1.0 - (asin(collisionPos.y) / (_textureFov)+0.5f); rv.setX(u * glWidget->width()); rv.setY(v * glWidget->height()); } } else { //if they did not click on the overlay, just set the coords to INT_MAX rv.setX(INT_MAX); rv.setY(INT_MAX); } } else { glm::dmat4 projection; application->getProjectionMatrix(&projection); glm::vec4 clipSpacePos = glm::vec4(projection * glm::dvec4(tipPos, 1.0)); glm::vec3 ndcSpacePos; if (clipSpacePos.w != 0) { ndcSpacePos = glm::vec3(clipSpacePos) / clipSpacePos.w; } rv.setX(((ndcSpacePos.x + 1.0) / 2.0) * glWidget->width()); rv.setY((1.0 - ((ndcSpacePos.y + 1.0) / 2.0)) * glWidget->height()); } return rv; }
//Renders a small magnification of the currently bound texture at the coordinates void ApplicationOverlay::renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder) const { Application* application = Application::getInstance(); GLCanvas* glWidget = application->getGLWidget(); const int widgetWidth = glWidget->width(); const int widgetHeight = glWidget->height(); const float halfWidth = (MAGNIFY_WIDTH / _textureAspectRatio) * sizeMult / 2.0f; const float halfHeight = MAGNIFY_HEIGHT * sizeMult / 2.0f; // Magnification Texture Coordinates const float magnifyULeft = (magPos.x - halfWidth) / (float)widgetWidth; const float magnifyURight = (magPos.x + halfWidth) / (float)widgetWidth; const float magnifyVTop = 1.0f - (magPos.y - halfHeight) / (float)widgetHeight; const float magnifyVBottom = 1.0f - (magPos.y + halfHeight) / (float)widgetHeight; const float newHalfWidth = halfWidth * MAGNIFY_MULT; const float newHalfHeight = halfHeight * MAGNIFY_MULT; //Get yaw / pitch value for the corners const glm::vec2 topLeftYawPitch = overlayToSpherical(glm::vec2(magPos.x - newHalfWidth, magPos.y - newHalfHeight)); const glm::vec2 bottomRightYawPitch = overlayToSpherical(glm::vec2(magPos.x + newHalfWidth, magPos.y + newHalfHeight)); const glm::vec3 bottomLeft = getPoint(topLeftYawPitch.x, bottomRightYawPitch.y); const glm::vec3 bottomRight = getPoint(bottomRightYawPitch.x, bottomRightYawPitch.y); const glm::vec3 topLeft = getPoint(topLeftYawPitch.x, topLeftYawPitch.y); const glm::vec3 topRight = getPoint(bottomRightYawPitch.x, topLeftYawPitch.y); glPushMatrix(); { if (showBorder) { glDisable(GL_TEXTURE_2D); glLineWidth(1.0f); //Outer Line glBegin(GL_LINE_STRIP); { glColor4f(1.0f, 0.0f, 0.0f, _alpha); glVertex3f(topLeft.x, topLeft.y, topLeft.z); glVertex3f(bottomLeft.x, bottomLeft.y, bottomLeft.z); glVertex3f(bottomRight.x, bottomRight.y, bottomRight.z); glVertex3f(topRight.x, topRight.y, topRight.z); glVertex3f(topLeft.x, topLeft.y, topLeft.z); } glEnd(); glEnable(GL_TEXTURE_2D); } glColor4f(1.0f, 1.0f, 1.0f, _alpha); glBegin(GL_QUADS); { glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(bottomLeft.x, bottomLeft.y, bottomLeft.z); glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(bottomRight.x, bottomRight.y, bottomRight.z); glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(topRight.x, topRight.y, topRight.z); glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(topLeft.x, topLeft.y, topLeft.z); } glEnd(); } glPopMatrix(); }
// called on mouse click release // check for clicks over stats in order to expand or contract them void Stats::checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseDragStartedY, int horizontalOffset) { GLCanvas* glWidget = Application::getInstance()->getGLWidget(); if (0 != glm::compMax(glm::abs(glm::ivec2(mouseX - mouseDragStartedX, mouseY - mouseDragStartedY)))) { // not worried about dragging on stats return; } int statsHeight = 0, statsWidth = 0, statsX = 0, statsY = 0, lines = 0; statsX = horizontalOffset; // top-left stats click lines = _expanded ? 5 : 3; statsHeight = lines * STATS_PELS_PER_LINE + 10; if (mouseX > statsX && mouseX < statsX + _generalStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) { toggleExpanded(); return; } statsX += _generalStatsWidth; // ping stats click if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { lines = _expanded ? 4 : 3; statsHeight = lines * STATS_PELS_PER_LINE + 10; if (mouseX > statsX && mouseX < statsX + _pingStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) { toggleExpanded(); return; } statsX += _pingStatsWidth; } // geo stats panel click lines = _expanded ? 4 : 3; statsHeight = lines * STATS_PELS_PER_LINE + 10; if (mouseX > statsX && mouseX < statsX + _geoStatsWidth && mouseY > statsY && mouseY < statsY + statsHeight) { toggleExpanded(); return; } statsX += _geoStatsWidth; // top-right stats click lines = _expanded ? 11 : 3; statsHeight = lines * STATS_PELS_PER_LINE + 10; statsWidth = glWidget->width() - statsX; if (mouseX > statsX && mouseX < statsX + statsWidth && mouseY > statsY && mouseY < statsY + statsHeight) { toggleExpanded(); return; } }
void ApplicationOverlay::renderStatsAndLogs() { Application* application = Application::getInstance(); GLCanvas* 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(); }
Stats::Stats(): _expanded(false), _recentMaxPackets(0), _resetRecentMaxPacketsSoon(true), _generalStatsWidth(STATS_GENERAL_MIN_WIDTH), _pingStatsWidth(STATS_PING_MIN_WIDTH), _geoStatsWidth(STATS_GEO_MIN_WIDTH), _voxelStatsWidth(STATS_VOXEL_MIN_WIDTH), _lastHorizontalOffset(0), _metavoxelInternal(0), _metavoxelLeaves(0), _metavoxelSendProgress(0), _metavoxelSendTotal(0), _metavoxelReceiveProgress(0), _metavoxelReceiveTotal(0) { GLCanvas* glWidget = Application::getInstance()->getGLWidget(); resetWidth(glWidget->width(), 0); }
void ApplicationOverlay::renderDomainConnectionStatusBorder() { NodeList* nodeList = NodeList::getInstance(); if (nodeList && !nodeList->getDomainHandler().isConnected()) { GLCanvas* glWidget = Application::getInstance()->getGLWidget(); int right = glWidget->width(); int bottom = glWidget->height(); glColor3f(CONNECTION_STATUS_BORDER_COLOR[0], CONNECTION_STATUS_BORDER_COLOR[1], CONNECTION_STATUS_BORDER_COLOR[2]); glLineWidth(CONNECTION_STATUS_BORDER_LINE_WIDTH); glBegin(GL_LINE_LOOP); glVertex2i(0, 0); glVertex2i(0, bottom); glVertex2i(right, bottom); glVertex2i(right, 0); glEnd(); } }
//Injecting mouse movements and clicks void SixenseManager::emulateMouse(PalmData* palm, int index) { Application* application = Application::getInstance(); MyAvatar* avatar = application->getAvatar(); GLCanvas* widget = application->getGLWidget(); QPoint pos; Qt::MouseButton bumperButton; Qt::MouseButton triggerButton; unsigned int deviceID = index == 0 ? CONTROLLER_0_EVENT : CONTROLLER_1_EVENT; if (Menu::getInstance()->getInvertSixenseButtons()) { bumperButton = Qt::LeftButton; triggerButton = Qt::RightButton; } else { bumperButton = Qt::RightButton; triggerButton = Qt::LeftButton; } if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { pos = application->getApplicationOverlay().getPalmClickLocation(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, deviceID); _bumperPressed[index] = false; } if (_triggerPressed[index]) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, triggerButton, triggerButton, 0); application->mouseReleaseEvent(&mouseEvent, deviceID); _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(QEvent::MouseMove, 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, deviceID); } } else { if (!_bumperPressed[(int)(!index)]) { application->mouseMoveEvent(&mouseEvent, deviceID); } } } _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, deviceID); } } else if (_bumperPressed[index]) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, bumperButton, bumperButton, 0); application->mouseReleaseEvent(&mouseEvent, deviceID); _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, deviceID); } } else if (_triggerPressed[index]) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, triggerButton, triggerButton, 0); application->mouseReleaseEvent(&mouseEvent, deviceID); _triggerPressed[index] = false; } }
// display expanded or contracted stats void Stats::display( const float* color, int horizontalOffset, float fps, int packetsPerSecond, int bytesPerSecond, int voxelPacketsToProcess) { GLCanvas* glWidget = Application::getInstance()->getGLWidget(); unsigned int backgroundColor = 0x33333399; int verticalOffset = 0, lines = 0; float scale = 0.10f; float rotation = 0.0f; int font = 2; 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; int columnOneWidth = _generalStatsWidth; PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) { columnOneWidth = _generalStatsWidth + _pingStatsWidth + _geoStatsWidth; // make it 3 columns wide... // we will also include room for 1 line per timing record and a header of 4 lines lines += 4; const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords(); QMapIterator<QString, PerformanceTimerRecord> i(allRecords); while (i.hasNext()) { i.next(); if (includeTimingRecord(i.key())) { lines++; } } } drawBackground(backgroundColor, horizontalOffset, 0, columnOneWidth, lines * STATS_PELS_PER_LINE + 10); horizontalOffset += 5; int columnOneHorizontalOffset = horizontalOffset; 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, scale, rotation, font, serverNodes, color); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarNodes, color); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, framesPerSecond, color); if (_expanded) { char packetsPerSecondString[30]; sprintf(packetsPerSecondString, "Pkts/sec: %d", packetsPerSecond); char averageMegabitsPerSecond[30]; sprintf(averageMegabitsPerSecond, "Mbps: %3.2f", (float)bytesPerSecond * 8.0f / 1000000.0f); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, packetsPerSecondString, color); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, averageMegabitsPerSecond, color); } // TODO: the display of these timing details should all be moved to JavaScript if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) { // Timing details... const int TIMER_OUTPUT_LINE_LENGTH = 1000; char perfLine[TIMER_OUTPUT_LINE_LENGTH]; verticalOffset += STATS_PELS_PER_LINE * 4; // skip 4 lines to be under the other columns drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, "-------------------------------------------------------- Function " "------------------------------------------------------- --msecs- -calls--", color); // First iterate all the records, and for the ones that should be included, insert them into // a new Map sorted by average time... QMap<float, QString> sortedRecords; const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords(); QMapIterator<QString, PerformanceTimerRecord> i(allRecords); while (i.hasNext()) { i.next(); if (includeTimingRecord(i.key())) { float averageTime = (float)i.value().getMovingAverage() / (float)USECS_PER_MSEC; sortedRecords.insertMulti(averageTime, i.key()); } } QMapIterator<float, QString> j(sortedRecords); j.toBack(); while (j.hasPrevious()) { j.previous(); QString functionName = j.value(); const PerformanceTimerRecord& record = allRecords.value(functionName); sprintf(perfLine, "%120s: %8.4f [%6llu]", qPrintable(functionName), (float)record.getMovingAverage() / (float)USECS_PER_MSEC, record.getCount()); verticalOffset += STATS_PELS_PER_LINE; drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, perfLine, color); } } verticalOffset = 0; horizontalOffset = _lastHorizontalOffset + _generalStatsWidth +1; if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { int pingAudio = -1, pingAvatar = -1, pingVoxel = -1, pingVoxelMax = -1; NodeList* nodeList = NodeList::getInstance(); SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); pingAudio = audioMixerNode ? audioMixerNode->getPingMs() : -1; pingAvatar = avatarMixerNode ? avatarMixerNode->getPingMs() : -1; // 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()) { // TODO: this should also support entities 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; // only draw our background if column one didn't draw a wide background if (columnOneWidth == _generalStatsWidth) { drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, lines * STATS_PELS_PER_LINE + 10); } horizontalOffset += 5; char audioPing[30]; if (pingAudio >= 0) { sprintf(audioPing, "Audio ping: %d", pingAudio); } else { sprintf(audioPing, "Audio ping: --"); } char avatarPing[30]; if (pingAvatar >= 0) { sprintf(avatarPing, "Avatar ping: %d", pingAvatar); } else { sprintf(avatarPing, "Avatar ping: --"); } char voxelAvgPing[30]; if (pingVoxel >= 0) { sprintf(voxelAvgPing, "Voxel avg ping: %d", pingVoxel); } else { sprintf(voxelAvgPing, "Voxel avg ping: --"); } verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, audioPing, color); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarPing, color); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelAvgPing, color); if (_expanded) { char voxelMaxPing[30]; if (pingVoxel >= 0) { // Average is only meaningful if pingVoxel is valid. sprintf(voxelMaxPing, "Voxel max ping: %d", pingVoxelMax); } else { sprintf(voxelMaxPing, "Voxel max ping: --"); } verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelMaxPing, color); } verticalOffset = 0; horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + 2; }
void ApplicationOverlay::renderAudioMeter() { Application* application = Application::getInstance(); GLCanvas* 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; bool smallMirrorVisible = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !OculusManager::isConnected(); bool boxed = smallMirrorVisible && !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror); if (boxed) { 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.0f); const float METER_LOUDNESS_SCALE = 2.8f / 5.0f; const float LOG2_LOUDNESS_FLOOR = 11.0f; float audioLevel = 0.0f; float loudness = audio->getLastInputLoudness() + 1.0f; _trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.0f - 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.0f)) * METER_LOUDNESS_SCALE * AUDIO_METER_SCALE_WIDTH; } if (audioLevel > AUDIO_METER_SCALE_WIDTH) { audioLevel = AUDIO_METER_SCALE_WIDTH; } bool isClipping = ((audio->getTimeSinceLastClip() > 0.0f) && (audio->getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)); if ((audio->getTimeSinceLastClip() > 0.0f) && (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, boxed); audio->renderScope(glWidget->width(), glWidget->height()); audio->renderStats(WHITE_TEXT, 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(); }
void ApplicationOverlay::renderControllerPointers() { Application* application = Application::getInstance(); GLCanvas* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); //Static variables used for storing controller state static quint64 pressedTime[NUMBER_OF_RETICLES] = { 0ULL, 0ULL, 0ULL }; static bool isPressed[NUMBER_OF_RETICLES] = { false, false, false }; static bool stateWhenPressed[NUMBER_OF_RETICLES] = { false, false, false }; const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) { const int index = palmIndex - 1; const PalmData* palmData = NULL; if (palmIndex >= handData->getPalms().size()) { return; } if (handData->getPalms()[palmIndex].isActive()) { palmData = &handData->getPalms()[palmIndex]; } else { continue; } int controllerButtons = palmData->getControllerButtons(); //Check for if we should toggle or drag the magnification window if (controllerButtons & BUTTON_3) { if (isPressed[index] == false) { //We are now dragging the window isPressed[index] = true; //set the pressed time in us pressedTime[index] = usecTimestampNow(); stateWhenPressed[index] = _magActive[index]; } } else if (isPressed[index]) { isPressed[index] = false; //If the button was only pressed for < 250 ms //then disable it. const int MAX_BUTTON_PRESS_TIME = 250 * MSECS_TO_USECS; if (usecTimestampNow() < pressedTime[index] + MAX_BUTTON_PRESS_TIME) { _magActive[index] = !stateWhenPressed[index]; } } //if we have the oculus, we should make the cursor smaller since it will be //magnified if (OculusManager::isConnected()) { QPoint point = getPalmClickLocation(palmData); _reticlePosition[index] = point; //When button 2 is pressed we drag the mag window if (isPressed[index]) { _magActive[index] = true; } // If oculus is enabled, we draw the crosshairs later continue; } int mouseX, mouseY; if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) { QPoint res = getPalmClickLocation(palmData); mouseX = res.x(); mouseY = res.y(); } else { // Get directon relative to avatar orientation glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->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 = glWidget->width() * SixenseManager::getInstance().getCursorPixelRangeMult(); mouseX = (glWidget->width() / 2.0f + cursorRange * xAngle); 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()) { _reticleActive[index] = false; continue; } _reticleActive[index] = true; const float reticleSize = 40.0f; mouseX -= reticleSize / 2.0f; mouseY += reticleSize / 2.0f; glBegin(GL_QUADS); 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 + reticleSize, mouseY); glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + reticleSize, mouseY - reticleSize); glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - reticleSize); glEnd(); } }
// 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(); GLCanvas* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); _textureFov = glm::radians(Menu::getInstance()->getOculusUIAngularSize()); _textureAspectRatio = (float)application->getGLWidget()->getDeviceWidth() / (float)application->getGLWidget()->getDeviceHeight(); //Handle fading and deactivation/activation of UI if (Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)) { _alpha += FADE_SPEED; if (_alpha > 1.0f) { _alpha = 1.0f; } } else { _alpha -= FADE_SPEED; if (_alpha <= 0.0f) { _alpha = 0.0f; } } // Render 2D overlay glMatrixMode(GL_PROJECTION); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); if (renderToTexture) { _overlays.buildFramebufferObject(); _overlays.bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } glPushMatrix(); { glLoadIdentity(); gluOrtho2D(0, glWidget->width(), glWidget->height(), 0); 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(); renderDomainConnectionStatusBorder(); } 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) { _overlays.release(); } }