History

merge from upstream

Seth Alves committed 9 years ago

Commit dc996be2 parents c4d1bee 61c60fc179 files · +5988 −1551Browse files

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

Unified · Split

interface/src/Application.cpp+707−85
@@ -42,16 +42,20 @@ · 41 unmodified lines
4242 #include <QtMultimedia/QMediaPlayer>
4343
4444 #include <QProcessEnvironment>
45+#include <QTemporaryDir>
4546
4647 #include <gl/QOpenGLContextWrapper.h>
4748
4849 #include <shared/GlobalAppProperties.h>
50+#include <StatTracker.h>
51+#include <Trace.h>
4952 #include <ResourceScriptingInterface.h>
5053 #include <AccountManager.h>
5154 #include <AddressManager.h>
5255 #include <AnimDebugDraw.h>
5356 #include <BuildInfo.h>
5457 #include <AssetClient.h>
58+#include <AssetUpload.h>
5559 #include <AutoUpdater.h>
5660 #include <AudioInjectorManager.h>
5761 #include <CursorManager.h>
@@ -77,6 +81,7 @@ · 19 unmodified lines
7781 #include <UserActivityLoggerScriptingInterface.h>
7882 #include <LogHandler.h>
7983 #include <MainWindow.h>
84+#include <MappingRequest.h>
8085 #include <MessagesClient.h>
8186 #include <ModelEntityItem.h>
8287 #include <NetworkAccessManager.h>
@@ -156,6 +161,7 @@ · 73 unmodified lines
156161 #include "ui/DialogsManager.h"
157162 #include "ui/LoginDialog.h"
158163 #include "ui/overlays/Cube3DOverlay.h"
164+#include "ui/overlays/Web3DOverlay.h"
159165 #include "ui/Snapshot.h"
160166 #include "ui/SnapshotAnimated.h"
161167 #include "ui/StandAloneJSConsole.h"
@@ -178,6 +184,8 @@ extern "C" { · 16 unmodified lines
178184 }
179185 #endif
180186
187+Q_LOGGING_CATEGORY(trace_app_input_mouse, "trace.app.input.mouse")
188+
181189 using namespace std;
182190
183191 static QTimer locationUpdateTimer;
@@ -424,6 +432,8 @@ bool setupEssentials(int& argc, char** argv) { · 240 unmodified lines
424432 steamClient->init();
425433 }
426434
435+ DependencyManager::set<tracing::Tracer>();
436+
427437 #if defined(Q_OS_WIN)
428438 // Select appropriate audio DLL
429439 QString audioDLLPath = QCoreApplication::applicationDirPath();
@@ -446,6 +456,7 @@ bool setupEssentials(int& argc, char** argv) { · 16 unmodified lines
446456
447457 // Set dependencies
448458 DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp));
459+ DependencyManager::set<StatTracker>();
449460 DependencyManager::set<ScriptEngines>();
450461 DependencyManager::set<Preferences>();
451462 DependencyManager::set<recording::Deck>();
@@ -559,6 +570,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 107 unmodified lines
559570
560571 {
561572 const QString TEST_SCRIPT = "--testScript";
573+ const QString TRACE_FILE = "--traceFile";
562574 const QStringList args = arguments();
563575 for (int i = 0; i < args.size() - 1; ++i) {
564576 if (args.at(i) == TEST_SCRIPT) {
@@ -566,6 +578,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 1 unmodified line
566578 if (QFileInfo(testScriptPath).exists()) {
567579 setProperty(hifi::properties::TEST, QUrl::fromLocalFile(testScriptPath));
568580 }
581+ } else if (args.at(i) == TRACE_FILE) {
582+ QString traceFilePath = args.at(i + 1);
583+ setProperty(hifi::properties::TRACING, traceFilePath);
584+ DependencyManager::get<tracing::Tracer>()->startTracing();
569585 }
570586 }
571587 }
@@ -1180,11 +1196,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 608 unmodified lines
11801196 auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
11811197 connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity,
11821198 [this](const EntityItemID& entityItemID, const PointerEvent& event) {
1199+ setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID);
11831200 setKeyboardFocusEntity(entityItemID);
11841201 });
11851202
11861203 connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, [=](const EntityItemID& entityItemID) {
1187− if (entityItemID == _keyboardFocusedItem.get()) {
1204+ if (entityItemID == _keyboardFocusedEntity.get()) {
11881205 setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
11891206 }
11901207 });
@@ -1194,7 +1211,26 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 3 unmodified lines
11941211 setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
11951212 });
11961213
1214+ // Keyboard focus handling for Web overlays.
1215+ auto overlays = &(qApp->getOverlays());
1216+
1217+ connect(overlays, &Overlays::mousePressOnOverlay, [=](unsigned int overlayID, const PointerEvent& event) {
1218+ setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
1219+ setKeyboardFocusOverlay(overlayID);
1220+ });
1221+
1222+ connect(overlays, &Overlays::overlayDeleted, [=](unsigned int overlayID) {
1223+ if (overlayID == _keyboardFocusedOverlay.get()) {
1224+ setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID);
1225+ }
1226+ });
1227+
1228+ connect(overlays, &Overlays::mousePressOffOverlay, [=]() {
1229+ setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID);
1230+ });
1231+
11971232 connect(this, &Application::aboutToQuit, [=]() {
1233+ setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID);
11981234 setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
11991235 });
12001236
@@ -1381,16 +1417,31 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 180 unmodified lines
13811417 }
13821418 qCDebug(interfaceapp) << "Server content version: " << contentVersion;
13831419
1384− bool hasTutorialContent = contentVersion >= 1;
1420+ static const int MIN_VIVE_CONTENT_VERSION = 1;
1421+ static const int MIN_OCULUS_TOUCH_CONTENT_VERSION = 27;
1422+
1423+ bool hasSufficientTutorialContent = false;
1424+ bool hasHandControllers = false;
1425+
1426+ // Only specific hand controllers are currently supported, so only send users to the tutorial
1427+ // if they have one of those hand controllers.
1428+ if (PluginUtils::isViveControllerAvailable()) {
1429+ hasHandControllers = true;
1430+ hasSufficientTutorialContent = contentVersion >= MIN_VIVE_CONTENT_VERSION;
1431+ } else if (PluginUtils::isOculusTouchControllerAvailable()) {
1432+ hasHandControllers = true;
1433+ hasSufficientTutorialContent = contentVersion >= MIN_OCULUS_TOUCH_CONTENT_VERSION;
1434+ }
13851435
13861436 Setting::Handle<bool> firstRun { Settings::firstRun, true };
1387− bool hasHMDAndHandControllers = PluginUtils::isHMDAvailable("OpenVR (Vive)") && PluginUtils::isHandControllerAvailable();
1437+
1438+ bool hasHMDAndHandControllers = PluginUtils::isHMDAvailable() && hasHandControllers;
13881439 Setting::Handle<bool> tutorialComplete { "tutorialComplete", false };
13891440
1390− bool shouldGoToTutorial = hasHMDAndHandControllers && hasTutorialContent && !tutorialComplete.get();
1441+ bool shouldGoToTutorial = hasHMDAndHandControllers && hasSufficientTutorialContent && !tutorialComplete.get();
13911442
13921443 qCDebug(interfaceapp) << "Has HMD + Hand Controllers: " << hasHMDAndHandControllers << ", current plugin: " << _displayPlugin->getName();
1393− qCDebug(interfaceapp) << "Has tutorial content: " << hasTutorialContent;
1444+ qCDebug(interfaceapp) << "Has sufficient tutorial content (" << contentVersion << ") : " << hasSufficientTutorialContent;
13941445 qCDebug(interfaceapp) << "Tutorial complete: " << tutorialComplete.get();
13951446 qCDebug(interfaceapp) << "Should go to tutorial: " << shouldGoToTutorial;
13961447
@@ -1451,6 +1502,24 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo · 54 unmodified lines
14511502 // After all of the constructor is completed, then set firstRun to false.
14521503 firstRun.set(false);
14531504 }
1505+
1506+ // Monitor model assets (e.g., from Clara.io) added to the world that may need resizing.
1507+ static const int ADD_ASSET_TO_WORLD_TIMER_INTERVAL_MS = 1000;
1508+ _addAssetToWorldResizeTimer.setInterval(ADD_ASSET_TO_WORLD_TIMER_INTERVAL_MS);
1509+ connect(&_addAssetToWorldResizeTimer, &QTimer::timeout, this, &Application::addAssetToWorldCheckModelSize);
1510+
1511+ // Auto-update and close adding asset to world info message box.
1512+ static const int ADD_ASSET_TO_WORLD_INFO_TIMEOUT_MS = 5000;
1513+ _addAssetToWorldInfoTimer.setInterval(ADD_ASSET_TO_WORLD_INFO_TIMEOUT_MS);
1514+ _addAssetToWorldInfoTimer.setSingleShot(true);
1515+ connect(&_addAssetToWorldInfoTimer, &QTimer::timeout, this, &Application::addAssetToWorldInfoTimeout);
1516+ static const int ADD_ASSET_TO_WORLD_ERROR_TIMEOUT_MS = 8000;
1517+ _addAssetToWorldErrorTimer.setInterval(ADD_ASSET_TO_WORLD_ERROR_TIMEOUT_MS);
1518+ _addAssetToWorldErrorTimer.setSingleShot(true);
1519+ connect(&_addAssetToWorldErrorTimer, &QTimer::timeout, this, &Application::addAssetToWorldErrorTimeout);
1520+
1521+ connect(this, &QCoreApplication::aboutToQuit, this, &Application::addAssetToWorldMessageClose);
1522+ connect(&domainHandler, &DomainHandler::hostnameChanged, this, &Application::addAssetToWorldMessageClose);
14541523 }
14551524
14561525 void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {
@@ -1579,6 +1648,13 @@ void Application::cleanupBeforeQuit() { · 122 unmodified lines
15791648 QString webengineRemoteDebugging = QProcessEnvironment::systemEnvironment().value("QTWEBENGINE_REMOTE_DEBUGGING", "false");
15801649 qCDebug(interfaceapp) << "QTWEBENGINE_REMOTE_DEBUGGING =" << webengineRemoteDebugging;
15811650
1651+ if (tracing::enabled()) {
1652+ auto tracer = DependencyManager::get<tracing::Tracer>();
1653+ tracer->stopTracing();
1654+ auto outputFile = property(hifi::properties::TRACING).toString();
1655+ tracer->serialize(outputFile);
1656+ }
1657+
15821658 // Stop third party processes so that they're not left running in the event of a subsequent shutdown crash.
15831659 #ifdef HAVE_DDE
15841660 DependencyManager::get<DdeFaceTracker>()->setEnabled(false);
@@ -1753,7 +1829,7 @@ void Application::initializeGL() { · 168 unmodified lines
17531829 // Set up the render engine
17541830 render::CullFunctor cullFunctor = LODManager::shouldRender;
17551831 _renderEngine->addJob<RenderShadowTask>("RenderShadowTask", cullFunctor);
1756− static const QString RENDER_FORWARD = "RENDER_FORWARD";
1832+ static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD";
17571833 if (QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD)) {
17581834 _renderEngine->addJob<RenderForwardTask>("RenderForwardTask", cullFunctor);
17591835 } else {
@@ -1838,9 +1914,9 @@ void Application::initializeUi() { · 78 unmodified lines
18381914 rootContext->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
18391915 rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
18401916 rootContext->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data());
1841− FileScriptingInterface* fileDownload = new FileScriptingInterface(engine);
1842− rootContext->setContextProperty("File", fileDownload);
1843− connect(fileDownload, &FileScriptingInterface::unzipSuccess, this, &Application::showAssetServerWidget);
1917+ _fileDownload = new FileScriptingInterface(engine);
1918+ rootContext->setContextProperty("File", _fileDownload);
1919+ connect(_fileDownload, &FileScriptingInterface::unzipResult, this, &Application::handleUnzip);
18441920 rootContext->setContextProperty("MyAvatar", getMyAvatar().get());
18451921 rootContext->setContextProperty("Messages", DependencyManager::get<MessagesClient>().data());
18461922 rootContext->setContextProperty("Recording", DependencyManager::get<RecordingScriptingInterface>().data());
@@ -1856,6 +1932,7 @@ void Application::initializeUi() { · 9 unmodified lines
18561932 rootContext->setContextProperty("Assets", new AssetMappingsScriptingInterface());
18571933
18581934 rootContext->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
1935+ rootContext->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data());
18591936
18601937 rootContext->setContextProperty("Camera", &_myCamera);
18611938
@@ -1945,7 +2022,7 @@ void Application::paintGL() { · 83 unmodified lines
19452022 _frameCount++;
19462023
19472024 auto lastPaintBegin = usecTimestampNow();
1948− PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, (uint64_t)_frameCount);
2025+ PROFILE_RANGE_EX(render, __FUNCTION__, 0xff0000ff, (uint64_t)_frameCount);
19492026 PerformanceTimer perfTimer("paintGL");
19502027
19512028 if (nullptr == _displayPlugin) {
@@ -2122,7 +2199,7 @@ void Application::paintGL() { · 170 unmodified lines
21222199 auto finalFramebuffer = framebufferCache->getFramebuffer();
21232200
21242201 {
2125− PROFILE_RANGE(__FUNCTION__ "/mainRender");
2202+ PROFILE_RANGE(render, "/mainRender");
21262203 PerformanceTimer perfTimer("mainRender");
21272204 renderArgs._boomOffset = boomOffset;
21282205 // Viewport is assigned to the size of the framebuffer
@@ -2177,7 +2254,7 @@ void Application::paintGL() { · 48 unmodified lines
21772254 frame->overlay = _applicationOverlay.getOverlayTexture();
21782255 // deliver final scene rendering commands to the display plugin
21792256 {
2180− PROFILE_RANGE(__FUNCTION__ "/pluginOutput");
2257+ PROFILE_RANGE(render, "/pluginOutput");
21812258 PerformanceTimer perfTimer("pluginOutput");
21822259 _frameCounter.increment();
21832260 displayPlugin->submitFrame(frame);
@@ -2265,7 +2342,7 @@ void Application::resizeEvent(QResizeEvent* event) { · 81 unmodified lines
22652342 }
22662343
22672344 void Application::resizeGL() {
2268− PROFILE_RANGE(__FUNCTION__);
2345+ PROFILE_RANGE(render, __FUNCTION__);
22692346 if (nullptr == _displayPlugin) {
22702347 return;
22712348 }
@@ -2320,7 +2397,6 @@ bool Application::importSVOFromURL(const QString& urlString) { · 48 unmodified lines
23202397 }
23212398
23222399 bool Application::event(QEvent* event) {
2323−
23242400 if (!Menu::getInstance()) {
23252401 return false;
23262402 }
@@ -2368,26 +2444,49 @@ bool Application::event(QEvent* event) { · 41 unmodified lines
23682444 }
23692445
23702446 {
2371− if (!_keyboardFocusedItem.get().isInvalidID()) {
2447+ if (!_keyboardFocusedEntity.get().isInvalidID()) {
23722448 switch (event->type()) {
2373− case QEvent::KeyPress:
2374− case QEvent::KeyRelease: {
2375− auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
2376− auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedItem.get());
2377− if (entity && entity->getEventHandler()) {
2378− event->setAccepted(false);
2379− QCoreApplication::sendEvent(entity->getEventHandler(), event);
2380− if (event->isAccepted()) {
2381− _lastAcceptedKeyPress = usecTimestampNow();
2382− return true;
2449+ case QEvent::KeyPress:
2450+ case QEvent::KeyRelease: {
2451+ //auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
2452+ auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedEntity.get());
2453+ if (entity && entity->getEventHandler()) {
2454+ event->setAccepted(false);
2455+ QCoreApplication::sendEvent(entity->getEventHandler(), event);
2456+ if (event->isAccepted()) {
2457+ _lastAcceptedKeyPress = usecTimestampNow();
2458+ return true;
2459+ }
23832460 }
2461+ break;
23842462 }
2385− break;
2386− }
2463+ default:
2464+ break;
2465+ }
2466+ }
2467+ }
23872468
2388− default:
2389− break;
2390− }
2469+ {
2470+ if (_keyboardFocusedOverlay.get() != UNKNOWN_OVERLAY_ID) {
2471+ switch (event->type()) {
2472+ case QEvent::KeyPress:
2473+ case QEvent::KeyRelease: {
2474+ // Only Web overlays can have focus.
2475+ auto overlay =
2476+ std::dynamic_pointer_cast<Web3DOverlay>(getOverlays().getOverlay(_keyboardFocusedOverlay.get()));
2477+ if (overlay && overlay->getEventHandler()) {
2478+ event->setAccepted(false);
2479+ QCoreApplication::sendEvent(overlay->getEventHandler(), event);
2480+ if (event->isAccepted()) {
2481+ _lastAcceptedKeyPress = usecTimestampNow();
2482+ return true;
2483+ }
2484+ }
2485+ break;
2486+ }
2487+ default:
2488+ break;
2489+ }
23912490 }
23922491 }
23932492
@@ -2911,7 +3010,7 @@ void Application::maybeToggleMenuVisible(QMouseEvent* event) const { · 517 unmodified lines
29113010 }
29123011
29133012 void Application::mouseMoveEvent(QMouseEvent* event) {
2914− PROFILE_RANGE(__FUNCTION__);
3013+ PROFILE_RANGE(app_input_mouse, __FUNCTION__);
29153014
29163015 if (_aboutToQuit) {
29173016 return;
@@ -2947,6 +3046,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) { · 29 unmodified lines
29473046
29483047 if (compositor.getReticleVisible() || !isHMDMode() || !compositor.getReticleOverDesktop() ||
29493048 getOverlays().getOverlayAtPoint(glm::vec2(transformedPos.x(), transformedPos.y()))) {
3049+ getOverlays().mouseMoveEvent(&mappedEvent);
29503050 getEntities()->mouseMoveEvent(&mappedEvent);
29513051 }
29523052 _controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent); // send events to any registered scripts
@@ -2959,7 +3059,6 @@ void Application::mouseMoveEvent(QMouseEvent* event) { · 6 unmodified lines
29593059 if (_keyboardMouseDevice->isActive()) {
29603060 _keyboardMouseDevice->mouseMoveEvent(event);
29613061 }
2962−
29633062 }
29643063
29653064 void Application::mousePressEvent(QMouseEvent* event) {
@@ -2981,6 +3080,7 @@ void Application::mousePressEvent(QMouseEvent* event) { · 15 unmodified lines
29813080 event->buttons(), event->modifiers());
29823081
29833082 if (!_aboutToQuit) {
3083+ getOverlays().mousePressEvent(&mappedEvent);
29843084 getEntities()->mousePressEvent(&mappedEvent);
29853085 }
29863086
@@ -2991,7 +3091,6 @@ void Application::mousePressEvent(QMouseEvent* event) { · 4 unmodified lines
29913091 return;
29923092 }
29933093
2994−
29953094 if (hasFocus()) {
29963095 if (_keyboardMouseDevice->isActive()) {
29973096 _keyboardMouseDevice->mousePressEvent(event);
@@ -3002,7 +3101,6 @@ void Application::mousePressEvent(QMouseEvent* event) { · 4 unmodified lines
30023101 HFActionEvent actionEvent(HFActionEvent::startType(),
30033102 computePickRay(mappedEvent.x(), mappedEvent.y()));
30043103 sendEvent(this, &actionEvent);
3005−
30063104 }
30073105 }
30083106 }
@@ -3027,6 +3125,7 @@ void Application::mouseReleaseEvent(QMouseEvent* event) { · 18 unmodified lines
30273125 event->buttons(), event->modifiers());
30283126
30293127 if (!_aboutToQuit) {
3128+ getOverlays().mouseReleaseEvent(&mappedEvent);
30303129 getEntities()->mouseReleaseEvent(&mappedEvent);
30313130 }
30323131
@@ -3205,6 +3304,7 @@ bool Application::shouldPaint(float nsecsElapsed) { · 172 unmodified lines
32053304 }
32063305
32073306 void Application::idle(float nsecsElapsed) {
3307+ PerformanceTimer perfTimer("idle");
32083308
32093309 // Update the deadlock watchdog
32103310 updateHeartbeat();
@@ -3219,7 +3319,17 @@ void Application::idle(float nsecsElapsed) { · 8 unmodified lines
32193319 connect(offscreenUi.data(), &OffscreenUi::showDesktop, this, &Application::showDesktop);
32203320 }
32213321
3222− PROFILE_RANGE(__FUNCTION__);
3322+ PROFILE_COUNTER(app, "fps", { { "fps", _frameCounter.rate() } });
3323+ PROFILE_COUNTER(app, "downloads", {
3324+ { "current", ResourceCache::getLoadingRequests().length() },
3325+ { "pending", ResourceCache::getPendingRequestCount() }
3326+ });
3327+ PROFILE_COUNTER(app, "processing", {
3328+ { "current", DependencyManager::get<StatTracker>()->getStat("Processing") },
3329+ { "pending", DependencyManager::get<StatTracker>()->getStat("PendingProcessing") }
3330+ });
3331+
3332+ PROFILE_RANGE(app, __FUNCTION__);
32233333
32243334 if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
32253335 steamClient->runCallbacks();
@@ -3241,8 +3351,6 @@ void Application::idle(float nsecsElapsed) { · 15 unmodified lines
32413351
32423352 _simCounter.increment();
32433353
3244− PerformanceTimer perfTimer("idle");
3245−
32463354 // Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing
32473355 // details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing
32483356 // details normally.
@@ -3257,20 +3365,30 @@ void Application::idle(float nsecsElapsed) { · 8 unmodified lines
32573365 }
32583366
32593367
3260− // Drop focus from _keyboardFocusedItem if no keyboard messages for 30 seconds
3368+ // Update focus highlight for entity or overlay.
32613369 {
3262− if (!_keyboardFocusedItem.get().isInvalidID()) {
3370+ if (!_keyboardFocusedEntity.get().isInvalidID() || _keyboardFocusedOverlay.get() != UNKNOWN_OVERLAY_ID) {
32633371 const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus
32643372 quint64 elapsedSinceAcceptedKeyPress = usecTimestampNow() - _lastAcceptedKeyPress;
32653373 if (elapsedSinceAcceptedKeyPress > LOSE_FOCUS_AFTER_ELAPSED_TIME) {
32663374 setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
3375+ setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID);
32673376 } else {
32683377 // update position of highlight overlay
3269− auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
3270− auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedItem.get());
3271− if (entity && _keyboardFocusHighlight) {
3272− _keyboardFocusHighlight->setRotation(entity->getRotation());
3273− _keyboardFocusHighlight->setPosition(entity->getPosition());
3378+ if (!_keyboardFocusedEntity.get().isInvalidID()) {
3379+ auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedEntity.get());
3380+ if (entity && _keyboardFocusHighlight) {
3381+ _keyboardFocusHighlight->setRotation(entity->getRotation());
3382+ _keyboardFocusHighlight->setPosition(entity->getPosition());
3383+ }
3384+ } else {
3385+ // Only Web overlays can have focus.
3386+ auto overlay =
3387+ std::dynamic_pointer_cast<Web3DOverlay>(getOverlays().getOverlay(_keyboardFocusedOverlay.get()));
3388+ if (overlay && _keyboardFocusHighlight) {
3389+ _keyboardFocusHighlight->setRotation(overlay->getRotation());
3390+ _keyboardFocusHighlight->setPosition(overlay->getPosition());
3391+ }
32743392 }
32753393 }
32763394 }
@@ -3858,8 +3976,31 @@ void Application::rotationModeChanged() const { · 581 unmodified lines
38583976 }
38593977 }
38603978
3979+void Application::setKeyboardFocusHighlight(const glm::vec3& position, const glm::quat& rotation, const glm::vec3& dimensions) {
3980+ // Create focus
3981+ if (_keyboardFocusHighlightID < 0 || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) {
3982+ _keyboardFocusHighlight = std::make_shared<Cube3DOverlay>();
3983+ _keyboardFocusHighlight->setAlpha(1.0f);
3984+ _keyboardFocusHighlight->setBorderSize(1.0f);
3985+ _keyboardFocusHighlight->setColor({ 0xFF, 0xEF, 0x00 });
3986+ _keyboardFocusHighlight->setIsSolid(false);
3987+ _keyboardFocusHighlight->setPulseMin(0.5);
3988+ _keyboardFocusHighlight->setPulseMax(1.0);
3989+ _keyboardFocusHighlight->setColorPulse(1.0);
3990+ _keyboardFocusHighlight->setIgnoreRayIntersection(true);
3991+ _keyboardFocusHighlight->setDrawInFront(false);
3992+ _keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight);
3993+ }
3994+
3995+ // Position focus
3996+ _keyboardFocusHighlight->setRotation(rotation);
3997+ _keyboardFocusHighlight->setPosition(position);
3998+ _keyboardFocusHighlight->setDimensions(dimensions);
3999+ _keyboardFocusHighlight->setVisible(true);
4000+}
4001+
38614002 QUuid Application::getKeyboardFocusEntity() const {
3862− return _keyboardFocusedItem.get();
4003+ return _keyboardFocusedEntity.get();
38634004 }
38644005
38654006 void Application::setKeyboardFocusEntity(QUuid id) {
@@ -3867,21 +4008,21 @@ void Application::setKeyboardFocusEntity(QUuid id) { · 1 unmodified line
38674008 setKeyboardFocusEntity(entityItemID);
38684009 }
38694010
4011+static const float FOCUS_HIGHLIGHT_EXPANSION_FACTOR = 1.05f;
4012+
38704013 void Application::setKeyboardFocusEntity(EntityItemID entityItemID) {
3871− auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
3872− if (_keyboardFocusedItem.get() != entityItemID) {
3873− // reset focused entity
3874− _keyboardFocusedItem.set(UNKNOWN_ENTITY_ID);
3875− if (_keyboardFocusHighlight) {
4014+ if (_keyboardFocusedEntity.get() != entityItemID) {
4015+ _keyboardFocusedEntity.set(entityItemID);
4016+
4017+ if (_keyboardFocusHighlight && _keyboardFocusedOverlay.get() == UNKNOWN_OVERLAY_ID) {
38764018 _keyboardFocusHighlight->setVisible(false);
38774019 }
38784020
3879− // if invalid, return without expensive (locking) operations
38804021 if (entityItemID == UNKNOWN_ENTITY_ID) {
38814022 return;
38824023 }
38834024
3884− // if valid, query properties
4025+ auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
38854026 auto properties = entityScriptingInterface->getEntityProperties(entityItemID);
38864027 if (!properties.getLocked() && properties.getVisible()) {
38874028 auto entity = getEntities()->getTree()->findEntityByID(entityItemID);
@@ -3890,30 +4031,45 @@ void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { · 2 unmodified lines
38904031 if (_keyboardMouseDevice->isActive()) {
38914032 _keyboardMouseDevice->pluginFocusOutEvent();
38924033 }
3893− _keyboardFocusedItem.set(entityItemID);
38944034 _lastAcceptedKeyPress = usecTimestampNow();
38954035
3896− // create a focus
3897− if (_keyboardFocusHighlightID < 0 || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) {
3898− _keyboardFocusHighlight = std::make_shared<Cube3DOverlay>();
3899− _keyboardFocusHighlight->setAlpha(1.0f);
3900− _keyboardFocusHighlight->setBorderSize(1.0f);
3901− _keyboardFocusHighlight->setColor({ 0xFF, 0xEF, 0x00 });
3902− _keyboardFocusHighlight->setIsSolid(false);
3903− _keyboardFocusHighlight->setPulseMin(0.5);
3904− _keyboardFocusHighlight->setPulseMax(1.0);
3905− _keyboardFocusHighlight->setColorPulse(1.0);
3906− _keyboardFocusHighlight->setIgnoreRayIntersection(true);
3907− _keyboardFocusHighlight->setDrawInFront(false);
3908− _keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight);
3909− }
4036+ setKeyboardFocusHighlight(entity->getPosition(), entity->getRotation(),
4037+ entity->getDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR);
4038+ }
4039+ }
4040+ }
4041+}
4042+
4043+unsigned int Application::getKeyboardFocusOverlay() {
4044+ return _keyboardFocusedOverlay.get();
4045+}
4046+
4047+void Application::setKeyboardFocusOverlay(unsigned int overlayID) {
4048+ if (overlayID != _keyboardFocusedOverlay.get()) {
4049+ _keyboardFocusedOverlay.set(overlayID);
4050+
4051+ if (_keyboardFocusHighlight && _keyboardFocusedEntity.get() == UNKNOWN_ENTITY_ID) {
4052+ _keyboardFocusHighlight->setVisible(false);
4053+ }
4054+
4055+ if (overlayID == UNKNOWN_OVERLAY_ID) {
4056+ return;
4057+ }
4058+
4059+ auto overlayType = getOverlays().getOverlayType(overlayID);
4060+ auto isVisible = getOverlays().getProperty(overlayID, "visible").value.toBool();
4061+ if (overlayType == Web3DOverlay::TYPE && isVisible) {
4062+ auto overlay = std::dynamic_pointer_cast<Web3DOverlay>(getOverlays().getOverlay(overlayID));
4063+ overlay->setProxyWindow(_window->windowHandle());
39104064
3911− // position the focus
3912− _keyboardFocusHighlight->setRotation(entity->getRotation());
3913− _keyboardFocusHighlight->setPosition(entity->getPosition());
3914− _keyboardFocusHighlight->setDimensions(entity->getDimensions() * 1.05f);
3915− _keyboardFocusHighlight->setVisible(true);
4065+ if (_keyboardMouseDevice->isActive()) {
4066+ _keyboardMouseDevice->pluginFocusOutEvent();
39164067 }
4068+ _lastAcceptedKeyPress = usecTimestampNow();
4069+
4070+ auto size = overlay->getSize() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR;
4071+ const float OVERLAY_DEPTH = 0.0105f;
4072+ setKeyboardFocusHighlight(overlay->getPosition(), overlay->getRotation(), glm::vec3(size.x, size.y, OVERLAY_DEPTH));
39174073 }
39184074 }
39194075 }
@@ -3936,9 +4092,11 @@ void Application::updateDialogs(float deltaTime) const { · 16 unmodified lines
39364092 }
39374093 }
39384094
4095+static bool domainLoadingInProgress = false;
4096+
39394097 void Application::update(float deltaTime) {
39404098
3941− PROFILE_RANGE_EX(__FUNCTION__, 0xffff0000, (uint64_t)_frameCount + 1);
4099+ PROFILE_RANGE_EX(app, __FUNCTION__, 0xffff0000, (uint64_t)_frameCount + 1);
39424100
39434101 bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
39444102 PerformanceWarning warn(showWarnings, "Application::update()");
@@ -3946,6 +4104,11 @@ void Application::update(float deltaTime) { · 1 unmodified line
39464104 updateLOD();
39474105
39484106 if (!_physicsEnabled) {
4107+ if (!domainLoadingInProgress) {
4108+ PROFILE_ASYNC_BEGIN(app, "Scene Loading", "");
4109+ domainLoadingInProgress = true;
4110+ }
4111+
39494112 // we haven't yet enabled physics. we wait until we think we have all the collision information
39504113 // for nearby entities before starting bullet up.
39514114 quint64 now = usecTimestampNow();
@@ -3975,6 +4138,9 @@ void Application::update(float deltaTime) { · 23 unmodified lines
39754138 }
39764139 }
39774140 }
4141+ } else if (domainLoadingInProgress) {
4142+ domainLoadingInProgress = false;
4143+ PROFILE_ASYNC_END(app, "Scene Loading", "");
39784144 }
39794145
39804146 {
@@ -4068,12 +4234,12 @@ void Application::update(float deltaTime) { · 87 unmodified lines
40684234 QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>();
40694235
40704236 if (_physicsEnabled) {
4071− PROFILE_RANGE_EX("Physics", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
4237+ PROFILE_RANGE_EX(simulation_physics, "Physics", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
40724238
40734239 PerformanceTimer perfTimer("physics");
40744240
40754241 {
4076− PROFILE_RANGE_EX("UpdateStats", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
4242+ PROFILE_RANGE_EX(simulation_physics, "UpdateStats", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
40774243
40784244 PerformanceTimer perfTimer("updateStates)");
40794245 static VectorOfMotionStates motionStates;
@@ -4107,14 +4273,14 @@ void Application::update(float deltaTime) { · 27 unmodified lines
41074273 });
41084274 }
41094275 {
4110− PROFILE_RANGE_EX("StepSimulation", 0xffff8000, (uint64_t)getActiveDisplayPlugin()->presentCount());
4276+ PROFILE_RANGE_EX(simulation_physics, "StepSimulation", 0xffff8000, (uint64_t)getActiveDisplayPlugin()->presentCount());
41114277 PerformanceTimer perfTimer("stepSimulation");
41124278 getEntities()->getTree()->withWriteLock([&] {
41134279 _physicsEngine->stepSimulation();
41144280 });
41154281 }
41164282 {
4117− PROFILE_RANGE_EX("HarvestChanges", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
4283+ PROFILE_RANGE_EX(simulation_physics, "HarvestChanges", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
41184284 PerformanceTimer perfTimer("harvestChanges");
41194285 if (_physicsEngine->hasOutgoingChanges()) {
41204286 getEntities()->getTree()->withWriteLock([&] {
@@ -4156,20 +4322,20 @@ void Application::update(float deltaTime) { · 35 unmodified lines
41564322 _avatarSimCounter.increment();
41574323
41584324 {
4159− PROFILE_RANGE_EX("OtherAvatars", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount());
4325+ PROFILE_RANGE_EX(simulation, "OtherAvatars", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount());
41604326 avatarManager->updateOtherAvatars(deltaTime);
41614327 }
41624328
41634329 qApp->updateMyAvatarLookAtPosition();
41644330
41654331 {
4166− PROFILE_RANGE_EX("MyAvatar", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount());
4332+ PROFILE_RANGE_EX(simulation, "MyAvatar", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount());
41674333 avatarManager->updateMyAvatar(deltaTime);
41684334 }
41694335 }
41704336
41714337 {
4172− PROFILE_RANGE_EX("Overlays", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
4338+ PROFILE_RANGE_EX(app, "Overlays", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
41734339 PerformanceTimer perfTimer("overlays");
41744340 _overlays.update(deltaTime);
41754341 }
@@ -4189,7 +4355,7 @@ void Application::update(float deltaTime) { · 13 unmodified lines
41894355
41904356 // Update my voxel servers with my current voxel query...
41914357 {
4192− PROFILE_RANGE_EX("QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
4358+ PROFILE_RANGE_EX(app, "QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
41934359 QMutexLocker viewLocker(&_viewMutex);
41944360 PerformanceTimer perfTimer("queryOctree");
41954361 quint64 sinceLastQuery = now - _lastQueriedTime;
@@ -4229,7 +4395,7 @@ void Application::update(float deltaTime) { · 33 unmodified lines
42294395 avatarManager->postUpdate(deltaTime);
42304396
42314397 {
4232− PROFILE_RANGE_EX("PreRenderLambdas", 0xffff0000, (uint64_t)0);
4398+ PROFILE_RANGE_EX(app, "PreRenderLambdas", 0xffff0000, (uint64_t)0);
42334399
42344400 std::unique_lock<std::mutex> guard(_postUpdateLambdasLock);
42354401 for (auto& iter : _postUpdateLambdas) {
@@ -4506,8 +4672,6 @@ QRect Application::getDesirableApplicationGeometry() const { · 270 unmodified lines
45064672 // or the "myCamera".
45074673 //
45084674 void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) {
4509− PerformanceTimer perfTimer("loadViewFrustum");
4510− PROFILE_RANGE(__FUNCTION__);
45114675 // We will use these below, from either the camera or head vectors calculated above
45124676 viewFrustum.setProjection(camera.getProjection());
45134677
@@ -4683,7 +4847,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 169 unmodified lines
46834847 myAvatar->preDisplaySide(renderArgs);
46844848
46854849 activeRenderingThread = QThread::currentThread();
4686− PROFILE_RANGE(__FUNCTION__);
4850+ PROFILE_RANGE(render, __FUNCTION__);
46874851 PerformanceTimer perfTimer("display");
46884852 PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()");
46894853
@@ -5468,6 +5632,464 @@ void Application::showAssetServerWidget(QString filePath) { · 778 unmodified lines
54685632 startUpload(nullptr, nullptr);
54695633 }
54705634
5635+void Application::addAssetToWorldFromURL(QString url) {
5636+ qInfo(interfaceapp) << "Download asset and add to world from" << url;
5637+
5638+ QString filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
5639+
5640+ if (!DependencyManager::get<NodeList>()->getThisNodeCanWriteAssets()) {
5641+ QString errorInfo = "You do not have permissions to write to the Asset Server.";
5642+ qWarning(interfaceapp) << "Error downloading asset: " + errorInfo;
5643+ addAssetToWorldError(filename, errorInfo);
5644+ return;
5645+ }
5646+
5647+ addAssetToWorldInfo(filename, "Downloading asset file " + filename + ".");
5648+
5649+ auto request = ResourceManager::createResourceRequest(nullptr, QUrl(url));
5650+ connect(request, &ResourceRequest::finished, this, &Application::addAssetToWorldFromURLRequestFinished);
5651+ request->send();
5652+}
5653+
5654+void Application::addAssetToWorldFromURLRequestFinished() {
5655+ auto request = qobject_cast<ResourceRequest*>(sender());
5656+ auto url = request->getUrl().toString();
5657+ auto result = request->getResult();
5658+
5659+ QString filename = url.section("filename=", 1, 1); // Filename from trailing "?filename=" URL parameter.
5660+
5661+ if (result == ResourceRequest::Success) {
5662+ qInfo(interfaceapp) << "Downloaded asset from" << url;
5663+ QTemporaryDir temporaryDir;
5664+ temporaryDir.setAutoRemove(false);
5665+ if (temporaryDir.isValid()) {
5666+ QString temporaryDirPath = temporaryDir.path();
5667+ QString downloadPath = temporaryDirPath + "/" + filename;
5668+ qInfo(interfaceapp) << "Download path:" << downloadPath;
5669+
5670+ QFile tempFile(downloadPath);
5671+ if (tempFile.open(QIODevice::WriteOnly)) {
5672+ tempFile.write(request->getData());
5673+ addAssetToWorldInfoClear(filename); // Remove message from list; next one added will have a different key.
5674+ qApp->getFileDownloadInterface()->runUnzip(downloadPath, url, true);
5675+ } else {
5676+ QString errorInfo = "Couldn't open temporary file for download";
5677+ qWarning(interfaceapp) << errorInfo;
5678+ addAssetToWorldError(filename, errorInfo);
5679+ }
5680+ } else {
5681+ QString errorInfo = "Couldn't create temporary directory for download";
5682+ qWarning(interfaceapp) << errorInfo;
5683+ addAssetToWorldError(filename, errorInfo);
5684+ }
5685+ } else {
5686+ qWarning(interfaceapp) << "Error downloading" << url << ":" << request->getResultString();
5687+ addAssetToWorldError(filename, "Error downloading " + filename + " : " + request->getResultString());
5688+ }
5689+
5690+ request->deleteLater();
5691+}
5692+
5693+
5694+QString filenameFromPath(QString filePath) {
5695+ return filePath.right(filePath.length() - filePath.lastIndexOf("/") - 1);
5696+}
5697+
5698+void Application::addAssetToWorldUnzipFailure(QString filePath) {
5699+ QString filename = filenameFromPath(QUrl(filePath).toLocalFile());
5700+ qWarning(interfaceapp) << "Couldn't unzip file" << filePath;
5701+ addAssetToWorldError(filename, "Couldn't unzip file " + filename + ".");
5702+}
5703+
5704+void Application::addAssetToWorld(QString filePath) {
5705+ // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget().
5706+
5707+ QString path = QUrl(filePath).toLocalFile();
5708+ QString filename = filenameFromPath(path);
5709+ QString mapping = "/" + filename;
5710+
5711+ // Test repeated because possibly different code paths.
5712+ if (!DependencyManager::get<NodeList>()->getThisNodeCanWriteAssets()) {
5713+ QString errorInfo = "You do not have permissions to write to the Asset Server.";
5714+ qWarning(interfaceapp) << "Error downloading asset: " + errorInfo;
5715+ addAssetToWorldError(filename, errorInfo);
5716+ return;
5717+ }
5718+
5719+ addAssetToWorldInfo(filename, "Adding " + mapping.mid(1) + " to the Asset Server.");
5720+
5721+ addAssetToWorldWithNewMapping(path, mapping, 0);
5722+}
5723+
5724+void Application::addAssetToWorldWithNewMapping(QString filePath, QString mapping, int copy) {
5725+ auto request = DependencyManager::get<AssetClient>()->createGetMappingRequest(mapping);
5726+
5727+ QObject::connect(request, &GetMappingRequest::finished, this, [=](GetMappingRequest* request) mutable {
5728+ const int MAX_COPY_COUNT = 100; // Limit number of duplicate assets; recursion guard.
5729+ auto result = request->getError();
5730+ if (result == GetMappingRequest::NotFound) {
5731+ addAssetToWorldUpload(filePath, mapping);
5732+ } else if (result != GetMappingRequest::NoError) {
5733+ QString errorInfo = "Could not map asset name: "
5734+ + mapping.left(mapping.length() - QString::number(copy).length() - 1);
5735+ qWarning(interfaceapp) << "Error downloading asset: " + errorInfo;
5736+ addAssetToWorldError(filenameFromPath(filePath), errorInfo);
5737+ } else if (copy < MAX_COPY_COUNT - 1) {
5738+ if (copy > 0) {
5739+ mapping = mapping.remove(mapping.lastIndexOf("-"), QString::number(copy).length() + 1);
5740+ }
5741+ copy++;
5742+ mapping = mapping.insert(mapping.lastIndexOf("."), "-" + QString::number(copy));
5743+ addAssetToWorldWithNewMapping(filePath, mapping, copy);
5744+ } else {
5745+ QString errorInfo = "Too many copies of asset name: "
5746+ + mapping.left(mapping.length() - QString::number(copy).length() - 1);
5747+ qWarning(interfaceapp) << "Error downloading asset: " + errorInfo;
5748+ addAssetToWorldError(filenameFromPath(filePath), errorInfo);
5749+ }
5750+ request->deleteLater();
5751+ });
5752+
5753+ request->start();
5754+}
5755+
5756+void Application::addAssetToWorldUpload(QString filePath, QString mapping) {
5757+ qInfo(interfaceapp) << "Uploading" << filePath << "to Asset Server as" << mapping;
5758+ auto upload = DependencyManager::get<AssetClient>()->createUpload(filePath);
5759+ QObject::connect(upload, &AssetUpload::finished, this, [=](AssetUpload* upload, const QString& hash) mutable {
5760+ if (upload->getError() != AssetUpload::NoError) {
5761+ QString errorInfo = "Could not upload asset to the Asset Server.";
5762+ qWarning(interfaceapp) << "Error downloading asset: " + errorInfo;
5763+ addAssetToWorldError(filenameFromPath(filePath), errorInfo);
5764+ } else {
5765+ addAssetToWorldSetMapping(filePath, mapping, hash);
5766+ }
5767+
5768+ // Remove temporary directory created by Clara.io market place download.
5769+ int index = filePath.lastIndexOf("/model_repo/");
5770+ if (index > 0) {
5771+ QString tempDir = filePath.left(index);
5772+ qCDebug(interfaceapp) << "Removing temporary directory at: " + tempDir;
5773+ QDir(tempDir).removeRecursively();
5774+ }
5775+
5776+ upload->deleteLater();
5777+ });
5778+
5779+ upload->start();
5780+}
5781+
5782+void Application::addAssetToWorldSetMapping(QString filePath, QString mapping, QString hash) {
5783+ auto request = DependencyManager::get<AssetClient>()->createSetMappingRequest(mapping, hash);
5784+ connect(request, &SetMappingRequest::finished, this, [=](SetMappingRequest* request) mutable {
5785+ if (request->getError() != SetMappingRequest::NoError) {
5786+ QString errorInfo = "Could not set asset mapping.";
5787+ qWarning(interfaceapp) << "Error downloading asset: " + errorInfo;
5788+ addAssetToWorldError(filenameFromPath(filePath), errorInfo);
5789+ } else {
5790+ addAssetToWorldAddEntity(filePath, mapping);
5791+ }
5792+ request->deleteLater();
5793+ });
5794+
5795+ request->start();
5796+}
5797+
5798+void Application::addAssetToWorldAddEntity(QString filePath, QString mapping) {
5799+ EntityItemProperties properties;
5800+ properties.setType(EntityTypes::Model);
5801+ properties.setName(mapping.right(mapping.length() - 1));
5802+ properties.setModelURL("atp:" + mapping);
5803+ properties.setShapeType(SHAPE_TYPE_SIMPLE_COMPOUND);
5804+ properties.setCollisionless(true); // Temporarily set so that doesn't collide with avatar.
5805+ properties.setVisible(false); // Temporarily set so that don't see at large unresized dimensions.
5806+ properties.setPosition(getMyAvatar()->getPosition() + getMyAvatar()->getOrientation() * glm::vec3(0.0f, 0.0f, -2.0f));
5807+ properties.setGravity(glm::vec3(0.0f, 0.0f, 0.0f));
5808+ auto entityID = DependencyManager::get<EntityScriptingInterface>()->addEntity(properties);
5809+
5810+ // Note: Model dimensions are not available here; model is scaled per FBX mesh in RenderableModelEntityItem::update() later
5811+ // on. But FBX dimensions may be in cm, so we monitor for the dimension change and rescale again if warranted.
5812+
5813+ if (entityID == QUuid()) {
5814+ QString errorInfo = "Could not add asset " + mapping + " to world.";
5815+ qWarning(interfaceapp) << "Could not add asset to world: " + errorInfo;
5816+ addAssetToWorldError(filenameFromPath(filePath), errorInfo);
5817+ } else {
5818+ // Monitor when asset is rendered in world so that can resize if necessary.
5819+ _addAssetToWorldResizeList.insert(entityID, 0); // List value is count of checks performed.
5820+ if (!_addAssetToWorldResizeTimer.isActive()) {
5821+ _addAssetToWorldResizeTimer.start();
5822+ }
5823+
5824+ // Close progress message box.
5825+ addAssetToWorldInfoDone(filenameFromPath(filePath));
5826+ }
5827+}
5828+
5829+void Application::addAssetToWorldCheckModelSize() {
5830+ if (_addAssetToWorldResizeList.size() == 0) {
5831+ return;
5832+ }
5833+
5834+ auto item = _addAssetToWorldResizeList.begin();
5835+ while (item != _addAssetToWorldResizeList.end()) {
5836+ auto entityID = item.key();
5837+
5838+ EntityPropertyFlags propertyFlags;
5839+ propertyFlags += PROP_NAME;
5840+ propertyFlags += PROP_DIMENSIONS;
5841+ auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
5842+ auto properties = entityScriptingInterface->getEntityProperties(entityID, propertyFlags);
5843+ auto name = properties.getName();
5844+ auto dimensions = properties.getDimensions();
5845+
5846+ const QString GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":true}}";
5847+ bool doResize = false;
5848+
5849+ const glm::vec3 DEFAULT_DIMENSIONS = glm::vec3(0.1f, 0.1f, 0.1f);
5850+ if (dimensions != DEFAULT_DIMENSIONS) {
5851+
5852+ // Scale model so that its maximum is exactly specific size.
5853+ const float MAXIMUM_DIMENSION = 1.0f;
5854+ auto previousDimensions = dimensions;
5855+ auto scale = std::min(MAXIMUM_DIMENSION / dimensions.x, std::min(MAXIMUM_DIMENSION / dimensions.y,
5856+ MAXIMUM_DIMENSION / dimensions.z));
5857+ dimensions *= scale;
5858+ qInfo(interfaceapp) << "Asset" << name << "auto-resized from" << previousDimensions << " to " << dimensions;
5859+ doResize = true;
5860+
5861+ item = _addAssetToWorldResizeList.erase(item); // Finished with this entity; advance to next.
5862+ } else {
5863+ // Increment count of checks done.
5864+ _addAssetToWorldResizeList[entityID]++;
5865+
5866+ const int CHECK_MODEL_SIZE_MAX_CHECKS = 300;
5867+ if (_addAssetToWorldResizeList[entityID] > CHECK_MODEL_SIZE_MAX_CHECKS) {
5868+ // Have done enough checks; model was either the default size or something's gone wrong.
5869+
5870+ // Rescale all dimensions.
5871+ const glm::vec3 UNIT_DIMENSIONS = glm::vec3(1.0f, 1.0f, 1.0f);
5872+ dimensions = UNIT_DIMENSIONS;
5873+ qInfo(interfaceapp) << "Asset" << name << "auto-resize timed out; resized to " << dimensions;
5874+ doResize = true;
5875+
5876+ item = _addAssetToWorldResizeList.erase(item); // Finished with this entity; advance to next.
5877+ } else {
5878+ // No action on this entity; advance to next.
5879+ ++item;
5880+ }
5881+ }
5882+
5883+ if (doResize) {
5884+ EntityItemProperties properties;
5885+ properties.setDimensions(dimensions);
5886+ properties.setVisible(true);
5887+ properties.setCollisionless(false);
5888+ properties.setUserData(GRABBABLE_USER_DATA);
5889+ properties.setLastEdited(usecTimestampNow());
5890+ entityScriptingInterface->editEntity(entityID, properties);
5891+ }
5892+ }
5893+
5894+ // Stop timer if nothing in list to check.
5895+ if (_addAssetToWorldResizeList.size() == 0) {
5896+ _addAssetToWorldResizeTimer.stop();
5897+ }
5898+}
5899+
5900+
5901+void Application::addAssetToWorldInfo(QString modelName, QString infoText) {
5902+ // Displays the most recent info message, subject to being overridden by error messages.
5903+
5904+ if (_aboutToQuit) {
5905+ return;
5906+ }
5907+
5908+ /*
5909+ Cancel info timer if running.
5910+ If list has an entry for modelName, delete it (just one).
5911+ Append modelName, infoText to list.
5912+ Display infoText in message box unless an error is being displayed (i.e., error timer is running).
5913+ Show message box if not already visible.
5914+ */
5915+
5916+ _addAssetToWorldInfoTimer.stop();
5917+
5918+ addAssetToWorldInfoClear(modelName);
5919+
5920+ _addAssetToWorldInfoKeys.append(modelName);
5921+ _addAssetToWorldInfoMessages.append(infoText);
5922+
5923+ if (!_addAssetToWorldErrorTimer.isActive()) {
5924+ if (!_addAssetToWorldMessageBox) {
5925+ _addAssetToWorldMessageBox = DependencyManager::get<OffscreenUi>()->createMessageBox(OffscreenUi::ICON_INFORMATION,
5926+ "Downloading Asset", "", QMessageBox::NoButton, QMessageBox::NoButton);
5927+ connect(_addAssetToWorldMessageBox, SIGNAL(destroyed()), this, SLOT(onAssetToWorldMessageBoxClosed()));
5928+ }
5929+
5930+ _addAssetToWorldMessageBox->setProperty("text", "\n" + infoText);
5931+ _addAssetToWorldMessageBox->setVisible(true);
5932+ }
5933+}
5934+
5935+void Application::addAssetToWorldInfoClear(QString modelName) {
5936+ // Clears modelName entry from message list without affecting message currently displayed.
5937+
5938+ if (_aboutToQuit) {
5939+ return;
5940+ }
5941+
5942+ /*
5943+ Delete entry for modelName from list.
5944+ */
5945+
5946+ auto index = _addAssetToWorldInfoKeys.indexOf(modelName);
5947+ if (index > -1) {
5948+ _addAssetToWorldInfoKeys.removeAt(index);
5949+ _addAssetToWorldInfoMessages.removeAt(index);
5950+ }
5951+}
5952+
5953+void Application::addAssetToWorldInfoDone(QString modelName) {
5954+ // Continues to display this message if the latest for a few seconds, then deletes it and displays the next latest.
5955+
5956+ if (_aboutToQuit) {
5957+ return;
5958+ }
5959+
5960+ /*
5961+ Delete entry for modelName from list.
5962+ (Re)start the info timer to update message box. ... onAddAssetToWorldInfoTimeout()
5963+ */
5964+
5965+ addAssetToWorldInfoClear(modelName);
5966+ _addAssetToWorldInfoTimer.start();
5967+}
5968+
5969+void Application::addAssetToWorldInfoTimeout() {
5970+ if (_aboutToQuit) {
5971+ return;
5972+ }
5973+
5974+ /*
5975+ If list not empty, display last message in list (may already be displayed ) unless an error is being displayed.
5976+ If list empty, close the message box unless an error is being displayed.
5977+ */
5978+
5979+ if (!_addAssetToWorldErrorTimer.isActive() && _addAssetToWorldMessageBox) {
5980+ if (_addAssetToWorldInfoKeys.length() > 0) {
5981+ _addAssetToWorldMessageBox->setProperty("text", "\n" + _addAssetToWorldInfoMessages.last());
5982+ } else {
5983+ disconnect(_addAssetToWorldMessageBox);
5984+ _addAssetToWorldMessageBox->setVisible(false);
5985+ _addAssetToWorldMessageBox->deleteLater();
5986+ _addAssetToWorldMessageBox = nullptr;
5987+ }
5988+ }
5989+}
5990+
5991+
5992+void Application::addAssetToWorldError(QString modelName, QString errorText) {
5993+ // Displays the most recent error message for a few seconds.
5994+
5995+ if (_aboutToQuit) {
5996+ return;
5997+ }
5998+
5999+ /*
6000+ If list has an entry for modelName, delete it.
6001+ Display errorText in message box.
6002+ Show message box if not already visible.
6003+ (Re)start error timer. ... onAddAssetToWorldErrorTimeout()
6004+ */
6005+
6006+ addAssetToWorldInfoClear(modelName);
6007+
6008+ if (!_addAssetToWorldMessageBox) {
6009+ _addAssetToWorldMessageBox = DependencyManager::get<OffscreenUi>()->createMessageBox(OffscreenUi::ICON_INFORMATION,
6010+ "Downloading Asset", "", QMessageBox::NoButton, QMessageBox::NoButton);
6011+ connect(_addAssetToWorldMessageBox, SIGNAL(destroyed()), this, SLOT(onAssetToWorldMessageBoxClosed()));
6012+ }
6013+
6014+ _addAssetToWorldMessageBox->setProperty("text", "\n" + errorText);
6015+ _addAssetToWorldMessageBox->setVisible(true);
6016+
6017+ _addAssetToWorldErrorTimer.start();
6018+}
6019+
6020+void Application::addAssetToWorldErrorTimeout() {
6021+ if (_aboutToQuit) {
6022+ return;
6023+ }
6024+
6025+ /*
6026+ If list is not empty, display message from last entry.
6027+ If list is empty, close the message box.
6028+ */
6029+
6030+ if (_addAssetToWorldMessageBox) {
6031+ if (_addAssetToWorldInfoKeys.length() > 0) {
6032+ _addAssetToWorldMessageBox->setProperty("text", "\n" + _addAssetToWorldInfoMessages.last());
6033+ } else {
6034+ disconnect(_addAssetToWorldMessageBox);
6035+ _addAssetToWorldMessageBox->setVisible(false);
6036+ _addAssetToWorldMessageBox->deleteLater();
6037+ _addAssetToWorldMessageBox = nullptr;
6038+ }
6039+ }
6040+}
6041+
6042+
6043+void Application::addAssetToWorldMessageClose() {
6044+ // Clear messages, e.g., if Interface is being closed or domain changes.
6045+
6046+ /*
6047+ Call if user manually closes message box.
6048+ Call if domain changes.
6049+ Call if application is shutting down.
6050+
6051+ Stop timers.
6052+ Close the message box if open.
6053+ Clear lists.
6054+ */
6055+
6056+ _addAssetToWorldInfoTimer.stop();
6057+ _addAssetToWorldErrorTimer.stop();
6058+
6059+ if (_addAssetToWorldMessageBox) {
6060+ disconnect(_addAssetToWorldMessageBox);
6061+ _addAssetToWorldMessageBox->setVisible(false);
6062+ _addAssetToWorldMessageBox->deleteLater();
6063+ _addAssetToWorldMessageBox = nullptr;
6064+ }
6065+
6066+ _addAssetToWorldInfoKeys.clear();
6067+ _addAssetToWorldInfoMessages.clear();
6068+}
6069+
6070+void Application::onAssetToWorldMessageBoxClosed() {
6071+ if (_addAssetToWorldMessageBox) {
6072+ // User manually closed message box; perhaps because it has become stuck, so reset all messages.
6073+ qInfo(interfaceapp) << "User manually closed download status message box";
6074+ disconnect(_addAssetToWorldMessageBox);
6075+ _addAssetToWorldMessageBox = nullptr;
6076+ addAssetToWorldMessageClose();
6077+ }
6078+}
6079+
6080+
6081+void Application::handleUnzip(QString zipFile, QString unzipFile, bool autoAdd) {
6082+ if (autoAdd) {
6083+ if (!unzipFile.isEmpty()) {
6084+ addAssetToWorld(unzipFile);
6085+ } else {
6086+ addAssetToWorldUnzipFailure(zipFile);
6087+ }
6088+ } else {
6089+ showAssetServerWidget(unzipFile);
6090+ }
6091+}
6092+
54716093 void Application::packageModel() {
54726094 ModelPackager::package();
54736095 }