diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5d50a1c9fe..7695bd1b99 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -739,7 +739,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : connect(&identityPacketTimer, &QTimer::timeout, getMyAvatar(), &MyAvatar::sendIdentityPacket); identityPacketTimer.start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); - ResourceCache::setRequestLimit(MAX_CONCURRENT_RESOURCE_DOWNLOADS); + const char** constArgv = const_cast(argv); + QString concurrentDownloadsStr = getCmdOption(argc, constArgv, "--concurrent-downloads"); + bool success; + int concurrentDownloads = concurrentDownloadsStr.toInt(&success); + if (!success) { + concurrentDownloads = MAX_CONCURRENT_RESOURCE_DOWNLOADS; + } + ResourceCache::setRequestLimit(concurrentDownloads); _glWidget = new GLCanvas(); getApplicationCompositor().setRenderingWidget(_glWidget); @@ -3240,6 +3247,18 @@ void Application::init() { getEntities()->setViewFrustum(_viewFrustum); } + getEntities()->setEntityLoadingPriorityFunction([this](const EntityItem& item) { + auto dims = item.getDimensions(); + auto maxSize = glm::max(dims.x, dims.y, dims.z); + + if (maxSize <= 0.0f) { + return 0.0f; + } + + auto distance = glm::distance(getMyAvatar()->getPosition(), item.getPosition()); + return atan2(maxSize, distance); + }); + ObjectMotionState::setShapeManager(&_shapeManager); _physicsEngine->init(); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 1ec934be92..24827ea111 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -529,7 +529,7 @@ void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const Sha std::static_pointer_cast(_tree)->processEraseMessage(message, sourceNode); } -ModelPointer EntityTreeRenderer::allocateModel(const QString& url, const QString& collisionUrl) { +ModelPointer EntityTreeRenderer::allocateModel(const QString& url, const QString& collisionUrl, float loadingPriority) { ModelPointer model = nullptr; // Only create and delete models on the thread that owns the EntityTreeRenderer @@ -543,6 +543,7 @@ ModelPointer EntityTreeRenderer::allocateModel(const QString& url, const QString } model = std::make_shared(std::make_shared()); + model->setLoadingPriority(loadingPriority); model->init(); model->setURL(QUrl(url)); model->setCollisionModelURL(QUrl(collisionUrl)); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index b0d0d2bacc..99c62ab5f6 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -28,11 +28,14 @@ class AbstractViewStateInterface; class Model; class ScriptEngine; class ZoneEntityItem; +class EntityItem; class Model; using ModelPointer = std::shared_ptr; using ModelWeakPointer = std::weak_ptr; +using CalculateEntityLoadingPriority = std::function; + // Generic client side Octree renderer class. class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService, public Dependency { Q_OBJECT @@ -46,6 +49,10 @@ public: virtual PacketType getExpectedPacketType() const { return PacketType::EntityData; } virtual void setTree(OctreePointer newTree); + // Returns the priority at which an entity should be loaded. Higher values indicate higher priority. + float getEntityLoadingPriority(const EntityItem& item) const { return _calculateEntityLoadingPriorityFunc(item); } + void setEntityLoadingPriorityFunction(CalculateEntityLoadingPriority fn) { this->_calculateEntityLoadingPriorityFunc = fn; } + void shutdown(); void update(); @@ -66,7 +73,7 @@ public: void reloadEntityScripts(); /// if a renderable entity item needs a model, we will allocate it for them - Q_INVOKABLE ModelPointer allocateModel(const QString& url, const QString& collisionUrl); + Q_INVOKABLE ModelPointer allocateModel(const QString& url, const QString& collisionUrl, float loadingPriority = 0.0f); /// if a renderable entity item needs to update the URL of a model, we will handle that for the entity Q_INVOKABLE ModelPointer updateModel(ModelPointer original, const QString& newUrl, const QString& collisionUrl); @@ -202,6 +209,10 @@ private: QList _entityIDsLastInScene; static int _entitiesScriptEngineCount; + + CalculateEntityLoadingPriority _calculateEntityLoadingPriorityFunc = [](const EntityItem& item) -> float { + return 0.0f; + }; }; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index eba2d4cf4b..29992e897a 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -484,7 +484,7 @@ ModelPointer RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { if (!getModelURL().isEmpty()) { // If we don't have a model, allocate one *immediately* if (!_model) { - _model = _myRenderer->allocateModel(getModelURL(), getCompoundShapeURL()); + _model = _myRenderer->allocateModel(getModelURL(), getCompoundShapeURL(), renderer->getEntityLoadingPriority(*this)); _needsInitialSimulation = true; // If we need to change URLs, update it *after rendering* (to avoid access violations) } else if ((QUrl(getModelURL()) != _model->getURL() || QUrl(getCompoundShapeURL()) != _model->getCollisionURL())) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index f487e79880..f63ffcbdb4 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -52,7 +52,6 @@ public: bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, void** intersectedObject, bool precisionPicking) const override; - ModelPointer getModel(EntityTreeRenderer* renderer); virtual bool needsToCallUpdate() const override; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index d755dc3aca..1ddc3cda47 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -826,7 +826,9 @@ void Model::setURL(const QUrl& url) { invalidCalculatedMeshBoxes(); deleteGeometry(); - _renderWatcher.setResource(DependencyManager::get()->getGeometryResource(url)); + auto resource = DependencyManager::get()->getGeometryResource(url); + resource->setLoadPriority(this, _loadingPriority); + _renderWatcher.setResource(resource); onInvalidate(); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index f7bf83ca5b..aecbcf2510 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -238,6 +238,8 @@ public: // returns 'true' if needs fullUpdate after geometry change bool updateGeometry(); + void setLoadingPriority(float priority) { _loadingPriority = priority; } + public slots: void loadURLFinished(bool success); void loadCollisionModelURLFinished(bool success); @@ -405,6 +407,10 @@ protected: bool _visualGeometryRequestFailed { false }; bool _collisionGeometryRequestFailed { false }; + +private: + float _loadingPriority { 0.0f }; + }; Q_DECLARE_METATYPE(ModelPointer)