diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 91a4fc2ff9..cb950e244a 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -611,7 +612,7 @@ void RenderableModelEntityItem::setShapeType(ShapeType type) { ModelEntityItem::setShapeType(type); if (getShapeType() == SHAPE_TYPE_COMPOUND) { if (!_compoundShapeResource && !getCompoundShapeURL().isEmpty()) { - _compoundShapeResource = DependencyManager::get()->getGeometryResource(getCompoundShapeURL()); + _compoundShapeResource = DependencyManager::get()->getCollisionGeometryResource(getCompoundShapeURL()); } } else if (_compoundShapeResource && !getCompoundShapeURL().isEmpty()) { // the compoundURL has been set but the shapeType does not agree @@ -620,8 +621,16 @@ void RenderableModelEntityItem::setShapeType(ShapeType type) { } void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) { + // because the caching system only allows one Geometry per url, and because this url might also be used + // as a visual model, we need to change this url in some way. We add a "collision-hull" query-arg so it + // will end up in a different hash-key in ResourceCache. TODO: It would be better to use the same URL and + // parse it twice. auto currentCompoundShapeURL = getCompoundShapeURL(); - ModelEntityItem::setCompoundShapeURL(url); + QUrl hullURL(url); + QUrlQuery queryArgs(hullURL); + queryArgs.addQueryItem("collision-hull", ""); + hullURL.setQuery(queryArgs); + ModelEntityItem::setCompoundShapeURL(hullURL.toString()); if (getCompoundShapeURL() != currentCompoundShapeURL || !_model) { EntityTreePointer tree = getTree(); @@ -629,7 +638,7 @@ void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) { QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID())); } if (getShapeType() == SHAPE_TYPE_COMPOUND) { - _compoundShapeResource = DependencyManager::get()->getGeometryResource(url); + _compoundShapeResource = DependencyManager::get()->getCollisionGeometryResource(hullURL); } } } @@ -661,7 +670,8 @@ bool RenderableModelEntityItem::isReadyToComputeShape() { } return true; } else if (!getCompoundShapeURL().isEmpty()) { - _compoundShapeResource = DependencyManager::get()->getGeometryResource(getCompoundShapeURL()); + _compoundShapeResource = + DependencyManager::get()->getCollisionGeometryResource(getCompoundShapeURL()); } } diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index c99b847722..7b46556530 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -302,7 +302,8 @@ QNetworkReply* OBJReader::request(QUrl& url, bool isTest) { } -bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess) { +bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, + float& scaleGuess, bool combineParts) { FaceGroup faces; FBXMesh& mesh = geometry.meshes[0]; mesh.parts.append(FBXMeshPart()); @@ -348,6 +349,9 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi } QByteArray groupName = tokenizer.getDatum(); currentGroup = groupName; + if (!combineParts) { + currentMaterialName = QString("part-") + QString::number(_partCounter++); + } } else if (token == "mtllib" && !_url.isEmpty()) { if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; @@ -361,7 +365,9 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi } QString nextName = tokenizer.getDatum(); if (nextName != currentMaterialName) { - currentMaterialName = nextName; + if (combineParts) { + currentMaterialName = nextName; + } #ifdef WANT_DEBUG qCDebug(modelformat) << "OBJ Reader new current material:" << currentMaterialName; #endif @@ -419,7 +425,7 @@ done: } -FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, const QUrl& url) { +FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url) { PROFILE_RANGE_EX(resource_parse, __FUNCTION__, 0xffff0000, nullptr); QBuffer buffer { &model }; buffer.open(QIODevice::ReadOnly); @@ -438,7 +444,7 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, try { // call parseOBJGroup as long as it's returning true. Each successful call will // add a new meshPart to the geometry's single mesh. - while (parseOBJGroup(tokenizer, mapping, geometry, scaleGuess)) {} + while (parseOBJGroup(tokenizer, mapping, geometry, scaleGuess, combineParts)) {} FBXMesh& mesh = geometry.meshes[0]; mesh.meshIndex = 0; diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index b4a48c570e..4be5705f9a 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -22,7 +22,7 @@ public: glm::vec3 getVec3(); glm::vec2 getVec2(); float getFloat() { return std::stof((nextToken() != OBJTokenizer::DATUM_TOKEN) ? nullptr : getDatum().data()); } - + private: QIODevice* _device; QByteArray _datum; @@ -73,15 +73,18 @@ public: QHash materials; QNetworkReply* request(QUrl& url, bool isTest); - FBXGeometry* readOBJ(QByteArray& model, const QVariantHash& mapping, const QUrl& url = QUrl()); - + FBXGeometry* readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl()); + private: QUrl _url; QHash librariesSeen; - bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess); + bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, + float& scaleGuess, bool combineParts); void parseMaterialLibrary(QIODevice* device); bool isValidTexture(const QByteArray &filename); // true if the file exists. TODO?: check content-type header and that it is a supported format. + + int _partCounter { 0 }; }; // What are these utilities doing here? One is used by fbx loading code in VHACD Utils, and the other a general debugging utility. diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 142ea74af4..d9c5294be2 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -32,6 +32,7 @@ class GeometryExtra { public: const QVariantHash& mapping; const QUrl& textureBaseUrl; + bool combineParts; }; QUrl resolveTextureBaseUrl(const QUrl& url, const QUrl& textureBaseUrl) { @@ -89,7 +90,7 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) { } auto modelCache = DependencyManager::get(); - GeometryExtra extra{ mapping, _textureBaseUrl }; + GeometryExtra extra{ mapping, _textureBaseUrl, false }; // Get the raw GeometryResource _geometryResource = modelCache->getResource(url, QUrl(), &extra).staticCast(); @@ -129,8 +130,8 @@ void GeometryMappingResource::onGeometryMappingLoaded(bool success) { class GeometryReader : public QRunnable { public: GeometryReader(QWeakPointer& resource, const QUrl& url, const QVariantHash& mapping, - const QByteArray& data) : - _resource(resource), _url(url), _mapping(mapping), _data(data) { + const QByteArray& data, bool combineParts) : + _resource(resource), _url(url), _mapping(mapping), _data(data), _combineParts(combineParts) { DependencyManager::get()->incrementStat("PendingProcessing"); } @@ -142,6 +143,7 @@ private: QUrl _url; QVariantHash _mapping; QByteArray _data; + bool _combineParts; }; void GeometryReader::run() { @@ -178,7 +180,7 @@ void GeometryReader::run() { throw QString("empty geometry, possibly due to an unsupported FBX version"); } } else if (_url.path().toLower().endsWith(".obj")) { - fbxGeometry.reset(OBJReader().readOBJ(_data, _mapping, _url)); + fbxGeometry.reset(OBJReader().readOBJ(_data, _mapping, _combineParts, _url)); } else { throw QString("unsupported format"); } @@ -209,8 +211,8 @@ void GeometryReader::run() { class GeometryDefinitionResource : public GeometryResource { Q_OBJECT public: - GeometryDefinitionResource(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) : - GeometryResource(url, resolveTextureBaseUrl(url, textureBaseUrl)), _mapping(mapping) {} + GeometryDefinitionResource(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl, bool combineParts) : + GeometryResource(url, resolveTextureBaseUrl(url, textureBaseUrl)), _mapping(mapping), _combineParts(combineParts) {} QString getType() const override { return "GeometryDefinition"; } @@ -221,10 +223,11 @@ protected: private: QVariantHash _mapping; + bool _combineParts; }; void GeometryDefinitionResource::downloadFinished(const QByteArray& data) { - QThreadPool::globalInstance()->start(new GeometryReader(_self, _url, _mapping, data)); + QThreadPool::globalInstance()->start(new GeometryReader(_self, _url, _mapping, data, _combineParts)); } void GeometryDefinitionResource::setGeometryDefinition(FBXGeometry::Pointer fbxGeometry) { @@ -265,7 +268,7 @@ ModelCache::ModelCache() { } QSharedPointer ModelCache::createResource(const QUrl& url, const QSharedPointer& fallback, - const void* extra) { + const void* extra) { Resource* resource = nullptr; if (url.path().toLower().endsWith(".fst")) { resource = new GeometryMappingResource(url); @@ -273,14 +276,30 @@ QSharedPointer ModelCache::createResource(const QUrl& url, const QShar const GeometryExtra* geometryExtra = static_cast(extra); auto mapping = geometryExtra ? geometryExtra->mapping : QVariantHash(); auto textureBaseUrl = geometryExtra ? geometryExtra->textureBaseUrl : QUrl(); - resource = new GeometryDefinitionResource(url, mapping, textureBaseUrl); + bool combineParts = geometryExtra ? geometryExtra->combineParts : true; + resource = new GeometryDefinitionResource(url, mapping, textureBaseUrl, combineParts); } return QSharedPointer(resource, &Resource::deleter); } -GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) { - GeometryExtra geometryExtra = { mapping, textureBaseUrl }; +GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url, + const QVariantHash& mapping, const QUrl& textureBaseUrl) { + bool combineParts = true; + GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts }; + GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra).staticCast(); + if (resource) { + if (resource->isLoaded() && resource->shouldSetTextures()) { + resource->setTextures(); + } + } + return resource; +} + +GeometryResource::Pointer ModelCache::getCollisionGeometryResource(const QUrl& url, + const QVariantHash& mapping, const QUrl& textureBaseUrl) { + bool combineParts = false; + GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts }; GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra).staticCast(); if (resource) { if (resource->isLoaded() && resource->shouldSetTextures()) { diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 2cd96a84c7..967897477d 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -136,13 +136,18 @@ class ModelCache : public ResourceCache, public Dependency { public: GeometryResource::Pointer getGeometryResource(const QUrl& url, - const QVariantHash& mapping = QVariantHash(), const QUrl& textureBaseUrl = QUrl()); + const QVariantHash& mapping = QVariantHash(), + const QUrl& textureBaseUrl = QUrl()); + + GeometryResource::Pointer getCollisionGeometryResource(const QUrl& url, + const QVariantHash& mapping = QVariantHash(), + const QUrl& textureBaseUrl = QUrl()); protected: friend class GeometryMappingResource; virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, - const void* extra) override; + const void* extra) override; private: ModelCache(); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 8f1f1baed2..53ccd2c386 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -295,8 +295,8 @@ protected: /// Creates a new resource. virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, - const void* extra) = 0; - + const void* extra) = 0; + void addUnusedResource(const QSharedPointer& resource); void removeUnusedResource(const QSharedPointer& resource); diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index 30d0b5e772..0b12cb64ed 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -43,7 +43,8 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { QByteArray fbxContents = fbx.readAll(); FBXGeometry* geom; if (filename.toLower().endsWith(".obj")) { - geom = OBJReader().readOBJ(fbxContents, QVariantHash()); + bool combineParts = false; + geom = OBJReader().readOBJ(fbxContents, QVariantHash(), combineParts); } else if (filename.toLower().endsWith(".fbx")) { geom = readFBX(fbxContents, QVariantHash(), filename); } else { diff --git a/tools/vhacd-util/src/VHACDUtilApp.cpp b/tools/vhacd-util/src/VHACDUtilApp.cpp index cae184a49c..4d48bdf2bf 100644 --- a/tools/vhacd-util/src/VHACDUtilApp.cpp +++ b/tools/vhacd-util/src/VHACDUtilApp.cpp @@ -10,6 +10,7 @@ // #include +#include #include #include "VHACDUtilApp.h" #include "VHACDUtil.h" @@ -98,6 +99,8 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : { vhacd::VHACDUtil vUtil; + DependencyManager::set(); + // parse command-line QCommandLineParser parser; parser.setApplicationDescription("High Fidelity Object Decomposer");