History

Merge branch 'master' into 20769

Triplelexx committed 10 years ago

Commit 11af0a42 parents 949eab6 4a78ad8846 files · +39180 −16237Browse files

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

Unified · Split

interface/src/Application.cpp+646−642
@@ -120,6 +120,7 @@ · 119 unmodified lines
120120 #include <recording/Recorder.h>
121121 #include <QmlWebWindowClass.h>
122122 #include <Preferences.h>
123+#include <display-plugins/CompositorHelper.h>
123124
124125 #include "AnimDebugDraw.h"
125126 #include "AudioClient.h"
@@ -140,6 +141,7 @@ · 14 unmodified lines
140141 #include "ModelPackager.h"
141142 #include "PluginContainerProxy.h"
142143 #include "scripting/AccountScriptingInterface.h"
144+#include "scripting/AssetMappingsScriptingInterface.h"
143145 #include "scripting/AudioDeviceScriptingInterface.h"
144146 #include "scripting/ClipboardScriptingInterface.h"
145147 #include "scripting/DesktopScriptingInterface.h"
@@ -157,7 +159,6 @@ · 11 unmodified lines
157159 #include "Stars.h"
158160 #include "ui/AddressBarDialog.h"
159161 #include "ui/AvatarInputs.h"
160−#include "ui/AssetUploadDialogFactory.h"
161162 #include "ui/DialogsManager.h"
162163 #include "ui/LoginDialog.h"
163164 #include "ui/overlays/Cube3DOverlay.h"
@@ -194,6 +195,7 @@ static const QString FBX_EXTENSION = ".fbx"; · 30 unmodified lines
194195 static const QString OBJ_EXTENSION = ".obj";
195196 static const QString AVA_JSON_EXTENSION = ".ava.json";
196197
198+static const int MSECS_PER_SEC = 1000;
197199 static const int MIRROR_VIEW_TOP_PADDING = 5;
198200 static const int MIRROR_VIEW_LEFT_PADDING = 10;
199201 static const int MIRROR_VIEW_WIDTH = 265;
@@ -210,8 +212,6 @@ static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html"; · 10 unmodified lines
210212
211213 static const unsigned int THROTTLED_SIM_FRAMERATE = 15;
212214 static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE;
213−static const unsigned int CAPPED_SIM_FRAMERATE = 120;
214−static const int CAPPED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / CAPPED_SIM_FRAMERATE;
215215
216216 static const uint32_t INVALID_FRAME = UINT32_MAX;
217217
@@ -224,7 +224,6 @@ static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStanda · 6 unmodified lines
224224 static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).append("/script.js");
225225 #endif
226226
227−const QString DEFAULT_SCRIPTS_JS_URL = "http://s3.amazonaws.com/hifi-public/scripts/defaultScripts.js";
228227 Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS);
229228
230229 const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensions {
@@ -236,6 +235,107 @@ const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensi · 5 unmodified lines
236235 { AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl }
237236 };
238237
238+class DeadlockWatchdogThread : public QThread {
239+public:
240+ static const unsigned long HEARTBEAT_CHECK_INTERVAL_SECS = 1;
241+ static const unsigned long HEARTBEAT_UPDATE_INTERVAL_SECS = 1;
242+ static const unsigned long HEARTBEAT_REPORT_INTERVAL_USECS = 5 * USECS_PER_SECOND;
243+ static const unsigned long MAX_HEARTBEAT_AGE_USECS = 30 * USECS_PER_SECOND;
244+ static const int WARNING_ELAPSED_HEARTBEAT = 500 * USECS_PER_MSEC; // warn if elapsed heartbeat average is large
245+ static const int HEARTBEAT_SAMPLES = 100000; // ~5 seconds worth of samples
246+
247+ // Set the heartbeat on launch
248+ DeadlockWatchdogThread() {
249+ setObjectName("Deadlock Watchdog");
250+ // Give the heartbeat an initial value
251+ _heartbeat = usecTimestampNow();
252+ connect(qApp, &QCoreApplication::aboutToQuit, [this] {
253+ _quit = true;
254+ });
255+ }
256+
257+ void updateHeartbeat() {
258+ auto now = usecTimestampNow();
259+ auto elapsed = now - _heartbeat;
260+ _movingAverage.addSample(elapsed);
261+ _heartbeat = now;
262+ }
263+
264+ void deadlockDetectionCrash() {
265+ uint32_t* crashTrigger = nullptr;
266+ *crashTrigger = 0xDEAD10CC;
267+ }
268+
269+ void run() override {
270+ while (!_quit) {
271+ QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS);
272+
273+ uint64_t lastHeartbeat = _heartbeat; // sample atomic _heartbeat, because we could context switch away and have it updated on us
274+ uint64_t now = usecTimestampNow();
275+ auto lastHeartbeatAge = (now > lastHeartbeat) ? now - lastHeartbeat : 0;
276+ auto sinceLastReport = (now > _lastReport) ? now - _lastReport : 0;
277+ auto elapsedMovingAverage = _movingAverage.getAverage();
278+
279+ if (elapsedMovingAverage > _maxElapsedAverage) {
280+ qDebug() << "DEADLOCK WATCHDOG NEW maxElapsedAverage:"
281+ << "lastHeartbeatAge:" << lastHeartbeatAge
282+ << "elapsedMovingAverage:" << elapsedMovingAverage
283+ << "maxElapsed:" << _maxElapsed
284+ << "PREVIOUS maxElapsedAverage:" << _maxElapsedAverage
285+ << "NEW maxElapsedAverage:" << elapsedMovingAverage
286+ << "samples:" << _movingAverage.getSamples();
287+ _maxElapsedAverage = elapsedMovingAverage;
288+ }
289+ if (lastHeartbeatAge > _maxElapsed) {
290+ qDebug() << "DEADLOCK WATCHDOG NEW maxElapsed:"
291+ << "lastHeartbeatAge:" << lastHeartbeatAge
292+ << "elapsedMovingAverage:" << elapsedMovingAverage
293+ << "PREVIOUS maxElapsed:" << _maxElapsed
294+ << "NEW maxElapsed:" << lastHeartbeatAge
295+ << "maxElapsedAverage:" << _maxElapsedAverage
296+ << "samples:" << _movingAverage.getSamples();
297+ _maxElapsed = lastHeartbeatAge;
298+ }
299+ if ((sinceLastReport > HEARTBEAT_REPORT_INTERVAL_USECS) || (elapsedMovingAverage > WARNING_ELAPSED_HEARTBEAT)) {
300+ qDebug() << "DEADLOCK WATCHDOG STATUS -- lastHeartbeatAge:" << lastHeartbeatAge
301+ << "elapsedMovingAverage:" << elapsedMovingAverage
302+ << "maxElapsed:" << _maxElapsed
303+ << "maxElapsedAverage:" << _maxElapsedAverage
304+ << "samples:" << _movingAverage.getSamples();
305+ _lastReport = now;
306+ }
307+
308+#ifdef NDEBUG
309+ if (lastHeartbeatAge > MAX_HEARTBEAT_AGE_USECS) {
310+ qDebug() << "DEADLOCK DETECTED -- "
311+ << "lastHeartbeatAge:" << lastHeartbeatAge
312+ << "[ lastHeartbeat :" << lastHeartbeat
313+ << "now:" << now << " ]"
314+ << "elapsedMovingAverage:" << elapsedMovingAverage
315+ << "maxElapsed:" << _maxElapsed
316+ << "maxElapsedAverage:" << _maxElapsedAverage
317+ << "samples:" << _movingAverage.getSamples();
318+ deadlockDetectionCrash();
319+ }
320+#endif
321+ }
322+ }
323+
324+ static std::atomic<uint64_t> _heartbeat;
325+ static std::atomic<uint64_t> _lastReport;
326+ static std::atomic<uint64_t> _maxElapsed;
327+ static std::atomic<int> _maxElapsedAverage;
328+ static ThreadSafeMovingAverage<int, HEARTBEAT_SAMPLES> _movingAverage;
329+
330+ bool _quit { false };
331+};
332+
333+std::atomic<uint64_t> DeadlockWatchdogThread::_heartbeat;
334+std::atomic<uint64_t> DeadlockWatchdogThread::_lastReport;
335+std::atomic<uint64_t> DeadlockWatchdogThread::_maxElapsed;
336+std::atomic<int> DeadlockWatchdogThread::_maxElapsedAverage;
337+ThreadSafeMovingAverage<int, DeadlockWatchdogThread::HEARTBEAT_SAMPLES> DeadlockWatchdogThread::_movingAverage;
338+
239339 #ifdef Q_OS_WIN
240340 class MyNativeEventFilter : public QAbstractNativeEventFilter {
241341 public:
@@ -317,7 +417,7 @@ bool setupEssentials(int& argc, char** argv) { · 75 unmodified lines
317417
318418 Setting::preInit();
319419
320− CrashHandler::checkForAndHandleCrash();
420+ bool previousSessionCrashed = CrashHandler::checkForAndHandleCrash();
321421 CrashHandler::writeRunningMarkerFiler();
322422 qAddPostRoutine(CrashHandler::deleteRunningMarkerFile);
323423
@@ -372,14 +472,14 @@ bool setupEssentials(int& argc, char** argv) { · 48 unmodified lines
372472 DependencyManager::set<AutoUpdater>();
373473 DependencyManager::set<PathUtils>();
374474 DependencyManager::set<InterfaceActionFactory>();
375− DependencyManager::set<AssetClient>();
376475 DependencyManager::set<AudioInjectorManager>();
377476 DependencyManager::set<MessagesClient>();
378477 DependencyManager::set<UserInputMapper>();
379478 DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>();
380479 DependencyManager::set<InterfaceParentFinder>();
381480 DependencyManager::set<EntityTreeRenderer>(true, qApp, qApp);
382− return true;
481+ DependencyManager::set<CompositorHelper>();
482+ return previousSessionCrashed;
383483 }
384484
385485 // FIXME move to header, or better yet, design some kind of UI manager
@@ -400,10 +500,13 @@ PluginContainer* _pluginContainer; · 14 unmodified lines
400500 OffscreenGLCanvas* _chromiumShareContext { nullptr };
401501 Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context);
402502
503+Setting::Handle<int> sessionRunTime{ "sessionRunTime", 0 };
504+
403505 Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
404506 QApplication(argc, argv),
405− _dependencyManagerIsSetup(setupEssentials(argc, argv)),
406507 _window(new MainWindow(desktop())),
508+ _sessionRunTimer(startupTimer),
509+ _previousSessionCrashed(setupEssentials(argc, argv)),
407510 _undoStackScriptingInterface(&_undoStack),
408511 _frameCount(0),
409512 _fps(60.0f),
@@ -425,11 +528,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 15 unmodified lines
425528 _maxOctreePPS(maxOctreePacketsPerSecond.get()),
426529 _lastFaceTrackerUpdate(0)
427530 {
428− // FIXME this may be excessivly conservative. On the other hand
531+ // FIXME this may be excessivly conservative. On the other hand
429532 // maybe I'm used to having an 8-core machine
430− // Perhaps find the ideal thread count and subtract 2 or 3
533+ // Perhaps find the ideal thread count and subtract 2 or 3
431534 // (main thread, present thread, random OS load)
432− // More threads == faster concurrent loads, but also more concurrent
535+ // More threads == faster concurrent loads, but also more concurrent
433536 // load on the GPU until we can serialize GPU transfers (off the main thread)
434537 QThreadPool::globalInstance()->setMaxThreadCount(2);
435538 thread()->setPriority(QThread::HighPriority);
@@ -458,6 +561,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 22 unmodified lines
458561
459562 auto nodeList = DependencyManager::get<NodeList>();
460563
564+ // Set up a watchdog thread to intentionally crash the application on deadlocks
565+ _deadlockWatchdogThread = new DeadlockWatchdogThread();
566+ _deadlockWatchdogThread->start();
567+
461568 qCDebug(interfaceapp) << "[VERSION] Build sequence:" << qPrintable(applicationVersion());
462569
463570 _bookmarks = new Bookmarks(); // Before setting up the menu
@@ -490,9 +597,18 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 26 unmodified lines
490597 audioThread->setObjectName("Audio Thread");
491598
492599 auto audioIO = DependencyManager::get<AudioClient>();
600+ audioIO->setPositionGetter([]{
601+ auto avatarManager = DependencyManager::get<AvatarManager>();
602+ auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr;
603+
604+ return myAvatar ? myAvatar->getPositionForAudio() : Vectors::ZERO;
605+ });
606+ audioIO->setOrientationGetter([]{
607+ auto avatarManager = DependencyManager::get<AvatarManager>();
608+ auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr;
493609
494− audioIO->setPositionGetter([this]{ return getMyAvatar()->getPositionForAudio(); });
495− audioIO->setOrientationGetter([this]{ return getMyAvatar()->getOrientationForAudio(); });
610+ return myAvatar ? myAvatar->getOrientationForAudio() : Quaternions::IDENTITY;
611+ });
496612
497613 audioIO->moveToThread(audioThread);
498614 recording::Frame::registerFrameHandler(AudioConstants::getAudioFrameName(), [=](recording::Frame::ConstPointer frame) {
@@ -508,7 +624,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 9 unmodified lines
508624 });
509625
510626 auto& audioScriptingInterface = AudioScriptingInterface::getInstance();
511−
512627 connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start);
513628 connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit);
514629 connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater);
@@ -530,13 +645,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 15 unmodified lines
530645
531646 audioThread->start();
532647
533− // Setup AssetClient
534− auto assetClient = DependencyManager::get<AssetClient>();
535− QThread* assetThread = new QThread;
536− assetThread->setObjectName("Asset Thread");
537− assetClient->moveToThread(assetThread);
538− connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init);
539− assetThread->start();
648+ ResourceManager::init();
649+ // Make sure we don't time out during slow operations at startup
650+ updateHeartbeat();
540651
541652 // Setup MessagesClient
542653 auto messagesClient = DependencyManager::get<MessagesClient>();
@@ -549,18 +660,17 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 6 unmodified lines
549660 const DomainHandler& domainHandler = nodeList->getDomainHandler();
550661
551662 connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
552− connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(connectedToDomain(const QString&)));
663+ connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain()));
553664 connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle()));
554665 connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle()));
555666 connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));
556− connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived);
557667
558668 // update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one
559− const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000;
669+ const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * MSECS_PER_SEC;
560670
561671 auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
562672 connect(&locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);
563− connect(&locationUpdateTimer, &QTimer::timeout,
673+ connect(&locationUpdateTimer, &QTimer::timeout,
564674 DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);
565675 locationUpdateTimer.start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS);
566676
@@ -570,6 +680,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 3 unmodified lines
570680
571681 connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded);
572682 connect(nodeList.data(), &NodeList::nodeKilled, this, &Application::nodeKilled);
683+ connect(nodeList.data(), &NodeList::nodeActivated, this, &Application::nodeActivated);
573684 connect(nodeList.data(), &NodeList::uuidChanged, getMyAvatar(), &MyAvatar::setSessionUUID);
574685 connect(nodeList.data(), &NodeList::uuidChanged, this, &Application::setSessionUUID);
575686 connect(nodeList.data(), &NodeList::limitOfSilentDomainCheckInsReached, nodeList.data(), &NodeList::reset);
@@ -578,7 +689,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 2 unmodified lines
578689 // connect to appropriate slots on AccountManager
579690 AccountManager& accountManager = AccountManager::getInstance();
580691
581− const qint64 BALANCE_UPDATE_INTERVAL_MSECS = 5 * 1000;
692+ const qint64 BALANCE_UPDATE_INTERVAL_MSECS = 5 * MSECS_PER_SEC;
582693
583694 connect(&balanceUpdateTimer, &QTimer::timeout, &accountManager, &AccountManager::updateBalance);
584695 balanceUpdateTimer.start(BALANCE_UPDATE_INTERVAL_MSECS);
@@ -590,8 +701,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 5 unmodified lines
590701 connect(&accountManager, &AccountManager::usernameChanged, this, &Application::updateWindowTitle);
591702
592703 // set the account manager's root URL and trigger a login request if we don't have the access token
704+ accountManager.setIsAgent(true);
593705 accountManager.setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL);
594− UserActivityLogger::getInstance().launch(applicationVersion());
706+
707+ // sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.
708+ // The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
709+ UserActivityLogger::getInstance().launch(applicationVersion(), _previousSessionCrashed, sessionRunTime.get());
595710
596711 // once the event loop has started, check and signal for an access token
597712 QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection);
@@ -604,7 +719,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 6 unmodified lines
604719
605720 connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle);
606721 connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress);
607−
722+
608723 // Save avatar location immediately after a teleport.
609724 connect(getMyAvatar(), &MyAvatar::positionGoneTo,
610725 DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);
@@ -625,7 +740,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 14 unmodified lines
625740 getEntities()->reloadEntityScripts();
626741 }, Qt::QueuedConnection);
627742
628− connect(scriptEngines, &ScriptEngines::scriptLoadError,
743+ connect(scriptEngines, &ScriptEngines::scriptLoadError,
629744 scriptEngines, [](const QString& filename, const QString& error){
630745 OffscreenUi::warning(nullptr, "Error Loading Script", filename + " failed to load.");
631746 }, Qt::QueuedConnection);
@@ -646,16 +761,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 14 unmodified lines
646761 connect(&identityPacketTimer, &QTimer::timeout, getMyAvatar(), &MyAvatar::sendIdentityPacket);
647762 identityPacketTimer.start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS);
648763
649− QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
650− QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
651− QNetworkDiskCache* cache = new QNetworkDiskCache();
652− cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE);
653− cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache");
654− networkAccessManager.setCache(cache);
655−
656764 ResourceCache::setRequestLimit(3);
657765
658766 _glWidget = new GLCanvas();
767+ getApplicationCompositor().setRenderingWidget(_glWidget);
659768 _window->setCentralWidget(_glWidget);
660769
661770 _window->restoreGeometry();
@@ -663,15 +772,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 1 unmodified line
663772
664773 _glWidget->setFocusPolicy(Qt::StrongFocus);
665774 _glWidget->setFocus();
775+
666776 #ifdef Q_OS_MAC
667− // OSX doesn't seem to provide for hiding the cursor only on the GL widget
668− _window->setCursor(Qt::BlankCursor);
777+ auto cursorTarget = _window; // OSX doesn't seem to provide for hiding the cursor only on the GL widget
669778 #else
670− // On windows and linux, hiding the top level cursor also means it's invisible
671− // when hovering over the window menu, which is a pain, so only hide it for
672− // the GL surface
673− _glWidget->setCursor(Qt::BlankCursor);
779+ // On windows and linux, hiding the top level cursor also means it's invisible when hovering over the
780+ // window menu, which is a pain, so only hide it for the GL surface
781+ auto cursorTarget = _glWidget;
674782 #endif
783+ cursorTarget->setCursor(Qt::BlankCursor);
675784
676785 // enable mouse tracking; otherwise, we only get drag events
677786 _glWidget->setMouseTracking(true);
@@ -687,8 +796,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 9 unmodified lines
687796 _offscreenContext->create(_glWidget->context()->contextHandle());
688797 _offscreenContext->makeCurrent();
689798 initializeGL();
690−
691799 _offscreenContext->makeCurrent();
800+ // Make sure we don't time out during slow operations at startup
801+ updateHeartbeat();
692802
693803 // Tell our entity edit sender about our known jurisdictions
694804 _entityEditSender.setServerJurisdictions(&_entityServerJurisdictions);
@@ -699,6 +809,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 4 unmodified lines
699809 _entityEditSender.setPacketsPerSecond(3000); // super high!!
700810
701811 _overlays.init(); // do this before scripts load
812+ // Make sure we don't time out during slow operations at startup
813+ updateHeartbeat();
702814
703815 connect(this, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()));
704816
@@ -709,7 +821,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 4 unmodified lines
709821 connect(&nodeList->getPacketReceiver(), &PacketReceiver::dataReceived,
710822 bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData);
711823
712− connect(&getMyAvatar()->getSkeletonModel(), &SkeletonModel::skeletonLoaded,
824+ // FIXME -- I'm a little concerned about this.
825+ connect(getMyAvatar()->getSkeletonModel().get(), &SkeletonModel::skeletonLoaded,
713826 this, &Application::checkSkeleton, Qt::QueuedConnection);
714827
715828 // Setup the userInputMapper with the actions
@@ -786,7 +899,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 70 unmodified lines
786899 }
787900
788901 if (action == controller::toInt(controller::Action::RETICLE_CLICK)) {
789− auto reticlePos = _compositor.getReticlePosition();
902+ auto reticlePos = getApplicationCompositor().getReticlePosition();
790903 QPoint globalPos(reticlePos.x, reticlePos.y);
791904
792905 // FIXME - it would be nice if this was self contained in the _compositor or Reticle class
@@ -809,14 +922,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 16 unmodified lines
809922 } else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) {
810923 cycleCamera();
811924 } else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) {
812− auto reticlePosition = _compositor.getReticlePosition();
925+ auto reticlePosition = getApplicationCompositor().getReticlePosition();
813926 offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y)));
814927 } else if (action == controller::toInt(controller::Action::RETICLE_X)) {
815− auto oldPos = _compositor.getReticlePosition();
816− _compositor.setReticlePosition({ oldPos.x + state, oldPos.y });
928+ auto oldPos = getApplicationCompositor().getReticlePosition();
929+ getApplicationCompositor().setReticlePosition({ oldPos.x + state, oldPos.y });
817930 } else if (action == controller::toInt(controller::Action::RETICLE_Y)) {
818− auto oldPos = _compositor.getReticlePosition();
819− _compositor.setReticlePosition({ oldPos.x, oldPos.y + state });
931+ auto oldPos = getApplicationCompositor().getReticlePosition();
932+ getApplicationCompositor().setReticlePosition({ oldPos.x, oldPos.y + state });
933+ } else if (action == controller::toInt(controller::Action::TOGGLE_OVERLAY)) {
934+ toggleOverlays();
820935 }
821936 }
822937 });
@@ -825,7 +940,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 2 unmodified lines
825940 _applicationStateDevice = std::make_shared<controller::StateController>();
826941
827942 _applicationStateDevice->addInputVariant(QString("InHMD"), controller::StateController::ReadLambda([]() -> float {
828− return (float)qApp->getAvatarUpdater()->isHMDMode();
943+ return (float)qApp->isHMDMode();
829944 }));
830945 _applicationStateDevice->addInputVariant(QString("SnapTurn"), controller::StateController::ReadLambda([]() -> float {
831946 return (float)qApp->getMyAvatar()->getSnapTurn();
@@ -849,8 +964,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 17 unmodified lines
849964 scriptEngines->setScriptsLocation(scriptEngines->getScriptsLocation());
850965 // do this as late as possible so that all required subsystems are initialized
851966 scriptEngines->loadScripts();
967+ // Make sure we don't time out during slow operations at startup
968+ updateHeartbeat();
852969
853970 loadSettings();
971+ // Make sure we don't time out during slow operations at startup
972+ updateHeartbeat();
973+
854974 int SAVE_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND; // Let's save every seconds for now
855975 connect(&_settingsTimer, &QTimer::timeout, this, &Application::saveSettings);
856976 connect(&_settingsThread, SIGNAL(started()), &_settingsTimer, SLOT(start()));
@@ -904,9 +1024,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 47 unmodified lines
9041024 SpacemouseManager::getInstance().init();
9051025 #endif
9061026
907− auto& packetReceiver = nodeList->getPacketReceiver();
908− packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket");
909−
9101027 // If the user clicks an an entity, we will check that it's an unlocked web entity, and if so, set the focus to it
9111028 auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
9121029 connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity,
@@ -966,8 +1083,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 53 unmodified lines
9661083 }
9671084 });
9681085
1086+ // Make sure we don't time out during slow operations at startup
1087+ updateHeartbeat();
1088+
9691089 connect(this, &Application::applicationStateChanged, this, &Application::activeChanged);
9701090 qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0);
1091+
9711092 _idleTimer = new QTimer(this);
9721093 connect(_idleTimer, &QTimer::timeout, [=] {
9731094 idle(usecTimestampNow());
@@ -976,14 +1097,49 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 2 unmodified lines
9761097 disconnect(_idleTimer);
9771098 });
9781099 // Setting the interval to zero forces this to get called whenever there are no messages
979− // in the queue, which can be pretty damn frequent. Hence the idle function has a bunch
1100+ // in the queue, which can be pretty damn frequent. Hence the idle function has a bunch
9801101 // of logic to abort early if it's being called too often.
9811102 _idleTimer->start(0);
9821103 }
9831104
1105+
1106+void Application::checkChangeCursor() {
1107+ QMutexLocker locker(&_changeCursorLock);
1108+ if (_cursorNeedsChanging) {
1109+#ifdef Q_OS_MAC
1110+ auto cursorTarget = _window; // OSX doesn't seem to provide for hiding the cursor only on the GL widget
1111+#else
1112+ // On windows and linux, hiding the top level cursor also means it's invisible when hovering over the
1113+ // window menu, which is a pain, so only hide it for the GL surface
1114+ auto cursorTarget = _glWidget;
1115+#endif
1116+ cursorTarget->setCursor(_desiredCursor);
1117+
1118+ _cursorNeedsChanging = false;
1119+ }
1120+}
1121+
1122+void Application::showCursor(const QCursor& cursor) {
1123+ QMutexLocker locker(&_changeCursorLock);
1124+ _desiredCursor = cursor;
1125+ _cursorNeedsChanging = true;
1126+}
1127+
1128+void Application::updateHeartbeat() {
1129+ static_cast<DeadlockWatchdogThread*>(_deadlockWatchdogThread)->updateHeartbeat();
1130+}
1131+
9841132 void Application::aboutToQuit() {
9851133 emit beforeAboutToQuit();
9861134
1135+ foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
1136+ QString name = inputPlugin->getName();
1137+ QAction* action = Menu::getInstance()->getActionForOption(name);
1138+ if (action->isChecked()) {
1139+ inputPlugin->deactivate();
1140+ }
1141+ }
1142+
9871143 getActiveDisplayPlugin()->deactivate();
9881144
9891145 _aboutToQuit = true;
@@ -1024,11 +1180,10 @@ void Application::cleanupBeforeQuit() { · 34 unmodified lines
10241180 getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
10251181 DependencyManager::get<ScriptEngines>()->saveScripts();
10261182 DependencyManager::get<ScriptEngines>()->shutdownScripting(); // stop all currently running global scripts
1027− DependencyManager::destroy<ScriptEngines>();
1183+ DependencyManager::destroy<ScriptEngines>();
10281184
10291185 // first stop all timers directly or by invokeMethod
10301186 // depending on what thread they run in
1031− _avatarUpdate->terminate();
10321187 locationUpdateTimer.stop();
10331188 balanceUpdateTimer.stop();
10341189 identityPacketTimer.stop();
@@ -1062,13 +1217,6 @@ void Application::cleanupBeforeQuit() { · 27 unmodified lines
10621217 DependencyManager::destroy<OffscreenUi>();
10631218 }
10641219
1065−void Application::emptyLocalCache() {
1066− if (auto cache = NetworkAccessManager::getInstance().cache()) {
1067− qDebug() << "DiskCacheEditor::clear(): Clearing disk cache.";
1068− cache->clear();
1069− }
1070−}
1071−
10721220 Application::~Application() {
10731221 EntityTreePointer tree = getEntities()->getTree();
10741222 tree->setSimulation(NULL);
@@ -1076,20 +1224,10 @@ Application::~Application() { · 1 unmodified line
10761224 _octreeProcessor.terminate();
10771225 _entityEditSender.terminate();
10781226
1079− Menu::getInstance()->deleteLater();
1080−
10811227 _physicsEngine->setCharacterController(NULL);
10821228
10831229 ModelEntityItem::cleanupLoadedAnimations();
10841230
1085− foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
1086− QString name = inputPlugin->getName();
1087− QAction* action = Menu::getInstance()->getActionForOption(name);
1088− if (action->isChecked()) {
1089− inputPlugin->deactivate();
1090− }
1091− }
1092−
10931231 // remove avatars from physics engine
10941232 DependencyManager::get<AvatarManager>()->clearOtherAvatars();
10951233 VectorOfMotionStates motionStates;
@@ -1106,11 +1244,7 @@ Application::~Application() { · 10 unmodified lines
11061244 DependencyManager::destroy<ScriptCache>();
11071245 DependencyManager::destroy<SoundCache>();
11081246
1109− // cleanup the AssetClient thread
1110− QThread* assetThread = DependencyManager::get<AssetClient>()->thread();
1111− DependencyManager::destroy<AssetClient>();
1112− assetThread->quit();
1113− assetThread->wait();
1247+ ResourceManager::cleanup();
11141248
11151249 QThread* nodeThread = DependencyManager::get<NodeList>()->thread();
11161250
@@ -1126,6 +1260,10 @@ Application::~Application() { · 9 unmodified lines
11261260 #if 0
11271261 ConnexionClient::getInstance().destroy();
11281262 #endif
1263+ // The window takes ownership of the menu, so this has the side effect of destroying it.
1264+ _window->setMenuBar(nullptr);
1265+
1266+ _window->deleteLater();
11291267
11301268 qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages
11311269 }
@@ -1199,7 +1337,7 @@ void Application::initializeUi() { · 67 unmodified lines
11991337 // OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to
12001338 // support the window management and scripting proxies for VR use
12011339 offscreenUi->createDesktop(QString("hifi/Desktop.qml"));
1202−
1340+
12031341 // FIXME either expose so that dialogs can set this themselves or
12041342 // do better detection in the offscreen UI of what has focus
12051343 offscreenUi->setNavigationFocused(false);
@@ -1212,10 +1350,10 @@ void Application::initializeUi() { · 6 unmodified lines
12121350
12131351 setupPreferences();
12141352
1215− // For some reason there is already an "Application" object in the QML context,
1353+ // For some reason there is already an "Application" object in the QML context,
12161354 // though I can't find it. Hence, "ApplicationInterface"
12171355 rootContext->setContextProperty("SnapshotUploader", new SnapshotUploader());
1218− rootContext->setContextProperty("ApplicationInterface", this);
1356+ rootContext->setContextProperty("ApplicationInterface", this);
12191357 rootContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCache>().data());
12201358 rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance());
12211359 rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
@@ -1229,6 +1367,7 @@ void Application::initializeUi() { · 7 unmodified lines
12291367 rootContext->setContextProperty("Quat", new Quat());
12301368 rootContext->setContextProperty("Vec3", new Vec3());
12311369 rootContext->setContextProperty("Uuid", new ScriptUUID());
1370+ rootContext->setContextProperty("Assets", new AssetMappingsScriptingInterface());
12321371
12331372 rootContext->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
12341373
@@ -1258,15 +1397,17 @@ void Application::initializeUi() { · 23 unmodified lines
12581397 rootContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
12591398 rootContext->setContextProperty("Scene", DependencyManager::get<SceneScriptingInterface>().data());
12601399 rootContext->setContextProperty("Render", _renderEngine->getConfiguration().get());
1261− rootContext->setContextProperty("Reticle", _compositor.getReticleInterface());
1400+ rootContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface());
1401+
1402+ rootContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor());
12621403
12631404 _glWidget->installEventFilter(offscreenUi.data());
12641405 offscreenUi->setMouseTranslator([=](const QPointF& pt) {
12651406 QPointF result = pt;
12661407 auto displayPlugin = getActiveDisplayPlugin();
12671408 if (displayPlugin->isHmd()) {
1268− _compositor.handleRealMouseMoveEvent(false);
1269− auto resultVec = _compositor.getReticlePosition();
1409+ getApplicationCompositor().handleRealMouseMoveEvent(false);
1410+ auto resultVec = getApplicationCompositor().getReticlePosition();
12701411 result = QPointF(resultVec.x, resultVec.y);
12711412 }
12721413 return result.toPoint();
@@ -1293,10 +1434,30 @@ void Application::initializeUi() { · 20 unmodified lines
12931434 _touchscreenDevice = std::dynamic_pointer_cast<TouchscreenDevice>(inputPlugin);
12941435 }
12951436 }
1437+ _window->setMenuBar(new Menu());
12961438 updateInputModes();
1439+
1440+ auto compositorHelper = DependencyManager::get<CompositorHelper>();
1441+ connect(compositorHelper.data(), &CompositorHelper::allowMouseCaptureChanged, [=] {
1442+ if (isHMDMode()) {
1443+ showCursor(compositorHelper->getAllowMouseCapture() ? Qt::BlankCursor : Qt::ArrowCursor);
1444+ }
1445+ });
12971446 }
12981447
12991448 void Application::paintGL() {
1449+
1450+ updateHeartbeat();
1451+
1452+ // Some plugins process message events, potentially leading to
1453+ // re-entering a paint event. don't allow further processing if this
1454+ // happens
1455+ if (_inPaint) {
1456+ return;
1457+ }
1458+ _inPaint = true;
1459+ Finally clearFlagLambda([this] { _inPaint = false; });
1460+
13001461 // paintGL uses a queued connection, so we can get messages from the queue even after we've quit
13011462 // and the plugins have shutdown
13021463 if (_aboutToQuit) {
@@ -1322,33 +1483,26 @@ void Application::paintGL() { · 19 unmodified lines
13221483 _lastFramesPerSecondUpdate = now;
13231484 }
13241485
1325− if (_isGLInitialized) {
1326− idle(now);
1327− }
1328−
1329− PROFILE_RANGE(__FUNCTION__);
1486+ PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, (uint64_t)_frameCount);
13301487 PerformanceTimer perfTimer("paintGL");
13311488
13321489 if (nullptr == _displayPlugin) {
13331490 return;
13341491 }
13351492
1336− // Some plugins process message events, potentially leading to
1337− // re-entering a paint event. don't allow further processing if this
1338− // happens
1339− if (_inPaint) {
1340− return;
1341− }
1342− _inPaint = true;
1343− Finally clearFlagLambda([this] { _inPaint = false; });
1344−
13451493 auto displayPlugin = getActiveDisplayPlugin();
13461494 // FIXME not needed anymore?
13471495 _offscreenContext->makeCurrent();
13481496
1497+ displayPlugin->beginFrameRender(_frameCount);
1498+
13491499 // update the avatar with a fresh HMD pose
13501500 getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose());
13511501
1502+ // update sensorToWorldMatrix for camera and hand controllers
1503+ getMyAvatar()->updateSensorToWorldMatrix();
1504+
1505+
13521506 auto lodManager = DependencyManager::get<LODManager>();
13531507
13541508
@@ -1390,9 +1544,10 @@ void Application::paintGL() { · 35 unmodified lines
13901544 QSize size = getDeviceSize();
13911545 renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height());
13921546 _applicationOverlay.renderOverlay(&renderArgs);
1393− gpu::FramebufferPointer overlayFramebuffer = _applicationOverlay.getOverlayFramebuffer();
1394−
1395−
1547+ auto overlayTexture = _applicationOverlay.acquireOverlay();
1548+ if (overlayTexture) {
1549+ displayPlugin->submitOverlayTexture(overlayTexture);
1550+ }
13961551 }
13971552
13981553 glm::vec3 boomOffset;
@@ -1402,7 +1557,6 @@ void Application::paintGL() { · 3 unmodified lines
14021557 auto myAvatar = getMyAvatar();
14031558 boomOffset = myAvatar->getScale() * myAvatar->getBoomLength() * -IDENTITY_FRONT;
14041559
1405− myAvatar->startCapture();
14061560 if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
14071561 Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN);
14081562 Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN));
@@ -1490,9 +1644,10 @@ void Application::paintGL() { · 81 unmodified lines
14901644 if (!isHMDMode()) {
14911645 _myCamera.update(1.0f / _fps);
14921646 }
1493− myAvatar->endCapture();
14941647 }
14951648
1649+ getApplicationCompositor().setFrameInfo(_frameCount, _myCamera.getTransform());
1650+
14961651 // Primary rendering pass
14971652 auto framebufferCache = DependencyManager::get<FramebufferCache>();
14981653 const QSize size = framebufferCache->getFrameBufferSize();
@@ -1523,12 +1678,7 @@ void Application::paintGL() { · 24 unmodified lines
15231678 auto baseProjection = renderArgs._viewFrustum->getProjection();
15241679 auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
15251680 float IPDScale = hmdInterface->getIPDScale();
1526−
1527− // Tell the plugin what pose we're using to render. In this case we're just using the
1528− // unmodified head pose because the only plugin that cares (the Oculus plugin) uses it
1529− // for rotational timewarp. If we move to support positonal timewarp, we need to
1530− // ensure this contains the full pose composed with the eye offsets.
1531− mat4 headPose = displayPlugin->getHeadPose(_frameCount);
1681+ mat4 headPose = displayPlugin->getHeadPose();
15321682
15331683 // FIXME we probably don't need to set the projection matrix every frame,
15341684 // only when the display plugin changes (or in non-HMD modes when the user
@@ -1545,7 +1695,11 @@ void Application::paintGL() { · 10 unmodified lines
15451695 mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale);
15461696 eyeOffsets[eye] = eyeOffsetTransform;
15471697
1548− displayPlugin->setEyeRenderPose(_frameCount, eye, headPose);
1698+ // Tell the plugin what pose we're using to render. In this case we're just using the
1699+ // unmodified head pose because the only plugin that cares (the Oculus plugin) uses it
1700+ // for rotational timewarp. If we move to support positonal timewarp, we need to
1701+ // ensure this contains the full pose composed with the eye offsets.
1702+ displayPlugin->setEyeRenderPose(_frameCount, eye, headPose * glm::inverse(eyeOffsetTransform));
15491703
15501704 eyeProjections[eye] = displayPlugin->getEyeProjection(eye, baseProjection);
15511705 });
@@ -1559,44 +1713,12 @@ void Application::paintGL() { · 7 unmodified lines
15591713 renderArgs._context->enableStereo(false);
15601714 }
15611715
1562− // Overlay Composition, needs to occur after screen space effects have completed
1563− // FIXME migrate composition into the display plugins
1564− {
1565− PROFILE_RANGE(__FUNCTION__ "/compositor");
1566− PerformanceTimer perfTimer("compositor");
1567−
1568− auto primaryFbo = finalFramebuffer;
1569−
1570− glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFbo));
1571− if (displayPlugin->isStereo()) {
1572− QRect currentViewport(QPoint(0, 0), QSize(size.width() / 2, size.height()));
1573− glClear(GL_DEPTH_BUFFER_BIT);
1574− for_each_eye([&](Eye eye) {
1575− renderArgs._viewport = toGlm(currentViewport);
1576− if (displayPlugin->isHmd()) {
1577− _compositor.displayOverlayTextureHmd(&renderArgs, eye);
1578− } else {
1579− _compositor.displayOverlayTexture(&renderArgs);
1580− }
1581− }, [&] {
1582− currentViewport.moveLeft(currentViewport.width());
1583− });
1584− } else {
1585− glViewport(0, 0, size.width(), size.height());
1586− _compositor.displayOverlayTexture(&renderArgs);
1587− }
1588− }
1589−
15901716 // deliver final composited scene to the display plugin
15911717 {
15921718 PROFILE_RANGE(__FUNCTION__ "/pluginOutput");
15931719 PerformanceTimer perfTimer("pluginOutput");
15941720
1595− auto finalTexturePointer = finalFramebuffer->getRenderBuffer(0);
1596−
1597− GLuint finalTexture = gpu::GLBackend::getTextureID(finalTexturePointer);
1598− Q_ASSERT(0 != finalTexture);
1599−
1721+ auto finalTexture = finalFramebuffer->getRenderBuffer(0);
16001722 Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture));
16011723 _lockedFramebufferMap[finalTexture] = finalFramebuffer;
16021724
@@ -1604,10 +1726,9 @@ void Application::paintGL() { · 1 unmodified line
16041726 {
16051727 PROFILE_RANGE(__FUNCTION__ "/pluginSubmitScene");
16061728 PerformanceTimer perfTimer("pluginSubmitScene");
1607− displayPlugin->submitSceneTexture(_frameCount, finalTexture, toGlm(size));
1729+ displayPlugin->submitSceneTexture(_frameCount, finalTexture);
16081730 }
16091731 Q_ASSERT(isCurrentContext(_offscreenContext->getContext()));
1610−
16111732 }
16121733
16131734 {
@@ -1620,7 +1741,6 @@ void Application::paintGL() { · 6 unmodified lines
16201741 }
16211742
16221743 _lastInstantaneousFps = instantaneousFps;
1623− _pendingPaint = false;
16241744 }
16251745
16261746 void Application::runTests() {
@@ -1704,6 +1824,11 @@ bool Application::importSVOFromURL(const QString& urlString) { · 77 unmodified lines
17041824 }
17051825
17061826 bool Application::event(QEvent* event) {
1827+
1828+ if (!Menu::getInstance()) {
1829+ return false;
1830+ }
1831+
17071832 if ((int)event->type() == (int)Lambda) {
17081833 ((LambdaEvent*)event)->call();
17091834 return true;
@@ -1808,7 +1933,7 @@ bool Application::event(QEvent* event) { · 98 unmodified lines
18081933 bool Application::eventFilter(QObject* object, QEvent* event) {
18091934
18101935 if (event->type() == QEvent::Leave) {
1811− _compositor.handleLeaveEvent();
1936+ getApplicationCompositor().handleLeaveEvent();
18121937 }
18131938
18141939 if (event->type() == QEvent::ShortcutOverride) {
@@ -1862,6 +1987,28 @@ void Application::keyPressEvent(QKeyEvent* event) { · 47 unmodified lines
18621987 }
18631988 break;
18641989
1990+ case Qt::Key_1:
1991+ case Qt::Key_2:
1992+ case Qt::Key_3:
1993+ case Qt::Key_4:
1994+ case Qt::Key_5:
1995+ case Qt::Key_6:
1996+ case Qt::Key_7:
1997+ if (isMeta || isOption) {
1998+ unsigned int index = static_cast<unsigned int>(event->key() - Qt::Key_1);
1999+ auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins();
2000+ if (index < displayPlugins.size()) {
2001+ auto targetPlugin = displayPlugins.at(index);
2002+ QString targetName = targetPlugin->getName();
2003+ auto menu = Menu::getInstance();
2004+ QAction* action = menu->getActionForOption(targetName);
2005+ if (action && !action->isChecked()) {
2006+ action->trigger();
2007+ }
2008+ }
2009+ }
2010+ break;
2011+
18652012 case Qt::Key_X:
18662013 if (isShifted && isMeta) {
18672014 auto offscreenUi = DependencyManager::get<OffscreenUi>();
@@ -1871,6 +2018,12 @@ void Application::keyPressEvent(QKeyEvent* event) { · 3 unmodified lines
18712018 }
18722019 break;
18732020
2021+ case Qt::Key_Y:
2022+ if (isShifted && isMeta) {
2023+ getActiveDisplayPlugin()->cycleDebugOutput();
2024+ }
2025+ break;
2026+
18742027 case Qt::Key_B:
18752028 if (isMeta) {
18762029 auto offscreenUi = DependencyManager::get<OffscreenUi>();
@@ -2022,21 +2175,22 @@ void Application::keyPressEvent(QKeyEvent* event) { · 145 unmodified lines
20222175 if (isShifted) {
20232176 Menu::getInstance()->triggerOption(MenuOption::MiniMirror);
20242177 } else {
2025− Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror));
2026− if (!Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
2178+ bool isMirrorChecked = Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror);
2179+ Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, !isMirrorChecked);
2180+ if (isMirrorChecked) {
20272181 Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true);
20282182 }
20292183 cameraMenuChanged();
20302184 }
20312185 break;
2032− case Qt::Key_P:
2033− Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, !Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson));
2034− Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson));
2035− cameraMenuChanged();
2036− break;
2037− case Qt::Key_O:
2038− _overlayConductor.setEnabled(!_overlayConductor.getEnabled());
2186+ case Qt::Key_P: {
2187+ bool isFirstPersonChecked = Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson);
2188+ Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, !isFirstPersonChecked);
2189+ Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, isFirstPersonChecked);
2190+ cameraMenuChanged();
20392191 break;
2192+ }
2193+
20402194 case Qt::Key_Slash:
20412195 Menu::getInstance()->triggerOption(MenuOption::Stats);
20422196 break;
@@ -2098,7 +2252,7 @@ void Application::keyPressEvent(QKeyEvent* event) { · 55 unmodified lines
20982252 void Application::keyReleaseEvent(QKeyEvent* event) {
20992253 if (event->key() == Qt::Key_Alt && _altPressed && hasFocus()) {
21002254 auto offscreenUi = DependencyManager::get<OffscreenUi>();
2101− auto reticlePosition = _compositor.getReticlePosition();
2255+ auto reticlePosition = getApplicationCompositor().getReticlePosition();
21022256 offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y)));
21032257 }
21042258
@@ -2187,13 +2341,6 @@ void Application::maybeToggleMenuVisible(QMouseEvent* event) { · 82 unmodified lines
21872341 #endif
21882342 }
21892343
2190−/// called by ApplicationCompositor when in HMD mode and we're faking our mouse movement
2191−void Application::fakeMouseEvent(QMouseEvent* event) {
2192− _fakedMouseEvent = true;
2193− sendEvent(_glWidget, event);
2194− _fakedMouseEvent = false;
2195−}
2196−
21972344 void Application::mouseMoveEvent(QMouseEvent* event) {
21982345 PROFILE_RANGE(__FUNCTION__);
21992346
@@ -2203,17 +2350,16 @@ void Application::mouseMoveEvent(QMouseEvent* event) { · 3 unmodified lines
22032350
22042351 maybeToggleMenuVisible(event);
22052352
2353+ auto& compositor = getApplicationCompositor();
22062354 // if this is a real mouse event, and we're in HMD mode, then we should use it to move the
22072355 // compositor reticle
2208− if (!_fakedMouseEvent) {
2209− // handleRealMouseMoveEvent() will return true, if we shouldn't process the event further
2210− if (_compositor.handleRealMouseMoveEvent()) {
2211− return; // bail
2212− }
2356+ // handleRealMouseMoveEvent() will return true, if we shouldn't process the event further
2357+ if (!compositor.fakeEventActive() && compositor.handleRealMouseMoveEvent()) {
2358+ return; // bail
22132359 }
22142360
22152361 auto offscreenUi = DependencyManager::get<OffscreenUi>();
2216− auto eventPosition = _compositor.getMouseEventPosition(event);
2362+ auto eventPosition = compositor.getMouseEventPosition(event);
22172363 QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget);
22182364 auto button = event->button();
22192365 auto buttons = event->buttons();
@@ -2255,7 +2401,7 @@ void Application::mousePressEvent(QMouseEvent* event) { · 35 unmodified lines
22552401 // will consume all keyboard events.
22562402 offscreenUi->unfocusWindows();
22572403
2258− auto eventPosition = _compositor.getMouseEventPosition(event);
2404+ auto eventPosition = getApplicationCompositor().getMouseEventPosition(event);
22592405 QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget);
22602406 QMouseEvent mappedEvent(event->type(),
22612407 transformedPos,
@@ -2301,7 +2447,7 @@ void Application::mouseDoublePressEvent(QMouseEvent* event) { · 39 unmodified lines
23012447 void Application::mouseReleaseEvent(QMouseEvent* event) {
23022448
23032449 auto offscreenUi = DependencyManager::get<OffscreenUi>();
2304− auto eventPosition = _compositor.getMouseEventPosition(event);
2450+ auto eventPosition = getApplicationCompositor().getMouseEventPosition(event);
23052451 QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget);
23062452 QMouseEvent mappedEvent(event->type(),
23072453 transformedPos,
@@ -2451,10 +2597,23 @@ bool Application::acceptSnapshot(const QString& urlString) { · 143 unmodified lines
24512597 static uint32_t _renderedFrameIndex { INVALID_FRAME };
24522598
24532599 void Application::idle(uint64_t now) {
2454− if (_aboutToQuit) {
2600+
2601+ updateHeartbeat();
2602+
2603+ if (_aboutToQuit || _inPaint) {
24552604 return; // bail early, nothing to do here.
24562605 }
2457−
2606+
2607+ // These tasks need to be done on our first idle, because we don't want the showing of
2608+ // overlay subwindows to do a showDesktop() until after the first time through
2609+ static bool firstIdle = true;
2610+ if (firstIdle) {
2611+ firstIdle = false;
2612+ auto offscreenUi = DependencyManager::get<OffscreenUi>();
2613+ connect(offscreenUi.data(), &OffscreenUi::showDesktop, this, &Application::showDesktop);
2614+ _overlayConductor.setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Overlays));
2615+ }
2616+
24582617 auto displayPlugin = getActiveDisplayPlugin();
24592618 // depending on whether we're throttling or not.
24602619 // Once rendering is off on another thread we should be able to have Application::idle run at start(0) in
@@ -2475,38 +2634,30 @@ void Application::idle(uint64_t now) { · 14 unmodified lines
24752634 _renderedFrameIndex = INVALID_FRAME;
24762635 }
24772636
2478− // Nested ifs are for clarity in the logic. Don't collapse them into a giant single if.
2479− // Don't saturate the main thread with rendering, no paint calls until the last one is complete
2480− if (!_pendingPaint) {
2481− // Also no paint calls until the display plugin has increased by at least one frame
2482− // (don't render at 90fps if the display plugin only goes at 60)
2483− if (_renderedFrameIndex == INVALID_FRAME || presentCount > _renderedFrameIndex) {
2484− // Record what present frame we're on
2485− _renderedFrameIndex = presentCount;
2486− // Don't allow paint requests to stack up in the event queue
2487− _pendingPaint = true;
2488− // But when we DO request a paint, get to it as soon as possible: high priority
2489− postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority);
2490− }
2491− }
2492−
2493− // For the rest of idle, we want to cap at the max sim rate, so we might not call
2494− // the remaining idle work every paint frame, or vice versa
2495− // In theory this means we could call idle processing more often than painting,
2496− // but in practice, when the paintGL calls aren't keeping up, there's no room left
2497− // in the main thread to call idle more often than paint.
2498− // This check is mostly to keep idle from burning up CPU cycles by running at
2499− // hundreds of idles per second when the rendering is that fast
2500− if ((timeSinceLastUpdateUs / USECS_PER_MSEC) < CAPPED_SIM_FRAME_PERIOD_MS) {
2501− // No paint this round, but might be time for a new idle, otherwise return
2637+ // Don't saturate the main thread with rendering and simulation,
2638+ // unless display plugin has increased by at least one frame
2639+ if (_renderedFrameIndex == INVALID_FRAME || presentCount > _renderedFrameIndex) {
2640+ // Record what present frame we're on
2641+ _renderedFrameIndex = presentCount;
2642+
2643+ // request a paint, get to it as soon as possible: high priority
2644+ postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority);
2645+ } else {
2646+ // there's no use in simulating or rendering faster then the present rate.
25022647 return;
25032648 }
25042649
2650+ PROFILE_RANGE(__FUNCTION__);
2651+
25052652 // We're going to execute idle processing, so restart the last idle timer
25062653 _lastTimeUpdated.start();
25072654
2655+ checkChangeCursor();
2656+
2657+ Stats::getInstance()->updateStats();
2658+ AvatarInputs::getInstance()->update();
2659+
25082660 {
2509− PROFILE_RANGE(__FUNCTION__);
25102661 static uint64_t lastIdleStart{ now };
25112662 uint64_t idleStartToStartDuration = now - lastIdleStart;
25122663 if (idleStartToStartDuration != 0) {
@@ -2596,7 +2747,7 @@ void Application::setLowVelocityFilter(bool lowVelocityFilter) { · 83 unmodified lines
25962747 }
25972748
25982749 ivec2 Application::getMouse() {
2599− auto reticlePosition = _compositor.getReticlePosition();
2750+ auto reticlePosition = getApplicationCompositor().getReticlePosition();
26002751
26012752 // in the HMD, the reticlePosition is the mouse position
26022753 if (isHMDMode()) {
@@ -2681,43 +2832,50 @@ void Application::calibrateEyeTracker5Points() { · 78 unmodified lines
26812832 }
26822833 #endif
26832834
2684−bool Application::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs) {
2685− QVector<EntityItemPointer> entities;
2835+bool Application::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs, const glm::vec3* givenOffset) {
2836+ QHash<EntityItemID, EntityItemPointer> entities;
26862837
26872838 auto entityTree = getEntities()->getTree();
26882839 auto exportTree = std::make_shared<EntityTree>();
26892840 exportTree->createRootElement();
26902841
26912842 glm::vec3 root(TREE_SCALE, TREE_SCALE, TREE_SCALE);
2692− for (auto entityID : entityIDs) {
2843+ for (auto entityID : entityIDs) { // Gather entities and properties.
26932844 auto entityItem = entityTree->findEntityByEntityItemID(entityID);
26942845 if (!entityItem) {
2846+ qCWarning(interfaceapp) << "Skipping export of" << entityID << "that is not in scene.";
26952847 continue;
26962848 }
26972849
2698− auto properties = entityItem->getProperties();
2699− auto position = properties.getPosition();
2700−
2701− root.x = glm::min(root.x, position.x);
2702− root.y = glm::min(root.y, position.y);
2703− root.z = glm::min(root.z, position.z);
2704−
2705− entities << entityItem;
2850+ if (!givenOffset) {
2851+ EntityItemID parentID = entityItem->getParentID();
2852+ if (parentID.isInvalidID() || !entityIDs.contains(parentID) || !entityTree->findEntityByEntityItemID(parentID)) {
2853+ auto position = entityItem->getPosition(); // If parent wasn't selected, we want absolute position, which isn't in properties.
2854+ root.x = glm::min(root.x, position.x);
2855+ root.y = glm::min(root.y, position.y);
2856+ root.z = glm::min(root.z, position.z);
2857+ }
2858+ }
2859+ entities[entityID] = entityItem;
27062860 }
27072861
27082862 if (entities.size() == 0) {
27092863 return false;
27102864 }
27112865
2712− for (auto entityItem : entities) {
2713− auto properties = entityItem->getProperties();
2714−
2715− properties.setPosition(properties.getPosition() - root);
2716− exportTree->addEntity(entityItem->getEntityItemID(), properties);
2866+ if (givenOffset) {
2867+ root = *givenOffset;
2868+ }
2869+ for (EntityItemPointer& entityDatum : entities) {
2870+ auto properties = entityDatum->getProperties();
2871+ EntityItemID parentID = properties.getParentID();
2872+ if (parentID.isInvalidID()) {
2873+ properties.setPosition(properties.getPosition() - root);
2874+ } else if (!entities.contains(parentID)) {
2875+ entityDatum->globalizeProperties(properties, "Parent %3 of %2 %1 is not selected for export.", -root);
2876+ } // else valid parent -- don't offset
2877+ exportTree->addEntity(entityDatum->getEntityItemID(), properties);
27172878 }
2718−
2719− // remap IDs on export so that we aren't publishing the IDs of entities in our domain
2720− exportTree->remapIDs();
27212879
27222880 exportTree->writeToJSONFile(filename.toLocal8Bit().constData());
27232881
@@ -2727,37 +2885,19 @@ bool Application::exportEntities(const QString& filename, const QVector<EntityIt · 3 unmodified lines
27272885 }
27282886
27292887 bool Application::exportEntities(const QString& filename, float x, float y, float z, float scale) {
2888+ glm::vec3 offset(x, y, z);
27302889 QVector<EntityItemPointer> entities;
2731− getEntities()->getTree()->findEntities(AACube(glm::vec3(x, y, z), scale), entities);
2732−
2733− if (entities.size() > 0) {
2734− glm::vec3 root(x, y, z);
2735− auto exportTree = std::make_shared<EntityTree>();
2736− exportTree->createRootElement();
2737−
2738− for (int i = 0; i < entities.size(); i++) {
2739− EntityItemProperties properties = entities.at(i)->getProperties();
2740− EntityItemID id = entities.at(i)->getEntityItemID();
2741− properties.setPosition(properties.getPosition() - root);
2742− exportTree->addEntity(id, properties);
2743− }
2744−
2745− // remap IDs on export so that we aren't publishing the IDs of entities in our domain
2746− exportTree->remapIDs();
2747−
2748− exportTree->writeToSVOFile(filename.toLocal8Bit().constData());
2749− } else {
2750− qCDebug(interfaceapp) << "No models were selected";
2751− return false;
2890+ QVector<EntityItemID> ids;
2891+ getEntities()->getTree()->findEntities(AACube(offset, scale), entities);
2892+ foreach(EntityItemPointer entity, entities) {
2893+ ids << entity->getEntityItemID();
27522894 }
2753−
2754− // restore the main window's active state
2755− _window->activateWindow();
2756− return true;
2895+ return exportEntities(filename, ids, &offset);
27572896 }
27582897
27592898 void Application::loadSettings() {
27602899
2900+ sessionRunTime.set(0); // Just clean living. We're about to saveSettings, which will update value.
27612901 DependencyManager::get<AudioClient>()->loadSettings();
27622902 DependencyManager::get<LODManager>()->loadSettings();
27632903
@@ -2771,6 +2911,7 @@ void Application::loadSettings() { · 7 unmodified lines
27712911 }
27722912
27732913 void Application::saveSettings() {
2914+ sessionRunTime.set(_sessionRunTimer.elapsed() / MSECS_PER_SEC);
27742915 DependencyManager::get<AudioClient>()->saveSettings();
27752916 DependencyManager::get<LODManager>()->saveSettings();
27762917
@@ -2782,16 +2923,8 @@ void Application::saveSettings() { · 5 unmodified lines
27822923 bool Application::importEntities(const QString& urlOrFilename) {
27832924 _entityClipboard->eraseAllOctreeElements();
27842925
2785− QUrl url(urlOrFilename);
2786−
2787− // if the URL appears to be invalid or relative, then it is probably a local file
2788− if (!url.isValid() || url.isRelative()) {
2789− url = QUrl::fromLocalFile(urlOrFilename);
2790− }
2791−
2792− bool success = _entityClipboard->readFromURL(url.toString());
2926+ bool success = _entityClipboard->readFromURL(urlOrFilename);
27932927 if (success) {
2794− _entityClipboard->remapIDs();
27952928 _entityClipboard->reaverageOctreeElements();
27962929 }
27972930 return success;
@@ -2862,37 +2995,6 @@ void Application::init() { · 64 unmodified lines
28622995 // Make sure any new sounds are loaded as soon as know about them.
28632996 connect(tree.get(), &EntityTree::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound);
28642997 connect(getMyAvatar(), &MyAvatar::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound);
2865−
2866− setAvatarUpdateThreading();
2867−}
2868−
2869−const bool ENABLE_AVATAR_UPDATE_THREADING = false;
2870−void Application::setAvatarUpdateThreading() {
2871− setAvatarUpdateThreading(ENABLE_AVATAR_UPDATE_THREADING);
2872−}
2873−void Application::setRawAvatarUpdateThreading() {
2874− setRawAvatarUpdateThreading(ENABLE_AVATAR_UPDATE_THREADING);
2875−}
2876−void Application::setRawAvatarUpdateThreading(bool isThreaded) {
2877− if (_avatarUpdate) {
2878− if (_avatarUpdate->isThreaded() == isThreaded) {
2879− return;
2880− }
2881− _avatarUpdate->terminate();
2882− }
2883− _avatarUpdate = new AvatarUpdate();
2884− _avatarUpdate->initialize(isThreaded);
2885−}
2886−void Application::setAvatarUpdateThreading(bool isThreaded) {
2887− if (_avatarUpdate && (_avatarUpdate->isThreaded() == isThreaded)) {
2888− return;
2889− }
2890−
2891− if (_avatarUpdate) {
2892− _avatarUpdate->terminate(); // Must be before we shutdown anim graph.
2893− }
2894− _avatarUpdate = new AvatarUpdate();
2895− _avatarUpdate->initialize(isThreaded);
28962998 }
28972999
28983000 void Application::updateLOD() {
@@ -2905,6 +3007,11 @@ void Application::updateLOD() { · 6 unmodified lines
29053007 }
29063008 }
29073009
3010+void Application::pushPreRenderLambda(void* key, std::function<void()> func) {
3011+ std::unique_lock<std::mutex> guard(_preRenderLambdasLock);
3012+ _preRenderLambdas[key] = func;
3013+}
3014+
29083015 // Called during Application::update immediately before AvatarManager::updateMyAvatar, updating my data that is then sent to everyone.
29093016 // (Maybe this code should be moved there?)
29103017 // The principal result is to call updateLookAtTargetAvatar() and then setLookAtPosition().
@@ -2920,7 +3027,7 @@ void Application::updateMyAvatarLookAtPosition() { · 9 unmodified lines
29203027 auto eyeTracker = DependencyManager::get<EyeTracker>();
29213028
29223029 bool isLookingAtSomeone = false;
2923− bool isHMD = _avatarUpdate->isHMDMode();
3030+ bool isHMD = qApp->isHMDMode();
29243031 glm::vec3 lookAtSpot;
29253032 if (eyeTracker->isTracking() && (isHMD || eyeTracker->isSimulating())) {
29263033 // Look at the point that the user is looking at.
@@ -2929,7 +3036,7 @@ void Application::updateMyAvatarLookAtPosition() { · 2 unmodified lines
29293036 lookAtPosition.x = -lookAtPosition.x;
29303037 }
29313038 if (isHMD) {
2932− glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose(_frameCount);
3039+ glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose();
29333040 glm::quat hmdRotation = glm::quat_cast(headPose);
29343041 lookAtSpot = _myCamera.getPosition() + myAvatar->getOrientation() * (hmdRotation * lookAtPosition);
29353042 } else {
@@ -2971,9 +3078,9 @@ void Application::updateMyAvatarLookAtPosition() { · 35 unmodified lines
29713078 } else {
29723079 // I am not looking at anyone else, so just look forward
29733080 if (isHMD) {
2974− glm::mat4 headPose = _avatarUpdate->getHeadPose() ;
3081+ glm::mat4 headPose = myAvatar->getHMDSensorMatrix();
29753082 glm::quat headRotation = glm::quat_cast(headPose);
2976− lookAtSpot = _myCamera.getPosition() +
3083+ lookAtSpot = myAvatar->getPosition() +
29773084 myAvatar->getOrientation() * (headRotation * glm::vec3(0.0f, 0.0f, -TREE_SCALE));
29783085 } else {
29793086 lookAtSpot = myAvatar->getHead()->getEyePosition() +
@@ -3012,6 +3119,16 @@ void Application::updateThreads(float deltaTime) { · 32 unmodified lines
30123119 }
30133120 }
30143121
3122+void Application::toggleOverlays() {
3123+ auto newOverlaysVisible = !_overlayConductor.getEnabled();
3124+ Menu::getInstance()->setIsOptionChecked(MenuOption::Overlays, newOverlaysVisible);
3125+ _overlayConductor.setEnabled(newOverlaysVisible);
3126+}
3127+
3128+void Application::setOverlaysVisible(bool visible) {
3129+ _overlayConductor.setEnabled(visible);
3130+}
3131+
30153132 void Application::cycleCamera() {
30163133 auto menu = Menu::getInstance();
30173134 if (menu->isOptionChecked(MenuOption::FullscreenMirror)) {
@@ -3070,7 +3187,7 @@ void Application::reloadResourceCaches() { · 52 unmodified lines
30703187 _viewFrustum.setOrientation(glm::quat());
30713188 queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions);
30723189
3073− emptyLocalCache();
3190+ DependencyManager::get<AssetClient>()->clearCache();
30743191
30753192 DependencyManager::get<AnimationCache>()->refreshAll();
30763193 DependencyManager::get<ModelCache>()->refreshAll();
@@ -3113,11 +3230,33 @@ void Application::updateDialogs(float deltaTime) { · 36 unmodified lines
31133230 }
31143231
31153232 void Application::update(float deltaTime) {
3233+
3234+ PROFILE_RANGE_EX(__FUNCTION__, 0xffff0000, (uint64_t)_frameCount + 1);
3235+
31163236 bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
31173237 PerformanceWarning warn(showWarnings, "Application::update()");
31183238
31193239 updateLOD();
31203240
3241+ if (!_physicsEnabled && _processOctreeStatsCounter > 0) {
3242+
3243+ // process octree stats packets are sent in between full sends of a scene.
3244+ // We keep physics disabled until we've recieved a full scene and everything near the avatar in that
3245+ // scene is ready to compute its collision shape.
3246+
3247+ if (nearbyEntitiesAreReadyForPhysics()) {
3248+ _physicsEnabled = true;
3249+ getMyAvatar()->updateMotionBehaviorFromMenu();
3250+ } else {
3251+ auto characterController = getMyAvatar()->getCharacterController();
3252+ if (characterController) {
3253+ // if we have a character controller, disable it here so the avatar doesn't get stuck due to
3254+ // a non-loading collision hull.
3255+ characterController->setEnabled(false);
3256+ }
3257+ }
3258+ }
3259+
31213260 {
31223261 PerformanceTimer perfTimer("devices");
31233262 DeviceTracker::updateAll();
@@ -3155,7 +3294,6 @@ void Application::update(float deltaTime) { · 31 unmodified lines
31553294
31563295 auto myAvatar = getMyAvatar();
31573296 auto userInputMapper = DependencyManager::get<UserInputMapper>();
3158− userInputMapper->update(deltaTime);
31593297
31603298 controller::InputCalibrationData calibrationData = {
31613299 myAvatar->getSensorToWorldMatrix(),
@@ -3163,9 +3301,12 @@ void Application::update(float deltaTime) { · 1 unmodified line
31633301 myAvatar->getHMDSensorMatrix()
31643302 };
31653303
3304+ InputPluginPointer keyboardMousePlugin;
31663305 bool jointsCaptured = false;
31673306 for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) {
3168− if (inputPlugin->isActive()) {
3307+ if (inputPlugin->getName() == KeyboardMouseDevice::NAME) {
3308+ keyboardMousePlugin = inputPlugin;
3309+ } else if (inputPlugin->isActive()) {
31693310 inputPlugin->pluginUpdate(deltaTime, calibrationData, jointsCaptured);
31703311 if (inputPlugin->isJointController()) {
31713312 jointsCaptured = true;
@@ -3173,6 +3314,12 @@ void Application::update(float deltaTime) { · 1 unmodified line
31733314 }
31743315 }
31753316
3317+ userInputMapper->update(deltaTime);
3318+
3319+ if (keyboardMousePlugin && keyboardMousePlugin->isActive()) {
3320+ keyboardMousePlugin->pluginUpdate(deltaTime, calibrationData, jointsCaptured);
3321+ }
3322+
31763323 _controllerScriptingInterface->updateInputControllers();
31773324
31783325 // Transfer the user inputs to the driveKeys
@@ -3192,21 +3339,26 @@ void Application::update(float deltaTime) { · 13 unmodified lines
31923339 myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z));
31933340 }
31943341
3195− controller::Pose leftHand = userInputMapper->getPoseState(controller::Action::LEFT_HAND);
3196− controller::Pose rightHand = userInputMapper->getPoseState(controller::Action::RIGHT_HAND);
3197− Hand* hand = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHand();
3198− setPalmData(hand, leftHand, deltaTime, HandData::LeftHand, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK));
3199− setPalmData(hand, rightHand, deltaTime, HandData::RightHand, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK));
3342+ controller::Pose leftHandPose = userInputMapper->getPoseState(controller::Action::LEFT_HAND);
3343+ controller::Pose rightHandPose = userInputMapper->getPoseState(controller::Action::RIGHT_HAND);
3344+ auto myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition());
3345+ auto worldToSensorMatrix = glm::inverse(myAvatar->getSensorToWorldMatrix());
3346+ auto avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix;
3347+ myAvatar->setHandControllerPosesInSensorFrame(leftHandPose.transform(avatarToSensorMatrix), rightHandPose.transform(avatarToSensorMatrix));
3348+
32003349 updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
32013350 updateDialogs(deltaTime); // update various stats dialogs if present
32023351
3203− _avatarUpdate->synchronousProcess();
3352+ QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>();
32043353
32053354 if (_physicsEnabled) {
3355+ PROFILE_RANGE_EX("Physics", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
3356+
32063357 PerformanceTimer perfTimer("physics");
3207− AvatarManager* avatarManager = DependencyManager::get<AvatarManager>().data();
32083358
32093359 {
3360+ PROFILE_RANGE_EX("UpdateStats", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
3361+
32103362 PerformanceTimer perfTimer("updateStates)");
32113363 static VectorOfMotionStates motionStates;
32123364 _entitySimulation.getObjectsToRemoveFromPhysics(motionStates);
@@ -3239,15 +3391,18 @@ void Application::update(float deltaTime) { · 26 unmodified lines
32393391 });
32403392 }
32413393 {
3394+ PROFILE_RANGE_EX("StepSimulation", 0xffff8000, (uint64_t)getActiveDisplayPlugin()->presentCount());
32423395 PerformanceTimer perfTimer("stepSimulation");
32433396 getEntities()->getTree()->withWriteLock([&] {
32443397 _physicsEngine->stepSimulation();
32453398 });
32463399 }
32473400 {
3248− PerformanceTimer perfTimer("havestChanges");
3401+ PROFILE_RANGE_EX("HarvestChanges", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
3402+ PerformanceTimer perfTimer("harvestChanges");
32493403 if (_physicsEngine->hasOutgoingChanges()) {
32503404 getEntities()->getTree()->withWriteLock([&] {
3405+ PerformanceTimer perfTimer("handleOutgoingChanges");
32513406 const VectorOfMotionStates& outgoingChanges = _physicsEngine->getOutgoingChanges();
32523407 _entitySimulation.handleOutgoingChanges(outgoingChanges, Physics::getSessionUUID());
32533408 avatarManager->handleOutgoingChanges(outgoingChanges);
@@ -3263,6 +3418,7 @@ void Application::update(float deltaTime) { · 9 unmodified lines
32633418 // Collision events (and their scripts) must not be handled when we're locked, above. (That would risk
32643419 // deadlock.)
32653420 _entitySimulation.handleCollisionEvents(collisionEvents);
3421+
32663422 // NOTE: the getEntities()->update() call below will wait for lock
32673423 // and will simulate entity motion (the EntityTree has been given an EntitySimulation).
32683424 getEntities()->update(); // update the models...
@@ -3273,7 +3429,27 @@ void Application::update(float deltaTime) { · 4 unmodified lines
32733429 }
32743430 }
32753431
3432+ // AvatarManager update
3433+ {
3434+ PerformanceTimer perfTimer("AvatarManger");
3435+
3436+ qApp->setAvatarSimrateSample(1.0f / deltaTime);
3437+
3438+ {
3439+ PROFILE_RANGE_EX("OtherAvatars", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount());
3440+ avatarManager->updateOtherAvatars(deltaTime);
3441+ }
3442+
3443+ qApp->updateMyAvatarLookAtPosition();
3444+
3445+ {
3446+ PROFILE_RANGE_EX("MyAvatar", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount());
3447+ avatarManager->updateMyAvatar(deltaTime);
3448+ }
3449+ }
3450+
32763451 {
3452+ PROFILE_RANGE_EX("Overlays", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
32773453 PerformanceTimer perfTimer("overlays");
32783454 _overlays.update(deltaTime);
32793455 }
@@ -3293,6 +3469,7 @@ void Application::update(float deltaTime) { · 13 unmodified lines
32933469
32943470 // Update my voxel servers with my current voxel query...
32953471 {
3472+ PROFILE_RANGE_EX("QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
32963473 PerformanceTimer perfTimer("queryOctree");
32973474 quint64 sinceLastQuery = now - _lastQueriedTime;
32983475 const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
@@ -3330,8 +3507,15 @@ void Application::update(float deltaTime) { · 31 unmodified lines
33303507 }
33313508 }
33323509
3333− // update sensorToWorldMatrix for rendering camera.
3334− myAvatar->updateSensorToWorldMatrix();
3510+ {
3511+ PROFILE_RANGE_EX("PreRenderLambdas", 0xffff0000, (uint64_t)0);
3512+
3513+ std::unique_lock<std::mutex> guard(_preRenderLambdasLock);
3514+ for (auto& iter : _preRenderLambdas) {
3515+ iter.second();
3516+ }
3517+ _preRenderLambdas.clear();
3518+ }
33353519 }
33363520
33373521
@@ -3406,7 +3590,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node · 68 unmodified lines
34063590 _octreeQuery.setCameraNearClip(_viewFrustum.getNearClip());
34073591 _octreeQuery.setCameraFarClip(_viewFrustum.getFarClip());
34083592 _octreeQuery.setCameraEyeOffsetPosition(glm::vec3());
3409− _octreeQuery.setKeyholeRadius(_viewFrustum.getKeyholeRadius());
3593+ _octreeQuery.setCameraCenterRadius(_viewFrustum.getCenterRadius());
34103594 auto lodManager = DependencyManager::get<LODManager>();
34113595 _octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale());
34123596 _octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust());
@@ -3442,9 +3626,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node · 29 unmodified lines
34423626 rootDetails.y * TREE_SCALE,
34433627 rootDetails.z * TREE_SCALE) - glm::vec3(HALF_TREE_SCALE),
34443628 rootDetails.s * TREE_SCALE);
3445− ViewFrustum::location serverFrustumLocation = _viewFrustum.cubeInFrustum(serverBounds);
3446−
3447− if (serverFrustumLocation != ViewFrustum::OUTSIDE) {
3629+ if (_viewFrustum.cubeIntersectsKeyhole(serverBounds)) {
34483630 inViewServers++;
34493631 }
34503632 }
@@ -3510,12 +3692,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node · 59 unmodified lines
35103692 rootDetails.s * TREE_SCALE);
35113693
35123694
3513− ViewFrustum::location serverFrustumLocation = _viewFrustum.cubeInFrustum(serverBounds);
3514− if (serverFrustumLocation != ViewFrustum::OUTSIDE) {
3515− inView = true;
3516− } else {
3517− inView = false;
3518− }
3695+ inView = _viewFrustum.cubeIntersectsKeyhole(serverBounds);
35193696 } else {
35203697 if (wantExtraDebugging) {
35213698 qCDebug(interfaceapp) << "Jurisdiction without RootCode for node " << *node << ". That's unusual!";
@@ -3740,19 +3917,19 @@ namespace render { · 218 unmodified lines
37403917 switch (backgroundMode) {
37413918 case model::SunSkyStage::SKY_BOX: {
37423919 auto skybox = skyStage->getSkybox();
3743− if (skybox && skybox->getCubemap() && skybox->getCubemap()->isDefined()) {
3920+ if (skybox) {
37443921 PerformanceTimer perfTimer("skybox");
37453922 skybox->render(batch, *(args->_viewFrustum));
37463923 break;
37473924 }
3748− // If no skybox texture is available, render the SKY_DOME while it loads
37493925 }
3750− // fall through to next case
3926+
3927+ // Fall through: if no skybox is available, render the SKY_DOME
37513928 case model::SunSkyStage::SKY_DOME: {
37523929 if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
37533930 PerformanceTimer perfTimer("stars");
37543931 PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
3755− "Application::payloadRender<BackgroundRenderData>() ... stars...");
3932+ "Application::payloadRender<BackgroundRenderData>() ... My god, it's full of stars...");
37563933 // should be the first rendering pass - w/o depth buffer / lighting
37573934
37583935 static const float alpha = 1.0f;
@@ -3760,6 +3937,7 @@ namespace render { · 1 unmodified line
37603937 }
37613938 }
37623939 break;
3940+
37633941 case model::SunSkyStage::NO_BACKGROUND:
37643942 default:
37653943 // this line intentionally left blank
@@ -3774,9 +3952,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 8 unmodified lines
37743952 // FIXME: This preRender call is temporary until we create a separate render::scene for the mirror rendering.
37753953 // Then we can move this logic into the Avatar::simulate call.
37763954 auto myAvatar = getMyAvatar();
3777− myAvatar->startRender();
37783955 myAvatar->preRender(renderArgs);
3779− myAvatar->endRender();
37803956
37813957 // Update animation debug draw renderer
37823958 AnimDebugDraw::getInstance().update();
@@ -3794,8 +3970,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 11 unmodified lines
37943970 // The pending changes collecting the changes here
37953971 render::PendingChanges pendingChanges;
37963972
3973+ // FIXME: Move this out of here!, Background / skybox should be driven by the enityt content just like the other entities
37973974 // Background rendering decision
3798− if (BackgroundRenderData::_item == 0) {
3975+ if (!render::Item::isValidID(BackgroundRenderData::_item)) {
37993976 auto backgroundRenderData = make_shared<BackgroundRenderData>();
38003977 auto backgroundRenderPayload = make_shared<BackgroundRenderData::Payload>(backgroundRenderData);
38013978 BackgroundRenderData::_item = _main3DScene->allocateID();
@@ -3820,8 +3997,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 18 unmodified lines
38203997 }
38213998 }
38223999
4000+ // FIXME: Move this out of here!, WorldBox should be driven by the entity content just like the other entities
38234001 // Make sure the WorldBox is in the scene
3824− if (WorldBoxRenderData::_item == 0) {
4002+ if (!render::Item::isValidID(WorldBoxRenderData::_item)) {
38254003 auto worldBoxRenderData = make_shared<WorldBoxRenderData>();
38264004 auto worldBoxRenderPayload = make_shared<WorldBoxRenderData::Payload>(worldBoxRenderData);
38274005
@@ -3835,18 +4013,10 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 7 unmodified lines
38354013 });
38364014 }
38374015
3838− // Setup the current Zone Entity lighting and skybox
3839− // Fixme: We need a better soution through an actual render item !!!
4016+ // Setup the current Zone Entity lighting
38404017 {
3841− DependencyManager::get<DeferredLightingEffect>()->setAmbientLightMode(getRenderAmbientLight());
3842− auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
3843− DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity(), skyStage->getSunLight()->getAmbientIntensity());
3844−
3845− auto skybox = model::SkyboxPointer();
3846− if (skyStage->getBackgroundMode() == model::SunSkyStage::SKY_BOX) {
3847− skybox = skyStage->getSkybox();
3848− }
3849− DependencyManager::get<DeferredLightingEffect>()->setGlobalSkybox(skybox);
4018+ auto stage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
4019+ DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(stage->getSunLight());
38504020 }
38514021
38524022 {
@@ -3864,9 +4034,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 11 unmodified lines
38644034 _renderEngine->getRenderContext()->args = renderArgs;
38654035
38664036 // Before the deferred pass, let's try to use the render engine
3867− myAvatar->startRenderRun();
38684037 _renderEngine->run();
3869− myAvatar->endRenderRun();
38704038 }
38714039
38724040 activeRenderingThread = nullptr;
@@ -3928,16 +4096,8 @@ void Application::resetSensors(bool andReload) { · 55 unmodified lines
39284096 DependencyManager::get<Faceshift>()->reset();
39294097 DependencyManager::get<DdeFaceTracker>()->reset();
39304098 DependencyManager::get<EyeTracker>()->reset();
3931−
39324099 getActiveDisplayPlugin()->resetSensors();
3933−
3934− QScreen* currentScreen = _window->windowHandle()->screen();
3935− QWindow* mainWindow = _window->windowHandle();
3936− QPoint windowCenter = mainWindow->geometry().center();
3937− _glWidget->cursor().setPos(currentScreen, windowCenter);
3938−
39394100 getMyAvatar()->reset(andReload);
3940−
39414101 QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "reset", Qt::QueuedConnection);
39424102 }
39434103
@@ -3990,46 +4150,40 @@ void Application::clearDomainOctreeDetails() { · 46 unmodified lines
39904150 void Application::domainChanged(const QString& domainHostname) {
39914151 updateWindowTitle();
39924152 clearDomainOctreeDetails();
3993− _domainConnectionRefusals.clear();
39944153 // disable physics until we have enough information about our new location to not cause craziness.
39954154 _physicsEnabled = false;
39964155 }
39974156
3998−void Application::handleDomainConnectionDeniedPacket(QSharedPointer<ReceivedMessage> message) {
3999− // Read deny reason from packet
4000− quint16 reasonSize;
4001− message->readPrimitive(&reasonSize);
4002− QString reason = QString::fromUtf8(message->readWithoutCopy(reasonSize));
4003−
4004− // output to the log so the user knows they got a denied connection request
4005− // and check and signal for an access token so that we can make sure they are logged in
4006− qCDebug(interfaceapp) << "The domain-server denied a connection request: " << reason;
4007− qCDebug(interfaceapp) << "You may need to re-log to generate a keypair so you can provide a username signature.";
4008−
4009− if (!_domainConnectionRefusals.contains(reason)) {
4010− _domainConnectionRefusals.append(reason);
4011− emit domainConnectionRefused(reason);
4012− }
4013−
4014− AccountManager::getInstance().checkAndSignalForAccessToken();
4015−}
4016−
4017−void Application::connectedToDomain(const QString& hostname) {
4018− AccountManager& accountManager = AccountManager::getInstance();
4019− const QUuid& domainID = DependencyManager::get<NodeList>()->getDomainHandler().getUUID();
40204157
4021− if (accountManager.isLoggedIn() && !domainID.isNull()) {
4022− _notifiedPacketVersionMismatchThisDomain = false;
4023− }
4158+void Application::resettingDomain() {
4159+ _notifiedPacketVersionMismatchThisDomain = false;
40244160 }
40254161
40264162 void Application::nodeAdded(SharedNodePointer node) {
40274163 if (node->getType() == NodeType::AvatarMixer) {
40284164 // new avatar mixer, send off our identity packet right away
40294165 getMyAvatar()->sendIdentityPacket();
4030− } else if (node->getType() == NodeType::AssetServer) {
4031− // the addition of an asset-server always re-enables the upload to asset server menu option
4032− Menu::getInstance()->getActionForOption(MenuOption::UploadAsset)->setEnabled(true);
4166+ }
4167+}
4168+
4169+void Application::nodeActivated(SharedNodePointer node) {
4170+ if (node->getType() == NodeType::AssetServer) {
4171+ // asset server just connected - check if we have the asset browser showing
4172+
4173+ auto offscreenUi = DependencyManager::get<OffscreenUi>();
4174+ auto assetDialog = offscreenUi->getRootItem()->findChild<QQuickItem*>("AssetServer");
4175+
4176+ if (assetDialog) {
4177+ auto nodeList = DependencyManager::get<NodeList>();
4178+
4179+ if (nodeList->getThisNodeCanRez()) {
4180+ // call reload on the shown asset browser dialog to get the mappings (if permissions allow)
4181+ QMetaObject::invokeMethod(assetDialog, "reload");
4182+ } else {
4183+ // we switched to an Asset Server that we can't modify, hide the Asset Browser
4184+ assetDialog->setVisible(false);
4185+ }
4186+ }
40334187 }
40344188 }
40354189
@@ -4045,10 +4199,7 @@ void Application::nodeKilled(SharedNodePointer node) { · 9 unmodified lines
40454199
40464200 if (node->getType() == NodeType::AudioMixer) {
40474201 QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "audioMixerKilled");
4048− }
4049−
4050− if (node->getType() == NodeType::EntityServer) {
4051−
4202+ } else if (node->getType() == NodeType::EntityServer) {
40524203 QUuid nodeUUID = node->getUUID();
40534204 // see if this is the first we've heard of this node...
40544205 _entityServerJurisdictions.withReadLock([&] {
@@ -4079,10 +4230,16 @@ void Application::nodeKilled(SharedNodePointer node) { · 24 unmodified lines
40794230 } else if (node->getType() == NodeType::AvatarMixer) {
40804231 // our avatar mixer has gone away - clear the hash of avatars
40814232 DependencyManager::get<AvatarManager>()->clearOtherAvatars();
4082− } else if (node->getType() == NodeType::AssetServer
4083− && !DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::AssetServer)) {
4084− // this was our last asset server - disable the menu option to upload an asset
4085− Menu::getInstance()->getActionForOption(MenuOption::UploadAsset)->setEnabled(false);
4233+ } else if (node->getType() == NodeType::AssetServer) {
4234+ // asset server going away - check if we have the asset browser showing
4235+
4236+ auto offscreenUi = DependencyManager::get<OffscreenUi>();
4237+ auto assetDialog = offscreenUi->getRootItem()->findChild<QQuickItem*>("AssetServer");
4238+
4239+ if (assetDialog) {
4240+ // call reload on the shown asset browser dialog
4241+ QMetaObject::invokeMethod(assetDialog, "clear");
4242+ }
40864243 }
40874244 }
40884245 void Application::trackIncomingOctreePacket(ReceivedMessage& message, SharedNodePointer sendingNode, bool wasStatsPacket) {
@@ -4173,22 +4330,7 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer · 84 unmodified lines
41734330 });
41744331 });
41754332
4176− if (!_physicsEnabled) {
4177− if (nearbyEntitiesAreReadyForPhysics()) {
4178− // These stats packets are sent in between full sends of a scene.
4179− // We keep physics disabled until we've recieved a full scene and everything near the avatar in that
4180− // scene is ready to compute its collision shape.
4181− _physicsEnabled = true;
4182− getMyAvatar()->updateMotionBehaviorFromMenu();
4183− } else {
4184− auto characterController = getMyAvatar()->getCharacterController();
4185− if (characterController) {
4186− // if we have a character controller, disable it here so the avatar doesn't get stuck due to
4187− // a non-loading collision hull.
4188− characterController->setEnabled(false);
4189− }
4190− }
4191− }
4333+ _processOctreeStatsCounter++;
41924334
41934335 return statsMessageLength;
41944336 }
@@ -4270,7 +4412,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri · 75 unmodified lines
42704412 scriptEngine->registerGlobalObject("Render", _renderEngine->getConfiguration().get());
42714413
42724414 scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
4273− scriptEngine->registerGlobalObject("Reticle", _compositor.getReticleInterface());
4415+ scriptEngine->registerGlobalObject("Reticle", getApplicationCompositor().getReticleInterface());
42744416 }
42754417
42764418 bool Application::canAcceptURL(const QString& urlString) const {
@@ -4308,7 +4450,10 @@ bool Application::acceptURL(const QString& urlString, bool defaultUpload) { · 31 unmodified lines
43084450 }
43094451 }
43104452
4311− return defaultUpload && askToUploadAsset(urlString);
4453+ if (defaultUpload) {
4454+ toggleAssetServerWidget(urlString);
4455+ }
4456+ return defaultUpload;
43124457 }
43134458
43144459 void Application::setSessionUUID(const QUuid& sessionUUID) {
@@ -4336,8 +4481,8 @@ bool Application::askToSetAvatarUrl(const QString& url) { · 21 unmodified lines
43364481
43374482 case FSTReader::HEAD_AND_BODY_MODEL:
43384483 ok = QMessageBox::Ok == OffscreenUi::question("Set Avatar",
4339− "Would you like to use '" + modelName + "' for your avatar?",
4340− QMessageBox::Ok | QMessageBox::Cancel);
4484+ "Would you like to use '" + modelName + "' for your avatar?",
4485+ QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok);
43414486 break;
43424487
43434488 default:
@@ -4370,79 +4515,6 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) { · 26 unmodified lines
43704515 return true;
43714516 }
43724517
4373−bool Application::askToUploadAsset(const QString& filename) {
4374− if (!DependencyManager::get<NodeList>()->getThisNodeCanRez()) {
4375− OffscreenUi::warning(_window, "Failed Upload",
4376− QString("You don't have upload rights on that domain.\n\n"));
4377− return false;
4378− }
4379−
4380− QUrl url { filename };
4381− if (auto upload = DependencyManager::get<AssetClient>()->createUpload(url.toLocalFile())) {
4382−
4383− QMessageBox messageBox;
4384− messageBox.setWindowTitle("Asset upload");
4385− messageBox.setText("You are about to upload the following file to the asset server:\n" +
4386− url.toDisplayString());
4387− messageBox.setInformativeText("Do you want to continue?");
4388− messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
4389− messageBox.setDefaultButton(QMessageBox::Ok);
4390−
4391− // Option to drop model in world for models
4392− if (filename.endsWith(FBX_EXTENSION, Qt::CaseInsensitive) || filename.endsWith(OBJ_EXTENSION, Qt::CaseInsensitive)) {
4393− auto checkBox = new QCheckBox(&messageBox);
4394− checkBox->setText("Add to scene");
4395− messageBox.setCheckBox(checkBox);
4396− }
4397−
4398− if (messageBox.exec() != QMessageBox::Ok) {
4399− upload->deleteLater();
4400− return false;
4401− }
4402−
4403− // connect to the finished signal so we know when the AssetUpload is done
4404− if (messageBox.checkBox() && (messageBox.checkBox()->checkState() == Qt::Checked)) {
4405− // Custom behavior for models
4406− QObject::connect(upload, &AssetUpload::finished, this, &Application::modelUploadFinished);
4407− } else {
4408− QObject::connect(upload, &AssetUpload::finished,
4409− &AssetUploadDialogFactory::getInstance(),
4410− &AssetUploadDialogFactory::handleUploadFinished);
4411− }
4412−
4413− // start the upload now
4414− upload->start();
4415− return true;
4416− }
4417−
4418− // display a message box with the error
4419− OffscreenUi::warning(_window, "Failed Upload", QString("Failed to upload %1.\n\n").arg(filename));
4420− return false;
4421−}
4422−
4423−void Application::modelUploadFinished(AssetUpload* upload, const QString& hash) {
4424− auto filename = QFileInfo(upload->getFilename()).fileName();
4425−
4426− if ((upload->getError() == AssetUpload::NoError) &&
4427− (FBX_EXTENSION.endsWith(upload->getExtension(), Qt::CaseInsensitive) ||
4428− OBJ_EXTENSION.endsWith(upload->getExtension(), Qt::CaseInsensitive))) {
4429−
4430− auto entities = DependencyManager::get<EntityScriptingInterface>();
4431−
4432− EntityItemProperties properties;
4433− properties.setType(EntityTypes::Model);
4434− properties.setModelURL(QString("%1:%2.%3").arg(URL_SCHEME_ATP).arg(hash).arg(upload->getExtension()));
4435− properties.setPosition(_myCamera.getPosition() + _myCamera.getOrientation() * Vectors::FRONT * 2.0f);
4436− properties.setName(QUrl(upload->getFilename()).fileName());
4437−
4438− entities->addEntity(properties);
4439−
4440− upload->deleteLater();
4441− } else {
4442− AssetUploadDialogFactory::getInstance().handleUploadFinished(upload, hash);
4443− }
4444−}
4445−
44464518 bool Application::askToWearAvatarAttachmentUrl(const QString& url) {
44474519
44484520 QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
@@ -4542,6 +4614,22 @@ void Application::toggleRunningScriptsWidget() { · 93 unmodified lines
45424614 //}
45434615 }
45444616
4617+void Application::toggleAssetServerWidget(QString filePath) {
4618+ if (!DependencyManager::get<NodeList>()->getThisNodeCanRez()) {
4619+ return;
4620+ }
4621+
4622+ static const QUrl url { "AssetServer.qml" };
4623+
4624+ auto startUpload = [=](QQmlContext* context, QObject* newObject){
4625+ if (!filePath.isEmpty()) {
4626+ emit uploadRequest(filePath);
4627+ }
4628+ };
4629+ DependencyManager::get<OffscreenUi>()->show(url, "AssetServer", startUpload);
4630+ startUpload(nullptr, nullptr);
4631+}
4632+
45454633 void Application::packageModel() {
45464634 ModelPackager::package();
45474635 }
@@ -4557,33 +4645,6 @@ void Application::openUrl(const QUrl& url) { · 9 unmodified lines
45574645 }
45584646 }
45594647
4560−void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject) {
4561− // from the domain-handler, figure out the satoshi cost per voxel and per meter cubed
4562− const QString VOXEL_SETTINGS_KEY = "voxels";
4563− const QString PER_VOXEL_COST_KEY = "per-voxel-credits";
4564− const QString PER_METER_CUBED_COST_KEY = "per-meter-cubed-credits";
4565− const QString VOXEL_WALLET_UUID = "voxel-wallet";
4566−
4567− const QJsonObject& voxelObject = domainSettingsObject[VOXEL_SETTINGS_KEY].toObject();
4568−
4569− qint64 satoshisPerVoxel = 0;
4570− qint64 satoshisPerMeterCubed = 0;
4571− QUuid voxelWalletUUID;
4572−
4573− if (!domainSettingsObject.isEmpty()) {
4574− float perVoxelCredits = (float) voxelObject[PER_VOXEL_COST_KEY].toDouble();
4575− float perMeterCubedCredits = (float) voxelObject[PER_METER_CUBED_COST_KEY].toDouble();
4576−
4577− satoshisPerVoxel = (qint64) floorf(perVoxelCredits * SATOSHIS_PER_CREDIT);
4578− satoshisPerMeterCubed = (qint64) floorf(perMeterCubedCredits * SATOSHIS_PER_CREDIT);
4579−
4580− voxelWalletUUID = QUuid(voxelObject[VOXEL_WALLET_UUID].toString());
4581− }
4582−
4583− qCDebug(interfaceapp) << "Octree edits costs are" << satoshisPerVoxel << "per octree cell and" << satoshisPerMeterCubed << "per meter cubed";
4584− qCDebug(interfaceapp) << "Destination wallet UUID for edit payments is" << voxelWalletUUID;
4585−}
4586−
45874648 void Application::loadDialog() {
45884649 auto scriptEngines = DependencyManager::get<ScriptEngines>();
45894650 QString fileNameString = OffscreenUi::getOpenFileName(
@@ -4657,34 +4718,6 @@ float Application::getRenderResolutionScale() const { · 67 unmodified lines
46574718 }
46584719 }
46594720
4660−int Application::getRenderAmbientLight() const {
4661− if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLightGlobal)) {
4662− return -1;
4663− } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight0)) {
4664− return 0;
4665− } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight1)) {
4666− return 1;
4667− } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight2)) {
4668− return 2;
4669− } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight3)) {
4670− return 3;
4671− } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight4)) {
4672− return 4;
4673− } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight5)) {
4674− return 5;
4675− } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight6)) {
4676− return 6;
4677− } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight7)) {
4678− return 7;
4679− } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight8)) {
4680− return 8;
4681− } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight9)) {
4682− return 9;
4683− } else {
4684− return -1;
4685− }
4686−}
4687−
46884721 void Application::notifyPacketVersionMismatch() {
46894722 if (!_notifiedPacketVersionMismatchThisDomain) {
46904723 _notifiedPacketVersionMismatchThisDomain = true;
@@ -4697,7 +4730,7 @@ void Application::notifyPacketVersionMismatch() { · 6 unmodified lines
46974730 }
46984731
46994732 void Application::checkSkeleton() {
4700− if (getMyAvatar()->getSkeletonModel().isActive() && !getMyAvatar()->getSkeletonModel().hasSkeleton()) {
4733+ if (getMyAvatar()->getSkeletonModel()->isActive() && !getMyAvatar()->getSkeletonModel()->hasSkeleton()) {
47014734 qCDebug(interfaceapp) << "MyAvatar model has no skeleton";
47024735
47034736 QString message = "Your selected avatar body has no skeleton.\n\nThe default body will be loaded...";
@@ -4786,11 +4819,18 @@ qreal Application::getDevicePixelRatio() { · 82 unmodified lines
47864819 }
47874820
47884821 DisplayPlugin* Application::getActiveDisplayPlugin() {
4789− if (nullptr == _displayPlugin) {
4790− updateDisplayMode();
4791− Q_ASSERT(_displayPlugin);
4822+ DisplayPlugin* result = nullptr;
4823+ if (QThread::currentThread() == thread()) {
4824+ if (nullptr == _displayPlugin) {
4825+ updateDisplayMode();
4826+ Q_ASSERT(_displayPlugin);
4827+ }
4828+ result = _displayPlugin.get();
4829+ } else {
4830+ std::unique_lock<std::mutex> lock(_displayPluginLock);
4831+ result = _displayPlugin.get();
47924832 }
4793− return _displayPlugin.get();
4833+ return result;
47944834 }
47954835
47964836 const DisplayPlugin* Application::getActiveDisplayPlugin() const {
@@ -4813,7 +4853,7 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti · 16 unmodified lines
48134853 groupingMenu = "Developer";
48144854 break;
48154855 default:
4816− groupingMenu = "Standard";
4856+ groupingMenu = "Standard";
48174857 break;
48184858 }
48194859
@@ -4826,7 +4866,7 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti · 6 unmodified lines
48264866 auto action = menu->addActionToQMenuAndActionHash(parent,
48274867 name, 0, qApp,
48284868 SLOT(updateDisplayMode()),
4829− QAction::NoRole, UNSPECIFIED_POSITION, groupingMenu);
4869+ QAction::NoRole, Menu::UNSPECIFIED_POSITION, groupingMenu);
48304870
48314871 action->setCheckable(true);
48324872 action->setChecked(active);
@@ -4835,6 +4875,19 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti · 2 unmodified lines
48354875 }
48364876
48374877 void Application::updateDisplayMode() {
4878+ // Unsafe to call this method from anything but the main thread
4879+ if (QThread::currentThread() != thread()) {
4880+ qFatal("Attempted to switch display plugins from a non-main thread");
4881+ }
4882+
4883+ // Some plugins *cough* Oculus *cough* process message events from inside their
4884+ // display function, and we don't want to change the display plugin underneath
4885+ // the paintGL call, so we need to guard against that
4886+ // The current oculus runtime doesn't do this anymore
4887+ if (_inPaint) {
4888+ qFatal("Attempted to switch display plugins while in painting");
4889+ }
4890+
48384891 auto menu = Menu::getInstance();
48394892 auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins();
48404893
@@ -4891,71 +4944,62 @@ void Application::updateDisplayMode() { · 50 unmodified lines
48914944 }
48924945 }
48934946
4894− auto offscreenUi = DependencyManager::get<OffscreenUi>();
4895− DisplayPluginPointer oldDisplayPlugin = _displayPlugin;
4896− if (newDisplayPlugin == oldDisplayPlugin) {
4947+ if (newDisplayPlugin == _displayPlugin) {
48974948 return;
48984949 }
48994950
4900− // Some plugins *cough* Oculus *cough* process message events from inside their
4901− // display function, and we don't want to change the display plugin underneath
4902− // the paintGL call, so we need to guard against that
4903− if (_inPaint) {
4904− qDebug() << "Deferring plugin switch until out of painting";
4905− // Have the old plugin stop requesting renders
4906− oldDisplayPlugin->stop();
4907− QTimer* timer = new QTimer();
4908− timer->singleShot(500, [this, timer] {
4909− timer->deleteLater();
4910− updateDisplayMode();
4911− });
4912− return;
4913− }
4951+ auto offscreenUi = DependencyManager::get<OffscreenUi>();
49144952
4953+ // Make the switch atomic from the perspective of other threads
4954+ {
4955+ std::unique_lock<std::mutex> lock(_displayPluginLock);
49154956
4916− if (!_pluginContainer->currentDisplayActions().isEmpty()) {
4917− auto menu = Menu::getInstance();
4918− foreach(auto itemInfo, _pluginContainer->currentDisplayActions()) {
4919− menu->removeMenuItem(itemInfo.first, itemInfo.second);
4957+ auto oldDisplayPlugin = _displayPlugin;
4958+ if (_displayPlugin) {
4959+ _displayPlugin->deactivate();
49204960 }
4921− _pluginContainer->currentDisplayActions().clear();
4922− }
49234961
4924− if (newDisplayPlugin) {
4925− _offscreenContext->makeCurrent();
4926− newDisplayPlugin->activate();
4962+ // FIXME probably excessive and useless context switching
49274963 _offscreenContext->makeCurrent();
4928− offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize()));
4929− _offscreenContext->makeCurrent();
4930− }
49314964
4932− oldDisplayPlugin = _displayPlugin;
4933− _displayPlugin = newDisplayPlugin;
4965+ bool active = newDisplayPlugin->activate();
49344966
4935− // If the displayPlugin is a screen based HMD, then it will want the HMDTools displayed
4936− // Direct Mode HMDs (like windows Oculus) will be isHmd() but will have a screen of -1
4937− bool newPluginWantsHMDTools = newDisplayPlugin ?
4938− (newDisplayPlugin->isHmd() && (newDisplayPlugin->getHmdScreen() >= 0)) : false;
4939− bool oldPluginWantedHMDTools = oldDisplayPlugin ?
4940− (oldDisplayPlugin->isHmd() && (oldDisplayPlugin->getHmdScreen() >= 0)) : false;
4967+ if (!active) {
4968+ // If the new plugin fails to activate, fallback to last display
4969+ qWarning() << "Failed to activate display: " << newDisplayPlugin->getName();
4970+ newDisplayPlugin = oldDisplayPlugin;
49414971
4942− // Only show the hmd tools after the correct plugin has
4943− // been activated so that it's UI is setup correctly
4944− if (newPluginWantsHMDTools) {
4945− _pluginContainer->showDisplayPluginsTools();
4946− }
4972+ if (newDisplayPlugin) {
4973+ qWarning() << "Falling back to last display: " << newDisplayPlugin->getName();
4974+ active = newDisplayPlugin->activate();
4975+ }
49474976
4948− if (oldDisplayPlugin) {
4949− oldDisplayPlugin->deactivate();
4950− _offscreenContext->makeCurrent();
4977+ // If there is no last display, or
4978+ // If the last display fails to activate, fallback to desktop
4979+ if (!active) {
4980+ newDisplayPlugin = displayPlugins.at(0);
4981+ qWarning() << "Falling back to display: " << newDisplayPlugin->getName();
4982+ active = newDisplayPlugin->activate();
4983+ }
49514984
4952− // if the old plugin was HMD and the new plugin is not HMD, then hide our hmdtools
4953− if (oldPluginWantedHMDTools && !newPluginWantsHMDTools) {
4954− DependencyManager::get<DialogsManager>()->hmdTools(false);
4985+ if (!active) {
4986+ qFatal("Failed to activate fallback plugin");
4987+ }
49554988 }
4989+
4990+ _offscreenContext->makeCurrent();
4991+ offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize()));
4992+ _offscreenContext->makeCurrent();
4993+ getApplicationCompositor().setDisplayPlugin(newDisplayPlugin);
4994+ _displayPlugin = newDisplayPlugin;
49564995 }
4996+
4997+
49574998 emit activeDisplayPluginChanged();
4958− resetSensors();
4999+
5000+ // reset the avatar, to set head and hand palms back to a resonable default pose.
5001+ getMyAvatar()->reset(false);
5002+
49595003 Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");
49605004 }
49615005
@@ -5049,70 +5093,11 @@ mat4 Application::getEyeOffset(int eye) const { · 87 unmodified lines
50495093
50505094 mat4 Application::getHMDSensorPose() const {
50515095 if (isHMDMode()) {
5052− return getActiveDisplayPlugin()->getHeadPose(_frameCount);
5096+ return getActiveDisplayPlugin()->getHeadPose();
50535097 }
50545098 return mat4();
50555099 }
50565100
5057−void Application::setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue) {
5058−
5059− // NOTE: the Hand::modifyPalm() will allow the lambda to modify the palm data while ensuring some other user isn't
5060− // reading or writing to the Palms. This is definitely not the best way of handling this, and I'd like to see more
5061− // of this palm manipulation in the Hand class itself. But unfortunately the Hand and Palm don't knbow about
5062− // controller::Pose. More work is needed to clean this up.
5063− hand->modifyPalm(whichHand, [&](PalmData& palm) {
5064− palm.setActive(pose.isValid());
5065−
5066− // controller pose is in Avatar frame.
5067− glm::vec3 position = pose.getTranslation();
5068− glm::quat rotation = pose.getRotation();
5069−
5070− // Compute current velocity from position change
5071− glm::vec3 rawVelocity;
5072− if (deltaTime > 0.0f) {
5073− rawVelocity = (position - palm.getRawPosition()) / deltaTime;
5074− } else {
5075− rawVelocity = glm::vec3(0.0f);
5076− }
5077− palm.setRawVelocity(rawVelocity); // meters/sec
5078−
5079− // Angular Velocity of Palm
5080− glm::quat deltaRotation = rotation * glm::inverse(palm.getRawRotation());
5081− glm::vec3 angularVelocity(0.0f);
5082− float rotationAngle = glm::angle(deltaRotation);
5083− if ((rotationAngle > EPSILON) && (deltaTime > 0.0f)) {
5084− angularVelocity = glm::normalize(glm::axis(deltaRotation));
5085− angularVelocity *= (rotationAngle / deltaTime);
5086− palm.setRawAngularVelocity(angularVelocity);
5087− } else {
5088− palm.setRawAngularVelocity(glm::vec3(0.0f));
5089− }
5090−
5091− if (controller::InputDevice::getLowVelocityFilter()) {
5092− // Use a velocity sensitive filter to damp small motions and preserve large ones with
5093− // no latency.
5094− float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f);
5095− position = palm.getRawPosition() * velocityFilter + position * (1.0f - velocityFilter);
5096− rotation = safeMix(palm.getRawRotation(), rotation, 1.0f - velocityFilter);
5097− }
5098− palm.setRawPosition(position);
5099− palm.setRawRotation(rotation);
5100−
5101− // Store the one fingertip in the palm structure so we can track velocity
5102− const float FINGER_LENGTH = 0.3f; // meters
5103− const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f);
5104− const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR;
5105− glm::vec3 oldTipPosition = palm.getTipRawPosition();
5106− if (deltaTime > 0.0f) {
5107− palm.setTipVelocity((newTipPosition - oldTipPosition) / deltaTime);
5108− } else {
5109− palm.setTipVelocity(glm::vec3(0.0f));
5110− }
5111− palm.setTipPosition(newTipPosition);
5112− palm.setTrigger(triggerValue); // FIXME - we want to get rid of this idea of PalmData having a trigger
5113− });
5114−}
5115−
51165101 void Application::crashApplication() {
51175102 qCDebug(interfaceapp) << "Intentionally crashed Interface";
51185103 QObject* object = nullptr;
@@ -5120,6 +5105,15 @@ void Application::crashApplication() { · 1 unmodified line
51205105 Q_UNUSED(value);
51215106 }
51225107
5108+void Application::deadlockApplication() {
5109+ qCDebug(interfaceapp) << "Intentionally deadlocked Interface";
5110+ // Using a loop that will *technically* eventually exit (in ~600 billion years)
5111+ // to avoid compiler warnings about a loop that will never exit
5112+ for (uint64_t i = 1; i != 0; ++i) {
5113+ QThread::sleep(1);
5114+ }
5115+}
5116+
51235117 void Application::setActiveDisplayPlugin(const QString& pluginName) {
51245118 auto menu = Menu::getInstance();
51255119 foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) {
@@ -5158,3 +5152,13 @@ void Application::readArgumentsFromLocalSocket() { · 32 unmodified lines
51585152 qApp->openUrl(QString::fromUtf8(message));
51595153 }
51605154 }
5155+
5156+void Application::showDesktop() {
5157+ if (!_overlayConductor.getEnabled()) {
5158+ _overlayConductor.setEnabled(true);
5159+ }
5160+}
5161+
5162+CompositorHelper& Application::getApplicationCompositor() const {
5163+ return *DependencyManager::get<CompositorHelper>();
5164+}