diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 69bd7523953..393a6db8784 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1237,16 +1237,21 @@ void ModelEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint render::Transaction transaction; // Check for removal - if (!_hasModel) { + bool shouldReloadModelURL = entity->shouldForceReloadModelURL(); + if (!_hasModel || shouldReloadModelURL) { if (model) { model->removeFromScene(scene, transaction); entity->bumpAncestorChainRenderableVersion(); emit DependencyManager::get()-> modelRemovedFromScene(entity->getEntityItemID(), NestableType::Entity, model); withWriteLock([&] { _model.reset(); }); + scene->enqueueTransaction(transaction); } _didLastVisualGeometryRequestSucceed = false; setKey(_didLastVisualGeometryRequestSucceed, model); + if (shouldReloadModelURL) { + emit requestRenderUpdate(); + } return; } diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 90c521215e9..d87691a3e81 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1329,6 +1329,13 @@ ParabolaToEntityIntersectionResult EntityScriptingInterface::evalParabolaInterse return result; } +void EntityScriptingInterface::reloadModelURL(const QUuid& entityID) { + if (auto entity = checkForTreeEntityAndTypeMatch(entityID, EntityTypes::Model)) { + auto modelEntity = std::dynamic_pointer_cast(entity); + modelEntity->forceReloadModelURL(); + } +} + bool EntityScriptingInterface::reloadServerScripts(const QUuid& entityID) { auto client = DependencyManager::get(); return client->reloadServerScript(entityID); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 5db278f51e9..a36fabd1002 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -837,10 +837,18 @@ public slots: const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue(), bool visibleOnly = false, bool collidableOnly = false) const; - /*@jsdoc - * Reloads an entity's server entity script such that the latest version re-downloaded. + + /**jsdoc + * Reloads an entity's model URL such that the latest version is re-downloaded. + * @function Entities.reloadModelURL + * @param {Uuid} entityID - The ID of the entity for which to reload the modelURL. + */ + Q_INVOKABLE void reloadModelURL(const QUuid& entityID); + + /**jsdoc + * Reloads an entity's server entity script such that the latest version is re-downloaded. * @function Entities.reloadServerScripts - * @param {Uuid} entityID - The ID of the entity to reload the server entity script of. + * @param {Uuid} entityID - The ID of the entity for which to reload the server entity script. * @returns {boolean} true if the reload request was successfully sent to the server, otherwise * false. */ diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 321e92b9f0d..8607e37cef3 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -754,3 +754,19 @@ bool ModelEntityItem::getUseOriginalPivot() const { return _useOriginalPivot; }); } + +void ModelEntityItem::forceReloadModelURL() { + withWriteLock([&] { + _forceReloadModelURL = true; + _needsRenderUpdate = true; + }); + somethingChangedNotification(); +} + +bool ModelEntityItem::shouldForceReloadModelURL() { + return resultWithWriteLock([&] { + bool toReturn = _forceReloadModelURL; + _forceReloadModelURL = false; // we also reset this here so we don't need to lock + unlock multiple times + return toReturn; + }); +} diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index a00327251c7..fe72619faab 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -123,6 +123,9 @@ class ModelEntityItem : public EntityItem { bool getUseOriginalPivot() const; void setUseOriginalPivot(bool useOriginalPivot); + void forceReloadModelURL(); + bool shouldForceReloadModelURL(); + private: void setAnimationSettings(const QString& value); // only called for old bitstream format bool applyNewAnimationProperties(AnimationPropertyGroup newProperties); @@ -157,6 +160,7 @@ class ModelEntityItem : public EntityItem { bool _groupCulled { false }; QVariantMap _blendshapeCoefficientsMap; bool _useOriginalPivot { false }; + bool _forceReloadModelURL { false }; ThreadSafeValueCache _compoundShapeURL; diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 9c0bb846f76..a76d55b98f1 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -19,7 +19,6 @@ #include #include -#include #include #include "AssetRequest.h" @@ -35,7 +34,6 @@ MessageID AssetClient::_currentID = 0; AssetClient::AssetClient() { - _cacheDir = qApp->property(hifi::properties::APP_LOCAL_DATA_PATH).toString(); setCustomDeleter([](Dependency* dependency){ static_cast(dependency)->deleteLater(); }); @@ -57,34 +55,6 @@ AssetClient::AssetClient() { this, &AssetClient::handleNodeClientConnectionReset); } -void AssetClient::initCaching() { - Q_ASSERT(QThread::currentThread() == thread()); - - // Setup disk cache if not already - auto& networkAccessManager = NetworkAccessManager::getInstance(); - if (!networkAccessManager.cache()) { - if (_cacheDir.isEmpty()) { -#ifdef Q_OS_ANDROID - QString cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); -#else - QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); -#endif - _cacheDir = !cachePath.isEmpty() ? cachePath : "interfaceCache"; - } - QNetworkDiskCache* cache = new QNetworkDiskCache(); - cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE); - cache->setCacheDirectory(_cacheDir); - networkAccessManager.setCache(cache); - qInfo() << "ResourceManager disk cache setup at" << _cacheDir - << "(size:" << MAXIMUM_CACHE_SIZE / BYTES_PER_GIGABYTES << "GB)"; - } else { - auto cache = qobject_cast(networkAccessManager.cache()); - qInfo() << "ResourceManager disk cache already setup at" << cache->cacheDirectory() - << "(size:" << cache->maximumCacheSize() / BYTES_PER_GIGABYTES << "GB)"; - } - -} - namespace { const QString& CACHE_ERROR_MESSAGE{ "AssetClient::Error: %1 %2" }; } diff --git a/libraries/networking/src/AssetClient.h b/libraries/networking/src/AssetClient.h index 1860a1744aa..a12eee0fb1a 100644 --- a/libraries/networking/src/AssetClient.h +++ b/libraries/networking/src/AssetClient.h @@ -64,8 +64,6 @@ class AssetClient : public QObject, public Dependency { Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data); public slots: - void initCaching(); - void cacheInfoRequest(QObject* reciever, QString slot); MiniPromise::Promise cacheInfoRequestAsync(MiniPromise::Promise deferred = nullptr); MiniPromise::Promise queryCacheMetaAsync(const QUrl& url, MiniPromise::Promise deferred = nullptr); @@ -117,8 +115,6 @@ private slots: std::unordered_map> _pendingInfoRequests; std::unordered_map> _pendingUploads; - QString _cacheDir; - friend class AssetRequest; friend class AssetUpload; friend class MappingRequest; diff --git a/libraries/networking/src/BaseAssetScriptingInterface.cpp b/libraries/networking/src/BaseAssetScriptingInterface.cpp index d7d14496baf..03fd91128f4 100644 --- a/libraries/networking/src/BaseAssetScriptingInterface.cpp +++ b/libraries/networking/src/BaseAssetScriptingInterface.cpp @@ -46,9 +46,6 @@ bool BaseAssetScriptingInterface::initializeCache() { return true; // cache is ready } - // attempt to initialize the cache - QMetaObject::invokeMethod(assetClient().data(), "initCaching"); - Promise deferred = makePromise("BaseAssetScriptingInterface--queryCacheStatus"); deferred->then([this](QVariantMap result) { _cacheReady = !result.value("cacheDirectory").toString().isEmpty(); diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index b13b66f7408..51216094737 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -58,7 +58,7 @@ void HTTPResourceRequest::doSend() { networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); if (_cacheEnabled) { - networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); + networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork); } else { networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); } diff --git a/libraries/networking/src/NetworkAccessManager.cpp b/libraries/networking/src/NetworkAccessManager.cpp index f73243e6754..0cf1a3bcb9d 100644 --- a/libraries/networking/src/NetworkAccessManager.cpp +++ b/libraries/networking/src/NetworkAccessManager.cpp @@ -11,18 +11,42 @@ #include "NetworkAccessManager.h" -#include - #include "AtpReply.h" +#include "ResourceCache.h" + +#include +#include #include +#include + +#include QThreadStorage networkAccessManagers; QNetworkAccessManager& NetworkAccessManager::getInstance() { if (!networkAccessManagers.hasLocalData()) { - networkAccessManagers.setLocalData(new QNetworkAccessManager()); + auto networkAccessManager = new QNetworkAccessManager(); + // Setup disk cache if not already + if (!networkAccessManager->cache()) { + QString cacheDir = qApp->property(hifi::properties::APP_LOCAL_DATA_PATH).toString(); + if (cacheDir.isEmpty()) { +#ifdef Q_OS_ANDROID + QString cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); +#else + QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); +#endif + cacheDir = !cachePath.isEmpty() ? cachePath : "interfaceCache"; + } + QNetworkDiskCache* cache = new QNetworkDiskCache(); + cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE); + cache->setCacheDirectory(cacheDir); + networkAccessManager->setCache(cache); + qInfo() << "NetworkAccessManager disk cache set up at" << cacheDir + << "(size:" << MAXIMUM_CACHE_SIZE / BYTES_PER_GIGABYTES << "GB)"; + } + networkAccessManagers.setLocalData(networkAccessManager); } - + return *networkAccessManagers.localData(); } diff --git a/libraries/networking/src/ResourceManager.cpp b/libraries/networking/src/ResourceManager.cpp index f4e4cca482d..44dc277e226 100644 --- a/libraries/networking/src/ResourceManager.cpp +++ b/libraries/networking/src/ResourceManager.cpp @@ -13,9 +13,6 @@ #include -#include -#include -#include #include #include @@ -37,7 +34,6 @@ ResourceManager::ResourceManager(bool atpSupportEnabled) : _atpSupportEnabled(at assetClient->moveToThread(&_thread); QObject::connect(&_thread, &QThread::started, assetClient.data(), [assetClient, name] { setThreadName(name.toStdString()); - assetClient->initCaching(); }); } diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js index 614bd5fd597..f87ae11e9dd 100644 --- a/scripts/system/create/edit.js +++ b/scripts/system/create/edit.js @@ -2601,6 +2601,12 @@ var PropertiesTool = function (opts) { pushCommandForSelections(); selectionManager._update(false, this); } + } else if (data.action === "reloadModelURL") { + if (selectionManager.hasSelection()) { + for (i = 0; i < selectionManager.selections.length; i++) { + Entities.reloadModelURL(selectionManager.selections[i]); + } + } } else if (data.action === "reloadClientScripts") { if (selectionManager.hasSelection()) { var timestamp = Date.now(); diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index 65e14d7203e..8b9fa859156 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -612,6 +612,7 @@ const GROUPS = [ type: "string", placeholder: "URL", propertyID: "modelURL", + buttons: [ { id: "reload", label: "F", className: "glyph", onClick: reloadModelURL } ], hideIfCertified: true, }, { @@ -3224,6 +3225,13 @@ function resetToNaturalDimensions() { })); } +function reloadModelURL() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "reloadModelURL" + })); +} + function reloadScripts() { EventBridge.emitWebEvent(JSON.stringify({ type: "action", diff --git a/tools/atp-client/src/ATPClientApp.cpp b/tools/atp-client/src/ATPClientApp.cpp index a4b95432577..d530edd4006 100644 --- a/tools/atp-client/src/ATPClientApp.cpp +++ b/tools/atp-client/src/ATPClientApp.cpp @@ -197,9 +197,6 @@ ATPClientApp::ATPClientApp(int argc, char* argv[]) : accountManager->requestAccessToken(_username, _password); } - auto assetClient = DependencyManager::set(); - assetClient->initCaching(); - if (_verbose) { qDebug() << "domain-server address is" << _domainServerAddress; }