From fc8b34f8c79d0940df835d6f9d49f02c515f12be Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 23 Mar 2016 14:01:11 -0700 Subject: [PATCH 01/15] Move tex alpha detection to cv method --- libraries/model/src/model/Material.cpp | 61 ++++++++++++++------------ libraries/model/src/model/Material.h | 12 ++--- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/libraries/model/src/model/Material.cpp b/libraries/model/src/model/Material.cpp index 0a6a4bb695..5260143a7f 100755 --- a/libraries/model/src/model/Material.cpp +++ b/libraries/model/src/model/Material.cpp @@ -83,41 +83,48 @@ void Material::setMetallic(float metallic) { void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textureMap) { if (textureMap) { _key.setMapChannel(channel, (true)); - - if (channel == MaterialKey::ALBEDO_MAP) { - // clear the previous flags whatever they were: - _key.setOpacityMaskMap(false); - _key.setTranslucentMap(false); - - if (textureMap->useAlphaChannel() && textureMap->isDefined() && textureMap->getTextureView().isValid()) { - auto usage = textureMap->getTextureView()._texture->getUsage(); - if (usage.isAlpha()) { - // Texture has alpha, is not just a mask or a true transparent channel - if (usage.isAlphaMask()) { - _key.setOpacityMaskMap(true); - _key.setTranslucentMap(false); - } else { - _key.setOpacityMaskMap(false); - _key.setTranslucentMap(true); - } - } - } - } - _textureMaps[channel] = textureMap; } else { _key.setMapChannel(channel, (false)); - - if (channel == MaterialKey::ALBEDO_MAP) { - _key.setOpacityMaskMap(false); - _key.setTranslucentMap(false); - } - _textureMaps.erase(channel); } _schemaBuffer.edit()._key = (uint32)_key._flags.to_ulong(); + if (channel == MaterialKey::ALBEDO_MAP) { + resetOpacityMap(); + } + + _schemaBuffer.edit()._key = (uint32)_key._flags.to_ulong(); + +} + +void Material::resetOpacityMap() const { + // Clear the previous flags + _key.setOpacityMaskMap(false); + _key.setTranslucentMap(false); + + const auto& textureMap = getTextureMap(MaterialKey::ALBEDO_MAP); + if (textureMap && + textureMap->useAlphaChannel() && + textureMap->isDefined() && + textureMap->getTextureView().isValid()) { + + auto usage = textureMap->getTextureView()._texture->getUsage(); + if (usage.isAlpha()) { + if (usage.isAlphaMask()) { + // Texture has alpha, but it is just a mask + _key.setOpacityMaskMap(true); + _key.setTranslucentMap(false); + } else { + // Texture has alpha, it is a true translucency channel + _key.setOpacityMaskMap(false); + _key.setTranslucentMap(true); + } + } + } + + _schemaBuffer.edit()._key = (uint32)_key._flags.to_ulong(); } diff --git a/libraries/model/src/model/Material.h b/libraries/model/src/model/Material.h index 5a7b919994..c500e9ec10 100755 --- a/libraries/model/src/model/Material.h +++ b/libraries/model/src/model/Material.h @@ -291,15 +291,17 @@ public: const TextureMaps& getTextureMaps() const { return _textureMaps; } const TextureMapPointer getTextureMap(MapChannel channel) const; + // Albedo maps cannot have opacity detected until they are loaded + // This method allows const changing of the key/schemaBuffer without touching the map + void resetOpacityMap() const; + // conversion from legacy material properties to PBR equivalent static float shininessToRoughness(float shininess) { return 1.0f - shininess / 100.0f; } -protected: - - MaterialKey _key; - UniformBufferView _schemaBuffer; +private: + mutable MaterialKey _key; + mutable UniformBufferView _schemaBuffer; TextureMaps _textureMaps; - }; typedef std::shared_ptr< Material > MaterialPointer; From 7f05e4453b6821bf6526255691522c83d431d1a2 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 23 Mar 2016 14:01:22 -0700 Subject: [PATCH 02/15] Fetch model geometries through cache --- .../src/model-networking/ModelCache.cpp | 821 ++++++++---------- .../src/model-networking/ModelCache.h | 266 +++--- 2 files changed, 493 insertions(+), 594 deletions(-) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index b0b769d5e9..bb7efb8f75 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -1,53 +1,101 @@ // // ModelCache.cpp -// interface/src/renderer +// libraries/model-networking // -// Created by Andrzej Kapolka on 6/21/13. -// Copyright 2013 High Fidelity, Inc. +// Created by Zach Pomerantz on 3/15/16. +// Copyright 2016 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "ModelCache.h" +#include +#include "FBXReader.h" +#include "OBJReader.h" -#include +#include +#include -#include #include -#include -#include - -#include "TextureCache.h" #include "ModelNetworkingLogging.h" -#include "model/TextureMap.h" +class GeometryReader; -//#define WANT_DEBUG +class GeometryExtra { +public: + const QVariantHash& mapping; + const QUrl& textureBaseUrl; +}; -ModelCache::ModelCache() -{ - const qint64 GEOMETRY_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE; - setUnusedResourceCacheSize(GEOMETRY_DEFAULT_UNUSED_MAX_SIZE); +class GeometryMappingResource : public GeometryResource { + Q_OBJECT +public: + GeometryMappingResource(const QUrl& url) : GeometryResource(url) {}; + + virtual void downloadFinished(const QByteArray& data) override; + +private slots: + void onGeometryMappingLoaded(bool success); + +private: + GeometryResource::Pointer _geometryResource; +}; + +void GeometryMappingResource::downloadFinished(const QByteArray& data) { + auto mapping = FSTReader::readMapping(data); + + QString filename = mapping.value("filename").toString(); + if (filename.isNull()) { + qCDebug(modelnetworking) << "Mapping file" << _url << "has no \"filename\" field"; + finishedLoading(false); + } else { + QUrl url = _url.resolved(filename); + QUrl textureBaseUrl; + + QString texdir = mapping.value("texdir").toString(); + if (!texdir.isNull()) { + if (!texdir.endsWith('/')) { + texdir += '/'; + } + textureBaseUrl = _url.resolved(texdir); + } + + auto modelCache = DependencyManager::get(); + GeometryExtra extra{ mapping, textureBaseUrl }; + + // Get the raw GeometryResource, not the wrapped NetworkGeometry + _geometryResource = modelCache->getResource(url, QUrl(), true, &extra).staticCast(); + connect(_geometryResource.data(), &Resource::finished, this, &GeometryMappingResource::onGeometryMappingLoaded); + } } -ModelCache::~ModelCache() { +void GeometryMappingResource::onGeometryMappingLoaded(bool success) { + if (success) { + _geometry = _geometryResource->_geometry; + _shapes = _geometryResource->_shapes; + _meshes = _geometryResource->_meshes; + _materials = _geometryResource->_materials; + } + finishedLoading(success); } -QSharedPointer ModelCache::createResource(const QUrl& url, const QSharedPointer& fallback, - bool delayLoad, const void* extra) { - // NetworkGeometry is no longer a subclass of Resource, but requires this method because, it is pure virtual. - assert(false); - return QSharedPointer(); -} +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) {} + virtual ~GeometryReader() = default; + virtual void run() override; -GeometryReader::GeometryReader(const QUrl& url, const QByteArray& data, const QVariantHash& mapping) : - _url(url), - _data(data), - _mapping(mapping) { -} +private: + QWeakPointer _resource; + QUrl _url; + QVariantHash _mapping; + QByteArray _data; +}; void GeometryReader::run() { auto originalPriority = QThread::currentThread()->priority(); @@ -55,458 +103,353 @@ void GeometryReader::run() { originalPriority = QThread::NormalPriority; } QThread::currentThread()->setPriority(QThread::LowPriority); + + // Ensure the resource is still being requested + auto resource = _resource.toStrongRef(); + if (!resource) { + qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref"; + return; + } + try { if (_data.isEmpty()) { - throw QString("Reply is NULL ?!"); + throw QString("reply is NULL"); } - QString urlname = _url.path().toLower(); - bool urlValid = true; - urlValid &= !urlname.isEmpty(); - urlValid &= !_url.path().isEmpty(); - urlValid &= _url.path().toLower().endsWith(".fbx") || _url.path().toLower().endsWith(".obj"); - if (urlValid) { - // Let's read the binaries from the network - FBXGeometry* fbxgeo = nullptr; + QString urlname = _url.path().toLower(); + if (!urlname.isEmpty() && !_url.path().isEmpty() && + (_url.path().toLower().endsWith(".fbx") || _url.path().toLower().endsWith(".obj"))) { + FBXGeometry* fbxGeometry = nullptr; + if (_url.path().toLower().endsWith(".fbx")) { - const bool grabLightmaps = true; - const float lightmapLevel = 1.0f; - fbxgeo = readFBX(_data, _mapping, _url.path(), grabLightmaps, lightmapLevel); - if (fbxgeo->meshes.size() == 0 && fbxgeo->joints.size() == 0) { - // empty fbx geometry, indicates error + fbxGeometry = readFBX(_data, _mapping, _url.path()); + if (fbxGeometry->meshes.size() == 0 && fbxGeometry->joints.size() == 0) { throw QString("empty geometry, possibly due to an unsupported FBX version"); } } else if (_url.path().toLower().endsWith(".obj")) { - fbxgeo = OBJReader().readOBJ(_data, _mapping, _url); + fbxGeometry = OBJReader().readOBJ(_data, _mapping, _url); } else { - QString errorStr("unsupported format"); - throw errorStr; + throw QString("unsupported format"); } - emit onSuccess(fbxgeo); + + QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition", + Q_ARG(void*, fbxGeometry)); } else { throw QString("url is invalid"); } - } catch (const QString& error) { qCDebug(modelnetworking) << "Error reading " << _url << ": " << error; - emit onError(NetworkGeometry::ModelParseError, error); + QMetaObject::invokeMethod(resource.data(), "finishedLoading", Q_ARG(bool, false)); } QThread::currentThread()->setPriority(originalPriority); } -NetworkGeometry::NetworkGeometry(const QUrl& url, bool delayLoad, const QVariantHash& mapping, const QUrl& textureBaseUrl) : - _url(url), - _mapping(mapping), - _textureBaseUrl(textureBaseUrl.isValid() ? textureBaseUrl : url) { +class GeometryDefinitionResource : public GeometryResource { + Q_OBJECT +public: + GeometryDefinitionResource(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) : + GeometryResource(url), _mapping(mapping), _textureBaseUrl(textureBaseUrl) {} - if (delayLoad) { - _state = DelayState; - } else { - attemptRequestInternal(); - } + virtual void downloadFinished(const QByteArray& data) override; + +protected: + Q_INVOKABLE void setGeometryDefinition(void* fbxGeometry); + +private: + QVariantHash _mapping; + QUrl _textureBaseUrl; +}; + +void GeometryDefinitionResource::downloadFinished(const QByteArray& data) { + QThreadPool::globalInstance()->start(new GeometryReader(_self, _url, _mapping, data)); } -NetworkGeometry::~NetworkGeometry() { - if (_resource) { - _resource->deleteLater(); - } -} +void GeometryDefinitionResource::setGeometryDefinition(void* fbxGeometry) { + // Assume ownership of the geometry pointer + _geometry.reset(static_cast(fbxGeometry)); -void NetworkGeometry::attemptRequest() { - if (_state == DelayState) { - attemptRequestInternal(); - } -} - -void NetworkGeometry::attemptRequestInternal() { - if (_url.path().toLower().endsWith(".fst")) { - _mappingUrl = _url; - requestMapping(_url); - } else { - _modelUrl = _url; - requestModel(_url); - } -} - -bool NetworkGeometry::isLoaded() const { - return _state == SuccessState; -} - -bool NetworkGeometry::isLoadedWithTextures() const { - if (!isLoaded()) { - return false; + // Copy materials + QHash materialIDAtlas; + for (const FBXMaterial& material : _geometry->materials) { + materialIDAtlas[material.materialID] = _materials.size(); + _materials.push_back(std::make_shared(material, _textureBaseUrl)); } - if (!_isLoadedWithTextures) { - _hasTransparentTextures = false; - - for (auto&& material : _materials) { - if ((material->albedoTexture && !material->albedoTexture->isLoaded()) || - (material->normalTexture && !material->normalTexture->isLoaded()) || - (material->roughnessTexture && !material->roughnessTexture->isLoaded()) || - (material->metallicTexture && !material->metallicTexture->isLoaded()) || - (material->occlusionTexture && !material->occlusionTexture->isLoaded()) || - (material->emissiveTexture && !material->emissiveTexture->isLoaded()) || - (material->lightmapTexture && !material->lightmapTexture->isLoaded())) { - return false; - } - if (material->albedoTexture && material->albedoTexture->getGPUTexture()) { - // Reassign the texture to make sure that itsalbedo alpha channel material key is detected correctly - material->_material->setTextureMap(model::MaterialKey::ALBEDO_MAP, material->_material->getTextureMap(model::MaterialKey::ALBEDO_MAP)); - const auto& usage = material->albedoTexture->getGPUTexture()->getUsage(); - bool isTransparentTexture = usage.isAlpha() && !usage.isAlphaMask(); - _hasTransparentTextures |= isTransparentTexture; - } - } - - _isLoadedWithTextures = true; - } - return true; -} - -void NetworkGeometry::setTextureWithNameToURL(const QString& name, const QUrl& url) { - if (_meshes.size() > 0) { - auto textureCache = DependencyManager::get(); - for (auto&& material : _materials) { - auto networkMaterial = material->_material; - auto oldTextureMaps = networkMaterial->getTextureMaps(); - if (material->albedoTextureName == name) { - material->albedoTexture = textureCache->getTexture(url, DEFAULT_TEXTURE); - - auto albedoMap = model::TextureMapPointer(new model::TextureMap()); - albedoMap->setTextureSource(material->albedoTexture->_textureSource); - albedoMap->setTextureTransform(oldTextureMaps[model::MaterialKey::ALBEDO_MAP]->getTextureTransform()); - // when reassigning the albedo texture we also check for the alpha channel used as opacity - albedoMap->setUseAlphaChannel(true); - networkMaterial->setTextureMap(model::MaterialKey::ALBEDO_MAP, albedoMap); - } else if (material->normalTextureName == name) { - material->normalTexture = textureCache->getTexture(url); - - auto normalMap = model::TextureMapPointer(new model::TextureMap()); - normalMap->setTextureSource(material->normalTexture->_textureSource); - - networkMaterial->setTextureMap(model::MaterialKey::NORMAL_MAP, normalMap); - } else if (material->roughnessTextureName == name) { - // FIXME: If passing a gloss map instead of a roughmap how to say that ? looking for gloss in the name ? - material->roughnessTexture = textureCache->getTexture(url, ROUGHNESS_TEXTURE); - - auto roughnessMap = model::TextureMapPointer(new model::TextureMap()); - roughnessMap->setTextureSource(material->roughnessTexture->_textureSource); - - networkMaterial->setTextureMap(model::MaterialKey::ROUGHNESS_MAP, roughnessMap); - } else if (material->metallicTextureName == name) { - // FIXME: If passing a specular map instead of a metallic how to say that ? looking for wtf in the name ? - material->metallicTexture = textureCache->getTexture(url, METALLIC_TEXTURE); - - auto glossMap = model::TextureMapPointer(new model::TextureMap()); - glossMap->setTextureSource(material->metallicTexture->_textureSource); - - networkMaterial->setTextureMap(model::MaterialKey::METALLIC_MAP, glossMap); - } else if (material->emissiveTextureName == name) { - material->emissiveTexture = textureCache->getTexture(url, EMISSIVE_TEXTURE); - - auto emissiveMap = model::TextureMapPointer(new model::TextureMap()); - emissiveMap->setTextureSource(material->emissiveTexture->_textureSource); - - networkMaterial->setTextureMap(model::MaterialKey::EMISSIVE_MAP, emissiveMap); - } else if (material->lightmapTextureName == name) { - material->lightmapTexture = textureCache->getTexture(url, LIGHTMAP_TEXTURE); - - auto lightmapMap = model::TextureMapPointer(new model::TextureMap()); - lightmapMap->setTextureSource(material->lightmapTexture->_textureSource); - lightmapMap->setTextureTransform( - oldTextureMaps[model::MaterialKey::LIGHTMAP_MAP]->getTextureTransform()); - glm::vec2 oldOffsetScale = - oldTextureMaps[model::MaterialKey::LIGHTMAP_MAP]->getLightmapOffsetScale(); - lightmapMap->setLightmapOffsetScale(oldOffsetScale.x, oldOffsetScale.y); - - networkMaterial->setTextureMap(model::MaterialKey::LIGHTMAP_MAP, lightmapMap); - } - } - } else { - qCWarning(modelnetworking) << "Ignoring setTextureWithNameToURL() geometry not ready." << name << url; - } - _isLoadedWithTextures = false; -} - -QStringList NetworkGeometry::getTextureNames() const { - QStringList result; - for (auto&& material : _materials) { - if (!material->emissiveTextureName.isEmpty() && material->emissiveTexture) { - QString textureURL = material->emissiveTexture->getURL().toString(); - result << material->emissiveTextureName + ":\"" + textureURL + "\""; - } - - if (!material->albedoTextureName.isEmpty() && material->albedoTexture) { - QString textureURL = material->albedoTexture->getURL().toString(); - result << material->albedoTextureName + ":\"" + textureURL + "\""; - } - - if (!material->normalTextureName.isEmpty() && material->normalTexture) { - QString textureURL = material->normalTexture->getURL().toString(); - result << material->normalTextureName + ":\"" + textureURL + "\""; - } - - if (!material->roughnessTextureName.isEmpty() && material->roughnessTexture) { - QString textureURL = material->roughnessTexture->getURL().toString(); - result << material->roughnessTextureName + ":\"" + textureURL + "\""; - } - - if (!material->metallicTextureName.isEmpty() && material->metallicTexture) { - QString textureURL = material->metallicTexture->getURL().toString(); - result << material->metallicTextureName + ":\"" + textureURL + "\""; - } - - if (!material->occlusionTextureName.isEmpty() && material->occlusionTexture) { - QString textureURL = material->occlusionTexture->getURL().toString(); - result << material->occlusionTextureName + ":\"" + textureURL + "\""; - } - - if (!material->lightmapTextureName.isEmpty() && material->lightmapTexture) { - QString textureURL = material->lightmapTexture->getURL().toString(); - result << material->lightmapTextureName + ":\"" + textureURL + "\""; - } - } - - return result; -} - -void NetworkGeometry::requestMapping(const QUrl& url) { - _state = RequestMappingState; - if (_resource) { - _resource->deleteLater(); - } - _resource = new Resource(url, false); - connect(_resource, &Resource::loaded, this, &NetworkGeometry::mappingRequestDone); - connect(_resource, &Resource::failed, this, &NetworkGeometry::mappingRequestError); -} - -void NetworkGeometry::requestModel(const QUrl& url) { - _state = RequestModelState; - if (_resource) { - _resource->deleteLater(); - } - _modelUrl = url; - _resource = new Resource(url, false); - connect(_resource, &Resource::loaded, this, &NetworkGeometry::modelRequestDone); - connect(_resource, &Resource::failed, this, &NetworkGeometry::modelRequestError); -} - -void NetworkGeometry::mappingRequestDone(const QByteArray& data) { - assert(_state == RequestMappingState); - - // parse the mapping file - _mapping = FSTReader::readMapping(data); - - QUrl replyUrl = _mappingUrl; - QString modelUrlStr = _mapping.value("filename").toString(); - if (modelUrlStr.isNull()) { - qCDebug(modelnetworking) << "Mapping file " << _url << "has no \"filename\" entry"; - emit onFailure(*this, MissingFilenameInMapping); - } else { - // read _textureBase from mapping file, if present - QString texdir = _mapping.value("texdir").toString(); - if (!texdir.isNull()) { - if (!texdir.endsWith('/')) { - texdir += '/'; - } - _textureBaseUrl = replyUrl.resolved(texdir); - } - - _modelUrl = replyUrl.resolved(modelUrlStr); - requestModel(_modelUrl); - } -} - -void NetworkGeometry::mappingRequestError(QNetworkReply::NetworkError error) { - assert(_state == RequestMappingState); - _state = ErrorState; - emit onFailure(*this, MappingRequestError); -} - -void NetworkGeometry::modelRequestDone(const QByteArray& data) { - assert(_state == RequestModelState); - - _state = ParsingModelState; - - // asynchronously parse the model file. - GeometryReader* geometryReader = new GeometryReader(_modelUrl, data, _mapping); - connect(geometryReader, SIGNAL(onSuccess(FBXGeometry*)), SLOT(modelParseSuccess(FBXGeometry*))); - connect(geometryReader, SIGNAL(onError(int, QString)), SLOT(modelParseError(int, QString))); - - QThreadPool::globalInstance()->start(geometryReader); -} - -void NetworkGeometry::modelRequestError(QNetworkReply::NetworkError error) { - assert(_state == RequestModelState); - _state = ErrorState; - emit onFailure(*this, ModelRequestError); -} - -static NetworkMesh* buildNetworkMesh(const FBXMesh& mesh, const QUrl& textureBaseUrl) { - NetworkMesh* networkMesh = new NetworkMesh(); - - networkMesh->_mesh = mesh._mesh; - - return networkMesh; -} - - -static model::TextureMapPointer setupNetworkTextureMap(NetworkGeometry* geometry, const QUrl& textureBaseUrl, - const FBXTexture& texture, TextureType type, - NetworkTexturePointer& networkTexture, QString& networkTextureName) { - auto textureCache = DependencyManager::get(); - - // If content is inline, cache it under the fbx file, not its base url - const auto baseUrl = texture.content.isEmpty() ? textureBaseUrl : QUrl(textureBaseUrl.url() + "/"); - const auto filename = baseUrl.resolved(QUrl(texture.filename)); - - networkTexture = textureCache->getTexture(filename, type, texture.content); - QObject::connect(networkTexture.data(), &NetworkTexture::networkTextureCreated, geometry, &NetworkGeometry::textureLoaded); - networkTextureName = texture.name; - - auto map = std::make_shared(); - map->setTextureSource(networkTexture->_textureSource); - return map; -} - -static NetworkMaterial* buildNetworkMaterial(NetworkGeometry* geometry, const FBXMaterial& material, const QUrl& textureBaseUrl) { - NetworkMaterial* networkMaterial = new NetworkMaterial(); - networkMaterial->_material = material._material; - - if (!material.albedoTexture.filename.isEmpty()) { - auto albedoMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.albedoTexture, DEFAULT_TEXTURE, - networkMaterial->albedoTexture, networkMaterial->albedoTextureName); - albedoMap->setTextureTransform(material.albedoTexture.transform); - - if (!material.opacityTexture.filename.isEmpty()) { - if (material.albedoTexture.filename == material.opacityTexture.filename) { - // Best case scenario, just indicating that the albedo map contains transparency - albedoMap->setUseAlphaChannel(true); - } else { - // Opacity Map is different from the Abledo map, not supported - } - } - - material._material->setTextureMap(model::MaterialKey::ALBEDO_MAP, albedoMap); - } - - - if (!material.normalTexture.filename.isEmpty()) { - auto normalMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.normalTexture, - (material.normalTexture.isBumpmap ? BUMP_TEXTURE : NORMAL_TEXTURE), - networkMaterial->normalTexture, networkMaterial->normalTextureName); - networkMaterial->_material->setTextureMap(model::MaterialKey::NORMAL_MAP, normalMap); - } - - // Roughness first or gloss maybe - if (!material.roughnessTexture.filename.isEmpty()) { - auto roughnessMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.roughnessTexture, ROUGHNESS_TEXTURE, - networkMaterial->roughnessTexture, networkMaterial->roughnessTextureName); - material._material->setTextureMap(model::MaterialKey::ROUGHNESS_MAP, roughnessMap); - } else if (!material.glossTexture.filename.isEmpty()) { - auto roughnessMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.glossTexture, GLOSS_TEXTURE, - networkMaterial->roughnessTexture, networkMaterial->roughnessTextureName); - material._material->setTextureMap(model::MaterialKey::ROUGHNESS_MAP, roughnessMap); - } - - // Metallic first or specular maybe - - if (!material.metallicTexture.filename.isEmpty()) { - auto metallicMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.metallicTexture, METALLIC_TEXTURE, - networkMaterial->metallicTexture, networkMaterial->metallicTextureName); - material._material->setTextureMap(model::MaterialKey::METALLIC_MAP, metallicMap); - } else if (!material.specularTexture.filename.isEmpty()) { - - auto metallicMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.specularTexture, SPECULAR_TEXTURE, - networkMaterial->metallicTexture, networkMaterial->metallicTextureName); - material._material->setTextureMap(model::MaterialKey::METALLIC_MAP, metallicMap); - } - - if (!material.occlusionTexture.filename.isEmpty()) { - auto occlusionMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.occlusionTexture, OCCLUSION_TEXTURE, - networkMaterial->occlusionTexture, networkMaterial->occlusionTextureName); - material._material->setTextureMap(model::MaterialKey::OCCLUSION_MAP, occlusionMap); - } - - if (!material.emissiveTexture.filename.isEmpty()) { - auto emissiveMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.emissiveTexture, EMISSIVE_TEXTURE, - networkMaterial->emissiveTexture, networkMaterial->emissiveTextureName); - material._material->setTextureMap(model::MaterialKey::EMISSIVE_MAP, emissiveMap); - } - - if (!material.lightmapTexture.filename.isEmpty()) { - auto lightmapMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.lightmapTexture, LIGHTMAP_TEXTURE, - networkMaterial->lightmapTexture, networkMaterial->lightmapTextureName); - lightmapMap->setTextureTransform(material.lightmapTexture.transform); - lightmapMap->setLightmapOffsetScale(material.lightmapParams.x, material.lightmapParams.y); - material._material->setTextureMap(model::MaterialKey::LIGHTMAP_MAP, lightmapMap); - } - - return networkMaterial; -} - - -void NetworkGeometry::modelParseSuccess(FBXGeometry* geometry) { - // assume owner ship of geometry pointer - _geometry.reset(geometry); - - - - foreach(const FBXMesh& mesh, _geometry->meshes) { - _meshes.emplace_back(buildNetworkMesh(mesh, _textureBaseUrl)); - } - - QHash fbxMatIDToMatID; - foreach(const FBXMaterial& material, _geometry->materials) { - fbxMatIDToMatID[material.materialID] = _materials.size(); - _materials.emplace_back(buildNetworkMaterial(this, material, _textureBaseUrl)); - } - - + std::shared_ptr meshes = std::make_shared(); + std::shared_ptr shapes = std::make_shared(); int meshID = 0; - foreach(const FBXMesh& mesh, _geometry->meshes) { + for (const FBXMesh& mesh : _geometry->meshes) { + // Copy mesh pointers + meshes->emplace_back(mesh._mesh); int partID = 0; - foreach (const FBXMeshPart& part, mesh.parts) { - NetworkShape* networkShape = new NetworkShape(); - networkShape->_meshID = meshID; - networkShape->_partID = partID; - networkShape->_materialID = (int)fbxMatIDToMatID[part.materialID]; - _shapes.emplace_back(networkShape); + for (const FBXMeshPart& part : mesh.parts) { + // Construct local shapes + shapes->emplace_back(meshID, partID, (int)materialIDAtlas[part.materialID]); partID++; } meshID++; } + _meshes = meshes; + _shapes = shapes; - _state = SuccessState; - emit onSuccess(*this, *_geometry.get()); - - delete _resource; - _resource = nullptr; + finishedLoading(true); } -void NetworkGeometry::modelParseError(int error, QString str) { - _state = ErrorState; - emit onFailure(*this, (NetworkGeometry::Error)error); - - delete _resource; - _resource = nullptr; +ModelCache::ModelCache() { + const qint64 GEOMETRY_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE; + setUnusedResourceCacheSize(GEOMETRY_DEFAULT_UNUSED_MAX_SIZE); } -const NetworkMaterial* NetworkGeometry::getShapeMaterial(int shapeID) { - if ((shapeID >= 0) && (shapeID < (int)_shapes.size())) { - int materialID = _shapes[shapeID]->_materialID; - if ((materialID >= 0) && ((unsigned int)materialID < _materials.size())) { - return _materials[materialID].get(); - } else { - return 0; +QSharedPointer ModelCache::createResource(const QUrl& url, const QSharedPointer& fallback, + bool delayLoad, const void* extra) { + const GeometryExtra* geometryExtra = static_cast(extra); + + Resource* resource = nullptr; + if (url.path().toLower().endsWith(".fst")) { + resource = new GeometryMappingResource(url); + } else { + resource = new GeometryDefinitionResource(url, geometryExtra->mapping, geometryExtra->textureBaseUrl); + } + + return QSharedPointer(resource, &Resource::allReferencesCleared); +} + +std::shared_ptr ModelCache::getGeometry(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) { + GeometryExtra geometryExtra = { mapping, textureBaseUrl }; + GeometryResource::Pointer resource = getResource(url, QUrl(), true, &geometryExtra).staticCast(); + return std::make_shared(resource); +} + +const QVariantMap Geometry::getTextures() const { + QVariantMap textures; + for (const auto& material : _materials) { + for (const auto& texture : material->_textures) { + if (texture.texture) { + textures[texture.name] = texture.texture->getURL(); + } + } + } + + return textures; +} + +void Geometry::setTextures(const QVariantMap& textureMap) { + if (_meshes->size() > 0) { + for (auto& material : _materials) { + // Check if any material textures actually changed + if (std::any_of(material->_textures.cbegin(), material->_textures.cend(), + [&textureMap](const NetworkMaterial::Textures::value_type& it) { return it.texture && textureMap.contains(it.name); })) { + + material = std::make_shared(*material, textureMap); + _areTexturesLoaded = false; + } } } else { - return 0; + qCWarning(modelnetworking) << "Ignoring setTextures(); geometry not ready"; } } -void NetworkGeometry::textureLoaded(const QWeakPointer& networkTexture) { - numTextureLoaded++; +bool Geometry::areTexturesLoaded() const { + if (!_areTexturesLoaded) { + _hasTransparentTextures = false; + + for (auto& material : _materials) { + // Check if material textures are loaded + if (std::any_of(material->_textures.cbegin(), material->_textures.cend(), + [](const NetworkMaterial::Textures::value_type& it) { return it.texture && !it.texture->isLoaded(); })) { + + return false; + } + + // If material textures are loaded, check the material translucency + const auto albedoTexture = material->_textures[NetworkMaterial::MapChannel::ALBEDO_MAP]; + if (albedoTexture.texture && albedoTexture.texture->getGPUTexture()) { + material->resetOpacityMap(); + + _hasTransparentTextures |= material->getKey().isTranslucent(); + } + } + + _areTexturesLoaded = true; + } + return true; } + +const std::shared_ptr Geometry::getShapeMaterial(int shapeID) const { + if ((shapeID >= 0) && (shapeID < (int)_shapes->size())) { + int materialID = _shapes->at(shapeID).materialID; + if ((materialID >= 0) && (materialID < (int)_materials.size())) { + return _materials[materialID]; + } + } + return nullptr; +} + +NetworkGeometry::NetworkGeometry(const GeometryResource::Pointer& networkGeometry) : _resource(networkGeometry) { + connect(_resource.data(), &Resource::finished, this, &NetworkGeometry::resourceFinished); + connect(_resource.data(), &Resource::onRefresh, this, &NetworkGeometry::resourceRefreshed); + if (_resource->isLoaded()) { + resourceFinished(); + } +} + +void NetworkGeometry::resourceFinished() { + _instance = std::make_shared(*_resource); +} + +void NetworkGeometry::resourceRefreshed() { + _instance.reset(); +} + +const QString NetworkMaterial::NO_TEXTURE = QString(); + +const QString& NetworkMaterial::getTextureName(MapChannel channel) { + if (_textures[channel].texture) { + return _textures[channel].name; + } + return NO_TEXTURE; +} + +QUrl NetworkMaterial::getTextureUrl(const QUrl& url, const FBXTexture& texture) { + // If content is inline, cache it under the fbx file, not its url + const auto baseUrl = texture.content.isEmpty() ? url: QUrl(url.url() + "/"); + return baseUrl.resolved(QUrl(texture.filename)); +} + +model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture, + TextureType type, MapChannel channel) { + const auto url = getTextureUrl(baseUrl, fbxTexture); + const auto texture = DependencyManager::get()->getTexture(url, type, fbxTexture.content); + _textures[channel] = Texture { fbxTexture.name, texture }; + + auto map = std::make_shared(); + map->setTextureSource(texture->_textureSource); + return map; +} + +model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, TextureType type, MapChannel channel) { + const auto texture = DependencyManager::get()->getTexture(url, type); + _textures[channel].texture = texture; + + auto map = std::make_shared(); + map->setTextureSource(texture->_textureSource); + return map; +} + +NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl) { + _textures = Textures(MapChannel::NUM_MAP_CHANNELS); + if (!material.albedoTexture.filename.isEmpty()) { + auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, DEFAULT_TEXTURE, MapChannel::ALBEDO_MAP); + map->setTextureTransform(material.albedoTexture.transform); + + if (!material.opacityTexture.filename.isEmpty()) { + if (material.albedoTexture.filename == material.opacityTexture.filename) { + // Best case scenario, just indicating that the albedo map contains transparency + // TODO: Different albedo/opacity maps are not currently supported + map->setUseAlphaChannel(true); + } + } + + setTextureMap(MapChannel::ALBEDO_MAP, map); + } + + + if (!material.normalTexture.filename.isEmpty()) { + auto type = (material.normalTexture.isBumpmap ? BUMP_TEXTURE : NORMAL_TEXTURE); + auto map = fetchTextureMap(textureBaseUrl, material.normalTexture, type, MapChannel::NORMAL_MAP); + setTextureMap(MapChannel::NORMAL_MAP, map); + } + + if (!material.roughnessTexture.filename.isEmpty()) { + auto map = fetchTextureMap(textureBaseUrl, material.roughnessTexture, ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP); + setTextureMap(MapChannel::ROUGHNESS_MAP, map); + } else if (!material.glossTexture.filename.isEmpty()) { + auto map = fetchTextureMap(textureBaseUrl, material.glossTexture, GLOSS_TEXTURE, MapChannel::ROUGHNESS_MAP); + setTextureMap(MapChannel::ROUGHNESS_MAP, map); + } + + if (!material.metallicTexture.filename.isEmpty()) { + auto map = fetchTextureMap(textureBaseUrl, material.metallicTexture, METALLIC_TEXTURE, MapChannel::METALLIC_MAP); + setTextureMap(MapChannel::METALLIC_MAP, map); + } else if (!material.specularTexture.filename.isEmpty()) { + auto map = fetchTextureMap(textureBaseUrl, material.specularTexture, SPECULAR_TEXTURE, MapChannel::METALLIC_MAP); + setTextureMap(MapChannel::METALLIC_MAP, map); + } + + if (!material.occlusionTexture.filename.isEmpty()) { + auto map = fetchTextureMap(textureBaseUrl, material.occlusionTexture, OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP); + setTextureMap(MapChannel::OCCLUSION_MAP, map); + } + + if (!material.emissiveTexture.filename.isEmpty()) { + auto map = fetchTextureMap(textureBaseUrl, material.emissiveTexture, EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP); + setTextureMap(MapChannel::EMISSIVE_MAP, map); + } + + if (!material.lightmapTexture.filename.isEmpty()) { + auto map = fetchTextureMap(textureBaseUrl, material.lightmapTexture, LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP); + map->setTextureTransform(material.lightmapTexture.transform); + map->setLightmapOffsetScale(material.lightmapParams.x, material.lightmapParams.y); + setTextureMap(MapChannel::LIGHTMAP_MAP, map); + } +} + +NetworkMaterial::NetworkMaterial(const NetworkMaterial& material, const QVariantMap& textureMap) : NetworkMaterial(material) { + _textures = material._textures; + + const auto& albedoName = getTextureName(MapChannel::ALBEDO_MAP); + const auto& normalName = getTextureName(MapChannel::NORMAL_MAP); + const auto& roughnessName = getTextureName(MapChannel::ROUGHNESS_MAP); + const auto& metallicName = getTextureName(MapChannel::METALLIC_MAP); + const auto& occlusionName = getTextureName(MapChannel::OCCLUSION_MAP); + const auto& emissiveName = getTextureName(MapChannel::EMISSIVE_MAP); + const auto& lightmapName = getTextureName(MapChannel::LIGHTMAP_MAP); + + if (!albedoName.isEmpty() && textureMap.contains(albedoName)) { + auto map = fetchTextureMap(textureMap[albedoName].toUrl(), DEFAULT_TEXTURE, MapChannel::ALBEDO_MAP); + map->setTextureTransform(getTextureMap(MapChannel::ALBEDO_MAP)->getTextureTransform()); + // when reassigning the albedo texture we also check for the alpha channel used as opacity + map->setUseAlphaChannel(true); + setTextureMap(MapChannel::ALBEDO_MAP, map); + } + + if (!normalName.isEmpty() && textureMap.contains(normalName)) { + auto map = fetchTextureMap(textureMap[normalName].toUrl(), DEFAULT_TEXTURE, MapChannel::NORMAL_MAP); + setTextureMap(MapChannel::NORMAL_MAP, map); + } + + if (!roughnessName.isEmpty() && textureMap.contains(roughnessName)) { + // FIXME: If passing a gloss map instead of a roughmap how do we know? + auto map = fetchTextureMap(textureMap[roughnessName].toUrl(), ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP); + setTextureMap(MapChannel::ROUGHNESS_MAP, map); + } + + if (!metallicName.isEmpty() && textureMap.contains(metallicName)) { + // FIXME: If passing a specular map instead of a metallic how do we know? + auto map = fetchTextureMap(textureMap[metallicName].toUrl(), METALLIC_TEXTURE, MapChannel::METALLIC_MAP); + setTextureMap(MapChannel::METALLIC_MAP, map); + } + + if (!occlusionName.isEmpty() && textureMap.contains(occlusionName)) { + auto map = fetchTextureMap(textureMap[occlusionName].toUrl(), OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP); + setTextureMap(MapChannel::OCCLUSION_MAP, map); + } + + if (!emissiveName.isEmpty() && textureMap.contains(emissiveName)) { + auto map = fetchTextureMap(textureMap[emissiveName].toUrl(), EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP); + setTextureMap(MapChannel::EMISSIVE_MAP, map); + } + + if (!lightmapName.isEmpty() && textureMap.contains(lightmapName)) { + auto map = fetchTextureMap(textureMap[lightmapName].toUrl(), LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP); + auto oldMap = getTextureMap(MapChannel::LIGHTMAP_MAP); + map->setTextureTransform(oldMap->getTextureTransform()); + glm::vec2 offsetScale = oldMap->getLightmapOffsetScale(); + map->setLightmapOffsetScale(offsetScale.x, offsetScale.y); + setTextureMap(MapChannel::LIGHTMAP_MAP, map); + } +} + +#include "ModelCache.moc" + diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 1c76a0b878..a2fcc9d741 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -1,9 +1,9 @@ // // ModelCache.h -// libraries/model-networking/src/model-networking +// libraries/model-networking // -// Created by Sam Gateau on 9/21/15. -// Copyright 2013 High Fidelity, Inc. +// Created by Zach Pomerantz on 3/15/16. +// Copyright 2016 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -12,200 +12,156 @@ #ifndef hifi_ModelCache_h #define hifi_ModelCache_h -#include -#include - #include #include -#include "FBXReader.h" -#include "OBJReader.h" - -#include -#include - - #include #include -class NetworkGeometry; -class NetworkMesh; +#include "FBXReader.h" +#include "TextureCache.h" + +// Alias instead of derive to avoid copying +using NetworkMesh = model::Mesh; + class NetworkTexture; class NetworkMaterial; class NetworkShape; +class NetworkGeometry; -/// Stores cached geometry. +class GeometryMappingResource; + +/// Stores cached model geometries. class ModelCache : public ResourceCache, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY public: - virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, - bool delayLoad, const void* extra); + /// Loads a model geometry from the specified URL. + std::shared_ptr getGeometry(const QUrl& url, + const QVariantHash& mapping = QVariantHash(), const QUrl& textureBaseUrl = QUrl()); - /// Loads geometry from the specified URL. - /// \param fallback a fallback URL to load if the desired one is unavailable - /// \param delayLoad if true, don't load the geometry immediately; wait until load is first requested - QSharedPointer getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false); +protected: + friend class GeometryMappingResource; + + virtual QSharedPointer createResource(const QUrl& url, + const QSharedPointer& fallback, bool delayLoad, const void* extra); private: ModelCache(); - virtual ~ModelCache(); + virtual ~ModelCache() = default; +}; - QHash > _networkGeometry; +class Geometry { +public: + using Pointer = std::shared_ptr; + + // Immutable over lifetime + using NetworkMeshes = std::vector>; + using NetworkShapes = std::vector; + + // Mutable, but must retain structure of vector + using NetworkMaterials = std::vector>; + + const FBXGeometry& getGeometry() const { return *_geometry; } + const NetworkMeshes& getMeshes() const { return *_meshes; } + const std::shared_ptr getShapeMaterial(int shapeID) const; + + const QVariantMap getTextures() const; + void setTextures(const QVariantMap& textureMap); + + virtual bool areTexturesLoaded() const; + // Returns true if any albedo texture has a non-masking alpha channel. + // This can only be known after areTexturesLoaded(). + bool hasTransparentTextures() const { return _hasTransparentTextures; } + +protected: + friend class GeometryMappingResource; + + // Shared across all geometries, constant throughout lifetime + std::shared_ptr _geometry; + std::shared_ptr _meshes; + std::shared_ptr _shapes; + + // Copied to each geometry, mutable throughout lifetime via setTextures + NetworkMaterials _materials; + +private: + mutable bool _areTexturesLoaded { false }; + mutable bool _hasTransparentTextures { false }; +}; + +/// A geometry loaded from the network. +class GeometryResource : public Resource, public Geometry { +public: + using Pointer = QSharedPointer; + + GeometryResource(const QUrl& url) : Resource(url) {} + + virtual bool areTexturesLoaded() const { return isLoaded() && Geometry::areTexturesLoaded(); } + +protected: + virtual bool isCacheable() const override { return _loaded; } }; class NetworkGeometry : public QObject { Q_OBJECT - public: - // mapping is only used if url is a .fbx or .obj file, it is essentially the content of an fst file. - // if delayLoad is true, the url will not be immediately downloaded. - // use the attemptRequest method to initiate the download. - NetworkGeometry(const QUrl& url, bool delayLoad, const QVariantHash& mapping, const QUrl& textureBaseUrl = QUrl()); - ~NetworkGeometry(); + using Pointer = std::shared_ptr; - const QUrl& getURL() const { return _url; } + NetworkGeometry() = delete; + NetworkGeometry(const GeometryResource::Pointer& networkGeometry); - void attemptRequest(); + const QUrl& getURL() { return _resource->getURL(); } - // true when the geometry is loaded (but maybe not it's associated textures) - bool isLoaded() const; + /// Returns the geometry, if it is loaded (must be checked!) + const Geometry::Pointer& getGeometry() { return _instance; } - // true when the requested geometry and its textures are loaded. - bool isLoadedWithTextures() const; +private slots: + void resourceFinished(); + void resourceRefreshed(); - // true if the albedo texture has a non-masking alpha channel. - // This can only be known after isLoadedWithTextures(). - bool hasTransparentTextures() const { return _hasTransparentTextures; } +private: + GeometryResource::Pointer _resource; + Geometry::Pointer _instance { nullptr }; +}; - // WARNING: only valid when isLoaded returns true. - const FBXGeometry& getFBXGeometry() const { return *_geometry; } - const std::vector>& getMeshes() const { return _meshes; } - // const model::AssetPointer getAsset() const { return _asset; } - - // model::MeshPointer getShapeMesh(int shapeID); - // int getShapePart(int shapeID); - - // This would be the final verison - // model::MaterialPointer getShapeMaterial(int shapeID); - const NetworkMaterial* getShapeMaterial(int shapeID); - - - void setTextureWithNameToURL(const QString& name, const QUrl& url); - QStringList getTextureNames() const; - - enum Error { - MissingFilenameInMapping = 0, - MappingRequestError, - ModelRequestError, - ModelParseError - }; - -signals: - // Fired when everything has downloaded and parsed successfully. - void onSuccess(NetworkGeometry& networkGeometry, FBXGeometry& fbxGeometry); - - // Fired when something went wrong. - void onFailure(NetworkGeometry& networkGeometry, Error error); - -public slots: - void textureLoaded(const QWeakPointer& networkTexture); - -protected slots: - void mappingRequestDone(const QByteArray& data); - void mappingRequestError(QNetworkReply::NetworkError error); - - void modelRequestDone(const QByteArray& data); - void modelRequestError(QNetworkReply::NetworkError error); - - void modelParseSuccess(FBXGeometry* geometry); - void modelParseError(int error, QString str); +class NetworkMaterial : public model::Material { +public: + using MapChannel = model::Material::MapChannel; + NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl); + NetworkMaterial(const NetworkMaterial& material, const QVariantMap& textureMap); protected: - void attemptRequestInternal(); - void requestMapping(const QUrl& url); - void requestModel(const QUrl& url); + friend class Geometry; - enum State { DelayState, - RequestMappingState, - RequestModelState, - ParsingModelState, - SuccessState, - ErrorState }; - State _state; + class Texture { + public: + QString name; + QSharedPointer texture; + }; + using Textures = std::vector; - QUrl _url; - QUrl _mappingUrl; - QUrl _modelUrl; - QVariantHash _mapping; - QUrl _textureBaseUrl; - int numTextureLoaded = 0; + Textures _textures; - Resource* _resource = nullptr; - std::unique_ptr _geometry; // This should go away evenutally once we can put everything we need in the model::AssetPointer - std::vector> _meshes; - std::vector> _materials; - std::vector> _shapes; + static const QString NO_TEXTURE; + const QString& getTextureName(MapChannel channel); - - // The model asset created from this NetworkGeometry - // model::AssetPointer _asset; - - // cache for isLoadedWithTextures() - mutable bool _isLoadedWithTextures = false; - mutable bool _hasTransparentTextures = false; -}; - -/// Reads geometry in a worker thread. -class GeometryReader : public QObject, public QRunnable { - Q_OBJECT -public: - GeometryReader(const QUrl& url, const QByteArray& data, const QVariantHash& mapping); - virtual void run(); -signals: - void onSuccess(FBXGeometry* geometry); - void onError(int error, QString str); private: - QUrl _url; - QByteArray _data; - QVariantHash _mapping; + // Helpers for the ctors + QUrl getTextureUrl(const QUrl& baseUrl, const FBXTexture& fbxTexture); + model::TextureMapPointer fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture, + TextureType type, MapChannel channel); + model::TextureMapPointer fetchTextureMap(const QUrl& url, TextureType type, MapChannel channel); }; - class NetworkShape { public: - int _meshID{ -1 }; - int _partID{ -1 }; - int _materialID{ -1 }; + NetworkShape(int mesh, int part, int material) : meshID { mesh }, partID { part }, materialID { material } {} + int meshID { -1 }; + int partID { -1 }; + int materialID { -1 }; }; -class NetworkMaterial { -public: - - model::MaterialPointer _material; - QString emissiveTextureName; - QSharedPointer emissiveTexture; - QString albedoTextureName; - QSharedPointer albedoTexture; - QString normalTextureName; - QSharedPointer normalTexture; - QString roughnessTextureName; - QSharedPointer roughnessTexture; - QString metallicTextureName; - QSharedPointer metallicTexture; - QString occlusionTextureName; - QSharedPointer occlusionTexture; - QString lightmapTextureName; - QSharedPointer lightmapTexture; -}; - - -/// The state associated with a single mesh. -class NetworkMesh { -public: - model::MeshPointer _mesh; -}; - -#endif // hifi_GeometryCache_h +#endif // hifi_ModelCache_h From 3e9e083df56146ed29d12e7f3c15b42e166b412a Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 23 Mar 2016 19:21:38 -0700 Subject: [PATCH 03/15] Update users of NetworkGeometry --- interface/src/avatar/Avatar.cpp | 12 +- interface/src/avatar/MyAvatar.cpp | 4 +- interface/src/avatar/SkeletonModel.cpp | 26 ++-- interface/src/avatar/SkeletonModel.h | 4 +- interface/src/avatar/SoftAttachmentModel.cpp | 2 +- .../src/EntityTreeRenderer.cpp | 11 +- .../src/RenderableModelEntityItem.cpp | 76 +++------- .../src/RenderableModelEntityItem.h | 5 +- .../entities/src/EntityItemProperties.cpp | 4 +- libraries/entities/src/EntityItemProperties.h | 6 +- .../render-utils/src/MeshPartPayload.cpp | 23 +-- libraries/render-utils/src/MeshPartPayload.h | 8 +- libraries/render-utils/src/Model.cpp | 131 ++++++++---------- libraries/render-utils/src/Model.h | 50 +++---- 14 files changed, 158 insertions(+), 204 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 7b63b7fc5a..778f5af4c8 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -466,8 +466,8 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { * (1.0f - ((float)(now - getHead()->getLookingAtMeStarted())) / (LOOKING_AT_ME_DURATION * (float)USECS_PER_SECOND)); if (alpha > 0.0f) { - QSharedPointer geometry = _skeletonModel->getGeometry(); - if (geometry && geometry->isLoaded()) { + if (_skeletonModel->isLoaded()) { + const auto& geometry = _skeletonModel->getFBXGeometry(); const float DEFAULT_EYE_DIAMETER = 0.048f; // Typical human eye const float RADIUS_INCREMENT = 0.005f; batch.setModelTransform(Transform()); @@ -475,7 +475,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { glm::vec3 position = getHead()->getLeftEyePosition(); Transform transform; transform.setTranslation(position); - float eyeDiameter = geometry->getFBXGeometry().leftEyeSize; + float eyeDiameter = geometry.leftEyeSize; if (eyeDiameter == 0.0f) { eyeDiameter = DEFAULT_EYE_DIAMETER; } @@ -486,7 +486,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { position = getHead()->getRightEyePosition(); transform.setTranslation(position); - eyeDiameter = geometry->getFBXGeometry().rightEyeSize; + eyeDiameter = geometry.rightEyeSize; if (eyeDiameter == 0.0f) { eyeDiameter = DEFAULT_EYE_DIAMETER; } @@ -815,7 +815,7 @@ int Avatar::getJointIndex(const QString& name) const { Q_RETURN_ARG(int, result), Q_ARG(const QString&, name)); return result; } - return _skeletonModel->isActive() ? _skeletonModel->getGeometry()->getFBXGeometry().getJointIndex(name) : -1; + return _skeletonModel->isActive() ? _skeletonModel->getFBXGeometry().getJointIndex(name) : -1; } QStringList Avatar::getJointNames() const { @@ -825,7 +825,7 @@ QStringList Avatar::getJointNames() const { Q_RETURN_ARG(QStringList, result)); return result; } - return _skeletonModel->isActive() ? _skeletonModel->getGeometry()->getFBXGeometry().getJointNames() : QStringList(); + return _skeletonModel->isActive() ? _skeletonModel->getFBXGeometry().getJointNames() : QStringList(); } glm::vec3 Avatar::getJointPosition(int index) const { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8f11c635e9..c69a6a9ebd 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1270,8 +1270,8 @@ void MyAvatar::setVisibleInSceneIfReady(Model* model, render::ScenePointer scene void MyAvatar::initHeadBones() { int neckJointIndex = -1; - if (_skeletonModel->getGeometry()) { - neckJointIndex = _skeletonModel->getGeometry()->getFBXGeometry().neckJointIndex; + if (_skeletonModel->isLoaded()) { + neckJointIndex = _skeletonModel->getFBXGeometry().neckJointIndex; } if (neckJointIndex == -1) { return; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 8bb097d97e..7019944f3b 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -39,12 +39,12 @@ SkeletonModel::~SkeletonModel() { } void SkeletonModel::initJointStates() { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const FBXGeometry& geometry = getFBXGeometry(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); _rig->initJointStates(geometry, modelOffset); // Determine the default eye position for avatar scale = 1.0 - int headJointIndex = _geometry->getFBXGeometry().headJointIndex; + int headJointIndex = geometry.headJointIndex; if (0 > headJointIndex || headJointIndex >= _rig->getJointStateCount()) { qCWarning(interfaceapp) << "Bad head joint! Got:" << headJointIndex << "jointCount:" << _rig->getJointStateCount(); } @@ -52,7 +52,7 @@ void SkeletonModel::initJointStates() { getEyeModelPositions(leftEyePosition, rightEyePosition); glm::vec3 midEyePosition = (leftEyePosition + rightEyePosition) / 2.0f; - int rootJointIndex = _geometry->getFBXGeometry().rootJointIndex; + int rootJointIndex = geometry.rootJointIndex; glm::vec3 rootModelPosition; getJointPosition(rootJointIndex, rootModelPosition); @@ -87,10 +87,12 @@ Rig::CharacterControllerState convertCharacterControllerState(CharacterControlle // Called within Model::simulate call, below. void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { + const FBXGeometry& geometry = getFBXGeometry(); + Head* head = _owningAvatar->getHead(); + if (_owningAvatar->isMyAvatar()) { MyAvatar* myAvatar = static_cast(_owningAvatar); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); Rig::HeadParameters headParams; headParams.enableLean = qApp->isHMDMode(); @@ -183,7 +185,6 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // Thus this should really only be ... else if (_owningAvatar->getHead()->isLookingAtMe()) {... // However, in the !isLookingAtMe case, the eyes aren't rotating the way they should right now. // We will revisit that as priorities allow, and particularly after the new rig/animation/joints. - const FBXGeometry& geometry = _geometry->getFBXGeometry(); // If the head is not positioned, updateEyeJoints won't get the math right glm::quat headOrientation; @@ -329,22 +330,23 @@ float SkeletonModel::getRightArmLength() const { } bool SkeletonModel::getHeadPosition(glm::vec3& headPosition) const { - return isActive() && getJointPositionInWorldFrame(_geometry->getFBXGeometry().headJointIndex, headPosition); + return isActive() && getJointPositionInWorldFrame(getFBXGeometry().headJointIndex, headPosition); } bool SkeletonModel::getNeckPosition(glm::vec3& neckPosition) const { - return isActive() && getJointPositionInWorldFrame(_geometry->getFBXGeometry().neckJointIndex, neckPosition); + return isActive() && getJointPositionInWorldFrame(getFBXGeometry().neckJointIndex, neckPosition); } bool SkeletonModel::getLocalNeckPosition(glm::vec3& neckPosition) const { - return isActive() && getJointPosition(_geometry->getFBXGeometry().neckJointIndex, neckPosition); + return isActive() && getJointPosition(getFBXGeometry().neckJointIndex, neckPosition); } bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { if (!isActive()) { return false; } - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const FBXGeometry& geometry = getFBXGeometry(); + if (getJointPosition(geometry.leftEyeJointIndex, firstEyePosition) && getJointPosition(geometry.rightEyeJointIndex, secondEyePosition)) { return true; @@ -386,11 +388,11 @@ float VERY_BIG_MASS = 1.0e6f; // virtual void SkeletonModel::computeBoundingShape() { - if (_geometry == NULL || _rig->jointStatesEmpty()) { + if (!isLoaded() || _rig->jointStatesEmpty()) { return; } - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const FBXGeometry& geometry = getFBXGeometry(); if (geometry.joints.isEmpty() || geometry.rootJointIndex == -1) { // rootJointIndex == -1 if the avatar model has no skeleton return; @@ -429,7 +431,7 @@ void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float scale } bool SkeletonModel::hasSkeleton() { - return isActive() ? _geometry->getFBXGeometry().rootJointIndex != -1 : false; + return isActive() ? getFBXGeometry().rootJointIndex != -1 : false; } void SkeletonModel::onInvalidate() { diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 26b2e9c666..8e61e6f3ca 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -38,10 +38,10 @@ public: void updateAttitude(); /// Returns the index of the left hand joint, or -1 if not found. - int getLeftHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().leftHandJointIndex : -1; } + int getLeftHandJointIndex() const { return isActive() ? getFBXGeometry().leftHandJointIndex : -1; } /// Returns the index of the right hand joint, or -1 if not found. - int getRightHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().rightHandJointIndex : -1; } + int getRightHandJointIndex() const { return isActive() ? getFBXGeometry().rightHandJointIndex : -1; } bool getLeftGrabPosition(glm::vec3& position) const; bool getRightGrabPosition(glm::vec3& position) const; diff --git a/interface/src/avatar/SoftAttachmentModel.cpp b/interface/src/avatar/SoftAttachmentModel.cpp index 8f0404e36c..b90fd9470c 100644 --- a/interface/src/avatar/SoftAttachmentModel.cpp +++ b/interface/src/avatar/SoftAttachmentModel.cpp @@ -43,7 +43,7 @@ void SoftAttachmentModel::updateClusterMatrices(glm::vec3 modelPosition, glm::qu } _needsUpdateClusterMatrices = false; - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const FBXGeometry& geometry = getFBXGeometry(); glm::mat4 modelToWorld = glm::mat4_cast(modelOrientation); for (int i = 0; i < _meshStates.size(); i++) { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 22695663e3..07d19dbd92 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -421,8 +421,8 @@ const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer en std::dynamic_pointer_cast(entityItem); assert(modelEntityItem); // we need this!!! ModelPointer model = modelEntityItem->getModel(this); - if (model) { - result = &model->getGeometry()->getFBXGeometry(); + if (model && model->isLoaded()) { + result = &model->getFBXGeometry(); } } return result; @@ -446,11 +446,8 @@ const FBXGeometry* EntityTreeRenderer::getCollisionGeometryForEntity(EntityItemP std::dynamic_pointer_cast(entityItem); if (modelEntityItem->hasCompoundShapeURL()) { ModelPointer model = modelEntityItem->getModel(this); - if (model) { - const QSharedPointer collisionNetworkGeometry = model->getCollisionGeometry(); - if (collisionNetworkGeometry && collisionNetworkGeometry->isLoaded()) { - result = &collisionNetworkGeometry->getFBXGeometry(); - } + if (model && model->isCollisionLoaded()) { + result = &model->getCollisionFBXGeometry(); } } } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index ff4ed28150..d6fbdd5229 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -110,14 +110,16 @@ int RenderableModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned c QVariantMap RenderableModelEntityItem::parseTexturesToMap(QString textures) { // If textures are unset, revert to original textures if (textures == "") { - return _originalTexturesMap; + return _originalTextures; } + // TODO: Remove this line and enforce passing a texturemap as stringified JSON QString jsonTextures = "{\"" + textures.replace(":\"", "\":\"").replace(",\n", ",\"") + "}"; QJsonParseError error; QJsonDocument texturesAsJson = QJsonDocument::fromJson(jsonTextures.toUtf8(), &error); if (error.error != QJsonParseError::NoError) { qCWarning(entitiesrenderer) << "Could not evaluate textures property value:" << _textures; + return _originalTextures; } QJsonObject texturesAsJsonObject = texturesAsJson.object(); return texturesAsJsonObject.toVariantMap(); @@ -131,44 +133,23 @@ void RenderableModelEntityItem::remapTextures() { if (!_model->isLoaded()) { return; // nothing to do if the model has not yet loaded } - + + auto& geometry = _model->getGeometry()->getGeometry(); + if (!_originalTexturesRead) { - const QSharedPointer& networkGeometry = _model->getGeometry(); - if (networkGeometry) { - _originalTextures = networkGeometry->getTextureNames(); - _originalTexturesMap = parseTexturesToMap(_originalTextures.join(",\n")); - _originalTexturesRead = true; - } - } - - if (_currentTextures == _textures) { - return; // nothing to do if our recently mapped textures match our desired textures - } - - // since we're changing here, we need to run through our current texture map - // and any textures in the recently mapped texture, that is not in our desired - // textures, we need to "unset" - QVariantMap currentTextureMap = parseTexturesToMap(_currentTextures); - QVariantMap textureMap = parseTexturesToMap(_textures); + _originalTextures = geometry->getTextures(); + _originalTexturesRead = true; - foreach(const QString& key, currentTextureMap.keys()) { - // if the desired texture map (what we're setting the textures to) doesn't - // contain this texture, then remove it by setting the URL to null - if (!textureMap.contains(key)) { - QUrl noURL; - qCDebug(entitiesrenderer) << "Removing texture named" << key << "by replacing it with no URL"; - _model->setTextureWithNameToURL(key, noURL); - } + // Default to _originalTextures to avoid remapping immediately and lagging on load + _currentTextures = _originalTextures; } - // here's where we remap any textures if needed... - foreach(const QString& key, textureMap.keys()) { - QUrl newTextureURL = textureMap[key].toUrl(); - qCDebug(entitiesrenderer) << "Updating texture named" << key << "to texture at URL" << newTextureURL; - _model->setTextureWithNameToURL(key, newTextureURL); + auto textures = parseTexturesToMap(_textures); + + if (textures != _currentTextures) { + geometry->setTextures(textures); + _currentTextures = textures; } - - _currentTextures = _textures; } // TODO: we need a solution for changes to the postion/rotation/etc of a model... @@ -519,8 +500,7 @@ bool RenderableModelEntityItem::needsToCallUpdate() const { void RenderableModelEntityItem::update(const quint64& now) { if (!_dimensionsInitialized && _model && _model->isActive()) { - const QSharedPointer renderNetworkGeometry = _model->getGeometry(); - if (renderNetworkGeometry && renderNetworkGeometry->isLoaded()) { + if (_model->isLoaded()) { EntityItemProperties properties; auto extents = _model->getMeshExtents(); properties.setDimensions(extents.maximum - extents.minimum); @@ -586,13 +566,8 @@ bool RenderableModelEntityItem::isReadyToComputeShape() { return false; } - const QSharedPointer collisionNetworkGeometry = _model->getCollisionGeometry(); - const QSharedPointer renderNetworkGeometry = _model->getGeometry(); - - if ((collisionNetworkGeometry && collisionNetworkGeometry->isLoaded()) && - (renderNetworkGeometry && renderNetworkGeometry->isLoaded())) { + if (_model->isLoaded() && _model->isCollisionLoaded()) { // we have both URLs AND both geometries AND they are both fully loaded. - if (_needsInitialSimulation) { // the _model's offset will be wrong until _needsInitialSimulation is false PerformanceTimer perfTimer("_model->simulate"); @@ -617,15 +592,12 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { adjustShapeInfoByRegistration(info); } else { updateModelBounds(); - const QSharedPointer collisionNetworkGeometry = _model->getCollisionGeometry(); // should never fall in here when collision model not fully loaded - // hence we assert collisionNetworkGeometry is not NULL - assert(collisionNetworkGeometry); - - const FBXGeometry& collisionGeometry = collisionNetworkGeometry->getFBXGeometry(); - const QSharedPointer renderNetworkGeometry = _model->getGeometry(); - const FBXGeometry& renderGeometry = renderNetworkGeometry->getFBXGeometry(); + // hence we assert that all geometries exist and are loaded + assert(_model->isLoaded() && _model->isCollisionLoaded()); + const FBXGeometry& renderGeometry = _model->getFBXGeometry(); + const FBXGeometry& collisionGeometry = _model->getCollisionFBXGeometry(); _points.clear(); unsigned int i = 0; @@ -727,10 +699,8 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { } bool RenderableModelEntityItem::contains(const glm::vec3& point) const { - if (EntityItem::contains(point) && _model && _model->getCollisionGeometry()) { - const QSharedPointer collisionNetworkGeometry = _model->getCollisionGeometry(); - const FBXGeometry& collisionGeometry = collisionNetworkGeometry->getFBXGeometry(); - return collisionGeometry.convexHullContains(worldToEntity(point)); + if (EntityItem::contains(point) && _model && _model->isCollisionLoaded()) { + return _model->getCollisionFBXGeometry().convexHullContains(worldToEntity(point)); } return false; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 069b7385b5..03aa9124d5 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -85,9 +85,8 @@ private: bool _needsInitialSimulation = true; bool _needsModelReload = true; EntityTreeRenderer* _myRenderer = nullptr; - QString _currentTextures; - QStringList _originalTextures; - QVariantMap _originalTexturesMap; + QVariantMap _currentTextures; + QVariantMap _originalTextures; bool _originalTexturesRead = false; QVector> _points; bool _dimensionsInitialized = true; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 550ec205c0..32aac1efe5 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -529,9 +529,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(boundingBox, boundingBox); // gettable, but not settable } - QString textureNamesList = _textureNames.join(",\n"); + QString textureNamesStr = QJsonDocument::fromVariant(_textureNames).toJson(); if (!skipDefaults) { - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(originalTextures, textureNamesList); // gettable, but not settable + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(originalTextures, textureNamesStr); // gettable, but not settable } COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARENT_ID, parentID); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index c732d01fa5..2cf31e5632 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -245,8 +245,8 @@ public: const glm::vec3& getNaturalPosition() const { return _naturalPosition; } void calculateNaturalPosition(const glm::vec3& min, const glm::vec3& max); - const QStringList& getTextureNames() const { return _textureNames; } - void setTextureNames(const QStringList& value) { _textureNames = value; } + const QVariantMap& getTextureNames() const { return _textureNames; } + void setTextureNames(const QVariantMap& value) { _textureNames = value; } QString getSimulatorIDAsString() const { return _simulationOwner.getID().toString().mid(1,36).toUpper(); } @@ -297,7 +297,7 @@ private: // NOTE: The following are pseudo client only properties. They are only used in clients which can access // properties of model geometry. But these properties are not serialized like other properties. QVector _sittingPoints; - QStringList _textureNames; + QVariantMap _textureNames; glm::vec3 _naturalDimensions; glm::vec3 _naturalPosition; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 363def05a1..854630abf0 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -45,14 +45,14 @@ template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderAr } } -MeshPartPayload::MeshPartPayload(model::MeshPointer mesh, int partIndex, model::MaterialPointer material, const Transform& transform, const Transform& offsetTransform) { +MeshPartPayload::MeshPartPayload(const std::shared_ptr& mesh, int partIndex, model::MaterialPointer material, const Transform& transform, const Transform& offsetTransform) { updateMeshPart(mesh, partIndex); updateMaterial(material); updateTransform(transform, offsetTransform); } -void MeshPartPayload::updateMeshPart(model::MeshPointer drawMesh, int partIndex) { +void MeshPartPayload::updateMeshPart(const std::shared_ptr& drawMesh, int partIndex) { _drawMesh = drawMesh; if (_drawMesh) { auto vertexFormat = _drawMesh->getVertexFormat(); @@ -320,7 +320,9 @@ ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int par _model(model), _meshIndex(_meshIndex), _shapeID(shapeIndex) { - auto& modelMesh = _model->_geometry->getMeshes().at(_meshIndex)->_mesh; + + assert(_model && _model->isLoaded()); + auto& modelMesh = _model->getGeometry()->getGeometry()->getMeshes().at(_meshIndex); updateMeshPart(modelMesh, partIndex); updateTransform(transform, offsetTransform); @@ -328,20 +330,22 @@ ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int par } void ModelMeshPartPayload::initCache() { + assert(_model->isLoaded()); + if (_drawMesh) { auto vertexFormat = _drawMesh->getVertexFormat(); _hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR); _isSkinned = vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT) && vertexFormat->hasAttribute(gpu::Stream::SKIN_CLUSTER_INDEX); - - const FBXGeometry& geometry = _model->_geometry->getFBXGeometry(); + const FBXGeometry& geometry = _model->getFBXGeometry(); const FBXMesh& mesh = geometry.meshes.at(_meshIndex); + _isBlendShaped = !mesh.blendshapes.isEmpty(); } - auto networkMaterial = _model->_geometry->getShapeMaterial(_shapeID); + auto networkMaterial = _model->getGeometry()->getGeometry()->getShapeMaterial(_shapeID); if (networkMaterial) { - _drawMaterial = networkMaterial->_material; + _drawMaterial = networkMaterial; }; } @@ -380,8 +384,9 @@ Item::Bound ModelMeshPartPayload::getBound() const { } ShapeKey ModelMeshPartPayload::getShapeKey() const { - const FBXGeometry& geometry = _model->_geometry->getFBXGeometry(); - const std::vector>& networkMeshes = _model->_geometry->getMeshes(); + assert(_model->isLoaded()); + const FBXGeometry& geometry = _model->getFBXGeometry(); + const auto& networkMeshes = _model->getGeometry()->getGeometry()->getMeshes(); // guard against partially loaded meshes if (_meshIndex >= (int)networkMeshes.size() || _meshIndex >= (int)geometry.meshes.size() || _meshIndex >= (int)_model->_meshStates.size()) { diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index feccf7ee64..9f73a46a93 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -24,12 +24,12 @@ class Model; class MeshPartPayload { public: MeshPartPayload() {} - MeshPartPayload(model::MeshPointer mesh, int partIndex, model::MaterialPointer material, const Transform& transform, const Transform& offsetTransform); + MeshPartPayload(const std::shared_ptr& mesh, int partIndex, model::MaterialPointer material, const Transform& transform, const Transform& offsetTransform); typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - virtual void updateMeshPart(model::MeshPointer drawMesh, int partIndex); + virtual void updateMeshPart(const std::shared_ptr& drawMesh, int partIndex); virtual void notifyLocationChanged() {} virtual void updateTransform(const Transform& transform, const Transform& offsetTransform); @@ -49,11 +49,11 @@ public: virtual void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, bool canCauterize = true) const; // Payload resource cached values - model::MeshPointer _drawMesh; + std::shared_ptr _drawMesh; int _partIndex = 0; model::Mesh::Part _drawPart; - model::MaterialPointer _drawMaterial; + std::shared_ptr _drawMaterial; model::Box _localBound; Transform _drawTransform; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 0a483914df..09cf433d47 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -30,7 +30,7 @@ using namespace std; static int nakedModelPointerTypeId = qRegisterMetaType(); -static int weakNetworkGeometryPointerTypeId = qRegisterMetaType >(); +static int weakNetworkGeometryPointerTypeId = qRegisterMetaType >(); static int vec3VectorTypeId = qRegisterMetaType >(); float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f; #define HTTP_INVALID_COM "http://invalid.com" @@ -74,12 +74,12 @@ Model::~Model() { AbstractViewStateInterface* Model::_viewState = NULL; -bool Model::needsFixupInScene() { +bool Model::needsFixupInScene() const { if (readyToAddToScene()) { // Once textures are loaded, fixup if they are now transparent - if (!_needsReload && _needsUpdateTransparentTextures && _geometry->isLoadedWithTextures()) { + if (_needsUpdateTransparentTextures && _geometry->getGeometry()->areTexturesLoaded()) { _needsUpdateTransparentTextures = false; - if (_hasTransparentTextures != _geometry->hasTransparentTextures()) { + if (_hasTransparentTextures != _geometry->getGeometry()->hasTransparentTextures()) { return true; } } @@ -150,19 +150,18 @@ void Model::enqueueLocationChange() { } void Model::initJointTransforms() { - if (!_geometry || !_geometry->isLoaded()) { - return; + if (isLoaded()) { + glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); + _rig->setModelOffset(modelOffset); } - glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); - _rig->setModelOffset(modelOffset); } void Model::init() { } void Model::reset() { - if (_geometry && _geometry->isLoaded()) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + if (isLoaded()) { + const FBXGeometry& geometry = getFBXGeometry(); _rig->reset(geometry); } } @@ -171,17 +170,16 @@ bool Model::updateGeometry() { PROFILE_RANGE(__FUNCTION__); bool needFullUpdate = false; - if (!_geometry || !_geometry->isLoaded()) { - // geometry is not ready + if (!isLoaded()) { return false; } _needsReload = false; - if (_rig->jointStatesEmpty() && _geometry->getFBXGeometry().joints.size() > 0) { + if (_rig->jointStatesEmpty() && getFBXGeometry().joints.size() > 0) { initJointStates(); - const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry(); + const FBXGeometry& fbxGeometry = getFBXGeometry(); foreach (const FBXMesh& mesh, fbxGeometry.meshes) { MeshState state; state.clusterMatrices.resize(mesh.clusters.size()); @@ -205,7 +203,7 @@ bool Model::updateGeometry() { // virtual void Model::initJointStates() { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const FBXGeometry& geometry = getFBXGeometry(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); _rig->initJointStates(geometry, modelOffset); @@ -248,7 +246,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g glm::vec3 subMeshSurfaceNormal; int subMeshIndex = 0; - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const FBXGeometry& geometry = getFBXGeometry(); // If we hit the models box, then consider the submeshes... _mutex.lock(); @@ -367,7 +365,7 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { bool calculatedMeshTrianglesNeeded = pickAgainstTriangles && !_calculatedMeshTrianglesValid; if (!_calculatedMeshBoxesValid || calculatedMeshTrianglesNeeded || (!_calculatedMeshPartBoxesValid && pickAgainstTriangles) ) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const FBXGeometry& geometry = getFBXGeometry(); int numberOfMeshes = geometry.meshes.size(); _calculatedMeshBoxes.resize(numberOfMeshes); _calculatedMeshTriangles.clear(); @@ -478,7 +476,7 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { void Model::renderSetup(RenderArgs* args) { // set up dilated textures on first render after load/simulate - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const FBXGeometry& geometry = getFBXGeometry(); if (_dilatedTextures.isEmpty()) { foreach (const FBXMesh& mesh, geometry.meshes) { QVector > dilated; @@ -627,7 +625,7 @@ Extents Model::getBindExtents() const { if (!isActive()) { return Extents(); } - const Extents& bindExtents = _geometry->getFBXGeometry().bindExtents; + const Extents& bindExtents = getFBXGeometry().bindExtents; Extents scaledExtents = { bindExtents.minimum * _scale, bindExtents.maximum * _scale }; return scaledExtents; } @@ -636,12 +634,12 @@ Extents Model::getMeshExtents() const { if (!isActive()) { return Extents(); } - const Extents& extents = _geometry->getFBXGeometry().meshExtents; + const Extents& extents = getFBXGeometry().meshExtents; // even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which // is captured in the offset matrix - glm::vec3 minimum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f)); - glm::vec3 maximum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f)); + glm::vec3 minimum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f)); + glm::vec3 maximum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f)); Extents scaledExtents = { minimum * _scale, maximum * _scale }; return scaledExtents; } @@ -651,12 +649,12 @@ Extents Model::getUnscaledMeshExtents() const { return Extents(); } - const Extents& extents = _geometry->getFBXGeometry().meshExtents; + const Extents& extents = getFBXGeometry().meshExtents; // even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which // is captured in the offset matrix - glm::vec3 minimum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f)); - glm::vec3 maximum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f)); + glm::vec3 minimum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f)); + glm::vec3 maximum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f)); Extents scaledExtents = { minimum, maximum }; return scaledExtents; @@ -665,8 +663,8 @@ Extents Model::getUnscaledMeshExtents() const { Extents Model::calculateScaledOffsetExtents(const Extents& extents, glm::vec3 modelPosition, glm::quat modelOrientation) const { // we need to include any fst scaling, translation, and rotation, which is captured in the offset matrix - glm::vec3 minimum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f)); - glm::vec3 maximum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f)); + glm::vec3 minimum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f)); + glm::vec3 maximum = glm::vec3(getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f)); Extents scaledOffsetExtents = { ((minimum + _offset) * _scale), ((maximum + _offset) * _scale) }; @@ -686,7 +684,7 @@ AABox Model::calculateScaledOffsetAABox(const AABox& box, glm::vec3 modelPositio glm::vec3 Model::calculateScaledOffsetPoint(const glm::vec3& point) const { // we need to include any fst scaling, translation, and rotation, which is captured in the offset matrix - glm::vec3 offsetPoint = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(point, 1.0f)); + glm::vec3 offsetPoint = glm::vec3(getFBXGeometry().offset * glm::vec4(point, 1.0f)); glm::vec3 scaledPoint = ((offsetPoint + _offset) * _scale); glm::vec3 rotatedPoint = _rotation * scaledPoint; glm::vec3 translatedPoint = rotatedPoint + _translation; @@ -714,11 +712,11 @@ void Model::setJointTranslation(int index, bool valid, const glm::vec3& translat } int Model::getParentJointIndex(int jointIndex) const { - return (isActive() && jointIndex != -1) ? _geometry->getFBXGeometry().joints.at(jointIndex).parentIndex : -1; + return (isActive() && jointIndex != -1) ? getFBXGeometry().joints.at(jointIndex).parentIndex : -1; } int Model::getLastFreeJointIndex(int jointIndex) const { - return (isActive() && jointIndex != -1) ? _geometry->getFBXGeometry().joints.at(jointIndex).freeLineage.last() : -1; + return (isActive() && jointIndex != -1) ? getFBXGeometry().joints.at(jointIndex).freeLineage.last() : -1; } void Model::setURL(const QUrl& url) { @@ -743,29 +741,16 @@ void Model::setURL(const QUrl& url) { invalidCalculatedMeshBoxes(); deleteGeometry(); - _geometry.reset(new NetworkGeometry(url, false, QVariantHash())); + _geometry = DependencyManager::get()->getGeometry(url); onInvalidate(); } -const QSharedPointer Model::getCollisionGeometry(bool delayLoad) -{ - if (_collisionGeometry.isNull() && !_collisionUrl.isEmpty()) { - _collisionGeometry.reset(new NetworkGeometry(_collisionUrl, delayLoad, QVariantHash())); - } - - if (_collisionGeometry && _collisionGeometry->isLoaded()) { - return _collisionGeometry; - } - - return QSharedPointer(); -} - void Model::setCollisionModelURL(const QUrl& url) { if (_collisionUrl == url) { return; } _collisionUrl = url; - _collisionGeometry.reset(new NetworkGeometry(url, false, QVariantHash())); + _collisionGeometry = DependencyManager::get()->getGeometry(url); } bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { @@ -815,13 +800,13 @@ QStringList Model::getJointNames() const { Q_RETURN_ARG(QStringList, result)); return result; } - return isActive() ? _geometry->getFBXGeometry().getJointNames() : QStringList(); + return isActive() ? getFBXGeometry().getJointNames() : QStringList(); } class Blender : public QRunnable { public: - Blender(ModelPointer model, int blendNumber, const QWeakPointer& geometry, + Blender(ModelPointer model, int blendNumber, const std::weak_ptr& geometry, const QVector& meshes, const QVector& blendshapeCoefficients); virtual void run(); @@ -830,12 +815,12 @@ private: ModelPointer _model; int _blendNumber; - QWeakPointer _geometry; + std::weak_ptr _geometry; QVector _meshes; QVector _blendshapeCoefficients; }; -Blender::Blender(ModelPointer model, int blendNumber, const QWeakPointer& geometry, +Blender::Blender(ModelPointer model, int blendNumber, const std::weak_ptr& geometry, const QVector& meshes, const QVector& blendshapeCoefficients) : _model(model), _blendNumber(blendNumber), @@ -878,7 +863,7 @@ void Blender::run() { // post the result to the geometry cache, which will dispatch to the model if still alive QMetaObject::invokeMethod(DependencyManager::get().data(), "setBlendedVertices", Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), - Q_ARG(const QWeakPointer&, _geometry), Q_ARG(const QVector&, vertices), + Q_ARG(const std::weak_ptr&, _geometry), Q_ARG(const QVector&, vertices), Q_ARG(const QVector&, normals)); } @@ -1010,7 +995,7 @@ void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrient return; } _needsUpdateClusterMatrices = false; - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const FBXGeometry& geometry = getFBXGeometry(); glm::mat4 zeroScale(glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), @@ -1067,26 +1052,26 @@ void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrient } void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const FBXGeometry& geometry = getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(endIndex).freeLineage; glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset); _rig->inverseKinematics(endIndex, targetPosition, targetRotation, priority, freeLineage, parentTransform); } bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const FBXGeometry& geometry = getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; return _rig->restoreJointPosition(jointIndex, fraction, priority, freeLineage); } float Model::getLimbLength(int jointIndex) const { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const FBXGeometry& geometry = getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; return _rig->getLimbLength(jointIndex, freeLineage, _scale, geometry.joints); } bool Model::maybeStartBlender() { - const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry(); + const FBXGeometry& fbxGeometry = getFBXGeometry(); if (fbxGeometry.hasBlendedMeshes()) { QThreadPool::globalInstance()->start(new Blender(getThisPointer(), ++_blendNumber, _geometry, fbxGeometry.meshes, _blendshapeCoefficients)); @@ -1095,13 +1080,13 @@ bool Model::maybeStartBlender() { return false; } -void Model::setBlendedVertices(int blendNumber, const QWeakPointer& geometry, +void Model::setBlendedVertices(int blendNumber, const std::weak_ptr& geometry, const QVector& vertices, const QVector& normals) { - if (_geometry != geometry || _blendedVertexBuffers.empty() || blendNumber < _appliedBlendNumber) { + if (_geometry != geometry.lock() || _blendedVertexBuffers.empty() || blendNumber < _appliedBlendNumber) { return; } _appliedBlendNumber = blendNumber; - const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry(); + const FBXGeometry& fbxGeometry = getFBXGeometry(); int index = 0; for (int i = 0; i < fbxGeometry.meshes.size(); i++) { const FBXMesh& mesh = fbxGeometry.meshes.at(i); @@ -1118,13 +1103,6 @@ void Model::setBlendedVertices(int blendNumber, const QWeakPointer& newGeometry) { - if (_geometry == newGeometry) { - return; - } - _geometry = newGeometry; -} - void Model::deleteGeometry() { _blendedVertexBuffers.clear(); _meshStates.clear(); @@ -1134,7 +1112,7 @@ void Model::deleteGeometry() { AABox Model::getPartBounds(int meshIndex, int partIndex, glm::vec3 modelPosition, glm::quat modelOrientation) const { - if (!_geometry || !_geometry->isLoaded()) { + if (!isLoaded()) { return AABox(); } @@ -1143,10 +1121,10 @@ AABox Model::getPartBounds(int meshIndex, int partIndex, glm::vec3 modelPosition bool isSkinned = state.clusterMatrices.size() > 1; if (isSkinned) { // if we're skinned return the entire mesh extents because we can't know for sure our clusters don't move us - return calculateScaledOffsetAABox(_geometry->getFBXGeometry().meshExtents, modelPosition, modelOrientation); + return calculateScaledOffsetAABox(getFBXGeometry().meshExtents, modelPosition, modelOrientation); } } - if (_geometry->getFBXGeometry().meshes.size() > meshIndex) { + if (getFBXGeometry().meshes.size() > meshIndex) { // FIX ME! - This is currently a hack because for some mesh parts our efforts to calculate the bounding // box of the mesh part fails. It seems to create boxes that are not consistent with where the @@ -1160,27 +1138,28 @@ AABox Model::getPartBounds(int meshIndex, int partIndex, glm::vec3 modelPosition // return _calculatedMeshBoxes[meshIndex]; // // If we not skinned use the bounds of the subMesh for all it's parts - const FBXMesh& mesh = _geometry->getFBXGeometry().meshes.at(meshIndex); + const FBXMesh& mesh = getFBXGeometry().meshes.at(meshIndex); return calculateScaledOffsetExtents(mesh.meshExtents, modelPosition, modelOrientation); } return AABox(); } void Model::segregateMeshGroups() { - QSharedPointer networkGeometry; + NetworkGeometry::Pointer networkGeometry; bool showingCollisionHull = false; if (_showCollisionHull && _collisionGeometry) { - if (_collisionGeometry->isLoaded()) { + if (isCollisionLoaded()) { networkGeometry = _collisionGeometry; showingCollisionHull = true; } else { return; } } else { + assert(isLoaded()); networkGeometry = _geometry; } - const FBXGeometry& geometry = networkGeometry->getFBXGeometry(); - const std::vector>& networkMeshes = networkGeometry->getMeshes(); + const FBXGeometry& geometry = networkGeometry->getGeometry()->getGeometry(); + const auto& networkMeshes = networkGeometry->getGeometry()->getMeshes(); // all of our mesh vectors must match in size auto geoMeshesSize = geometry.meshes.size(); @@ -1208,7 +1187,7 @@ void Model::segregateMeshGroups() { int shapeID = 0; for (int i = 0; i < (int)networkMeshes.size(); i++) { const FBXMesh& mesh = geometry.meshes.at(i); - const NetworkMesh& networkMesh = *(networkMeshes.at(i).get()); + const auto& networkMesh = networkMeshes.at(i); // Create the render payloads int totalParts = mesh.parts.size(); @@ -1220,7 +1199,7 @@ void Model::segregateMeshGroups() { _collisionHullMaterial->setMetallic(0.02f); _collisionHullMaterial->setRoughness(0.5f); } - _renderItemsSet << std::make_shared(networkMesh._mesh, partIndex, _collisionHullMaterial, transform, offset); + _renderItemsSet << std::make_shared(networkMesh, partIndex, _collisionHullMaterial, transform, offset); } else { _renderItemsSet << std::make_shared(this, i, partIndex, shapeID, transform, offset); } @@ -1285,7 +1264,7 @@ void ModelBlender::noteRequiresBlend(ModelPointer model) { } void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, - const QWeakPointer& geometry, const QVector& vertices, const QVector& normals) { + const std::weak_ptr& geometry, const QVector& vertices, const QVector& normals) { if (model) { model->setBlendedVertices(blendNumber, geometry, vertices, normals); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 4e51dc4f33..632e8089a8 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -78,9 +78,9 @@ public: // new Scene/Engine rendering support void setVisibleInScene(bool newValue, std::shared_ptr scene); - bool needsFixupInScene(); - bool readyToAddToScene(RenderArgs* renderArgs = nullptr) { - return !_needsReload && isRenderable() && isActive() && isLoaded(); + bool needsFixupInScene() const; + bool readyToAddToScene(RenderArgs* renderArgs = nullptr) const { + return !_needsReload && isRenderable() && isActive(); } bool initWhenReady(render::ScenePointer scene); bool addToScene(std::shared_ptr scene, @@ -92,7 +92,7 @@ public: bool showCollisionHull = false); void removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); void renderSetup(RenderArgs* args); - bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _geometry->getMeshes().empty()); } + bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && getGeometry()->getGeometry()->getMeshes().empty()); } bool isVisible() const { return _isVisible; } @@ -102,11 +102,11 @@ public: bool maybeStartBlender(); /// Sets blended vertices computed in a separate thread. - void setBlendedVertices(int blendNumber, const QWeakPointer& geometry, + void setBlendedVertices(int blendNumber, const std::weak_ptr& geometry, const QVector& vertices, const QVector& normals); - bool isLoaded() const { return _geometry && _geometry->isLoaded(); } - bool isLoadedWithTextures() const { return _geometry && _geometry->isLoadedWithTextures(); } + bool isLoaded() const { return _geometry && _geometry->getGeometry(); } + bool isCollisionLoaded() const { return _collisionGeometry && _collisionGeometry->getGeometry(); } void setIsWireframe(bool isWireframe) { _isWireframe = isWireframe; } bool isWireframe() const { return _isWireframe; } @@ -123,12 +123,22 @@ public: virtual void updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation); /// Returns a reference to the shared geometry. - const QSharedPointer& getGeometry() const { return _geometry; } + const NetworkGeometry::Pointer& getGeometry() const { return _geometry; } + /// Returns a reference to the shared collision geometry. + const NetworkGeometry::Pointer& getCollisionGeometry() const { return _collisionGeometry; } - bool isActive() const { return _geometry && _geometry->isLoaded(); } + /// Provided as a convenience, will crash if !isLoaded() + // And so that getGeometry() isn't chained everywhere + const FBXGeometry& getFBXGeometry() const { assert(isLoaded()); return getGeometry()->getGeometry()->getGeometry(); } + /// Provided as a convenience, will crash if !isCollisionLoaded() + const FBXGeometry& getCollisionFBXGeometry() const { assert(isCollisionLoaded()); return getCollisionGeometry()->getGeometry()->getGeometry(); } - Q_INVOKABLE void setTextureWithNameToURL(const QString& name, const QUrl& url) - { _geometry->setTextureWithNameToURL(name, url); } + // Set the model to use for collisions + Q_INVOKABLE void setCollisionModelURL(const QUrl& url); + const QUrl& getCollisionURL() const { return _collisionUrl; } + + + bool isActive() const { return isLoaded(); } bool convexHullContains(glm::vec3 point); @@ -143,13 +153,6 @@ public: BoxFace& face, glm::vec3& surfaceNormal, QString& extraInfo, bool pickAgainstTriangles = false); - // Set the model to use for collisions - Q_INVOKABLE void setCollisionModelURL(const QUrl& url); - const QUrl& getCollisionURL() const { return _collisionUrl; } - - /// Returns a reference to the shared collision geometry. - const QSharedPointer getCollisionGeometry(bool delayLoad = false); - void setOffset(const glm::vec3& offset); const glm::vec3& getOffset() const { return _offset; } @@ -257,8 +260,7 @@ protected: /// \return true if joint exists bool getJointPosition(int jointIndex, glm::vec3& position) const; - QSharedPointer _geometry; - void setGeometry(const QSharedPointer& newGeometry); + NetworkGeometry::Pointer _geometry; glm::vec3 _translation; glm::quat _rotation; @@ -325,7 +327,7 @@ protected: void deleteGeometry(); void initJointTransforms(); - QSharedPointer _collisionGeometry; + NetworkGeometry::Pointer _collisionGeometry; float _pupilDilation; QVector _blendshapeCoefficients; @@ -376,7 +378,7 @@ protected: bool _readyWhenAdded { false }; bool _needsReload { true }; bool _needsUpdateClusterMatrices { true }; - bool _needsUpdateTransparentTextures { true }; + mutable bool _needsUpdateTransparentTextures { true }; bool _hasTransparentTextures { false }; bool _showCollisionHull { false }; @@ -385,7 +387,7 @@ protected: }; Q_DECLARE_METATYPE(ModelPointer) -Q_DECLARE_METATYPE(QWeakPointer) +Q_DECLARE_METATYPE(std::weak_ptr) /// Handle management of pending models that need blending class ModelBlender : public QObject, public Dependency { @@ -398,7 +400,7 @@ public: void noteRequiresBlend(ModelPointer model); public slots: - void setBlendedVertices(ModelPointer model, int blendNumber, const QWeakPointer& geometry, + void setBlendedVertices(ModelPointer model, int blendNumber, const std::weak_ptr& geometry, const QVector& vertices, const QVector& normals); private: From 4d47126ae4a993e9b420f988feef5a3fdf7f2eba Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 24 Mar 2016 12:17:55 -0700 Subject: [PATCH 04/15] Don't mark failed models loaded, emit failure --- .../model-networking/src/model-networking/ModelCache.cpp | 9 ++++++--- .../model-networking/src/model-networking/ModelCache.h | 6 +++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index bb7efb8f75..ce255f0b4e 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -290,12 +290,15 @@ NetworkGeometry::NetworkGeometry(const GeometryResource::Pointer& networkGeometr connect(_resource.data(), &Resource::finished, this, &NetworkGeometry::resourceFinished); connect(_resource.data(), &Resource::onRefresh, this, &NetworkGeometry::resourceRefreshed); if (_resource->isLoaded()) { - resourceFinished(); + resourceFinished(true); } } -void NetworkGeometry::resourceFinished() { - _instance = std::make_shared(*_resource); +void NetworkGeometry::resourceFinished(bool success) { + if (success) { + _instance = std::make_shared(*_resource); + } + emit finished(success); } void NetworkGeometry::resourceRefreshed() { diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index a2fcc9d741..7da3e2894f 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -117,8 +117,12 @@ public: /// Returns the geometry, if it is loaded (must be checked!) const Geometry::Pointer& getGeometry() { return _instance; } +signals: + /// Emitted when the NetworkGeometry loads (or fails to) + void finished(bool success); + private slots: - void resourceFinished(); + void resourceFinished(bool success); void resourceRefreshed(); private: From aa038bac58a282619dceb35e1bbac5dfb86ecb99 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 24 Mar 2016 14:26:03 -0700 Subject: [PATCH 05/15] Don't cache nested geometries --- .../src/model-networking/ModelCache.cpp | 12 ++++++++++-- .../src/model-networking/ModelCache.h | 6 +++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index ce255f0b4e..dfb3cf60c1 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -67,7 +67,15 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) { // Get the raw GeometryResource, not the wrapped NetworkGeometry _geometryResource = modelCache->getResource(url, QUrl(), true, &extra).staticCast(); - connect(_geometryResource.data(), &Resource::finished, this, &GeometryMappingResource::onGeometryMappingLoaded); + + if (_geometryResource->isLoaded()) { + onGeometryMappingLoaded(!_geometryResource->getURL().isEmpty()); + } else { + connect(_geometryResource.data(), &Resource::finished, this, &GeometryMappingResource::onGeometryMappingLoaded); + } + + // Avoid caching nested resources - their references will be held by the parent + _geometryResource->_isCacheable = false; } } @@ -290,7 +298,7 @@ NetworkGeometry::NetworkGeometry(const GeometryResource::Pointer& networkGeometr connect(_resource.data(), &Resource::finished, this, &NetworkGeometry::resourceFinished); connect(_resource.data(), &Resource::onRefresh, this, &NetworkGeometry::resourceRefreshed); if (_resource->isLoaded()) { - resourceFinished(true); + resourceFinished(!_resource->getURL().isEmpty()); } } diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 7da3e2894f..5f860e8990 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -101,7 +101,11 @@ public: virtual bool areTexturesLoaded() const { return isLoaded() && Geometry::areTexturesLoaded(); } protected: - virtual bool isCacheable() const override { return _loaded; } + friend class GeometryMappingResource; + + virtual bool isCacheable() const override { return _loaded && _isCacheable; } + + bool _isCacheable { true }; }; class NetworkGeometry : public QObject { From 3402635f995532f2ba424e3123cebbfad1dbf333 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 24 Mar 2016 17:19:32 -0700 Subject: [PATCH 06/15] Lock geometry on blender --- libraries/render-utils/src/Model.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 09cf433d47..67d7d5df8f 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1071,18 +1071,21 @@ float Model::getLimbLength(int jointIndex) const { } bool Model::maybeStartBlender() { - const FBXGeometry& fbxGeometry = getFBXGeometry(); - if (fbxGeometry.hasBlendedMeshes()) { - QThreadPool::globalInstance()->start(new Blender(getThisPointer(), ++_blendNumber, _geometry, - fbxGeometry.meshes, _blendshapeCoefficients)); - return true; + if (isLoaded()) { + const FBXGeometry& fbxGeometry = getFBXGeometry(); + if (fbxGeometry.hasBlendedMeshes()) { + QThreadPool::globalInstance()->start(new Blender(getThisPointer(), ++_blendNumber, _geometry, + fbxGeometry.meshes, _blendshapeCoefficients)); + return true; + } } return false; } void Model::setBlendedVertices(int blendNumber, const std::weak_ptr& geometry, const QVector& vertices, const QVector& normals) { - if (_geometry != geometry.lock() || _blendedVertexBuffers.empty() || blendNumber < _appliedBlendNumber) { + auto geometryRef = geometry.lock(); + if (!geometryRef || _geometry != geometryRef || _blendedVertexBuffers.empty() || blendNumber < _appliedBlendNumber) { return; } _appliedBlendNumber = blendNumber; From eeeb175bb8bba000e0084a8b66c11b1d14a05e99 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 24 Mar 2016 17:55:03 -0700 Subject: [PATCH 07/15] Derive NetworkMaterial, avoid copying it --- .../src/model-networking/ModelCache.cpp | 26 ++++++++++++++----- .../src/model-networking/ModelCache.h | 10 +++++-- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index dfb3cf60c1..586990db09 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -193,7 +193,7 @@ void GeometryDefinitionResource::setGeometryDefinition(void* fbxGeometry) { int partID = 0; for (const FBXMeshPart& part : mesh.parts) { // Construct local shapes - shapes->emplace_back(meshID, partID, (int)materialIDAtlas[part.materialID]); + shapes->push_back(std::make_shared(meshID, partID, (int)materialIDAtlas[part.materialID])); partID++; } meshID++; @@ -249,7 +249,13 @@ void Geometry::setTextures(const QVariantMap& textureMap) { if (std::any_of(material->_textures.cbegin(), material->_textures.cend(), [&textureMap](const NetworkMaterial::Textures::value_type& it) { return it.texture && textureMap.contains(it.name); })) { - material = std::make_shared(*material, textureMap); + if (material->isOriginal()) { + // On first mutation, copy the material to avoid altering the cache + material = std::make_shared(*material, textureMap); + } else { + // On later mutations, we can mutate in-place + material->setTextures(textureMap); + } _areTexturesLoaded = false; } } @@ -286,7 +292,7 @@ bool Geometry::areTexturesLoaded() const { const std::shared_ptr Geometry::getShapeMaterial(int shapeID) const { if ((shapeID >= 0) && (shapeID < (int)_shapes->size())) { - int materialID = _shapes->at(shapeID).materialID; + int materialID = _shapes->at(shapeID)->materialID; if ((materialID >= 0) && (materialID < (int)_materials.size())) { return _materials[materialID]; } @@ -348,7 +354,15 @@ model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, Textu return map; } -NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl) { +NetworkMaterial::NetworkMaterial(const NetworkMaterial& material, const QVariantMap& textureMap) : + _textures(material._textures), _isOriginal(false) +{ + setTextures(textureMap); +} + +NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl) : + model::Material(*material._material), _isOriginal(true) +{ _textures = Textures(MapChannel::NUM_MAP_CHANNELS); if (!material.albedoTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, DEFAULT_TEXTURE, MapChannel::ALBEDO_MAP); @@ -406,9 +420,7 @@ NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textur } } -NetworkMaterial::NetworkMaterial(const NetworkMaterial& material, const QVariantMap& textureMap) : NetworkMaterial(material) { - _textures = material._textures; - +void NetworkMaterial::setTextures(const QVariantMap& textureMap) { const auto& albedoName = getTextureName(MapChannel::ALBEDO_MAP); const auto& normalName = getTextureName(MapChannel::NORMAL_MAP); const auto& roughnessName = getTextureName(MapChannel::ROUGHNESS_MAP); diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 5f860e8990..0832478648 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -58,10 +58,10 @@ public: // Immutable over lifetime using NetworkMeshes = std::vector>; - using NetworkShapes = std::vector; + using NetworkShapes = std::vector>; // Mutable, but must retain structure of vector - using NetworkMaterials = std::vector>; + using NetworkMaterials = std::vector>; const FBXGeometry& getGeometry() const { return *_geometry; } const NetworkMeshes& getMeshes() const { return *_meshes; } @@ -156,12 +156,18 @@ protected: static const QString NO_TEXTURE; const QString& getTextureName(MapChannel channel); + void setTextures(const QVariantMap& textureMap); + + const bool& isOriginal() const { return _isOriginal; } + private: // Helpers for the ctors QUrl getTextureUrl(const QUrl& baseUrl, const FBXTexture& fbxTexture); model::TextureMapPointer fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture, TextureType type, MapChannel channel); model::TextureMapPointer fetchTextureMap(const QUrl& url, TextureType type, MapChannel channel); + + bool _isOriginal; }; class NetworkShape { From c625d36bd8ee2f37ae84c68729d0c75df157e54b Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 24 Mar 2016 19:18:38 -0700 Subject: [PATCH 08/15] Persist material transform/params --- .../src/model-networking/ModelCache.cpp | 22 ++++++++++--------- .../src/model-networking/ModelCache.h | 4 ++++ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 586990db09..6c7c98047d 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -355,8 +355,9 @@ model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, Textu } NetworkMaterial::NetworkMaterial(const NetworkMaterial& material, const QVariantMap& textureMap) : - _textures(material._textures), _isOriginal(false) + NetworkMaterial(material) { + _isOriginal = false; setTextures(textureMap); } @@ -366,7 +367,8 @@ NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textur _textures = Textures(MapChannel::NUM_MAP_CHANNELS); if (!material.albedoTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, DEFAULT_TEXTURE, MapChannel::ALBEDO_MAP); - map->setTextureTransform(material.albedoTexture.transform); + _albedoTransform = material.albedoTexture.transform; + map->setTextureTransform(_albedoTransform); if (!material.opacityTexture.filename.isEmpty()) { if (material.albedoTexture.filename == material.opacityTexture.filename) { @@ -414,8 +416,10 @@ NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textur if (!material.lightmapTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.lightmapTexture, LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP); - map->setTextureTransform(material.lightmapTexture.transform); - map->setLightmapOffsetScale(material.lightmapParams.x, material.lightmapParams.y); + _lightmapTransform = material.lightmapTexture.transform; + _lightmapParams = material.lightmapParams; + map->setTextureTransform(_lightmapTransform); + map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y); setTextureMap(MapChannel::LIGHTMAP_MAP, map); } } @@ -431,9 +435,9 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) { if (!albedoName.isEmpty() && textureMap.contains(albedoName)) { auto map = fetchTextureMap(textureMap[albedoName].toUrl(), DEFAULT_TEXTURE, MapChannel::ALBEDO_MAP); - map->setTextureTransform(getTextureMap(MapChannel::ALBEDO_MAP)->getTextureTransform()); + map->setTextureTransform(_albedoTransform); // when reassigning the albedo texture we also check for the alpha channel used as opacity - map->setUseAlphaChannel(true); + map->setUseAlphaChannel(true); setTextureMap(MapChannel::ALBEDO_MAP, map); } @@ -466,10 +470,8 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) { if (!lightmapName.isEmpty() && textureMap.contains(lightmapName)) { auto map = fetchTextureMap(textureMap[lightmapName].toUrl(), LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP); - auto oldMap = getTextureMap(MapChannel::LIGHTMAP_MAP); - map->setTextureTransform(oldMap->getTextureTransform()); - glm::vec2 offsetScale = oldMap->getLightmapOffsetScale(); - map->setLightmapOffsetScale(offsetScale.x, offsetScale.y); + map->setTextureTransform(_lightmapTransform); + map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y); setTextureMap(MapChannel::LIGHTMAP_MAP, map); } } diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 0832478648..01a5e2165e 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -167,6 +167,10 @@ private: TextureType type, MapChannel channel); model::TextureMapPointer fetchTextureMap(const QUrl& url, TextureType type, MapChannel channel); + Transform _albedoTransform; + Transform _lightmapTransform; + vec2 _lightmapParams; + bool _isOriginal; }; From 864def31eabdfe1c6bebf71f64abd72b7065a892 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 25 Mar 2016 10:56:20 -0700 Subject: [PATCH 09/15] Don't refresh NetworkGeometry with cache --- .../model-networking/src/model-networking/ModelCache.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 6c7c98047d..de4409a5a3 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -309,6 +309,10 @@ NetworkGeometry::NetworkGeometry(const GeometryResource::Pointer& networkGeometr } void NetworkGeometry::resourceFinished(bool success) { + // FIXME: Model is not set up to handle a refresh + if (_instance) { + return; + } if (success) { _instance = std::make_shared(*_resource); } @@ -316,7 +320,8 @@ void NetworkGeometry::resourceFinished(bool success) { } void NetworkGeometry::resourceRefreshed() { - _instance.reset(); + // FIXME: Model is not set up to handle a refresh + // _instance.reset(); } const QString NetworkMaterial::NO_TEXTURE = QString(); From af55547766a2be8888344855087a919de43677a6 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 25 Mar 2016 11:46:24 -0700 Subject: [PATCH 10/15] Copy material schema buffers as new ref --- libraries/model/src/model/Material.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/libraries/model/src/model/Material.cpp b/libraries/model/src/model/Material.cpp index 5260143a7f..802650df93 100755 --- a/libraries/model/src/model/Material.cpp +++ b/libraries/model/src/model/Material.cpp @@ -18,26 +18,34 @@ using namespace gpu; Material::Material() : _key(0), _schemaBuffer(), - _textureMaps() { - - // only if created from nothing shall we create the Buffer to store the properties - Schema schema; - _schemaBuffer = gpu::BufferView(std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema)); + _textureMaps() +{ + // created from nothing: create the Buffer to store the properties + Schema schema; + _schemaBuffer = gpu::BufferView(std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema)); } Material::Material(const Material& material) : _key(material._key), - _schemaBuffer(material._schemaBuffer), - _textureMaps(material._textureMaps) { + _textureMaps(material._textureMaps) +{ + // copied: create the Buffer to store the properties, avoid holding a ref to the old Buffer + Schema schema; + _schemaBuffer = gpu::BufferView(std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema)); + _schemaBuffer.edit() = material._schemaBuffer.get(); } Material& Material::operator= (const Material& material) { _key = (material._key); - _schemaBuffer = (material._schemaBuffer); _textureMaps = (material._textureMaps); + // copied: create the Buffer to store the properties, avoid holding a ref to the old Buffer + Schema schema; + _schemaBuffer = gpu::BufferView(std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema)); + _schemaBuffer.edit() = material._schemaBuffer.get(); + return (*this); } From a262d9960cce3b02ce014197c257f5dded9e5504 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 25 Mar 2016 11:47:33 -0700 Subject: [PATCH 11/15] Mind differently tex'ed instances --- .../src/model-networking/ModelCache.cpp | 20 +++++++------------ .../src/model-networking/ModelCache.h | 3 +-- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index de4409a5a3..7c692f1565 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -250,12 +250,11 @@ void Geometry::setTextures(const QVariantMap& textureMap) { [&textureMap](const NetworkMaterial::Textures::value_type& it) { return it.texture && textureMap.contains(it.name); })) { if (material->isOriginal()) { - // On first mutation, copy the material to avoid altering the cache - material = std::make_shared(*material, textureMap); - } else { - // On later mutations, we can mutate in-place - material->setTextures(textureMap); + // Copy the material to avoid mutating the cached version + material = std::make_shared(*material); } + + material->setTextures(textureMap); _areTexturesLoaded = false; } } @@ -359,15 +358,8 @@ model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, Textu return map; } -NetworkMaterial::NetworkMaterial(const NetworkMaterial& material, const QVariantMap& textureMap) : - NetworkMaterial(material) -{ - _isOriginal = false; - setTextures(textureMap); -} - NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl) : - model::Material(*material._material), _isOriginal(true) + model::Material(*material._material) { _textures = Textures(MapChannel::NUM_MAP_CHANNELS); if (!material.albedoTexture.filename.isEmpty()) { @@ -430,6 +422,8 @@ NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textur } void NetworkMaterial::setTextures(const QVariantMap& textureMap) { + _isOriginal = false; + const auto& albedoName = getTextureName(MapChannel::ALBEDO_MAP); const auto& normalName = getTextureName(MapChannel::NORMAL_MAP); const auto& roughnessName = getTextureName(MapChannel::ROUGHNESS_MAP); diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 01a5e2165e..166ad128f2 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -139,7 +139,6 @@ public: using MapChannel = model::Material::MapChannel; NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl); - NetworkMaterial(const NetworkMaterial& material, const QVariantMap& textureMap); protected: friend class Geometry; @@ -171,7 +170,7 @@ private: Transform _lightmapTransform; vec2 _lightmapParams; - bool _isOriginal; + bool _isOriginal { true }; }; class NetworkShape { From 581bbea36ed3326d4d2ced740b49652908cba2df Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 25 Mar 2016 11:57:02 -0700 Subject: [PATCH 12/15] Accomodate the caching Model --- .../src/model-networking/ModelCache.cpp | 22 +++++++++++++++---- .../src/model-networking/ModelCache.h | 3 +++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 7c692f1565..d16fef040f 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -242,6 +242,18 @@ const QVariantMap Geometry::getTextures() const { return textures; } +// FIXME: The materials should only be copied when modified, but the Model currently caches the original +Geometry::Geometry(const Geometry& geometry) { + _geometry = geometry._geometry; + _meshes = geometry._meshes; + _shapes = geometry._shapes; + + _materials.reserve(geometry._materials.size()); + for (const auto& material : geometry._materials) { + _materials.push_back(std::make_shared(*material)); + } +} + void Geometry::setTextures(const QVariantMap& textureMap) { if (_meshes->size() > 0) { for (auto& material : _materials) { @@ -249,10 +261,12 @@ void Geometry::setTextures(const QVariantMap& textureMap) { if (std::any_of(material->_textures.cbegin(), material->_textures.cend(), [&textureMap](const NetworkMaterial::Textures::value_type& it) { return it.texture && textureMap.contains(it.name); })) { - if (material->isOriginal()) { - // Copy the material to avoid mutating the cached version - material = std::make_shared(*material); - } + // FIXME: The Model currently caches the materials (waste of space!) + // so they must be copied in the Geometry copy-ctor + // if (material->isOriginal()) { + // // Copy the material to avoid mutating the cached version + // material = std::make_shared(*material); + //} material->setTextures(textureMap); _areTexturesLoaded = false; diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 166ad128f2..dad7883a6a 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -56,6 +56,9 @@ class Geometry { public: using Pointer = std::shared_ptr; + Geometry() = default; + Geometry(const Geometry& geometry); + // Immutable over lifetime using NetworkMeshes = std::vector>; using NetworkShapes = std::vector>; From fac54b6ee3900e5d7443b416be117123a752641e Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 25 Mar 2016 13:00:18 -0700 Subject: [PATCH 13/15] Maintain parity with tex changes --- .../src/model-networking/ModelCache.cpp | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index d16fef040f..78ea7dd30e 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -348,7 +348,7 @@ const QString& NetworkMaterial::getTextureName(MapChannel channel) { QUrl NetworkMaterial::getTextureUrl(const QUrl& url, const FBXTexture& texture) { // If content is inline, cache it under the fbx file, not its url - const auto baseUrl = texture.content.isEmpty() ? url: QUrl(url.url() + "/"); + const auto baseUrl = texture.content.isEmpty() ? url : QUrl(url.url() + "/"); return baseUrl.resolved(QUrl(texture.filename)); } @@ -446,43 +446,50 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) { const auto& emissiveName = getTextureName(MapChannel::EMISSIVE_MAP); const auto& lightmapName = getTextureName(MapChannel::LIGHTMAP_MAP); - if (!albedoName.isEmpty() && textureMap.contains(albedoName)) { - auto map = fetchTextureMap(textureMap[albedoName].toUrl(), DEFAULT_TEXTURE, MapChannel::ALBEDO_MAP); + if (!albedoName.isEmpty()) { + auto url = textureMap.contains(albedoName) ? textureMap[albedoName].toUrl() : QUrl(); + auto map = fetchTextureMap(url, DEFAULT_TEXTURE, MapChannel::ALBEDO_MAP); map->setTextureTransform(_albedoTransform); // when reassigning the albedo texture we also check for the alpha channel used as opacity map->setUseAlphaChannel(true); setTextureMap(MapChannel::ALBEDO_MAP, map); } - if (!normalName.isEmpty() && textureMap.contains(normalName)) { - auto map = fetchTextureMap(textureMap[normalName].toUrl(), DEFAULT_TEXTURE, MapChannel::NORMAL_MAP); + if (!normalName.isEmpty()) { + auto url = textureMap.contains(normalName) ? textureMap[normalName].toUrl() : QUrl(); + auto map = fetchTextureMap(url, DEFAULT_TEXTURE, MapChannel::NORMAL_MAP); setTextureMap(MapChannel::NORMAL_MAP, map); } - if (!roughnessName.isEmpty() && textureMap.contains(roughnessName)) { + if (!roughnessName.isEmpty()) { + auto url = textureMap.contains(roughnessName) ? textureMap[roughnessName].toUrl() : QUrl(); // FIXME: If passing a gloss map instead of a roughmap how do we know? - auto map = fetchTextureMap(textureMap[roughnessName].toUrl(), ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP); + auto map = fetchTextureMap(url, ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP); setTextureMap(MapChannel::ROUGHNESS_MAP, map); } - if (!metallicName.isEmpty() && textureMap.contains(metallicName)) { + if (!metallicName.isEmpty()) { + auto url = textureMap.contains(metallicName) ? textureMap[metallicName].toUrl() : QUrl(); // FIXME: If passing a specular map instead of a metallic how do we know? - auto map = fetchTextureMap(textureMap[metallicName].toUrl(), METALLIC_TEXTURE, MapChannel::METALLIC_MAP); + auto map = fetchTextureMap(url, METALLIC_TEXTURE, MapChannel::METALLIC_MAP); setTextureMap(MapChannel::METALLIC_MAP, map); } - if (!occlusionName.isEmpty() && textureMap.contains(occlusionName)) { - auto map = fetchTextureMap(textureMap[occlusionName].toUrl(), OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP); + if (!occlusionName.isEmpty()) { + auto url = textureMap.contains(occlusionName) ? textureMap[occlusionName].toUrl() : QUrl(); + auto map = fetchTextureMap(url, OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP); setTextureMap(MapChannel::OCCLUSION_MAP, map); } - if (!emissiveName.isEmpty() && textureMap.contains(emissiveName)) { - auto map = fetchTextureMap(textureMap[emissiveName].toUrl(), EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP); + if (!emissiveName.isEmpty()) { + auto url = textureMap.contains(emissiveName) ? textureMap[emissiveName].toUrl() : QUrl(); + auto map = fetchTextureMap(url, EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP); setTextureMap(MapChannel::EMISSIVE_MAP, map); } - if (!lightmapName.isEmpty() && textureMap.contains(lightmapName)) { - auto map = fetchTextureMap(textureMap[lightmapName].toUrl(), LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP); + if (!lightmapName.isEmpty()) { + auto url = textureMap.contains(lightmapName) ? textureMap[lightmapName].toUrl() : QUrl(); + auto map = fetchTextureMap(url, LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP); map->setTextureTransform(_lightmapTransform); map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y); setTextureMap(MapChannel::LIGHTMAP_MAP, map); From b90f2b2e0a9b895c6f6111e56df5f9a9fa69b2a0 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Fri, 25 Mar 2016 13:17:29 -0700 Subject: [PATCH 14/15] Use correct textureBaseUrl --- libraries/model-networking/src/model-networking/ModelCache.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 78ea7dd30e..2a6f33b964 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -157,7 +157,7 @@ class GeometryDefinitionResource : public GeometryResource { Q_OBJECT public: GeometryDefinitionResource(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) : - GeometryResource(url), _mapping(mapping), _textureBaseUrl(textureBaseUrl) {} + GeometryResource(url), _mapping(mapping), _textureBaseUrl(textureBaseUrl.isValid() ? textureBaseUrl : url) {} virtual void downloadFinished(const QByteArray& data) override; @@ -497,4 +497,3 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) { } #include "ModelCache.moc" - From d6640ac5207c9077110a054032524220b48f4e86 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Sun, 27 Mar 2016 16:38:45 -0700 Subject: [PATCH 15/15] Update Model::_hasTransparentTextures --- libraries/render-utils/src/Model.cpp | 4 +++- libraries/render-utils/src/Model.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 67d7d5df8f..545d239549 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -79,7 +79,9 @@ bool Model::needsFixupInScene() const { // Once textures are loaded, fixup if they are now transparent if (_needsUpdateTransparentTextures && _geometry->getGeometry()->areTexturesLoaded()) { _needsUpdateTransparentTextures = false; - if (_hasTransparentTextures != _geometry->getGeometry()->hasTransparentTextures()) { + bool hasTransparentTextures = _geometry->getGeometry()->hasTransparentTextures(); + if (_hasTransparentTextures != hasTransparentTextures) { + _hasTransparentTextures = hasTransparentTextures; return true; } } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 632e8089a8..744a4ce605 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -379,7 +379,7 @@ protected: bool _needsReload { true }; bool _needsUpdateClusterMatrices { true }; mutable bool _needsUpdateTransparentTextures { true }; - bool _hasTransparentTextures { false }; + mutable bool _hasTransparentTextures { false }; bool _showCollisionHull { false }; friend class ModelMeshPartPayload;