History

Merge branch 'master' of https://github.com/worklist/hifi into 19507

stojce committed 12 years ago

Commit 8d45a532 parents 57fe0a4 dc89026122 files · +7493 −2861Browse files

Showing the changes to interface/src/Application.cpp (1 of 122 files). Show all

Unified · Split

interface/src/Application.cpp+354−613
@@ -49,7 +49,7 @@ · 48 unmodified lines
4949 #include <QXmlStreamReader>
5050 #include <QXmlStreamAttributes>
5151 #include <QMediaPlayer>
52−#include <QMimeData>
52+#include <QMimeData>
5353 #include <QMessageBox>
5454
5555 #include <AccountManager.h>
@@ -63,7 +63,7 @@ · 7 unmodified lines
6363 #include <UUID.h>
6464 #include <OctreeSceneStats.h>
6565 #include <LocalVoxelsList.h>
66−#include <FstReader.h>
66+#include <ModelUploader.h>
6767
6868 #include "Application.h"
6969 #include "InterfaceVersion.h"
@@ -81,6 +81,7 @@ · 11 unmodified lines
8181 #include "ui/InfoView.h"
8282 #include "ui/Snapshot.h"
8383 #include "ui/TextRenderer.h"
84+#include "ui/Stats.h"
8485
8586 using namespace std;
8687
@@ -110,15 +111,11 @@ const float MIRROR_FIELD_OF_VIEW = 30.0f; · 23 unmodified lines
110111 const QString CHECK_VERSION_URL = "http://highfidelity.io/latestVersion.xml";
111112 const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion";
112113
113−const int STATS_PELS_PER_LINE = 20;
114−
115−const QString CUSTOM_URL_SCHEME = "hifi:";
116−
117114 void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
118115 if (message.size() > 0) {
119116 QString dateString = QDateTime::currentDateTime().toTimeSpec(Qt::LocalTime).toString(Qt::ISODate);
120117 QString formattedMessage = QString("[%1] %2\n").arg(dateString).arg(message);
121−
118+
122119 fprintf(stdout, "%s", qPrintable(formattedMessage));
123120 Application::getInstance()->getLogger()->addMessage(qPrintable(formattedMessage));
124121 }
@@ -137,7 +134,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 12 unmodified lines
137134 QApplication(argc, argv),
138135 _window(new QMainWindow(desktop())),
139136 _glWidget(new GLCanvas()),
140− _statsExpanded(false),
141137 _nodeThread(new QThread(this)),
142138 _datagramProcessor(),
143139 _frameCount(0),
@@ -167,29 +163,28 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 23 unmodified lines
167163 _voxelHideShowThread(&_voxels),
168164 _packetsPerSecond(0),
169165 _bytesPerSecond(0),
170− _recentMaxPackets(0),
171− _resetRecentMaxPacketsSoon(true),
166+ _previousScriptLocation(),
172167 _logger(new FileLogger(this))
173168 {
174169 // read the ApplicationInfo.ini file for Name/Version/Domain information
175170 QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat);
176−
171+
177172 // set the associated application properties
178173 applicationInfo.beginGroup("INFO");
179−
174+
180175 qDebug() << "[VERSION] Build sequence: " << qPrintable(applicationVersion());
181−
176+
182177 setApplicationName(applicationInfo.value("name").toString());
183178 setApplicationVersion(BUILD_VERSION);
184179 setOrganizationName(applicationInfo.value("organizationName").toString());
185180 setOrganizationDomain(applicationInfo.value("organizationDomain").toString());
186−
181+
187182 QSettings::setDefaultFormat(QSettings::IniFormat);
188−
183+
189184 _myAvatar = _avatarManager.getMyAvatar();
190185
191186 _applicationStartupTime = startup_time;
192−
187+
193188 QFontDatabase::addApplicationFont(Application::resourcesPath() + "styles/Inconsolata.otf");
194189 _window->setWindowTitle("Interface");
195190
@@ -204,19 +199,19 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 8 unmodified lines
204199 if (portStr) {
205200 listenPort = atoi(portStr);
206201 }
207−
202+
208203 // start the nodeThread so its event loop is running
209204 _nodeThread->start();
210−
205+
211206 // make sure the node thread is given highest priority
212207 _nodeThread->setPriority(QThread::TimeCriticalPriority);
213−
208+
214209 // put the NodeList and datagram processing on the node thread
215210 NodeList* nodeList = NodeList::createInstance(NodeType::Agent, listenPort);
216−
211+
217212 nodeList->moveToThread(_nodeThread);
218213 _datagramProcessor.moveToThread(_nodeThread);
219−
214+
220215 // connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal
221216 connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), &_datagramProcessor, SLOT(processDatagrams()));
222217
@@ -238,20 +233,20 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 15 unmodified lines
238233 connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer)));
239234 connect(nodeList, &NodeList::uuidChanged, this, &Application::updateWindowTitle);
240235 connect(nodeList, &NodeList::limitOfSilentDomainCheckInsReached, nodeList, &NodeList::reset);
241−
236+
242237 // connect to appropriate slots on AccountManager
243238 AccountManager& accountManager = AccountManager::getInstance();
244239 connect(&accountManager, &AccountManager::authRequired, Menu::getInstance(), &Menu::loginForCurrentDomain);
245240 connect(&accountManager, &AccountManager::usernameChanged, this, &Application::updateWindowTitle);
246−
241+
247242 // set the account manager's root URL and trigger a login request if we don't have the access token
248243 accountManager.setAuthURL(DEFAULT_NODE_AUTH_URL);
249−
244+
250245 // once the event loop has started, check and signal for an access token
251246 QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection);
252247
253248 _settings = new QSettings(this);
254−
249+
255250 // Check to see if the user passed in a command line option for loading a local
256251 // Voxel File.
257252 _voxelsFilename = getCmdOption(argc, constArgv, "-i");
@@ -265,7 +260,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 7 unmodified lines
265260 nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
266261 << NodeType::VoxelServer << NodeType::ParticleServer
267262 << NodeType::MetavoxelServer);
268−
263+
269264 // connect to the packet sent signal of the _voxelEditSender and the _particleEditSender
270265 connect(&_voxelEditSender, &VoxelEditPacketSender::packetSent, this, &Application::packetSent);
271266 connect(&_particleEditSender, &ParticleEditPacketSender::packetSent, this, &Application::packetSent);
@@ -275,7 +270,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 3 unmodified lines
275270 connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
276271 silentNodeTimer->moveToThread(_nodeThread);
277272 silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
278−
273+
279274 // send the identity packet for our avatar each second to our avatar mixer
280275 QTimer* identityPacketTimer = new QTimer();
281276 connect(identityPacketTimer, &QTimer::timeout, _myAvatar, &MyAvatar::sendIdentityPacket);
@@ -323,14 +318,18 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 41 unmodified lines
323318
324319 // Set the sixense filtering
325320 _sixenseManager.setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense));
326−
321+
327322 checkVersion();
328−
323+
329324 _overlays.init(_glWidget); // do this before scripts load
330−
325+
331326 LocalVoxelsList::getInstance()->addPersistantTree(DOMAIN_TREE_NAME, _voxels.getTree());
332327 LocalVoxelsList::getInstance()->addPersistantTree(CLIPBOARD_TREE_NAME, &_clipboard);
333328
329+ _window->addDockWidget(Qt::NoDockWidgetArea, _runningScriptsWidget = new RunningScriptsWidget());
330+ _runningScriptsWidget->setRunningScripts(getRunningScripts());
331+ connect(_runningScriptsWidget, &RunningScriptsWidget::stopScriptName, this, &Application::stopScript);
332+
334333 // check first run...
335334 QVariant firstRunValue = _settings->value("firstRun",QVariant(true));
336335 if (firstRunValue.isValid() && firstRunValue.toBool()) {
@@ -338,7 +337,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : · 1 unmodified line
338337 // clear the scripts, and set out script to our default scripts
339338 clearScriptsBeforeRunning();
340339 loadScript("http://public.highfidelity.io/scripts/defaultScripts.js");
341−
340+
342341 _settings->setValue("firstRun",QVariant(false));
343342 } else {
344343 // 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
349348 Application::~Application() {
350349
351350 qInstallMessageHandler(NULL);
352−
351+
353352 // make sure we don't call the idle timer any more
354353 delete idleTimer;
355−
354+
356355 Menu::getInstance()->saveSettings();
357356 _rearMirrorTools->saveSettings(_settings);
358−
357+
359358 _sharedVoxelSystem.changeTree(new VoxelTree);
360359 if (_voxelImporter) {
361360 _voxelImporter->saveSettings(_settings);
362361 delete _voxelImporter;
363362 }
364363 _settings->sync();
365−
364+
366365 // let the avatar mixer know we're out
367366 MyAvatar::sendKillAvatar();
368−
367+
369368 // ask the datagram processing thread to quit and wait until it is done
370369 _nodeThread->quit();
371370 _nodeThread->wait();
372−
371+
372+ // stop the audio process
373+ QMetaObject::invokeMethod(&_audio, "stop");
374+
373375 // ask the audio thread to quit and wait until it is done
374376 _audio.thread()->quit();
375377 _audio.thread()->wait();
376−
378+
377379 _voxelProcessor.terminate();
378380 _voxelHideShowThread.terminate();
379381 _voxelEditSender.terminate();
@@ -386,9 +388,9 @@ Application::~Application() { · 6 unmodified lines
386388 Menu::getInstance()->deleteLater();
387389
388390 _myAvatar = NULL;
389−
391+
390392 delete _glWidget;
391−
393+
392394 AccountManager::getInstance().destroy();
393395 }
394396
@@ -508,7 +510,7 @@ void Application::paintGL() { · 113 unmodified lines
508510 _myCamera.setDistance(0.0f);
509511 _myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing
510512 _myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition());
511− _myCamera.setTargetRotation(_myAvatar->getHead()->getOrientation());
513+ _myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation());
512514
513515 } else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
514516 _myCamera.setTightness(0.0f); // In first person, camera follows (untweaked) head exactly without delay
@@ -522,10 +524,22 @@ void Application::paintGL() { · 7 unmodified lines
522524
523525 } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
524526 _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;
526529 _myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _myAvatar->getScale());
527530 _myCamera.setTargetPosition(_myAvatar->getPosition() + glm::vec3(0, headHeight, 0));
528531 _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+ }
529543 }
530544
531545 // Update camera position
@@ -580,7 +594,7 @@ void Application::paintGL() { · 48 unmodified lines
580594
581595 if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
582596 renderRearViewMirror(_mirrorViewRect);
583−
597+
584598 } else if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
585599 _rearMirrorTools->render(true);
586600 }
@@ -610,6 +624,14 @@ void Application::resizeGL(int width, int height) { · 23 unmodified lines
610624
611625 updateProjectionMatrix();
612626 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);
613635 }
614636
615637 void Application::updateProjectionMatrix() {
@@ -653,10 +675,10 @@ void Application::controlledBroadcastToNodes(const QByteArray& packet, const Nod · 37 unmodified lines
653675 if (type == NodeType::VoxelServer && !Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
654676 continue;
655677 }
656−
678+
657679 // Perform the broadcast for one type
658680 int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(packet, NodeSet() << type);
659−
681+
660682 // Feed number of bytes to corresponding channel of the bandwidth meter, if any (done otherwise)
661683 BandwidthMeter::ChannelIndex channel;
662684 switch (type) {
@@ -675,32 +697,15 @@ void Application::controlledBroadcastToNodes(const QByteArray& packet, const Nod · 12 unmodified lines
675697 }
676698
677699 bool Application::event(QEvent* event) {
678−
700+
679701 // handle custom URL
680702 if (event->type() == QEvent::FileOpen) {
681703 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());
702707 }
703−
708+
704709 return false;
705710 }
706711 return QApplication::event(event);
@@ -711,7 +716,7 @@ void Application::keyPressEvent(QKeyEvent* event) { · 4 unmodified lines
711716 _keysPressed.insert(event->key());
712717
713718 _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts
714−
719+
715720 // if one of our scripts have asked to capture this event, then stop processing it
716721 if (_controllerScriptingInterface.isKeyCaptured(event)) {
717722 return;
@@ -1069,8 +1074,12 @@ void Application::mouseReleaseEvent(QMouseEvent* event) { · 351 unmodified lines
10691074 _mousePressed = false;
10701075 checkBandwidthMeterClick();
10711076 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+ }
10741083 }
10751084 }
10761085 }
@@ -1119,7 +1128,7 @@ void Application::touchBeginEvent(QTouchEvent* event) { · 42 unmodified lines
11191128 if (_controllerScriptingInterface.isTouchCaptured()) {
11201129 return;
11211130 }
1122−
1131+
11231132 // put any application specific touch behavior below here..
11241133 _lastTouchAvgX = _touchAvgX;
11251134 _lastTouchAvgY = _touchAvgY;
@@ -1162,13 +1171,13 @@ void Application::dropEvent(QDropEvent *event) { · 36 unmodified lines
11621171 break;
11631172 }
11641173 }
1165−
1174+
11661175 SnapshotMetaData* snapshotData = Snapshot::parseSnapshotData(snapshotPath);
11671176 if (snapshotData) {
11681177 if (!snapshotData->getDomain().isEmpty()) {
11691178 Menu::getInstance()->goToDomain(snapshotData->getDomain());
11701179 }
1171−
1180+
11721181 _myAvatar->setPosition(snapshotData->getLocation());
11731182 _myAvatar->setOrientation(snapshotData->getOrientation());
11741183 } else {
@@ -1196,19 +1205,19 @@ void Application::timer() { · 21 unmodified lines
11961205 }
11971206
11981207 _fps = (float)_frameCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f);
1199−
1208+
12001209 _packetsPerSecond = (float) _datagramProcessor.getPacketCount() / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f);
12011210 _bytesPerSecond = (float) _datagramProcessor.getByteCount() / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f);
12021211 _frameCount = 0;
1203−
1212+
12041213 _datagramProcessor.resetCounters();
12051214
12061215 gettimeofday(&_timerStart, NULL);
12071216
12081217 // ask the node list to check in with the domain server
12091218 NodeList::getInstance()->sendDomainServerCheckIn();
1210−
1211−
1219+
1220+
12121221 }
12131222
12141223 void Application::idle() {
@@ -1245,11 +1254,11 @@ void Application::idle() { · 30 unmodified lines
12451254 _idleLoopMeasuredJitter = _idleLoopStdev.getStDev();
12461255 _idleLoopStdev.reset();
12471256 }
1248−
1257+
12491258 if (Menu::getInstance()->isOptionChecked(MenuOption::BuckyBalls)) {
12501259 _buckyBalls.simulate(timeSinceLastUpdate / 1000.f, Application::getInstance()->getAvatar()->getHandData());
12511260 }
1252−
1261+
12531262 // After finishing all of the above work, restart the idle timer, allowing 2ms to process events.
12541263 idleTimer->start(2);
12551264 }
@@ -1381,7 +1390,7 @@ void Application::exportVoxels(const VoxelDetail& sourceVoxel) { · 125 unmodified lines
13811390 tr("Sparse Voxel Octree Files (*.svo)"));
13821391 QByteArray fileNameAscii = fileNameString.toLocal8Bit();
13831392 const char* fileName = fileNameAscii.data();
1384−
1393+
13851394 VoxelTreeElement* selectedNode = _voxels.getTree()->getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
13861395 if (selectedNode) {
13871396 VoxelTree exportTree;
@@ -1395,12 +1404,12 @@ void Application::exportVoxels(const VoxelDetail& sourceVoxel) { · 7 unmodified lines
13951404
13961405 void Application::importVoxels() {
13971406 _importSucceded = false;
1398−
1407+
13991408 if (!_voxelImporter) {
14001409 _voxelImporter = new VoxelImporter(_window);
14011410 _voxelImporter->loadSettings(_settings);
14021411 }
1403−
1412+
14041413 if (!_voxelImporter->exec()) {
14051414 qDebug() << "[DEBUG] Import succeeded." << endl;
14061415 _importSucceded = true;
@@ -1414,7 +1423,7 @@ void Application::importVoxels() { · 7 unmodified lines
14141423
14151424 // restore the main window's active state
14161425 _window->activateWindow();
1417−
1426+
14181427 emit importDone();
14191428 }
14201429
@@ -1470,7 +1479,7 @@ void Application::pasteVoxels(const VoxelDetail& sourceVoxel) { · 49 unmodified lines
14701479 }
14711480
14721481 pasteVoxelsToOctalCode(octalCodeDestination);
1473−
1482+
14741483 if (calculatedOctCode) {
14751484 delete[] calculatedOctCode;
14761485 }
@@ -1507,9 +1516,9 @@ void Application::init() { · 30 unmodified lines
15071516
15081517 // Cleanup of the original shared tree
15091518 _sharedVoxelSystem.init();
1510−
1519+
15111520 _voxelImporter = new VoxelImporter(_window);
1512−
1521+
15131522 _environment.init();
15141523
15151524 _glowEffect.init();
@@ -1551,11 +1560,11 @@ void Application::init() { · 35 unmodified lines
15511560 _audio.setJitterBufferSamples(Menu::getInstance()->getAudioJitterBufferSamples());
15521561 }
15531562 qDebug("Loaded settings");
1554−
1563+
15551564 // initialize Visage and Faceshift after loading the menu settings
15561565 _faceshift.init();
15571566 _visage.init();
1558−
1567+
15591568 // fire off an immediate domain-server check in now that settings are loaded
15601569 NodeList::getInstance()->sendDomainServerCheckIn();
15611570
@@ -1574,20 +1583,20 @@ void Application::init() { · 12 unmodified lines
15741583 _particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio, &_avatarManager);
15751584
15761585 // 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+
15871596 _audio.init(_glWidget);
15881597
15891598 _rearMirrorTools = new RearMirrorTools(_glWidget, _mirrorViewRect, _settings);
1590−
1599+
15911600 connect(_rearMirrorTools, SIGNAL(closeView()), SLOT(closeMirrorView()));
15921601 connect(_rearMirrorTools, SIGNAL(restoreView()), SLOT(restoreMirrorView()));
15931602 connect(_rearMirrorTools, SIGNAL(shrinkView()), SLOT(shrinkMirrorView()));
@@ -1636,6 +1645,8 @@ void Application::updateLOD() { · 42 unmodified lines
16361645 // adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode
16371646 if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableAutoAdjustLOD) && !isThrottleRendering()) {
16381647 Menu::getInstance()->autoAdjustLOD(_fps);
1648+ } else {
1649+ Menu::getInstance()->resetLODAdjust();
16391650 }
16401651 }
16411652
@@ -1708,7 +1719,7 @@ void Application::updateMyAvatarLookAtPosition() { · 66 unmodified lines
17081719 float distance = TREE_SCALE;
17091720 if (_myAvatar->getLookAtTargetAvatar() && _myAvatar != _myAvatar->getLookAtTargetAvatar()) {
17101721 distance = glm::distance(_mouseRayOrigin,
1711− static_cast<Avatar*>(_myAvatar->getLookAtTargetAvatar())->getHead()->calculateAverageEyePosition());
1722+ static_cast<Avatar*>(_myAvatar->getLookAtTargetAvatar())->getHead()->calculateAverageEyePosition());
17121723 }
17131724 const float FIXED_MIN_EYE_DISTANCE = 0.3f;
17141725 float minEyeDistance = FIXED_MIN_EYE_DISTANCE + (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON ? 0.0f :
@@ -1721,7 +1732,7 @@ void Application::updateMyAvatarLookAtPosition() { · 6 unmodified lines
17211732 eyePitch = _faceshift.getEstimatedEyePitch();
17221733 eyeYaw = _faceshift.getEstimatedEyeYaw();
17231734 trackerActive = true;
1724−
1735+
17251736 } else if (_visage.isActive()) {
17261737 eyePitch = _visage.getEstimatedEyePitch();
17271738 eyeYaw = _visage.getEstimatedEyeYaw();
@@ -1842,15 +1853,6 @@ void Application::updateDialogs(float deltaTime) { · 114 unmodified lines
18421853 }
18431854 }
18441855
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−
18541856 void Application::updateCursor(float deltaTime) {
18551857 bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
18561858 PerformanceWarning warn(showWarnings, "Application::updateCursor()");
@@ -1899,14 +1901,13 @@ void Application::update(float deltaTime) { · 42 unmodified lines
18991901 updateMetavoxels(deltaTime); // update metavoxels
19001902 updateCamera(deltaTime); // handle various camera tweaks like off axis projection
19011903 updateDialogs(deltaTime); // update various stats dialogs if present
1902− updateAudio(deltaTime); // Update audio stats for procedural sounds
19031904 updateCursor(deltaTime); // Handle cursor updates
19041905
19051906 _particles.update(); // update the particles...
19061907 _particleCollisionSystem.update(); // collide the particles...
1907−
1908+
19081909 _overlays.update(deltaTime);
1909−
1910+
19101911 // let external parties know we're updating
19111912 emit simulating(deltaTime);
19121913 }
@@ -1930,7 +1931,7 @@ void Application::updateMyAvatar(float deltaTime) { · 17 unmodified lines
19301931 // actually need to calculate the view frustum planes to send these details
19311932 // to the server.
19321933 loadViewFrustum(_myCamera, _viewFrustum);
1933−
1934+
19341935 // Update my voxel servers with my current voxel query...
19351936 quint64 now = usecTimestampNow();
19361937 quint64 sinceLastQuery = now - _lastQueriedTime;
@@ -2187,19 +2188,26 @@ void Application::updateShadowMap() { · 250 unmodified lines
21872188 (_viewFrustum.getFarClip() - _viewFrustum.getNearClip());
21882189 loadViewFrustum(_myCamera, _viewFrustum);
21892190 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;
21992200 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];
22022202 }
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);
22032211
22042212 // stretch out our extents in z so that we get all of the avatars
22052213 minima.z -= _viewFrustum.getFarClip() * 0.5f;
@@ -2220,7 +2228,7 @@ void Application::updateShadowMap() { · 14 unmodified lines
22202228 _shadowViewFrustum.setEyeOffsetPosition(glm::vec3());
22212229 _shadowViewFrustum.setEyeOffsetOrientation(glm::quat());
22222230 _shadowViewFrustum.calculate();
2223−
2231+
22242232 glMatrixMode(GL_PROJECTION);
22252233 glPushMatrix();
22262234 glLoadIdentity();
@@ -2233,8 +2241,7 @@ void Application::updateShadowMap() { · 6 unmodified lines
22332241 glRotatef(glm::degrees(glm::angle(inverseRotation)), axis.x, axis.y, axis.z);
22342242
22352243 // 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();
22382245
22392246 _avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE);
22402247 _particles.render();
@@ -2275,19 +2282,19 @@ void Application::setupWorldLight() { · 34 unmodified lines
22752282
22762283 QImage Application::renderAvatarBillboard() {
22772284 _textureCache.getPrimaryFramebufferObject()->bind();
2278−
2285+
22792286 glDisable(GL_BLEND);
22802287
22812288 const int BILLBOARD_SIZE = 64;
22822289 renderRearViewMirror(QRect(0, _glWidget->height() - BILLBOARD_SIZE, BILLBOARD_SIZE, BILLBOARD_SIZE), true);
2283−
2290+
22842291 QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32);
22852292 glReadPixels(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE, GL_BGRA, GL_UNSIGNED_BYTE, image.bits());
2286−
2293+
22872294 glEnable(GL_BLEND);
2288−
2295+
22892296 _textureCache.getPrimaryFramebufferObject()->release();
2290−
2297+
22912298 return image;
22922299 }
22932300
@@ -2319,8 +2326,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { · 25 unmodified lines
23192326 glRotatef(-glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
23202327
23212328 // 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());
23242330
23252331 glTranslatef(_viewMatrixTranslation.x, _viewMatrixTranslation.y, _viewMatrixTranslation.z);
23262332
@@ -2388,7 +2394,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { · 61 unmodified lines
23882394 "Application::displaySide() ... metavoxels...");
23892395 _metavoxels.render();
23902396 }
2391−
2397+
23922398 if (Menu::getInstance()->isOptionChecked(MenuOption::BuckyBalls)) {
23932399 PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
23942400 "Application::displaySide() ... bucky balls...");
@@ -2401,7 +2407,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { · 6 unmodified lines
24012407 "Application::displaySide() ... particles...");
24022408 _particles.render();
24032409 }
2404−
2410+
24052411 // render the ambient occlusion effect if enabled
24062412 if (Menu::getInstance()->isOptionChecked(MenuOption::AmbientOcclusion)) {
24072413 PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
@@ -2445,12 +2451,17 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { · 37 unmodified lines
24452451
24462452 // give external parties a change to hook in
24472453 emit renderingInWorldInterface();
2448−
2454+
24492455 // render JS/scriptable overlays
24502456 _overlays.render3D();
24512457 }
24522458 }
24532459
2460+void Application::updateUntranslatedViewMatrix(const glm::vec3& viewMatrixTranslation) {
2461+ glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&_untranslatedViewMatrix);
2462+ _viewMatrixTranslation = viewMatrixTranslation;
2463+}
2464+
24542465 void Application::loadTranslatedViewMatrix(const glm::vec3& translation) {
24552466 glLoadMatrixf((const GLfloat*)&_untranslatedViewMatrix);
24562467 glTranslatef(translation.x + _viewMatrixTranslation.x, translation.y + _viewMatrixTranslation.y,
@@ -2495,14 +2506,111 @@ void Application::displayOverlay() { · 38 unmodified lines
24952506 }
24962507 }
24972508
2509+ // Audio Scope
2510+ const int AUDIO_SCOPE_Y_OFFSET = 135;
24982511 if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
2499− _audio.renderMuteIcon(1, _glWidget->height() - 50);
25002512 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);
25032515 }
25042516 }
25052517
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+
25062614 if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse)) {
25072615 _myAvatar->renderHeadMouse();
25082616 }
@@ -2512,11 +2620,17 @@ void Application::displayOverlay() { · 3 unmodified lines
25122620 glPointSize(1.0f);
25132621
25142622 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();
25152629 // Onscreen text about position, servers, etc
2516− displayStats();
2630+ Stats::getInstance()->display(WHITE_TEXT, horizontalOffset, _fps, _packetsPerSecond, _bytesPerSecond, voxelPacketsToProcess);
25172631 // Bandwidth meter
25182632 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);
25202634 _bandwidthMeter.render(_glWidget->width(), _glWidget->height());
25212635 }
25222636 }
@@ -2526,8 +2640,8 @@ void Application::displayOverlay() { · 3 unmodified lines
25262640 char frameTimer[10];
25272641 quint64 mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5);
25282642 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) &&
25312645 Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth))
25322646 ? 80 : 20;
25332647 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
25382652 glPopMatrix();
25392653 }
25402654
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−
29502655 glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) {
29512656 float horizontalScale = _glWidget->width() / 2.0f;
29522657 float verticalScale = _glWidget->height() / 2.0f;
@@ -2979,12 +2684,12 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { · 26 unmodified lines
29792684 _mirrorCamera.setFieldOfView(BILLBOARD_FIELD_OF_VIEW); // degees
29802685 _mirrorCamera.setDistance(BILLBOARD_DISTANCE * _myAvatar->getScale());
29812686 _mirrorCamera.setTargetPosition(_myAvatar->getPosition());
2982−
2687+
29832688 } else if (_rearMirrorTools->getZoomLevel() == BODY) {
29842689 _mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees
29852690 _mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale());
29862691 _mirrorCamera.setTargetPosition(_myAvatar->getChestPosition());
2987−
2692+
29882693 } else { // HEAD zoom level
29892694 _mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees
29902695 _mirrorCamera.setDistance(MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale());
@@ -2999,7 +2704,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { · 8 unmodified lines
29992704 }
30002705 }
30012706 _mirrorCamera.setAspectRatio((float)region.width() / region.height());
3002−
2707+
30032708 _mirrorCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f)));
30042709 _mirrorCamera.update(1.0f/_fps);
30052710
@@ -3044,7 +2749,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { · 38 unmodified lines
30442749 if (!billboard) {
30452750 _rearMirrorTools->render(false);
30462751 }
3047−
2752+
30482753 // reset Viewport and projection matrix
30492754 glViewport(0, 0, _glWidget->width(), _glWidget->height());
30502755 glDisable(GL_SCISSOR_TEST);
@@ -3264,14 +2969,14 @@ void Application::setMenuShortcutsEnabled(bool enabled) { · 213 unmodified lines
32642969 }
32652970
32662971 void Application::updateWindowTitle(){
3267−
2972+
32682973 QString buildVersion = " (build " + applicationVersion() + ")";
32692974 NodeList* nodeList = NodeList::getInstance();
3270−
2975+
32712976 QString username = AccountManager::getInstance().getUsername();
32722977 QString title = QString() + (!username.isEmpty() ? username + " " : QString()) + nodeList->getSessionUUID().toString()
32732978 + " @ " + nodeList->getDomainInfo().getHostname() + buildVersion;
3274−
2979+
32752980 qDebug("Application title set to: %s", title.toStdString().c_str());
32762981 _window->setWindowTitle(title);
32772982 }
@@ -3286,7 +2991,7 @@ void Application::domainChanged(const QString& domainHostname) { · 8 unmodified lines
32862991 _voxelServerJurisdictions.clear();
32872992 _octreeServerSceneStats.clear();
32882993 _particleServerJurisdictions.clear();
3289−
2994+
32902995 // reset the particle renderer
32912996 _particles.clear();
32922997
@@ -3296,12 +3001,12 @@ void Application::domainChanged(const QString& domainHostname) { · 3 unmodified lines
32963001
32973002 void Application::connectedToDomain(const QString& hostname) {
32983003 AccountManager& accountManager = AccountManager::getInstance();
3299−
3004+
33003005 if (accountManager.isLoggedIn()) {
33013006 // update our domain-server with the data-server we're logged in with
3302−
3007+
33033008 QString domainPutJsonString = "{\"address\":{\"domain\":\"" + hostname + "\"}}";
3304−
3009+
33053010 accountManager.authenticatedRequest("/api/v1/users/address", QNetworkAccessManager::PutOperation,
33063011 JSONCallbackParameters(), domainPutJsonString.toUtf8());
33073012 }
@@ -3466,13 +3171,13 @@ void Application::loadScripts() { · 158 unmodified lines
34663171 // loads all saved scripts
34673172 QSettings* settings = new QSettings(this);
34683173 int size = settings->beginReadArray("Settings");
3469−
3174+
34703175 for (int i = 0; i < size; ++i){
34713176 settings->setArrayIndex(i);
34723177 QString string = settings->value("script").toString();
34733178 loadScript(string);
34743179 }
3475−
3180+
34763181 settings->endArray();
34773182 }
34783183
@@ -3487,80 +3192,110 @@ void Application::saveScripts() { · 8 unmodified lines
34873192 // saves all current running scripts
34883193 QSettings* settings = new QSettings(this);
34893194 settings->beginWriteArray("Settings");
3490− for (int i = 0; i < _activeScripts.size(); ++i){
3195+ for (int i = 0; i < getRunningScripts().size(); ++i){
34913196 settings->setArrayIndex(i);
3492− settings->setValue("script", _activeScripts.at(i));
3197+ settings->setValue("script", getRunningScripts().at(i));
34933198 }
3494−
3199+
34953200 settings->endArray();
34963201 }
34973202
34983203 void Application::stopAllScripts() {
34993204 // 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);
35043208 }
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());
35063219 }
35073220
35083221 void Application::reloadAllScripts() {
35093222 // remember all the current scripts so we can reload them
3510− QStringList reloadList = _activeScripts;
3223+ QStringList reloadList = getRunningScripts();
35113224 // 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();
35173226
3518− // NOTE: we don't need to clear the _activeScripts list because that is handled on script shutdown.
3519−
35203227 foreach (QString scriptName, reloadList){
35213228 qDebug() << "reloading script..." << scriptName;
35223229 loadScript(scriptName);
35233230 }
35243231 }
35253232
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()));
35303258 }
35313259 }
35323260
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);
35353273 }
35363274
3537−void Application::cleanupScriptMenuItem(const QString& scriptMenuName) {
3538− Menu::getInstance()->removeAction(Menu::getInstance()->getActiveScriptsMenu(), scriptMenuName);
3275+void Application::uploadSkeleton() {
3276+ uploadFST(false);
35393277 }
35403278
35413279 void Application::loadScript(const QString& scriptName) {
35423280
35433281 // 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);
35463284
35473285 if (!scriptEngine->hasScript()) {
35483286 qDebug() << "Application::loadScript(), script failed to load...";
35493287 return;
35503288 }
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());
35563290
35573291 // setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so
35583292 // we can use the same ones from the application.
35593293 scriptEngine->getVoxelsScriptingInterface()->setPacketSender(&_voxelEditSender);
35603294 scriptEngine->getVoxelsScriptingInterface()->setVoxelTree(_voxels.getTree());
3295+ scriptEngine->getVoxelsScriptingInterface()->setUndoStack(&_undoStack);
35613296 scriptEngine->getParticlesScriptingInterface()->setPacketSender(&_particleEditSender);
35623297 scriptEngine->getParticlesScriptingInterface()->setParticleTree(_particles.getTree());
3563−
3298+
35643299 // hook our avatar object into this script engine
35653300 scriptEngine->setAvatarData(_myAvatar, "MyAvatar"); // leave it as a MyAvatar class to expose thrust features
35663301
@@ -3585,8 +3320,6 @@ void Application::loadScript(const QString& scriptName) { · 18 unmodified lines
35853320 // when the thread is terminated, add both scriptEngine and thread to the deleteLater queue
35863321 connect(scriptEngine, SIGNAL(finished(const QString&)), scriptEngine, SLOT(deleteLater()));
35873322 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&)));
35903323
35913324 // when the application is about to quit, stop our script engine so it unwinds properly
35923325 connect(this, SIGNAL(aboutToQuit()), scriptEngine, SLOT(stop()));
@@ -3601,13 +3334,21 @@ void Application::loadScript(const QString& scriptName) { · 8 unmodified lines
36013334 }
36023335
36033336 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;
36073338
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,
36093347 tr("JavaScript Files (*.js)"));
3610−
3348+ if (!fileNameString.isEmpty()) {
3349+ _previousScriptLocation = fileNameString;
3350+ }
3351+
36113352 loadScript(fileNameString);
36123353 }
36133354
@@ -3618,7 +3359,7 @@ void Application::loadScriptURLDialog() { · 4 unmodified lines
36183359 scriptURLDialog.setLabelText("Script:");
36193360 scriptURLDialog.setWindowFlags(Qt::Sheet);
36203361 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,
36223363 scriptURLDialog.size().height());
36233364
36243365 int dialogReturn = scriptURLDialog.exec();
@@ -3656,29 +3397,29 @@ void Application::checkVersion() { · 31 unmodified lines
36563397 }
36573398
36583399 void Application::parseVersionXml() {
3659−
3400+
36603401 #ifdef Q_OS_WIN32
36613402 QString operatingSystem("win");
36623403 #endif
3663−
3404+
36643405 #ifdef Q_OS_MAC
36653406 QString operatingSystem("mac");
36663407 #endif
3667−
3408+
36683409 #ifdef Q_OS_LINUX
36693410 QString operatingSystem("ubuntu");
36703411 #endif
3671−
3412+
36723413 QString releaseDate;
36733414 QString releaseNotes;
36743415 QString latestVersion;
36753416 QUrl downloadUrl;
36763417 QObject* sender = QObject::sender();
3677−
3418+
36783419 QXmlStreamReader xml(qobject_cast<QNetworkReply*>(sender));
36793420 while (!xml.atEnd() && !xml.hasError()) {
36803421 QXmlStreamReader::TokenType token = xml.readNext();
3681−
3422+
36823423 if (token == QXmlStreamReader::StartElement) {
36833424 if (xml.name() == "ReleaseDate") {
36843425 xml.readNext();

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t