Showing the changes to interface/src/Application.cpp (1 of 846 files). Show all
Unified · Split
| @@ -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 | |||
| 194 | static const QString OBJ_EXTENSION = ".obj"; | 195 | static const QString OBJ_EXTENSION = ".obj"; |
| 195 | static const QString AVA_JSON_EXTENSION = ".ava.json"; | 196 | static const QString AVA_JSON_EXTENSION = ".ava.json"; |
| 196 | 197 | ||
| 198 | static const int MSECS_PER_SEC = 1000; | ||
| 197 | static const int MIRROR_VIEW_TOP_PADDING = 5; | 199 | static const int MIRROR_VIEW_TOP_PADDING = 5; |
| 198 | static const int MIRROR_VIEW_LEFT_PADDING = 10; | 200 | static const int MIRROR_VIEW_LEFT_PADDING = 10; |
| 199 | static const int MIRROR_VIEW_WIDTH = 265; | 201 | static const int MIRROR_VIEW_WIDTH = 265; |
| @@ -210,8 +212,6 @@ static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html"; · 10 unmodified lines | |||
| 210 | 212 | ||
| 211 | static const unsigned int THROTTLED_SIM_FRAMERATE = 15; | 213 | static const unsigned int THROTTLED_SIM_FRAMERATE = 15; |
| 212 | static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; | 214 | static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; |
| 213 | static const unsigned int CAPPED_SIM_FRAMERATE = 120; | ||
| 214 | static const int CAPPED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / CAPPED_SIM_FRAMERATE; | ||
| 215 | 215 | ||
| 216 | static const uint32_t INVALID_FRAME = UINT32_MAX; | 216 | static const uint32_t INVALID_FRAME = UINT32_MAX; |
| 217 | 217 | ||
| @@ -224,7 +224,6 @@ static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStanda · 6 unmodified lines | |||
| 224 | static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).append("/script.js"); | 224 | static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).append("/script.js"); |
| 225 | #endif | 225 | #endif |
| 226 | 226 | ||
| 227 | const QString DEFAULT_SCRIPTS_JS_URL = "http://s3.amazonaws.com/hifi-public/scripts/defaultScripts.js"; | ||
| 228 | Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS); | 227 | Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS); |
| 229 | 228 | ||
| 230 | const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensions { | 229 | const 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 | ||
| 238 | class DeadlockWatchdogThread : public QThread { | ||
| 239 | public: | ||
| 240 | static const unsigned long HEARTBEAT_CHECK_INTERVAL_SECS = 1; | ||
| 241 | static const unsigned long HEARTBEAT_UPDATE_INTERVAL_SECS = 1; | ||
| 242 | static const unsigned long HEARTBEAT_REPORT_INTERVAL_USECS = 5 * USECS_PER_SECOND; | ||
| 243 | static const unsigned long MAX_HEARTBEAT_AGE_USECS = 30 * USECS_PER_SECOND; | ||
| 244 | static const int WARNING_ELAPSED_HEARTBEAT = 500 * USECS_PER_MSEC; // warn if elapsed heartbeat average is large | ||
| 245 | static const int HEARTBEAT_SAMPLES = 100000; // ~5 seconds worth of samples | ||
| 246 | |||
| 247 | // Set the heartbeat on launch | ||
| 248 | DeadlockWatchdogThread() { | ||
| 249 | setObjectName("Deadlock Watchdog"); | ||
| 250 | // Give the heartbeat an initial value | ||
| 251 | _heartbeat = usecTimestampNow(); | ||
| 252 | connect(qApp, &QCoreApplication::aboutToQuit, [this] { | ||
| 253 | _quit = true; | ||
| 254 | }); | ||
| 255 | } | ||
| 256 | |||
| 257 | void updateHeartbeat() { | ||
| 258 | auto now = usecTimestampNow(); | ||
| 259 | auto elapsed = now - _heartbeat; | ||
| 260 | _movingAverage.addSample(elapsed); | ||
| 261 | _heartbeat = now; | ||
| 262 | } | ||
| 263 | |||
| 264 | void deadlockDetectionCrash() { | ||
| 265 | uint32_t* crashTrigger = nullptr; | ||
| 266 | *crashTrigger = 0xDEAD10CC; | ||
| 267 | } | ||
| 268 | |||
| 269 | void run() override { | ||
| 270 | while (!_quit) { | ||
| 271 | QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS); | ||
| 272 | |||
| 273 | uint64_t lastHeartbeat = _heartbeat; // sample atomic _heartbeat, because we could context switch away and have it updated on us | ||
| 274 | uint64_t now = usecTimestampNow(); | ||
| 275 | auto lastHeartbeatAge = (now > lastHeartbeat) ? now - lastHeartbeat : 0; | ||
| 276 | auto sinceLastReport = (now > _lastReport) ? now - _lastReport : 0; | ||
| 277 | auto elapsedMovingAverage = _movingAverage.getAverage(); | ||
| 278 | |||
| 279 | if (elapsedMovingAverage > _maxElapsedAverage) { | ||
| 280 | qDebug() << "DEADLOCK WATCHDOG NEW maxElapsedAverage:" | ||
| 281 | << "lastHeartbeatAge:" << lastHeartbeatAge | ||
| 282 | << "elapsedMovingAverage:" << elapsedMovingAverage | ||
| 283 | << "maxElapsed:" << _maxElapsed | ||
| 284 | << "PREVIOUS maxElapsedAverage:" << _maxElapsedAverage | ||
| 285 | << "NEW maxElapsedAverage:" << elapsedMovingAverage | ||
| 286 | << "samples:" << _movingAverage.getSamples(); | ||
| 287 | _maxElapsedAverage = elapsedMovingAverage; | ||
| 288 | } | ||
| 289 | if (lastHeartbeatAge > _maxElapsed) { | ||
| 290 | qDebug() << "DEADLOCK WATCHDOG NEW maxElapsed:" | ||
| 291 | << "lastHeartbeatAge:" << lastHeartbeatAge | ||
| 292 | << "elapsedMovingAverage:" << elapsedMovingAverage | ||
| 293 | << "PREVIOUS maxElapsed:" << _maxElapsed | ||
| 294 | << "NEW maxElapsed:" << lastHeartbeatAge | ||
| 295 | << "maxElapsedAverage:" << _maxElapsedAverage | ||
| 296 | << "samples:" << _movingAverage.getSamples(); | ||
| 297 | _maxElapsed = lastHeartbeatAge; | ||
| 298 | } | ||
| 299 | if ((sinceLastReport > HEARTBEAT_REPORT_INTERVAL_USECS) || (elapsedMovingAverage > WARNING_ELAPSED_HEARTBEAT)) { | ||
| 300 | qDebug() << "DEADLOCK WATCHDOG STATUS -- lastHeartbeatAge:" << lastHeartbeatAge | ||
| 301 | << "elapsedMovingAverage:" << elapsedMovingAverage | ||
| 302 | << "maxElapsed:" << _maxElapsed | ||
| 303 | << "maxElapsedAverage:" << _maxElapsedAverage | ||
| 304 | << "samples:" << _movingAverage.getSamples(); | ||
| 305 | _lastReport = now; | ||
| 306 | } | ||
| 307 | |||
| 308 | #ifdef NDEBUG | ||
| 309 | if (lastHeartbeatAge > MAX_HEARTBEAT_AGE_USECS) { | ||
| 310 | qDebug() << "DEADLOCK DETECTED -- " | ||
| 311 | << "lastHeartbeatAge:" << lastHeartbeatAge | ||
| 312 | << "[ lastHeartbeat :" << lastHeartbeat | ||
| 313 | << "now:" << now << " ]" | ||
| 314 | << "elapsedMovingAverage:" << elapsedMovingAverage | ||
| 315 | << "maxElapsed:" << _maxElapsed | ||
| 316 | << "maxElapsedAverage:" << _maxElapsedAverage | ||
| 317 | << "samples:" << _movingAverage.getSamples(); | ||
| 318 | deadlockDetectionCrash(); | ||
| 319 | } | ||
| 320 | #endif | ||
| 321 | } | ||
| 322 | } | ||
| 323 | |||
| 324 | static std::atomic<uint64_t> _heartbeat; | ||
| 325 | static std::atomic<uint64_t> _lastReport; | ||
| 326 | static std::atomic<uint64_t> _maxElapsed; | ||
| 327 | static std::atomic<int> _maxElapsedAverage; | ||
| 328 | static ThreadSafeMovingAverage<int, HEARTBEAT_SAMPLES> _movingAverage; | ||
| 329 | |||
| 330 | bool _quit { false }; | ||
| 331 | }; | ||
| 332 | |||
| 333 | std::atomic<uint64_t> DeadlockWatchdogThread::_heartbeat; | ||
| 334 | std::atomic<uint64_t> DeadlockWatchdogThread::_lastReport; | ||
| 335 | std::atomic<uint64_t> DeadlockWatchdogThread::_maxElapsed; | ||
| 336 | std::atomic<int> DeadlockWatchdogThread::_maxElapsedAverage; | ||
| 337 | ThreadSafeMovingAverage<int, DeadlockWatchdogThread::HEARTBEAT_SAMPLES> DeadlockWatchdogThread::_movingAverage; | ||
| 338 | |||
| 239 | #ifdef Q_OS_WIN | 339 | #ifdef Q_OS_WIN |
| 240 | class MyNativeEventFilter : public QAbstractNativeEventFilter { | 340 | class MyNativeEventFilter : public QAbstractNativeEventFilter { |
| 241 | public: | 341 | public: |
| @@ -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 manager | 485 | // FIXME move to header, or better yet, design some kind of UI manager |
| @@ -400,10 +500,13 @@ PluginContainer* _pluginContainer; · 14 unmodified lines | |||
| 400 | OffscreenGLCanvas* _chromiumShareContext { nullptr }; | 500 | OffscreenGLCanvas* _chromiumShareContext { nullptr }; |
| 401 | Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); | 501 | Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); |
| 402 | 502 | ||
| 503 | Setting::Handle<int> sessionRunTime{ "sessionRunTime", 0 }; | ||
| 504 | |||
| 403 | Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : | 505 | Application::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 machine | 532 | // 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 menu | 570 | _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 AssetClient | 648 | 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 MessagesClient | 652 | // 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 one | 668 | // 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 AccountManager | 689 | // 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 token | 703 | // 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 token | 711 | // 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_MAC | 776 | #ifdef Q_OS_MAC |
| 667 | // OSX doesn't seem to provide for hiding the cursor only on the GL widget | 777 | auto cursorTarget = _window; // OSX doesn't seem to provide for hiding the cursor only on the GL widget |
| 668 | _window->setCursor(Qt::BlankCursor); | ||
| 669 | #else | 778 | #else |
| 670 | // On windows and linux, hiding the top level cursor also means it's invisible | 779 | // 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 for | 780 | // window menu, which is a pain, so only hide it for the GL surface |
| 672 | // the GL surface | 781 | auto cursorTarget = _glWidget; |
| 673 | _glWidget->setCursor(Qt::BlankCursor); | ||
| 674 | #endif | 782 | #endif |
| 783 | cursorTarget->setCursor(Qt::BlankCursor); | ||
| 675 | 784 | ||
| 676 | // enable mouse tracking; otherwise, we only get drag events | 785 | // 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 jurisdictions | 803 | // 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 load | 811 | _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 actions | 828 | // 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 class | 905 | // 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 initialized | 965 | // 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 now | 974 | 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 | #endif | 1025 | #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 it | 1027 | // 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 messages | 1099 | // 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 | |||
| 1106 | void Application::checkChangeCursor() { | ||
| 1107 | QMutexLocker locker(&_changeCursorLock); | ||
| 1108 | if (_cursorNeedsChanging) { | ||
| 1109 | #ifdef Q_OS_MAC | ||
| 1110 | auto cursorTarget = _window; // OSX doesn't seem to provide for hiding the cursor only on the GL widget | ||
| 1111 | #else | ||
| 1112 | // On windows and linux, hiding the top level cursor also means it's invisible when hovering over the | ||
| 1113 | // window menu, which is a pain, so only hide it for the GL surface | ||
| 1114 | auto cursorTarget = _glWidget; | ||
| 1115 | #endif | ||
| 1116 | cursorTarget->setCursor(_desiredCursor); | ||
| 1117 | |||
| 1118 | _cursorNeedsChanging = false; | ||
| 1119 | } | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | void Application::showCursor(const QCursor& cursor) { | ||
| 1123 | QMutexLocker locker(&_changeCursorLock); | ||
| 1124 | _desiredCursor = cursor; | ||
| 1125 | _cursorNeedsChanging = true; | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | void Application::updateHeartbeat() { | ||
| 1129 | static_cast<DeadlockWatchdogThread*>(_deadlockWatchdogThread)->updateHeartbeat(); | ||
| 1130 | } | ||
| 1131 | |||
| 984 | void Application::aboutToQuit() { | 1132 | void 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 scripts | 1180 | 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 scripts | 1182 | 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 invokeMethod | 1185 | // first stop all timers directly or by invokeMethod |
| 1030 | // depending on what thread they run in | 1186 | // 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 | ||
| 1065 | void Application::emptyLocalCache() { | ||
| 1066 | if (auto cache = NetworkAccessManager::getInstance().cache()) { | ||
| 1067 | qDebug() << "DiskCacheEditor::clear(): Clearing disk cache."; | ||
| 1068 | cache->clear(); | ||
| 1069 | } | ||
| 1070 | } | ||
| 1071 | |||
| 1072 | Application::~Application() { | 1220 | Application::~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 engine | 1231 | // 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 thread | 1247 | 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 0 | 1260 | #if 0 |
| 1127 | ConnexionClient::getInstance().destroy(); | 1261 | ConnexionClient::getInstance().destroy(); |
| 1128 | #endif | 1262 | #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 messages | 1268 | 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 to | 1337 | // OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to |
| 1200 | // support the window management and scripting proxies for VR use | 1338 | // 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 or | 1341 | // FIXME either expose so that dialogs can set this themselves or |
| 1204 | // do better detection in the offscreen UI of what has focus | 1342 | // 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 | ||
| 1299 | void Application::paintGL() { | 1448 | void Application::paintGL() { |
| 1449 | |||
| 1450 | updateHeartbeat(); | ||
| 1451 | |||
| 1452 | // Some plugins process message events, potentially leading to | ||
| 1453 | // re-entering a paint event. don't allow further processing if this | ||
| 1454 | // happens | ||
| 1455 | if (_inPaint) { | ||
| 1456 | return; | ||
| 1457 | } | ||
| 1458 | _inPaint = true; | ||
| 1459 | Finally clearFlagLambda([this] { _inPaint = false; }); | ||
| 1460 | |||
| 1300 | // paintGL uses a queued connection, so we can get messages from the queue even after we've quit | 1461 | // paintGL uses a queued connection, so we can get messages from the queue even after we've quit |
| 1301 | // and the plugins have shutdown | 1462 | // 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 pose | 1499 | // 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 pass | 1651 | // 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 user | 1684 | // 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 plugin | 1716 | // 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 | ||
| 1626 | void Application::runTests() { | 1746 | void Application::runTests() { |
| @@ -1704,6 +1824,11 @@ bool Application::importSVOFromURL(const QString& urlString) { · 77 unmodified lines | |||
| 1704 | } | 1824 | } |
| 1705 | 1825 | ||
| 1706 | bool Application::event(QEvent* event) { | 1826 | bool 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 | |||
| 1808 | bool Application::eventFilter(QObject* object, QEvent* event) { | 1933 | bool 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 | |||
| 2098 | void Application::keyReleaseEvent(QKeyEvent* event) { | 2252 | void 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 | #endif | 2341 | #endif |
| 2188 | } | 2342 | } |
| 2189 | 2343 | ||
| 2190 | /// called by ApplicationCompositor when in HMD mode and we're faking our mouse movement | ||
| 2191 | void Application::fakeMouseEvent(QMouseEvent* event) { | ||
| 2192 | _fakedMouseEvent = true; | ||
| 2193 | sendEvent(_glWidget, event); | ||
| 2194 | _fakedMouseEvent = false; | ||
| 2195 | } | ||
| 2196 | |||
| 2197 | void Application::mouseMoveEvent(QMouseEvent* event) { | 2344 | void 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 reticle | 2355 | // 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 further | 2357 | 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 | |||
| 2301 | void Application::mouseReleaseEvent(QMouseEvent* event) { | 2447 | void 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 | |||
| 2451 | static uint32_t _renderedFrameIndex { INVALID_FRAME }; | 2597 | static uint32_t _renderedFrameIndex { INVALID_FRAME }; |
| 2452 | 2598 | ||
| 2453 | void Application::idle(uint64_t now) { | 2599 | void 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) in | 2619 | // 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 complete | 2638 | // 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 on | 2643 | // 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 queue | 2645 | } 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 timer | 2652 | // 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 | ||
| 2598 | ivec2 Application::getMouse() { | 2749 | ivec2 Application::getMouse() { |
| 2599 | auto reticlePosition = _compositor.getReticlePosition(); | 2750 | auto reticlePosition = getApplicationCompositor().getReticlePosition(); |
| 2600 | 2751 | ||
| 2601 | // in the HMD, the reticlePosition is the mouse position | 2752 | // 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 | #endif | 2833 | #endif |
| 2683 | 2834 | ||
| 2684 | bool Application::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs) { | 2835 | bool 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 | ||
| 2729 | bool Application::exportEntities(const QString& filename, float x, float y, float z, float scale) { | 2887 | bool 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 | ||
| 2759 | void Application::loadSettings() { | 2898 | void 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 | ||
| 2773 | void Application::saveSettings() { | 2913 | void 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 | |||
| 2782 | bool Application::importEntities(const QString& urlOrFilename) { | 2923 | bool 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 | |||
| 2869 | const bool ENABLE_AVATAR_UPDATE_THREADING = false; | ||
| 2870 | void Application::setAvatarUpdateThreading() { | ||
| 2871 | setAvatarUpdateThreading(ENABLE_AVATAR_UPDATE_THREADING); | ||
| 2872 | } | ||
| 2873 | void Application::setRawAvatarUpdateThreading() { | ||
| 2874 | setRawAvatarUpdateThreading(ENABLE_AVATAR_UPDATE_THREADING); | ||
| 2875 | } | ||
| 2876 | void Application::setRawAvatarUpdateThreading(bool isThreaded) { | ||
| 2877 | if (_avatarUpdate) { | ||
| 2878 | if (_avatarUpdate->isThreaded() == isThreaded) { | ||
| 2879 | return; | ||
| 2880 | } | ||
| 2881 | _avatarUpdate->terminate(); | ||
| 2882 | } | ||
| 2883 | _avatarUpdate = new AvatarUpdate(); | ||
| 2884 | _avatarUpdate->initialize(isThreaded); | ||
| 2885 | } | ||
| 2886 | void Application::setAvatarUpdateThreading(bool isThreaded) { | ||
| 2887 | if (_avatarUpdate && (_avatarUpdate->isThreaded() == isThreaded)) { | ||
| 2888 | return; | ||
| 2889 | } | ||
| 2890 | |||
| 2891 | if (_avatarUpdate) { | ||
| 2892 | _avatarUpdate->terminate(); // Must be before we shutdown anim graph. | ||
| 2893 | } | ||
| 2894 | _avatarUpdate = new AvatarUpdate(); | ||
| 2895 | _avatarUpdate->initialize(isThreaded); | ||
| 2896 | } | 2998 | } |
| 2897 | 2999 | ||
| 2898 | void Application::updateLOD() { | 3000 | void Application::updateLOD() { |
| @@ -2905,6 +3007,11 @@ void Application::updateLOD() { · 6 unmodified lines | |||
| 2905 | } | 3007 | } |
| 2906 | } | 3008 | } |
| 2907 | 3009 | ||
| 3010 | void 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 forward | 3079 | // 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 | ||
| 3122 | void Application::toggleOverlays() { | ||
| 3123 | auto newOverlaysVisible = !_overlayConductor.getEnabled(); | ||
| 3124 | Menu::getInstance()->setIsOptionChecked(MenuOption::Overlays, newOverlaysVisible); | ||
| 3125 | _overlayConductor.setEnabled(newOverlaysVisible); | ||
| 3126 | } | ||
| 3127 | |||
| 3128 | void Application::setOverlaysVisible(bool visible) { | ||
| 3129 | _overlayConductor.setEnabled(visible); | ||
| 3130 | } | ||
| 3131 | |||
| 3015 | void Application::cycleCamera() { | 3132 | void 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 | ||
| 3115 | void Application::update(float deltaTime) { | 3232 | void 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 driveKeys | 3325 | // 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 present | 3350 | 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 risk | 3418 | // 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 lock | 3422 | // 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 case | 3926 | |
| 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 / lighting | 3933 | // 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 blank | 3943 | // 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 renderer | 3957 | // 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 here | 3970 | // 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 decision | 3974 | // 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 scene | 4001 | // 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 skybox | 4016 | // 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 engine | 4036 | // 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 | |||
| 3990 | void Application::domainChanged(const QString& domainHostname) { | 4150 | void 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 | ||
| 3998 | void Application::handleDomainConnectionDeniedPacket(QSharedPointer<ReceivedMessage> message) { | ||
| 3999 | // Read deny reason from packet | ||
| 4000 | quint16 reasonSize; | ||
| 4001 | message->readPrimitive(&reasonSize); | ||
| 4002 | QString reason = QString::fromUtf8(message->readWithoutCopy(reasonSize)); | ||
| 4003 | |||
| 4004 | // output to the log so the user knows they got a denied connection request | ||
| 4005 | // and check and signal for an access token so that we can make sure they are logged in | ||
| 4006 | qCDebug(interfaceapp) << "The domain-server denied a connection request: " << reason; | ||
| 4007 | qCDebug(interfaceapp) << "You may need to re-log to generate a keypair so you can provide a username signature."; | ||
| 4008 | |||
| 4009 | if (!_domainConnectionRefusals.contains(reason)) { | ||
| 4010 | _domainConnectionRefusals.append(reason); | ||
| 4011 | emit domainConnectionRefused(reason); | ||
| 4012 | } | ||
| 4013 | |||
| 4014 | AccountManager::getInstance().checkAndSignalForAccessToken(); | ||
| 4015 | } | ||
| 4016 | |||
| 4017 | void Application::connectedToDomain(const QString& hostname) { | ||
| 4018 | AccountManager& accountManager = AccountManager::getInstance(); | ||
| 4019 | const QUuid& domainID = DependencyManager::get<NodeList>()->getDomainHandler().getUUID(); | ||
| 4020 | 4157 | ||
| 4021 | if (accountManager.isLoggedIn() && !domainID.isNull()) { | 4158 | void Application::resettingDomain() { |
| 4022 | _notifiedPacketVersionMismatchThisDomain = false; | 4159 | _notifiedPacketVersionMismatchThisDomain = false; |
| 4023 | } | ||
| 4024 | } | 4160 | } |
| 4025 | 4161 | ||
| 4026 | void Application::nodeAdded(SharedNodePointer node) { | 4162 | void 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 away | 4164 | // 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 option | 4167 | } |
| 4032 | Menu::getInstance()->getActionForOption(MenuOption::UploadAsset)->setEnabled(true); | 4168 | |
| 4169 | void Application::nodeActivated(SharedNodePointer node) { | ||
| 4170 | if (node->getType() == NodeType::AssetServer) { | ||
| 4171 | // asset server just connected - check if we have the asset browser showing | ||
| 4172 | |||
| 4173 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); | ||
| 4174 | auto assetDialog = offscreenUi->getRootItem()->findChild<QQuickItem*>("AssetServer"); | ||
| 4175 | |||
| 4176 | if (assetDialog) { | ||
| 4177 | auto nodeList = DependencyManager::get<NodeList>(); | ||
| 4178 | |||
| 4179 | if (nodeList->getThisNodeCanRez()) { | ||
| 4180 | // call reload on the shown asset browser dialog to get the mappings (if permissions allow) | ||
| 4181 | QMetaObject::invokeMethod(assetDialog, "reload"); | ||
| 4182 | } else { | ||
| 4183 | // we switched to an Asset Server that we can't modify, hide the Asset Browser | ||
| 4184 | assetDialog->setVisible(false); | ||
| 4185 | } | ||
| 4186 | } | ||
| 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 avatars | 4231 | // 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::AssetServer | 4233 | } 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 asset | 4235 | |
| 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 | } |
| 4088 | void Application::trackIncomingOctreePacket(ReceivedMessage& message, SharedNodePointer sendingNode, bool wasStatsPacket) { | 4245 | void 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 | ||
| 4276 | bool Application::canAcceptURL(const QString& urlString) const { | 4418 | bool 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 | ||
| 4314 | void Application::setSessionUUID(const QUuid& sessionUUID) { | 4459 | void 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 | ||
| 4373 | bool Application::askToUploadAsset(const QString& filename) { | ||
| 4374 | if (!DependencyManager::get<NodeList>()->getThisNodeCanRez()) { | ||
| 4375 | OffscreenUi::warning(_window, "Failed Upload", | ||
| 4376 | QString("You don't have upload rights on that domain.\n\n")); | ||
| 4377 | return false; | ||
| 4378 | } | ||
| 4379 | |||
| 4380 | QUrl url { filename }; | ||
| 4381 | if (auto upload = DependencyManager::get<AssetClient>()->createUpload(url.toLocalFile())) { | ||
| 4382 | |||
| 4383 | QMessageBox messageBox; | ||
| 4384 | messageBox.setWindowTitle("Asset upload"); | ||
| 4385 | messageBox.setText("You are about to upload the following file to the asset server:\n" + | ||
| 4386 | url.toDisplayString()); | ||
| 4387 | messageBox.setInformativeText("Do you want to continue?"); | ||
| 4388 | messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); | ||
| 4389 | messageBox.setDefaultButton(QMessageBox::Ok); | ||
| 4390 | |||
| 4391 | // Option to drop model in world for models | ||
| 4392 | if (filename.endsWith(FBX_EXTENSION, Qt::CaseInsensitive) || filename.endsWith(OBJ_EXTENSION, Qt::CaseInsensitive)) { | ||
| 4393 | auto checkBox = new QCheckBox(&messageBox); | ||
| 4394 | checkBox->setText("Add to scene"); | ||
| 4395 | messageBox.setCheckBox(checkBox); | ||
| 4396 | } | ||
| 4397 | |||
| 4398 | if (messageBox.exec() != QMessageBox::Ok) { | ||
| 4399 | upload->deleteLater(); | ||
| 4400 | return false; | ||
| 4401 | } | ||
| 4402 | |||
| 4403 | // connect to the finished signal so we know when the AssetUpload is done | ||
| 4404 | if (messageBox.checkBox() && (messageBox.checkBox()->checkState() == Qt::Checked)) { | ||
| 4405 | // Custom behavior for models | ||
| 4406 | QObject::connect(upload, &AssetUpload::finished, this, &Application::modelUploadFinished); | ||
| 4407 | } else { | ||
| 4408 | QObject::connect(upload, &AssetUpload::finished, | ||
| 4409 | &AssetUploadDialogFactory::getInstance(), | ||
| 4410 | &AssetUploadDialogFactory::handleUploadFinished); | ||
| 4411 | } | ||
| 4412 | |||
| 4413 | // start the upload now | ||
| 4414 | upload->start(); | ||
| 4415 | return true; | ||
| 4416 | } | ||
| 4417 | |||
| 4418 | // display a message box with the error | ||
| 4419 | OffscreenUi::warning(_window, "Failed Upload", QString("Failed to upload %1.\n\n").arg(filename)); | ||
| 4420 | return false; | ||
| 4421 | } | ||
| 4422 | |||
| 4423 | void Application::modelUploadFinished(AssetUpload* upload, const QString& hash) { | ||
| 4424 | auto filename = QFileInfo(upload->getFilename()).fileName(); | ||
| 4425 | |||
| 4426 | if ((upload->getError() == AssetUpload::NoError) && | ||
| 4427 | (FBX_EXTENSION.endsWith(upload->getExtension(), Qt::CaseInsensitive) || | ||
| 4428 | OBJ_EXTENSION.endsWith(upload->getExtension(), Qt::CaseInsensitive))) { | ||
| 4429 | |||
| 4430 | auto entities = DependencyManager::get<EntityScriptingInterface>(); | ||
| 4431 | |||
| 4432 | EntityItemProperties properties; | ||
| 4433 | properties.setType(EntityTypes::Model); | ||
| 4434 | properties.setModelURL(QString("%1:%2.%3").arg(URL_SCHEME_ATP).arg(hash).arg(upload->getExtension())); | ||
| 4435 | properties.setPosition(_myCamera.getPosition() + _myCamera.getOrientation() * Vectors::FRONT * 2.0f); | ||
| 4436 | properties.setName(QUrl(upload->getFilename()).fileName()); | ||
| 4437 | |||
| 4438 | entities->addEntity(properties); | ||
| 4439 | |||
| 4440 | upload->deleteLater(); | ||
| 4441 | } else { | ||
| 4442 | AssetUploadDialogFactory::getInstance().handleUploadFinished(upload, hash); | ||
| 4443 | } | ||
| 4444 | } | ||
| 4445 | |||
| 4446 | bool Application::askToWearAvatarAttachmentUrl(const QString& url) { | 4518 | bool 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 | ||
| 4617 | void Application::toggleAssetServerWidget(QString filePath) { | ||
| 4618 | if (!DependencyManager::get<NodeList>()->getThisNodeCanRez()) { | ||
| 4619 | return; | ||
| 4620 | } | ||
| 4621 | |||
| 4622 | static const QUrl url { "AssetServer.qml" }; | ||
| 4623 | |||
| 4624 | auto startUpload = [=](QQmlContext* context, QObject* newObject){ | ||
| 4625 | if (!filePath.isEmpty()) { | ||
| 4626 | emit uploadRequest(filePath); | ||
| 4627 | } | ||
| 4628 | }; | ||
| 4629 | DependencyManager::get<OffscreenUi>()->show(url, "AssetServer", startUpload); | ||
| 4630 | startUpload(nullptr, nullptr); | ||
| 4631 | } | ||
| 4632 | |||
| 4545 | void Application::packageModel() { | 4633 | void 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 | ||
| 4560 | void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject) { | ||
| 4561 | // from the domain-handler, figure out the satoshi cost per voxel and per meter cubed | ||
| 4562 | const QString VOXEL_SETTINGS_KEY = "voxels"; | ||
| 4563 | const QString PER_VOXEL_COST_KEY = "per-voxel-credits"; | ||
| 4564 | const QString PER_METER_CUBED_COST_KEY = "per-meter-cubed-credits"; | ||
| 4565 | const QString VOXEL_WALLET_UUID = "voxel-wallet"; | ||
| 4566 | |||
| 4567 | const QJsonObject& voxelObject = domainSettingsObject[VOXEL_SETTINGS_KEY].toObject(); | ||
| 4568 | |||
| 4569 | qint64 satoshisPerVoxel = 0; | ||
| 4570 | qint64 satoshisPerMeterCubed = 0; | ||
| 4571 | QUuid voxelWalletUUID; | ||
| 4572 | |||
| 4573 | if (!domainSettingsObject.isEmpty()) { | ||
| 4574 | float perVoxelCredits = (float) voxelObject[PER_VOXEL_COST_KEY].toDouble(); | ||
| 4575 | float perMeterCubedCredits = (float) voxelObject[PER_METER_CUBED_COST_KEY].toDouble(); | ||
| 4576 | |||
| 4577 | satoshisPerVoxel = (qint64) floorf(perVoxelCredits * SATOSHIS_PER_CREDIT); | ||
| 4578 | satoshisPerMeterCubed = (qint64) floorf(perMeterCubedCredits * SATOSHIS_PER_CREDIT); | ||
| 4579 | |||
| 4580 | voxelWalletUUID = QUuid(voxelObject[VOXEL_WALLET_UUID].toString()); | ||
| 4581 | } | ||
| 4582 | |||
| 4583 | qCDebug(interfaceapp) << "Octree edits costs are" << satoshisPerVoxel << "per octree cell and" << satoshisPerMeterCubed << "per meter cubed"; | ||
| 4584 | qCDebug(interfaceapp) << "Destination wallet UUID for edit payments is" << voxelWalletUUID; | ||
| 4585 | } | ||
| 4586 | |||
| 4587 | void Application::loadDialog() { | 4648 | void 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 | ||
| 4660 | int Application::getRenderAmbientLight() const { | ||
| 4661 | if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLightGlobal)) { | ||
| 4662 | return -1; | ||
| 4663 | } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight0)) { | ||
| 4664 | return 0; | ||
| 4665 | } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight1)) { | ||
| 4666 | return 1; | ||
| 4667 | } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight2)) { | ||
| 4668 | return 2; | ||
| 4669 | } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight3)) { | ||
| 4670 | return 3; | ||
| 4671 | } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight4)) { | ||
| 4672 | return 4; | ||
| 4673 | } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight5)) { | ||
| 4674 | return 5; | ||
| 4675 | } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight6)) { | ||
| 4676 | return 6; | ||
| 4677 | } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight7)) { | ||
| 4678 | return 7; | ||
| 4679 | } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight8)) { | ||
| 4680 | return 8; | ||
| 4681 | } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderAmbientLight9)) { | ||
| 4682 | return 9; | ||
| 4683 | } else { | ||
| 4684 | return -1; | ||
| 4685 | } | ||
| 4686 | } | ||
| 4687 | |||
| 4688 | void Application::notifyPacketVersionMismatch() { | 4721 | void 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 | ||
| 4699 | void Application::checkSkeleton() { | 4732 | void 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 | ||
| 4788 | DisplayPlugin* Application::getActiveDisplayPlugin() { | 4821 | DisplayPlugin* 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 | ||
| 4796 | const DisplayPlugin* Application::getActiveDisplayPlugin() const { | 4836 | const 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 | ||
| 4837 | void Application::updateDisplayMode() { | 4877 | void Application::updateDisplayMode() { |
| 4878 | // Unsafe to call this method from anything but the main thread | ||
| 4879 | if (QThread::currentThread() != thread()) { | ||
| 4880 | qFatal("Attempted to switch display plugins from a non-main thread"); | ||
| 4881 | } | ||
| 4882 | |||
| 4883 | // Some plugins *cough* Oculus *cough* process message events from inside their | ||
| 4884 | // display function, and we don't want to change the display plugin underneath | ||
| 4885 | // the paintGL call, so we need to guard against that | ||
| 4886 | // The current oculus runtime doesn't do this anymore | ||
| 4887 | if (_inPaint) { | ||
| 4888 | qFatal("Attempted to switch display plugins while in painting"); | ||
| 4889 | } | ||
| 4890 | |||
| 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 their | 4951 | 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 displayed | 4967 | if (!active) { |
| 4936 | // Direct Mode HMDs (like windows Oculus) will be isHmd() but will have a screen of -1 | 4968 | // 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 has | 4972 | if (newDisplayPlugin) { |
| 4943 | // been activated so that it's UI is setup correctly | 4973 | 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 hmdtools | 4985 | 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 | ||
| 5050 | mat4 Application::getHMDSensorPose() const { | 5094 | mat4 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 | ||
| 5057 | void Application::setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue) { | ||
| 5058 | |||
| 5059 | // NOTE: the Hand::modifyPalm() will allow the lambda to modify the palm data while ensuring some other user isn't | ||
| 5060 | // reading or writing to the Palms. This is definitely not the best way of handling this, and I'd like to see more | ||
| 5061 | // of this palm manipulation in the Hand class itself. But unfortunately the Hand and Palm don't knbow about | ||
| 5062 | // controller::Pose. More work is needed to clean this up. | ||
| 5063 | hand->modifyPalm(whichHand, [&](PalmData& palm) { | ||
| 5064 | palm.setActive(pose.isValid()); | ||
| 5065 | |||
| 5066 | // controller pose is in Avatar frame. | ||
| 5067 | glm::vec3 position = pose.getTranslation(); | ||
| 5068 | glm::quat rotation = pose.getRotation(); | ||
| 5069 | |||
| 5070 | // Compute current velocity from position change | ||
| 5071 | glm::vec3 rawVelocity; | ||
| 5072 | if (deltaTime > 0.0f) { | ||
| 5073 | rawVelocity = (position - palm.getRawPosition()) / deltaTime; | ||
| 5074 | } else { | ||
| 5075 | rawVelocity = glm::vec3(0.0f); | ||
| 5076 | } | ||
| 5077 | palm.setRawVelocity(rawVelocity); // meters/sec | ||
| 5078 | |||
| 5079 | // Angular Velocity of Palm | ||
| 5080 | glm::quat deltaRotation = rotation * glm::inverse(palm.getRawRotation()); | ||
| 5081 | glm::vec3 angularVelocity(0.0f); | ||
| 5082 | float rotationAngle = glm::angle(deltaRotation); | ||
| 5083 | if ((rotationAngle > EPSILON) && (deltaTime > 0.0f)) { | ||
| 5084 | angularVelocity = glm::normalize(glm::axis(deltaRotation)); | ||
| 5085 | angularVelocity *= (rotationAngle / deltaTime); | ||
| 5086 | palm.setRawAngularVelocity(angularVelocity); | ||
| 5087 | } else { | ||
| 5088 | palm.setRawAngularVelocity(glm::vec3(0.0f)); | ||
| 5089 | } | ||
| 5090 | |||
| 5091 | if (controller::InputDevice::getLowVelocityFilter()) { | ||
| 5092 | // Use a velocity sensitive filter to damp small motions and preserve large ones with | ||
| 5093 | // no latency. | ||
| 5094 | float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f); | ||
| 5095 | position = palm.getRawPosition() * velocityFilter + position * (1.0f - velocityFilter); | ||
| 5096 | rotation = safeMix(palm.getRawRotation(), rotation, 1.0f - velocityFilter); | ||
| 5097 | } | ||
| 5098 | palm.setRawPosition(position); | ||
| 5099 | palm.setRawRotation(rotation); | ||
| 5100 | |||
| 5101 | // Store the one fingertip in the palm structure so we can track velocity | ||
| 5102 | const float FINGER_LENGTH = 0.3f; // meters | ||
| 5103 | const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f); | ||
| 5104 | const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR; | ||
| 5105 | glm::vec3 oldTipPosition = palm.getTipRawPosition(); | ||
| 5106 | if (deltaTime > 0.0f) { | ||
| 5107 | palm.setTipVelocity((newTipPosition - oldTipPosition) / deltaTime); | ||
| 5108 | } else { | ||
| 5109 | palm.setTipVelocity(glm::vec3(0.0f)); | ||
| 5110 | } | ||
| 5111 | palm.setTipPosition(newTipPosition); | ||
| 5112 | palm.setTrigger(triggerValue); // FIXME - we want to get rid of this idea of PalmData having a trigger | ||
| 5113 | }); | ||
| 5114 | } | ||
| 5115 | |||
| 5116 | void Application::crashApplication() { | 5101 | void 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 | ||
| 5108 | void Application::deadlockApplication() { | ||
| 5109 | qCDebug(interfaceapp) << "Intentionally deadlocked Interface"; | ||
| 5110 | // Using a loop that will *technically* eventually exit (in ~600 billion years) | ||
| 5111 | // to avoid compiler warnings about a loop that will never exit | ||
| 5112 | for (uint64_t i = 1; i != 0; ++i) { | ||
| 5113 | QThread::sleep(1); | ||
| 5114 | } | ||
| 5115 | } | ||
| 5116 | |||
| 5123 | void Application::setActiveDisplayPlugin(const QString& pluginName) { | 5117 | void 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 | |||
| 5156 | void Application::showDesktop() { | ||
| 5157 | if (!_overlayConductor.getEnabled()) { | ||
| 5158 | _overlayConductor.setEnabled(true); | ||
| 5159 | } | ||
| 5160 | } | ||
| 5161 | |||
| 5162 | CompositorHelper& Application::getApplicationCompositor() const { | ||
| 5163 | return *DependencyManager::get<CompositorHelper>(); | ||
| 5164 | } | ||