diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8c865f906c..b21d660452 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1612,6 +1612,13 @@ bool Application::isLookingAtMyAvatar(Avatar* avatar) { return false; } +void Application::updateLOD() { + // adjust it unless we were asked to disable this feature + if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableAutoAdjustLOD)) { + Menu::getInstance()->autoAdjustLOD(_fps); + } +} + void Application::updateMouseRay() { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); @@ -1852,6 +1859,8 @@ void Application::update(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::update()"); + updateLOD(); + // check what's under the mouse and update the mouse voxel updateMouseRay(); @@ -2325,14 +2334,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... voxels..."); - _voxels.render(); - - // double check that our LOD doesn't need to be auto-adjusted - // adjust it unless we were asked to disable this feature - if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableAutoAdjustLOD)) { - Menu::getInstance()->autoAdjustLOD(_fps); - } } // also, metavoxels diff --git a/interface/src/Application.h b/interface/src/Application.h index 78826b4fd0..8c64da86b6 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -284,6 +284,7 @@ private: void update(float deltaTime); // Various helper functions called during update() + void updateLOD(); void updateMouseRay(); void updateFaceshift(); void updateVisage(); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 9d4c7eaab9..e2e0d3895a 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -84,6 +84,7 @@ public: void autoAdjustLOD(float currentFPS); void setVoxelSizeScale(float sizeScale); float getVoxelSizeScale() const { return _voxelSizeScale; } + float getAvatarLODDistanceMultiplier() const { return DEFAULT_OCTREE_SIZE_SCALE / _voxelSizeScale; } void setBoundaryLevelAdjust(int boundaryLevelAdjust); int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; } diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 7ada8b0764..ef6975d223 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -112,7 +112,8 @@ glm::quat Avatar::getWorldAlignedOrientation () const { } float Avatar::getLODDistance() const { - return glm::distance(Application::getInstance()->getCamera()->getPosition(), _position) / _scale; + return Menu::getInstance()->getAvatarLODDistanceMultiplier() * + glm::distance(Application::getInstance()->getCamera()->getPosition(), _position) / _scale; } void Avatar::simulate(float deltaTime) { @@ -305,13 +306,11 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { } void Avatar::renderBody() { - if (_shouldRenderBillboard) { + if (_shouldRenderBillboard || !(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) { + // render the billboard until both models are loaded renderBillboard(); return; } - if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) { - return; // wait until both models are loaded - } _skeletonModel.render(1.0f); getHead()->render(1.0f); getHand()->render(false); diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 170ebc60e5..5b13e2da42 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -450,7 +450,7 @@ class GeometryReader : public QRunnable { public: GeometryReader(const QWeakPointer& geometry, const QUrl& url, - const QByteArray& data, const QVariantHash& mapping); + QNetworkReply* reply, const QVariantHash& mapping); virtual void run(); @@ -458,40 +458,41 @@ private: QWeakPointer _geometry; QUrl _url; - QByteArray _data; + QNetworkReply* _reply; QVariantHash _mapping; }; GeometryReader::GeometryReader(const QWeakPointer& geometry, const QUrl& url, - const QByteArray& data, const QVariantHash& mapping) : + QNetworkReply* reply, const QVariantHash& mapping) : _geometry(geometry), _url(url), - _data(data), + _reply(reply), _mapping(mapping) { } void GeometryReader::run() { QSharedPointer geometry = _geometry.toStrongRef(); if (geometry.isNull()) { + _reply->deleteLater(); return; } try { QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, - _url.path().toLower().endsWith(".svo") ? readSVO(_data) : readFBX(_data, _mapping))); + _url.path().toLower().endsWith(".svo") ? readSVO(_reply->readAll()) : readFBX(_reply->readAll(), _mapping))); } catch (const QString& error) { qDebug() << "Error reading " << _url << ": " << error; QMetaObject::invokeMethod(geometry.data(), "finishedLoading", Q_ARG(bool, false)); } + _reply->deleteLater(); } void NetworkGeometry::downloadFinished(QNetworkReply* reply) { QUrl url = reply->url(); - QByteArray data = reply->readAll(); - if (url.path().toLower().endsWith(".fst")) { // it's a mapping file; parse it and get the mesh filename - _mapping = readMapping(data); + _mapping = readMapping(reply->readAll()); + reply->deleteLater(); QString filename = _mapping.value("filename").toString(); if (filename.isNull()) { qDebug() << "Mapping file " << url << " has no filename."; @@ -525,7 +526,7 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) { } // send the reader off to the thread pool - QThreadPool::globalInstance()->start(new GeometryReader(_self, url, data, _mapping)); + QThreadPool::globalInstance()->start(new GeometryReader(_self, url, reply, _mapping)); } void NetworkGeometry::setGeometry(const FBXGeometry& geometry) { diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index 794516f7b7..dcaf059714 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -273,27 +273,28 @@ NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) : class ImageReader : public QRunnable { public: - ImageReader(const QWeakPointer& texture, const QByteArray& data); + ImageReader(const QWeakPointer& texture, QNetworkReply* reply); virtual void run(); private: QWeakPointer _texture; - QByteArray _data; + QNetworkReply* _reply; }; -ImageReader::ImageReader(const QWeakPointer& texture, const QByteArray& data) : +ImageReader::ImageReader(const QWeakPointer& texture, QNetworkReply* reply) : _texture(texture), - _data(data) { + _reply(reply) { } void ImageReader::run() { QSharedPointer texture = _texture.toStrongRef(); if (texture.isNull()) { + _reply->deleteLater(); return; } - QImage image = QImage::fromData(_data); + QImage image = QImage::fromData(_reply->readAll()); if (image.format() != QImage::Format_ARGB32) { image = image.convertToFormat(QImage::Format_ARGB32); } @@ -320,11 +321,12 @@ void ImageReader::run() { QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image), Q_ARG(const glm::vec4&, accumulated / (imageArea * EIGHT_BIT_MAXIMUM)), Q_ARG(bool, translucentPixels >= imageArea / 2)); + _reply->deleteLater(); } void NetworkTexture::downloadFinished(QNetworkReply* reply) { // send the reader off to the thread pool - QThreadPool::globalInstance()->start(new ImageReader(_self, reply->readAll())); + QThreadPool::globalInstance()->start(new ImageReader(_self, reply)); } void NetworkTexture::setImage(const QImage& image, const glm::vec4& averageColor, bool translucent) { diff --git a/libraries/metavoxels/src/ScriptCache.cpp b/libraries/metavoxels/src/ScriptCache.cpp index fd58f5b6f3..702a36de2a 100644 --- a/libraries/metavoxels/src/ScriptCache.cpp +++ b/libraries/metavoxels/src/ScriptCache.cpp @@ -61,6 +61,7 @@ NetworkProgram::NetworkProgram(ScriptCache* cache, const QUrl& url) : void NetworkProgram::downloadFinished(QNetworkReply* reply) { _program = QScriptProgram(QTextStream(reply).readAll(), reply->url().toString()); + reply->deleteLater(); finishedLoading(true); emit loaded(); } diff --git a/libraries/shared/src/ResourceCache.cpp b/libraries/shared/src/ResourceCache.cpp index 90dc5961dc..bd08b240c5 100644 --- a/libraries/shared/src/ResourceCache.cpp +++ b/libraries/shared/src/ResourceCache.cpp @@ -161,7 +161,6 @@ void Resource::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { return; } _reply->disconnect(this); - _reply->deleteLater(); QNetworkReply* reply = _reply; _reply = NULL; ResourceCache::requestCompleted(); diff --git a/libraries/shared/src/ResourceCache.h b/libraries/shared/src/ResourceCache.h index eae5e752e2..c77318aac2 100644 --- a/libraries/shared/src/ResourceCache.h +++ b/libraries/shared/src/ResourceCache.h @@ -99,6 +99,7 @@ protected slots: protected: + /// Called when the download has finished. The recipient should delete the reply when done with it. virtual void downloadFinished(QNetworkReply* reply) = 0; /// Should be called by subclasses when all the loading that will be done has been done.