Showing the changes to interface/src/Application.cpp (1 of 122 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,29 +163,28 @@ 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 | // read the ApplicationInfo.ini file for Name/Version/Domain information |
| 175 | 170 | QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat); |
| 176 | − | |
| 171 | + | |
| 177 | 172 | // set the associated application properties |
| 178 | 173 | applicationInfo.beginGroup("INFO"); |
| 179 | − | |
| 174 | + | |
| 180 | 175 | qDebug() << "[VERSION] Build sequence: " << qPrintable(applicationVersion()); |
| 181 | − | |
| 176 | + | |
| 182 | 177 | setApplicationName(applicationInfo.value("name").toString()); |
| 183 | 178 | setApplicationVersion(BUILD_VERSION); |
| 184 | 179 | setOrganizationName(applicationInfo.value("organizationName").toString()); |
| 185 | 180 | setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); |
| 186 | − | |
| 181 | + | |
| 187 | 182 | QSettings::setDefaultFormat(QSettings::IniFormat); |
| 188 | − | |
| 183 | + | |
| 189 | 184 | _myAvatar = _avatarManager.getMyAvatar(); |
| 190 | 185 | |
| 191 | 186 | _applicationStartupTime = startup_time; |
| 192 | − | |
| 187 | + | |
| 193 | 188 | QFontDatabase::addApplicationFont(Application::resourcesPath() + "styles/Inconsolata.otf"); |
| 194 | 189 | _window->setWindowTitle("Interface"); |
| 195 | 190 | |
| @@ -204,19 +199,19 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 8 unmodified lines | ||
| 204 | 199 | if (portStr) { |
| 205 | 200 | listenPort = atoi(portStr); |
| 206 | 201 | } |
| 207 | − | |
| 202 | + | |
| 208 | 203 | // start the nodeThread so its event loop is running |
| 209 | 204 | _nodeThread->start(); |
| 210 | − | |
| 205 | + | |
| 211 | 206 | // make sure the node thread is given highest priority |
| 212 | 207 | _nodeThread->setPriority(QThread::TimeCriticalPriority); |
| 213 | − | |
| 208 | + | |
| 214 | 209 | // put the NodeList and datagram processing on the node thread |
| 215 | 210 | NodeList* nodeList = NodeList::createInstance(NodeType::Agent, listenPort); |
| 216 | − | |
| 211 | + | |
| 217 | 212 | nodeList->moveToThread(_nodeThread); |
| 218 | 213 | _datagramProcessor.moveToThread(_nodeThread); |
| 219 | − | |
| 214 | + | |
| 220 | 215 | // connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal |
| 221 | 216 | connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), &_datagramProcessor, SLOT(processDatagrams())); |
| 222 | 217 | |
| @@ -238,20 +233,20 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 15 unmodified lines | ||
| 238 | 233 | connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer))); |
| 239 | 234 | connect(nodeList, &NodeList::uuidChanged, this, &Application::updateWindowTitle); |
| 240 | 235 | connect(nodeList, &NodeList::limitOfSilentDomainCheckInsReached, nodeList, &NodeList::reset); |
| 241 | − | |
| 236 | + | |
| 242 | 237 | // connect to appropriate slots on AccountManager |
| 243 | 238 | AccountManager& accountManager = AccountManager::getInstance(); |
| 244 | 239 | connect(&accountManager, &AccountManager::authRequired, Menu::getInstance(), &Menu::loginForCurrentDomain); |
| 245 | 240 | connect(&accountManager, &AccountManager::usernameChanged, this, &Application::updateWindowTitle); |
| 246 | − | |
| 241 | + | |
| 247 | 242 | // set the account manager's root URL and trigger a login request if we don't have the access token |
| 248 | 243 | accountManager.setAuthURL(DEFAULT_NODE_AUTH_URL); |
| 249 | − | |
| 244 | + | |
| 250 | 245 | // once the event loop has started, check and signal for an access token |
| 251 | 246 | QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection); |
| 252 | 247 | |
| 253 | 248 | _settings = new QSettings(this); |
| 254 | − | |
| 249 | + | |
| 255 | 250 | // Check to see if the user passed in a command line option for loading a local |
| 256 | 251 | // Voxel File. |
| 257 | 252 | _voxelsFilename = getCmdOption(argc, constArgv, "-i"); |
| @@ -265,7 +260,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 7 unmodified lines | ||
| 265 | 260 | nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer |
| 266 | 261 | << NodeType::VoxelServer << NodeType::ParticleServer |
| 267 | 262 | << NodeType::MetavoxelServer); |
| 268 | − | |
| 263 | + | |
| 269 | 264 | // connect to the packet sent signal of the _voxelEditSender and the _particleEditSender |
| 270 | 265 | connect(&_voxelEditSender, &VoxelEditPacketSender::packetSent, this, &Application::packetSent); |
| 271 | 266 | connect(&_particleEditSender, &ParticleEditPacketSender::packetSent, this, &Application::packetSent); |
| @@ -275,7 +270,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 3 unmodified lines | ||
| 275 | 270 | connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); |
| 276 | 271 | silentNodeTimer->moveToThread(_nodeThread); |
| 277 | 272 | silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); |
| 278 | − | |
| 273 | + | |
| 279 | 274 | // send the identity packet for our avatar each second to our avatar mixer |
| 280 | 275 | QTimer* identityPacketTimer = new QTimer(); |
| 281 | 276 | connect(identityPacketTimer, &QTimer::timeout, _myAvatar, &MyAvatar::sendIdentityPacket); |
| @@ -323,14 +318,18 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 41 unmodified lines | ||
| 323 | 318 | |
| 324 | 319 | // Set the sixense filtering |
| 325 | 320 | _sixenseManager.setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense)); |
| 326 | − | |
| 321 | + | |
| 327 | 322 | checkVersion(); |
| 328 | − | |
| 323 | + | |
| 329 | 324 | _overlays.init(_glWidget); // do this before scripts load |
| 330 | − | |
| 325 | + | |
| 331 | 326 | LocalVoxelsList::getInstance()->addPersistantTree(DOMAIN_TREE_NAME, _voxels.getTree()); |
| 332 | 327 | LocalVoxelsList::getInstance()->addPersistantTree(CLIPBOARD_TREE_NAME, &_clipboard); |
| 333 | 328 | |
| 329 | + _window->addDockWidget(Qt::NoDockWidgetArea, _runningScriptsWidget = new RunningScriptsWidget()); | |
| 330 | + _runningScriptsWidget->setRunningScripts(getRunningScripts()); | |
| 331 | + connect(_runningScriptsWidget, &RunningScriptsWidget::stopScriptName, this, &Application::stopScript); | |
| 332 | + | |
| 334 | 333 | // check first run... |
| 335 | 334 | QVariant firstRunValue = _settings->value("firstRun",QVariant(true)); |
| 336 | 335 | if (firstRunValue.isValid() && firstRunValue.toBool()) { |
| @@ -338,7 +337,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 1 unmodified line | ||
| 338 | 337 | // clear the scripts, and set out script to our default scripts |
| 339 | 338 | clearScriptsBeforeRunning(); |
| 340 | 339 | loadScript("http://public.highfidelity.io/scripts/defaultScripts.js"); |
| 341 | − | |
| 340 | + | |
| 342 | 341 | _settings->setValue("firstRun",QVariant(false)); |
| 343 | 342 | } else { |
| 344 | 343 | // do this as late as possible so that all required subsystems are inialized |
| @@ -349,31 +348,34 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 4 unmodified lines | ||
| 349 | 348 | Application::~Application() { |
| 350 | 349 | |
| 351 | 350 | qInstallMessageHandler(NULL); |
| 352 | − | |
| 351 | + | |
| 353 | 352 | // make sure we don't call the idle timer any more |
| 354 | 353 | delete idleTimer; |
| 355 | − | |
| 354 | + | |
| 356 | 355 | Menu::getInstance()->saveSettings(); |
| 357 | 356 | _rearMirrorTools->saveSettings(_settings); |
| 358 | − | |
| 357 | + | |
| 359 | 358 | _sharedVoxelSystem.changeTree(new VoxelTree); |
| 360 | 359 | if (_voxelImporter) { |
| 361 | 360 | _voxelImporter->saveSettings(_settings); |
| 362 | 361 | delete _voxelImporter; |
| 363 | 362 | } |
| 364 | 363 | _settings->sync(); |
| 365 | − | |
| 364 | + | |
| 366 | 365 | // let the avatar mixer know we're out |
| 367 | 366 | MyAvatar::sendKillAvatar(); |
| 368 | − | |
| 367 | + | |
| 369 | 368 | // ask the datagram processing thread to quit and wait until it is done |
| 370 | 369 | _nodeThread->quit(); |
| 371 | 370 | _nodeThread->wait(); |
| 372 | − | |
| 371 | + | |
| 372 | + // stop the audio process | |
| 373 | + QMetaObject::invokeMethod(&_audio, "stop"); | |
| 374 | + | |
| 373 | 375 | // ask the audio thread to quit and wait until it is done |
| 374 | 376 | _audio.thread()->quit(); |
| 375 | 377 | _audio.thread()->wait(); |
| 376 | − | |
| 378 | + | |
| 377 | 379 | _voxelProcessor.terminate(); |
| 378 | 380 | _voxelHideShowThread.terminate(); |
| 379 | 381 | _voxelEditSender.terminate(); |
| @@ -386,9 +388,9 @@ Application::~Application() { · 6 unmodified lines | ||
| 386 | 388 | Menu::getInstance()->deleteLater(); |
| 387 | 389 | |
| 388 | 390 | _myAvatar = NULL; |
| 389 | − | |
| 391 | + | |
| 390 | 392 | delete _glWidget; |
| 391 | − | |
| 393 | + | |
| 392 | 394 | AccountManager::getInstance().destroy(); |
| 393 | 395 | } |
| 394 | 396 | |
| @@ -508,7 +510,7 @@ void Application::paintGL() { · 113 unmodified lines | ||
| 508 | 510 | _myCamera.setDistance(0.0f); |
| 509 | 511 | _myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing |
| 510 | 512 | _myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition()); |
| 511 | − _myCamera.setTargetRotation(_myAvatar->getHead()->getOrientation()); | |
| 513 | + _myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation()); | |
| 512 | 514 | |
| 513 | 515 | } else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { |
| 514 | 516 | _myCamera.setTightness(0.0f); // In first person, camera follows (untweaked) head exactly without delay |
| @@ -522,10 +524,22 @@ void Application::paintGL() { · 7 unmodified lines | ||
| 522 | 524 | |
| 523 | 525 | } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { |
| 524 | 526 | _myCamera.setTightness(0.0f); |
| 525 | − float headHeight = _myAvatar->getHead()->calculateAverageEyePosition().y - _myAvatar->getPosition().y; | |
| 527 | + glm::vec3 eyePosition = _myAvatar->getHead()->calculateAverageEyePosition(); | |
| 528 | + float headHeight = eyePosition.y - _myAvatar->getPosition().y; | |
| 526 | 529 | _myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _myAvatar->getScale()); |
| 527 | 530 | _myCamera.setTargetPosition(_myAvatar->getPosition() + glm::vec3(0, headHeight, 0)); |
| 528 | 531 | _myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); |
| 532 | + | |
| 533 | + // if the head would intersect the near clip plane, we must push the camera out | |
| 534 | + glm::vec3 relativePosition = glm::inverse(_myCamera.getTargetRotation()) * | |
| 535 | + (eyePosition - _myCamera.getTargetPosition()); | |
| 536 | + const float PUSHBACK_RADIUS = 0.2f; | |
| 537 | + float pushback = relativePosition.z + _myCamera.getNearClip() + | |
| 538 | + _myAvatar->getScale() * PUSHBACK_RADIUS - _myCamera.getDistance(); | |
| 539 | + if (pushback > 0.0f) { | |
| 540 | + _myCamera.setTargetPosition(_myCamera.getTargetPosition() + | |
| 541 | + _myCamera.getTargetRotation() * glm::vec3(0.0f, 0.0f, pushback)); | |
| 542 | + } | |
| 529 | 543 | } |
| 530 | 544 | |
| 531 | 545 | // Update camera position |
| @@ -580,7 +594,7 @@ void Application::paintGL() { · 48 unmodified lines | ||
| 580 | 594 | |
| 581 | 595 | if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { |
| 582 | 596 | renderRearViewMirror(_mirrorViewRect); |
| 583 | − | |
| 597 | + | |
| 584 | 598 | } else if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { |
| 585 | 599 | _rearMirrorTools->render(true); |
| 586 | 600 | } |
| @@ -610,6 +624,14 @@ void Application::resizeGL(int width, int height) { · 23 unmodified lines | ||
| 610 | 624 | |
| 611 | 625 | updateProjectionMatrix(); |
| 612 | 626 | glLoadIdentity(); |
| 627 | + | |
| 628 | + // update Stats width | |
| 629 | + int horizontalOffset = 0; | |
| 630 | + if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { | |
| 631 | + // mirror is enabled, let's set horizontal offset to give stats some margin | |
| 632 | + horizontalOffset += MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; | |
| 633 | + } | |
| 634 | + Stats::getInstance()->resetWidth(width, horizontalOffset); | |
| 613 | 635 | } |
| 614 | 636 | |
| 615 | 637 | void Application::updateProjectionMatrix() { |
| @@ -653,10 +675,10 @@ void Application::controlledBroadcastToNodes(const QByteArray& packet, const Nod · 37 unmodified lines | ||
| 653 | 675 | if (type == NodeType::VoxelServer && !Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { |
| 654 | 676 | continue; |
| 655 | 677 | } |
| 656 | − | |
| 678 | + | |
| 657 | 679 | // Perform the broadcast for one type |
| 658 | 680 | int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(packet, NodeSet() << type); |
| 659 | − | |
| 681 | + | |
| 660 | 682 | // Feed number of bytes to corresponding channel of the bandwidth meter, if any (done otherwise) |
| 661 | 683 | BandwidthMeter::ChannelIndex channel; |
| 662 | 684 | switch (type) { |
| @@ -675,32 +697,15 @@ void Application::controlledBroadcastToNodes(const QByteArray& packet, const Nod · 12 unmodified lines | ||
| 675 | 697 | } |
| 676 | 698 | |
| 677 | 699 | bool Application::event(QEvent* event) { |
| 678 | − | |
| 700 | + | |
| 679 | 701 | // handle custom URL |
| 680 | 702 | if (event->type() == QEvent::FileOpen) { |
| 681 | 703 | QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event); |
| 682 | − if (!fileEvent->url().isEmpty() && fileEvent->url().toLocalFile().startsWith(CUSTOM_URL_SCHEME)) { | |
| 683 | − QString destination = fileEvent->url().toLocalFile().remove(CUSTOM_URL_SCHEME); | |
| 684 | − QStringList urlParts = destination.split('/', QString::SkipEmptyParts); | |
| 685 | − | |
| 686 | − if (urlParts.count() > 1) { | |
| 687 | − // if url has 2 or more parts, the first one is domain name | |
| 688 | − Menu::getInstance()->goToDomain(urlParts[0]); | |
| 689 | − | |
| 690 | − // location coordinates | |
| 691 | − Menu::getInstance()->goToDestination(urlParts[1]); | |
| 692 | − if (urlParts.count() > 2) { | |
| 693 | − | |
| 694 | − // location orientation | |
| 695 | − Menu::getInstance()->goToOrientation(urlParts[2]); | |
| 696 | − } | |
| 697 | − } else if (urlParts.count() == 1) { | |
| 698 | − | |
| 699 | − // location coordinates | |
| 700 | − Menu::getInstance()->goToDestination(urlParts[0]); | |
| 701 | − } | |
| 704 | + bool isHifiSchemeURL = !fileEvent->url().isEmpty() && fileEvent->url().toLocalFile().startsWith(CUSTOM_URL_SCHEME); | |
| 705 | + if (isHifiSchemeURL) { | |
| 706 | + Menu::getInstance()->goTo(fileEvent->url().toString()); | |
| 702 | 707 | } |
| 703 | − | |
| 708 | + | |
| 704 | 709 | return false; |
| 705 | 710 | } |
| 706 | 711 | return QApplication::event(event); |
| @@ -711,7 +716,7 @@ void Application::keyPressEvent(QKeyEvent* event) { · 4 unmodified lines | ||
| 711 | 716 | _keysPressed.insert(event->key()); |
| 712 | 717 | |
| 713 | 718 | _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts |
| 714 | − | |
| 719 | + | |
| 715 | 720 | // if one of our scripts have asked to capture this event, then stop processing it |
| 716 | 721 | if (_controllerScriptingInterface.isKeyCaptured(event)) { |
| 717 | 722 | return; |
| @@ -1069,8 +1074,12 @@ void Application::mouseReleaseEvent(QMouseEvent* event) { · 351 unmodified lines | ||
| 1069 | 1074 | _mousePressed = false; |
| 1070 | 1075 | checkBandwidthMeterClick(); |
| 1071 | 1076 | if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { |
| 1072 | − checkStatsClick(); | |
| 1073 | − } | |
| 1077 | + int horizontalOffset = 0; | |
| 1078 | + if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { | |
| 1079 | + horizontalOffset = MIRROR_VIEW_WIDTH; | |
| 1080 | + } | |
| 1081 | + Stats::getInstance()->checkClick(_mouseX, _mouseY, _mouseDragStartedX, _mouseDragStartedY, horizontalOffset); | |
| 1082 | + } | |
| 1074 | 1083 | } |
| 1075 | 1084 | } |
| 1076 | 1085 | } |
| @@ -1119,7 +1128,7 @@ void Application::touchBeginEvent(QTouchEvent* event) { · 42 unmodified lines | ||
| 1119 | 1128 | if (_controllerScriptingInterface.isTouchCaptured()) { |
| 1120 | 1129 | return; |
| 1121 | 1130 | } |
| 1122 | − | |
| 1131 | + | |
| 1123 | 1132 | // put any application specific touch behavior below here.. |
| 1124 | 1133 | _lastTouchAvgX = _touchAvgX; |
| 1125 | 1134 | _lastTouchAvgY = _touchAvgY; |
| @@ -1162,13 +1171,13 @@ void Application::dropEvent(QDropEvent *event) { · 36 unmodified lines | ||
| 1162 | 1171 | break; |
| 1163 | 1172 | } |
| 1164 | 1173 | } |
| 1165 | − | |
| 1174 | + | |
| 1166 | 1175 | SnapshotMetaData* snapshotData = Snapshot::parseSnapshotData(snapshotPath); |
| 1167 | 1176 | if (snapshotData) { |
| 1168 | 1177 | if (!snapshotData->getDomain().isEmpty()) { |
| 1169 | 1178 | Menu::getInstance()->goToDomain(snapshotData->getDomain()); |
| 1170 | 1179 | } |
| 1171 | − | |
| 1180 | + | |
| 1172 | 1181 | _myAvatar->setPosition(snapshotData->getLocation()); |
| 1173 | 1182 | _myAvatar->setOrientation(snapshotData->getOrientation()); |
| 1174 | 1183 | } else { |
| @@ -1196,19 +1205,19 @@ void Application::timer() { · 21 unmodified lines | ||
| 1196 | 1205 | } |
| 1197 | 1206 | |
| 1198 | 1207 | _fps = (float)_frameCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); |
| 1199 | − | |
| 1208 | + | |
| 1200 | 1209 | _packetsPerSecond = (float) _datagramProcessor.getPacketCount() / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); |
| 1201 | 1210 | _bytesPerSecond = (float) _datagramProcessor.getByteCount() / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); |
| 1202 | 1211 | _frameCount = 0; |
| 1203 | − | |
| 1212 | + | |
| 1204 | 1213 | _datagramProcessor.resetCounters(); |
| 1205 | 1214 | |
| 1206 | 1215 | gettimeofday(&_timerStart, NULL); |
| 1207 | 1216 | |
| 1208 | 1217 | // ask the node list to check in with the domain server |
| 1209 | 1218 | NodeList::getInstance()->sendDomainServerCheckIn(); |
| 1210 | − | |
| 1211 | − | |
| 1219 | + | |
| 1220 | + | |
| 1212 | 1221 | } |
| 1213 | 1222 | |
| 1214 | 1223 | void Application::idle() { |
| @@ -1245,11 +1254,11 @@ void Application::idle() { · 30 unmodified lines | ||
| 1245 | 1254 | _idleLoopMeasuredJitter = _idleLoopStdev.getStDev(); |
| 1246 | 1255 | _idleLoopStdev.reset(); |
| 1247 | 1256 | } |
| 1248 | − | |
| 1257 | + | |
| 1249 | 1258 | if (Menu::getInstance()->isOptionChecked(MenuOption::BuckyBalls)) { |
| 1250 | 1259 | _buckyBalls.simulate(timeSinceLastUpdate / 1000.f, Application::getInstance()->getAvatar()->getHandData()); |
| 1251 | 1260 | } |
| 1252 | − | |
| 1261 | + | |
| 1253 | 1262 | // After finishing all of the above work, restart the idle timer, allowing 2ms to process events. |
| 1254 | 1263 | idleTimer->start(2); |
| 1255 | 1264 | } |
| @@ -1381,7 +1390,7 @@ void Application::exportVoxels(const VoxelDetail& sourceVoxel) { · 125 unmodified lines | ||
| 1381 | 1390 | tr("Sparse Voxel Octree Files (*.svo)")); |
| 1382 | 1391 | QByteArray fileNameAscii = fileNameString.toLocal8Bit(); |
| 1383 | 1392 | const char* fileName = fileNameAscii.data(); |
| 1384 | − | |
| 1393 | + | |
| 1385 | 1394 | VoxelTreeElement* selectedNode = _voxels.getTree()->getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s); |
| 1386 | 1395 | if (selectedNode) { |
| 1387 | 1396 | VoxelTree exportTree; |
| @@ -1395,12 +1404,12 @@ void Application::exportVoxels(const VoxelDetail& sourceVoxel) { · 7 unmodified lines | ||
| 1395 | 1404 | |
| 1396 | 1405 | void Application::importVoxels() { |
| 1397 | 1406 | _importSucceded = false; |
| 1398 | − | |
| 1407 | + | |
| 1399 | 1408 | if (!_voxelImporter) { |
| 1400 | 1409 | _voxelImporter = new VoxelImporter(_window); |
| 1401 | 1410 | _voxelImporter->loadSettings(_settings); |
| 1402 | 1411 | } |
| 1403 | − | |
| 1412 | + | |
| 1404 | 1413 | if (!_voxelImporter->exec()) { |
| 1405 | 1414 | qDebug() << "[DEBUG] Import succeeded." << endl; |
| 1406 | 1415 | _importSucceded = true; |
| @@ -1414,7 +1423,7 @@ void Application::importVoxels() { · 7 unmodified lines | ||
| 1414 | 1423 | |
| 1415 | 1424 | // restore the main window's active state |
| 1416 | 1425 | _window->activateWindow(); |
| 1417 | − | |
| 1426 | + | |
| 1418 | 1427 | emit importDone(); |
| 1419 | 1428 | } |
| 1420 | 1429 | |
| @@ -1470,7 +1479,7 @@ void Application::pasteVoxels(const VoxelDetail& sourceVoxel) { · 49 unmodified lines | ||
| 1470 | 1479 | } |
| 1471 | 1480 | |
| 1472 | 1481 | pasteVoxelsToOctalCode(octalCodeDestination); |
| 1473 | − | |
| 1482 | + | |
| 1474 | 1483 | if (calculatedOctCode) { |
| 1475 | 1484 | delete[] calculatedOctCode; |
| 1476 | 1485 | } |
| @@ -1507,9 +1516,9 @@ void Application::init() { · 30 unmodified lines | ||
| 1507 | 1516 | |
| 1508 | 1517 | // Cleanup of the original shared tree |
| 1509 | 1518 | _sharedVoxelSystem.init(); |
| 1510 | − | |
| 1519 | + | |
| 1511 | 1520 | _voxelImporter = new VoxelImporter(_window); |
| 1512 | − | |
| 1521 | + | |
| 1513 | 1522 | _environment.init(); |
| 1514 | 1523 | |
| 1515 | 1524 | _glowEffect.init(); |
| @@ -1551,11 +1560,11 @@ void Application::init() { · 35 unmodified lines | ||
| 1551 | 1560 | _audio.setJitterBufferSamples(Menu::getInstance()->getAudioJitterBufferSamples()); |
| 1552 | 1561 | } |
| 1553 | 1562 | qDebug("Loaded settings"); |
| 1554 | − | |
| 1563 | + | |
| 1555 | 1564 | // initialize Visage and Faceshift after loading the menu settings |
| 1556 | 1565 | _faceshift.init(); |
| 1557 | 1566 | _visage.init(); |
| 1558 | − | |
| 1567 | + | |
| 1559 | 1568 | // fire off an immediate domain-server check in now that settings are loaded |
| 1560 | 1569 | NodeList::getInstance()->sendDomainServerCheckIn(); |
| 1561 | 1570 | |
| @@ -1574,20 +1583,20 @@ void Application::init() { · 12 unmodified lines | ||
| 1574 | 1583 | _particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio, &_avatarManager); |
| 1575 | 1584 | |
| 1576 | 1585 | // connect the _particleCollisionSystem to our script engine's ParticleScriptingInterface |
| 1577 | − connect(&_particleCollisionSystem, | |
| 1578 | − SIGNAL(particleCollisionWithVoxel(const ParticleID&, const VoxelDetail&, const glm::vec3&)), | |
| 1579 | − ScriptEngine::getParticlesScriptingInterface(), | |
| 1580 | − SLOT(forwardParticleCollisionWithVoxel(const ParticleID&, const VoxelDetail&, const glm::vec3&))); | |
| 1581 | − | |
| 1582 | − connect(&_particleCollisionSystem, | |
| 1583 | − SIGNAL(particleCollisionWithParticle(const ParticleID&, const ParticleID&, const glm::vec3&)), | |
| 1584 | − ScriptEngine::getParticlesScriptingInterface(), | |
| 1585 | − SLOT(forwardParticleCollisionWithParticle(const ParticleID&, const ParticleID&, const glm::vec3&))); | |
| 1586 | − | |
| 1586 | + connect(&_particleCollisionSystem, | |
| 1587 | + SIGNAL(particleCollisionWithVoxel(const ParticleID&, const VoxelDetail&, const CollisionInfo&)), | |
| 1588 | + ScriptEngine::getParticlesScriptingInterface(), | |
| 1589 | + SIGNAL(particleCollisionWithVoxels(const ParticleID&, const VoxelDetail&, const CollisionInfo&))); | |
| 1590 | + | |
| 1591 | + connect(&_particleCollisionSystem, | |
| 1592 | + SIGNAL(particleCollisionWithParticle(const ParticleID&, const ParticleID&, const CollisionInfo&)), | |
| 1593 | + ScriptEngine::getParticlesScriptingInterface(), | |
| 1594 | + SIGNAL(particleCollisionWithParticle(const ParticleID&, const ParticleID&, const CollisionInfo&))); | |
| 1595 | + | |
| 1587 | 1596 | _audio.init(_glWidget); |
| 1588 | 1597 | |
| 1589 | 1598 | _rearMirrorTools = new RearMirrorTools(_glWidget, _mirrorViewRect, _settings); |
| 1590 | − | |
| 1599 | + | |
| 1591 | 1600 | connect(_rearMirrorTools, SIGNAL(closeView()), SLOT(closeMirrorView())); |
| 1592 | 1601 | connect(_rearMirrorTools, SIGNAL(restoreView()), SLOT(restoreMirrorView())); |
| 1593 | 1602 | connect(_rearMirrorTools, SIGNAL(shrinkView()), SLOT(shrinkMirrorView())); |
| @@ -1636,6 +1645,8 @@ void Application::updateLOD() { · 42 unmodified lines | ||
| 1636 | 1645 | // adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode |
| 1637 | 1646 | if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableAutoAdjustLOD) && !isThrottleRendering()) { |
| 1638 | 1647 | Menu::getInstance()->autoAdjustLOD(_fps); |
| 1648 | + } else { | |
| 1649 | + Menu::getInstance()->resetLODAdjust(); | |
| 1639 | 1650 | } |
| 1640 | 1651 | } |
| 1641 | 1652 | |
| @@ -1708,7 +1719,7 @@ void Application::updateMyAvatarLookAtPosition() { · 66 unmodified lines | ||
| 1708 | 1719 | float distance = TREE_SCALE; |
| 1709 | 1720 | if (_myAvatar->getLookAtTargetAvatar() && _myAvatar != _myAvatar->getLookAtTargetAvatar()) { |
| 1710 | 1721 | distance = glm::distance(_mouseRayOrigin, |
| 1711 | − static_cast<Avatar*>(_myAvatar->getLookAtTargetAvatar())->getHead()->calculateAverageEyePosition()); | |
| 1722 | + static_cast<Avatar*>(_myAvatar->getLookAtTargetAvatar())->getHead()->calculateAverageEyePosition()); | |
| 1712 | 1723 | } |
| 1713 | 1724 | const float FIXED_MIN_EYE_DISTANCE = 0.3f; |
| 1714 | 1725 | float minEyeDistance = FIXED_MIN_EYE_DISTANCE + (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON ? 0.0f : |
| @@ -1721,7 +1732,7 @@ void Application::updateMyAvatarLookAtPosition() { · 6 unmodified lines | ||
| 1721 | 1732 | eyePitch = _faceshift.getEstimatedEyePitch(); |
| 1722 | 1733 | eyeYaw = _faceshift.getEstimatedEyeYaw(); |
| 1723 | 1734 | trackerActive = true; |
| 1724 | − | |
| 1735 | + | |
| 1725 | 1736 | } else if (_visage.isActive()) { |
| 1726 | 1737 | eyePitch = _visage.getEstimatedEyePitch(); |
| 1727 | 1738 | eyeYaw = _visage.getEstimatedEyeYaw(); |
| @@ -1842,15 +1853,6 @@ void Application::updateDialogs(float deltaTime) { · 114 unmodified lines | ||
| 1842 | 1853 | } |
| 1843 | 1854 | } |
| 1844 | 1855 | |
| 1845 | −void Application::updateAudio(float deltaTime) { | |
| 1846 | − bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); | |
| 1847 | − PerformanceWarning warn(showWarnings, "Application::updateAudio()"); | |
| 1848 | − | |
| 1849 | − // Update audio stats for procedural sounds | |
| 1850 | − _audio.setLastAcceleration(_myAvatar->getThrust()); | |
| 1851 | − _audio.setLastVelocity(_myAvatar->getVelocity()); | |
| 1852 | −} | |
| 1853 | − | |
| 1854 | 1856 | void Application::updateCursor(float deltaTime) { |
| 1855 | 1857 | bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); |
| 1856 | 1858 | PerformanceWarning warn(showWarnings, "Application::updateCursor()"); |
| @@ -1899,14 +1901,13 @@ void Application::update(float deltaTime) { · 42 unmodified lines | ||
| 1899 | 1901 | updateMetavoxels(deltaTime); // update metavoxels |
| 1900 | 1902 | updateCamera(deltaTime); // handle various camera tweaks like off axis projection |
| 1901 | 1903 | updateDialogs(deltaTime); // update various stats dialogs if present |
| 1902 | − updateAudio(deltaTime); // Update audio stats for procedural sounds | |
| 1903 | 1904 | updateCursor(deltaTime); // Handle cursor updates |
| 1904 | 1905 | |
| 1905 | 1906 | _particles.update(); // update the particles... |
| 1906 | 1907 | _particleCollisionSystem.update(); // collide the particles... |
| 1907 | − | |
| 1908 | + | |
| 1908 | 1909 | _overlays.update(deltaTime); |
| 1909 | − | |
| 1910 | + | |
| 1910 | 1911 | // let external parties know we're updating |
| 1911 | 1912 | emit simulating(deltaTime); |
| 1912 | 1913 | } |
| @@ -1930,7 +1931,7 @@ void Application::updateMyAvatar(float deltaTime) { · 17 unmodified lines | ||
| 1930 | 1931 | // actually need to calculate the view frustum planes to send these details |
| 1931 | 1932 | // to the server. |
| 1932 | 1933 | loadViewFrustum(_myCamera, _viewFrustum); |
| 1933 | − | |
| 1934 | + | |
| 1934 | 1935 | // Update my voxel servers with my current voxel query... |
| 1935 | 1936 | quint64 now = usecTimestampNow(); |
| 1936 | 1937 | quint64 sinceLastQuery = now - _lastQueriedTime; |
| @@ -2187,19 +2188,26 @@ void Application::updateShadowMap() { · 250 unmodified lines | ||
| 2187 | 2188 | (_viewFrustum.getFarClip() - _viewFrustum.getNearClip()); |
| 2188 | 2189 | loadViewFrustum(_myCamera, _viewFrustum); |
| 2189 | 2190 | glm::vec3 points[] = { |
| 2190 | − inverseRotation * (glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), nearScale)), | |
| 2191 | − inverseRotation * (glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), nearScale)), | |
| 2192 | − inverseRotation * (glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), nearScale)), | |
| 2193 | − inverseRotation * (glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), nearScale)), | |
| 2194 | − inverseRotation * (glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), farScale)), | |
| 2195 | − inverseRotation * (glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), farScale)), | |
| 2196 | − inverseRotation * (glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), farScale)), | |
| 2197 | − inverseRotation * (glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), farScale)) }; | |
| 2198 | − glm::vec3 minima(FLT_MAX, FLT_MAX, FLT_MAX), maxima(-FLT_MAX, -FLT_MAX, -FLT_MAX); | |
| 2191 | + glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), nearScale), | |
| 2192 | + glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), nearScale), | |
| 2193 | + glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), nearScale), | |
| 2194 | + glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), nearScale), | |
| 2195 | + glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), farScale), | |
| 2196 | + glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), farScale), | |
| 2197 | + glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), farScale), | |
| 2198 | + glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), farScale) }; | |
| 2199 | + glm::vec3 center; | |
| 2199 | 2200 | for (size_t i = 0; i < sizeof(points) / sizeof(points[0]); i++) { |
| 2200 | − minima = glm::min(minima, points[i]); | |
| 2201 | − maxima = glm::max(maxima, points[i]); | |
| 2201 | + center += points[i]; | |
| 2202 | 2202 | } |
| 2203 | + center /= (float)(sizeof(points) / sizeof(points[0])); | |
| 2204 | + float radius = 0.0f; | |
| 2205 | + for (size_t i = 0; i < sizeof(points) / sizeof(points[0]); i++) { | |
| 2206 | + radius = qMax(radius, glm::distance(points[i], center)); | |
| 2207 | + } | |
| 2208 | + center = inverseRotation * center; | |
| 2209 | + glm::vec3 minima(center.x - radius, center.y - radius, center.z - radius); | |
| 2210 | + glm::vec3 maxima(center.x + radius, center.y + radius, center.z + radius); | |
| 2203 | 2211 | |
| 2204 | 2212 | // stretch out our extents in z so that we get all of the avatars |
| 2205 | 2213 | minima.z -= _viewFrustum.getFarClip() * 0.5f; |
| @@ -2220,7 +2228,7 @@ void Application::updateShadowMap() { · 14 unmodified lines | ||
| 2220 | 2228 | _shadowViewFrustum.setEyeOffsetPosition(glm::vec3()); |
| 2221 | 2229 | _shadowViewFrustum.setEyeOffsetOrientation(glm::quat()); |
| 2222 | 2230 | _shadowViewFrustum.calculate(); |
| 2223 | − | |
| 2231 | + | |
| 2224 | 2232 | glMatrixMode(GL_PROJECTION); |
| 2225 | 2233 | glPushMatrix(); |
| 2226 | 2234 | glLoadIdentity(); |
| @@ -2233,8 +2241,7 @@ void Application::updateShadowMap() { · 6 unmodified lines | ||
| 2233 | 2241 | glRotatef(glm::degrees(glm::angle(inverseRotation)), axis.x, axis.y, axis.z); |
| 2234 | 2242 | |
| 2235 | 2243 | // store view matrix without translation, which we'll use for precision-sensitive objects |
| 2236 | − glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&_untranslatedViewMatrix); | |
| 2237 | − _viewMatrixTranslation = glm::vec3(); | |
| 2244 | + updateUntranslatedViewMatrix(); | |
| 2238 | 2245 | |
| 2239 | 2246 | _avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE); |
| 2240 | 2247 | _particles.render(); |
| @@ -2275,19 +2282,19 @@ void Application::setupWorldLight() { · 34 unmodified lines | ||
| 2275 | 2282 | |
| 2276 | 2283 | QImage Application::renderAvatarBillboard() { |
| 2277 | 2284 | _textureCache.getPrimaryFramebufferObject()->bind(); |
| 2278 | − | |
| 2285 | + | |
| 2279 | 2286 | glDisable(GL_BLEND); |
| 2280 | 2287 | |
| 2281 | 2288 | const int BILLBOARD_SIZE = 64; |
| 2282 | 2289 | renderRearViewMirror(QRect(0, _glWidget->height() - BILLBOARD_SIZE, BILLBOARD_SIZE, BILLBOARD_SIZE), true); |
| 2283 | − | |
| 2290 | + | |
| 2284 | 2291 | QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32); |
| 2285 | 2292 | glReadPixels(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE, GL_BGRA, GL_UNSIGNED_BYTE, image.bits()); |
| 2286 | − | |
| 2293 | + | |
| 2287 | 2294 | glEnable(GL_BLEND); |
| 2288 | − | |
| 2295 | + | |
| 2289 | 2296 | _textureCache.getPrimaryFramebufferObject()->release(); |
| 2290 | − | |
| 2297 | + | |
| 2291 | 2298 | return image; |
| 2292 | 2299 | } |
| 2293 | 2300 | |
| @@ -2319,8 +2326,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { · 25 unmodified lines | ||
| 2319 | 2326 | glRotatef(-glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); |
| 2320 | 2327 | |
| 2321 | 2328 | // store view matrix without translation, which we'll use for precision-sensitive objects |
| 2322 | − glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&_untranslatedViewMatrix); | |
| 2323 | − _viewMatrixTranslation = -whichCamera.getPosition(); | |
| 2329 | + updateUntranslatedViewMatrix(-whichCamera.getPosition()); | |
| 2324 | 2330 | |
| 2325 | 2331 | glTranslatef(_viewMatrixTranslation.x, _viewMatrixTranslation.y, _viewMatrixTranslation.z); |
| 2326 | 2332 | |
| @@ -2388,7 +2394,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { · 61 unmodified lines | ||
| 2388 | 2394 | "Application::displaySide() ... metavoxels..."); |
| 2389 | 2395 | _metavoxels.render(); |
| 2390 | 2396 | } |
| 2391 | − | |
| 2397 | + | |
| 2392 | 2398 | if (Menu::getInstance()->isOptionChecked(MenuOption::BuckyBalls)) { |
| 2393 | 2399 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), |
| 2394 | 2400 | "Application::displaySide() ... bucky balls..."); |
| @@ -2401,7 +2407,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { · 6 unmodified lines | ||
| 2401 | 2407 | "Application::displaySide() ... particles..."); |
| 2402 | 2408 | _particles.render(); |
| 2403 | 2409 | } |
| 2404 | − | |
| 2410 | + | |
| 2405 | 2411 | // render the ambient occlusion effect if enabled |
| 2406 | 2412 | if (Menu::getInstance()->isOptionChecked(MenuOption::AmbientOcclusion)) { |
| 2407 | 2413 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), |
| @@ -2445,12 +2451,17 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { · 37 unmodified lines | ||
| 2445 | 2451 | |
| 2446 | 2452 | // give external parties a change to hook in |
| 2447 | 2453 | emit renderingInWorldInterface(); |
| 2448 | − | |
| 2454 | + | |
| 2449 | 2455 | // render JS/scriptable overlays |
| 2450 | 2456 | _overlays.render3D(); |
| 2451 | 2457 | } |
| 2452 | 2458 | } |
| 2453 | 2459 | |
| 2460 | +void Application::updateUntranslatedViewMatrix(const glm::vec3& viewMatrixTranslation) { | |
| 2461 | + glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&_untranslatedViewMatrix); | |
| 2462 | + _viewMatrixTranslation = viewMatrixTranslation; | |
| 2463 | +} | |
| 2464 | + | |
| 2454 | 2465 | void Application::loadTranslatedViewMatrix(const glm::vec3& translation) { |
| 2455 | 2466 | glLoadMatrixf((const GLfloat*)&_untranslatedViewMatrix); |
| 2456 | 2467 | glTranslatef(translation.x + _viewMatrixTranslation.x, translation.y + _viewMatrixTranslation.y, |
| @@ -2495,14 +2506,111 @@ void Application::displayOverlay() { · 38 unmodified lines | ||
| 2495 | 2506 | } |
| 2496 | 2507 | } |
| 2497 | 2508 | |
| 2509 | + // Audio Scope | |
| 2510 | + const int AUDIO_SCOPE_Y_OFFSET = 135; | |
| 2498 | 2511 | if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { |
| 2499 | − _audio.renderMuteIcon(1, _glWidget->height() - 50); | |
| 2500 | 2512 | if (Menu::getInstance()->isOptionChecked(MenuOption::Oscilloscope)) { |
| 2501 | − int oscilloscopeTop = _glWidget->height() - 135; | |
| 2502 | − _audioScope.render(25, oscilloscopeTop); | |
| 2513 | + int oscilloscopeTop = _glWidget->height() - AUDIO_SCOPE_Y_OFFSET; | |
| 2514 | + _audioScope.render(MIRROR_VIEW_LEFT_PADDING, oscilloscopeTop); | |
| 2503 | 2515 | } |
| 2504 | 2516 | } |
| 2505 | 2517 | |
| 2518 | + // Audio VU Meter and Mute Icon | |
| 2519 | + const int MUTE_ICON_SIZE = 24; | |
| 2520 | + const int AUDIO_METER_INSET = 2; | |
| 2521 | + const int AUDIO_METER_WIDTH = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - AUDIO_METER_INSET; | |
| 2522 | + const int AUDIO_METER_SCALE_WIDTH = AUDIO_METER_WIDTH - 2 * AUDIO_METER_INSET; | |
| 2523 | + const int AUDIO_METER_HEIGHT = 8; | |
| 2524 | + const int AUDIO_METER_Y_GAP = 8; | |
| 2525 | + const int AUDIO_METER_X = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_INSET; | |
| 2526 | + | |
| 2527 | + int audioMeterY; | |
| 2528 | + if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { | |
| 2529 | + audioMeterY = MIRROR_VIEW_HEIGHT + AUDIO_METER_Y_GAP; | |
| 2530 | + } else { | |
| 2531 | + audioMeterY = AUDIO_METER_Y_GAP; | |
| 2532 | + } | |
| 2533 | + _audio.renderMuteIcon(MIRROR_VIEW_LEFT_PADDING, audioMeterY); | |
| 2534 | + | |
| 2535 | + | |
| 2536 | + const float AUDIO_METER_BLUE[] = {0.0, 0.0, 1.0}; | |
| 2537 | + const float AUDIO_METER_GREEN[] = {0.0, 1.0, 0.0}; | |
| 2538 | + const float AUDIO_METER_RED[] = {1.0, 0.0, 0.0}; | |
| 2539 | + const float AUDIO_GREEN_START = 0.25 * AUDIO_METER_SCALE_WIDTH; | |
| 2540 | + const float AUDIO_RED_START = 0.80 * AUDIO_METER_SCALE_WIDTH; | |
| 2541 | + const float CLIPPING_INDICATOR_TIME = 1.0f; | |
| 2542 | + const float AUDIO_METER_AVERAGING = 0.5; | |
| 2543 | + const float LOG2 = log(2.f); | |
| 2544 | + const float METER_LOUDNESS_SCALE = 2.8f / 5.f; | |
| 2545 | + const float LOG2_LOUDNESS_FLOOR = 11.f; | |
| 2546 | + float audioLevel = 0.f; | |
| 2547 | + float loudness = _audio.getLastInputLoudness() + 1.f; | |
| 2548 | + | |
| 2549 | + _trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.f - AUDIO_METER_AVERAGING) * loudness; | |
| 2550 | + float log2loudness = log(_trailingAudioLoudness) / LOG2; | |
| 2551 | + | |
| 2552 | + if (log2loudness <= LOG2_LOUDNESS_FLOOR) { | |
| 2553 | + audioLevel = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE * AUDIO_METER_SCALE_WIDTH; | |
| 2554 | + } else { | |
| 2555 | + audioLevel = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.f)) * METER_LOUDNESS_SCALE * AUDIO_METER_SCALE_WIDTH; | |
| 2556 | + } | |
| 2557 | + if (audioLevel > AUDIO_METER_SCALE_WIDTH) { | |
| 2558 | + audioLevel = AUDIO_METER_SCALE_WIDTH; | |
| 2559 | + } | |
| 2560 | + | |
| 2561 | + bool isClipping = ((_audio.getTimeSinceLastClip() > 0.f) && (_audio.getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)); | |
| 2562 | + | |
| 2563 | + glBegin(GL_QUADS); | |
| 2564 | + if (isClipping) { | |
| 2565 | + glColor3f(1, 0, 0); | |
| 2566 | + } else { | |
| 2567 | + glColor3f(0, 0, 0); | |
| 2568 | + } | |
| 2569 | + // Draw audio meter background Quad | |
| 2570 | + glVertex2i(AUDIO_METER_X, audioMeterY); | |
| 2571 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_WIDTH, audioMeterY); | |
| 2572 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_WIDTH, audioMeterY + AUDIO_METER_HEIGHT); | |
| 2573 | + glVertex2i(AUDIO_METER_X, audioMeterY + AUDIO_METER_HEIGHT); | |
| 2574 | + | |
| 2575 | + if (audioLevel > AUDIO_RED_START) { | |
| 2576 | + if (!isClipping) { | |
| 2577 | + glColor3fv(AUDIO_METER_RED); | |
| 2578 | + } else { | |
| 2579 | + glColor3f(1, 1, 1); | |
| 2580 | + } | |
| 2581 | + // Draw Red Quad | |
| 2582 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_RED_START, audioMeterY + AUDIO_METER_INSET); | |
| 2583 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET); | |
| 2584 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); | |
| 2585 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_RED_START, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); | |
| 2586 | + audioLevel = AUDIO_RED_START; | |
| 2587 | + } | |
| 2588 | + if (audioLevel > AUDIO_GREEN_START) { | |
| 2589 | + if (!isClipping) { | |
| 2590 | + glColor3fv(AUDIO_METER_GREEN); | |
| 2591 | + } else { | |
| 2592 | + glColor3f(1, 1, 1); | |
| 2593 | + } | |
| 2594 | + // Draw Green Quad | |
| 2595 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_GREEN_START, audioMeterY + AUDIO_METER_INSET); | |
| 2596 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET); | |
| 2597 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); | |
| 2598 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_GREEN_START, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); | |
| 2599 | + audioLevel = AUDIO_GREEN_START; | |
| 2600 | + } | |
| 2601 | + // Draw Blue Quad | |
| 2602 | + if (!isClipping) { | |
| 2603 | + glColor3fv(AUDIO_METER_BLUE); | |
| 2604 | + } else { | |
| 2605 | + glColor3f(1, 1, 1); | |
| 2606 | + } | |
| 2607 | + // Draw Blue (low level) quad | |
| 2608 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, audioMeterY + AUDIO_METER_INSET); | |
| 2609 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_INSET); | |
| 2610 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); | |
| 2611 | + glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); | |
| 2612 | + glEnd(); | |
| 2613 | + | |
| 2506 | 2614 | if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse)) { |
| 2507 | 2615 | _myAvatar->renderHeadMouse(); |
| 2508 | 2616 | } |
| @@ -2512,11 +2620,17 @@ void Application::displayOverlay() { · 3 unmodified lines | ||
| 2512 | 2620 | glPointSize(1.0f); |
| 2513 | 2621 | |
| 2514 | 2622 | if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { |
| 2623 | + int horizontalOffset = 0; | |
| 2624 | + if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { | |
| 2625 | + // mirror is enabled, let's set horizontal offset to give stats some margin | |
| 2626 | + horizontalOffset += MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; | |
| 2627 | + } | |
| 2628 | + int voxelPacketsToProcess = _voxelProcessor.packetsToProcessCount(); | |
| 2515 | 2629 | // Onscreen text about position, servers, etc |
| 2516 | − displayStats(); | |
| 2630 | + Stats::getInstance()->display(WHITE_TEXT, horizontalOffset, _fps, _packetsPerSecond, _bytesPerSecond, voxelPacketsToProcess); | |
| 2517 | 2631 | // Bandwidth meter |
| 2518 | 2632 | if (Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth)) { |
| 2519 | − displayStatsBackground(0x33333399, _glWidget->width() - 296, _glWidget->height() - 68, 296, 68); | |
| 2633 | + Stats::drawBackground(0x33333399, _glWidget->width() - 296, _glWidget->height() - 68, 296, 68); | |
| 2520 | 2634 | _bandwidthMeter.render(_glWidget->width(), _glWidget->height()); |
| 2521 | 2635 | } |
| 2522 | 2636 | } |
| @@ -2526,8 +2640,8 @@ void Application::displayOverlay() { · 3 unmodified lines | ||
| 2526 | 2640 | char frameTimer[10]; |
| 2527 | 2641 | quint64 mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5); |
| 2528 | 2642 | sprintf(frameTimer, "%d\n", (int)(mSecsNow % 1000)); |
| 2529 | − int timerBottom = | |
| 2530 | − (Menu::getInstance()->isOptionChecked(MenuOption::Stats) && | |
| 2643 | + int timerBottom = | |
| 2644 | + (Menu::getInstance()->isOptionChecked(MenuOption::Stats) && | |
| 2531 | 2645 | Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth)) |
| 2532 | 2646 | ? 80 : 20; |
| 2533 | 2647 | drawText(_glWidget->width() - 100, _glWidget->height() - timerBottom, 0.30f, 1.0f, 0.f, frameTimer, WHITE_TEXT); |
| @@ -2538,415 +2652,6 @@ void Application::displayOverlay() { · 4 unmodified lines | ||
| 2538 | 2652 | glPopMatrix(); |
| 2539 | 2653 | } |
| 2540 | 2654 | |
| 2541 | −// translucent background box that makes stats more readable | |
| 2542 | −void Application::displayStatsBackground(unsigned int rgba, int x, int y, int width, int height) { | |
| 2543 | − glBegin(GL_QUADS); | |
| 2544 | − glColor4f(((rgba >> 24) & 0xff) / 255.0f, | |
| 2545 | − ((rgba >> 16) & 0xff) / 255.0f, | |
| 2546 | − ((rgba >> 8) & 0xff) / 255.0f, | |
| 2547 | − (rgba & 0xff) / 255.0f); | |
| 2548 | − glVertex3f(x, y, 0); | |
| 2549 | − glVertex3f(x + width, y, 0); | |
| 2550 | − glVertex3f(x + width, y + height, 0); | |
| 2551 | − glVertex3f(x , y + height, 0); | |
| 2552 | − glEnd(); | |
| 2553 | − glColor4f(1, 1, 1, 1); | |
| 2554 | −} | |
| 2555 | − | |
| 2556 | −// display expanded or contracted stats | |
| 2557 | − | |
| 2558 | −void Application::displayStats() { | |
| 2559 | − unsigned int backgroundColor = 0x33333399; | |
| 2560 | − int verticalOffset = 0, horizontalOffset = 0, lines = 0; | |
| 2561 | − bool mirrorEnabled = Menu::getInstance()->isOptionChecked(MenuOption::Mirror); | |
| 2562 | − | |
| 2563 | − QLocale locale(QLocale::English); | |
| 2564 | − std::stringstream voxelStats; | |
| 2565 | − | |
| 2566 | − glPointSize(1.0f); | |
| 2567 | − | |
| 2568 | − // we need to take one avatar out so we don't include ourselves | |
| 2569 | − int totalAvatars = _avatarManager.size() - 1; | |
| 2570 | − int totalServers = NodeList::getInstance()->size(); | |
| 2571 | − | |
| 2572 | − if (mirrorEnabled) { | |
| 2573 | − horizontalOffset += MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2; | |
| 2574 | − } | |
| 2575 | − | |
| 2576 | − lines = _statsExpanded ? 5 : 3; | |
| 2577 | − displayStatsBackground(backgroundColor, horizontalOffset, 0, 165, lines * STATS_PELS_PER_LINE + 10); | |
| 2578 | − horizontalOffset += 5; | |
| 2579 | − | |
| 2580 | − char serverNodes[30]; | |
| 2581 | − sprintf(serverNodes, "Servers: %d", totalServers); | |
| 2582 | − char avatarNodes[30]; | |
| 2583 | − sprintf(avatarNodes, "Avatars: %d", totalAvatars); | |
| 2584 | − char framesPerSecond[30]; | |
| 2585 | − sprintf(framesPerSecond, "Framerate: %3.0f FPS", _fps); | |
| 2586 | − | |
| 2587 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2588 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, serverNodes, WHITE_TEXT); | |
| 2589 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2590 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarNodes, WHITE_TEXT); | |
| 2591 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2592 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, framesPerSecond, WHITE_TEXT); | |
| 2593 | − | |
| 2594 | − if (_statsExpanded) { | |
| 2595 | − char packetsPerSecond[30]; | |
| 2596 | − sprintf(packetsPerSecond, "Pkts/sec: %d", _packetsPerSecond); | |
| 2597 | − char averageMegabitsPerSecond[30]; | |
| 2598 | − sprintf(averageMegabitsPerSecond, "Mbps: %3.2f", (float)_bytesPerSecond * 8.f / 1000000.f); | |
| 2599 | − | |
| 2600 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2601 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, packetsPerSecond, WHITE_TEXT); | |
| 2602 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2603 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, averageMegabitsPerSecond, WHITE_TEXT); | |
| 2604 | − } | |
| 2605 | − | |
| 2606 | − verticalOffset = 0; | |
| 2607 | − horizontalOffset += 161; | |
| 2608 | − | |
| 2609 | − if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { | |
| 2610 | − int pingAudio = 0, pingAvatar = 0, pingVoxel = 0, pingVoxelMax = 0; | |
| 2611 | − | |
| 2612 | − NodeList* nodeList = NodeList::getInstance(); | |
| 2613 | − SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); | |
| 2614 | − SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); | |
| 2615 | − | |
| 2616 | − pingAudio = audioMixerNode ? audioMixerNode->getPingMs() : 0; | |
| 2617 | − pingAvatar = avatarMixerNode ? avatarMixerNode->getPingMs() : 0; | |
| 2618 | − | |
| 2619 | − // Now handle voxel servers, since there could be more than one, we average their ping times | |
| 2620 | − unsigned long totalPingVoxel = 0; | |
| 2621 | − int voxelServerCount = 0; | |
| 2622 | − | |
| 2623 | − foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { | |
| 2624 | − if (node->getType() == NodeType::VoxelServer) { | |
| 2625 | − totalPingVoxel += node->getPingMs(); | |
| 2626 | − voxelServerCount++; | |
| 2627 | − if (pingVoxelMax < node->getPingMs()) { | |
| 2628 | − pingVoxelMax = node->getPingMs(); | |
| 2629 | − } | |
| 2630 | − } | |
| 2631 | − } | |
| 2632 | − | |
| 2633 | − if (voxelServerCount) { | |
| 2634 | − pingVoxel = totalPingVoxel/voxelServerCount; | |
| 2635 | − } | |
| 2636 | − | |
| 2637 | − lines = _statsExpanded ? 4 : 3; | |
| 2638 | − displayStatsBackground(backgroundColor, horizontalOffset, 0, 175, lines * STATS_PELS_PER_LINE + 10); | |
| 2639 | − horizontalOffset += 5; | |
| 2640 | − | |
| 2641 | − char audioJitter[30]; | |
| 2642 | − sprintf(audioJitter, | |
| 2643 | − "Buffer msecs %.1f", | |
| 2644 | − (float) (_audio.getNetworkBufferLengthSamplesPerChannel() + (float) _audio.getJitterBufferSamples()) / | |
| 2645 | − (float)_audio.getNetworkSampleRate() * 1000.f); | |
| 2646 | − drawText(30, _glWidget->height() - 22, 0.10f, 0.f, 2.f, audioJitter, WHITE_TEXT); | |
| 2647 | − | |
| 2648 | − | |
| 2649 | − char audioPing[30]; | |
| 2650 | − sprintf(audioPing, "Audio ping: %d", pingAudio); | |
| 2651 | − | |
| 2652 | − | |
| 2653 | − char avatarPing[30]; | |
| 2654 | − sprintf(avatarPing, "Avatar ping: %d", pingAvatar); | |
| 2655 | − char voxelAvgPing[30]; | |
| 2656 | − sprintf(voxelAvgPing, "Voxel avg ping: %d", pingVoxel); | |
| 2657 | − | |
| 2658 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2659 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, audioPing, WHITE_TEXT); | |
| 2660 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2661 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarPing, WHITE_TEXT); | |
| 2662 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2663 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, voxelAvgPing, WHITE_TEXT); | |
| 2664 | − | |
| 2665 | − if (_statsExpanded) { | |
| 2666 | − char voxelMaxPing[30]; | |
| 2667 | − sprintf(voxelMaxPing, "Voxel max ping: %d", pingVoxelMax); | |
| 2668 | − | |
| 2669 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2670 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, voxelMaxPing, WHITE_TEXT); | |
| 2671 | − } | |
| 2672 | − | |
| 2673 | − verticalOffset = 0; | |
| 2674 | − horizontalOffset += 171; | |
| 2675 | − } | |
| 2676 | − | |
| 2677 | − glm::vec3 avatarPos = _myAvatar->getPosition(); | |
| 2678 | − | |
| 2679 | − lines = _statsExpanded ? 5 : 3; | |
| 2680 | − displayStatsBackground(backgroundColor, horizontalOffset, 0, _glWidget->width() - (mirrorEnabled ? 301 : 411) - horizontalOffset, lines * STATS_PELS_PER_LINE + 10); | |
| 2681 | − horizontalOffset += 5; | |
| 2682 | − | |
| 2683 | − char avatarPosition[200]; | |
| 2684 | − if (mirrorEnabled) { | |
| 2685 | − // shorthand formatting | |
| 2686 | − sprintf(avatarPosition, "Pos: %.0f,%.0f,%.0f", avatarPos.x, avatarPos.y, avatarPos.z); | |
| 2687 | − } else { | |
| 2688 | − // longhand way | |
| 2689 | − sprintf(avatarPosition, "Position: %.1f, %.1f, %.1f", avatarPos.x, avatarPos.y, avatarPos.z); | |
| 2690 | − } | |
| 2691 | − char avatarVelocity[30]; | |
| 2692 | − sprintf(avatarVelocity, "Velocity: %.1f", glm::length(_myAvatar->getVelocity())); | |
| 2693 | − char avatarBodyYaw[30]; | |
| 2694 | − sprintf(avatarBodyYaw, "Yaw: %.1f", _myAvatar->getBodyYaw()); | |
| 2695 | − char avatarMixerStats[200]; | |
| 2696 | − | |
| 2697 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2698 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarPosition, WHITE_TEXT); | |
| 2699 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2700 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarVelocity, WHITE_TEXT); | |
| 2701 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2702 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarBodyYaw, WHITE_TEXT); | |
| 2703 | − | |
| 2704 | − if (_statsExpanded) { | |
| 2705 | − SharedNodePointer avatarMixer = NodeList::getInstance()->soloNodeOfType(NodeType::AvatarMixer); | |
| 2706 | − if (avatarMixer) { | |
| 2707 | − sprintf(avatarMixerStats, "Avatar Mixer: %.f kbps, %.f pps", | |
| 2708 | − roundf(avatarMixer->getAverageKilobitsPerSecond()), | |
| 2709 | − roundf(avatarMixer->getAveragePacketsPerSecond())); | |
| 2710 | − } else { | |
| 2711 | − sprintf(avatarMixerStats, "No Avatar Mixer"); | |
| 2712 | − } | |
| 2713 | − | |
| 2714 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2715 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarMixerStats, WHITE_TEXT); | |
| 2716 | − | |
| 2717 | − stringstream downloadStats; | |
| 2718 | − downloadStats << "Downloads: "; | |
| 2719 | − foreach (Resource* resource, ResourceCache::getLoadingRequests()) { | |
| 2720 | − const float MAXIMUM_PERCENTAGE = 100.0f; | |
| 2721 | − downloadStats << roundf(resource->getProgress() * MAXIMUM_PERCENTAGE) << "% "; | |
| 2722 | − } | |
| 2723 | − downloadStats << "(" << ResourceCache::getPendingRequestCount() << " pending)"; | |
| 2724 | − | |
| 2725 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2726 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, downloadStats.str().c_str(), WHITE_TEXT); | |
| 2727 | − } | |
| 2728 | − | |
| 2729 | − verticalOffset = 0; | |
| 2730 | − horizontalOffset = _glWidget->width() - (mirrorEnabled ? 300 : 410); | |
| 2731 | − | |
| 2732 | − lines = _statsExpanded ? 12 : 3; | |
| 2733 | − displayStatsBackground(backgroundColor, horizontalOffset, 0, _glWidget->width() - horizontalOffset, lines * STATS_PELS_PER_LINE + 10); | |
| 2734 | − horizontalOffset += 5; | |
| 2735 | − | |
| 2736 | − if (_statsExpanded) { | |
| 2737 | − // Local Voxel Memory Usage | |
| 2738 | − voxelStats.str(""); | |
| 2739 | − voxelStats << "Voxels Memory Nodes: " << VoxelTreeElement::getTotalMemoryUsage() / 1000000.f << "MB"; | |
| 2740 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2741 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2742 | − | |
| 2743 | − voxelStats.str(""); | |
| 2744 | − voxelStats << | |
| 2745 | − "Geometry RAM: " << _voxels.getVoxelMemoryUsageRAM() / 1000000.f << "MB / " << | |
| 2746 | − "VBO: " << _voxels.getVoxelMemoryUsageVBO() / 1000000.f << "MB"; | |
| 2747 | − if (_voxels.hasVoxelMemoryUsageGPU()) { | |
| 2748 | − voxelStats << " / GPU: " << _voxels.getVoxelMemoryUsageGPU() / 1000000.f << "MB"; | |
| 2749 | − } | |
| 2750 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2751 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2752 | − | |
| 2753 | − // Voxel Rendering | |
| 2754 | − voxelStats.str(""); | |
| 2755 | − voxelStats.precision(4); | |
| 2756 | − voxelStats << "Voxel Rendering Slots Max: " << _voxels.getMaxVoxels() / 1000.f << "K"; | |
| 2757 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2758 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2759 | − } | |
| 2760 | − | |
| 2761 | − voxelStats.str(""); | |
| 2762 | − voxelStats.precision(4); | |
| 2763 | − voxelStats << "Drawn: " << _voxels.getVoxelsWritten() / 1000.f << "K " << | |
| 2764 | − "Abandoned: " << _voxels.getAbandonedVoxels() / 1000.f << "K "; | |
| 2765 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2766 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2767 | − | |
| 2768 | − // iterate all the current voxel stats, and list their sending modes, and total voxel counts | |
| 2769 | − std::stringstream sendingMode(""); | |
| 2770 | − sendingMode << "Octree Sending Mode: ["; | |
| 2771 | − int serverCount = 0; | |
| 2772 | − int movingServerCount = 0; | |
| 2773 | − unsigned long totalNodes = 0; | |
| 2774 | − unsigned long totalInternal = 0; | |
| 2775 | − unsigned long totalLeaves = 0; | |
| 2776 | − for(NodeToOctreeSceneStatsIterator i = _octreeServerSceneStats.begin(); i != _octreeServerSceneStats.end(); i++) { | |
| 2777 | − //const QUuid& uuid = i->first; | |
| 2778 | − OctreeSceneStats& stats = i->second; | |
| 2779 | − serverCount++; | |
| 2780 | − if (_statsExpanded) { | |
| 2781 | − if (serverCount > 1) { | |
| 2782 | − sendingMode << ","; | |
| 2783 | − } | |
| 2784 | − if (stats.isMoving()) { | |
| 2785 | − sendingMode << "M"; | |
| 2786 | − movingServerCount++; | |
| 2787 | − } else { | |
| 2788 | − sendingMode << "S"; | |
| 2789 | − } | |
| 2790 | − } | |
| 2791 | − | |
| 2792 | − // calculate server node totals | |
| 2793 | − totalNodes += stats.getTotalElements(); | |
| 2794 | − if (_statsExpanded) { | |
| 2795 | − totalInternal += stats.getTotalInternal(); | |
| 2796 | − totalLeaves += stats.getTotalLeaves(); | |
| 2797 | − } | |
| 2798 | − } | |
| 2799 | − if (_statsExpanded) { | |
| 2800 | − if (serverCount == 0) { | |
| 2801 | − sendingMode << "---"; | |
| 2802 | − } | |
| 2803 | − sendingMode << "] " << serverCount << " servers"; | |
| 2804 | − if (movingServerCount > 0) { | |
| 2805 | − sendingMode << " <SCENE NOT STABLE>"; | |
| 2806 | − } else { | |
| 2807 | − sendingMode << " <SCENE STABLE>"; | |
| 2808 | − } | |
| 2809 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2810 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)sendingMode.str().c_str(), WHITE_TEXT); | |
| 2811 | − } | |
| 2812 | − | |
| 2813 | − // Incoming packets | |
| 2814 | − int voxelPacketsToProcess = _voxelProcessor.packetsToProcessCount(); | |
| 2815 | − if (_statsExpanded) { | |
| 2816 | − voxelStats.str(""); | |
| 2817 | − QString packetsString = locale.toString((int)voxelPacketsToProcess); | |
| 2818 | − QString maxString = locale.toString((int)_recentMaxPackets); | |
| 2819 | − voxelStats << "Voxel Packets to Process: " << qPrintable(packetsString) | |
| 2820 | − << " [Recent Max: " << qPrintable(maxString) << "]"; | |
| 2821 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2822 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2823 | − } | |
| 2824 | − | |
| 2825 | − if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) { | |
| 2826 | − _recentMaxPackets = 0; | |
| 2827 | − _resetRecentMaxPacketsSoon = false; | |
| 2828 | − } | |
| 2829 | − if (voxelPacketsToProcess == 0) { | |
| 2830 | − _resetRecentMaxPacketsSoon = true; | |
| 2831 | − } else { | |
| 2832 | − if (voxelPacketsToProcess > _recentMaxPackets) { | |
| 2833 | − _recentMaxPackets = voxelPacketsToProcess; | |
| 2834 | − } | |
| 2835 | − } | |
| 2836 | − | |
| 2837 | − verticalOffset += (_statsExpanded ? STATS_PELS_PER_LINE : 0); | |
| 2838 | − | |
| 2839 | − QString serversTotalString = locale.toString((uint)totalNodes); // consider adding: .rightJustified(10, ' '); | |
| 2840 | − | |
| 2841 | − // Server Voxels | |
| 2842 | − voxelStats.str(""); | |
| 2843 | − voxelStats << "Server voxels: " << qPrintable(serversTotalString); | |
| 2844 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2845 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2846 | − | |
| 2847 | − if (_statsExpanded) { | |
| 2848 | − QString serversInternalString = locale.toString((uint)totalInternal); | |
| 2849 | − QString serversLeavesString = locale.toString((uint)totalLeaves); | |
| 2850 | − | |
| 2851 | − voxelStats.str(""); | |
| 2852 | − voxelStats << | |
| 2853 | − "Internal: " << qPrintable(serversInternalString) << " " << | |
| 2854 | − "Leaves: " << qPrintable(serversLeavesString) << ""; | |
| 2855 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2856 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2857 | − } | |
| 2858 | − | |
| 2859 | − unsigned long localTotal = VoxelTreeElement::getNodeCount(); | |
| 2860 | − QString localTotalString = locale.toString((uint)localTotal); // consider adding: .rightJustified(10, ' '); | |
| 2861 | − | |
| 2862 | − // Local Voxels | |
| 2863 | − voxelStats.str(""); | |
| 2864 | − voxelStats << "Local voxels: " << qPrintable(localTotalString); | |
| 2865 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2866 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2867 | − | |
| 2868 | − if (_statsExpanded) { | |
| 2869 | − unsigned long localInternal = VoxelTreeElement::getInternalNodeCount(); | |
| 2870 | − unsigned long localLeaves = VoxelTreeElement::getLeafNodeCount(); | |
| 2871 | − QString localInternalString = locale.toString((uint)localInternal); | |
| 2872 | − QString localLeavesString = locale.toString((uint)localLeaves); | |
| 2873 | − | |
| 2874 | − voxelStats.str(""); | |
| 2875 | − voxelStats << | |
| 2876 | − "Internal: " << qPrintable(localInternalString) << " " << | |
| 2877 | − "Leaves: " << qPrintable(localLeavesString) << ""; | |
| 2878 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2879 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2880 | − } | |
| 2881 | − | |
| 2882 | − // LOD Details | |
| 2883 | − if (_statsExpanded) { | |
| 2884 | − voxelStats.str(""); | |
| 2885 | − QString displayLODDetails = Menu::getInstance()->getLODFeedbackText(); | |
| 2886 | − voxelStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed()); | |
| 2887 | − verticalOffset += STATS_PELS_PER_LINE; | |
| 2888 | − drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT); | |
| 2889 | − } | |
| 2890 | −} | |
| 2891 | − | |
| 2892 | −// called on mouse click release | |
| 2893 | −// check for clicks over stats in order to expand or contract them | |
| 2894 | −void Application::checkStatsClick() { | |
| 2895 | − if (0 != glm::compMax(glm::abs(glm::ivec2(_mouseX - _mouseDragStartedX, _mouseY - _mouseDragStartedY)))) { | |
| 2896 | − // not worried about dragging on stats | |
| 2897 | − return; | |
| 2898 | − } | |
| 2899 | − | |
| 2900 | − int statsHeight = 0, statsWidth = 0, statsX = 0, statsY = 0, lines = 0; | |
| 2901 | − | |
| 2902 | − if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { | |
| 2903 | − statsX += MIRROR_VIEW_WIDTH; | |
| 2904 | − } | |
| 2905 | − | |
| 2906 | − // top-left stats click | |
| 2907 | − lines = _statsExpanded ? 5 : 3; | |
| 2908 | − statsHeight = lines * STATS_PELS_PER_LINE + 10; | |
| 2909 | − statsWidth = 165; | |
| 2910 | − if (_mouseX > statsX && _mouseX < statsX + statsWidth && _mouseY > statsY && _mouseY < statsY + statsHeight) { | |
| 2911 | − toggleStatsExpanded(); | |
| 2912 | − return; | |
| 2913 | − } | |
| 2914 | − | |
| 2915 | − // ping stats click | |
| 2916 | − lines = _statsExpanded ? 4 : 3; | |
| 2917 | − statsX += statsWidth; | |
| 2918 | − statsHeight = lines * STATS_PELS_PER_LINE + 10; | |
| 2919 | − statsWidth = 175; | |
| 2920 | − if (_mouseX > statsX && _mouseX < statsX + statsWidth && _mouseY > statsY && _mouseY < statsY + statsHeight) { | |
| 2921 | − toggleStatsExpanded(); | |
| 2922 | − return; | |
| 2923 | − } | |
| 2924 | − | |
| 2925 | − // top-center stats panel click | |
| 2926 | − lines = _statsExpanded ? 4 : 3; | |
| 2927 | − statsX += statsWidth; | |
| 2928 | − statsHeight = lines * STATS_PELS_PER_LINE + 10; | |
| 2929 | − statsWidth = _glWidget->width() - 411 - statsX; | |
| 2930 | − if (_mouseX > statsX && _mouseX < statsX + statsWidth && _mouseY > statsY && _mouseY < statsY + statsHeight) { | |
| 2931 | − toggleStatsExpanded(); | |
| 2932 | − return; | |
| 2933 | − } | |
| 2934 | − | |
| 2935 | − // top-right stats click | |
| 2936 | − lines = _statsExpanded ? 11 : 3; | |
| 2937 | − statsX = _glWidget->width() - 410; | |
| 2938 | − statsHeight = lines * STATS_PELS_PER_LINE + 10; | |
| 2939 | − statsWidth = _glWidget->width() - statsX; | |
| 2940 | − if (_mouseX > statsX && _mouseX < statsX + statsWidth && _mouseY > statsY && _mouseY < statsY + statsHeight) { | |
| 2941 | − toggleStatsExpanded(); | |
| 2942 | − return; | |
| 2943 | − } | |
| 2944 | −} | |
| 2945 | − | |
| 2946 | −void Application::toggleStatsExpanded() { | |
| 2947 | − _statsExpanded = !_statsExpanded; | |
| 2948 | −} | |
| 2949 | − | |
| 2950 | 2655 | glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) { |
| 2951 | 2656 | float horizontalScale = _glWidget->width() / 2.0f; |
| 2952 | 2657 | float verticalScale = _glWidget->height() / 2.0f; |
| @@ -2979,12 +2684,12 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { · 26 unmodified lines | ||
| 2979 | 2684 | _mirrorCamera.setFieldOfView(BILLBOARD_FIELD_OF_VIEW); // degees |
| 2980 | 2685 | _mirrorCamera.setDistance(BILLBOARD_DISTANCE * _myAvatar->getScale()); |
| 2981 | 2686 | _mirrorCamera.setTargetPosition(_myAvatar->getPosition()); |
| 2982 | − | |
| 2687 | + | |
| 2983 | 2688 | } else if (_rearMirrorTools->getZoomLevel() == BODY) { |
| 2984 | 2689 | _mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees |
| 2985 | 2690 | _mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); |
| 2986 | 2691 | _mirrorCamera.setTargetPosition(_myAvatar->getChestPosition()); |
| 2987 | − | |
| 2692 | + | |
| 2988 | 2693 | } else { // HEAD zoom level |
| 2989 | 2694 | _mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees |
| 2990 | 2695 | _mirrorCamera.setDistance(MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); |
| @@ -2999,7 +2704,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { · 8 unmodified lines | ||
| 2999 | 2704 | } |
| 3000 | 2705 | } |
| 3001 | 2706 | _mirrorCamera.setAspectRatio((float)region.width() / region.height()); |
| 3002 | − | |
| 2707 | + | |
| 3003 | 2708 | _mirrorCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); |
| 3004 | 2709 | _mirrorCamera.update(1.0f/_fps); |
| 3005 | 2710 | |
| @@ -3044,7 +2749,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { · 38 unmodified lines | ||
| 3044 | 2749 | if (!billboard) { |
| 3045 | 2750 | _rearMirrorTools->render(false); |
| 3046 | 2751 | } |
| 3047 | − | |
| 2752 | + | |
| 3048 | 2753 | // reset Viewport and projection matrix |
| 3049 | 2754 | glViewport(0, 0, _glWidget->width(), _glWidget->height()); |
| 3050 | 2755 | glDisable(GL_SCISSOR_TEST); |
| @@ -3264,14 +2969,14 @@ void Application::setMenuShortcutsEnabled(bool enabled) { · 213 unmodified lines | ||
| 3264 | 2969 | } |
| 3265 | 2970 | |
| 3266 | 2971 | void Application::updateWindowTitle(){ |
| 3267 | − | |
| 2972 | + | |
| 3268 | 2973 | QString buildVersion = " (build " + applicationVersion() + ")"; |
| 3269 | 2974 | NodeList* nodeList = NodeList::getInstance(); |
| 3270 | − | |
| 2975 | + | |
| 3271 | 2976 | QString username = AccountManager::getInstance().getUsername(); |
| 3272 | 2977 | QString title = QString() + (!username.isEmpty() ? username + " " : QString()) + nodeList->getSessionUUID().toString() |
| 3273 | 2978 | + " @ " + nodeList->getDomainInfo().getHostname() + buildVersion; |
| 3274 | − | |
| 2979 | + | |
| 3275 | 2980 | qDebug("Application title set to: %s", title.toStdString().c_str()); |
| 3276 | 2981 | _window->setWindowTitle(title); |
| 3277 | 2982 | } |
| @@ -3286,7 +2991,7 @@ void Application::domainChanged(const QString& domainHostname) { · 8 unmodified lines | ||
| 3286 | 2991 | _voxelServerJurisdictions.clear(); |
| 3287 | 2992 | _octreeServerSceneStats.clear(); |
| 3288 | 2993 | _particleServerJurisdictions.clear(); |
| 3289 | − | |
| 2994 | + | |
| 3290 | 2995 | // reset the particle renderer |
| 3291 | 2996 | _particles.clear(); |
| 3292 | 2997 | |
| @@ -3296,12 +3001,12 @@ void Application::domainChanged(const QString& domainHostname) { · 3 unmodified lines | ||
| 3296 | 3001 | |
| 3297 | 3002 | void Application::connectedToDomain(const QString& hostname) { |
| 3298 | 3003 | AccountManager& accountManager = AccountManager::getInstance(); |
| 3299 | − | |
| 3004 | + | |
| 3300 | 3005 | if (accountManager.isLoggedIn()) { |
| 3301 | 3006 | // update our domain-server with the data-server we're logged in with |
| 3302 | − | |
| 3007 | + | |
| 3303 | 3008 | QString domainPutJsonString = "{\"address\":{\"domain\":\"" + hostname + "\"}}"; |
| 3304 | − | |
| 3009 | + | |
| 3305 | 3010 | accountManager.authenticatedRequest("/api/v1/users/address", QNetworkAccessManager::PutOperation, |
| 3306 | 3011 | JSONCallbackParameters(), domainPutJsonString.toUtf8()); |
| 3307 | 3012 | } |
| @@ -3466,13 +3171,13 @@ void Application::loadScripts() { · 158 unmodified lines | ||
| 3466 | 3171 | // loads all saved scripts |
| 3467 | 3172 | QSettings* settings = new QSettings(this); |
| 3468 | 3173 | int size = settings->beginReadArray("Settings"); |
| 3469 | − | |
| 3174 | + | |
| 3470 | 3175 | for (int i = 0; i < size; ++i){ |
| 3471 | 3176 | settings->setArrayIndex(i); |
| 3472 | 3177 | QString string = settings->value("script").toString(); |
| 3473 | 3178 | loadScript(string); |
| 3474 | 3179 | } |
| 3475 | − | |
| 3180 | + | |
| 3476 | 3181 | settings->endArray(); |
| 3477 | 3182 | } |
| 3478 | 3183 | |
| @@ -3487,80 +3192,110 @@ void Application::saveScripts() { · 8 unmodified lines | ||
| 3487 | 3192 | // saves all current running scripts |
| 3488 | 3193 | QSettings* settings = new QSettings(this); |
| 3489 | 3194 | settings->beginWriteArray("Settings"); |
| 3490 | − for (int i = 0; i < _activeScripts.size(); ++i){ | |
| 3195 | + for (int i = 0; i < getRunningScripts().size(); ++i){ | |
| 3491 | 3196 | settings->setArrayIndex(i); |
| 3492 | − settings->setValue("script", _activeScripts.at(i)); | |
| 3197 | + settings->setValue("script", getRunningScripts().at(i)); | |
| 3493 | 3198 | } |
| 3494 | − | |
| 3199 | + | |
| 3495 | 3200 | settings->endArray(); |
| 3496 | 3201 | } |
| 3497 | 3202 | |
| 3498 | 3203 | void Application::stopAllScripts() { |
| 3499 | 3204 | // stops all current running scripts |
| 3500 | − QList<QAction*> scriptActions = Menu::getInstance()->getActiveScriptsMenu()->actions(); | |
| 3501 | − foreach (QAction* scriptAction, scriptActions) { | |
| 3502 | − scriptAction->activate(QAction::Trigger); | |
| 3503 | − qDebug() << "stopping script..." << scriptAction->text(); | |
| 3205 | + for (int i = 0; i < _scriptEnginesHash.size(); ++i) { | |
| 3206 | + _scriptEnginesHash.values().at(i)->stop(); | |
| 3207 | + qDebug() << "stopping script..." << getRunningScripts().at(i); | |
| 3504 | 3208 | } |
| 3505 | − _activeScripts.clear(); | |
| 3209 | + _scriptEnginesHash.clear(); | |
| 3210 | + _runningScriptsWidget->setRunningScripts(getRunningScripts()); | |
| 3211 | +} | |
| 3212 | + | |
| 3213 | +void Application::stopScript(const QString &scriptName) | |
| 3214 | +{ | |
| 3215 | + _scriptEnginesHash.value(scriptName)->stop(); | |
| 3216 | + qDebug() << "stopping script..." << scriptName; | |
| 3217 | + _scriptEnginesHash.remove(scriptName); | |
| 3218 | + _runningScriptsWidget->setRunningScripts(getRunningScripts()); | |
| 3506 | 3219 | } |
| 3507 | 3220 | |
| 3508 | 3221 | void Application::reloadAllScripts() { |
| 3509 | 3222 | // remember all the current scripts so we can reload them |
| 3510 | − QStringList reloadList = _activeScripts; | |
| 3223 | + QStringList reloadList = getRunningScripts(); | |
| 3511 | 3224 | // reloads all current running scripts |
| 3512 | − QList<QAction*> scriptActions = Menu::getInstance()->getActiveScriptsMenu()->actions(); | |
| 3513 | − foreach (QAction* scriptAction, scriptActions) { | |
| 3514 | − scriptAction->activate(QAction::Trigger); | |
| 3515 | − qDebug() << "stopping script..." << scriptAction->text(); | |
| 3516 | − } | |
| 3225 | + stopAllScripts(); | |
| 3517 | 3226 | |
| 3518 | − // NOTE: we don't need to clear the _activeScripts list because that is handled on script shutdown. | |
| 3519 | − | |
| 3520 | 3227 | foreach (QString scriptName, reloadList){ |
| 3521 | 3228 | qDebug() << "reloading script..." << scriptName; |
| 3522 | 3229 | loadScript(scriptName); |
| 3523 | 3230 | } |
| 3524 | 3231 | } |
| 3525 | 3232 | |
| 3526 | −void Application::uploadFST() { | |
| 3527 | − FstReader reader; | |
| 3528 | − if (reader.zip()) { | |
| 3529 | − reader.send(); | |
| 3233 | +void Application::toggleRunningScriptsWidget() | |
| 3234 | +{ | |
| 3235 | + if (!_runningScriptsWidget->toggleViewAction()->isChecked()) { | |
| 3236 | + _runningScriptsWidget->move(_window->geometry().topLeft().x(), _window->geometry().topLeft().y()); | |
| 3237 | + _runningScriptsWidget->resize(0, _window->height()); | |
| 3238 | + _runningScriptsWidget->toggleViewAction()->trigger(); | |
| 3239 | + _runningScriptsWidget->grabKeyboard(); | |
| 3240 | + | |
| 3241 | + QPropertyAnimation* slideAnimation = new QPropertyAnimation(_runningScriptsWidget, "geometry", _runningScriptsWidget); | |
| 3242 | + slideAnimation->setStartValue(_runningScriptsWidget->geometry()); | |
| 3243 | + slideAnimation->setEndValue(QRect(_window->geometry().topLeft().x(), _window->geometry().topLeft().y(), | |
| 3244 | + 310, _runningScriptsWidget->height())); | |
| 3245 | + slideAnimation->setDuration(250); | |
| 3246 | + slideAnimation->start(QAbstractAnimation::DeleteWhenStopped); | |
| 3247 | + } else { | |
| 3248 | + _runningScriptsWidget->releaseKeyboard(); | |
| 3249 | + | |
| 3250 | + QPropertyAnimation* slideAnimation = new QPropertyAnimation(_runningScriptsWidget, "geometry", _runningScriptsWidget); | |
| 3251 | + slideAnimation->setStartValue(_runningScriptsWidget->geometry()); | |
| 3252 | + slideAnimation->setEndValue(QRect(_window->geometry().topLeft().x(), _window->geometry().topLeft().y(), | |
| 3253 | + 0, _runningScriptsWidget->height())); | |
| 3254 | + slideAnimation->setDuration(250); | |
| 3255 | + slideAnimation->start(QAbstractAnimation::DeleteWhenStopped); | |
| 3256 | + | |
| 3257 | + QTimer::singleShot(260, _runningScriptsWidget->toggleViewAction(), SLOT(trigger())); | |
| 3530 | 3258 | } |
| 3531 | 3259 | } |
| 3532 | 3260 | |
| 3533 | −void Application::removeScriptName(const QString& fileNameString) { | |
| 3534 | − _activeScripts.removeOne(fileNameString); | |
| 3261 | +void Application::uploadFST(bool isHead) { | |
| 3262 | + ModelUploader* uploader = new ModelUploader(isHead); | |
| 3263 | + QThread* thread = new QThread(); | |
| 3264 | + thread->connect(uploader, SIGNAL(destroyed()), SLOT(quit())); | |
| 3265 | + thread->connect(thread, SIGNAL(finished()), SLOT(deleteLater())); | |
| 3266 | + uploader->connect(thread, SIGNAL(started()), SLOT(send())); | |
| 3267 | + | |
| 3268 | + thread->start(); | |
| 3269 | +} | |
| 3270 | + | |
| 3271 | +void Application::uploadHead() { | |
| 3272 | + uploadFST(true); | |
| 3535 | 3273 | } |
| 3536 | 3274 | |
| 3537 | −void Application::cleanupScriptMenuItem(const QString& scriptMenuName) { | |
| 3538 | − Menu::getInstance()->removeAction(Menu::getInstance()->getActiveScriptsMenu(), scriptMenuName); | |
| 3275 | +void Application::uploadSkeleton() { | |
| 3276 | + uploadFST(false); | |
| 3539 | 3277 | } |
| 3540 | 3278 | |
| 3541 | 3279 | void Application::loadScript(const QString& scriptName) { |
| 3542 | 3280 | |
| 3543 | 3281 | // start the script on a new thread... |
| 3544 | − bool wantMenuItems = true; // tells the ScriptEngine object to add menu items for itself | |
| 3545 | − ScriptEngine* scriptEngine = new ScriptEngine(QUrl(scriptName), wantMenuItems, &_controllerScriptingInterface); | |
| 3282 | + ScriptEngine* scriptEngine = new ScriptEngine(QUrl(scriptName), &_controllerScriptingInterface); | |
| 3283 | + _scriptEnginesHash.insert(scriptName, scriptEngine); | |
| 3546 | 3284 | |
| 3547 | 3285 | if (!scriptEngine->hasScript()) { |
| 3548 | 3286 | qDebug() << "Application::loadScript(), script failed to load..."; |
| 3549 | 3287 | return; |
| 3550 | 3288 | } |
| 3551 | − _activeScripts.append(scriptName); | |
| 3552 | − | |
| 3553 | − // add a stop menu item | |
| 3554 | − Menu::getInstance()->addActionToQMenuAndActionHash(Menu::getInstance()->getActiveScriptsMenu(), | |
| 3555 | − scriptEngine->getScriptMenuName(), 0, scriptEngine, SLOT(stop())); | |
| 3289 | + _runningScriptsWidget->setRunningScripts(getRunningScripts()); | |
| 3556 | 3290 | |
| 3557 | 3291 | // setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so |
| 3558 | 3292 | // we can use the same ones from the application. |
| 3559 | 3293 | scriptEngine->getVoxelsScriptingInterface()->setPacketSender(&_voxelEditSender); |
| 3560 | 3294 | scriptEngine->getVoxelsScriptingInterface()->setVoxelTree(_voxels.getTree()); |
| 3295 | + scriptEngine->getVoxelsScriptingInterface()->setUndoStack(&_undoStack); | |
| 3561 | 3296 | scriptEngine->getParticlesScriptingInterface()->setPacketSender(&_particleEditSender); |
| 3562 | 3297 | scriptEngine->getParticlesScriptingInterface()->setParticleTree(_particles.getTree()); |
| 3563 | − | |
| 3298 | + | |
| 3564 | 3299 | // hook our avatar object into this script engine |
| 3565 | 3300 | scriptEngine->setAvatarData(_myAvatar, "MyAvatar"); // leave it as a MyAvatar class to expose thrust features |
| 3566 | 3301 | |
| @@ -3585,8 +3320,6 @@ void Application::loadScript(const QString& scriptName) { · 18 unmodified lines | ||
| 3585 | 3320 | // when the thread is terminated, add both scriptEngine and thread to the deleteLater queue |
| 3586 | 3321 | connect(scriptEngine, SIGNAL(finished(const QString&)), scriptEngine, SLOT(deleteLater())); |
| 3587 | 3322 | connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater())); |
| 3588 | − connect(scriptEngine, SIGNAL(finished(const QString&)), this, SLOT(removeScriptName(const QString&))); | |
| 3589 | − connect(scriptEngine, SIGNAL(cleanupMenuItem(const QString&)), this, SLOT(cleanupScriptMenuItem(const QString&))); | |
| 3590 | 3323 | |
| 3591 | 3324 | // when the application is about to quit, stop our script engine so it unwinds properly |
| 3592 | 3325 | connect(this, SIGNAL(aboutToQuit()), scriptEngine, SLOT(stop())); |
| @@ -3601,13 +3334,21 @@ void Application::loadScript(const QString& scriptName) { · 8 unmodified lines | ||
| 3601 | 3334 | } |
| 3602 | 3335 | |
| 3603 | 3336 | void Application::loadDialog() { |
| 3604 | − // shut down and stop any existing script | |
| 3605 | − QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); | |
| 3606 | − QString suggestedName = desktopLocation.append("/script.js"); | |
| 3337 | + QString suggestedName; | |
| 3607 | 3338 | |
| 3608 | − QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Open Script"), suggestedName, | |
| 3339 | + if (_previousScriptLocation.isEmpty()) { | |
| 3340 | + QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); | |
| 3341 | + suggestedName = desktopLocation.append("/script.js"); | |
| 3342 | + } else { | |
| 3343 | + suggestedName = _previousScriptLocation; | |
| 3344 | + } | |
| 3345 | + | |
| 3346 | + QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Open Script"), suggestedName, | |
| 3609 | 3347 | tr("JavaScript Files (*.js)")); |
| 3610 | − | |
| 3348 | + if (!fileNameString.isEmpty()) { | |
| 3349 | + _previousScriptLocation = fileNameString; | |
| 3350 | + } | |
| 3351 | + | |
| 3611 | 3352 | loadScript(fileNameString); |
| 3612 | 3353 | } |
| 3613 | 3354 | |
| @@ -3618,7 +3359,7 @@ void Application::loadScriptURLDialog() { · 4 unmodified lines | ||
| 3618 | 3359 | scriptURLDialog.setLabelText("Script:"); |
| 3619 | 3360 | scriptURLDialog.setWindowFlags(Qt::Sheet); |
| 3620 | 3361 | const float DIALOG_RATIO_OF_WINDOW = 0.30f; |
| 3621 | − scriptURLDialog.resize(scriptURLDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, | |
| 3362 | + scriptURLDialog.resize(scriptURLDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, | |
| 3622 | 3363 | scriptURLDialog.size().height()); |
| 3623 | 3364 | |
| 3624 | 3365 | int dialogReturn = scriptURLDialog.exec(); |
| @@ -3656,29 +3397,29 @@ void Application::checkVersion() { · 31 unmodified lines | ||
| 3656 | 3397 | } |
| 3657 | 3398 | |
| 3658 | 3399 | void Application::parseVersionXml() { |
| 3659 | − | |
| 3400 | + | |
| 3660 | 3401 | #ifdef Q_OS_WIN32 |
| 3661 | 3402 | QString operatingSystem("win"); |
| 3662 | 3403 | #endif |
| 3663 | − | |
| 3404 | + | |
| 3664 | 3405 | #ifdef Q_OS_MAC |
| 3665 | 3406 | QString operatingSystem("mac"); |
| 3666 | 3407 | #endif |
| 3667 | − | |
| 3408 | + | |
| 3668 | 3409 | #ifdef Q_OS_LINUX |
| 3669 | 3410 | QString operatingSystem("ubuntu"); |
| 3670 | 3411 | #endif |
| 3671 | − | |
| 3412 | + | |
| 3672 | 3413 | QString releaseDate; |
| 3673 | 3414 | QString releaseNotes; |
| 3674 | 3415 | QString latestVersion; |
| 3675 | 3416 | QUrl downloadUrl; |
| 3676 | 3417 | QObject* sender = QObject::sender(); |
| 3677 | − | |
| 3418 | + | |
| 3678 | 3419 | QXmlStreamReader xml(qobject_cast<QNetworkReply*>(sender)); |
| 3679 | 3420 | while (!xml.atEnd() && !xml.hasError()) { |
| 3680 | 3421 | QXmlStreamReader::TokenType token = xml.readNext(); |
| 3681 | − | |
| 3422 | + | |
| 3682 | 3423 | if (token == QXmlStreamReader::StartElement) { |
| 3683 | 3424 | if (xml.name() == "ReleaseDate") { |
| 3684 | 3425 | xml.readNext(); |