template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) { if (args) { if (overlay->getAnchor() == Overlay::MY_AVATAR) { auto batch = args->_batch; MyAvatar* avatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); glm::quat myAvatarRotation = avatar->getOrientation(); glm::vec3 myAvatarPosition = avatar->getPosition(); float angle = glm::degrees(glm::angle(myAvatarRotation)); glm::vec3 axis = glm::axis(myAvatarRotation); float myAvatarScale = avatar->getScale(); Transform transform = Transform(); transform.setTranslation(myAvatarPosition); transform.setRotation(glm::angleAxis(angle, axis)); transform.setScale(myAvatarScale); batch->setModelTransform(transform); overlay->render(args); } else { overlay->render(args); } } }
template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) { if (args) { if (overlay->getAnchor() == Overlay::MY_AVATAR) { glPushMatrix(); MyAvatar* avatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); glm::quat myAvatarRotation = avatar->getOrientation(); glm::vec3 myAvatarPosition = avatar->getPosition(); float angle = glm::degrees(glm::angle(myAvatarRotation)); glm::vec3 axis = glm::axis(myAvatarRotation); float myAvatarScale = avatar->getScale(); glTranslatef(myAvatarPosition.x, myAvatarPosition.y, myAvatarPosition.z); glRotatef(angle, axis.x, axis.y, axis.z); glScalef(myAvatarScale, myAvatarScale, myAvatarScale); overlay->render(args); glPopMatrix(); } else { overlay->render(args); } } }
bool LocationManager::goToDestination(QString destination) { QStringList coordinateItems = destination.remove(' ').split(QRegExp("_|,"), QString::SkipEmptyParts); const int NUMBER_OF_COORDINATE_ITEMS = 3; const int X_ITEM = 0; const int Y_ITEM = 1; const int Z_ITEM = 2; if (coordinateItems.size() == NUMBER_OF_COORDINATE_ITEMS) { // replace last occurrence of '_' with decimal point replaceLastOccurrence('-', '.', coordinateItems[X_ITEM]); replaceLastOccurrence('-', '.', coordinateItems[Y_ITEM]); replaceLastOccurrence('-', '.', coordinateItems[Z_ITEM]); double x = coordinateItems[X_ITEM].toDouble(); double y = coordinateItems[Y_ITEM].toDouble(); double z = coordinateItems[Z_ITEM].toDouble(); glm::vec3 newAvatarPos(x, y, z); MyAvatar* myAvatar = Application::getInstance()->getAvatar(); glm::vec3 avatarPos = myAvatar->getPosition(); if (newAvatarPos != avatarPos) { // send a node kill request, indicating to other clients that they should play the "disappeared" effect MyAvatar::sendKillAvatar(); qDebug("Going To Location: %f, %f, %f...", x, y, z); myAvatar->setPosition(newAvatarPos); emit myAvatar->transformChanged(); } return true; } // no coordinates were parsed return false; }
void Stats::updateStats() { if (!Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { if (isVisible()) { setVisible(false); } return; } else { if (!isVisible()) { setVisible(true); } } bool shouldDisplayTimingDetail = Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) && Menu::getInstance()->isOptionChecked(MenuOption::Stats) && isExpanded(); if (shouldDisplayTimingDetail != PerformanceTimer::isActive()) { PerformanceTimer::setActive(shouldDisplayTimingDetail); } auto nodeList = DependencyManager::get<NodeList>(); auto avatarManager = DependencyManager::get<AvatarManager>(); // we need to take one avatar out so we don't include ourselves STAT_UPDATE(avatarCount, avatarManager->size() - 1); STAT_UPDATE(serverCount, nodeList->size()); STAT_UPDATE(framerate, (int)qApp->getFps()); auto bandwidthRecorder = DependencyManager::get<BandwidthRecorder>(); STAT_UPDATE(packetInCount, bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond()); STAT_UPDATE(packetOutCount, bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond()); STAT_UPDATE_FLOAT(mbpsIn, (float)bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond() / 1000.0f, 0.01f); STAT_UPDATE_FLOAT(mbpsOut, (float)bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond() / 1000.0f, 0.01f); // Second column: ping if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); STAT_UPDATE(audioPing, audioMixerNode ? audioMixerNode->getPingMs() : -1); STAT_UPDATE(avatarPing, avatarMixerNode ? avatarMixerNode->getPingMs() : -1); //// Now handle voxel servers, since there could be more than one, we average their ping times unsigned long totalPingOctree = 0; int octreeServerCount = 0; int pingOctreeMax = 0; int pingVoxel; nodeList->eachNode([&](const SharedNodePointer& node) { // TODO: this should also support entities if (node->getType() == NodeType::EntityServer) { totalPingOctree += node->getPingMs(); octreeServerCount++; if (pingOctreeMax < node->getPingMs()) { pingOctreeMax = node->getPingMs(); } } }); if (octreeServerCount) { pingVoxel = totalPingOctree / octreeServerCount; } //STAT_UPDATE(entitiesPing, pingVoxel); //if (_expanded) { // QString voxelMaxPing; // if (pingVoxel >= 0) { // Average is only meaningful if pingVoxel is valid. // voxelMaxPing = QString("Voxel max ping: %1").arg(pingOctreeMax); // } else { // voxelMaxPing = QString("Voxel max ping: --"); // } } else { // -2 causes the QML to hide the ping column STAT_UPDATE(audioPing, -2); } // Third column, avatar stats MyAvatar* myAvatar = avatarManager->getMyAvatar(); glm::vec3 avatarPos = myAvatar->getPosition(); STAT_UPDATE(position, QVector3D(avatarPos.x, avatarPos.y, avatarPos.z)); STAT_UPDATE_FLOAT(velocity, glm::length(myAvatar->getVelocity()), 0.1f); STAT_UPDATE_FLOAT(yaw, myAvatar->getBodyYaw(), 0.1f); if (_expanded) { SharedNodePointer avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer); if (avatarMixer) { STAT_UPDATE(avatarMixerKbps, roundf( bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AvatarMixer) + bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AvatarMixer))); STAT_UPDATE(avatarMixerPps, roundf( bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AvatarMixer) + bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AvatarMixer))); } else { STAT_UPDATE(avatarMixerKbps, -1); STAT_UPDATE(avatarMixerPps, -1); } SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); if (audioMixerNode) { STAT_UPDATE(audioMixerKbps, roundf( bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) + bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer))); STAT_UPDATE(audioMixerPps, roundf( bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) + bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer))); } else { STAT_UPDATE(audioMixerKbps, -1); STAT_UPDATE(audioMixerPps, -1); } STAT_UPDATE(downloads, ResourceCache::getLoadingRequests().size()); STAT_UPDATE(downloadsPending, ResourceCache::getPendingRequestCount()); // TODO fix to match original behavior //stringstream downloads; //downloads << "Downloads: "; //foreach(Resource* resource, ) { // downloads << (int)(resource->getProgress() * 100.0f) << "% "; //} //downloads << "(" << << " pending)"; } // expanded avatar column // Fourth column, octree stats int serverCount = 0; int movingServerCount = 0; unsigned long totalNodes = 0; unsigned long totalInternal = 0; unsigned long totalLeaves = 0; std::stringstream sendingModeStream(""); sendingModeStream << "["; NodeToOctreeSceneStats* octreeServerSceneStats = Application::getInstance()->getOcteeSceneStats(); for (NodeToOctreeSceneStatsIterator i = octreeServerSceneStats->begin(); i != octreeServerSceneStats->end(); i++) { //const QUuid& uuid = i->first; OctreeSceneStats& stats = i->second; serverCount++; if (_expanded) { if (serverCount > 1) { sendingModeStream << ","; } if (stats.isMoving()) { sendingModeStream << "M"; movingServerCount++; } else { sendingModeStream << "S"; } } // calculate server node totals totalNodes += stats.getTotalElements(); if (_expanded) { totalInternal += stats.getTotalInternal(); totalLeaves += stats.getTotalLeaves(); } } if (_expanded) { if (serverCount == 0) { sendingModeStream << "---"; } sendingModeStream << "] " << serverCount << " servers"; if (movingServerCount > 0) { sendingModeStream << " <SCENE NOT STABLE>"; } else { sendingModeStream << " <SCENE STABLE>"; } QString sendingModeResult = sendingModeStream.str().c_str(); STAT_UPDATE(sendingMode, sendingModeResult); } // Incoming packets QLocale locale(QLocale::English); auto voxelPacketsToProcess = qApp->getOctreePacketProcessor().packetsToProcessCount(); if (_expanded) { std::stringstream octreeStats; QString packetsString = locale.toString((int)voxelPacketsToProcess); QString maxString = locale.toString((int)_recentMaxPackets); octreeStats << "Octree Packets to Process: " << qPrintable(packetsString) << " [Recent Max: " << qPrintable(maxString) << "]"; QString str = octreeStats.str().c_str(); STAT_UPDATE(packetStats, str); // drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); } if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) { _recentMaxPackets = 0; _resetRecentMaxPacketsSoon = false; } if (voxelPacketsToProcess == 0) { _resetRecentMaxPacketsSoon = true; } else if (voxelPacketsToProcess > _recentMaxPackets) { _recentMaxPackets = voxelPacketsToProcess; } // Server Octree Elements STAT_UPDATE(serverElements, totalNodes); STAT_UPDATE(localElements, OctreeElement::getNodeCount()); if (_expanded) { STAT_UPDATE(serverInternal, totalInternal); STAT_UPDATE(serverLeaves, totalLeaves); // Local Voxels STAT_UPDATE(localInternal, OctreeElement::getInternalNodeCount()); STAT_UPDATE(localLeaves, OctreeElement::getLeafNodeCount()); // LOD Details STAT_UPDATE(lodStatus, "You can see " + DependencyManager::get<LODManager>()->getLODFeedbackText()); } bool performanceTimerIsActive = PerformanceTimer::isActive(); bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails); if (displayPerf && performanceTimerIsActive) { if (!_timingExpanded) { _timingExpanded = true; emit timingExpandedChanged(); } PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up // we will also include room for 1 line per timing record and a header of 4 lines // Timing details... // First iterate all the records, and for the ones that should be included, insert them into // a new Map sorted by average time... bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen); 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()); } } int linesDisplayed = 0; QMapIterator<float, QString> j(sortedRecords); j.toBack(); QString perfLines; while (j.hasPrevious()) { j.previous(); static const QChar noBreakingSpace = QChar::Nbsp; QString functionName = j.value(); const PerformanceTimerRecord& record = allRecords.value(functionName); perfLines += QString("%1: %2 [%3]\n"). arg(QString(qPrintable(functionName)), 90, noBreakingSpace). arg((float)record.getMovingAverage() / (float)USECS_PER_MSEC, 8, 'f', 3, noBreakingSpace). arg((int)record.getCount(), 6, 10, noBreakingSpace); linesDisplayed++; if (onlyDisplayTopTen && linesDisplayed == 10) { break; } } _timingStats = perfLines; emit timingStatsChanged(); } else if (_timingExpanded) { _timingExpanded = false; emit timingExpandedChanged(); } }
void Stats::updateStats(bool force) { if (!force) { if (!Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { if (isVisible()) { setVisible(false); } return; } else if (!isVisible()) { setVisible(true); } } bool shouldDisplayTimingDetail = Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) && Menu::getInstance()->isOptionChecked(MenuOption::Stats) && isExpanded(); if (shouldDisplayTimingDetail != PerformanceTimer::isActive()) { PerformanceTimer::setActive(shouldDisplayTimingDetail); } auto nodeList = DependencyManager::get<NodeList>(); auto avatarManager = DependencyManager::get<AvatarManager>(); // we need to take one avatar out so we don't include ourselves STAT_UPDATE(avatarCount, avatarManager->size() - 1); STAT_UPDATE(serverCount, (int)nodeList->size()); STAT_UPDATE(renderrate, (int)qApp->getFps()); if (qApp->getActiveDisplayPlugin()) { STAT_UPDATE(presentrate, (int)round(qApp->getActiveDisplayPlugin()->presentRate())); } else { STAT_UPDATE(presentrate, -1); } STAT_UPDATE(simrate, (int)qApp->getAverageSimsPerSecond()); STAT_UPDATE(avatarSimrate, (int)qApp->getAvatarSimrate()); auto bandwidthRecorder = DependencyManager::get<BandwidthRecorder>(); STAT_UPDATE(packetInCount, bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond()); STAT_UPDATE(packetOutCount, bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond()); STAT_UPDATE_FLOAT(mbpsIn, (float)bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond() / 1000.0f, 0.01f); STAT_UPDATE_FLOAT(mbpsOut, (float)bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond() / 1000.0f, 0.01f); // Second column: ping SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); SharedNodePointer assetServerNode = nodeList->soloNodeOfType(NodeType::AssetServer); SharedNodePointer messageMixerNode = nodeList->soloNodeOfType(NodeType::MessagesMixer); STAT_UPDATE(audioPing, audioMixerNode ? audioMixerNode->getPingMs() : -1); STAT_UPDATE(avatarPing, avatarMixerNode ? avatarMixerNode->getPingMs() : -1); STAT_UPDATE(assetPing, assetServerNode ? assetServerNode->getPingMs() : -1); STAT_UPDATE(messagePing, messageMixerNode ? messageMixerNode->getPingMs() : -1); //// Now handle entity servers, since there could be more than one, we average their ping times int totalPingOctree = 0; int octreeServerCount = 0; int pingOctreeMax = 0; nodeList->eachNode([&](const SharedNodePointer& node) { // TODO: this should also support entities if (node->getType() == NodeType::EntityServer) { totalPingOctree += node->getPingMs(); octreeServerCount++; if (pingOctreeMax < node->getPingMs()) { pingOctreeMax = node->getPingMs(); } } }); // update the entities ping with the average for all connected entity servers STAT_UPDATE(entitiesPing, octreeServerCount ? totalPingOctree / octreeServerCount : -1); // Third column, avatar stats MyAvatar* myAvatar = avatarManager->getMyAvatar(); glm::vec3 avatarPos = myAvatar->getPosition(); STAT_UPDATE(position, QVector3D(avatarPos.x, avatarPos.y, avatarPos.z)); STAT_UPDATE_FLOAT(speed, glm::length(myAvatar->getVelocity()), 0.01f); STAT_UPDATE_FLOAT(yaw, myAvatar->getBodyYaw(), 0.1f); if (_expanded || force) { SharedNodePointer avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer); if (avatarMixer) { STAT_UPDATE(avatarMixerInKbps, roundf(bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AvatarMixer))); STAT_UPDATE(avatarMixerInPps, roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AvatarMixer))); STAT_UPDATE(avatarMixerOutKbps, roundf(bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AvatarMixer))); STAT_UPDATE(avatarMixerOutPps, roundf(bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AvatarMixer))); } else { STAT_UPDATE(avatarMixerInKbps, -1); STAT_UPDATE(avatarMixerInPps, -1); STAT_UPDATE(avatarMixerOutKbps, -1); STAT_UPDATE(avatarMixerOutPps, -1); } SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); if (audioMixerNode || force) { STAT_UPDATE(audioMixerKbps, roundf( bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) + bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer))); STAT_UPDATE(audioMixerPps, roundf( bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) + bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer))); } else { STAT_UPDATE(audioMixerKbps, -1); STAT_UPDATE(audioMixerPps, -1); } QList<Resource*> loadingRequests = ResourceCache::getLoadingRequests(); STAT_UPDATE(downloads, loadingRequests.size()); STAT_UPDATE(downloadLimit, ResourceCache::getRequestLimit()) STAT_UPDATE(downloadsPending, ResourceCache::getPendingRequestCount()); // See if the active download urls have changed bool shouldUpdateUrls = _downloads != _downloadUrls.size(); if (!shouldUpdateUrls) { for (int i = 0; i < _downloads; i++) { if (loadingRequests[i]->getURL().toString() != _downloadUrls[i]) { shouldUpdateUrls = true; break; } } } // If the urls have changed, update the list if (shouldUpdateUrls) { _downloadUrls.clear(); foreach (Resource* resource, loadingRequests) { _downloadUrls << resource->getURL().toString(); } emit downloadUrlsChanged(); } // TODO fix to match original behavior //stringstream downloads; //downloads << "Downloads: "; //foreach(Resource* resource, ) { // downloads << (int)(resource->getProgress() * 100.0f) << "% "; //} //downloads << "(" << << " pending)"; } // expanded avatar column