From 2639796402486d173eeb31300a26dcc989073305 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 28 Feb 2014 12:22:10 -0800 Subject: [PATCH 1/6] Wait until both head and body are loaded before rendering. Closes #2145. --- interface/src/avatar/Avatar.cpp | 3 +++ interface/src/avatar/MyAvatar.cpp | 4 ++++ interface/src/renderer/Model.h | 2 ++ 3 files changed, 9 insertions(+) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 27788eb354..85671c9e30 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -309,6 +309,9 @@ void Avatar::renderBody() { 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/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 96e2cc2221..02d14b7a2c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -677,6 +677,10 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { } void MyAvatar::renderBody(bool forceRenderHead) { + if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) { + return; // wait until both models are loaded + } + // Render the body's voxels and head _skeletonModel.render(1.0f); diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 52d0622ead..e200960b98 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -46,6 +46,8 @@ public: bool isActive() const { return _geometry && _geometry->isLoaded(); } + bool isRenderable() const { return !_meshStates.isEmpty(); } + bool isLoadedWithTextures() const; void init(); From 21a149348b843596e34d5de532b663fc7230975e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 28 Feb 2014 13:57:25 -0800 Subject: [PATCH 2/6] When switching models, retain old one until new one is loaded. Closes #2146. --- interface/src/avatar/Avatar.cpp | 4 +-- interface/src/renderer/GeometryCache.cpp | 21 +++++++++++-- interface/src/renderer/GeometryCache.h | 4 +-- interface/src/renderer/Model.cpp | 39 +++++++++++++----------- interface/src/renderer/Model.h | 11 +++++-- interface/src/renderer/TextureCache.cpp | 6 ++-- interface/src/renderer/TextureCache.h | 3 -- libraries/metavoxels/src/ScriptCache.cpp | 1 + libraries/metavoxels/src/ScriptCache.h | 2 -- libraries/shared/src/ResourceCache.cpp | 23 ++++++++++++-- libraries/shared/src/ResourceCache.h | 7 +++++ 11 files changed, 83 insertions(+), 38 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 85671c9e30..d537b86854 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -567,13 +567,13 @@ bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float parti void Avatar::setFaceModelURL(const QUrl& faceModelURL) { AvatarData::setFaceModelURL(faceModelURL); const QUrl DEFAULT_FACE_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_head.fst"); - getHead()->getFaceModel().setURL(_faceModelURL, DEFAULT_FACE_MODEL_URL, !isMyAvatar()); + getHead()->getFaceModel().setURL(_faceModelURL, DEFAULT_FACE_MODEL_URL, true, !isMyAvatar()); } void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { AvatarData::setSkeletonModelURL(skeletonModelURL); const QUrl DEFAULT_SKELETON_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_body.fst"); - _skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, !isMyAvatar()); + _skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar()); } void Avatar::setDisplayName(const QString& displayName) { diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index e9719e7a84..cf59f9578a 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -307,6 +307,21 @@ NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointerisLoaded()) || + (part.normalTexture && !part.normalTexture->isLoaded())) { + return false; + } + } + } + return true; +} + QSharedPointer NetworkGeometry::getLODOrFallback(float distance, float& hysteresis, bool delayLoad) const { if (_lodParent.data() != this) { return _lodParent.data()->getLODOrFallback(distance, hysteresis, delayLoad); @@ -438,7 +453,7 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) { QString filename = _mapping.value("filename").toString(); if (filename.isNull()) { qDebug() << "Mapping file " << url << " has no filename."; - _failedToLoad = true; + finishedLoading(false); } else { QString texdir = _mapping.value("texdir").toString(); @@ -471,7 +486,7 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) { } catch (const QString& error) { qDebug() << "Error reading " << url << ": " << error; - _failedToLoad = true; + finishedLoading(false); return; } @@ -567,6 +582,8 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) { _meshes.append(networkMesh); } + + finishedLoading(true); } bool NetworkMeshPart::isTranslucent() const { diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index e5139088d3..2e6500b86a 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -69,8 +69,8 @@ public: NetworkGeometry(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, const QVariantHash& mapping = QVariantHash(), const QUrl& textureBase = QUrl()); - /// Checks whether the geometry is fulled loaded. - bool isLoaded() const { return !_geometry.joints.isEmpty(); } + /// Checks whether the geometry and its textures are loaded. + bool isLoadedWithTextures() const; /// Returns a pointer to the geometry appropriate for the specified distance. /// \param hysteresis a hysteresis parameter that prevents rapid model switching diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 599abf29aa..d87879ac27 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -57,21 +57,6 @@ QVector Model::createJointStates(const FBXGeometry& geometry) return jointStates; } -bool Model::isLoadedWithTextures() const { - if (!isActive()) { - return false; - } - foreach (const NetworkMesh& mesh, _geometry->getMeshes()) { - foreach (const NetworkMeshPart& part, mesh.parts) { - if ((part.diffuseTexture && !part.diffuseTexture->isLoaded()) || - (part.normalTexture && !part.normalTexture->isLoaded())) { - return false; - } - } - } - return true; -} - void Model::init() { if (!_program.isLinked()) { switchToResourcesParentIfRequired(); @@ -119,7 +104,18 @@ void Model::simulate(float deltaTime, bool delayLoad) { // update our LOD QVector newJointStates; if (_geometry) { - QSharedPointer geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis, delayLoad); + QSharedPointer geometry = _geometry; + if (_nextGeometry) { + if (!delayLoad) { + _nextGeometry->setLoadPriority(this, -_lodDistance); + _nextGeometry->ensureLoading(); + } + if (_nextGeometry->isLoaded()) { + geometry = _nextGeometry; + _nextGeometry.clear(); + } + } + geometry = geometry->getLODOrFallback(_lodDistance, _lodHysteresis, delayLoad); if (_geometry != geometry) { if (!_jointStates.isEmpty()) { // copy the existing joint states @@ -447,20 +443,27 @@ float Model::getRightArmLength() const { return getLimbLength(getRightHandJointIndex()); } -void Model::setURL(const QUrl& url, const QUrl& fallback, bool delayLoad) { +void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bool delayLoad) { // don't recreate the geometry if it's the same URL if (_url == url) { return; } _url = url; + // if so instructed, keep the current geometry until the new one is loaded + _nextGeometry = Application::getInstance()->getGeometryCache()->getGeometry(url, fallback, delayLoad); + if (retainCurrent && isActive() && !_nextGeometry->isLoaded()) { + return; + } + // delete our local geometry and custom textures deleteGeometry(); _dilatedTextures.clear(); _lodHysteresis = NetworkGeometry::NO_HYSTERESIS; // we retain a reference to the base geometry so that its reference count doesn't fall to zero - _baseGeometry = _geometry = Application::getInstance()->getGeometryCache()->getGeometry(url, fallback, delayLoad); + _baseGeometry = _geometry = _nextGeometry; + _nextGeometry.reset(); } glm::vec4 Model::computeAverageColor() const { diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index e200960b98..25bd1f08f5 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -48,14 +48,20 @@ public: bool isRenderable() const { return !_meshStates.isEmpty(); } - bool isLoadedWithTextures() const; + bool isLoadedWithTextures() const { return _geometry && _geometry->isLoadedWithTextures(); } void init(); void reset(); void simulate(float deltaTime, bool delayLoad = false); bool render(float alpha); - Q_INVOKABLE void setURL(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false); + /// Sets the URL of the model to render. + /// \param fallback the URL of a fallback model to render if the requested model fails to load + /// \param retainCurrent if true, keep rendering the current model until the new one is loaded + /// \param delayLoad if true, don't load the model immediately; wait until actually requested + Q_INVOKABLE void setURL(const QUrl& url, const QUrl& fallback = QUrl(), + bool retainCurrent = false, bool delayLoad = false); + const QUrl& getURL() const { return _url; } /// Sets the distance parameter used for LOD computations. @@ -235,6 +241,7 @@ private: void renderMeshes(float alpha, bool translucent); QSharedPointer _baseGeometry; ///< reference required to prevent collection of base + QSharedPointer _nextGeometry; float _lodDistance; float _lodHysteresis; diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index 0a6a3fe6f8..2b14ff8c18 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -254,8 +254,7 @@ Texture::~Texture() { NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) : Resource(url), _averageColor(1.0f, 1.0f, 1.0f, 1.0f), - _translucent(false), - _loaded(false) { + _translucent(false) { if (!url.isValid()) { _loaded = true; @@ -269,8 +268,6 @@ NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) : } void NetworkTexture::downloadFinished(QNetworkReply* reply) { - _loaded = true; - QImage image = QImage::fromData(reply->readAll()); if (image.format() != QImage::Format_ARGB32) { image = image.convertToFormat(QImage::Format_ARGB32); @@ -298,6 +295,7 @@ void NetworkTexture::downloadFinished(QNetworkReply* reply) { _averageColor = accumulated / (imageArea * EIGHT_BIT_MAXIMUM); _translucent = (translucentPixels >= imageArea / 2); + finishedLoading(true); imageLoaded(image); glBindTexture(GL_TEXTURE_2D, getID()); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1, diff --git a/interface/src/renderer/TextureCache.h b/interface/src/renderer/TextureCache.h index 2c8f4d7cf9..33b2834a9d 100644 --- a/interface/src/renderer/TextureCache.h +++ b/interface/src/renderer/TextureCache.h @@ -117,8 +117,6 @@ public: NetworkTexture(const QUrl& url, bool normalMap); - bool isLoaded() const { return _loaded; } - /// Returns the average color over the entire texture. const glm::vec4& getAverageColor() const { return _averageColor; } @@ -135,7 +133,6 @@ private: glm::vec4 _averageColor; bool _translucent; - bool _loaded; }; /// Caches derived, dilated textures. diff --git a/libraries/metavoxels/src/ScriptCache.cpp b/libraries/metavoxels/src/ScriptCache.cpp index da29186541..fd58f5b6f3 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()); + finishedLoading(true); emit loaded(); } diff --git a/libraries/metavoxels/src/ScriptCache.h b/libraries/metavoxels/src/ScriptCache.h index 072020d882..ac53e602f8 100644 --- a/libraries/metavoxels/src/ScriptCache.h +++ b/libraries/metavoxels/src/ScriptCache.h @@ -72,8 +72,6 @@ public: ScriptCache* getCache() const { return _cache; } - bool isLoaded() const { return !_program.isNull(); } - const QScriptProgram& getProgram() const { return _program; } signals: diff --git a/libraries/shared/src/ResourceCache.cpp b/libraries/shared/src/ResourceCache.cpp index 24350794bc..86aca599ae 100644 --- a/libraries/shared/src/ResourceCache.cpp +++ b/libraries/shared/src/ResourceCache.cpp @@ -77,6 +77,7 @@ Resource::Resource(const QUrl& url, bool delayLoad) : _request(url), _startedLoading(false), _failedToLoad(false), + _loaded(false), _attempts(0), _reply(NULL) { @@ -106,10 +107,15 @@ void Resource::ensureLoading() { } void Resource::setLoadPriority(const QPointer& owner, float priority) { - _loadPriorities.insert(owner, priority); + if (!(_failedToLoad || _loaded)) { + _loadPriorities.insert(owner, priority); + } } void Resource::setLoadPriorities(const QHash, float>& priorities) { + if (_failedToLoad || _loaded) { + return; + } for (QHash, float>::const_iterator it = priorities.constBegin(); it != priorities.constEnd(); it++) { _loadPriorities.insert(it.key(), it.value()); @@ -117,7 +123,9 @@ void Resource::setLoadPriorities(const QHash, float>& prioriti } void Resource::clearLoadPriority(const QPointer& owner) { - _loadPriorities.remove(owner); + if (!(_failedToLoad || _loaded)) { + _loadPriorities.remove(owner); + } } float Resource::getLoadPriority() { @@ -138,6 +146,15 @@ void Resource::attemptRequest() { ResourceCache::attemptRequest(this); } +void Resource::finishedLoading(bool success) { + if (success) { + _loaded = true; + } else { + _failedToLoad = true; + } + _loadPriorities.clear(); +} + void Resource::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { if (!_reply->isFinished()) { return; @@ -182,7 +199,7 @@ void Resource::handleReplyError() { // fall through to final failure } default: - _failedToLoad = true; + finishedLoading(false); break; } } diff --git a/libraries/shared/src/ResourceCache.h b/libraries/shared/src/ResourceCache.h index ac8ef68a05..1c3f94ab7d 100644 --- a/libraries/shared/src/ResourceCache.h +++ b/libraries/shared/src/ResourceCache.h @@ -88,6 +88,9 @@ public: /// Returns the highest load priority across all owners. float getLoadPriority(); + /// Checks whether the resource has loaded. + bool isLoaded() const { return _loaded; } + protected slots: void attemptRequest(); @@ -96,9 +99,13 @@ protected: virtual void downloadFinished(QNetworkReply* reply) = 0; + /// Should be called by subclasses when all the loading that will be done has been done. + void finishedLoading(bool success); + QNetworkRequest _request; bool _startedLoading; bool _failedToLoad; + bool _loaded; QHash, float> _loadPriorities; private slots: From 0bb3752121966fe40b455aa6b20421e986ce7e87 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 28 Feb 2014 14:29:18 -0800 Subject: [PATCH 3/6] Fixed seg fault, don't attempt to transfer joints between different models (as opposed to different LODs). --- interface/src/renderer/Model.cpp | 108 +++++++++++++++++-------------- interface/src/renderer/Model.h | 3 + 2 files changed, 62 insertions(+), 49 deletions(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index d87879ac27..acb7b50ec9 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -102,43 +102,7 @@ void Model::reset() { void Model::simulate(float deltaTime, bool delayLoad) { // update our LOD - QVector newJointStates; - if (_geometry) { - QSharedPointer geometry = _geometry; - if (_nextGeometry) { - if (!delayLoad) { - _nextGeometry->setLoadPriority(this, -_lodDistance); - _nextGeometry->ensureLoading(); - } - if (_nextGeometry->isLoaded()) { - geometry = _nextGeometry; - _nextGeometry.clear(); - } - } - geometry = geometry->getLODOrFallback(_lodDistance, _lodHysteresis, delayLoad); - if (_geometry != geometry) { - if (!_jointStates.isEmpty()) { - // copy the existing joint states - const FBXGeometry& oldGeometry = _geometry->getFBXGeometry(); - const FBXGeometry& newGeometry = geometry->getFBXGeometry(); - newJointStates = createJointStates(newGeometry); - for (QHash::const_iterator it = oldGeometry.jointIndices.constBegin(); - it != oldGeometry.jointIndices.constEnd(); it++) { - int newIndex = newGeometry.jointIndices.value(it.key()); - if (newIndex != 0) { - newJointStates[newIndex - 1] = _jointStates.at(it.value() - 1); - } - } - } - deleteGeometry(); - _dilatedTextures.clear(); - _geometry = geometry; - } - if (!delayLoad) { - _geometry->setLoadPriority(this, -_lodDistance); - _geometry->ensureLoading(); - } - } + QVector newJointStates = updateGeometry(delayLoad); if (!isActive()) { return; } @@ -451,19 +415,10 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo _url = url; // if so instructed, keep the current geometry until the new one is loaded - _nextGeometry = Application::getInstance()->getGeometryCache()->getGeometry(url, fallback, delayLoad); - if (retainCurrent && isActive() && !_nextGeometry->isLoaded()) { - return; + _nextBaseGeometry = _nextGeometry = Application::getInstance()->getGeometryCache()->getGeometry(url, fallback, delayLoad); + if (!retainCurrent || !isActive() || _nextGeometry->isLoaded()) { + applyNextGeometry(); } - - // delete our local geometry and custom textures - deleteGeometry(); - _dilatedTextures.clear(); - _lodHysteresis = NetworkGeometry::NO_HYSTERESIS; - - // we retain a reference to the base geometry so that its reference count doesn't fall to zero - _baseGeometry = _geometry = _nextGeometry; - _nextGeometry.reset(); } glm::vec4 Model::computeAverageColor() const { @@ -826,6 +781,61 @@ void Model::applyCollision(CollisionInfo& collision) { } } +QVector Model::updateGeometry(bool delayLoad) { + QVector newJointStates; + if (_nextGeometry) { + _nextGeometry = _nextGeometry->getLODOrFallback(_lodDistance, _lodHysteresis, delayLoad); + if (!delayLoad) { + _nextGeometry->setLoadPriority(this, -_lodDistance); + _nextGeometry->ensureLoading(); + } + if (_nextGeometry->isLoaded()) { + applyNextGeometry(); + return newJointStates; + } + } + if (!_geometry) { + return newJointStates; + } + QSharedPointer geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis, delayLoad); + if (_geometry != geometry) { + if (!_jointStates.isEmpty()) { + // copy the existing joint states + const FBXGeometry& oldGeometry = _geometry->getFBXGeometry(); + const FBXGeometry& newGeometry = geometry->getFBXGeometry(); + newJointStates = createJointStates(newGeometry); + for (QHash::const_iterator it = oldGeometry.jointIndices.constBegin(); + it != oldGeometry.jointIndices.constEnd(); it++) { + int newIndex = newGeometry.jointIndices.value(it.key()); + if (newIndex != 0) { + newJointStates[newIndex - 1] = _jointStates.at(it.value() - 1); + } + } + } + deleteGeometry(); + _dilatedTextures.clear(); + _geometry = geometry; + } + if (!delayLoad) { + _geometry->setLoadPriority(this, -_lodDistance); + _geometry->ensureLoading(); + } + return newJointStates; +} + +void Model::applyNextGeometry() { + // delete our local geometry and custom textures + deleteGeometry(); + _dilatedTextures.clear(); + _lodHysteresis = NetworkGeometry::NO_HYSTERESIS; + + // we retain a reference to the base geometry so that its reference count doesn't fall to zero + _baseGeometry = _nextBaseGeometry; + _geometry = _nextGeometry; + _nextBaseGeometry.reset(); + _nextGeometry.reset(); +} + void Model::deleteGeometry() { foreach (Model* attachment, _attachments) { delete attachment; diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 25bd1f08f5..524bd9dc79 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -237,10 +237,13 @@ protected: private: + QVector updateGeometry(bool delayLoad); + void applyNextGeometry(); void deleteGeometry(); void renderMeshes(float alpha, bool translucent); QSharedPointer _baseGeometry; ///< reference required to prevent collection of base + QSharedPointer _nextBaseGeometry; QSharedPointer _nextGeometry; float _lodDistance; float _lodHysteresis; From 2f27d8ecd39fa69adb1ed61835c4684a1be43670 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 28 Feb 2014 16:14:02 -0800 Subject: [PATCH 4/6] Process geometry/textures in worker threads (via QThreadPool). Closes #2137. --- interface/src/renderer/FBXReader.cpp | 2 + interface/src/renderer/FBXReader.h | 3 ++ interface/src/renderer/GeometryCache.cpp | 57 +++++++++++++++++--- interface/src/renderer/GeometryCache.h | 2 + interface/src/renderer/TextureCache.cpp | 44 +++++++++++++-- interface/src/renderer/TextureCache.h | 2 + libraries/shared/src/RegisteredMetaTypes.cpp | 24 +++++++++ libraries/shared/src/RegisteredMetaTypes.h | 4 ++ libraries/shared/src/ResourceCache.cpp | 1 + libraries/shared/src/ResourceCache.h | 5 +- 10 files changed, 131 insertions(+), 13 deletions(-) diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index c9201f8f51..35d1dee9b0 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -28,6 +28,8 @@ using namespace std; +static int fbxGeometryMetaTypeId = qRegisterMetaType(); + template QVariant readBinaryArray(QDataStream& in) { quint32 arrayLength; quint32 encoding; diff --git a/interface/src/renderer/FBXReader.h b/interface/src/renderer/FBXReader.h index b89d0954b4..95622a2a8a 100644 --- a/interface/src/renderer/FBXReader.h +++ b/interface/src/renderer/FBXReader.h @@ -9,6 +9,7 @@ #ifndef __interface__FBXReader__ #define __interface__FBXReader__ +#include #include #include #include @@ -167,6 +168,8 @@ public: QVector attachments; }; +Q_DECLARE_METATYPE(FBXGeometry) + /// Reads an FST mapping from the supplied data. QVariantHash readMapping(const QByteArray& data); diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index cf59f9578a..0097c5ca20 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -8,6 +8,8 @@ #include #include +#include +#include #include "Application.h" #include "GeometryCache.h" @@ -443,6 +445,46 @@ void NetworkGeometry::clearLoadPriority(const QPointer& owner) { } } +/// Reads geometry in a worker thread. +class GeometryReader : public QRunnable { +public: + + GeometryReader(const QWeakPointer& geometry, const QUrl& url, + const QByteArray& data, const QVariantHash& mapping); + + virtual void run(); + +private: + + QWeakPointer _geometry; + QUrl _url; + QByteArray _data; + QVariantHash _mapping; +}; + +GeometryReader::GeometryReader(const QWeakPointer& geometry, const QUrl& url, + const QByteArray& data, const QVariantHash& mapping) : + _geometry(geometry), + _url(url), + _data(data), + _mapping(mapping) { +} + +void GeometryReader::run() { + QSharedPointer geometry = _geometry.toStrongRef(); + if (geometry.isNull()) { + return; + } + try { + QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, + _url.path().toLower().endsWith(".svo") ? readSVO(_data) : readFBX(_data, _mapping))); + + } catch (const QString& error) { + qDebug() << "Error reading " << _url << ": " << error; + QMetaObject::invokeMethod(geometry.data(), "finishedLoading", Q_ARG(bool, false)); + } +} + void NetworkGeometry::downloadFinished(QNetworkReply* reply) { QUrl url = reply->url(); QByteArray data = reply->readAll(); @@ -467,6 +509,7 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) { for (QVariantHash::const_iterator it = lods.begin(); it != lods.end(); it++) { QSharedPointer geometry(new NetworkGeometry(url.resolved(it.key()), QSharedPointer(), true, _mapping, _textureBase)); + geometry->setSelf(geometry.staticCast()); geometry->setLODParent(_lodParent); _lods.insert(it.value().toFloat(), geometry); } @@ -481,14 +524,12 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) { return; } - try { - _geometry = url.path().toLower().endsWith(".svo") ? readSVO(data) : readFBX(data, _mapping); - - } catch (const QString& error) { - qDebug() << "Error reading " << url << ": " << error; - finishedLoading(false); - return; - } + // send the reader off to the thread pool + QThreadPool::globalInstance()->start(new GeometryReader(_self, url, data, _mapping)); +} + +void NetworkGeometry::setGeometry(const FBXGeometry& geometry) { + _geometry = geometry; foreach (const FBXMesh& mesh, _geometry.meshes) { NetworkMesh networkMesh = { QOpenGLBuffer(QOpenGLBuffer::IndexBuffer), QOpenGLBuffer(QOpenGLBuffer::VertexBuffer) }; diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index 2e6500b86a..8c59ee7dc5 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -90,6 +90,8 @@ protected: virtual void downloadFinished(QNetworkReply* reply); + Q_INVOKABLE void setGeometry(const FBXGeometry& geometry); + private: friend class GeometryCache; diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index 2b14ff8c18..22569f925a 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include @@ -129,6 +131,7 @@ QSharedPointer TextureCache::getTexture(const QUrl& url, bool no QSharedPointer texture = _dilatableNetworkTextures.value(url); if (texture.isNull()) { texture = QSharedPointer(new DilatableNetworkTexture(url)); + texture->setSelf(texture); _dilatableNetworkTextures.insert(url, texture); } return texture; @@ -267,8 +270,30 @@ NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) : glBindTexture(GL_TEXTURE_2D, 0); } -void NetworkTexture::downloadFinished(QNetworkReply* reply) { - QImage image = QImage::fromData(reply->readAll()); +class ImageReader : public QRunnable { +public: + + ImageReader(const QWeakPointer& texture, const QByteArray& data); + + virtual void run(); + +private: + + QWeakPointer _texture; + QByteArray _data; +}; + +ImageReader::ImageReader(const QWeakPointer& texture, const QByteArray& data) : + _texture(texture), + _data(data) { +} + +void ImageReader::run() { + QSharedPointer texture = _texture.toStrongRef(); + if (texture.isNull()) { + return; + } + QImage image = QImage::fromData(_data); if (image.format() != QImage::Format_ARGB32) { image = image.convertToFormat(QImage::Format_ARGB32); } @@ -292,8 +317,19 @@ void NetworkTexture::downloadFinished(QNetworkReply* reply) { } } int imageArea = image.width() * image.height(); - _averageColor = accumulated / (imageArea * EIGHT_BIT_MAXIMUM); - _translucent = (translucentPixels >= imageArea / 2); + 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)); +} + +void NetworkTexture::downloadFinished(QNetworkReply* reply) { + // send the reader off to the thread pool + QThreadPool::globalInstance()->start(new ImageReader(_self, reply->readAll())); +} + +void NetworkTexture::setImage(const QImage& image, const glm::vec4& averageColor, bool translucent) { + _averageColor = averageColor; + _translucent = translucent; finishedLoading(true); imageLoaded(image); diff --git a/interface/src/renderer/TextureCache.h b/interface/src/renderer/TextureCache.h index 33b2834a9d..dba34a0d0e 100644 --- a/interface/src/renderer/TextureCache.h +++ b/interface/src/renderer/TextureCache.h @@ -129,6 +129,8 @@ protected: virtual void downloadFinished(QNetworkReply* reply); virtual void imageLoaded(const QImage& image); + Q_INVOKABLE void setImage(const QImage& image, const glm::vec4& averageColor, bool translucent); + private: glm::vec4 _averageColor; diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 0358f169b8..d106977ae0 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -10,7 +10,15 @@ #include "RegisteredMetaTypes.h" +static int vec4MetaTypeId = qRegisterMetaType(); +static int vec3MetaTypeId = qRegisterMetaType(); +static int vec2MetaTypeId = qRegisterMetaType(); +static int quatMetaTypeId = qRegisterMetaType(); +static int xColorMetaTypeId = qRegisterMetaType(); +static int pickRayMetaTypeId = qRegisterMetaType(); + void registerMetaTypes(QScriptEngine* engine) { + qScriptRegisterMetaType(engine, vec4toScriptValue, vec4FromScriptValue); qScriptRegisterMetaType(engine, vec3toScriptValue, vec3FromScriptValue); qScriptRegisterMetaType(engine, vec2toScriptValue, vec2FromScriptValue); qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue); @@ -18,6 +26,22 @@ void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, pickRayToScriptValue, pickRayFromScriptValue); } +QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4) { + QScriptValue obj = engine->newObject(); + obj.setProperty("x", vec4.x); + obj.setProperty("y", vec4.y); + obj.setProperty("z", vec4.z); + obj.setProperty("w", vec4.w); + return obj; +} + +void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4) { + vec4.x = object.property("x").toVariant().toFloat(); + vec4.y = object.property("y").toVariant().toFloat(); + vec4.z = object.property("z").toVariant().toFloat(); + vec4.w = object.property("w").toVariant().toFloat(); +} + QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3) { QScriptValue obj = engine->newObject(); obj.setProperty("x", vec3.x); diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 130a48cdeb..ea65e45c95 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -17,6 +17,7 @@ #include "SharedUtil.h" +Q_DECLARE_METATYPE(glm::vec4) Q_DECLARE_METATYPE(glm::vec3) Q_DECLARE_METATYPE(glm::vec2) Q_DECLARE_METATYPE(glm::quat) @@ -24,6 +25,9 @@ Q_DECLARE_METATYPE(xColor) void registerMetaTypes(QScriptEngine* engine); +QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4); +void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4); + QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3); void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3); diff --git a/libraries/shared/src/ResourceCache.cpp b/libraries/shared/src/ResourceCache.cpp index 86aca599ae..8dfdf1abb8 100644 --- a/libraries/shared/src/ResourceCache.cpp +++ b/libraries/shared/src/ResourceCache.cpp @@ -27,6 +27,7 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& if (resource.isNull()) { resource = createResource(url, fallback.isValid() ? getResource(fallback, QUrl(), true) : QSharedPointer(), delayLoad, extra); + resource->setSelf(resource); _resources.insert(url, resource); } return resource; diff --git a/libraries/shared/src/ResourceCache.h b/libraries/shared/src/ResourceCache.h index 1c3f94ab7d..eae5e752e2 100644 --- a/libraries/shared/src/ResourceCache.h +++ b/libraries/shared/src/ResourceCache.h @@ -91,6 +91,8 @@ public: /// Checks whether the resource has loaded. bool isLoaded() const { return _loaded; } + void setSelf(const QWeakPointer& self) { _self = self; } + protected slots: void attemptRequest(); @@ -100,13 +102,14 @@ protected: virtual void downloadFinished(QNetworkReply* reply) = 0; /// Should be called by subclasses when all the loading that will be done has been done. - void finishedLoading(bool success); + Q_INVOKABLE void finishedLoading(bool success); QNetworkRequest _request; bool _startedLoading; bool _failedToLoad; bool _loaded; QHash, float> _loadPriorities; + QWeakPointer _self; private slots: From 380b0536b37503d1b0ee1e3528532300765f07bb Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 28 Feb 2014 16:18:44 -0800 Subject: [PATCH 5/6] Indentation fix. --- interface/src/renderer/GeometryCache.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 0097c5ca20..170ebc60e5 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -456,10 +456,10 @@ public: private: - QWeakPointer _geometry; - QUrl _url; - QByteArray _data; - QVariantHash _mapping; + QWeakPointer _geometry; + QUrl _url; + QByteArray _data; + QVariantHash _mapping; }; GeometryReader::GeometryReader(const QWeakPointer& geometry, const QUrl& url, From 094159eefd6266bc01e8ec466e2cb45d13b5a429 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 28 Feb 2014 16:47:54 -0800 Subject: [PATCH 6/6] make Agent parent of ScriptEngine so they stay on same thread --- assignment-client/src/Agent.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index b61410f22b..13b1f2bdcf 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -27,6 +27,9 @@ Agent::Agent(const QByteArray& packet) : _voxelEditSender(), _particleEditSender() { + // be the parent of the script engine so it gets moved when we do + _scriptEngine.setParent(this); + _scriptEngine.getVoxelsScriptingInterface()->setPacketSender(&_voxelEditSender); _scriptEngine.getParticlesScriptingInterface()->setPacketSender(&_particleEditSender); }