diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0f7155d4e1..d743c2a8c8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1617,6 +1617,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); @@ -1857,6 +1864,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(); @@ -2332,14 +2341,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/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 0847590720..05760ef675 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -39,12 +39,13 @@ float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeSc } Octree::Octree(bool shouldReaverage) : + _rootNode(NULL), _isDirty(true), _shouldReaverage(shouldReaverage), _stopImport(false), - _lock(QReadWriteLock::Recursive) { - _rootNode = NULL; - _isViewing = false; + _lock(), + _isViewing(false) +{ } Octree::~Octree() { @@ -552,10 +553,7 @@ OctreeElement* Octree::getOctreeElementAt(float x, float y, float z, float s) co OctreeElement* Octree::getOrCreateChildElementAt(float x, float y, float z, float s) { - lockForWrite(); - OctreeElement* result = getRoot()->getOrCreateChildElementAt(x, y, z, s); - unlock(); - return result; + return getRoot()->getOrCreateChildElementAt(x, y, z, s); } diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index b493ec6bc7..d9f0beb81a 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -385,7 +385,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr } else { // look up the existing particle - const Particle* existingParticle = tree->findParticleByID(editID); + const Particle* existingParticle = tree->findParticleByID(editID, true); // copy existing properties before over-writing with new properties if (existingParticle) { diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp index 30270b8fcf..a254305789 100644 --- a/libraries/particles/src/ParticleTree.cpp +++ b/libraries/particles/src/ParticleTree.cpp @@ -370,12 +370,16 @@ bool ParticleTree::findByIDOperation(OctreeElement* element, void* extraData) { } -const Particle* ParticleTree::findParticleByID(uint32_t id) { +const Particle* ParticleTree::findParticleByID(uint32_t id, bool alreadyLocked) { FindByIDArgs args = { id, false, NULL }; - lockForRead(); + if (!alreadyLocked) { + lockForRead(); + } recurseTreeWithOperation(findByIDOperation, &args); - unlock(); + if (!alreadyLocked) { + unlock(); + } return args.foundParticle; } @@ -455,6 +459,7 @@ bool ParticleTree::pruneOperation(OctreeElement* element, void* extraData) { } void ParticleTree::update() { + lockForWrite(); _isDirty = true; ParticleTreeUpdateArgs args = { }; @@ -469,9 +474,7 @@ void ParticleTree::update() { AABox treeBounds = getRoot()->getAABox(); if (!shouldDie && treeBounds.contains(args._movingParticles[i].getPosition())) { - lockForWrite(); storeParticle(args._movingParticles[i]); - unlock(); } else { uint32_t particleID = args._movingParticles[i].getID(); quint64 deletedAt = usecTimestampNow(); @@ -482,7 +485,6 @@ void ParticleTree::update() { } // prune the tree... - lockForWrite(); recurseTreeWithOperation(pruneOperation, NULL); unlock(); } diff --git a/libraries/particles/src/ParticleTree.h b/libraries/particles/src/ParticleTree.h index 08e8e51a05..f3b8f5183d 100644 --- a/libraries/particles/src/ParticleTree.h +++ b/libraries/particles/src/ParticleTree.h @@ -44,7 +44,7 @@ public: void addParticle(const ParticleID& particleID, const ParticleProperties& properties); void deleteParticle(const ParticleID& particleID); const Particle* findClosestParticle(glm::vec3 position, float targetRadius); - const Particle* findParticleByID(uint32_t id); + const Particle* findParticleByID(uint32_t id, bool alreadyLocked = false); /// finds all particles that touch a sphere /// \param center the center of the sphere diff --git a/libraries/particles/src/ParticlesScriptingInterface.cpp b/libraries/particles/src/ParticlesScriptingInterface.cpp index c2376e8d72..a25dde1b9e 100644 --- a/libraries/particles/src/ParticlesScriptingInterface.cpp +++ b/libraries/particles/src/ParticlesScriptingInterface.cpp @@ -33,7 +33,9 @@ ParticleID ParticlesScriptingInterface::addParticle(const ParticleProperties& pr // If we have a local particle tree set, then also update it. if (_particleTree) { + _particleTree->lockForWrite(); _particleTree->addParticle(id, properties); + _particleTree->unlock(); } return id; @@ -64,7 +66,7 @@ ParticleProperties ParticlesScriptingInterface::getParticleProperties(ParticleID } if (_particleTree) { _particleTree->lockForRead(); - const Particle* particle = _particleTree->findParticleByID(identity.id); + const Particle* particle = _particleTree->findParticleByID(identity.id, true); if (particle) { results.copyFromParticle(*particle); } else { 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.