Showing the changes to interface/src/Application.cpp (1 of 76 files). Show all
Unified · Split
| @@ -49,7 +49,7 @@ · 48 unmodified lines | ||
| 49 | 49 | #include <QXmlStreamReader> |
| 50 | 50 | #include <QXmlStreamAttributes> |
| 51 | 51 | #include <QMediaPlayer> |
| 52 | −#include <QMimeData> | |
| 52 | +#include <QMimeData> | |
| 53 | 53 | #include <QMessageBox> |
| 54 | 54 | |
| 55 | 55 | #include <AccountManager.h> |
| @@ -63,7 +63,7 @@ · 7 unmodified lines | ||
| 63 | 63 | #include <UUID.h> |
| 64 | 64 | #include <OctreeSceneStats.h> |
| 65 | 65 | #include <LocalVoxelsList.h> |
| 66 | −#include <FstReader.h> | |
| 66 | +#include <ModelUploader.h> | |
| 67 | 67 | |
| 68 | 68 | #include "Application.h" |
| 69 | 69 | #include "InterfaceVersion.h" |
| @@ -81,6 +81,7 @@ · 11 unmodified lines | ||
| 81 | 81 | #include "ui/InfoView.h" |
| 82 | 82 | #include "ui/Snapshot.h" |
| 83 | 83 | #include "ui/TextRenderer.h" |
| 84 | +#include "ui/Stats.h" | |
| 84 | 85 | |
| 85 | 86 | using namespace std; |
| 86 | 87 | |
| @@ -110,15 +111,11 @@ const float MIRROR_FIELD_OF_VIEW = 30.0f; · 23 unmodified lines | ||
| 110 | 111 | const QString CHECK_VERSION_URL = "http://highfidelity.io/latestVersion.xml"; |
| 111 | 112 | const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion"; |
| 112 | 113 | |
| 113 | −const int STATS_PELS_PER_LINE = 20; | |
| 114 | − | |
| 115 | −const QString CUSTOM_URL_SCHEME = "hifi:"; | |
| 116 | − | |
| 117 | 114 | void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { |
| 118 | 115 | if (message.size() > 0) { |
| 119 | 116 | QString dateString = QDateTime::currentDateTime().toTimeSpec(Qt::LocalTime).toString(Qt::ISODate); |
| 120 | 117 | QString formattedMessage = QString("[%1] %2\n").arg(dateString).arg(message); |
| 121 | − | |
| 118 | + | |
| 122 | 119 | fprintf(stdout, "%s", qPrintable(formattedMessage)); |
| 123 | 120 | Application::getInstance()->getLogger()->addMessage(qPrintable(formattedMessage)); |
| 124 | 121 | } |
| @@ -137,7 +134,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 12 unmodified lines | ||
| 137 | 134 | QApplication(argc, argv), |
| 138 | 135 | _window(new QMainWindow(desktop())), |
| 139 | 136 | _glWidget(new GLCanvas()), |
| 140 | − _statsExpanded(false), | |
| 141 | 137 | _nodeThread(new QThread(this)), |
| 142 | 138 | _datagramProcessor(), |
| 143 | 139 | _frameCount(0), |
| @@ -167,8 +163,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 23 unmodified lines | ||
| 167 | 163 | _voxelHideShowThread(&_voxels), |
| 168 | 164 | _packetsPerSecond(0), |
| 169 | 165 | _bytesPerSecond(0), |
| 170 | − _recentMaxPackets(0), | |
| 171 | − _resetRecentMaxPacketsSoon(true), | |
| 166 | + _previousScriptLocation(), | |
| 172 | 167 | _logger(new FileLogger(this)) |
| 173 | 168 | { |
| 174 | 169 | // init GnuTLS for DTLS with domain-servers |
| @@ -176,23 +171,23 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 1 unmodified line | ||
| 176 | 171 | |
| 177 | 172 | // read the ApplicationInfo.ini file for Name/Version/Domain information |
| 178 | 173 | QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat); |
| 179 | − | |
| 174 | + | |
| 180 | 175 | // set the associated application properties |
| 181 | 176 | applicationInfo.beginGroup("INFO"); |
| 182 | − | |
| 177 | + | |
| 183 | 178 | qDebug() << "[VERSION] Build sequence: " << qPrintable(applicationVersion()); |
| 184 | − | |
| 179 | + | |
| 185 | 180 | setApplicationName(applicationInfo.value("name").toString()); |
| 186 | 181 | setApplicationVersion(BUILD_VERSION); |
| 187 | 182 | setOrganizationName(applicationInfo.value("organizationName").toString()); |
| 188 | 183 | setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); |
| 189 | − | |
| 184 | + | |
| 190 | 185 | QSettings::setDefaultFormat(QSettings::IniFormat); |
| 191 | − | |
| 186 | + | |
| 192 | 187 | _myAvatar = _avatarManager.getMyAvatar(); |
| 193 | 188 | |
| 194 | 189 | _applicationStartupTime = startup_time; |
| 195 | − | |
| 190 | + | |
| 196 | 191 | QFontDatabase::addApplicationFont(Application::resourcesPath() + "styles/Inconsolata.otf"); |
| 197 | 192 | _window->setWindowTitle("Interface"); |
| 198 | 193 | |
| @@ -207,19 +202,19 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 8 unmodified lines | ||
| 207 | 202 | if (portStr) { |
| 208 | 203 | listenPort = atoi(portStr); |
| 209 | 204 | } |
| 210 | − | |
| 205 | + | |
| 211 | 206 | // start the nodeThread so its event loop is running |
| 212 | 207 | _nodeThread->start(); |
| 213 | − | |
| 208 | + | |
| 214 | 209 | // make sure the node thread is given highest priority |
| 215 | 210 | _nodeThread->setPriority(QThread::TimeCriticalPriority); |
| 216 | − | |
| 211 | + | |
| 217 | 212 | // put the NodeList and datagram processing on the node thread |
| 218 | 213 | NodeList* nodeList = NodeList::createInstance(NodeType::Agent, listenPort); |
| 219 | − | |
| 214 | + | |
| 220 | 215 | nodeList->moveToThread(_nodeThread); |
| 221 | 216 | _datagramProcessor.moveToThread(_nodeThread); |
| 222 | − | |
| 217 | + | |
| 223 | 218 | // connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal |
| 224 | 219 | connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), &_datagramProcessor, SLOT(processDatagrams())); |
| 225 | 220 | |
| @@ -241,20 +236,20 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 15 unmodified lines | ||
| 241 | 236 | connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer))); |
| 242 | 237 | connect(nodeList, &NodeList::uuidChanged, this, &Application::updateWindowTitle); |
| 243 | 238 | connect(nodeList, &NodeList::limitOfSilentDomainCheckInsReached, nodeList, &NodeList::reset); |
| 244 | − | |
| 239 | + | |
| 245 | 240 | // connect to appropriate slots on AccountManager |
| 246 | 241 | AccountManager& accountManager = AccountManager::getInstance(); |
| 247 | 242 | connect(&accountManager, &AccountManager::authRequired, Menu::getInstance(), &Menu::loginForCurrentDomain); |
| 248 | 243 | connect(&accountManager, &AccountManager::usernameChanged, this, &Application::updateWindowTitle); |
| 249 | − | |
| 244 | + | |
| 250 | 245 | // set the account manager's root URL and trigger a login request if we don't have the access token |
| 251 | 246 | accountManager.setAuthURL(DEFAULT_NODE_AUTH_URL); |
| 252 | − | |
| 247 | + | |
| 253 | 248 | // once the event loop has started, check and signal for an access token |
| 254 | 249 | QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection); |
| 255 | 250 | |
| 256 | 251 | _settings = new QSettings(this); |
| 257 | − | |
| 252 | + | |
| 258 | 253 | // Check to see if the user passed in a command line option for loading a local |
| 259 | 254 | // Voxel File. |
| 260 | 255 | _voxelsFilename = getCmdOption(argc, constArgv, "-i"); |
| @@ -268,7 +263,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 7 unmodified lines | ||
| 268 | 263 | nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer |
| 269 | 264 | << NodeType::VoxelServer << NodeType::ParticleServer |
| 270 | 265 | << NodeType::MetavoxelServer); |
| 271 | − | |
| 266 | + | |
| 272 | 267 | // connect to the packet sent signal of the _voxelEditSender and the _particleEditSender |
| 273 | 268 | connect(&_voxelEditSender, &VoxelEditPacketSender::packetSent, this, &Application::packetSent); |
| 274 | 269 | connect(&_particleEditSender, &ParticleEditPacketSender::packetSent, this, &Application::packetSent); |
| @@ -278,7 +273,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 3 unmodified lines | ||
| 278 | 273 | connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); |
| 279 | 274 | silentNodeTimer->moveToThread(_nodeThread); |
| 280 | 275 | silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS); |
| 281 | − | |
| 276 | + | |
| 282 | 277 | // send the identity packet for our avatar each second to our avatar mixer |
| 283 | 278 | QTimer* identityPacketTimer = new QTimer(); |
| 284 | 279 | connect(identityPacketTimer, &QTimer::timeout, _myAvatar, &MyAvatar::sendIdentityPacket); |
| @@ -326,14 +321,18 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 41 unmodified lines | ||
| 326 | 321 | |
| 327 | 322 | // Set the sixense filtering |
| 328 | 323 | _sixenseManager.setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense)); |
| 329 | − | |
| 324 | + | |
| 330 | 325 | checkVersion(); |
| 331 | − | |
| 326 | + | |
| 332 | 327 | _overlays.init(_glWidget); // do this before scripts load |
| 333 | − | |
| 328 | + | |
| 334 | 329 | LocalVoxelsList::getInstance()->addPersistantTree(DOMAIN_TREE_NAME, _voxels.getTree()); |
| 335 | 330 | LocalVoxelsList::getInstance()->addPersistantTree(CLIPBOARD_TREE_NAME, &_clipboard); |
| 336 | 331 | |
| 332 | + _window->addDockWidget(Qt::NoDockWidgetArea, _runningScriptsWidget = new RunningScriptsWidget()); | |
| 333 | + _runningScriptsWidget->setRunningScripts(getRunningScripts()); | |
| 334 | + connect(_runningScriptsWidget, &RunningScriptsWidget::stopScriptName, this, &Application::stopScript); | |
| 335 | + | |
| 337 | 336 | // check first run... |
| 338 | 337 | QVariant firstRunValue = _settings->value("firstRun",QVariant(true)); |
| 339 | 338 | if (firstRunValue.isValid() && firstRunValue.toBool()) { |
| @@ -341,7 +340,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 1 unmodified line | ||
| 341 | 340 | // clear the scripts, and set out script to our default scripts |
| 342 | 341 | clearScriptsBeforeRunning(); |
| 343 | 342 | loadScript("http://public.highfidelity.io/scripts/defaultScripts.js"); |
| 344 | − | |
| 343 | + | |
| 345 | 344 | _settings->setValue("firstRun",QVariant(false)); |
| 346 | 345 | } else { |
| 347 | 346 | // do this as late as possible so that all required subsystems are inialized |
| @@ -352,31 +351,34 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 4 unmodified lines | ||
| 352 | 351 | Application::~Application() { |
| 353 | 352 | |
| 354 | 353 | qInstallMessageHandler(NULL); |
| 355 | − | |
| 354 | + | |
| 356 | 355 | // make sure we don't call the idle timer any more |
| 357 | 356 | delete idleTimer; |
| 358 | − | |
| 357 | + | |
| 359 | 358 | Menu::getInstance()->saveSettings(); |
| 360 | 359 | _rearMirrorTools->saveSettings(_settings); |
| 361 | − | |
| 360 | + | |
| 362 | 361 | _sharedVoxelSystem.changeTree(new VoxelTree); |
| 363 | 362 | if (_voxelImporter) { |
| 364 | 363 | _voxelImporter->saveSettings(_settings); |
| 365 | 364 | delete _voxelImporter; |
| 366 | 365 | } |
| 367 | 366 | _settings->sync(); |
| 368 | − | |
| 367 | + | |
| 369 | 368 | // let the avatar mixer know we're out |
| 370 | 369 | MyAvatar::sendKillAvatar(); |
| 371 | − | |
| 370 | + | |
| 372 | 371 | // ask the datagram processing thread to quit and wait until it is done |
| 373 | 372 | _nodeThread->quit(); |
| 374 | 373 | _nodeThread->wait(); |
| 375 | − | |
| 374 | + | |
| 375 | + // stop the audio process | |
| 376 | + QMetaObject::invokeMethod(&_audio, "stop"); | |
| 377 | + | |
| 376 | 378 | // ask the audio thread to quit and wait until it is done |
| 377 | 379 | _audio.thread()->quit(); |
| 378 | 380 | _audio.thread()->wait(); |
| 379 | − | |
| 381 | + | |
| 380 | 382 | _voxelProcessor.terminate(); |
| 381 | 383 | _voxelHideShowThread.terminate(); |
| 382 | 384 | _voxelEditSender.terminate(); |
| @@ -389,9 +391,9 @@ Application::~Application() { · 6 unmodified lines | ||
| 389 | 391 | Menu::getInstance()->deleteLater(); |
| 390 | 392 | |
| 391 | 393 | _myAvatar = NULL; |
| 392 | − | |
| 394 | + | |
| 393 | 395 | delete _glWidget; |
| 394 | − | |
| 396 | + | |
| 395 | 397 | AccountManager::getInstance().destroy(); |
| 396 | 398 | |
| 397 | 399 | gnutls_global_deinit(); |
| @@ -513,7 +515,7 @@ void Application::paintGL() { · 115 unmodified lines | ||
| 513 | 515 | _myCamera.setDistance(0.0f); |
| 514 | 516 | _myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing |
| 515 | 517 | _myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition()); |
| 516 | − _myCamera.setTargetRotation(_myAvatar->getHead()->getOrientation()); | |
| 518 | + _myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation()); | |
| 517 | 519 | |
| 518 | 520 | } else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { |
| 519 | 521 | _myCamera.setTightness(0.0f); // In first person, camera follows (untweaked) head exactly without delay |
| @@ -585,7 +587,7 @@ void Application::paintGL() { · 65 unmodified lines | ||
| 585 | 587 | |
| 586 | 588 | if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { |
| 587 | 589 | renderRearViewMirror(_mirrorViewRect); |
| 588 | − | |
| 590 | + | |
| 589 | 591 | } else if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { |
| 590 | 592 | _rearMirrorTools->render(true); |
| 591 | 593 | } |
| @@ -615,6 +617,14 @@ void Application::resizeGL(int width, int height) { · 23 unmodified lines | ||
| 615 | 617 | |
| 616 | 618 | updateProjectionMatrix(); |
| 617 | 619 | glLoadIdentity(); |
| 620 | + | |
| 621 | + // update Stats width | |
| 622 | + int horizontalOffset = 0; | |
| 623 | + if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { | |
| 624 | + // mirror is enabled, let's set horizontal offset to give stats some margin | |
| 625 | + horizontalOffset += MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; | |
| 626 | + } | |
| 627 | + Stats::getInstance()->resetWidth(width, horizontalOffset); | |
| 618 | 628 | } |
| 619 | 629 | |
| 620 | 630 | void Application::updateProjectionMatrix() { |
| @@ -658,10 +668,10 @@ void Application::controlledBroadcastToNodes(const QByteArray& packet, const Nod · 37 unmodified lines | ||
| 658 | 668 | if (type == NodeType::VoxelServer && !Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { |
| 659 | 669 | continue; |
| 660 | 670 | } |
| 661 | − | |
| 671 | + | |
| 662 | 672 | // Perform the broadcast for one type |
| 663 | 673 | int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(packet, NodeSet() << type); |
| 664 | − | |
| 674 | + | |
| 665 | 675 | // Feed number of bytes to corresponding channel of the bandwidth meter, if any (done otherwise) |
| 666 | 676 | BandwidthMeter::ChannelIndex channel; |
| 667 | 677 | switch (type) { |
| @@ -680,32 +690,15 @@ void Application::controlledBroadcastToNodes(const QByteArray& packet, const Nod · 12 unmodified lines | ||
| 680 | 690 | } |
| 681 | 691 | |
| 682 | 692 | bool Application::event(QEvent* event) { |
| 683 | − | |
| 693 | + | |
| 684 | 694 | // handle custom URL |
| 685 | 695 | if (event->type() == QEvent::FileOpen) { |
| 686 | 696 | QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event); |
| 687 | − if (!fileEvent->url().isEmpty() && fileEvent->url().toLocalFile().startsWith(CUSTOM_URL_SCHEME)) { | |
| 688 | − QString destination = fileEvent->url().toLocalFile().remove(CUSTOM_URL_SCHEME); | |
| 689 | − QStringList urlParts = destination.split('/', QString::SkipEmptyParts); | |
| 690 | − | |
| 691 | − if (urlParts.count() > 1) { | |
| 692 | − // if url has 2 or more parts, the first one is domain name | |
| 693 | − Menu::getInstance()->goToDomain(urlParts[0]); | |
| 694 | − | |
| 695 | − // location coordinates | |
| 696 | − Menu::getInstance()->goToDestination(urlParts[1]); | |
| 697 | − if (urlParts.count() > 2) { | |
| 698 | − | |
| 699 | − // location orientation | |
| 700 | − Menu::getInstance()->goToOrientation(urlParts[2]); | |
| 701 | − } | |
| 702 | − } else if (urlParts.count() == 1) { | |
| 703 | − | |
| 704 | − // location coordinates | |
| 705 | − Menu::getInstance()->goToDestination(urlParts[0]); | |
| 706 | − } | |
| 697 | + bool isHifiSchemeURL = !fileEvent->url().isEmpty() && fileEvent->url().toLocalFile().startsWith(CUSTOM_URL_SCHEME); | |
| 698 | + if (isHifiSchemeURL) { | |
| 699 | + Menu::getInstance()->goTo(fileEvent->url().toString()); | |
| 707 | 700 | } |
| 708 | − | |
| 701 | + | |
| 709 | 702 | return false; |
| 710 | 703 | } |
| 711 | 704 | return QApplication::event(event); |
| @@ -716,7 +709,7 @@ void Application::keyPressEvent(QKeyEvent* event) { · 4 unmodified lines | ||
| 716 | 709 | _keysPressed.insert(event->key()); |
| 717 | 710 | |
| 718 | 711 | _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts |
| 719 | − | |
| 712 | + | |
| 720 | 713 | // if one of our scripts have asked to capture this event, then stop processing it |
| 721 | 714 | if (_controllerScriptingInterface.isKeyCaptured(event)) { |
| 722 | 715 | return; |
| @@ -1074,8 +1067,12 @@ void Application::mouseReleaseEvent(QMouseEvent* event) { · 351 unmodified lines | ||
| 1074 | 1067 | _mousePressed = false; |
| 1075 | 1068 | checkBandwidthMeterClick(); |
| 1076 | 1069 | if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { |
| 1077 | − checkStatsClick(); | |
| 1078 | − } | |
| 1070 | + int horizontalOffset = 0; | |
| 1071 | + if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { | |
| 1072 | + horizontalOffset = MIRROR_VIEW_WIDTH; | |
| 1073 | + } | |
| 1074 | + Stats::getInstance()->checkClick(_mouseX, _mouseY, _mouseDragStartedX, _mouseDragStartedY, horizontalOffset); | |
| 1075 | + } | |
| 1079 | 1076 | } |
| 1080 | 1077 | } |
| 1081 | 1078 | } |
| @@ -1124,7 +1121,7 @@ void Application::touchBeginEvent(QTouchEvent* event) { · 42 unmodified lines | ||
| 1124 | 1121 | if (_controllerScriptingInterface.isTouchCaptured()) { |
| 1125 | 1122 | return; |
| 1126 | 1123 | } |
| 1127 | − | |
| 1124 | + | |
| 1128 | 1125 | // put any application specific touch behavior below here.. |
| 1129 | 1126 | _lastTouchAvgX = _touchAvgX; |
| 1130 | 1127 | _lastTouchAvgY = _touchAvgY; |
| @@ -1167,13 +1164,13 @@ void Application::dropEvent(QDropEvent *event) { · 36 unmodified lines | ||
| 1167 | 1164 | break; |
| 1168 | 1165 | } |
| 1169 | 1166 | } |
| 1170 | − | |
| 1167 | + | |
| 1171 | 1168 | SnapshotMetaData* snapshotData = Snapshot::parseSnapshotData(snapshotPath); |
| 1172 | 1169 | if (snapshotData) { |
| 1173 | 1170 | if (!snapshotData->getDomain().isEmpty()) { |
| 1174 | 1171 | Menu::getInstance()->goToDomain(snapshotData->getDomain()); |
| 1175 | 1172 | } |
| 1176 | − | |
| 1173 | + | |
| 1177 | 1174 | _myAvatar->setPosition(snapshotData->getLocation()); |
| 1178 | 1175 | _myAvatar->setOrientation(snapshotData->getOrientation()); |
| 1179 | 1176 | } else { |
| @@ -1201,19 +1198,19 @@ void Application::timer() { · 21 unmodified lines | ||
| 1201 | 1198 | } |
| 1202 | 1199 | |
| 1203 | 1200 | _fps = (float)_frameCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); |
| 1204 | − | |
| 1201 | + | |
| 1205 | 1202 | _packetsPerSecond = (float) _datagramProcessor.getPacketCount() / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); |
| 1206 | 1203 | _bytesPerSecond = (float) _datagramProcessor.getByteCount() / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); |
| 1207 | 1204 | _frameCount = 0; |
| 1208 | − | |
| 1205 | + | |
| 1209 | 1206 | _datagramProcessor.resetCounters(); |
| 1210 | 1207 | |
| 1211 | 1208 | gettimeofday(&_timerStart, NULL); |
| 1212 | 1209 | |
| 1213 | 1210 | // ask the node list to check in with the domain server |
| 1214 | 1211 | NodeList::getInstance()->sendDomainServerCheckIn(); |
| 1215 | − | |
| 1216 | − | |
| 1212 | + | |
| 1213 | + | |
| 1217 | 1214 | } |
| 1218 | 1215 | |
| 1219 | 1216 | void Application::idle() { |
| @@ -1250,11 +1247,11 @@ void Application::idle() { · 30 unmodified lines | ||
| 1250 | 1247 | _idleLoopMeasuredJitter = _idleLoopStdev.getStDev(); |
| 1251 | 1248 | _idleLoopStdev.reset(); |
| 1252 | 1249 | } |
| 1253 | − | |
| 1250 | + | |
| 1254 | 1251 | if (Menu::getInstance()->isOptionChecked(MenuOption::BuckyBalls)) { |
| 1255 | 1252 | _buckyBalls.simulate(timeSinceLastUpdate / 1000.f, Application::getInstance()->getAvatar()->getHandData()); |
| 1256 | 1253 | } |
| 1257 | − | |
| 1254 | + | |
| 1258 | 1255 | // After finishing all of the above work, restart the idle timer, allowing 2ms to process events. |
| 1259 | 1256 | idleTimer->start(2); |
| 1260 | 1257 | } |
| @@ -1386,7 +1383,7 @@ void Application::exportVoxels(const VoxelDetail& sourceVoxel) { · 125 unmodified lines | ||
| 1386 | 1383 | tr("Sparse Voxel Octree Files (*.svo)")); |
| 1387 | 1384 | QByteArray fileNameAscii = fileNameString.toLocal8Bit(); |
| 1388 | 1385 | const char* fileName = fileNameAscii.data(); |
| 1389 | − | |
| 1386 | + | |
| 1390 | 1387 | VoxelTreeElement* selectedNode = _voxels.getTree()->getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s); |
| 1391 | 1388 | if (selectedNode) { |
| 1392 | 1389 | VoxelTree exportTree; |
| @@ -1400,12 +1397,12 @@ void Application::exportVoxels(const VoxelDetail& sourceVoxel) { · 7 unmodified lines | ||
| 1400 | 1397 | |
| 1401 | 1398 | void Application::importVoxels() { |
| 1402 | 1399 | _importSucceded = false; |
| 1403 | − | |
| 1400 | + | |
| 1404 | 1401 | if (!_voxelImporter) { |
| 1405 | 1402 | _voxelImporter = new VoxelImporter(_window); |
| 1406 | 1403 | _voxelImporter->loadSettings(_settings); |
| 1407 | 1404 | } |
| 1408 | − | |
| 1405 | + | |
| 1409 | 1406 | if (!_voxelImporter->exec()) { |
| 1410 | 1407 | qDebug() << "[DEBUG] Import succeeded." << endl; |
| 1411 | 1408 | _importSucceded = true; |
| @@ -1419,7 +1416,7 @@ void Application::importVoxels() { · 7 unmodified lines | ||
| 1419 | 1416 | |
| 1420 | 1417 | // restore the main window's active state |
| 1421 | 1418 | _window->activateWindow(); |
| 1422 | − | |
| 1419 | + | |
| 1423 | 1420 | emit importDone(); |
| 1424 | 1421 | } |
| 1425 | 1422 | |
| @@ -1475,7 +1472,7 @@ void Application::pasteVoxels(const VoxelDetail& sourceVoxel) { · 49 unmodified lines | ||
| 1475 | 1472 | } |
| 1476 | 1473 | |
| 1477 | 1474 | pasteVoxelsToOctalCode(octalCodeDestination); |
| 1478 | − | |
| 1475 | + | |
| 1479 | 1476 | if (calculatedOctCode) { |
| 1480 | 1477 | delete[] calculatedOctCode; |
| 1481 | 1478 | } |
| @@ -1512,9 +1509,9 @@ void Application::init() { · 30 unmodified lines | ||
| 1512 | 1509 | |
| 1513 | 1510 | // Cleanup of the original shared tree |
| 1514 | 1511 | _sharedVoxelSystem.init(); |
| 1515 | − | |
| 1512 | + | |
| 1516 | 1513 | _voxelImporter = new VoxelImporter(_window); |
| 1517 | − | |
| 1514 | + | |
| 1518 | 1515 | _environment.init(); |
| 1519 | 1516 | |
| 1520 | 1517 | _glowEffect.init(); |
| @@ -1556,11 +1553,11 @@ void Application::init() { · 35 unmodified lines | ||
| 1556 | 1553 | _audio.setJitterBufferSamples(Menu::getInstance()->getAudioJitterBufferSamples()); |
| 1557 | 1554 | } |
| 1558 | 1555 | qDebug("Loaded settings"); |
| 1559 | − | |
| 1556 | + | |
| 1560 | 1557 | // initialize Visage and Faceshift after loading the menu settings |
| 1561 | 1558 | _faceshift.init(); |
| 1562 | 1559 | _visage.init(); |
| 1563 | − | |
| 1560 | + | |
| 1564 | 1561 | // fire off an immediate domain-server check in now that settings are loaded |
| 1565 | 1562 | NodeList::getInstance()->sendDomainServerCheckIn(); |
| 1566 | 1563 | |
| @@ -1579,20 +1576,20 @@ void Application::init() { · 12 unmodified lines | ||
| 1579 | 1576 | _particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio, &_avatarManager); |
| 1580 | 1577 | |
| 1581 | 1578 | // connect the _particleCollisionSystem to our script engine's ParticleScriptingInterface |
| 1582 | − connect(&_particleCollisionSystem, | |
| 1579 | + connect(&_particleCollisionSystem, | |
| 1583 | 1580 | SIGNAL(particleCollisionWithVoxel(const ParticleID&, const VoxelDetail&, const glm::vec3&)), |
| 1584 | − ScriptEngine::getParticlesScriptingInterface(), | |
| 1581 | + ScriptEngine::getParticlesScriptingInterface(), | |
| 1585 | 1582 | SLOT(forwardParticleCollisionWithVoxel(const ParticleID&, const VoxelDetail&, const glm::vec3&))); |
| 1586 | 1583 | |
| 1587 | − connect(&_particleCollisionSystem, | |
| 1584 | + connect(&_particleCollisionSystem, | |
| 1588 | 1585 | SIGNAL(particleCollisionWithParticle(const ParticleID&, const ParticleID&, const glm::vec3&)), |
| 1589 | − ScriptEngine::getParticlesScriptingInterface(), | |
| 1586 | + ScriptEngine::getParticlesScriptingInterface(), | |
| 1590 | 1587 | SLOT(forwardParticleCollisionWithParticle(const ParticleID&, const ParticleID&, const glm::vec3&))); |
| 1591 | − | |
| 1588 | + | |
| 1592 | 1589 | _audio.init(_glWidget); |
| 1593 | 1590 | |
| 1594 | 1591 | _rearMirrorTools = new RearMirrorTools(_glWidget, _mirrorViewRect, _settings); |
| 1595 | − | |
| 1592 | + | |
| 1596 | 1593 | connect(_rearMirrorTools, SIGNAL(closeView()), SLOT(closeMirrorView())); |
| 1597 | 1594 | connect(_rearMirrorTools, SIGNAL(restoreView()), SLOT(restoreMirrorView())); |
| 1598 | 1595 | connect(_rearMirrorTools, SIGNAL(shrinkView()), SLOT(shrinkMirrorView())); |
| @@ -1715,7 +1712,7 @@ void Application::updateMyAvatarLookAtPosition() { · 116 unmodified lines | ||
| 1715 | 1712 | float distance = TREE_SCALE; |
| 1716 | 1713 | if (_myAvatar->getLookAtTargetAvatar() && _myAvatar != _myAvatar->getLookAtTargetAvatar()) { |
| 1717 | 1714 | distance = glm::distance(_mouseRayOrigin, |
| 1718 | − static_cast<Avatar*>(_myAvatar->getLookAtTargetAvatar())->getHead()->calculateAverageEyePosition()); | |
| 1715 | + static_cast<Avatar*>(_myAvatar->getLookAtTargetAvatar())->getHead()->calculateAverageEyePosition()); | |
| 1719 | 1716 | } |
| 1720 | 1717 | const float FIXED_MIN_EYE_DISTANCE = 0.3f; |
| 1721 | 1718 | float minEyeDistance = FIXED_MIN_EYE_DISTANCE + (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON ? 0.0f : |
| @@ -1728,7 +1725,7 @@ void Application::updateMyAvatarLookAtPosition() { · 6 unmodified lines | ||
| 1728 | 1725 | eyePitch = _faceshift.getEstimatedEyePitch(); |
| 1729 | 1726 | eyeYaw = _faceshift.getEstimatedEyeYaw(); |
| 1730 | 1727 | trackerActive = true; |
| 1731 | − | |
| 1728 | + | |
| 1732 | 1729 | } else if (_visage.isActive()) { |
| 1733 | 1730 | eyePitch = _visage.getEstimatedEyePitch(); |
| 1734 | 1731 | eyeYaw = _visage.getEstimatedEyeYaw(); |
| @@ -1849,15 +1846,6 @@ void Application::updateDialogs(float deltaTime) { · 114 unmodified lines | ||
| 1849 | 1846 | } |
| 1850 | 1847 | } |
| 1851 | 1848 | |
| 1852 | −void Application::updateAudio(float deltaTime) { | |
| 1853 | − bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); | |
| 1854 | − PerformanceWarning warn(showWarnings, "Application::updateAudio()"); | |
| 1855 | − | |
| 1856 | − // Update audio stats for procedural sounds | |
| 1857 | − _audio.setLastAcceleration(_myAvatar->getThrust()); | |
| 1858 | − _audio.setLastVelocity(_myAvatar->getVelocity()); | |
| 1859 | −} | |
| 1860 | − | |
| 1861 | 1849 | void Application::updateCursor(float deltaTime) { |
| 1862 | 1850 | bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); |
| 1863 | 1851 | PerformanceWarning warn(showWarnings, "Application::updateCursor()"); |
| @@ -1906,14 +1894,13 @@ void Application::update(float deltaTime) { · 42 unmodified lines | ||
| 1906 | 1894 | updateMetavoxels(deltaTime); // update metavoxels |
| 1907 | 1895 | updateCamera(deltaTime); // handle various camera tweaks like off axis projection |
| 1908 | 1896 | updateDialogs(deltaTime); // update various stats dialogs if present |
| 1909 | − updateAudio(deltaTime); // Update audio stats for procedural sounds | |
| 1910 | 1897 | updateCursor(deltaTime); // Handle cursor updates |
| 1911 | 1898 | |
| 1912 | 1899 | _particles.update(); // update the particles... |
| 1913 | 1900 | _particleCollisionSystem.update(); // collide the particles... |
| 1914 | − | |
| 1901 | + | |
| 1915 | 1902 | _overlays.update(deltaTime); |
| 1916 | − | |
| 1903 | + | |
| 1917 | 1904 | // let external parties know we're updating |
| 1918 | 1905 | emit simulating(deltaTime); |
| 1919 | 1906 | } |
| @@ -1937,7 +1924,7 @@ void Application::updateMyAvatar(float deltaTime) { · 17 unmodified lines | ||
| 1937 | 1924 | // actually need to calculate the view frustum planes to send these details |
| 1938 | 1925 | // to the server. |
| 1939 | 1926 | loadViewFrustum(_myCamera, _viewFrustum); |
| 1940 | − | |
| 1927 | + | |
| 1941 | 1928 | // Update my voxel servers with my current voxel query... |
| 1942 | 1929 | quint64 now = usecTimestampNow(); |
| 1943 | 1930 | quint64 sinceLastQuery = now - _lastQueriedTime; |
| @@ -2213,7 +2200,7 @@ void Application::updateShadowMap() { · 269 unmodified lines | ||
| 2213 | 2200 | } |
| 2214 | 2201 | center = inverseRotation * center; |
| 2215 | 2202 | glm::vec3 minima(center.x - radius, center.y - radius, center.z - radius); |
| 2216 | − glm::vec3 maxima(center.x + radius, center.y + radius, center.z + radius); | |
| 2203 | + glm::vec3 maxima(center.x + radius, center.y + radius, center.z + radius); | |
| 2217 | 2204 | |
| 2218 | 2205 | // stretch out our extents in z so that we get all of the avatars |
| 2219 | 2206 | minima.z -= _viewFrustum.getFarClip() * 0.5f; |
| @@ -2234,7 +2221,7 @@ void Application::updateShadowMap() { · 14 unmodified lines | ||
| 2234 | 2221 | _shadowViewFrustum.setEyeOffsetPosition(glm::vec3()); |
| 2235 | 2222 | _shadowViewFrustum.setEyeOffsetOrientation(glm::quat()); |
| 2236 | 2223 | _shadowViewFrustum.calculate(); |
| 2237 | − | |
| 2224 | + | |
| 2238 | 2225 | glMatrixMode(GL_PROJECTION); |
| 2239 | 2226 | glPushMatrix(); |
| 2240 | 2227 | glLoadIdentity(); |
| @@ -2289,19 +2276,19 @@ void Application::setupWorldLight() { · 48 unmodified lines | ||
| 2289 | 2276 | |
| 2290 | 2277 | QImage Application::renderAvatarBillboard() { |
| 2291 | 2278 | _textureCache.getPrimaryFramebufferObject()->bind(); |
| 2292 | − | |
| 2279 | + | |
| 2293 | 2280 | glDisable(GL_BLEND); |
| 2294 | 2281 | |
| 2295 | 2282 | const int BILLBOARD_SIZE = 64; |
| 2296 | 2283 | renderRearViewMirror(QRect(0, _glWidget->height() - BILLBOARD_SIZE, BILLBOARD_SIZE, BILLBOARD_SIZE), true); |
| 2297 | − | |
| 2284 | + | |
| 2298 | 2285 | QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32); |
| 2299 | 2286 | glReadPixels(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE, GL_BGRA, GL_UNSIGNED_BYTE, image.bits()); |
| 2300 | − | |
| 2287 | + | |
| 2301 | 2288 | glEnable(GL_BLEND); |
| 2302 | − | |
| 2289 | + | |
| 2303 | 2290 | _textureCache.getPrimaryFramebufferObject()->release(); |
| 2304 | − | |
| 2291 | + | |
| 2305 | 2292 | return image; |
| 2306 | 2293 | } |
| 2307 | 2294 | |
| @@ -2402,7 +2389,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { · 94 unmodified lines | ||
| 2402 | 2389 | "Application::displaySide() ... metavoxels..."); |
| 2403 | 2390 | _metavoxels.render(); |
| 2404 | 2391 | } |
| 2405 | − | |
| 2392 | + | |
| 2406 | 2393 | if (Menu::getInstance()->isOptionChecked(MenuOption::BuckyBalls)) { |
| 2407 | 2394 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), |
| 2408 | 2395 | "Application::displaySide() ... bucky balls..."); |
| @@ -2415,7 +2402,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { · 6 unmodified lines | ||
| 2415 | 2402 | "Application::displaySide() ... particles..."); |
| 2416 | 2403 | _particles.render(); |
| 2417 | 2404 | } |
| 2418 | − | |
| 2405 | + | |
| 2419 | 2406 | // render the ambient occlusion effect if enabled |
| 2420 | 2407 | if (Menu::getInstance()->isOptionChecked(MenuOption::AmbientOcclusion)) { |
| 2421 | 2408 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), |
| @@ -2459,7 +2446,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { · 37 unmodified lines | ||
| 2459 | 2446 | |
| 2460 | 2447 | // give external parties a change to hook in |
| 2461 | 2448 | emit renderingInWorldInterface(); |
| 2462 | − | |
| 2449 | + | |
| 2463 | 2450 | // render JS/scriptable overlays |
| 2464 | 2451 | _overlays.render3D(); |
| 2465 | 2452 | } |
| @@ -2509,14 +2496,111 @@ void Application::displayOverlay() { · 43 unmodified lines | ||
| 2509 | 2496 | } |
| 2510 | 2497 | } |
| 2511 | 2498 | |
| 2499 | + // Audio Scope | |
| 2500 | + const int AUDIO_SCOPE_Y_OFFSET = 135; | |
| 2512 | 2501 | if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { |
| 2513 | − _audio.renderMuteIcon(1, _glWidget->height() - 50); | |
| 2514 | 2502 | if (Menu::getInstance()->isOptionChecked(MenuOption::Oscilloscope)) { |
| 2515 | − int oscilloscopeTop = _glWidget->height() - 135; | |
| 2516 | − _audioScope.render(25, oscilloscopeTop); | |
| 2503 | + int oscilloscopeTop = _glWidget->height() - AUDIO_SCOPE_Y_OFFSET; | |
| 2504 | + _audioScope.render(MIRROR_VIEW_LEFT_PADDING, oscilloscopeTop); | |
| 2517 | 2505 | } |
| 2518 | 2506 | } |
| 2519 | 2507 | |
| 2508 | + // Audio VU Meter and Mute Icon | |
| 2509 | + const int MUTE_ICON_SIZE = 24; | |
| 2510 | + const int AUDIO_METER_INSET = 2; | |
| 2511 | + const int AUDIO_METER_WIDTH = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - AUDIO_METER_INSET; | |
| 2512 | + const int AUDIO_METER_SCALE_WIDTH = AUDIO_METER_WIDTH - 2 * AUDIO_METER_INSET; | |
| 2513 | + const int AUDIO_METER_HEIGHT = 8; | |
| 2514 | + const int AUDIO_METER_Y_GAP = 8; | |
| 2515 | + const int AUDIO_METER_X = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_INSET; | |
| 2516 | + | |
| 2517 | + int audioMeterY; | |
| 2518 | + if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { | |
| 2519 | + audioMeterY = MIRROR_VIEW_HEIGHT + AUDIO_METER_Y_GAP; | |
| 2520 | + } else { | |
| 2521 | + audioMeterY = AUDIO_METER_Y_GAP; | |
| 2522 | + } | |
| 2523 | + _audio.renderMuteIcon(MIRROR_VIEW_LEFT_PADDING, audioMeterY); | |
| 2524 | + | |
| 2525 | + | |
| 2526 | + const float AUDIO_METER_BLUE[] = {0.0, 0.0, 1.0}; | |
| 2527 | + const float AUDIO_METER_GREEN[] = {0.0, 1.0, 0.0}; | |
| 2528 | + const float AUDIO_METER_RED[] = {1.0, 0.0, 0.0}; | |
| 2529 | + const float AUDIO_GREEN_START = 0.25 * AUDIO_METER_SCALE_WIDTH; | |
| 2530 | + const float AUDIO_RED_START = 0.80 * AUDIO_METER_SCALE_WIDTH; | |
| 2531 | + const float CLIPPING_INDICATOR_TIME = 1.0f; | |
| 2532 | + const float AUDIO_METER_AVERAGING = 0.5; | |
| 2533 | + const float LOG2 = log(2.f); | |
| 2534 | + const float METER_LOUDNESS_SCALE = 2.8f / 5.f; | |
| 2535 | + const float LOG2_LOUDNESS_FLOOR = 11.f; | |
| 2536 | + float audioLevel = 0.f; | |
| 2537 | + float loudness = _audio.getLastInputLoudness() + 1.f; | |
| 2538 | + | |
| 2539 | + _trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.f - AUDIO_METER_AVERAGING) * loudness; | |
| 2540 | + float log2loudness = log(_trailingAudioLoudness) / LOG2; | |
| 2541 | + | |
| 2542 | + if (log2loudness <= LOG2_LOUDNESS_FLOOR) { | |
| 2543 | + audioLevel = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE * AUDIO_METER_SCALE_WIDTH; | |
| 2544 | + } else { | |
| 2545 | + audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.f)) * METER_LOUDNESS_SCALE * AUDIO_METER_SCALE_WIDTH; | |
| 2546 | + } | |
| 2547 | + if (audioLevel > AUDIO_METER_SCALE_WIDTH) { | |
| 2548 | + audioLevel = AUDIO_METER_SCALE_WIDTH; | |
| 2549 | + } | |
| 2550 | + | |
| 2551 | + bool isClipping = ((_audio.getTimeSinceLastClip() > 0.f) && (_audio.getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)); | |
| 2552 | + | |
| 2553 | + glBegin(GL_QUADS); | |
| 2554 | + if (isClipping) { | |
| 2555 | + glColor3f(1, 0, 0); | |
| 2556 | + } else { | |
| 2557 | + glColor3f(0, 0, 0); | |
| 2558 | + } | |
| 2559 | + // Draw audio meter background Quad | |
| 2560 | + glVertex2i(AUDIO_METER_X, audioMeterY); | |
| 2561 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_WIDTH, audioMeterY); | |
| 2562 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_WIDTH, audioMeterY + AUDIO_METER_HEIGHT); | |
| 2563 | + glVertex2i(AUDIO_METER_X, audioMeterY + AUDIO_METER_HEIGHT); | |
| 2564 | + | |
| 2565 | + if (audioLevel > AUDIO_RED_START) { | |
| 2566 | + if (!isClipping) { | |
| 2567 | + glColor3fv(AUDIO_METER_RED); | |
| 2568 | + } else { | |
| 2569 | + glColor3f(1, 1, 1); | |
| 2570 | + } | |
| 2571 | + // Draw Red Quad | |
| 2572 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_RED_START, audioMeterY + AUDIO_METER_INSET); | |
| 2573 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET); | |
| 2574 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); | |
| 2575 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_RED_START, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); | |
| 2576 | + audioLevel = AUDIO_RED_START; | |
| 2577 | + } | |
| 2578 | + if (audioLevel > AUDIO_GREEN_START) { | |
| 2579 | + if (!isClipping) { | |
| 2580 | + glColor3fv(AUDIO_METER_GREEN); | |
| 2581 | + } else { | |
| 2582 | + glColor3f(1, 1, 1); | |
| 2583 | + } | |
| 2584 | + // Draw Green Quad | |
| 2585 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_GREEN_START, audioMeterY + AUDIO_METER_INSET); | |
| 2586 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET); | |
| 2587 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); | |
| 2588 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_GREEN_START, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); | |
| 2589 | + audioLevel = AUDIO_GREEN_START; | |
| 2590 | + } | |
| 2591 | + // Draw Blue Quad | |
| 2592 | + if (!isClipping) { | |
| 2593 | + glColor3fv(AUDIO_METER_BLUE); | |
| 2594 | + } else { | |
| 2595 | + glColor3f(1, 1, 1); | |
| 2596 | + } | |
| 2597 | + // Draw Blue (low level) quad | |
| 2598 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, audioMeterY + AUDIO_METER_INSET); | |
| 2599 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET); | |
| 2600 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); | |
| 2601 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); | |
| 2602 | + glEnd(); | |
| 2603 | + | |
| 2520 | 2604 | if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse)) { |
| 2521 | 2605 | _myAvatar->renderHeadMouse(); |
| 2522 | 2606 | } |
| @@ -2526,11 +2610,17 @@ void Application::displayOverlay() { · 3 unmodified lines | ||
| 2526 | 2610 | glPointSize(1.0f); |
| 2527 | 2611 | |
| 2528 | 2612 | if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { |
| 2613 | + int horizontalOffset = 0; | |
| 2614 | + if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { | |
| 2615 | + // mirror is enabled, let's set horizontal offset to give stats some margin | |
| 2616 | + horizontalOffset += MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; | |
| 2617 | + } | |
| 2618 | + int voxelPacketsToProcess = _voxelProcessor.packetsToProcessCount(); | |
| 2529 | 2619 | // Onscreen text about position, servers, etc |
| 2530 | − displayStats(); | |
| 2620 | + Stats::getInstance()->display(WHITE_TEXT, horizontalOffset, _fps, _packetsPerSecond, _bytesPerSecond, voxelPacketsToProcess); | |
| 2531 | 2621 | // Bandwidth meter |
| 2532 | 2622 | if (Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth)) { |
| 2533 | − displayStatsBackground(0x33333399, _glWidget->width() - 296, _glWidget->height() - 68, 296, 68); | |
| 2623 | + Stats::drawBackground(0x33333399, _glWidget->width() - 296, _glWidget->height() - 68, 296, 68); | |
| 2534 | 2624 | _bandwidthMeter.render(_glWidget->width(), _glWidget->height()); |
| 2535 | 2625 | } |
| 2536 | 2626 | } |
| @@ -2540,8 +2630,8 @@ void Application::displayOverlay() { · 3 unmodified lines | ||
| 2540 | 2630 | char frameTimer[10]; |
| 2541 | 2631 | quint64 mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5); |
| 2542 | 2632 | sprintf(frameTimer, "%d\n", (int)(mSecsNow % 1000)); |
| 2543 | − int timerBottom = | |
| 2544 | − (Menu::getInstance()->isOptionChecked(MenuOption::Stats) && | |
| 2633 | + int timerBottom = | |
| 2634 | + (Menu::getInstance()->isOptionChecked(MenuOption::Stats) && | |
| 2545 | 2635 | Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth)) |
| 2546 | 2636 | ? 80 : 20; |
| 2547 | 2637 | drawText(_glWidget->width() - 100, _glWidget->height() - timerBottom, 0.30f, 1.0f, 0.f, frameTimer, WHITE_TEXT); |
| @@ -2552,415 +2642,6 @@ void Application::displayOverlay() { · 4 unmodified lines | ||
| 2552 | 2642 | glPopMatrix(); |
| 2553 | 2643 | } |
| 2554 | 2644 | |
| 2555 | −// translucent background box that makes stats more readable | |
| 2556 | −void Application::displayStatsBackground(unsigned int rgba, int x, int y, int width, int height) { | |
| 2557 | − glBegin(GL_QUADS); | |
| 2558 | − glColor4f(((rgba >> 24) & 0xff) / 255.0f, | |
| 2559 | − ((rgba >> 16) & 0xff) / 255.0f, | |
| 2560 | − ((rgba >> 8) & 0xff) / 255.0f, | |
| 2561 | − (rgba & 0xff) / 255.0f); | |
| 2562 | − glVertex3f(x, y, 0); | |
| 2563 | − glVertex3f(x + width, y, 0); | |
| 2564 | − glVertex3f(x + width, y + height, 0); | |
| 2565 | − glVertex3f(x , y + height, 0); | |
| 2566 | − glEnd(); | |
| 2567 | − glColor4f(1, 1, 1, 1); | |
| 2568 | −} | |
| 2569 | − | |
| 2570 | −// display expanded or contracted stats | |
| 2571 | − | |
| 2572 | −void Application::displayStats() { | |
| 2573 | − unsigned int backgroundColor = 0x33333399; | |
| 2574 | − int verticalOffset = 0, horizontalOffset = 0, lines = 0; | |
| 2575 | − bool mirrorEnabled = Menu::getInstance()->isOptionChecked(MenuOption::Mirror); | |
| 2576 | − | |
| 2577 | − QLocale locale(QLocale::English); | |
| 2578 | − std::stringstream voxelStats; | |
| 2579 | − | |
| 2580 | − glPointSize(1.0f); | |
| 2581 | − | |
| 2582 | − // we need to take one avatar out so we don't include ourselves | |
| 2583 | − int totalAvatars = _avatarManager.size() - 1; | |
| 2584 | − int totalServers = NodeList::getInstance()->size(); | |
| 2585 | − | |
| 2586 | − if (mirrorEnabled) { | |
| 2587 | − horizontalOffset += MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; | |
| 2588 | − } | |
| 2589 | − | |
| 2590 | − lines = _statsExpanded ? 5 : 3; | |
| 2591 | − displayStatsBackground(backgroundColor, horizontalOffset, 0, 165, lines * STATS_PELS_PER_LINE + 10); | |
| 2592 | − horizontalOffset += 5; | |
| 2593 | − | |
| 2594 | − char serverNodes[30]; | |
| 2595 | − sprintf(serverNodes, "Servers: %d", totalServers); | |
| 2596 | − char avatarNodes[30]; | |
| 2597 | − sprintf(avatarNodes, "Avatars: %d", totalAvatars); | |
| 2598 | − char framesPerSecond[30]; | |
| 2599 | − sprintf(framesPerSecond, "Framerate: %3.0f FPS", _fps); | |
| 2600 | − | |
| 2601 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2602 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, serverNodes, WHITE_TEXT); | |
| 2603 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2604 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarNodes, WHITE_TEXT); | |
| 2605 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2606 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, framesPerSecond, WHITE_TEXT); | |
| 2607 | − | |
| 2608 | − if (_statsExpanded) { | |
| 2609 | − char packetsPerSecond[30]; | |
| 2610 | − sprintf(packetsPerSecond, "Pkts/sec: %d", _packetsPerSecond); | |
| 2611 | − char averageMegabitsPerSecond[30]; | |
| 2612 | − sprintf(averageMegabitsPerSecond, "Mbps: %3.2f", (float)_bytesPerSecond * 8.f / 1000000.f); | |
| 2613 | − | |
| 2614 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2615 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, packetsPerSecond, WHITE_TEXT); | |
| 2616 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2617 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, averageMegabitsPerSecond, WHITE_TEXT); | |
| 2618 | − } | |
| 2619 | − | |
| 2620 | − verticalOffset = 0; | |
| 2621 | − horizontalOffset += 161; | |
| 2622 | − | |
| 2623 | − if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { | |
| 2624 | − int pingAudio = 0, pingAvatar = 0, pingVoxel = 0, pingVoxelMax = 0; | |
| 2625 | − | |
| 2626 | − NodeList* nodeList = NodeList::getInstance(); | |
| 2627 | − SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); | |
| 2628 | − SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); | |
| 2629 | − | |
| 2630 | − pingAudio = audioMixerNode ? audioMixerNode->getPingMs() : 0; | |
| 2631 | − pingAvatar = avatarMixerNode ? avatarMixerNode->getPingMs() : 0; | |
| 2632 | − | |
| 2633 | − // Now handle voxel servers, since there could be more than one, we average their ping times | |
| 2634 | − unsigned long totalPingVoxel = 0; | |
| 2635 | − int voxelServerCount = 0; | |
| 2636 | − | |
| 2637 | − foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { | |
| 2638 | − if (node->getType() == NodeType::VoxelServer) { | |
| 2639 | − totalPingVoxel += node->getPingMs(); | |
| 2640 | − voxelServerCount++; | |
| 2641 | − if (pingVoxelMax < node->getPingMs()) { | |
| 2642 | − pingVoxelMax = node->getPingMs(); | |
| 2643 | − } | |
| 2644 | − } | |
| 2645 | − } | |
| 2646 | − | |
| 2647 | − if (voxelServerCount) { | |
| 2648 | − pingVoxel = totalPingVoxel/voxelServerCount; | |
| 2649 | − } | |
| 2650 | − | |
| 2651 | − lines = _statsExpanded ? 4 : 3; | |
| 2652 | − displayStatsBackground(backgroundColor, horizontalOffset, 0, 175, lines * STATS_PELS_PER_LINE + 10); | |
| 2653 | − horizontalOffset += 5; | |
| 2654 | − | |
| 2655 | − char audioJitter[30]; | |
| 2656 | − sprintf(audioJitter, | |
| 2657 | − "Buffer msecs %.1f", | |
| 2658 | − (float) (_audio.getNetworkBufferLengthSamplesPerChannel() + (float) _audio.getJitterBufferSamples()) / | |
| 2659 | − (float)_audio.getNetworkSampleRate() * 1000.f); | |
| 2660 | − drawText(30, _glWidget->height() - 22, 0.10f, 0.f, 2.f, audioJitter, WHITE_TEXT); | |
| 2661 | − | |
| 2662 | − | |
| 2663 | − char audioPing[30]; | |
| 2664 | − sprintf(audioPing, "Audio ping: %d", pingAudio); | |
| 2665 | − | |
| 2666 | − | |
| 2667 | − char avatarPing[30]; | |
| 2668 | − sprintf(avatarPing, "Avatar ping: %d", pingAvatar); | |
| 2669 | − char voxelAvgPing[30]; | |
| 2670 | − sprintf(voxelAvgPing, "Voxel avg ping: %d", pingVoxel); | |
| 2671 | − | |
| 2672 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2673 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, audioPing, WHITE_TEXT); | |
| 2674 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2675 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarPing, WHITE_TEXT); | |
| 2676 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2677 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, voxelAvgPing, WHITE_TEXT); | |
| 2678 | − | |
| 2679 | − if (_statsExpanded) { | |
| 2680 | − char voxelMaxPing[30]; | |
| 2681 | − sprintf(voxelMaxPing, "Voxel max ping: %d", pingVoxelMax); | |
| 2682 | − | |
| 2683 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2684 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, voxelMaxPing, WHITE_TEXT); | |
| 2685 | − } | |
| 2686 | − | |
| 2687 | − verticalOffset = 0; | |
| 2688 | − horizontalOffset += 171; | |
| 2689 | − } | |
| 2690 | − | |
| 2691 | − glm::vec3 avatarPos = _myAvatar->getPosition(); | |
| 2692 | − | |
| 2693 | − lines = _statsExpanded ? 5 : 3; | |
| 2694 | − displayStatsBackground(backgroundColor, horizontalOffset, 0, _glWidget->width() - (mirrorEnabled ? 301 : 411) - horizontalOffset, lines * STATS_PELS_PER_LINE + 10); | |
| 2695 | − horizontalOffset += 5; | |
| 2696 | − | |
| 2697 | − char avatarPosition[200]; | |
| 2698 | − if (mirrorEnabled) { | |
| 2699 | − // shorthand formatting | |
| 2700 | − sprintf(avatarPosition, "Pos: %.0f,%.0f,%.0f", avatarPos.x, avatarPos.y, avatarPos.z); | |
| 2701 | − } else { | |
| 2702 | − // longhand way | |
| 2703 | − sprintf(avatarPosition, "Position: %.1f, %.1f, %.1f", avatarPos.x, avatarPos.y, avatarPos.z); | |
| 2704 | − } | |
| 2705 | − char avatarVelocity[30]; | |
| 2706 | − sprintf(avatarVelocity, "Velocity: %.1f", glm::length(_myAvatar->getVelocity())); | |
| 2707 | − char avatarBodyYaw[30]; | |
| 2708 | − sprintf(avatarBodyYaw, "Yaw: %.1f", _myAvatar->getBodyYaw()); | |
| 2709 | − char avatarMixerStats[200]; | |
| 2710 | − | |
| 2711 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2712 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarPosition, WHITE_TEXT); | |
| 2713 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2714 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarVelocity, WHITE_TEXT); | |
| 2715 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2716 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarBodyYaw, WHITE_TEXT); | |
| 2717 | − | |
| 2718 | − if (_statsExpanded) { | |
| 2719 | − SharedNodePointer avatarMixer = NodeList::getInstance()->soloNodeOfType(NodeType::AvatarMixer); | |
| 2720 | − if (avatarMixer) { | |
| 2721 | − sprintf(avatarMixerStats, "Avatar Mixer: %.f kbps, %.f pps", | |
| 2722 | − roundf(avatarMixer->getAverageKilobitsPerSecond()), | |
| 2723 | − roundf(avatarMixer->getAveragePacketsPerSecond())); | |
| 2724 | − } else { | |
| 2725 | − sprintf(avatarMixerStats, "No Avatar Mixer"); | |
| 2726 | − } | |
| 2727 | − | |
| 2728 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2729 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarMixerStats, WHITE_TEXT); | |
| 2730 | − | |
| 2731 | − stringstream downloadStats; | |
| 2732 | − downloadStats << "Downloads: "; | |
| 2733 | − foreach (Resource* resource, ResourceCache::getLoadingRequests()) { | |
| 2734 | − const float MAXIMUM_PERCENTAGE = 100.0f; | |
| 2735 | − downloadStats << roundf(resource->getProgress() * MAXIMUM_PERCENTAGE) << "% "; | |
| 2736 | − } | |
| 2737 | − downloadStats << "(" << ResourceCache::getPendingRequestCount() << " pending)"; | |
| 2738 | − | |
| 2739 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2740 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, downloadStats.str().c_str(), WHITE_TEXT); | |
| 2741 | − } | |
| 2742 | − | |
| 2743 | − verticalOffset = 0; | |
| 2744 | − horizontalOffset = _glWidget->width() - (mirrorEnabled ? 300 : 410); | |
| 2745 | − | |
| 2746 | − lines = _statsExpanded ? 12 : 3; | |
| 2747 | − displayStatsBackground(backgroundColor, horizontalOffset, 0, _glWidget->width() - horizontalOffset, lines * STATS_PELS_PER_LINE + 10); | |
| 2748 | − horizontalOffset += 5; | |
| 2749 | − | |
| 2750 | − if (_statsExpanded) { | |
| 2751 | − // Local Voxel Memory Usage | |
| 2752 | − voxelStats.str(""); | |
| 2753 | − voxelStats << "Voxels Memory Nodes: " << VoxelTreeElement::getTotalMemoryUsage() / 1000000.f << "MB"; | |
| 2754 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2755 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2756 | − | |
| 2757 | − voxelStats.str(""); | |
| 2758 | − voxelStats << | |
| 2759 | − "Geometry RAM: " << _voxels.getVoxelMemoryUsageRAM() / 1000000.f << "MB / " << | |
| 2760 | − "VBO: " << _voxels.getVoxelMemoryUsageVBO() / 1000000.f << "MB"; | |
| 2761 | − if (_voxels.hasVoxelMemoryUsageGPU()) { | |
| 2762 | − voxelStats << " / GPU: " << _voxels.getVoxelMemoryUsageGPU() / 1000000.f << "MB"; | |
| 2763 | − } | |
| 2764 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2765 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2766 | − | |
| 2767 | − // Voxel Rendering | |
| 2768 | − voxelStats.str(""); | |
| 2769 | − voxelStats.precision(4); | |
| 2770 | − voxelStats << "Voxel Rendering Slots Max: " << _voxels.getMaxVoxels() / 1000.f << "K"; | |
| 2771 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2772 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2773 | − } | |
| 2774 | − | |
| 2775 | − voxelStats.str(""); | |
| 2776 | − voxelStats.precision(4); | |
| 2777 | − voxelStats << "Drawn: " << _voxels.getVoxelsWritten() / 1000.f << "K " << | |
| 2778 | − "Abandoned: " << _voxels.getAbandonedVoxels() / 1000.f << "K "; | |
| 2779 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2780 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2781 | − | |
| 2782 | − // iterate all the current voxel stats, and list their sending modes, and total voxel counts | |
| 2783 | − std::stringstream sendingMode(""); | |
| 2784 | − sendingMode << "Octree Sending Mode: ["; | |
| 2785 | − int serverCount = 0; | |
| 2786 | − int movingServerCount = 0; | |
| 2787 | − unsigned long totalNodes = 0; | |
| 2788 | − unsigned long totalInternal = 0; | |
| 2789 | − unsigned long totalLeaves = 0; | |
| 2790 | − for(NodeToOctreeSceneStatsIterator i = _octreeServerSceneStats.begin(); i != _octreeServerSceneStats.end(); i++) { | |
| 2791 | − //const QUuid& uuid = i->first; | |
| 2792 | − OctreeSceneStats& stats = i->second; | |
| 2793 | − serverCount++; | |
| 2794 | − if (_statsExpanded) { | |
| 2795 | − if (serverCount > 1) { | |
| 2796 | − sendingMode << ","; | |
| 2797 | − } | |
| 2798 | − if (stats.isMoving()) { | |
| 2799 | − sendingMode << "M"; | |
| 2800 | − movingServerCount++; | |
| 2801 | − } else { | |
| 2802 | − sendingMode << "S"; | |
| 2803 | − } | |
| 2804 | − } | |
| 2805 | − | |
| 2806 | − // calculate server node totals | |
| 2807 | − totalNodes += stats.getTotalElements(); | |
| 2808 | − if (_statsExpanded) { | |
| 2809 | − totalInternal += stats.getTotalInternal(); | |
| 2810 | − totalLeaves += stats.getTotalLeaves(); | |
| 2811 | − } | |
| 2812 | − } | |
| 2813 | − if (_statsExpanded) { | |
| 2814 | − if (serverCount == 0) { | |
| 2815 | − sendingMode << "---"; | |
| 2816 | − } | |
| 2817 | − sendingMode << "] " << serverCount << " servers"; | |
| 2818 | − if (movingServerCount > 0) { | |
| 2819 | − sendingMode << " <SCENE NOT STABLE>"; | |
| 2820 | − } else { | |
| 2821 | − sendingMode << " <SCENE STABLE>"; | |
| 2822 | − } | |
| 2823 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2824 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)sendingMode.str().c_str(), WHITE_TEXT); | |
| 2825 | − } | |
| 2826 | − | |
| 2827 | − // Incoming packets | |
| 2828 | − int voxelPacketsToProcess = _voxelProcessor.packetsToProcessCount(); | |
| 2829 | − if (_statsExpanded) { | |
| 2830 | − voxelStats.str(""); | |
| 2831 | − QString packetsString = locale.toString((int)voxelPacketsToProcess); | |
| 2832 | − QString maxString = locale.toString((int)_recentMaxPackets); | |
| 2833 | − voxelStats << "Voxel Packets to Process: " << qPrintable(packetsString) | |
| 2834 | − << " [Recent Max: " << qPrintable(maxString) << "]"; | |
| 2835 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2836 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2837 | − } | |
| 2838 | − | |
| 2839 | − if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) { | |
| 2840 | − _recentMaxPackets = 0; | |
| 2841 | − _resetRecentMaxPacketsSoon = false; | |
| 2842 | − } | |
| 2843 | − if (voxelPacketsToProcess == 0) { | |
| 2844 | − _resetRecentMaxPacketsSoon = true; | |
| 2845 | − } else { | |
| 2846 | − if (voxelPacketsToProcess > _recentMaxPackets) { | |
| 2847 | − _recentMaxPackets = voxelPacketsToProcess; | |
| 2848 | − } | |
| 2849 | − } | |
| 2850 | − | |
| 2851 | − verticalOffset += (_statsExpanded ? STATS_PELS_PER_LINE : 0); | |
| 2852 | − | |
| 2853 | − QString serversTotalString = locale.toString((uint)totalNodes); // consider adding: .rightJustified(10, ' '); | |
| 2854 | − | |
| 2855 | − // Server Voxels | |
| 2856 | − voxelStats.str(""); | |
| 2857 | − voxelStats << "Server voxels: " << qPrintable(serversTotalString); | |
| 2858 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2859 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2860 | − | |
| 2861 | − if (_statsExpanded) { | |
| 2862 | − QString serversInternalString = locale.toString((uint)totalInternal); | |
| 2863 | − QString serversLeavesString = locale.toString((uint)totalLeaves); | |
| 2864 | − | |
| 2865 | − voxelStats.str(""); | |
| 2866 | − voxelStats << | |
| 2867 | − "Internal: " << qPrintable(serversInternalString) << " " << | |
| 2868 | − "Leaves: " << qPrintable(serversLeavesString) << ""; | |
| 2869 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2870 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2871 | − } | |
| 2872 | − | |
| 2873 | − unsigned long localTotal = VoxelTreeElement::getNodeCount(); | |
| 2874 | − QString localTotalString = locale.toString((uint)localTotal); // consider adding: .rightJustified(10, ' '); | |
| 2875 | − | |
| 2876 | − // Local Voxels | |
| 2877 | − voxelStats.str(""); | |
| 2878 | − voxelStats << "Local voxels: " << qPrintable(localTotalString); | |
| 2879 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2880 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2881 | − | |
| 2882 | − if (_statsExpanded) { | |
| 2883 | − unsigned long localInternal = VoxelTreeElement::getInternalNodeCount(); | |
| 2884 | − unsigned long localLeaves = VoxelTreeElement::getLeafNodeCount(); | |
| 2885 | − QString localInternalString = locale.toString((uint)localInternal); | |
| 2886 | − QString localLeavesString = locale.toString((uint)localLeaves); | |
| 2887 | − | |
| 2888 | − voxelStats.str(""); | |
| 2889 | − voxelStats << | |
| 2890 | − "Internal: " << qPrintable(localInternalString) << " " << | |
| 2891 | − "Leaves: " << qPrintable(localLeavesString) << ""; | |
| 2892 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2893 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2894 | − } | |
| 2895 | − | |
| 2896 | − // LOD Details | |
| 2897 | − if (_statsExpanded) { | |
| 2898 | − voxelStats.str(""); | |
| 2899 | − QString displayLODDetails = Menu::getInstance()->getLODFeedbackText(); | |
| 2900 | − voxelStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed()); | |
| 2901 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2902 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2903 | − } | |
| 2904 | −} | |
| 2905 | − | |
| 2906 | −// called on mouse click release | |
| 2907 | −// check for clicks over stats in order to expand or contract them | |
| 2908 | −void Application::checkStatsClick() { | |
| 2909 | − if (0 != glm::compMax(glm::abs(glm::ivec2(_mouseX - _mouseDragStartedX, _mouseY - _mouseDragStartedY)))) { | |
| 2910 | − // not worried about dragging on stats | |
| 2911 | − return; | |
| 2912 | − } | |
| 2913 | − | |
| 2914 | − int statsHeight = 0, statsWidth = 0, statsX = 0, statsY = 0, lines = 0; | |
| 2915 | − | |
| 2916 | − if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { | |
| 2917 | − statsX += MIRROR_VIEW_WIDTH; | |
| 2918 | − } | |
| 2919 | − | |
| 2920 | − // top-left stats click | |
| 2921 | − lines = _statsExpanded ? 5 : 3; | |
| 2922 | − statsHeight = lines * STATS_PELS_PER_LINE + 10; | |
| 2923 | − statsWidth = 165; | |
| 2924 | − if (_mouseX > statsX && _mouseX < statsX + statsWidth && _mouseY > statsY && _mouseY < statsY + statsHeight) { | |
| 2925 | − toggleStatsExpanded(); | |
| 2926 | − return; | |
| 2927 | − } | |
| 2928 | − | |
| 2929 | − // ping stats click | |
| 2930 | − lines = _statsExpanded ? 4 : 3; | |
| 2931 | − statsX += statsWidth; | |
| 2932 | − statsHeight = lines * STATS_PELS_PER_LINE + 10; | |
| 2933 | − statsWidth = 175; | |
| 2934 | − if (_mouseX > statsX && _mouseX < statsX + statsWidth && _mouseY > statsY && _mouseY < statsY + statsHeight) { | |
| 2935 | − toggleStatsExpanded(); | |
| 2936 | − return; | |
| 2937 | − } | |
| 2938 | − | |
| 2939 | − // top-center stats panel click | |
| 2940 | − lines = _statsExpanded ? 4 : 3; | |
| 2941 | − statsX += statsWidth; | |
| 2942 | − statsHeight = lines * STATS_PELS_PER_LINE + 10; | |
| 2943 | − statsWidth = _glWidget->width() - 411 - statsX; | |
| 2944 | − if (_mouseX > statsX && _mouseX < statsX + statsWidth && _mouseY > statsY && _mouseY < statsY + statsHeight) { | |
| 2945 | − toggleStatsExpanded(); | |
| 2946 | − return; | |
| 2947 | − } | |
| 2948 | − | |
| 2949 | − // top-right stats click | |
| 2950 | − lines = _statsExpanded ? 11 : 3; | |
| 2951 | − statsX = _glWidget->width() - 410; | |
| 2952 | − statsHeight = lines * STATS_PELS_PER_LINE + 10; | |
| 2953 | − statsWidth = _glWidget->width() - statsX; | |
| 2954 | − if (_mouseX > statsX && _mouseX < statsX + statsWidth && _mouseY > statsY && _mouseY < statsY + statsHeight) { | |
| 2955 | − toggleStatsExpanded(); | |
| 2956 | − return; | |
| 2957 | − } | |
| 2958 | −} | |
| 2959 | − | |
| 2960 | −void Application::toggleStatsExpanded() { | |
| 2961 | − _statsExpanded = !_statsExpanded; | |
| 2962 | −} | |
| 2963 | − | |
| 2964 | 2645 | glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) { |
| 2965 | 2646 | float horizontalScale = _glWidget->width() / 2.0f; |
| 2966 | 2647 | float verticalScale = _glWidget->height() / 2.0f; |
| @@ -2993,12 +2674,12 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { · 26 unmodified lines | ||
| 2993 | 2674 | _mirrorCamera.setFieldOfView(BILLBOARD_FIELD_OF_VIEW); // degees |
| 2994 | 2675 | _mirrorCamera.setDistance(BILLBOARD_DISTANCE * _myAvatar->getScale()); |
| 2995 | 2676 | _mirrorCamera.setTargetPosition(_myAvatar->getPosition()); |
| 2996 | − | |
| 2677 | + | |
| 2997 | 2678 | } else if (_rearMirrorTools->getZoomLevel() == BODY) { |
| 2998 | 2679 | _mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees |
| 2999 | 2680 | _mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); |
| 3000 | 2681 | _mirrorCamera.setTargetPosition(_myAvatar->getChestPosition()); |
| 3001 | − | |
| 2682 | + | |
| 3002 | 2683 | } else { // HEAD zoom level |
| 3003 | 2684 | _mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees |
| 3004 | 2685 | _mirrorCamera.setDistance(MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); |
| @@ -3013,7 +2694,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { · 8 unmodified lines | ||
| 3013 | 2694 | } |
| 3014 | 2695 | } |
| 3015 | 2696 | _mirrorCamera.setAspectRatio((float)region.width() / region.height()); |
| 3016 | − | |
| 2697 | + | |
| 3017 | 2698 | _mirrorCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); |
| 3018 | 2699 | _mirrorCamera.update(1.0f/_fps); |
| 3019 | 2700 | |
| @@ -3058,7 +2739,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { · 38 unmodified lines | ||
| 3058 | 2739 | if (!billboard) { |
| 3059 | 2740 | _rearMirrorTools->render(false); |
| 3060 | 2741 | } |
| 3061 | − | |
| 2742 | + | |
| 3062 | 2743 | // reset Viewport and projection matrix |
| 3063 | 2744 | glViewport(0, 0, _glWidget->width(), _glWidget->height()); |
| 3064 | 2745 | glDisable(GL_SCISSOR_TEST); |
| @@ -3278,14 +2959,13 @@ void Application::setMenuShortcutsEnabled(bool enabled) { · 213 unmodified lines | ||
| 3278 | 2959 | } |
| 3279 | 2960 | |
| 3280 | 2961 | void Application::updateWindowTitle(){ |
| 3281 | − | |
| 2962 | + | |
| 3282 | 2963 | QString buildVersion = " (build " + applicationVersion() + ")"; |
| 3283 | 2964 | NodeList* nodeList = NodeList::getInstance(); |
| 3284 | − | |
| 2965 | + | |
| 3285 | 2966 | QString username = AccountManager::getInstance().getUsername(); |
| 3286 | 2967 | QString title = QString() + (!username.isEmpty() ? username + " " : QString()) + nodeList->getSessionUUID().toString() |
| 3287 | 2968 | + " @ " + nodeList->getDomainHandler().getHostname() + buildVersion; |
| 3288 | − | |
| 3289 | 2969 | qDebug("Application title set to: %s", title.toStdString().c_str()); |
| 3290 | 2970 | _window->setWindowTitle(title); |
| 3291 | 2971 | } |
| @@ -3300,7 +2980,7 @@ void Application::domainChanged(const QString& domainHostname) { · 8 unmodified lines | ||
| 3300 | 2980 | _voxelServerJurisdictions.clear(); |
| 3301 | 2981 | _octreeServerSceneStats.clear(); |
| 3302 | 2982 | _particleServerJurisdictions.clear(); |
| 3303 | − | |
| 2983 | + | |
| 3304 | 2984 | // reset the particle renderer |
| 3305 | 2985 | _particles.clear(); |
| 3306 | 2986 | |
| @@ -3310,12 +2990,12 @@ void Application::domainChanged(const QString& domainHostname) { · 3 unmodified lines | ||
| 3310 | 2990 | |
| 3311 | 2991 | void Application::connectedToDomain(const QString& hostname) { |
| 3312 | 2992 | AccountManager& accountManager = AccountManager::getInstance(); |
| 3313 | − | |
| 2993 | + | |
| 3314 | 2994 | if (accountManager.isLoggedIn()) { |
| 3315 | 2995 | // update our domain-server with the data-server we're logged in with |
| 3316 | − | |
| 2996 | + | |
| 3317 | 2997 | QString domainPutJsonString = "{\"address\":{\"domain\":\"" + hostname + "\"}}"; |
| 3318 | − | |
| 2998 | + | |
| 3319 | 2999 | accountManager.authenticatedRequest("/api/v1/users/address", QNetworkAccessManager::PutOperation, |
| 3320 | 3000 | JSONCallbackParameters(), domainPutJsonString.toUtf8()); |
| 3321 | 3001 | } |
| @@ -3480,13 +3160,13 @@ void Application::loadScripts() { · 158 unmodified lines | ||
| 3480 | 3160 | // loads all saved scripts |
| 3481 | 3161 | QSettings* settings = new QSettings(this); |
| 3482 | 3162 | int size = settings->beginReadArray("Settings"); |
| 3483 | − | |
| 3163 | + | |
| 3484 | 3164 | for (int i = 0; i < size; ++i){ |
| 3485 | 3165 | settings->setArrayIndex(i); |
| 3486 | 3166 | QString string = settings->value("script").toString(); |
| 3487 | 3167 | loadScript(string); |
| 3488 | 3168 | } |
| 3489 | − | |
| 3169 | + | |
| 3490 | 3170 | settings->endArray(); |
| 3491 | 3171 | } |
| 3492 | 3172 | |
| @@ -3501,72 +3181,98 @@ void Application::saveScripts() { · 8 unmodified lines | ||
| 3501 | 3181 | // saves all current running scripts |
| 3502 | 3182 | QSettings* settings = new QSettings(this); |
| 3503 | 3183 | settings->beginWriteArray("Settings"); |
| 3504 | − for (int i = 0; i < _activeScripts.size(); ++i){ | |
| 3184 | + for (int i = 0; i < getRunningScripts().size(); ++i){ | |
| 3505 | 3185 | settings->setArrayIndex(i); |
| 3506 | − settings->setValue("script", _activeScripts.at(i)); | |
| 3186 | + settings->setValue("script", getRunningScripts().at(i)); | |
| 3507 | 3187 | } |
| 3508 | − | |
| 3188 | + | |
| 3509 | 3189 | settings->endArray(); |
| 3510 | 3190 | } |
| 3511 | 3191 | |
| 3512 | 3192 | void Application::stopAllScripts() { |
| 3513 | 3193 | // stops all current running scripts |
| 3514 | − QList<QAction*> scriptActions = Menu::getInstance()->getActiveScriptsMenu()->actions(); | |
| 3515 | − foreach (QAction* scriptAction, scriptActions) { | |
| 3516 | − scriptAction->activate(QAction::Trigger); | |
| 3517 | − qDebug() << "stopping script..." << scriptAction->text(); | |
| 3194 | + for (int i = 0; i < _scriptEnginesHash.size(); ++i) { | |
| 3195 | + _scriptEnginesHash.values().at(i)->stop(); | |
| 3196 | + qDebug() << "stopping script..." << getRunningScripts().at(i); | |
| 3518 | 3197 | } |
| 3519 | − _activeScripts.clear(); | |
| 3198 | + _scriptEnginesHash.clear(); | |
| 3199 | + _runningScriptsWidget->setRunningScripts(getRunningScripts()); | |
| 3200 | +} | |
| 3201 | + | |
| 3202 | +void Application::stopScript(const QString &scriptName) | |
| 3203 | +{ | |
| 3204 | + _scriptEnginesHash.value(scriptName)->stop(); | |
| 3205 | + qDebug() << "stopping script..." << scriptName; | |
| 3206 | + _scriptEnginesHash.remove(scriptName); | |
| 3207 | + _runningScriptsWidget->setRunningScripts(getRunningScripts()); | |
| 3520 | 3208 | } |
| 3521 | 3209 | |
| 3522 | 3210 | void Application::reloadAllScripts() { |
| 3523 | 3211 | // remember all the current scripts so we can reload them |
| 3524 | − QStringList reloadList = _activeScripts; | |
| 3212 | + QStringList reloadList = getRunningScripts(); | |
| 3525 | 3213 | // reloads all current running scripts |
| 3526 | − QList<QAction*> scriptActions = Menu::getInstance()->getActiveScriptsMenu()->actions(); | |
| 3527 | − foreach (QAction* scriptAction, scriptActions) { | |
| 3528 | − scriptAction->activate(QAction::Trigger); | |
| 3529 | − qDebug() << "stopping script..." << scriptAction->text(); | |
| 3530 | − } | |
| 3214 | + stopAllScripts(); | |
| 3531 | 3215 | |
| 3532 | − // NOTE: we don't need to clear the _activeScripts list because that is handled on script shutdown. | |
| 3533 | − | |
| 3534 | 3216 | foreach (QString scriptName, reloadList){ |
| 3535 | 3217 | qDebug() << "reloading script..." << scriptName; |
| 3536 | 3218 | loadScript(scriptName); |
| 3537 | 3219 | } |
| 3538 | 3220 | } |
| 3539 | 3221 | |
| 3540 | −void Application::uploadFST() { | |
| 3541 | − FstReader reader; | |
| 3542 | − if (reader.zip()) { | |
| 3543 | − reader.send(); | |
| 3222 | +void Application::toggleRunningScriptsWidget() | |
| 3223 | +{ | |
| 3224 | + if (!_runningScriptsWidget->toggleViewAction()->isChecked()) { | |
| 3225 | + _runningScriptsWidget->move(_window->geometry().topLeft().x(), _window->geometry().topLeft().y()); | |
| 3226 | + _runningScriptsWidget->resize(0, _window->height()); | |
| 3227 | + _runningScriptsWidget->toggleViewAction()->trigger(); | |
| 3228 | + _runningScriptsWidget->grabKeyboard(); | |
| 3229 | + | |
| 3230 | + QPropertyAnimation* slideAnimation = new QPropertyAnimation(_runningScriptsWidget, "geometry", _runningScriptsWidget); | |
| 3231 | + slideAnimation->setStartValue(_runningScriptsWidget->geometry()); | |
| 3232 | + slideAnimation->setEndValue(QRect(_window->geometry().topLeft().x(), _window->geometry().topLeft().y(), | |
| 3233 | + 310, _runningScriptsWidget->height())); | |
| 3234 | + slideAnimation->setDuration(250); | |
| 3235 | + slideAnimation->start(QAbstractAnimation::DeleteWhenStopped); | |
| 3236 | + } else { | |
| 3237 | + _runningScriptsWidget->releaseKeyboard(); | |
| 3238 | + | |
| 3239 | + QPropertyAnimation* slideAnimation = new QPropertyAnimation(_runningScriptsWidget, "geometry", _runningScriptsWidget); | |
| 3240 | + slideAnimation->setStartValue(_runningScriptsWidget->geometry()); | |
| 3241 | + slideAnimation->setEndValue(QRect(_window->geometry().topLeft().x(), _window->geometry().topLeft().y(), | |
| 3242 | + 0, _runningScriptsWidget->height())); | |
| 3243 | + slideAnimation->setDuration(250); | |
| 3244 | + slideAnimation->start(QAbstractAnimation::DeleteWhenStopped); | |
| 3245 | + | |
| 3246 | + QTimer::singleShot(260, _runningScriptsWidget->toggleViewAction(), SLOT(trigger())); | |
| 3247 | + } | |
| 3248 | +} | |
| 3249 | + | |
| 3250 | +void Application::uploadFST(bool isHead) { | |
| 3251 | + ModelUploader* uploader = new ModelUploader(isHead); | |
| 3252 | + if (uploader->zip()) { | |
| 3253 | + uploader->send(); | |
| 3544 | 3254 | } |
| 3545 | 3255 | } |
| 3546 | 3256 | |
| 3547 | −void Application::removeScriptName(const QString& fileNameString) { | |
| 3548 | − _activeScripts.removeOne(fileNameString); | |
| 3257 | +void Application::uploadHead() { | |
| 3258 | + uploadFST(true); | |
| 3549 | 3259 | } |
| 3550 | 3260 | |
| 3551 | −void Application::cleanupScriptMenuItem(const QString& scriptMenuName) { | |
| 3552 | − Menu::getInstance()->removeAction(Menu::getInstance()->getActiveScriptsMenu(), scriptMenuName); | |
| 3261 | +void Application::uploadSkeleton() { | |
| 3262 | + uploadFST(false); | |
| 3553 | 3263 | } |
| 3554 | 3264 | |
| 3555 | 3265 | void Application::loadScript(const QString& scriptName) { |
| 3556 | 3266 | |
| 3557 | 3267 | // start the script on a new thread... |
| 3558 | − bool wantMenuItems = true; // tells the ScriptEngine object to add menu items for itself | |
| 3559 | − ScriptEngine* scriptEngine = new ScriptEngine(QUrl(scriptName), wantMenuItems, &_controllerScriptingInterface); | |
| 3268 | + ScriptEngine* scriptEngine = new ScriptEngine(QUrl(scriptName), &_controllerScriptingInterface); | |
| 3269 | + _scriptEnginesHash.insert(scriptName, scriptEngine); | |
| 3560 | 3270 | |
| 3561 | 3271 | if (!scriptEngine->hasScript()) { |
| 3562 | 3272 | qDebug() << "Application::loadScript(), script failed to load..."; |
| 3563 | 3273 | return; |
| 3564 | 3274 | } |
| 3565 | − _activeScripts.append(scriptName); | |
| 3566 | − | |
| 3567 | − // add a stop menu item | |
| 3568 | − Menu::getInstance()->addActionToQMenuAndActionHash(Menu::getInstance()->getActiveScriptsMenu(), | |
| 3569 | − scriptEngine->getScriptMenuName(), 0, scriptEngine, SLOT(stop())); | |
| 3275 | + _runningScriptsWidget->setRunningScripts(getRunningScripts()); | |
| 3570 | 3276 | |
| 3571 | 3277 | // setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so |
| 3572 | 3278 | // we can use the same ones from the application. |
| @@ -3574,7 +3280,7 @@ void Application::loadScript(const QString& scriptName) { · 1 unmodified line | ||
| 3574 | 3280 | scriptEngine->getVoxelsScriptingInterface()->setVoxelTree(_voxels.getTree()); |
| 3575 | 3281 | scriptEngine->getParticlesScriptingInterface()->setPacketSender(&_particleEditSender); |
| 3576 | 3282 | scriptEngine->getParticlesScriptingInterface()->setParticleTree(_particles.getTree()); |
| 3577 | − | |
| 3283 | + | |
| 3578 | 3284 | // hook our avatar object into this script engine |
| 3579 | 3285 | scriptEngine->setAvatarData(_myAvatar, "MyAvatar"); // leave it as a MyAvatar class to expose thrust features |
| 3580 | 3286 | |
| @@ -3599,8 +3305,6 @@ void Application::loadScript(const QString& scriptName) { · 18 unmodified lines | ||
| 3599 | 3305 | // when the thread is terminated, add both scriptEngine and thread to the deleteLater queue |
| 3600 | 3306 | connect(scriptEngine, SIGNAL(finished(const QString&)), scriptEngine, SLOT(deleteLater())); |
| 3601 | 3307 | connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater())); |
| 3602 | − connect(scriptEngine, SIGNAL(finished(const QString&)), this, SLOT(removeScriptName(const QString&))); | |
| 3603 | − connect(scriptEngine, SIGNAL(cleanupMenuItem(const QString&)), this, SLOT(cleanupScriptMenuItem(const QString&))); | |
| 3604 | 3308 | |
| 3605 | 3309 | // when the application is about to quit, stop our script engine so it unwinds properly |
| 3606 | 3310 | connect(this, SIGNAL(aboutToQuit()), scriptEngine, SLOT(stop())); |
| @@ -3615,13 +3319,21 @@ void Application::loadScript(const QString& scriptName) { · 8 unmodified lines | ||
| 3615 | 3319 | } |
| 3616 | 3320 | |
| 3617 | 3321 | void Application::loadDialog() { |
| 3618 | − // shut down and stop any existing script | |
| 3619 | − QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); | |
| 3620 | − QString suggestedName = desktopLocation.append("/script.js"); | |
| 3322 | + QString suggestedName; | |
| 3323 | + | |
| 3324 | + if (_previousScriptLocation.isEmpty()) { | |
| 3325 | + QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); | |
| 3326 | + suggestedName = desktopLocation.append("/script.js"); | |
| 3327 | + } else { | |
| 3328 | + suggestedName = _previousScriptLocation; | |
| 3329 | + } | |
| 3621 | 3330 | |
| 3622 | − QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Open Script"), suggestedName, | |
| 3331 | + QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Open Script"), suggestedName, | |
| 3623 | 3332 | tr("JavaScript Files (*.js)")); |
| 3624 | − | |
| 3333 | + if (!fileNameString.isEmpty()) { | |
| 3334 | + _previousScriptLocation = fileNameString; | |
| 3335 | + } | |
| 3336 | + | |
| 3625 | 3337 | loadScript(fileNameString); |
| 3626 | 3338 | } |
| 3627 | 3339 | |
| @@ -3632,7 +3344,7 @@ void Application::loadScriptURLDialog() { · 4 unmodified lines | ||
| 3632 | 3344 | scriptURLDialog.setLabelText("Script:"); |
| 3633 | 3345 | scriptURLDialog.setWindowFlags(Qt::Sheet); |
| 3634 | 3346 | const float DIALOG_RATIO_OF_WINDOW = 0.30f; |
| 3635 | − scriptURLDialog.resize(scriptURLDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, | |
| 3347 | + scriptURLDialog.resize(scriptURLDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, | |
| 3636 | 3348 | scriptURLDialog.size().height()); |
| 3637 | 3349 | |
| 3638 | 3350 | int dialogReturn = scriptURLDialog.exec(); |
| @@ -3670,29 +3382,29 @@ void Application::checkVersion() { · 31 unmodified lines | ||
| 3670 | 3382 | } |
| 3671 | 3383 | |
| 3672 | 3384 | void Application::parseVersionXml() { |
| 3673 | − | |
| 3385 | + | |
| 3674 | 3386 | #ifdef Q_OS_WIN32 |
| 3675 | 3387 | QString operatingSystem("win"); |
| 3676 | 3388 | #endif |
| 3677 | − | |
| 3389 | + | |
| 3678 | 3390 | #ifdef Q_OS_MAC |
| 3679 | 3391 | QString operatingSystem("mac"); |
| 3680 | 3392 | #endif |
| 3681 | − | |
| 3393 | + | |
| 3682 | 3394 | #ifdef Q_OS_LINUX |
| 3683 | 3395 | QString operatingSystem("ubuntu"); |
| 3684 | 3396 | #endif |
| 3685 | − | |
| 3397 | + | |
| 3686 | 3398 | QString releaseDate; |
| 3687 | 3399 | QString releaseNotes; |
| 3688 | 3400 | QString latestVersion; |
| 3689 | 3401 | QUrl downloadUrl; |
| 3690 | 3402 | QObject* sender = QObject::sender(); |
| 3691 | − | |
| 3403 | + | |
| 3692 | 3404 | QXmlStreamReader xml(qobject_cast<QNetworkReply*>(sender)); |
| 3693 | 3405 | while (!xml.atEnd() && !xml.hasError()) { |
| 3694 | 3406 | QXmlStreamReader::TokenType token = xml.readNext(); |
| 3695 | − | |
| 3407 | + | |
| 3696 | 3408 | if (token == QXmlStreamReader::StartElement) { |
| 3697 | 3409 | if (xml.name() == "ReleaseDate") { |
| 3698 | 3410 | xml.readNext(); |