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
120#include <recording/Recorder.h>120#include <recording/Recorder.h>
121#include <QmlWebWindowClass.h>121#include <QmlWebWindowClass.h>
122#include <Preferences.h>122#include <Preferences.h>
123#include <display-plugins/CompositorHelper.h>
123 124
124#include "AnimDebugDraw.h"125#include "AnimDebugDraw.h"
125#include "AudioClient.h"126#include "AudioClient.h"
@@ -140,6 +141,7 @@ · 14 unmodified lines
140#include "ModelPackager.h"141#include "ModelPackager.h"
141#include "PluginContainerProxy.h"142#include "PluginContainerProxy.h"
142#include "scripting/AccountScriptingInterface.h"143#include "scripting/AccountScriptingInterface.h"
144#include "scripting/AssetMappingsScriptingInterface.h"
143#include "scripting/AudioDeviceScriptingInterface.h"145#include "scripting/AudioDeviceScriptingInterface.h"
144#include "scripting/ClipboardScriptingInterface.h"146#include "scripting/ClipboardScriptingInterface.h"
145#include "scripting/DesktopScriptingInterface.h"147#include "scripting/DesktopScriptingInterface.h"
@@ -157,7 +159,6 @@ · 11 unmodified lines
157#include "Stars.h"159#include "Stars.h"
158#include "ui/AddressBarDialog.h"160#include "ui/AddressBarDialog.h"
159#include "ui/AvatarInputs.h"161#include "ui/AvatarInputs.h"
160#include "ui/AssetUploadDialogFactory.h"
161#include "ui/DialogsManager.h"162#include "ui/DialogsManager.h"
162#include "ui/LoginDialog.h"163#include "ui/LoginDialog.h"
163#include "ui/overlays/Cube3DOverlay.h"164#include "ui/overlays/Cube3DOverlay.h"
@@ -194,6 +195,7 @@ static const QString FBX_EXTENSION = ".fbx"; · 30 unmodified lines
194static const QString OBJ_EXTENSION = ".obj";195static const QString OBJ_EXTENSION = ".obj";
195static const QString AVA_JSON_EXTENSION = ".ava.json";196static const QString AVA_JSON_EXTENSION = ".ava.json";
196 197
198static const int MSECS_PER_SEC = 1000;
197static const int MIRROR_VIEW_TOP_PADDING = 5;199static const int MIRROR_VIEW_TOP_PADDING = 5;
198static const int MIRROR_VIEW_LEFT_PADDING = 10;200static const int MIRROR_VIEW_LEFT_PADDING = 10;
199static const int MIRROR_VIEW_WIDTH = 265;201static const int MIRROR_VIEW_WIDTH = 265;
@@ -210,8 +212,6 @@ static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html"; · 10 unmodified lines
210 212
211static const unsigned int THROTTLED_SIM_FRAMERATE = 15;213static const unsigned int THROTTLED_SIM_FRAMERATE = 15;
212static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE;214static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE;
213static const unsigned int CAPPED_SIM_FRAMERATE = 120;
214static const int CAPPED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / CAPPED_SIM_FRAMERATE;
215 215
216static const uint32_t INVALID_FRAME = UINT32_MAX;216static const uint32_t INVALID_FRAME = UINT32_MAX;
217 217
@@ -224,7 +224,6 @@ static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStanda · 6 unmodified lines
224static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).append("/script.js");224static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).append("/script.js");
225#endif225#endif
226 226
227const QString DEFAULT_SCRIPTS_JS_URL = "http://s3.amazonaws.com/hifi-public/scripts/defaultScripts.js";
228Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS);227Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS);
229 228
230const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensions {229const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensions {
@@ -236,6 +235,107 @@ const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensi · 5 unmodified lines
236 { AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl }235 { AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl }
237};236};
238 237
238class DeadlockWatchdogThread : public QThread {
239public:
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
333std::atomic<uint64_t> DeadlockWatchdogThread::_heartbeat;
334std::atomic<uint64_t> DeadlockWatchdogThread::_lastReport;
335std::atomic<uint64_t> DeadlockWatchdogThread::_maxElapsed;
336std::atomic<int> DeadlockWatchdogThread::_maxElapsedAverage;
337ThreadSafeMovingAverage<int, DeadlockWatchdogThread::HEARTBEAT_SAMPLES> DeadlockWatchdogThread::_movingAverage;
338
239#ifdef Q_OS_WIN339#ifdef Q_OS_WIN
240class MyNativeEventFilter : public QAbstractNativeEventFilter {340class MyNativeEventFilter : public QAbstractNativeEventFilter {
241public:341public:
@@ -317,7 +417,7 @@ bool setupEssentials(int& argc, char** argv) { · 75 unmodified lines
317 417
318 Setting::preInit();418 Setting::preInit();
319 419
320 CrashHandler::checkForAndHandleCrash();420 bool previousSessionCrashed = CrashHandler::checkForAndHandleCrash();
321 CrashHandler::writeRunningMarkerFiler();421 CrashHandler::writeRunningMarkerFiler();
322 qAddPostRoutine(CrashHandler::deleteRunningMarkerFile);422 qAddPostRoutine(CrashHandler::deleteRunningMarkerFile);
323 423
@@ -372,14 +472,14 @@ bool setupEssentials(int& argc, char** argv) { · 48 unmodified lines
372 DependencyManager::set<AutoUpdater>();472 DependencyManager::set<AutoUpdater>();
373 DependencyManager::set<PathUtils>();473 DependencyManager::set<PathUtils>();
374 DependencyManager::set<InterfaceActionFactory>();474 DependencyManager::set<InterfaceActionFactory>();
375 DependencyManager::set<AssetClient>();
376 DependencyManager::set<AudioInjectorManager>();475 DependencyManager::set<AudioInjectorManager>();
377 DependencyManager::set<MessagesClient>();476 DependencyManager::set<MessagesClient>();
378 DependencyManager::set<UserInputMapper>();477 DependencyManager::set<UserInputMapper>();
379 DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>();478 DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>();
380 DependencyManager::set<InterfaceParentFinder>();479 DependencyManager::set<InterfaceParentFinder>();
381 DependencyManager::set<EntityTreeRenderer>(true, qApp, qApp);480 DependencyManager::set<EntityTreeRenderer>(true, qApp, qApp);
382 return true;481 DependencyManager::set<CompositorHelper>();
482 return previousSessionCrashed;
383}483}
384 484
385// FIXME move to header, or better yet, design some kind of UI manager485// FIXME move to header, or better yet, design some kind of UI manager
@@ -400,10 +500,13 @@ PluginContainer* _pluginContainer; · 14 unmodified lines
400OffscreenGLCanvas* _chromiumShareContext { nullptr };500OffscreenGLCanvas* _chromiumShareContext { nullptr };
401Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context);501Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context);
402 502
503Setting::Handle<int> sessionRunTime{ "sessionRunTime", 0 };
504
403Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :505Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
404 QApplication(argc, argv),506 QApplication(argc, argv),
405 _dependencyManagerIsSetup(setupEssentials(argc, argv)),
406 _window(new MainWindow(desktop())),507 _window(new MainWindow(desktop())),
508 _sessionRunTimer(startupTimer),
509 _previousSessionCrashed(setupEssentials(argc, argv)),
407 _undoStackScriptingInterface(&_undoStack),510 _undoStackScriptingInterface(&_undoStack),
408 _frameCount(0),511 _frameCount(0),
409 _fps(60.0f),512 _fps(60.0f),
@@ -425,11 +528,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 15 unmodified lines
425 _maxOctreePPS(maxOctreePacketsPerSecond.get()),528 _maxOctreePPS(maxOctreePacketsPerSecond.get()),
426 _lastFaceTrackerUpdate(0)529 _lastFaceTrackerUpdate(0)
427{530{
428 // FIXME this may be excessivly conservative. On the other hand 531 // FIXME this may be excessivly conservative. On the other hand
429 // maybe I'm used to having an 8-core machine532 // 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
431 // (main thread, present thread, random OS load)534 // (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
433 // load on the GPU until we can serialize GPU transfers (off the main thread)536 // load on the GPU until we can serialize GPU transfers (off the main thread)
434 QThreadPool::globalInstance()->setMaxThreadCount(2);537 QThreadPool::globalInstance()->setMaxThreadCount(2);
435 thread()->setPriority(QThread::HighPriority);538 thread()->setPriority(QThread::HighPriority);
@@ -458,6 +561,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 22 unmodified lines
458 561
459 auto nodeList = DependencyManager::get<NodeList>();562 auto nodeList = DependencyManager::get<NodeList>();
460 563
564 // Set up a watchdog thread to intentionally crash the application on deadlocks
565 _deadlockWatchdogThread = new DeadlockWatchdogThread();
566 _deadlockWatchdogThread->start();
567
461 qCDebug(interfaceapp) << "[VERSION] Build sequence:" << qPrintable(applicationVersion());568 qCDebug(interfaceapp) << "[VERSION] Build sequence:" << qPrintable(applicationVersion());
462 569
463 _bookmarks = new Bookmarks(); // Before setting up the menu570 _bookmarks = new Bookmarks(); // Before setting up the menu
@@ -490,9 +597,18 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 26 unmodified lines
490 audioThread->setObjectName("Audio Thread");597 audioThread->setObjectName("Audio Thread");
491 598
492 auto audioIO = DependencyManager::get<AudioClient>();599 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;
493 609
494 audioIO->setPositionGetter([this]{ return getMyAvatar()->getPositionForAudio(); });610 return myAvatar ? myAvatar->getOrientationForAudio() : Quaternions::IDENTITY;
495 audioIO->setOrientationGetter([this]{ return getMyAvatar()->getOrientationForAudio(); });611 });
496 612
497 audioIO->moveToThread(audioThread);613 audioIO->moveToThread(audioThread);
498 recording::Frame::registerFrameHandler(AudioConstants::getAudioFrameName(), [=](recording::Frame::ConstPointer frame) {614 recording::Frame::registerFrameHandler(AudioConstants::getAudioFrameName(), [=](recording::Frame::ConstPointer frame) {
@@ -508,7 +624,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 9 unmodified lines
508 });624 });
509 625
510 auto& audioScriptingInterface = AudioScriptingInterface::getInstance();626 auto& audioScriptingInterface = AudioScriptingInterface::getInstance();
511
512 connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start);627 connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start);
513 connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit);628 connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit);
514 connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater);629 connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater);
@@ -530,13 +645,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 15 unmodified lines
530 645
531 audioThread->start();646 audioThread->start();
532 647
533 // Setup AssetClient648 ResourceManager::init();
534 auto assetClient = DependencyManager::get<AssetClient>();649 // Make sure we don't time out during slow operations at startup
535 QThread* assetThread = new QThread;650 updateHeartbeat();
536 assetThread->setObjectName("Asset Thread");
537 assetClient->moveToThread(assetThread);
538 connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init);
539 assetThread->start();
540 651
541 // Setup MessagesClient652 // Setup MessagesClient
542 auto messagesClient = DependencyManager::get<MessagesClient>();653 auto messagesClient = DependencyManager::get<MessagesClient>();
@@ -549,18 +660,17 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 6 unmodified lines
549 const DomainHandler& domainHandler = nodeList->getDomainHandler();660 const DomainHandler& domainHandler = nodeList->getDomainHandler();
550 661
551 connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));662 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()));
553 connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle()));664 connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle()));
554 connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle()));665 connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle()));
555 connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));666 connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));
556 connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived);
557 667
558 // update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one668 // 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;
560 670
561 auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();671 auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
562 connect(&locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);672 connect(&locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);
563 connect(&locationUpdateTimer, &QTimer::timeout, 673 connect(&locationUpdateTimer, &QTimer::timeout,
564 DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);674 DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);
565 locationUpdateTimer.start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS);675 locationUpdateTimer.start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS);
566 676
@@ -570,6 +680,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 3 unmodified lines
570 680
571 connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded);681 connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded);
572 connect(nodeList.data(), &NodeList::nodeKilled, this, &Application::nodeKilled);682 connect(nodeList.data(), &NodeList::nodeKilled, this, &Application::nodeKilled);
683 connect(nodeList.data(), &NodeList::nodeActivated, this, &Application::nodeActivated);
573 connect(nodeList.data(), &NodeList::uuidChanged, getMyAvatar(), &MyAvatar::setSessionUUID);684 connect(nodeList.data(), &NodeList::uuidChanged, getMyAvatar(), &MyAvatar::setSessionUUID);
574 connect(nodeList.data(), &NodeList::uuidChanged, this, &Application::setSessionUUID);685 connect(nodeList.data(), &NodeList::uuidChanged, this, &Application::setSessionUUID);
575 connect(nodeList.data(), &NodeList::limitOfSilentDomainCheckInsReached, nodeList.data(), &NodeList::reset);686 connect(nodeList.data(), &NodeList::limitOfSilentDomainCheckInsReached, nodeList.data(), &NodeList::reset);
@@ -578,7 +689,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 2 unmodified lines
578 // connect to appropriate slots on AccountManager689 // connect to appropriate slots on AccountManager
579 AccountManager& accountManager = AccountManager::getInstance();690 AccountManager& accountManager = AccountManager::getInstance();
580 691
581 const qint64 BALANCE_UPDATE_INTERVAL_MSECS = 5 * 1000;692 const qint64 BALANCE_UPDATE_INTERVAL_MSECS = 5 * MSECS_PER_SEC;
582 693
583 connect(&balanceUpdateTimer, &QTimer::timeout, &accountManager, &AccountManager::updateBalance);694 connect(&balanceUpdateTimer, &QTimer::timeout, &accountManager, &AccountManager::updateBalance);
584 balanceUpdateTimer.start(BALANCE_UPDATE_INTERVAL_MSECS);695 balanceUpdateTimer.start(BALANCE_UPDATE_INTERVAL_MSECS);
@@ -590,8 +701,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 5 unmodified lines
590 connect(&accountManager, &AccountManager::usernameChanged, this, &Application::updateWindowTitle);701 connect(&accountManager, &AccountManager::usernameChanged, this, &Application::updateWindowTitle);
591 702
592 // set the account manager's root URL and trigger a login request if we don't have the access token703 // set the account manager's root URL and trigger a login request if we don't have the access token
704 accountManager.setIsAgent(true);
593 accountManager.setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL);705 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());
595 710
596 // once the event loop has started, check and signal for an access token711 // once the event loop has started, check and signal for an access token
597 QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection);712 QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection);
@@ -604,7 +719,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 6 unmodified lines
604 719
605 connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle);720 connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle);
606 connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress);721 connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress);
607 722
608 // Save avatar location immediately after a teleport.723 // Save avatar location immediately after a teleport.
609 connect(getMyAvatar(), &MyAvatar::positionGoneTo,724 connect(getMyAvatar(), &MyAvatar::positionGoneTo,
610 DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);725 DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);
@@ -625,7 +740,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 14 unmodified lines
625 getEntities()->reloadEntityScripts();740 getEntities()->reloadEntityScripts();
626 }, Qt::QueuedConnection);741 }, Qt::QueuedConnection);
627 742
628 connect(scriptEngines, &ScriptEngines::scriptLoadError, 743 connect(scriptEngines, &ScriptEngines::scriptLoadError,
629 scriptEngines, [](const QString& filename, const QString& error){744 scriptEngines, [](const QString& filename, const QString& error){
630 OffscreenUi::warning(nullptr, "Error Loading Script", filename + " failed to load.");745 OffscreenUi::warning(nullptr, "Error Loading Script", filename + " failed to load.");
631 }, Qt::QueuedConnection);746 }, Qt::QueuedConnection);
@@ -646,16 +761,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 14 unmodified lines
646 connect(&identityPacketTimer, &QTimer::timeout, getMyAvatar(), &MyAvatar::sendIdentityPacket);761 connect(&identityPacketTimer, &QTimer::timeout, getMyAvatar(), &MyAvatar::sendIdentityPacket);
647 identityPacketTimer.start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS);762 identityPacketTimer.start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS);
648 763
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
656 ResourceCache::setRequestLimit(3);764 ResourceCache::setRequestLimit(3);
657 765
658 _glWidget = new GLCanvas();766 _glWidget = new GLCanvas();
767 getApplicationCompositor().setRenderingWidget(_glWidget);
659 _window->setCentralWidget(_glWidget);768 _window->setCentralWidget(_glWidget);
660 769
661 _window->restoreGeometry();770 _window->restoreGeometry();
@@ -663,15 +772,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 1 unmodified line
663 772
664 _glWidget->setFocusPolicy(Qt::StrongFocus);773 _glWidget->setFocusPolicy(Qt::StrongFocus);
665 _glWidget->setFocus();774 _glWidget->setFocus();
775
666#ifdef Q_OS_MAC776#ifdef Q_OS_MAC
667 // OSX doesn't seem to provide for hiding the cursor only on the GL widget777 auto cursorTarget = _window; // OSX doesn't seem to provide for hiding the cursor only on the GL widget
668 _window->setCursor(Qt::BlankCursor);
669#else778#else
670 // On windows and linux, hiding the top level cursor also means it's invisible779 // On windows and linux, hiding the top level cursor also means it's invisible when hovering over the
671 // when hovering over the window menu, which is a pain, so only hide it for780 // window menu, which is a pain, so only hide it for the GL surface
672 // the GL surface781 auto cursorTarget = _glWidget;
673 _glWidget->setCursor(Qt::BlankCursor);
674#endif782#endif
783 cursorTarget->setCursor(Qt::BlankCursor);
675 784
676 // enable mouse tracking; otherwise, we only get drag events785 // enable mouse tracking; otherwise, we only get drag events
677 _glWidget->setMouseTracking(true);786 _glWidget->setMouseTracking(true);
@@ -687,8 +796,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 9 unmodified lines
687 _offscreenContext->create(_glWidget->context()->contextHandle());796 _offscreenContext->create(_glWidget->context()->contextHandle());
688 _offscreenContext->makeCurrent();797 _offscreenContext->makeCurrent();
689 initializeGL();798 initializeGL();
690
691 _offscreenContext->makeCurrent();799 _offscreenContext->makeCurrent();
800 // Make sure we don't time out during slow operations at startup
801 updateHeartbeat();
692 802
693 // Tell our entity edit sender about our known jurisdictions803 // Tell our entity edit sender about our known jurisdictions
694 _entityEditSender.setServerJurisdictions(&_entityServerJurisdictions);804 _entityEditSender.setServerJurisdictions(&_entityServerJurisdictions);
@@ -699,6 +809,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 4 unmodified lines
699 _entityEditSender.setPacketsPerSecond(3000); // super high!!809 _entityEditSender.setPacketsPerSecond(3000); // super high!!
700 810
701 _overlays.init(); // do this before scripts load811 _overlays.init(); // do this before scripts load
812 // Make sure we don't time out during slow operations at startup
813 updateHeartbeat();
702 814
703 connect(this, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()));815 connect(this, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()));
704 816
@@ -709,7 +821,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 4 unmodified lines
709 connect(&nodeList->getPacketReceiver(), &PacketReceiver::dataReceived,821 connect(&nodeList->getPacketReceiver(), &PacketReceiver::dataReceived,
710 bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData);822 bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData);
711 823
712 connect(&getMyAvatar()->getSkeletonModel(), &SkeletonModel::skeletonLoaded,824 // FIXME -- I'm a little concerned about this.
825 connect(getMyAvatar()->getSkeletonModel().get(), &SkeletonModel::skeletonLoaded,
713 this, &Application::checkSkeleton, Qt::QueuedConnection);826 this, &Application::checkSkeleton, Qt::QueuedConnection);
714 827
715 // Setup the userInputMapper with the actions828 // Setup the userInputMapper with the actions
@@ -786,7 +899,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 70 unmodified lines
786 }899 }
787 900
788 if (action == controller::toInt(controller::Action::RETICLE_CLICK)) {901 if (action == controller::toInt(controller::Action::RETICLE_CLICK)) {
789 auto reticlePos = _compositor.getReticlePosition();902 auto reticlePos = getApplicationCompositor().getReticlePosition();
790 QPoint globalPos(reticlePos.x, reticlePos.y);903 QPoint globalPos(reticlePos.x, reticlePos.y);
791 904
792 // FIXME - it would be nice if this was self contained in the _compositor or Reticle class905 // 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
809 } else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) {922 } else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) {
810 cycleCamera();923 cycleCamera();
811 } else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) {924 } else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) {
812 auto reticlePosition = _compositor.getReticlePosition();925 auto reticlePosition = getApplicationCompositor().getReticlePosition();
813 offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y)));926 offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y)));
814 } else if (action == controller::toInt(controller::Action::RETICLE_X)) {927 } else if (action == controller::toInt(controller::Action::RETICLE_X)) {
815 auto oldPos = _compositor.getReticlePosition();928 auto oldPos = getApplicationCompositor().getReticlePosition();
816 _compositor.setReticlePosition({ oldPos.x + state, oldPos.y });929 getApplicationCompositor().setReticlePosition({ oldPos.x + state, oldPos.y });
817 } else if (action == controller::toInt(controller::Action::RETICLE_Y)) {930 } else if (action == controller::toInt(controller::Action::RETICLE_Y)) {
818 auto oldPos = _compositor.getReticlePosition();931 auto oldPos = getApplicationCompositor().getReticlePosition();
819 _compositor.setReticlePosition({ oldPos.x, oldPos.y + state });932 getApplicationCompositor().setReticlePosition({ oldPos.x, oldPos.y + state });
933 } else if (action == controller::toInt(controller::Action::TOGGLE_OVERLAY)) {
934 toggleOverlays();
820 }935 }
821 }936 }
822 });937 });
@@ -825,7 +940,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 2 unmodified lines
825 _applicationStateDevice = std::make_shared<controller::StateController>();940 _applicationStateDevice = std::make_shared<controller::StateController>();
826 941
827 _applicationStateDevice->addInputVariant(QString("InHMD"), controller::StateController::ReadLambda([]() -> float {942 _applicationStateDevice->addInputVariant(QString("InHMD"), controller::StateController::ReadLambda([]() -> float {
828 return (float)qApp->getAvatarUpdater()->isHMDMode();943 return (float)qApp->isHMDMode();
829 }));944 }));
830 _applicationStateDevice->addInputVariant(QString("SnapTurn"), controller::StateController::ReadLambda([]() -> float {945 _applicationStateDevice->addInputVariant(QString("SnapTurn"), controller::StateController::ReadLambda([]() -> float {
831 return (float)qApp->getMyAvatar()->getSnapTurn();946 return (float)qApp->getMyAvatar()->getSnapTurn();
@@ -849,8 +964,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 17 unmodified lines
849 scriptEngines->setScriptsLocation(scriptEngines->getScriptsLocation());964 scriptEngines->setScriptsLocation(scriptEngines->getScriptsLocation());
850 // do this as late as possible so that all required subsystems are initialized965 // do this as late as possible so that all required subsystems are initialized
851 scriptEngines->loadScripts();966 scriptEngines->loadScripts();
967 // Make sure we don't time out during slow operations at startup
968 updateHeartbeat();
852 969
853 loadSettings();970 loadSettings();
971 // Make sure we don't time out during slow operations at startup
972 updateHeartbeat();
973
854 int SAVE_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND; // Let's save every seconds for now974 int SAVE_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND; // Let's save every seconds for now
855 connect(&_settingsTimer, &QTimer::timeout, this, &Application::saveSettings);975 connect(&_settingsTimer, &QTimer::timeout, this, &Application::saveSettings);
856 connect(&_settingsThread, SIGNAL(started()), &_settingsTimer, SLOT(start()));976 connect(&_settingsThread, SIGNAL(started()), &_settingsTimer, SLOT(start()));
@@ -904,9 +1024,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 47 unmodified lines
904 SpacemouseManager::getInstance().init();1024 SpacemouseManager::getInstance().init();
905#endif1025#endif
906 1026
907 auto& packetReceiver = nodeList->getPacketReceiver();
908 packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket");
909
910 // If the user clicks an an entity, we will check that it's an unlocked web entity, and if so, set the focus to it1027 // 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
911 auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();1028 auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
912 connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity,1029 connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity,
@@ -966,8 +1083,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 53 unmodified lines
966 }1083 }
967 });1084 });
968 1085
1086 // Make sure we don't time out during slow operations at startup
1087 updateHeartbeat();
1088
969 connect(this, &Application::applicationStateChanged, this, &Application::activeChanged);1089 connect(this, &Application::applicationStateChanged, this, &Application::activeChanged);
970 qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0);1090 qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0);
1091
971 _idleTimer = new QTimer(this);1092 _idleTimer = new QTimer(this);
972 connect(_idleTimer, &QTimer::timeout, [=] {1093 connect(_idleTimer, &QTimer::timeout, [=] {
973 idle(usecTimestampNow());1094 idle(usecTimestampNow());
@@ -976,14 +1097,49 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 2 unmodified lines
976 disconnect(_idleTimer);1097 disconnect(_idleTimer);
977 });1098 });
978 // Setting the interval to zero forces this to get called whenever there are no messages1099 // 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
980 // of logic to abort early if it's being called too often.1101 // of logic to abort early if it's being called too often.
981 _idleTimer->start(0);1102 _idleTimer->start(0);
982}1103}
983 1104
1105
1106void 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
1122void Application::showCursor(const QCursor& cursor) {
1123 QMutexLocker locker(&_changeCursorLock);
1124 _desiredCursor = cursor;
1125 _cursorNeedsChanging = true;
1126}
1127
1128void Application::updateHeartbeat() {
1129 static_cast<DeadlockWatchdogThread*>(_deadlockWatchdogThread)->updateHeartbeat();
1130}
1131
984void Application::aboutToQuit() {1132void Application::aboutToQuit() {
985 emit beforeAboutToQuit();1133 emit beforeAboutToQuit();
986 1134
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
987 getActiveDisplayPlugin()->deactivate();1143 getActiveDisplayPlugin()->deactivate();
988 1144
989 _aboutToQuit = true;1145 _aboutToQuit = true;
@@ -1024,11 +1180,10 @@ void Application::cleanupBeforeQuit() { · 34 unmodified lines
1024 getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts1180 getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
1025 DependencyManager::get<ScriptEngines>()->saveScripts();1181 DependencyManager::get<ScriptEngines>()->saveScripts();
1026 DependencyManager::get<ScriptEngines>()->shutdownScripting(); // stop all currently running global scripts1182 DependencyManager::get<ScriptEngines>()->shutdownScripting(); // stop all currently running global scripts
1027 DependencyManager::destroy<ScriptEngines>(); 1183 DependencyManager::destroy<ScriptEngines>();
1028 1184
1029 // first stop all timers directly or by invokeMethod1185 // first stop all timers directly or by invokeMethod
1030 // depending on what thread they run in1186 // depending on what thread they run in
1031 _avatarUpdate->terminate();
1032 locationUpdateTimer.stop();1187 locationUpdateTimer.stop();
1033 balanceUpdateTimer.stop();1188 balanceUpdateTimer.stop();
1034 identityPacketTimer.stop();1189 identityPacketTimer.stop();
@@ -1062,13 +1217,6 @@ void Application::cleanupBeforeQuit() { · 27 unmodified lines
1062 DependencyManager::destroy<OffscreenUi>();1217 DependencyManager::destroy<OffscreenUi>();
1063}1218}
1064 1219
1065void Application::emptyLocalCache() {
1066 if (auto cache = NetworkAccessManager::getInstance().cache()) {
1067 qDebug() << "DiskCacheEditor::clear(): Clearing disk cache.";
1068 cache->clear();
1069 }
1070}
1071
1072Application::~Application() {1220Application::~Application() {
1073 EntityTreePointer tree = getEntities()->getTree();1221 EntityTreePointer tree = getEntities()->getTree();
1074 tree->setSimulation(NULL);1222 tree->setSimulation(NULL);
@@ -1076,20 +1224,10 @@ Application::~Application() { · 1 unmodified line
1076 _octreeProcessor.terminate();1224 _octreeProcessor.terminate();
1077 _entityEditSender.terminate();1225 _entityEditSender.terminate();
1078 1226
1079 Menu::getInstance()->deleteLater();
1080
1081 _physicsEngine->setCharacterController(NULL);1227 _physicsEngine->setCharacterController(NULL);
1082 1228
1083 ModelEntityItem::cleanupLoadedAnimations();1229 ModelEntityItem::cleanupLoadedAnimations();
1084 1230
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
1093 // remove avatars from physics engine1231 // remove avatars from physics engine
1094 DependencyManager::get<AvatarManager>()->clearOtherAvatars();1232 DependencyManager::get<AvatarManager>()->clearOtherAvatars();
1095 VectorOfMotionStates motionStates;1233 VectorOfMotionStates motionStates;
@@ -1106,11 +1244,7 @@ Application::~Application() { · 10 unmodified lines
1106 DependencyManager::destroy<ScriptCache>();1244 DependencyManager::destroy<ScriptCache>();
1107 DependencyManager::destroy<SoundCache>();1245 DependencyManager::destroy<SoundCache>();
1108 1246
1109 // cleanup the AssetClient thread1247 ResourceManager::cleanup();
1110 QThread* assetThread = DependencyManager::get<AssetClient>()->thread();
1111 DependencyManager::destroy<AssetClient>();
1112 assetThread->quit();
1113 assetThread->wait();
1114 1248
1115 QThread* nodeThread = DependencyManager::get<NodeList>()->thread();1249 QThread* nodeThread = DependencyManager::get<NodeList>()->thread();
1116 1250
@@ -1126,6 +1260,10 @@ Application::~Application() { · 9 unmodified lines
1126#if 01260#if 0
1127 ConnexionClient::getInstance().destroy();1261 ConnexionClient::getInstance().destroy();
1128#endif1262#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();
1129 1267
1130 qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages1268 qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages
1131}1269}
@@ -1199,7 +1337,7 @@ void Application::initializeUi() { · 67 unmodified lines
1199 // OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to1337 // OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to
1200 // support the window management and scripting proxies for VR use1338 // support the window management and scripting proxies for VR use
1201 offscreenUi->createDesktop(QString("hifi/Desktop.qml"));1339 offscreenUi->createDesktop(QString("hifi/Desktop.qml"));
1202 1340
1203 // FIXME either expose so that dialogs can set this themselves or1341 // FIXME either expose so that dialogs can set this themselves or
1204 // do better detection in the offscreen UI of what has focus1342 // do better detection in the offscreen UI of what has focus
1205 offscreenUi->setNavigationFocused(false);1343 offscreenUi->setNavigationFocused(false);
@@ -1212,10 +1350,10 @@ void Application::initializeUi() { · 6 unmodified lines
1212 1350
1213 setupPreferences();1351 setupPreferences();
1214 1352
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,
1216 // though I can't find it. Hence, "ApplicationInterface"1354 // though I can't find it. Hence, "ApplicationInterface"
1217 rootContext->setContextProperty("SnapshotUploader", new SnapshotUploader());1355 rootContext->setContextProperty("SnapshotUploader", new SnapshotUploader());
1218 rootContext->setContextProperty("ApplicationInterface", this); 1356 rootContext->setContextProperty("ApplicationInterface", this);
1219 rootContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCache>().data());1357 rootContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCache>().data());
1220 rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance());1358 rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance());
1221 rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());1359 rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
@@ -1229,6 +1367,7 @@ void Application::initializeUi() { · 7 unmodified lines
1229 rootContext->setContextProperty("Quat", new Quat());1367 rootContext->setContextProperty("Quat", new Quat());
1230 rootContext->setContextProperty("Vec3", new Vec3());1368 rootContext->setContextProperty("Vec3", new Vec3());
1231 rootContext->setContextProperty("Uuid", new ScriptUUID());1369 rootContext->setContextProperty("Uuid", new ScriptUUID());
1370 rootContext->setContextProperty("Assets", new AssetMappingsScriptingInterface());
1232 1371
1233 rootContext->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());1372 rootContext->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
1234 1373
@@ -1258,15 +1397,17 @@ void Application::initializeUi() { · 23 unmodified lines
1258 rootContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());1397 rootContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
1259 rootContext->setContextProperty("Scene", DependencyManager::get<SceneScriptingInterface>().data());1398 rootContext->setContextProperty("Scene", DependencyManager::get<SceneScriptingInterface>().data());
1260 rootContext->setContextProperty("Render", _renderEngine->getConfiguration().get());1399 rootContext->setContextProperty("Render", _renderEngine->getConfiguration().get());
1261 rootContext->setContextProperty("Reticle", _compositor.getReticleInterface());1400 rootContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface());
1401
1402 rootContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor());
1262 1403
1263 _glWidget->installEventFilter(offscreenUi.data());1404 _glWidget->installEventFilter(offscreenUi.data());
1264 offscreenUi->setMouseTranslator([=](const QPointF& pt) {1405 offscreenUi->setMouseTranslator([=](const QPointF& pt) {
1265 QPointF result = pt;1406 QPointF result = pt;
1266 auto displayPlugin = getActiveDisplayPlugin();1407 auto displayPlugin = getActiveDisplayPlugin();
1267 if (displayPlugin->isHmd()) {1408 if (displayPlugin->isHmd()) {
1268 _compositor.handleRealMouseMoveEvent(false);1409 getApplicationCompositor().handleRealMouseMoveEvent(false);
1269 auto resultVec = _compositor.getReticlePosition();1410 auto resultVec = getApplicationCompositor().getReticlePosition();
1270 result = QPointF(resultVec.x, resultVec.y);1411 result = QPointF(resultVec.x, resultVec.y);
1271 }1412 }
1272 return result.toPoint();1413 return result.toPoint();
@@ -1293,10 +1434,30 @@ void Application::initializeUi() { · 20 unmodified lines
1293 _touchscreenDevice = std::dynamic_pointer_cast<TouchscreenDevice>(inputPlugin);1434 _touchscreenDevice = std::dynamic_pointer_cast<TouchscreenDevice>(inputPlugin);
1294 }1435 }
1295 }1436 }
1437 _window->setMenuBar(new Menu());
1296 updateInputModes();1438 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 });
1297}1446}
1298 1447
1299void Application::paintGL() {1448void 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
1300 // paintGL uses a queued connection, so we can get messages from the queue even after we've quit1461 // paintGL uses a queued connection, so we can get messages from the queue even after we've quit
1301 // and the plugins have shutdown1462 // and the plugins have shutdown
1302 if (_aboutToQuit) {1463 if (_aboutToQuit) {
@@ -1322,33 +1483,26 @@ void Application::paintGL() { · 19 unmodified lines
1322 _lastFramesPerSecondUpdate = now;1483 _lastFramesPerSecondUpdate = now;
1323 }1484 }
1324 1485
1325 if (_isGLInitialized) {1486 PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, (uint64_t)_frameCount);
1326 idle(now);
1327 }
1328
1329 PROFILE_RANGE(__FUNCTION__);
1330 PerformanceTimer perfTimer("paintGL");1487 PerformanceTimer perfTimer("paintGL");
1331 1488
1332 if (nullptr == _displayPlugin) {1489 if (nullptr == _displayPlugin) {
1333 return;1490 return;
1334 }1491 }
1335 1492
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
1345 auto displayPlugin = getActiveDisplayPlugin();1493 auto displayPlugin = getActiveDisplayPlugin();
1346 // FIXME not needed anymore?1494 // FIXME not needed anymore?
1347 _offscreenContext->makeCurrent();1495 _offscreenContext->makeCurrent();
1348 1496
1497 displayPlugin->beginFrameRender(_frameCount);
1498
1349 // update the avatar with a fresh HMD pose1499 // update the avatar with a fresh HMD pose
1350 getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose());1500 getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose());
1351 1501
1502 // update sensorToWorldMatrix for camera and hand controllers
1503 getMyAvatar()->updateSensorToWorldMatrix();
1504
1505
1352 auto lodManager = DependencyManager::get<LODManager>();1506 auto lodManager = DependencyManager::get<LODManager>();
1353 1507
1354 1508
@@ -1390,9 +1544,10 @@ void Application::paintGL() { · 35 unmodified lines
1390 QSize size = getDeviceSize();1544 QSize size = getDeviceSize();
1391 renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height());1545 renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height());
1392 _applicationOverlay.renderOverlay(&renderArgs);1546 _applicationOverlay.renderOverlay(&renderArgs);
1393 gpu::FramebufferPointer overlayFramebuffer = _applicationOverlay.getOverlayFramebuffer();1547 auto overlayTexture = _applicationOverlay.acquireOverlay();
1394 1548 if (overlayTexture) {
1395 1549 displayPlugin->submitOverlayTexture(overlayTexture);
1550 }
1396 }1551 }
1397 1552
1398 glm::vec3 boomOffset;1553 glm::vec3 boomOffset;
@@ -1402,7 +1557,6 @@ void Application::paintGL() { · 3 unmodified lines
1402 auto myAvatar = getMyAvatar();1557 auto myAvatar = getMyAvatar();
1403 boomOffset = myAvatar->getScale() * myAvatar->getBoomLength() * -IDENTITY_FRONT;1558 boomOffset = myAvatar->getScale() * myAvatar->getBoomLength() * -IDENTITY_FRONT;
1404 1559
1405 myAvatar->startCapture();
1406 if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {1560 if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
1407 Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN);1561 Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN);
1408 Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN));1562 Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN));
@@ -1490,9 +1644,10 @@ void Application::paintGL() { · 81 unmodified lines
1490 if (!isHMDMode()) {1644 if (!isHMDMode()) {
1491 _myCamera.update(1.0f / _fps);1645 _myCamera.update(1.0f / _fps);
1492 }1646 }
1493 myAvatar->endCapture();
1494 }1647 }
1495 1648
1649 getApplicationCompositor().setFrameInfo(_frameCount, _myCamera.getTransform());
1650
1496 // Primary rendering pass1651 // Primary rendering pass
1497 auto framebufferCache = DependencyManager::get<FramebufferCache>();1652 auto framebufferCache = DependencyManager::get<FramebufferCache>();
1498 const QSize size = framebufferCache->getFrameBufferSize();1653 const QSize size = framebufferCache->getFrameBufferSize();
@@ -1523,12 +1678,7 @@ void Application::paintGL() { · 24 unmodified lines
1523 auto baseProjection = renderArgs._viewFrustum->getProjection();1678 auto baseProjection = renderArgs._viewFrustum->getProjection();
1524 auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();1679 auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
1525 float IPDScale = hmdInterface->getIPDScale();1680 float IPDScale = hmdInterface->getIPDScale();
1526 1681 mat4 headPose = displayPlugin->getHeadPose();
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);
1532 1682
1533 // FIXME we probably don't need to set the projection matrix every frame,1683 // FIXME we probably don't need to set the projection matrix every frame,
1534 // only when the display plugin changes (or in non-HMD modes when the user1684 // only when the display plugin changes (or in non-HMD modes when the user
@@ -1545,7 +1695,11 @@ void Application::paintGL() { · 10 unmodified lines
1545 mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale);1695 mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale);
1546 eyeOffsets[eye] = eyeOffsetTransform;1696 eyeOffsets[eye] = eyeOffsetTransform;
1547 1697
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));
1549 1703
1550 eyeProjections[eye] = displayPlugin->getEyeProjection(eye, baseProjection);1704 eyeProjections[eye] = displayPlugin->getEyeProjection(eye, baseProjection);
1551 });1705 });
@@ -1559,44 +1713,12 @@ void Application::paintGL() { · 7 unmodified lines
1559 renderArgs._context->enableStereo(false);1713 renderArgs._context->enableStereo(false);
1560 }1714 }
1561 1715
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
1590 // deliver final composited scene to the display plugin1716 // deliver final composited scene to the display plugin
1591 {1717 {
1592 PROFILE_RANGE(__FUNCTION__ "/pluginOutput");1718 PROFILE_RANGE(__FUNCTION__ "/pluginOutput");
1593 PerformanceTimer perfTimer("pluginOutput");1719 PerformanceTimer perfTimer("pluginOutput");
1594 1720
1595 auto finalTexturePointer = finalFramebuffer->getRenderBuffer(0);1721 auto finalTexture = finalFramebuffer->getRenderBuffer(0);
1596
1597 GLuint finalTexture = gpu::GLBackend::getTextureID(finalTexturePointer);
1598 Q_ASSERT(0 != finalTexture);
1599
1600 Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture));1722 Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture));
1601 _lockedFramebufferMap[finalTexture] = finalFramebuffer;1723 _lockedFramebufferMap[finalTexture] = finalFramebuffer;
1602 1724
@@ -1604,10 +1726,9 @@ void Application::paintGL() { · 1 unmodified line
1604 {1726 {
1605 PROFILE_RANGE(__FUNCTION__ "/pluginSubmitScene");1727 PROFILE_RANGE(__FUNCTION__ "/pluginSubmitScene");
1606 PerformanceTimer perfTimer("pluginSubmitScene");1728 PerformanceTimer perfTimer("pluginSubmitScene");
1607 displayPlugin->submitSceneTexture(_frameCount, finalTexture, toGlm(size));1729 displayPlugin->submitSceneTexture(_frameCount, finalTexture);
1608 }1730 }
1609 Q_ASSERT(isCurrentContext(_offscreenContext->getContext()));1731 Q_ASSERT(isCurrentContext(_offscreenContext->getContext()));
1610
1611 }1732 }
1612 1733
1613 {1734 {
@@ -1620,7 +1741,6 @@ void Application::paintGL() { · 6 unmodified lines
1620 }1741 }
1621 1742
1622 _lastInstantaneousFps = instantaneousFps;1743 _lastInstantaneousFps = instantaneousFps;
1623 _pendingPaint = false;
1624}1744}
1625 1745
1626void Application::runTests() {1746void Application::runTests() {
@@ -1704,6 +1824,11 @@ bool Application::importSVOFromURL(const QString& urlString) { · 77 unmodified lines
1704}1824}
1705 1825
1706bool Application::event(QEvent* event) {1826bool Application::event(QEvent* event) {
1827
1828 if (!Menu::getInstance()) {
1829 return false;
1830 }
1831
1707 if ((int)event->type() == (int)Lambda) {1832 if ((int)event->type() == (int)Lambda) {
1708 ((LambdaEvent*)event)->call();1833 ((LambdaEvent*)event)->call();
1709 return true;1834 return true;
@@ -1808,7 +1933,7 @@ bool Application::event(QEvent* event) { · 98 unmodified lines
1808bool Application::eventFilter(QObject* object, QEvent* event) {1933bool Application::eventFilter(QObject* object, QEvent* event) {
1809 1934
1810 if (event->type() == QEvent::Leave) {1935 if (event->type() == QEvent::Leave) {
1811 _compositor.handleLeaveEvent();1936 getApplicationCompositor().handleLeaveEvent();
1812 }1937 }
1813 1938
1814 if (event->type() == QEvent::ShortcutOverride) {1939 if (event->type() == QEvent::ShortcutOverride) {
@@ -1862,6 +1987,28 @@ void Application::keyPressEvent(QKeyEvent* event) { · 47 unmodified lines
1862 }1987 }
1863 break;1988 break;
1864 1989
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
1865 case Qt::Key_X:2012 case Qt::Key_X:
1866 if (isShifted && isMeta) {2013 if (isShifted && isMeta) {
1867 auto offscreenUi = DependencyManager::get<OffscreenUi>();2014 auto offscreenUi = DependencyManager::get<OffscreenUi>();
@@ -1871,6 +2018,12 @@ void Application::keyPressEvent(QKeyEvent* event) { · 3 unmodified lines
1871 }2018 }
1872 break;2019 break;
1873 2020
2021 case Qt::Key_Y:
2022 if (isShifted && isMeta) {
2023 getActiveDisplayPlugin()->cycleDebugOutput();
2024 }
2025 break;
2026
1874 case Qt::Key_B:2027 case Qt::Key_B:
1875 if (isMeta) {2028 if (isMeta) {
1876 auto offscreenUi = DependencyManager::get<OffscreenUi>();2029 auto offscreenUi = DependencyManager::get<OffscreenUi>();
@@ -2022,21 +2175,22 @@ void Application::keyPressEvent(QKeyEvent* event) { · 145 unmodified lines
2022 if (isShifted) {2175 if (isShifted) {
2023 Menu::getInstance()->triggerOption(MenuOption::MiniMirror);2176 Menu::getInstance()->triggerOption(MenuOption::MiniMirror);
2024 } else {2177 } else {
2025 Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror));2178 bool isMirrorChecked = Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror);
2026 if (!Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {2179 Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, !isMirrorChecked);
2180 if (isMirrorChecked) {
2027 Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true);2181 Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true);
2028 }2182 }
2029 cameraMenuChanged();2183 cameraMenuChanged();
2030 }2184 }
2031 break;2185 break;
2032 case Qt::Key_P:2186 case Qt::Key_P: {
2033 Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, !Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson));2187 bool isFirstPersonChecked = Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson);
2034 Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson));2188 Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, !isFirstPersonChecked);
2035 cameraMenuChanged();2189 Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, isFirstPersonChecked);
2036 break;2190 cameraMenuChanged();
2037 case Qt::Key_O:
2038 _overlayConductor.setEnabled(!_overlayConductor.getEnabled());
2039 break;2191 break;
2192 }
2193
2040 case Qt::Key_Slash:2194 case Qt::Key_Slash:
2041 Menu::getInstance()->triggerOption(MenuOption::Stats);2195 Menu::getInstance()->triggerOption(MenuOption::Stats);
2042 break;2196 break;
@@ -2098,7 +2252,7 @@ void Application::keyPressEvent(QKeyEvent* event) { · 55 unmodified lines
2098void Application::keyReleaseEvent(QKeyEvent* event) {2252void Application::keyReleaseEvent(QKeyEvent* event) {
2099 if (event->key() == Qt::Key_Alt && _altPressed && hasFocus()) {2253 if (event->key() == Qt::Key_Alt && _altPressed && hasFocus()) {
2100 auto offscreenUi = DependencyManager::get<OffscreenUi>();2254 auto offscreenUi = DependencyManager::get<OffscreenUi>();
2101 auto reticlePosition = _compositor.getReticlePosition();2255 auto reticlePosition = getApplicationCompositor().getReticlePosition();
2102 offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y)));2256 offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y)));
2103 }2257 }
2104 2258
@@ -2187,13 +2341,6 @@ void Application::maybeToggleMenuVisible(QMouseEvent* event) { · 82 unmodified lines
2187#endif2341#endif
2188}2342}
2189 2343
2190/// called by ApplicationCompositor when in HMD mode and we're faking our mouse movement
2191void Application::fakeMouseEvent(QMouseEvent* event) {
2192 _fakedMouseEvent = true;
2193 sendEvent(_glWidget, event);
2194 _fakedMouseEvent = false;
2195}
2196
2197void Application::mouseMoveEvent(QMouseEvent* event) {2344void Application::mouseMoveEvent(QMouseEvent* event) {
2198 PROFILE_RANGE(__FUNCTION__);2345 PROFILE_RANGE(__FUNCTION__);
2199 2346
@@ -2203,17 +2350,16 @@ void Application::mouseMoveEvent(QMouseEvent* event) { · 3 unmodified lines
2203 2350
2204 maybeToggleMenuVisible(event);2351 maybeToggleMenuVisible(event);
2205 2352
2353 auto& compositor = getApplicationCompositor();
2206 // if this is a real mouse event, and we're in HMD mode, then we should use it to move the 2354 // if this is a real mouse event, and we're in HMD mode, then we should use it to move the
2207 // compositor reticle2355 // compositor reticle
2208 if (!_fakedMouseEvent) {2356 // handleRealMouseMoveEvent() will return true, if we shouldn't process the event further
2209 // handleRealMouseMoveEvent() will return true, if we shouldn't process the event further2357 if (!compositor.fakeEventActive() && compositor.handleRealMouseMoveEvent()) {
2210 if (_compositor.handleRealMouseMoveEvent()) {2358 return; // bail
2211 return; // bail
2212 }
2213 }2359 }
2214 2360
2215 auto offscreenUi = DependencyManager::get<OffscreenUi>();2361 auto offscreenUi = DependencyManager::get<OffscreenUi>();
2216 auto eventPosition = _compositor.getMouseEventPosition(event);2362 auto eventPosition = compositor.getMouseEventPosition(event);
2217 QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget);2363 QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget);
2218 auto button = event->button();2364 auto button = event->button();
2219 auto buttons = event->buttons();2365 auto buttons = event->buttons();
@@ -2255,7 +2401,7 @@ void Application::mousePressEvent(QMouseEvent* event) { · 35 unmodified lines
2255 // will consume all keyboard events.2401 // will consume all keyboard events.
2256 offscreenUi->unfocusWindows();2402 offscreenUi->unfocusWindows();
2257 2403
2258 auto eventPosition = _compositor.getMouseEventPosition(event);2404 auto eventPosition = getApplicationCompositor().getMouseEventPosition(event);
2259 QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget);2405 QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget);
2260 QMouseEvent mappedEvent(event->type(),2406 QMouseEvent mappedEvent(event->type(),
2261 transformedPos,2407 transformedPos,
@@ -2301,7 +2447,7 @@ void Application::mouseDoublePressEvent(QMouseEvent* event) { · 39 unmodified lines
2301void Application::mouseReleaseEvent(QMouseEvent* event) {2447void Application::mouseReleaseEvent(QMouseEvent* event) {
2302 2448
2303 auto offscreenUi = DependencyManager::get<OffscreenUi>();2449 auto offscreenUi = DependencyManager::get<OffscreenUi>();
2304 auto eventPosition = _compositor.getMouseEventPosition(event);2450 auto eventPosition = getApplicationCompositor().getMouseEventPosition(event);
2305 QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget);2451 QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget);
2306 QMouseEvent mappedEvent(event->type(),2452 QMouseEvent mappedEvent(event->type(),
2307 transformedPos,2453 transformedPos,
@@ -2451,10 +2597,23 @@ bool Application::acceptSnapshot(const QString& urlString) { · 143 unmodified lines
2451static uint32_t _renderedFrameIndex { INVALID_FRAME };2597static uint32_t _renderedFrameIndex { INVALID_FRAME };
2452 2598
2453void Application::idle(uint64_t now) {2599void Application::idle(uint64_t now) {
2454 if (_aboutToQuit) {2600
2601 updateHeartbeat();
2602
2603 if (_aboutToQuit || _inPaint) {
2455 return; // bail early, nothing to do here.2604 return; // bail early, nothing to do here.
2456 }2605 }
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
2458 auto displayPlugin = getActiveDisplayPlugin();2617 auto displayPlugin = getActiveDisplayPlugin();
2459 // depending on whether we're throttling or not.2618 // depending on whether we're throttling or not.
2460 // Once rendering is off on another thread we should be able to have Application::idle run at start(0) in2619 // 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
2475 _renderedFrameIndex = INVALID_FRAME;2634 _renderedFrameIndex = INVALID_FRAME;
2476 }2635 }
2477 2636
2478 // Nested ifs are for clarity in the logic. Don't collapse them into a giant single if.2637 // Don't saturate the main thread with rendering and simulation,
2479 // Don't saturate the main thread with rendering, no paint calls until the last one is complete2638 // unless display plugin has increased by at least one frame
2480 if (!_pendingPaint) {2639 if (_renderedFrameIndex == INVALID_FRAME || presentCount > _renderedFrameIndex) {
2481 // Also no paint calls until the display plugin has increased by at least one frame 2640 // Record what present frame we're on
2482 // (don't render at 90fps if the display plugin only goes at 60)2641 _renderedFrameIndex = presentCount;
2483 if (_renderedFrameIndex == INVALID_FRAME || presentCount > _renderedFrameIndex) {2642
2484 // Record what present frame we're on2643 // request a paint, get to it as soon as possible: high priority
2485 _renderedFrameIndex = presentCount;2644 postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority);
2486 // Don't allow paint requests to stack up in the event queue2645 } else {
2487 _pendingPaint = true;2646 // there's no use in simulating or rendering faster then the present rate.
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
2502 return;2647 return;
2503 }2648 }
2504 2649
2650 PROFILE_RANGE(__FUNCTION__);
2651
2505 // We're going to execute idle processing, so restart the last idle timer2652 // We're going to execute idle processing, so restart the last idle timer
2506 _lastTimeUpdated.start();2653 _lastTimeUpdated.start();
2507 2654
2655 checkChangeCursor();
2656
2657 Stats::getInstance()->updateStats();
2658 AvatarInputs::getInstance()->update();
2659
2508 {2660 {
2509 PROFILE_RANGE(__FUNCTION__);
2510 static uint64_t lastIdleStart{ now };2661 static uint64_t lastIdleStart{ now };
2511 uint64_t idleStartToStartDuration = now - lastIdleStart;2662 uint64_t idleStartToStartDuration = now - lastIdleStart;
2512 if (idleStartToStartDuration != 0) {2663 if (idleStartToStartDuration != 0) {
@@ -2596,7 +2747,7 @@ void Application::setLowVelocityFilter(bool lowVelocityFilter) { · 83 unmodified lines
2596}2747}
2597 2748
2598ivec2 Application::getMouse() {2749ivec2 Application::getMouse() {
2599 auto reticlePosition = _compositor.getReticlePosition();2750 auto reticlePosition = getApplicationCompositor().getReticlePosition();
2600 2751
2601 // in the HMD, the reticlePosition is the mouse position2752 // in the HMD, the reticlePosition is the mouse position
2602 if (isHMDMode()) {2753 if (isHMDMode()) {
@@ -2681,43 +2832,50 @@ void Application::calibrateEyeTracker5Points() { · 78 unmodified lines
2681}2832}
2682#endif2833#endif
2683 2834
2684bool Application::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs) {2835bool Application::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs, const glm::vec3* givenOffset) {
2685 QVector<EntityItemPointer> entities;2836 QHash<EntityItemID, EntityItemPointer> entities;
2686 2837
2687 auto entityTree = getEntities()->getTree();2838 auto entityTree = getEntities()->getTree();
2688 auto exportTree = std::make_shared<EntityTree>();2839 auto exportTree = std::make_shared<EntityTree>();
2689 exportTree->createRootElement();2840 exportTree->createRootElement();
2690 2841
2691 glm::vec3 root(TREE_SCALE, TREE_SCALE, TREE_SCALE);2842 glm::vec3 root(TREE_SCALE, TREE_SCALE, TREE_SCALE);
2692 for (auto entityID : entityIDs) {2843 for (auto entityID : entityIDs) { // Gather entities and properties.
2693 auto entityItem = entityTree->findEntityByEntityItemID(entityID);2844 auto entityItem = entityTree->findEntityByEntityItemID(entityID);
2694 if (!entityItem) {2845 if (!entityItem) {
2846 qCWarning(interfaceapp) << "Skipping export of" << entityID << "that is not in scene.";
2695 continue;2847 continue;
2696 }2848 }
2697 2849
2698 auto properties = entityItem->getProperties();2850 if (!givenOffset) {
2699 auto position = properties.getPosition();2851 EntityItemID parentID = entityItem->getParentID();
2700 2852 if (parentID.isInvalidID() || !entityIDs.contains(parentID) || !entityTree->findEntityByEntityItemID(parentID)) {
2701 root.x = glm::min(root.x, position.x);2853 auto position = entityItem->getPosition(); // If parent wasn't selected, we want absolute position, which isn't in properties.
2702 root.y = glm::min(root.y, position.y);2854 root.x = glm::min(root.x, position.x);
2703 root.z = glm::min(root.z, position.z);2855 root.y = glm::min(root.y, position.y);
2704 2856 root.z = glm::min(root.z, position.z);
2705 entities << entityItem;2857 }
2858 }
2859 entities[entityID] = entityItem;
2706 }2860 }
2707 2861
2708 if (entities.size() == 0) {2862 if (entities.size() == 0) {
2709 return false;2863 return false;
2710 }2864 }
2711 2865
2712 for (auto entityItem : entities) {2866 if (givenOffset) {
2713 auto properties = entityItem->getProperties();2867 root = *givenOffset;
2714 2868 }
2715 properties.setPosition(properties.getPosition() - root);2869 for (EntityItemPointer& entityDatum : entities) {
2716 exportTree->addEntity(entityItem->getEntityItemID(), properties);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);
2717 }2878 }
2718
2719 // remap IDs on export so that we aren't publishing the IDs of entities in our domain
2720 exportTree->remapIDs();
2721 2879
2722 exportTree->writeToJSONFile(filename.toLocal8Bit().constData());2880 exportTree->writeToJSONFile(filename.toLocal8Bit().constData());
2723 2881
@@ -2727,37 +2885,19 @@ bool Application::exportEntities(const QString& filename, const QVector<EntityIt · 3 unmodified lines
2727}2885}
2728 2886
2729bool Application::exportEntities(const QString& filename, float x, float y, float z, float scale) {2887bool Application::exportEntities(const QString& filename, float x, float y, float z, float scale) {
2888 glm::vec3 offset(x, y, z);
2730 QVector<EntityItemPointer> entities;2889 QVector<EntityItemPointer> entities;
2731 getEntities()->getTree()->findEntities(AACube(glm::vec3(x, y, z), scale), entities);2890 QVector<EntityItemID> ids;
2732 2891 getEntities()->getTree()->findEntities(AACube(offset, scale), entities);
2733 if (entities.size() > 0) {2892 foreach(EntityItemPointer entity, entities) {
2734 glm::vec3 root(x, y, z);2893 ids << entity->getEntityItemID();
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;
2752 }2894 }
2753 2895 return exportEntities(filename, ids, &offset);
2754 // restore the main window's active state
2755 _window->activateWindow();
2756 return true;
2757}2896}
2758 2897
2759void Application::loadSettings() {2898void Application::loadSettings() {
2760 2899
2900 sessionRunTime.set(0); // Just clean living. We're about to saveSettings, which will update value.
2761 DependencyManager::get<AudioClient>()->loadSettings();2901 DependencyManager::get<AudioClient>()->loadSettings();
2762 DependencyManager::get<LODManager>()->loadSettings();2902 DependencyManager::get<LODManager>()->loadSettings();
2763 2903
@@ -2771,6 +2911,7 @@ void Application::loadSettings() { · 7 unmodified lines
2771}2911}
2772 2912
2773void Application::saveSettings() {2913void Application::saveSettings() {
2914 sessionRunTime.set(_sessionRunTimer.elapsed() / MSECS_PER_SEC);
2774 DependencyManager::get<AudioClient>()->saveSettings();2915 DependencyManager::get<AudioClient>()->saveSettings();
2775 DependencyManager::get<LODManager>()->saveSettings();2916 DependencyManager::get<LODManager>()->saveSettings();
2776 2917
@@ -2782,16 +2923,8 @@ void Application::saveSettings() { · 5 unmodified lines
2782bool Application::importEntities(const QString& urlOrFilename) {2923bool Application::importEntities(const QString& urlOrFilename) {
2783 _entityClipboard->eraseAllOctreeElements();2924 _entityClipboard->eraseAllOctreeElements();
2784 2925
2785 QUrl url(urlOrFilename);2926 bool success = _entityClipboard->readFromURL(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());
2793 if (success) {2927 if (success) {
2794 _entityClipboard->remapIDs();
2795 _entityClipboard->reaverageOctreeElements();2928 _entityClipboard->reaverageOctreeElements();
2796 }2929 }
2797 return success;2930 return success;
@@ -2862,37 +2995,6 @@ void Application::init() { · 64 unmodified lines
2862 // Make sure any new sounds are loaded as soon as know about them.2995 // Make sure any new sounds are loaded as soon as know about them.
2863 connect(tree.get(), &EntityTree::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound);2996 connect(tree.get(), &EntityTree::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound);
2864 connect(getMyAvatar(), &MyAvatar::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound);2997 connect(getMyAvatar(), &MyAvatar::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound);
2865
2866 setAvatarUpdateThreading();
2867}
2868
2869const bool ENABLE_AVATAR_UPDATE_THREADING = false;
2870void Application::setAvatarUpdateThreading() {
2871 setAvatarUpdateThreading(ENABLE_AVATAR_UPDATE_THREADING);
2872}
2873void Application::setRawAvatarUpdateThreading() {
2874 setRawAvatarUpdateThreading(ENABLE_AVATAR_UPDATE_THREADING);
2875}
2876void 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}
2886void 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);
2896}2998}
2897 2999
2898void Application::updateLOD() {3000void Application::updateLOD() {
@@ -2905,6 +3007,11 @@ void Application::updateLOD() { · 6 unmodified lines
2905 }3007 }
2906}3008}
2907 3009
3010void Application::pushPreRenderLambda(void* key, std::function<void()> func) {
3011 std::unique_lock<std::mutex> guard(_preRenderLambdasLock);
3012 _preRenderLambdas[key] = func;
3013}
3014
2908// Called during Application::update immediately before AvatarManager::updateMyAvatar, updating my data that is then sent to everyone.3015// Called during Application::update immediately before AvatarManager::updateMyAvatar, updating my data that is then sent to everyone.
2909// (Maybe this code should be moved there?)3016// (Maybe this code should be moved there?)
2910// The principal result is to call updateLookAtTargetAvatar() and then setLookAtPosition().3017// The principal result is to call updateLookAtTargetAvatar() and then setLookAtPosition().
@@ -2920,7 +3027,7 @@ void Application::updateMyAvatarLookAtPosition() { · 9 unmodified lines
2920 auto eyeTracker = DependencyManager::get<EyeTracker>();3027 auto eyeTracker = DependencyManager::get<EyeTracker>();
2921 3028
2922 bool isLookingAtSomeone = false;3029 bool isLookingAtSomeone = false;
2923 bool isHMD = _avatarUpdate->isHMDMode();3030 bool isHMD = qApp->isHMDMode();
2924 glm::vec3 lookAtSpot;3031 glm::vec3 lookAtSpot;
2925 if (eyeTracker->isTracking() && (isHMD || eyeTracker->isSimulating())) {3032 if (eyeTracker->isTracking() && (isHMD || eyeTracker->isSimulating())) {
2926 // Look at the point that the user is looking at.3033 // Look at the point that the user is looking at.
@@ -2929,7 +3036,7 @@ void Application::updateMyAvatarLookAtPosition() { · 2 unmodified lines
2929 lookAtPosition.x = -lookAtPosition.x;3036 lookAtPosition.x = -lookAtPosition.x;
2930 }3037 }
2931 if (isHMD) {3038 if (isHMD) {
2932 glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose(_frameCount);3039 glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose();
2933 glm::quat hmdRotation = glm::quat_cast(headPose);3040 glm::quat hmdRotation = glm::quat_cast(headPose);
2934 lookAtSpot = _myCamera.getPosition() + myAvatar->getOrientation() * (hmdRotation * lookAtPosition);3041 lookAtSpot = _myCamera.getPosition() + myAvatar->getOrientation() * (hmdRotation * lookAtPosition);
2935 } else {3042 } else {
@@ -2971,9 +3078,9 @@ void Application::updateMyAvatarLookAtPosition() { · 35 unmodified lines
2971 } else {3078 } else {
2972 // I am not looking at anyone else, so just look forward3079 // I am not looking at anyone else, so just look forward
2973 if (isHMD) {3080 if (isHMD) {
2974 glm::mat4 headPose = _avatarUpdate->getHeadPose() ;3081 glm::mat4 headPose = myAvatar->getHMDSensorMatrix();
2975 glm::quat headRotation = glm::quat_cast(headPose);3082 glm::quat headRotation = glm::quat_cast(headPose);
2976 lookAtSpot = _myCamera.getPosition() +3083 lookAtSpot = myAvatar->getPosition() +
2977 myAvatar->getOrientation() * (headRotation * glm::vec3(0.0f, 0.0f, -TREE_SCALE));3084 myAvatar->getOrientation() * (headRotation * glm::vec3(0.0f, 0.0f, -TREE_SCALE));
2978 } else {3085 } else {
2979 lookAtSpot = myAvatar->getHead()->getEyePosition() +3086 lookAtSpot = myAvatar->getHead()->getEyePosition() +
@@ -3012,6 +3119,16 @@ void Application::updateThreads(float deltaTime) { · 32 unmodified lines
3012 }3119 }
3013}3120}
3014 3121
3122void Application::toggleOverlays() {
3123 auto newOverlaysVisible = !_overlayConductor.getEnabled();
3124 Menu::getInstance()->setIsOptionChecked(MenuOption::Overlays, newOverlaysVisible);
3125 _overlayConductor.setEnabled(newOverlaysVisible);
3126}
3127
3128void Application::setOverlaysVisible(bool visible) {
3129 _overlayConductor.setEnabled(visible);
3130}
3131
3015void Application::cycleCamera() {3132void Application::cycleCamera() {
3016 auto menu = Menu::getInstance();3133 auto menu = Menu::getInstance();
3017 if (menu->isOptionChecked(MenuOption::FullscreenMirror)) {3134 if (menu->isOptionChecked(MenuOption::FullscreenMirror)) {
@@ -3070,7 +3187,7 @@ void Application::reloadResourceCaches() { · 52 unmodified lines
3070 _viewFrustum.setOrientation(glm::quat());3187 _viewFrustum.setOrientation(glm::quat());
3071 queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions);3188 queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions);
3072 3189
3073 emptyLocalCache();3190 DependencyManager::get<AssetClient>()->clearCache();
3074 3191
3075 DependencyManager::get<AnimationCache>()->refreshAll();3192 DependencyManager::get<AnimationCache>()->refreshAll();
3076 DependencyManager::get<ModelCache>()->refreshAll();3193 DependencyManager::get<ModelCache>()->refreshAll();
@@ -3113,11 +3230,33 @@ void Application::updateDialogs(float deltaTime) { · 36 unmodified lines
3113}3230}
3114 3231
3115void Application::update(float deltaTime) {3232void Application::update(float deltaTime) {
3233
3234 PROFILE_RANGE_EX(__FUNCTION__, 0xffff0000, (uint64_t)_frameCount + 1);
3235
3116 bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);3236 bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
3117 PerformanceWarning warn(showWarnings, "Application::update()");3237 PerformanceWarning warn(showWarnings, "Application::update()");
3118 3238
3119 updateLOD();3239 updateLOD();
3120 3240
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
3121 {3260 {
3122 PerformanceTimer perfTimer("devices");3261 PerformanceTimer perfTimer("devices");
3123 DeviceTracker::updateAll();3262 DeviceTracker::updateAll();
@@ -3155,7 +3294,6 @@ void Application::update(float deltaTime) { · 31 unmodified lines
3155 3294
3156 auto myAvatar = getMyAvatar();3295 auto myAvatar = getMyAvatar();
3157 auto userInputMapper = DependencyManager::get<UserInputMapper>();3296 auto userInputMapper = DependencyManager::get<UserInputMapper>();
3158 userInputMapper->update(deltaTime);
3159 3297
3160 controller::InputCalibrationData calibrationData = {3298 controller::InputCalibrationData calibrationData = {
3161 myAvatar->getSensorToWorldMatrix(),3299 myAvatar->getSensorToWorldMatrix(),
@@ -3163,9 +3301,12 @@ void Application::update(float deltaTime) { · 1 unmodified line
3163 myAvatar->getHMDSensorMatrix()3301 myAvatar->getHMDSensorMatrix()
3164 };3302 };
3165 3303
3304 InputPluginPointer keyboardMousePlugin;
3166 bool jointsCaptured = false;3305 bool jointsCaptured = false;
3167 for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) {3306 for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) {
3168 if (inputPlugin->isActive()) {3307 if (inputPlugin->getName() == KeyboardMouseDevice::NAME) {
3308 keyboardMousePlugin = inputPlugin;
3309 } else if (inputPlugin->isActive()) {
3169 inputPlugin->pluginUpdate(deltaTime, calibrationData, jointsCaptured);3310 inputPlugin->pluginUpdate(deltaTime, calibrationData, jointsCaptured);
3170 if (inputPlugin->isJointController()) {3311 if (inputPlugin->isJointController()) {
3171 jointsCaptured = true;3312 jointsCaptured = true;
@@ -3173,6 +3314,12 @@ void Application::update(float deltaTime) { · 1 unmodified line
3173 }3314 }
3174 }3315 }
3175 3316
3317 userInputMapper->update(deltaTime);
3318
3319 if (keyboardMousePlugin && keyboardMousePlugin->isActive()) {
3320 keyboardMousePlugin->pluginUpdate(deltaTime, calibrationData, jointsCaptured);
3321 }
3322
3176 _controllerScriptingInterface->updateInputControllers();3323 _controllerScriptingInterface->updateInputControllers();
3177 3324
3178 // Transfer the user inputs to the driveKeys3325 // Transfer the user inputs to the driveKeys
@@ -3192,21 +3339,26 @@ void Application::update(float deltaTime) { · 13 unmodified lines
3192 myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z));3339 myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z));
3193 }3340 }
3194 3341
3195 controller::Pose leftHand = userInputMapper->getPoseState(controller::Action::LEFT_HAND);3342 controller::Pose leftHandPose = userInputMapper->getPoseState(controller::Action::LEFT_HAND);
3196 controller::Pose rightHand = userInputMapper->getPoseState(controller::Action::RIGHT_HAND);3343 controller::Pose rightHandPose = userInputMapper->getPoseState(controller::Action::RIGHT_HAND);
3197 Hand* hand = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHand();3344 auto myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition());
3198 setPalmData(hand, leftHand, deltaTime, HandData::LeftHand, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK));3345 auto worldToSensorMatrix = glm::inverse(myAvatar->getSensorToWorldMatrix());
3199 setPalmData(hand, rightHand, deltaTime, HandData::RightHand, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK));3346 auto avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix;
3347 myAvatar->setHandControllerPosesInSensorFrame(leftHandPose.transform(avatarToSensorMatrix), rightHandPose.transform(avatarToSensorMatrix));
3348
3200 updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...3349 updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
3201 updateDialogs(deltaTime); // update various stats dialogs if present3350 updateDialogs(deltaTime); // update various stats dialogs if present
3202 3351
3203 _avatarUpdate->synchronousProcess();3352 QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>();
3204 3353
3205 if (_physicsEnabled) {3354 if (_physicsEnabled) {
3355 PROFILE_RANGE_EX("Physics", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
3356
3206 PerformanceTimer perfTimer("physics");3357 PerformanceTimer perfTimer("physics");
3207 AvatarManager* avatarManager = DependencyManager::get<AvatarManager>().data();
3208 3358
3209 {3359 {
3360 PROFILE_RANGE_EX("UpdateStats", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
3361
3210 PerformanceTimer perfTimer("updateStates)");3362 PerformanceTimer perfTimer("updateStates)");
3211 static VectorOfMotionStates motionStates;3363 static VectorOfMotionStates motionStates;
3212 _entitySimulation.getObjectsToRemoveFromPhysics(motionStates);3364 _entitySimulation.getObjectsToRemoveFromPhysics(motionStates);
@@ -3239,15 +3391,18 @@ void Application::update(float deltaTime) { · 26 unmodified lines
3239 });3391 });
3240 }3392 }
3241 {3393 {
3394 PROFILE_RANGE_EX("StepSimulation", 0xffff8000, (uint64_t)getActiveDisplayPlugin()->presentCount());
3242 PerformanceTimer perfTimer("stepSimulation");3395 PerformanceTimer perfTimer("stepSimulation");
3243 getEntities()->getTree()->withWriteLock([&] {3396 getEntities()->getTree()->withWriteLock([&] {
3244 _physicsEngine->stepSimulation();3397 _physicsEngine->stepSimulation();
3245 });3398 });
3246 }3399 }
3247 {3400 {
3248 PerformanceTimer perfTimer("havestChanges");3401 PROFILE_RANGE_EX("HarvestChanges", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
3402 PerformanceTimer perfTimer("harvestChanges");
3249 if (_physicsEngine->hasOutgoingChanges()) {3403 if (_physicsEngine->hasOutgoingChanges()) {
3250 getEntities()->getTree()->withWriteLock([&] {3404 getEntities()->getTree()->withWriteLock([&] {
3405 PerformanceTimer perfTimer("handleOutgoingChanges");
3251 const VectorOfMotionStates& outgoingChanges = _physicsEngine->getOutgoingChanges();3406 const VectorOfMotionStates& outgoingChanges = _physicsEngine->getOutgoingChanges();
3252 _entitySimulation.handleOutgoingChanges(outgoingChanges, Physics::getSessionUUID());3407 _entitySimulation.handleOutgoingChanges(outgoingChanges, Physics::getSessionUUID());
3253 avatarManager->handleOutgoingChanges(outgoingChanges);3408 avatarManager->handleOutgoingChanges(outgoingChanges);
@@ -3263,6 +3418,7 @@ void Application::update(float deltaTime) { · 9 unmodified lines
3263 // Collision events (and their scripts) must not be handled when we're locked, above. (That would risk3418 // Collision events (and their scripts) must not be handled when we're locked, above. (That would risk
3264 // deadlock.)3419 // deadlock.)
3265 _entitySimulation.handleCollisionEvents(collisionEvents);3420 _entitySimulation.handleCollisionEvents(collisionEvents);
3421
3266 // NOTE: the getEntities()->update() call below will wait for lock3422 // NOTE: the getEntities()->update() call below will wait for lock
3267 // and will simulate entity motion (the EntityTree has been given an EntitySimulation).3423 // and will simulate entity motion (the EntityTree has been given an EntitySimulation).
3268 getEntities()->update(); // update the models...3424 getEntities()->update(); // update the models...
@@ -3273,7 +3429,27 @@ void Application::update(float deltaTime) { · 4 unmodified lines
3273 }3429 }
3274 }3430 }
3275 3431
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
3276 {3451 {
3452 PROFILE_RANGE_EX("Overlays", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
3277 PerformanceTimer perfTimer("overlays");3453 PerformanceTimer perfTimer("overlays");
3278 _overlays.update(deltaTime);3454 _overlays.update(deltaTime);
3279 }3455 }
@@ -3293,6 +3469,7 @@ void Application::update(float deltaTime) { · 13 unmodified lines
3293 3469
3294 // Update my voxel servers with my current voxel query...3470 // Update my voxel servers with my current voxel query...
3295 {3471 {
3472 PROFILE_RANGE_EX("QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
3296 PerformanceTimer perfTimer("queryOctree");3473 PerformanceTimer perfTimer("queryOctree");
3297 quint64 sinceLastQuery = now - _lastQueriedTime;3474 quint64 sinceLastQuery = now - _lastQueriedTime;
3298 const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;3475 const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
@@ -3330,8 +3507,15 @@ void Application::update(float deltaTime) { · 31 unmodified lines
3330 }3507 }
3331 }3508 }
3332 3509
3333 // update sensorToWorldMatrix for rendering camera.3510 {
3334 myAvatar->updateSensorToWorldMatrix();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 }
3335}3519}
3336 3520
3337 3521
@@ -3406,7 +3590,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node · 68 unmodified lines
3406 _octreeQuery.setCameraNearClip(_viewFrustum.getNearClip());3590 _octreeQuery.setCameraNearClip(_viewFrustum.getNearClip());
3407 _octreeQuery.setCameraFarClip(_viewFrustum.getFarClip());3591 _octreeQuery.setCameraFarClip(_viewFrustum.getFarClip());
3408 _octreeQuery.setCameraEyeOffsetPosition(glm::vec3());3592 _octreeQuery.setCameraEyeOffsetPosition(glm::vec3());
3409 _octreeQuery.setKeyholeRadius(_viewFrustum.getKeyholeRadius());3593 _octreeQuery.setCameraCenterRadius(_viewFrustum.getCenterRadius());
3410 auto lodManager = DependencyManager::get<LODManager>();3594 auto lodManager = DependencyManager::get<LODManager>();
3411 _octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale());3595 _octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale());
3412 _octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust());3596 _octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust());
@@ -3442,9 +3626,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node · 29 unmodified lines
3442 rootDetails.y * TREE_SCALE,3626 rootDetails.y * TREE_SCALE,
3443 rootDetails.z * TREE_SCALE) - glm::vec3(HALF_TREE_SCALE),3627 rootDetails.z * TREE_SCALE) - glm::vec3(HALF_TREE_SCALE),
3444 rootDetails.s * TREE_SCALE);3628 rootDetails.s * TREE_SCALE);
3445 ViewFrustum::location serverFrustumLocation = _viewFrustum.cubeInFrustum(serverBounds);3629 if (_viewFrustum.cubeIntersectsKeyhole(serverBounds)) {
3446
3447 if (serverFrustumLocation != ViewFrustum::OUTSIDE) {
3448 inViewServers++;3630 inViewServers++;
3449 }3631 }
3450 }3632 }
@@ -3510,12 +3692,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node · 59 unmodified lines
3510 rootDetails.s * TREE_SCALE);3692 rootDetails.s * TREE_SCALE);
3511 3693
3512 3694
3513 ViewFrustum::location serverFrustumLocation = _viewFrustum.cubeInFrustum(serverBounds);3695 inView = _viewFrustum.cubeIntersectsKeyhole(serverBounds);
3514 if (serverFrustumLocation != ViewFrustum::OUTSIDE) {
3515 inView = true;
3516 } else {
3517 inView = false;
3518 }
3519 } else {3696 } else {
3520 if (wantExtraDebugging) {3697 if (wantExtraDebugging) {
3521 qCDebug(interfaceapp) << "Jurisdiction without RootCode for node " << *node << ". That's unusual!";3698 qCDebug(interfaceapp) << "Jurisdiction without RootCode for node " << *node << ". That's unusual!";
@@ -3740,19 +3917,19 @@ namespace render { · 218 unmodified lines
3740 switch (backgroundMode) {3917 switch (backgroundMode) {
3741 case model::SunSkyStage::SKY_BOX: {3918 case model::SunSkyStage::SKY_BOX: {
3742 auto skybox = skyStage->getSkybox();3919 auto skybox = skyStage->getSkybox();
3743 if (skybox && skybox->getCubemap() && skybox->getCubemap()->isDefined()) {3920 if (skybox) {
3744 PerformanceTimer perfTimer("skybox");3921 PerformanceTimer perfTimer("skybox");
3745 skybox->render(batch, *(args->_viewFrustum));3922 skybox->render(batch, *(args->_viewFrustum));
3746 break;3923 break;
3747 }3924 }
3748 // If no skybox texture is available, render the SKY_DOME while it loads
3749 }3925 }
3750 // fall through to next case3926
3927 // Fall through: if no skybox is available, render the SKY_DOME
3751 case model::SunSkyStage::SKY_DOME: {3928 case model::SunSkyStage::SKY_DOME: {
3752 if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {3929 if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
3753 PerformanceTimer perfTimer("stars");3930 PerformanceTimer perfTimer("stars");
3754 PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),3931 PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
3755 "Application::payloadRender<BackgroundRenderData>() ... stars...");3932 "Application::payloadRender<BackgroundRenderData>() ... My god, it's full of stars...");
3756 // should be the first rendering pass - w/o depth buffer / lighting3933 // should be the first rendering pass - w/o depth buffer / lighting
3757 3934
3758 static const float alpha = 1.0f;3935 static const float alpha = 1.0f;
@@ -3760,6 +3937,7 @@ namespace render { · 1 unmodified line
3760 }3937 }
3761 }3938 }
3762 break;3939 break;
3940
3763 case model::SunSkyStage::NO_BACKGROUND:3941 case model::SunSkyStage::NO_BACKGROUND:
3764 default:3942 default:
3765 // this line intentionally left blank3943 // this line intentionally left blank
@@ -3774,9 +3952,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 8 unmodified lines
3774 // FIXME: This preRender call is temporary until we create a separate render::scene for the mirror rendering.3952 // FIXME: This preRender call is temporary until we create a separate render::scene for the mirror rendering.
3775 // Then we can move this logic into the Avatar::simulate call.3953 // Then we can move this logic into the Avatar::simulate call.
3776 auto myAvatar = getMyAvatar();3954 auto myAvatar = getMyAvatar();
3777 myAvatar->startRender();
3778 myAvatar->preRender(renderArgs);3955 myAvatar->preRender(renderArgs);
3779 myAvatar->endRender();
3780 3956
3781 // Update animation debug draw renderer3957 // Update animation debug draw renderer
3782 AnimDebugDraw::getInstance().update();3958 AnimDebugDraw::getInstance().update();
@@ -3794,8 +3970,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 11 unmodified lines
3794 // The pending changes collecting the changes here3970 // The pending changes collecting the changes here
3795 render::PendingChanges pendingChanges;3971 render::PendingChanges pendingChanges;
3796 3972
3973 // FIXME: Move this out of here!, Background / skybox should be driven by the enityt content just like the other entities
3797 // Background rendering decision3974 // Background rendering decision
3798 if (BackgroundRenderData::_item == 0) {3975 if (!render::Item::isValidID(BackgroundRenderData::_item)) {
3799 auto backgroundRenderData = make_shared<BackgroundRenderData>();3976 auto backgroundRenderData = make_shared<BackgroundRenderData>();
3800 auto backgroundRenderPayload = make_shared<BackgroundRenderData::Payload>(backgroundRenderData);3977 auto backgroundRenderPayload = make_shared<BackgroundRenderData::Payload>(backgroundRenderData);
3801 BackgroundRenderData::_item = _main3DScene->allocateID();3978 BackgroundRenderData::_item = _main3DScene->allocateID();
@@ -3820,8 +3997,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 18 unmodified lines
3820 }3997 }
3821 }3998 }
3822 3999
4000 // FIXME: Move this out of here!, WorldBox should be driven by the entity content just like the other entities
3823 // Make sure the WorldBox is in the scene4001 // Make sure the WorldBox is in the scene
3824 if (WorldBoxRenderData::_item == 0) {4002 if (!render::Item::isValidID(WorldBoxRenderData::_item)) {
3825 auto worldBoxRenderData = make_shared<WorldBoxRenderData>();4003 auto worldBoxRenderData = make_shared<WorldBoxRenderData>();
3826 auto worldBoxRenderPayload = make_shared<WorldBoxRenderData::Payload>(worldBoxRenderData);4004 auto worldBoxRenderPayload = make_shared<WorldBoxRenderData::Payload>(worldBoxRenderData);
3827 4005
@@ -3835,18 +4013,10 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 7 unmodified lines
3835 });4013 });
3836 }4014 }
3837 4015
3838 // Setup the current Zone Entity lighting and skybox4016 // Setup the current Zone Entity lighting
3839 // Fixme: We need a better soution through an actual render item !!!
3840 {4017 {
3841 DependencyManager::get<DeferredLightingEffect>()->setAmbientLightMode(getRenderAmbientLight());4018 auto stage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
3842 auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();4019 DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(stage->getSunLight());
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);
3850 }4020 }
3851 4021
3852 {4022 {
@@ -3864,9 +4034,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 11 unmodified lines
3864 _renderEngine->getRenderContext()->args = renderArgs;4034 _renderEngine->getRenderContext()->args = renderArgs;
3865 4035
3866 // Before the deferred pass, let's try to use the render engine4036 // Before the deferred pass, let's try to use the render engine
3867 myAvatar->startRenderRun();
3868 _renderEngine->run();4037 _renderEngine->run();
3869 myAvatar->endRenderRun();
3870 }4038 }
3871 4039
3872 activeRenderingThread = nullptr;4040 activeRenderingThread = nullptr;
@@ -3928,16 +4096,8 @@ void Application::resetSensors(bool andReload) { · 55 unmodified lines
3928 DependencyManager::get<Faceshift>()->reset();4096 DependencyManager::get<Faceshift>()->reset();
3929 DependencyManager::get<DdeFaceTracker>()->reset();4097 DependencyManager::get<DdeFaceTracker>()->reset();
3930 DependencyManager::get<EyeTracker>()->reset();4098 DependencyManager::get<EyeTracker>()->reset();
3931
3932 getActiveDisplayPlugin()->resetSensors();4099 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
3939 getMyAvatar()->reset(andReload);4100 getMyAvatar()->reset(andReload);
3940
3941 QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "reset", Qt::QueuedConnection);4101 QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "reset", Qt::QueuedConnection);
3942}4102}
3943 4103
@@ -3990,46 +4150,40 @@ void Application::clearDomainOctreeDetails() { · 46 unmodified lines
3990void Application::domainChanged(const QString& domainHostname) {4150void Application::domainChanged(const QString& domainHostname) {
3991 updateWindowTitle();4151 updateWindowTitle();
3992 clearDomainOctreeDetails();4152 clearDomainOctreeDetails();
3993 _domainConnectionRefusals.clear();
3994 // disable physics until we have enough information about our new location to not cause craziness.4153 // disable physics until we have enough information about our new location to not cause craziness.
3995 _physicsEnabled = false;4154 _physicsEnabled = false;
3996}4155}
3997 4156
3998void 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
4017void Application::connectedToDomain(const QString& hostname) {
4018 AccountManager& accountManager = AccountManager::getInstance();
4019 const QUuid& domainID = DependencyManager::get<NodeList>()->getDomainHandler().getUUID();
4020 4157
4021 if (accountManager.isLoggedIn() && !domainID.isNull()) {4158void Application::resettingDomain() {
4022 _notifiedPacketVersionMismatchThisDomain = false;4159 _notifiedPacketVersionMismatchThisDomain = false;
4023 }
4024}4160}
4025 4161
4026void Application::nodeAdded(SharedNodePointer node) {4162void Application::nodeAdded(SharedNodePointer node) {
4027 if (node->getType() == NodeType::AvatarMixer) {4163 if (node->getType() == NodeType::AvatarMixer) {
4028 // new avatar mixer, send off our identity packet right away4164 // new avatar mixer, send off our identity packet right away
4029 getMyAvatar()->sendIdentityPacket();4165 getMyAvatar()->sendIdentityPacket();
4030 } else if (node->getType() == NodeType::AssetServer) {4166 }
4031 // the addition of an asset-server always re-enables the upload to asset server menu option4167}
4032 Menu::getInstance()->getActionForOption(MenuOption::UploadAsset)->setEnabled(true);4168
4169void 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 }
4033 }4187 }
4034}4188}
4035 4189
@@ -4045,10 +4199,7 @@ void Application::nodeKilled(SharedNodePointer node) { · 9 unmodified lines
4045 4199
4046 if (node->getType() == NodeType::AudioMixer) {4200 if (node->getType() == NodeType::AudioMixer) {
4047 QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "audioMixerKilled");4201 QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "audioMixerKilled");
4048 }4202 } else if (node->getType() == NodeType::EntityServer) {
4049
4050 if (node->getType() == NodeType::EntityServer) {
4051
4052 QUuid nodeUUID = node->getUUID();4203 QUuid nodeUUID = node->getUUID();
4053 // see if this is the first we've heard of this node...4204 // see if this is the first we've heard of this node...
4054 _entityServerJurisdictions.withReadLock([&] {4205 _entityServerJurisdictions.withReadLock([&] {
@@ -4079,10 +4230,16 @@ void Application::nodeKilled(SharedNodePointer node) { · 24 unmodified lines
4079 } else if (node->getType() == NodeType::AvatarMixer) {4230 } else if (node->getType() == NodeType::AvatarMixer) {
4080 // our avatar mixer has gone away - clear the hash of avatars4231 // our avatar mixer has gone away - clear the hash of avatars
4081 DependencyManager::get<AvatarManager>()->clearOtherAvatars();4232 DependencyManager::get<AvatarManager>()->clearOtherAvatars();
4082 } else if (node->getType() == NodeType::AssetServer4233 } else if (node->getType() == NodeType::AssetServer) {
4083 && !DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::AssetServer)) {4234 // asset server going away - check if we have the asset browser showing
4084 // this was our last asset server - disable the menu option to upload an asset4235
4085 Menu::getInstance()->getActionForOption(MenuOption::UploadAsset)->setEnabled(false);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 }
4086 }4243 }
4087}4244}
4088void Application::trackIncomingOctreePacket(ReceivedMessage& message, SharedNodePointer sendingNode, bool wasStatsPacket) {4245void Application::trackIncomingOctreePacket(ReceivedMessage& message, SharedNodePointer sendingNode, bool wasStatsPacket) {
@@ -4173,22 +4330,7 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer · 84 unmodified lines
4173 });4330 });
4174 });4331 });
4175 4332
4176 if (!_physicsEnabled) {4333 _processOctreeStatsCounter++;
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 }
4192 4334
4193 return statsMessageLength;4335 return statsMessageLength;
4194}4336}
@@ -4270,7 +4412,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri · 75 unmodified lines
4270 scriptEngine->registerGlobalObject("Render", _renderEngine->getConfiguration().get());4412 scriptEngine->registerGlobalObject("Render", _renderEngine->getConfiguration().get());
4271 4413
4272 scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());4414 scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
4273 scriptEngine->registerGlobalObject("Reticle", _compositor.getReticleInterface());4415 scriptEngine->registerGlobalObject("Reticle", getApplicationCompositor().getReticleInterface());
4274}4416}
4275 4417
4276bool Application::canAcceptURL(const QString& urlString) const {4418bool Application::canAcceptURL(const QString& urlString) const {
@@ -4308,7 +4450,10 @@ bool Application::acceptURL(const QString& urlString, bool defaultUpload) { · 31 unmodified lines
4308 }4450 }
4309 }4451 }
4310 4452
4311 return defaultUpload && askToUploadAsset(urlString);4453 if (defaultUpload) {
4454 toggleAssetServerWidget(urlString);
4455 }
4456 return defaultUpload;
4312}4457}
4313 4458
4314void Application::setSessionUUID(const QUuid& sessionUUID) {4459void Application::setSessionUUID(const QUuid& sessionUUID) {
@@ -4336,8 +4481,8 @@ bool Application::askToSetAvatarUrl(const QString& url) { · 21 unmodified lines
4336 4481
4337 case FSTReader::HEAD_AND_BODY_MODEL:4482 case FSTReader::HEAD_AND_BODY_MODEL:
4338 ok = QMessageBox::Ok == OffscreenUi::question("Set Avatar",4483 ok = QMessageBox::Ok == OffscreenUi::question("Set Avatar",
4339 "Would you like to use '" + modelName + "' for your avatar?",4484 "Would you like to use '" + modelName + "' for your avatar?",
4340 QMessageBox::Ok | QMessageBox::Cancel);4485 QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok);
4341 break;4486 break;
4342 4487
4343 default:4488 default:
@@ -4370,79 +4515,6 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) { · 26 unmodified lines
4370 return true;4515 return true;
4371}4516}
4372 4517
4373bool 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
4423void 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
4446bool Application::askToWearAvatarAttachmentUrl(const QString& url) {4518bool Application::askToWearAvatarAttachmentUrl(const QString& url) {
4447 4519
4448 QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();4520 QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
@@ -4542,6 +4614,22 @@ void Application::toggleRunningScriptsWidget() { · 93 unmodified lines
4542 //}4614 //}
4543}4615}
4544 4616
4617void 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
4545void Application::packageModel() {4633void Application::packageModel() {
4546 ModelPackager::package();4634 ModelPackager::package();
4547}4635}
@@ -4557,33 +4645,6 @@ void Application::openUrl(const QUrl& url) { · 9 unmodified lines
4557 }4645 }
4558}4646}
4559 4647
4560void 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
4587void Application::loadDialog() {4648void Application::loadDialog() {
4588 auto scriptEngines = DependencyManager::get<ScriptEngines>();4649 auto scriptEngines = DependencyManager::get<ScriptEngines>();
4589 QString fileNameString = OffscreenUi::getOpenFileName(4650 QString fileNameString = OffscreenUi::getOpenFileName(
@@ -4657,34 +4718,6 @@ float Application::getRenderResolutionScale() const { · 67 unmodified lines
4657 }4718 }
4658}4719}
4659 4720
4660int 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
4688void Application::notifyPacketVersionMismatch() {4721void Application::notifyPacketVersionMismatch() {
4689 if (!_notifiedPacketVersionMismatchThisDomain) {4722 if (!_notifiedPacketVersionMismatchThisDomain) {
4690 _notifiedPacketVersionMismatchThisDomain = true;4723 _notifiedPacketVersionMismatchThisDomain = true;
@@ -4697,7 +4730,7 @@ void Application::notifyPacketVersionMismatch() { · 6 unmodified lines
4697}4730}
4698 4731
4699void Application::checkSkeleton() {4732void Application::checkSkeleton() {
4700 if (getMyAvatar()->getSkeletonModel().isActive() && !getMyAvatar()->getSkeletonModel().hasSkeleton()) {4733 if (getMyAvatar()->getSkeletonModel()->isActive() && !getMyAvatar()->getSkeletonModel()->hasSkeleton()) {
4701 qCDebug(interfaceapp) << "MyAvatar model has no skeleton";4734 qCDebug(interfaceapp) << "MyAvatar model has no skeleton";
4702 4735
4703 QString message = "Your selected avatar body has no skeleton.\n\nThe default body will be loaded...";4736 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
4786}4819}
4787 4820
4788DisplayPlugin* Application::getActiveDisplayPlugin() {4821DisplayPlugin* Application::getActiveDisplayPlugin() {
4789 if (nullptr == _displayPlugin) {4822 DisplayPlugin* result = nullptr;
4790 updateDisplayMode();4823 if (QThread::currentThread() == thread()) {
4791 Q_ASSERT(_displayPlugin);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();
4792 }4832 }
4793 return _displayPlugin.get();4833 return result;
4794}4834}
4795 4835
4796const DisplayPlugin* Application::getActiveDisplayPlugin() const {4836const DisplayPlugin* Application::getActiveDisplayPlugin() const {
@@ -4813,7 +4853,7 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti · 16 unmodified lines
4813 groupingMenu = "Developer";4853 groupingMenu = "Developer";
4814 break;4854 break;
4815 default:4855 default:
4816 groupingMenu = "Standard"; 4856 groupingMenu = "Standard";
4817 break;4857 break;
4818 }4858 }
4819 4859
@@ -4826,7 +4866,7 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti · 6 unmodified lines
4826 auto action = menu->addActionToQMenuAndActionHash(parent,4866 auto action = menu->addActionToQMenuAndActionHash(parent,
4827 name, 0, qApp,4867 name, 0, qApp,
4828 SLOT(updateDisplayMode()),4868 SLOT(updateDisplayMode()),
4829 QAction::NoRole, UNSPECIFIED_POSITION, groupingMenu);4869 QAction::NoRole, Menu::UNSPECIFIED_POSITION, groupingMenu);
4830 4870
4831 action->setCheckable(true);4871 action->setCheckable(true);
4832 action->setChecked(active);4872 action->setChecked(active);
@@ -4835,6 +4875,19 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti · 2 unmodified lines
4835}4875}
4836 4876
4837void Application::updateDisplayMode() {4877void 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
4838 auto menu = Menu::getInstance();4891 auto menu = Menu::getInstance();
4839 auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins();4892 auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins();
4840 4893
@@ -4891,71 +4944,62 @@ void Application::updateDisplayMode() { · 50 unmodified lines
4891 }4944 }
4892 }4945 }
4893 4946
4894 auto offscreenUi = DependencyManager::get<OffscreenUi>();4947 if (newDisplayPlugin == _displayPlugin) {
4895 DisplayPluginPointer oldDisplayPlugin = _displayPlugin;
4896 if (newDisplayPlugin == oldDisplayPlugin) {
4897 return;4948 return;
4898 }4949 }
4899 4950
4900 // Some plugins *cough* Oculus *cough* process message events from inside their4951 auto offscreenUi = DependencyManager::get<OffscreenUi>();
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 }
4914 4952
4953 // Make the switch atomic from the perspective of other threads
4954 {
4955 std::unique_lock<std::mutex> lock(_displayPluginLock);
4915 4956
4916 if (!_pluginContainer->currentDisplayActions().isEmpty()) {4957 auto oldDisplayPlugin = _displayPlugin;
4917 auto menu = Menu::getInstance();4958 if (_displayPlugin) {
4918 foreach(auto itemInfo, _pluginContainer->currentDisplayActions()) {4959 _displayPlugin->deactivate();
4919 menu->removeMenuItem(itemInfo.first, itemInfo.second);
4920 }4960 }
4921 _pluginContainer->currentDisplayActions().clear();
4922 }
4923 4961
4924 if (newDisplayPlugin) {4962 // FIXME probably excessive and useless context switching
4925 _offscreenContext->makeCurrent();
4926 newDisplayPlugin->activate();
4927 _offscreenContext->makeCurrent();4963 _offscreenContext->makeCurrent();
4928 offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize()));
4929 _offscreenContext->makeCurrent();
4930 }
4931 4964
4932 oldDisplayPlugin = _displayPlugin;4965 bool active = newDisplayPlugin->activate();
4933 _displayPlugin = newDisplayPlugin;
4934 4966
4935 // If the displayPlugin is a screen based HMD, then it will want the HMDTools displayed4967 if (!active) {
4936 // Direct Mode HMDs (like windows Oculus) will be isHmd() but will have a screen of -14968 // If the new plugin fails to activate, fallback to last display
4937 bool newPluginWantsHMDTools = newDisplayPlugin ?4969 qWarning() << "Failed to activate display: " << newDisplayPlugin->getName();
4938 (newDisplayPlugin->isHmd() && (newDisplayPlugin->getHmdScreen() >= 0)) : false;4970 newDisplayPlugin = oldDisplayPlugin;
4939 bool oldPluginWantedHMDTools = oldDisplayPlugin ?
4940 (oldDisplayPlugin->isHmd() && (oldDisplayPlugin->getHmdScreen() >= 0)) : false;
4941 4971
4942 // Only show the hmd tools after the correct plugin has4972 if (newDisplayPlugin) {
4943 // been activated so that it's UI is setup correctly4973 qWarning() << "Falling back to last display: " << newDisplayPlugin->getName();
4944 if (newPluginWantsHMDTools) {4974 active = newDisplayPlugin->activate();
4945 _pluginContainer->showDisplayPluginsTools();4975 }
4946 }
4947 4976
4948 if (oldDisplayPlugin) {4977 // If there is no last display, or
4949 oldDisplayPlugin->deactivate();4978 // If the last display fails to activate, fallback to desktop
4950 _offscreenContext->makeCurrent();4979 if (!active) {
4980 newDisplayPlugin = displayPlugins.at(0);
4981 qWarning() << "Falling back to display: " << newDisplayPlugin->getName();
4982 active = newDisplayPlugin->activate();
4983 }
4951 4984
4952 // if the old plugin was HMD and the new plugin is not HMD, then hide our hmdtools4985 if (!active) {
4953 if (oldPluginWantedHMDTools && !newPluginWantsHMDTools) {4986 qFatal("Failed to activate fallback plugin");
4954 DependencyManager::get<DialogsManager>()->hmdTools(false);4987 }
4955 }4988 }
4989
4990 _offscreenContext->makeCurrent();
4991 offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize()));
4992 _offscreenContext->makeCurrent();
4993 getApplicationCompositor().setDisplayPlugin(newDisplayPlugin);
4994 _displayPlugin = newDisplayPlugin;
4956 }4995 }
4996
4997
4957 emit activeDisplayPluginChanged();4998 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
4959 Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");5003 Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");
4960}5004}
4961 5005
@@ -5049,70 +5093,11 @@ mat4 Application::getEyeOffset(int eye) const { · 87 unmodified lines
5049 5093
5050mat4 Application::getHMDSensorPose() const {5094mat4 Application::getHMDSensorPose() const {
5051 if (isHMDMode()) {5095 if (isHMDMode()) {
5052 return getActiveDisplayPlugin()->getHeadPose(_frameCount);5096 return getActiveDisplayPlugin()->getHeadPose();
5053 }5097 }
5054 return mat4();5098 return mat4();
5055}5099}
5056 5100
5057void 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
5116void Application::crashApplication() {5101void Application::crashApplication() {
5117 qCDebug(interfaceapp) << "Intentionally crashed Interface";5102 qCDebug(interfaceapp) << "Intentionally crashed Interface";
5118 QObject* object = nullptr;5103 QObject* object = nullptr;
@@ -5120,6 +5105,15 @@ void Application::crashApplication() { · 1 unmodified line
5120 Q_UNUSED(value);5105 Q_UNUSED(value);
5121}5106}
5122 5107
5108void 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
5123void Application::setActiveDisplayPlugin(const QString& pluginName) {5117void Application::setActiveDisplayPlugin(const QString& pluginName) {
5124 auto menu = Menu::getInstance();5118 auto menu = Menu::getInstance();
5125 foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) {5119 foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) {
@@ -5158,3 +5152,13 @@ void Application::readArgumentsFromLocalSocket() { · 32 unmodified lines
5158 qApp->openUrl(QString::fromUtf8(message));5152 qApp->openUrl(QString::fromUtf8(message));
5159 }5153 }
5160}5154}
5155
5156void Application::showDesktop() {
5157 if (!_overlayConductor.getEnabled()) {
5158 _overlayConductor.setEnabled(true);
5159 }
5160}
5161
5162CompositorHelper& Application::getApplicationCompositor() const {
5163 return *DependencyManager::get<CompositorHelper>();
5164}