diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d4b88e8265..759a96388d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -164,6 +164,7 @@ #include #include #include +#include #include "recording/ClipCache.h" #include "AudioClient.h" @@ -2158,6 +2159,32 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo return QSizeF(0.0f, 0.0f); }); + Texture::setUnboundTextureForUUIDOperator([this](const QUuid& entityID) -> gpu::TexturePointer { + if (_aboutToQuit) { + return nullptr; + } + + auto renderable = getEntities()->renderableForEntityId(entityID); + if (renderable) { + return renderable->getTexture(); + } + + return nullptr; + }); + + ReferenceMaterial::setMaterialForUUIDOperator([this](const QUuid& entityID) -> graphics::MaterialPointer { + if (_aboutToQuit) { + return nullptr; + } + + auto renderable = getEntities()->renderableForEntityId(entityID); + if (renderable) { + return renderable->getTopMaterial(); + } + + return nullptr; + }); + connect(this, &Application::aboutToQuit, [this]() { setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); }); diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index b7cfcde224..212baa6634 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -495,6 +495,15 @@ void EntityRenderer::removeMaterial(graphics::MaterialPointer material, const st emit requestRenderUpdate(); } +graphics::MaterialPointer EntityRenderer::getTopMaterial() { + std::lock_guard lock(_materialsLock); + auto materials = _materials.find("0"); + if (materials != _materials.end()) { + return materials->second.top().material; + } + return nullptr; +} + EntityRenderer::Pipeline EntityRenderer::getPipelineType(const graphics::MultiMaterial& materials) { if (materials.top().material && materials.top().material->isProcedural() && materials.top().material->isReady()) { return Pipeline::PROCEDURAL; @@ -626,6 +635,7 @@ void EntityRenderer::updateShapeKeyBuilderFromMaterials(ShapeKey::Builder& build { std::lock_guard lock(_materialsLock); materials = _materials.find("0"); + if (materials != _materials.end()) { if (materials->second.shouldUpdate()) { RenderPipelines::updateMultiMaterial(materials->second); @@ -653,7 +663,6 @@ void EntityRenderer::updateShapeKeyBuilderFromMaterials(ShapeKey::Builder& build auto pipelineType = getPipelineType(materials->second); if (pipelineType == Pipeline::MATERIAL) { builder.withMaterial(); - if (drawMaterialKey.isNormalMap()) { builder.withTangents(); } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 2731fe6a50..3caeef0713 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -62,7 +62,9 @@ public: }; virtual void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName); virtual void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName); + virtual graphics::MaterialPointer getTopMaterial(); static Pipeline getPipelineType(const graphics::MultiMaterial& materials); + virtual gpu::TexturePointer getTexture() { return nullptr; } virtual scriptable::ScriptableModelBase getScriptableModel() override { return scriptable::ScriptableModelBase(); } diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp index 3fa2174114..9592a3e57f 100644 --- a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp @@ -198,10 +198,8 @@ void ImageEntityRenderer::doRender(RenderArgs* args) { procedural->prepare(*batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(transparent)); } else if (pipelineType == Pipeline::SIMPLE) { batch->setResourceTexture(0, _texture->getGPUTexture()); - } else { - if (RenderPipelines::bindMaterials(materials, *batch, args->_renderMode, args->_enableTexturing)) { - args->_details._materialSwitches++; - } + } else if (RenderPipelines::bindMaterials(materials, *batch, args->_renderMode, args->_enableTexturing)) { + args->_details._materialSwitches++; } DependencyManager::get()->renderQuad( diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.h b/libraries/entities-renderer/src/RenderableImageEntityItem.h index ff82dc157a..cc923daf4b 100644 --- a/libraries/entities-renderer/src/RenderableImageEntityItem.h +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.h @@ -24,6 +24,8 @@ public: ImageEntityRenderer(const EntityItemPointer& entity); ~ImageEntityRenderer(); + gpu::TexturePointer getTexture() override { return _texture ? _texture->getGPUTexture() : nullptr; } + protected: Item::Bound getBound(RenderArgs* args) override; ShapeKey getShapeKey() override; diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index 8f8a2076f8..b8f829f4ba 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -79,26 +79,42 @@ void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo } } + bool usingMaterialData = _materialURL.startsWith("materialData"); + bool usingEntityID = _materialURL.startsWith("{"); + bool materialDataChanged = false; bool urlChanged = false; std::string newCurrentMaterialName = _currentMaterialName; + QString targetEntityID = _materialURL; { QString materialURL = entity->getMaterialURL(); if (materialURL != _materialURL) { _materialURL = materialURL; + usingMaterialData = _materialURL.startsWith("materialData"); + usingEntityID = _materialURL.startsWith("{"); + targetEntityID = _materialURL; if (_materialURL.contains("#")) { auto split = _materialURL.split("#"); newCurrentMaterialName = split.last().toStdString(); + if (usingEntityID) { + targetEntityID = split.first(); + } } else if (_materialURL.contains("?")) { qDebug() << "DEPRECATED: Use # instead of ? for material URLS:" << _materialURL; auto split = _materialURL.split("?"); newCurrentMaterialName = split.last().toStdString(); + if (usingEntityID) { + targetEntityID = split.first(); + } + } + + if (usingMaterialData) { + materialDataChanged = true; + } else { + urlChanged = true; } - urlChanged = true; } } - bool usingMaterialData = _materialURL.startsWith("materialData"); - bool materialDataChanged = false; QUuid oldParentID = _parentID; QString oldParentMaterialName = _parentMaterialName; { @@ -135,7 +151,7 @@ void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo } } - if (urlChanged && !usingMaterialData) { + if (urlChanged && !usingMaterialData && !usingEntityID) { _networkMaterial = DependencyManager::get()->getMaterial(_materialURL); auto onMaterialRequestFinished = [this, entity, oldParentID, oldParentMaterialName, newCurrentMaterialName](bool success) { deleteMaterial(oldParentID, oldParentMaterialName); @@ -159,6 +175,13 @@ void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo }); } } + } else if (urlChanged && usingEntityID) { + deleteMaterial(oldParentID, oldParentMaterialName); + _texturesLoaded = true; + _parsedMaterials = NetworkMaterialResource::parseMaterialForUUID(QJsonValue(targetEntityID)); + // Since our material changed, the current name might not be valid anymore, so we need to update + setCurrentMaterialName(newCurrentMaterialName); + applyMaterial(entity); } else if (materialDataChanged && usingMaterialData) { deleteMaterial(oldParentID, oldParentMaterialName); _texturesLoaded = false; @@ -167,6 +190,10 @@ void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo setCurrentMaterialName(newCurrentMaterialName); applyMaterial(entity); } else { + if (newCurrentMaterialName != _currentMaterialName) { + setCurrentMaterialName(newCurrentMaterialName); + } + if (deleteNeeded) { deleteMaterial(oldParentID, oldParentMaterialName); } diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.h b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h index 340d169f29..25403e8a2b 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.h +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h @@ -26,6 +26,8 @@ public: MaterialEntityRenderer(const EntityItemPointer& entity) : Parent(entity) {} ~MaterialEntityRenderer() { deleteMaterial(_parentID, _parentMaterialName); } + graphics::MaterialPointer getTopMaterial() override { return getMaterial(); } + private: virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 7932a19110..6e436a0dcd 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -274,6 +274,7 @@ void WebEntityRenderer::doRender(RenderArgs* args) { // Try to update the texture OffscreenQmlSurface::TextureAndFence newTextureAndFence; + QSize windowSize; bool newTextureAvailable = false; if (!resultWithReadLock([&] { if (!_webSurface) { @@ -281,6 +282,7 @@ void WebEntityRenderer::doRender(RenderArgs* args) { } newTextureAvailable = _webSurface->fetchTexture(newTextureAndFence); + windowSize = _webSurface->size(); return true; })) { return; @@ -288,6 +290,8 @@ void WebEntityRenderer::doRender(RenderArgs* args) { if (newTextureAvailable) { _texture->setExternalTexture(newTextureAndFence.first, newTextureAndFence.second); + _texture->setSize(windowSize.width(), windowSize.height()); + _texture->setOriginalSize(windowSize.width(), windowSize.height()); } static const glm::vec2 texMin(0.0f), texMax(1.0f), topLeft(-0.5f), bottomRight(0.5f); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 1ac415e5e5..fb04e08ec7 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -55,6 +55,8 @@ public: virtual void setProxyWindow(QWindow* proxyWindow) override; virtual QObject* getEventHandler() override; + gpu::TexturePointer getTexture() override { return _texture; } + protected: virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 425cfe9439..945804a0fc 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -951,10 +951,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * * @typedef {object} Entities.EntityProperties-Material * @property {Vec3} dimensions=0.1,0.1,0.1 - Used when materialMappingMode == "projected". - * @property {string} materialURL="" - URL to a {@link Entities.MaterialResource|MaterialResource}. If you append - * "#name" to the URL, the material with that name in the {@link Entities.MaterialResource|MaterialResource} - * will be applied to the entity. Alternatively, set the property value to "materialData" to use the - * materialData property for the {@link Entities.MaterialResource|MaterialResource} values. + * @property {string} materialURL="" - URL to a {@link Entities.MaterialResource|MaterialResource}. Alternatively, set the + * property value to "materialData" to use the materialData property for the + * {@link Entities.MaterialResource|MaterialResource} values. If you append "#name" to the URL, the material + * with that name will be applied to the entity. You can also use the ID of another Material entity as the URL, in which + * case this material will act as a copy of that material, with its own unique material transform, priority, etc. * @property {string} materialData="" - Used to store {@link Entities.MaterialResource|MaterialResource} data as a JSON string. * You can use JSON.parse() to parse the string into a JavaScript object which you can manipulate the * properties of, and use JSON.stringify() to convert the object into a string to put in the property. @@ -1379,7 +1380,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { /*@jsdoc * The "Web" {@link Entities.EntityType|EntityType} displays a browsable web page. Each user views their own copy * of the web page: if one user navigates to another page on the entity, other users do not see the change; if a video is being - * played, users don't see it in sync. It has properties in addition to the common + * played, users don't see it in sync. Internally, a Web entity is rendered as a non-repeating, upside down texture, so additional + * transformations may be necessary if you reference a Web entity texture by UUID. It has properties in addition to the common * {@link Entities.EntityProperties|EntityProperties}. * * @typedef {object} Entities.EntityProperties-Web diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index eeb42626c2..63d8842a91 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2751,6 +2751,47 @@ QVector EntityTree::sendEntities(EntityEditPacketSender* packetSen return map.values().toVector(); } +QJsonValue replaceEntityIDsInJSONHelper(const QJsonValue& jsonValue, std::function getMapped) { + if (jsonValue.isString()) { + QString stringValue = jsonValue.toString(); + QUuid oldID = stringValue; + if (!oldID.isNull()) { + return QJsonValue(getMapped(oldID).toString()); + } + return stringValue; + } else if (jsonValue.isArray()) { + QJsonArray jsonArray = jsonValue.toArray(); + for (int i = 0; i < jsonArray.count(); i++) { + jsonArray[i] = replaceEntityIDsInJSONHelper(jsonArray[i], getMapped); + } + return jsonArray; + } else if (jsonValue.isObject()) { + QJsonObject jsonObject = jsonValue.toObject(); + auto keys = jsonObject.keys(); + for (auto& key : keys) { + auto value = jsonObject.value(key); + jsonObject[key] = replaceEntityIDsInJSONHelper(value, getMapped); + } + return jsonObject; + } else { + return jsonValue; + } +} + +QString replaceEntityIDsInJSON(const QString& json, std::function getMapped) { + QJsonDocument document = QJsonDocument::fromJson(json.toUtf8()); + if (!document.isNull() && document.isObject()) { + QJsonObject jsonObject = document.object(); + auto keys = jsonObject.keys(); + for (auto& key : keys) { + auto value = jsonObject.value(key); + jsonObject[key] = replaceEntityIDsInJSONHelper(value, getMapped); + } + document = QJsonDocument(jsonObject); + } + return document.toJson(); +} + bool EntityTree::sendEntitiesOperation(const OctreeElementPointer& element, void* extraData) { SendEntitiesOperationArgs* args = static_cast(extraData); EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); @@ -2819,6 +2860,61 @@ bool EntityTree::sendEntitiesOperation(const OctreeElementPointer& element, void QByteArray actionData = properties.getActionData(); properties.setActionData(remapActionDataIDs(actionData, *args->map)); + { + QString materialURL = properties.getMaterialURL(); + QString uuidString = materialURL; + QString materialName = ""; + + if (materialURL.contains("?")) { + QStringList split = materialURL.split("?"); + uuidString = split[0]; + if (split.length() > 1) { + materialName = split[1]; + } + } else if (materialURL.contains("#")) { + QStringList split = materialURL.split("#"); + uuidString = split[0]; + if (split.length() > 1) { + materialName = split[1]; + } + } + + QUuid oldID = uuidString; + if (!oldID.isNull()) { + uuidString = getMapped(oldID).toString(); + } + QUuid oldMaterialName = materialName; + if (!oldMaterialName.isNull()) { + materialName = getMapped(oldMaterialName).toString(); + } + + if (!materialName.isEmpty()) { + properties.setMaterialURL(uuidString + "?" + materialName); + } else { + properties.setMaterialURL(uuidString); + } + } + + QString imageURL = properties.getImageURL(); + if (imageURL.startsWith("{")) { + QUuid oldID = imageURL; + if (!oldID.isNull()) { + properties.setImageURL(getMapped(oldID).toString()); + } + } + + QString materialData = properties.getMaterialData(); + if (!materialData.isEmpty()) { + materialData = replaceEntityIDsInJSON(materialData, getMapped); + properties.setMaterialData(materialData); + } + + QString userData = properties.getUserData(); + if (!userData.isEmpty()) { + userData = replaceEntityIDsInJSON(userData, getMapped); + properties.setUserData(userData); + } + // set creation time to "now" for imported entities properties.setCreated(usecTimestampNow()); diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 1edd7d33cf..48159c5da5 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -322,6 +322,16 @@ bool Texture::isDepthStencilRenderTarget() const { return (_texelFormat.getSemantic() == gpu::DEPTH) || (_texelFormat.getSemantic() == gpu::DEPTH_STENCIL); } +void Texture::setSize(int width, int height) { + _width = width; + _height = height; +} + +void Texture::setOriginalSize(int width, int height) { + _originalWidth = width; + _originalHeight = height; +} + uint16 Texture::evalDimMaxNumMips(uint16 size) { double largerDim = size; double val = log(largerDim)/log(2.0); @@ -887,16 +897,34 @@ void SphericalHarmonics::evalFromTexture(const Texture& texture, gpu::BackendTar // TextureSource -void TextureSource::resetTexture(gpu::TexturePointer texture) { +const gpu::TexturePointer TextureSource::getGPUTexture() const { + if (_gpuTextureOperator && !_locked) { + _locked = true; + auto gpuTexture = _gpuTextureOperator(); + _locked = false; + return gpuTexture; + } + return _gpuTexture; +} + +void TextureSource::resetTexture(const gpu::TexturePointer& texture) { _gpuTexture = texture; + _gpuTextureOperator = nullptr; +} + +void TextureSource::resetTextureOperator(const std::function& textureOperator) { + _gpuTexture = nullptr; + _gpuTextureOperator = textureOperator; } bool TextureSource::isDefined() const { - if (_gpuTexture) { - return _gpuTexture->isDefined(); - } else { - return false; + if (_gpuTextureOperator && !_locked) { + _locked = true; + auto gpuTexture = _gpuTextureOperator(); + _locked = false; + return gpuTexture && gpuTexture->isDefined(); } + return _gpuTexture && _gpuTexture->isDefined(); } bool Texture::setMinMip(uint16 newMinMip) { @@ -930,6 +958,7 @@ void Texture::setExternalTexture(uint32 externalId, void* externalFence) { Lock lock(_externalMutex); assert(_externalRecycler); _externalUpdates.push_back({ externalId, externalFence }); + _defined = true; } Texture::ExternalUpdates Texture::getUpdates() const { diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index a525cda1ab..a0cfc60b6e 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -417,11 +417,16 @@ public: Element getTexelFormat() const { return _texelFormat; } + void setSize(int width, int height); Vec3u getDimensions() const { return Vec3u(_width, _height, _depth); } uint16 getWidth() const { return _width; } uint16 getHeight() const { return _height; } uint16 getDepth() const { return _depth; } + void setOriginalSize(int width, int height); + int getOriginalWidth() const { return _originalWidth; } + int getOriginalHeight() const { return _originalHeight; } + // The number of faces is mostly used for cube map, and maybe for stereo ? otherwise it's 1 // For cube maps, this means the pixels of the different faces are supposed to be packed back to back in a mip // as if the height was NUM_FACES time bigger. @@ -615,6 +620,8 @@ protected: uint16 _width { 1 }; uint16 _height { 1 }; uint16 _depth { 1 }; + int _originalWidth { 0 }; + int _originalHeight { 0 }; uint16 _numSamples { 1 }; @@ -676,9 +683,10 @@ public: _element(element) {}; - TextureView(const TexturePointer& texture, uint16 subresource) : + TextureView(const TexturePointer& texture, uint16 subresource, std::function textureOperator = nullptr) : _texture(texture), - _subresource(subresource) + _subresource(subresource), + _textureOperator(textureOperator) {}; ~TextureView() {} @@ -689,6 +697,12 @@ public: bool operator !() const { return (!_texture); } bool isValid() const { return bool(_texture); } + + bool isReference() const { return (bool)_textureOperator; } + std::function getTextureOperator() const { return _textureOperator; } + +private: + std::function _textureOperator { nullptr }; }; typedef std::vector TextureViews; @@ -700,16 +714,20 @@ public: void setUrl(const QUrl& url) { _imageUrl = url; } const QUrl& getUrl() const { return _imageUrl; } - const gpu::TexturePointer getGPUTexture() const { return _gpuTexture; } + const gpu::TexturePointer getGPUTexture() const; void setType(int type) { _type = type; } int getType() const { return _type; } - void resetTexture(gpu::TexturePointer texture); + void resetTexture(const gpu::TexturePointer& texture); + void resetTextureOperator(const std::function& textureOperator); bool isDefined() const; + std::function getTextureOperator() const { return _gpuTextureOperator; } protected: gpu::TexturePointer _gpuTexture; + std::function _gpuTextureOperator { nullptr }; + mutable bool _locked { false }; QUrl _imageUrl; int _type { 0 }; }; diff --git a/libraries/graphics/src/graphics/Material.cpp b/libraries/graphics/src/graphics/Material.cpp index 41cd319595..836487de14 100755 --- a/libraries/graphics/src/graphics/Material.cpp +++ b/libraries/graphics/src/graphics/Material.cpp @@ -281,4 +281,33 @@ void MultiMaterial::calculateMaterialInfo() const { } _hasCalculatedTextureInfo = allTextures; } -} \ No newline at end of file +} + +void MultiMaterial::resetReferenceTexturesAndMaterials() { + _referenceTextures.clear(); + _referenceMaterials.clear(); +} + +void MultiMaterial::addReferenceTexture(const std::function& textureOperator) { + _referenceTextures.emplace_back(textureOperator, textureOperator()); +} + +void MultiMaterial::addReferenceMaterial(const std::function& materialOperator) { + _referenceMaterials.emplace_back(materialOperator, materialOperator()); +} + +bool MultiMaterial::anyReferenceMaterialsOrTexturesChanged() const { + for (auto textureOperatorPair : _referenceTextures) { + if (textureOperatorPair.first() != textureOperatorPair.second) { + return true; + } + } + + for (auto materialOperatorPair : _referenceMaterials) { + if (materialOperatorPair.first() != materialOperatorPair.second) { + return true; + } + } + + return false; +} diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h index 7a411e5b2c..2eb4e0cbe1 100755 --- a/libraries/graphics/src/graphics/Material.h +++ b/libraries/graphics/src/graphics/Material.h @@ -341,57 +341,57 @@ public: virtual ~Material() = default; Material& operator= (const Material& material); - const MaterialKey& getKey() const { return _key; } + virtual MaterialKey getKey() const { return _key; } static const float DEFAULT_EMISSIVE; void setEmissive(const glm::vec3& emissive, bool isSRGB = true); - glm::vec3 getEmissive(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_emissive) : _emissive); } + virtual glm::vec3 getEmissive(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_emissive) : _emissive); } static const float DEFAULT_OPACITY; void setOpacity(float opacity); - float getOpacity() const { return _opacity; } + virtual float getOpacity() const { return _opacity; } static const MaterialKey::OpacityMapMode DEFAULT_OPACITY_MAP_MODE; void setOpacityMapMode(MaterialKey::OpacityMapMode opacityMapMode); - MaterialKey::OpacityMapMode getOpacityMapMode() const; + virtual MaterialKey::OpacityMapMode getOpacityMapMode() const; static const float DEFAULT_OPACITY_CUTOFF; void setOpacityCutoff(float opacityCutoff); - float getOpacityCutoff() const { return _opacityCutoff; } + virtual float getOpacityCutoff() const { return _opacityCutoff; } static const MaterialKey::CullFaceMode DEFAULT_CULL_FACE_MODE; void setCullFaceMode(MaterialKey::CullFaceMode cullFaceMode) { _cullFaceMode = cullFaceMode; } - MaterialKey::CullFaceMode getCullFaceMode() const { return _cullFaceMode; } + virtual MaterialKey::CullFaceMode getCullFaceMode() const { return _cullFaceMode; } void setUnlit(bool value); - bool isUnlit() const { return _key.isUnlit(); } + virtual bool isUnlit() const { return _key.isUnlit(); } static const float DEFAULT_ALBEDO; void setAlbedo(const glm::vec3& albedo, bool isSRGB = true); - glm::vec3 getAlbedo(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_albedo) : _albedo); } + virtual glm::vec3 getAlbedo(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_albedo) : _albedo); } static const float DEFAULT_METALLIC; void setMetallic(float metallic); - float getMetallic() const { return _metallic; } + virtual float getMetallic() const { return _metallic; } static const float DEFAULT_ROUGHNESS; void setRoughness(float roughness); - float getRoughness() const { return _roughness; } + virtual float getRoughness() const { return _roughness; } static const float DEFAULT_SCATTERING; void setScattering(float scattering); - float getScattering() const { return _scattering; } + virtual float getScattering() const { return _scattering; } // The texture map to channel association static const int NUM_TEXCOORD_TRANSFORMS { 2 }; void setTextureMap(MapChannel channel, const TextureMapPointer& textureMap); - const TextureMaps& getTextureMaps() const { return _textureMaps; } // FIXME - not thread safe... + virtual TextureMaps getTextureMaps() const { return _textureMaps; } // FIXME - not thread safe... 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 // return true if the opacity changed, flase otherwise - bool resetOpacityMap() const; + virtual bool resetOpacityMap() const; // conversion from legacy material properties to PBR equivalent static float shininessToRoughness(float shininess) { return 1.0f - shininess / 100.0f; } @@ -404,12 +404,12 @@ public: const std::string& getModel() const { return _model; } void setModel(const std::string& model) { _model = model; } - glm::mat4 getTexCoordTransform(uint i) const { return _texcoordTransforms[i]; } + virtual glm::mat4 getTexCoordTransform(uint i) const { return _texcoordTransforms[i]; } void setTexCoordTransform(uint i, const glm::mat4& mat4) { _texcoordTransforms[i] = mat4; } - glm::vec2 getLightmapParams() const { return _lightmapParams; } - glm::vec2 getMaterialParams() const { return _materialParams; } + virtual glm::vec2 getLightmapParams() const { return _lightmapParams; } + virtual glm::vec2 getMaterialParams() const { return _materialParams; } - bool getDefaultFallthrough() const { return _defaultFallthrough; } + virtual bool getDefaultFallthrough() const { return _defaultFallthrough; } void setDefaultFallthrough(bool defaultFallthrough) { _defaultFallthrough = defaultFallthrough; } enum ExtraFlagBit { @@ -430,6 +430,8 @@ public: virtual bool isReady() const { return true; } virtual QString getProceduralString() const { return QString(); } + virtual bool isReference() const { return false; } + static const std::string HIFI_PBR; static const std::string HIFI_SHADER_SIMPLE; @@ -547,12 +549,16 @@ public: void setTexturesLoading(bool value) { _texturesLoading = value; } void setInitialized() { _initialized = true; } - bool shouldUpdate() const { return !_initialized || _needsUpdate || _texturesLoading; } + bool shouldUpdate() const { return !_initialized || _needsUpdate || _texturesLoading || anyReferenceMaterialsOrTexturesChanged(); } int getTextureCount() const { calculateMaterialInfo(); return _textureCount; } size_t getTextureSize() const { calculateMaterialInfo(); return _textureSize; } bool hasTextureInfo() const { return _hasCalculatedTextureInfo; } + void resetReferenceTexturesAndMaterials(); + void addReferenceTexture(const std::function& textureOperator); + void addReferenceMaterial(const std::function& materialOperator); + private: gpu::BufferView _schemaBuffer; graphics::MaterialKey::CullFaceMode _cullFaceMode { graphics::Material::DEFAULT_CULL_FACE_MODE }; @@ -565,6 +571,11 @@ private: mutable int _textureCount { 0 }; mutable bool _hasCalculatedTextureInfo { false }; void calculateMaterialInfo() const; + + bool anyReferenceMaterialsOrTexturesChanged() const; + + std::vector, gpu::TexturePointer>> _referenceTextures; + std::vector, graphics::MaterialPointer>> _referenceMaterials; }; }; diff --git a/libraries/graphics/src/graphics/TextureMap.cpp b/libraries/graphics/src/graphics/TextureMap.cpp index e2f24d68ff..0590203907 100755 --- a/libraries/graphics/src/graphics/TextureMap.cpp +++ b/libraries/graphics/src/graphics/TextureMap.cpp @@ -18,16 +18,12 @@ void TextureMap::setTextureSource(TextureSourcePointer& textureSource) { } bool TextureMap::isDefined() const { - if (_textureSource) { - return _textureSource->isDefined(); - } else { - return false; - } + return _textureSource && _textureSource->isDefined(); } gpu::TextureView TextureMap::getTextureView() const { if (_textureSource) { - return gpu::TextureView(_textureSource->getGPUTexture(), 0); + return gpu::TextureView(_textureSource->getGPUTexture(), 0, _textureSource->getTextureOperator()); } else { return gpu::TextureView(); } diff --git a/libraries/material-networking/src/material-networking/TextureCache.cpp b/libraries/material-networking/src/material-networking/TextureCache.cpp index 92e5e0873c..0307e38cc9 100644 --- a/libraries/material-networking/src/material-networking/TextureCache.cpp +++ b/libraries/material-networking/src/material-networking/TextureCache.cpp @@ -98,6 +98,8 @@ static const QUrl HMD_PREVIEW_FRAME_URL("resource://hmdPreviewFrame"); static const float SKYBOX_LOAD_PRIORITY { 10.0f }; // Make sure skybox loads first static const float HIGH_MIPS_LOAD_PRIORITY { 9.0f }; // Make sure high mips loads after skybox but before models +std::function Texture::_unboundTextureForUUIDOperator { nullptr }; + TextureCache::TextureCache() { _ktxCache->initialize(); #if defined(DISABLE_KTX_CACHE) @@ -252,6 +254,10 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUs if (url.scheme() == RESOURCE_SCHEME) { return getResourceTexture(url); } + QString decodedURL = QUrl::fromPercentEncoding(url.toEncoded()); + if (decodedURL.startsWith("{")) { + return getTextureByUUID(decodedURL); + } auto modifiedUrl = url; if (type == image::TextureUsage::SKY_TEXTURE) { QUrlQuery query { url.query() }; @@ -385,8 +391,6 @@ NetworkTexture::NetworkTexture(const NetworkTexture& other) : _type(other._type), _sourceChannel(other._sourceChannel), _currentlyLoadingResourceType(other._currentlyLoadingResourceType), - _originalWidth(other._originalWidth), - _originalHeight(other._originalHeight), _width(other._width), _height(other._height), _maxNumPixels(other._maxNumPixels), @@ -462,13 +466,12 @@ void NetworkTexture::setExtra(void* extra) { void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight) { - _originalWidth = originalWidth; - _originalHeight = originalHeight; // Passing ownership _textureSource->resetTexture(texture); if (texture) { + texture->setOriginalSize(originalWidth, originalHeight); _width = texture->getWidth(); _height = texture->getHeight(); setSize(texture->getStoredSize()); @@ -481,6 +484,12 @@ void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth, emit networkTextureCreated(qWeakPointerCast (_self)); } +void NetworkTexture::setImageOperator(std::function textureOperator) { + _textureSource->resetTextureOperator(textureOperator); + finishedLoading((bool)textureOperator); + emit networkTextureCreated(qWeakPointerCast (_self)); +} + gpu::TexturePointer NetworkTexture::getFallbackTexture() const { return getFallbackTextureForType(_type); } @@ -1312,7 +1321,6 @@ void ImageReader::read() { } NetworkTexturePointer TextureCache::getResourceTexture(const QUrl& resourceTextureUrl) { - gpu::TexturePointer texture; if (resourceTextureUrl == SPECTATOR_CAMERA_FRAME_URL) { if (!_spectatorCameraNetworkTexture) { _spectatorCameraNetworkTexture.reset(new NetworkTexture(resourceTextureUrl, true)); @@ -1329,6 +1337,7 @@ NetworkTexturePointer TextureCache::getResourceTexture(const QUrl& resourceTextu _hmdPreviewNetworkTexture.reset(new NetworkTexture(resourceTextureUrl, true)); } if (_hmdPreviewFramebuffer) { + gpu::TexturePointer texture; texture = _hmdPreviewFramebuffer->getRenderBuffer(0); if (texture) { texture->setSource(HMD_PREVIEW_FRAME_URL.toString().toStdString()); @@ -1375,3 +1384,27 @@ void TextureCache::updateSpectatorCameraNetworkTexture() { } } } + +NetworkTexturePointer TextureCache::getTextureByUUID(const QString& uuid) { + QUuid quuid = QUuid(uuid); + if (!quuid.isNull()) { + // We mark this as a resource texture because it's just a reference to another texture. The source + // texture will be marked properly + NetworkTexturePointer toReturn = NetworkTexturePointer(new NetworkTexture(uuid, true)); + toReturn->setImageOperator(Texture::getTextureForUUIDOperator(uuid)); + return toReturn; + } + return NetworkTexturePointer(); +} + +std::function Texture::getTextureForUUIDOperator(const QUuid& uuid) { + if (_unboundTextureForUUIDOperator) { + return std::bind(_unboundTextureForUUIDOperator, uuid); + } + return nullptr; +} + + +void Texture::setUnboundTextureForUUIDOperator(std::function textureForUUIDOperator) { + _unboundTextureForUUIDOperator = textureForUUIDOperator; +} diff --git a/libraries/material-networking/src/material-networking/TextureCache.h b/libraries/material-networking/src/material-networking/TextureCache.h index aab458e184..c0633c2f9b 100644 --- a/libraries/material-networking/src/material-networking/TextureCache.h +++ b/libraries/material-networking/src/material-networking/TextureCache.h @@ -39,6 +39,12 @@ class Texture { public: gpu::TexturePointer getGPUTexture() const { return _textureSource->getGPUTexture(); } gpu::TextureSourcePointer _textureSource; + + static std::function getTextureForUUIDOperator(const QUuid& uuid); + static void setUnboundTextureForUUIDOperator(std::function textureForUUIDOperator); + +private: + static std::function _unboundTextureForUUIDOperator; }; /// A texture loaded from the network. @@ -52,10 +58,10 @@ public: QString getType() const override { return "NetworkTexture"; } - int getOriginalWidth() const { return _originalWidth; } - int getOriginalHeight() const { return _originalHeight; } - int getWidth() const { return _width; } - int getHeight() const { return _height; } + int getOriginalWidth() const { return _textureSource->getGPUTexture() ? _textureSource->getGPUTexture()->getOriginalWidth() : 0; } + int getOriginalHeight() const { return _textureSource->getGPUTexture() ? _textureSource->getGPUTexture()->getOriginalHeight() : 0; } + int getWidth() const { return _textureSource->getGPUTexture() ? _textureSource->getGPUTexture()->getWidth() : 0; } + int getHeight() const { return _textureSource->getGPUTexture() ? _textureSource->getGPUTexture()->getHeight() : 0; } image::TextureUsage::Type getTextureType() const { return _type; } gpu::TexturePointer getFallbackTexture() const; @@ -86,6 +92,7 @@ protected: Q_INVOKABLE void loadTextureContent(const QByteArray& content); Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); + void setImageOperator(std::function textureOperator); Q_INVOKABLE void startRequestForNextMipLevel(); @@ -136,8 +143,6 @@ private: // mip offsets to change. ktx::KTXDescriptorPointer _originalKtxDescriptor; - int _originalWidth { 0 }; - int _originalHeight { 0 }; int _width { 0 }; int _height { 0 }; int _maxNumPixels { ABSOLUTE_MAX_TEXTURE_NUM_PIXELS }; @@ -192,6 +197,8 @@ public: const gpu::FramebufferPointer& getSpectatorCameraFramebuffer(int width, int height); void updateSpectatorCameraNetworkTexture(); + NetworkTexturePointer getTextureByUUID(const QString& uuid); + static const int DEFAULT_SPECTATOR_CAM_WIDTH { 2048 }; static const int DEFAULT_SPECTATOR_CAM_HEIGHT { 1024 }; diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h index ee301d0a72..39c619c687 100644 --- a/libraries/procedural/src/procedural/Procedural.h +++ b/libraries/procedural/src/procedural/Procedural.h @@ -40,7 +40,7 @@ const size_t MAX_PROCEDURAL_TEXTURE_CHANNELS{ 4 }; * If a procedural material contains a vertex shader, the bounding box of the material entity is used to cull the object to which the material is applied. * @property {string} fragmentShaderURL - A link to a fragment shader. Currently, only GLSL shaders are supported. The shader must implement a different method depending on the version. * shaderUrl is an alias. - * @property {string[]} channels=[] - An array of input texture URLs. Currently, up to 4 are supported. + * @property {string[]} channels=[] - An array of input texture URLs or entity IDs. Currently, up to 4 are supported. An entity ID may be that of an Image or Web entity. * @property {ProceduralUniforms} uniforms={} - A {@link ProceduralUniforms} object containing all the custom uniforms to be passed to the shader. */ @@ -212,26 +212,26 @@ public: ProceduralMaterial() : NetworkMaterial() { initializeProcedural(); } ProceduralMaterial(const NetworkMaterial& material) : NetworkMaterial(material) { initializeProcedural(); } - bool isProcedural() const override { return true; } - bool isEnabled() const override { return _procedural.isEnabled(); } - bool isReady() const override { return _procedural.isReady(); } - QString getProceduralString() const override { return _proceduralString; } + virtual bool isProcedural() const override { return true; } + virtual bool isEnabled() const override { return _procedural.isEnabled(); } + virtual bool isReady() const override { return _procedural.isReady(); } + virtual QString getProceduralString() const override { return _proceduralString; } void setProceduralData(const QString& data) { _proceduralString = data; _procedural.setProceduralData(ProceduralData::parse(data)); } - glm::vec4 getColor(const glm::vec4& color) const { return _procedural.getColor(color); } - bool isFading() const { return _procedural.isFading(); } + virtual glm::vec4 getColor(const glm::vec4& color) const { return _procedural.getColor(color); } + virtual bool isFading() const { return _procedural.isFading(); } void setIsFading(bool isFading) { _procedural.setIsFading(isFading); } - uint64_t getFadeStartTime() const { return _procedural.getFadeStartTime(); } - bool hasVertexShader() const { return _procedural.hasVertexShader(); } - void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size, const glm::quat& orientation, + virtual uint64_t getFadeStartTime() const { return _procedural.getFadeStartTime(); } + virtual bool hasVertexShader() const { return _procedural.hasVertexShader(); } + virtual void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size, const glm::quat& orientation, const uint64_t& created, const ProceduralProgramKey key = ProceduralProgramKey()) { _procedural.prepare(batch, position, size, orientation, created, key); } - void initializeProcedural(); + virtual void initializeProcedural(); void setBoundOperator(const std::function& boundOperator) { _procedural.setBoundOperator(boundOperator); } bool hasBoundOperator() const { return _procedural.hasBoundOperator(); } @@ -243,4 +243,4 @@ private: }; typedef std::shared_ptr ProceduralMaterialPointer; -} \ No newline at end of file +} diff --git a/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp b/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp index af31418bf0..d99f74769c 100644 --- a/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp +++ b/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp @@ -14,6 +14,7 @@ #include "RegisteredMetaTypes.h" #include "Procedural.h" +#include "ReferenceMaterial.h" NetworkMaterialResource::NetworkMaterialResource(const QUrl& url) : Resource(url) {} @@ -75,7 +76,8 @@ bool NetworkMaterialResource::parseJSONColor(const QJsonValue& array, glm::vec3& * A material or set of materials used by a {@link Entities.EntityType|Material entity}. * @typedef {object} Entities.MaterialResource * @property {number} materialVersion=1 - The version of the material. Currently not used. - * @property {Entities.Material|Entities.Material[]} materials - The details of the material or materials. + * @property {Entities.Material|Entities.Material[]|string} materials - The details of the material or materials, or the ID of another + * Material entity. */ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMaterials(const QJsonDocument& materialJSON, const QUrl& baseUrl) { ParsedMaterials toReturn; @@ -92,14 +94,14 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater if (materialsValue.isArray()) { QJsonArray materials = materialsValue.toArray(); for (auto material : materials) { - if (!material.isNull() && material.isObject()) { - auto parsedMaterial = parseJSONMaterial(material.toObject(), baseUrl); + if (!material.isNull() && (material.isObject() || material.isString())) { + auto parsedMaterial = parseJSONMaterial(material, baseUrl); toReturn.networkMaterials[parsedMaterial.first] = parsedMaterial.second; toReturn.names.push_back(parsedMaterial.first); } } - } else if (materialsValue.isObject()) { - auto parsedMaterial = parseJSONMaterial(materialsValue.toObject(), baseUrl); + } else if (materialsValue.isObject() || materialsValue.isString()) { + auto parsedMaterial = parseJSONMaterial(materialsValue, baseUrl); toReturn.networkMaterials[parsedMaterial.first] = parsedMaterial.second; toReturn.names.push_back(parsedMaterial.first); } @@ -110,6 +112,17 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater return toReturn; } +NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseMaterialForUUID(const QJsonValue& entityIDJSON) { + ParsedMaterials toReturn; + if (!entityIDJSON.isNull() && entityIDJSON.isString()) { + auto parsedMaterial = parseJSONMaterial(entityIDJSON); + toReturn.networkMaterials[parsedMaterial.first] = parsedMaterial.second; + toReturn.names.push_back(parsedMaterial.first); + } + + return toReturn; +} + /*@jsdoc * A material used in a {@link Entities.MaterialResource|MaterialResource}. * @typedef {object} Entities.Material @@ -136,12 +149,14 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. * @property {number|string} scattering - The scattering, range 0.01.0. * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. - * @property {string} emissiveMap - The URL of the emissive texture image. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. - * @property {string} albedoMap - The URL of the albedo texture image. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. - * @property {string} opacityMap - The URL of the opacity texture image. Set the value the same as the albedoMap - * value for transparency. + * @property {string} emissiveMap - The URL of the emissive texture image, or an entity ID. An entity ID may be that of an + * Image or Web entity. Set to "fallthrough" to fall through to the material below. + * "hifi_pbr" model only. + * @property {string} albedoMap - The URL of the albedo texture image, or an entity ID. An entity ID may be that of an Image + * or Web entity. Set to "fallthrough" to fall through to the material below. "hifi_pbr" + * model only. + * @property {string} opacityMap - The URL of the opacity texture image, or an entity ID. An entity ID may be that of an Image + * or Web entity. Set the value the same as the albedoMap value for transparency. * "hifi_pbr" model only. * @property {string} opacityMapMode - The mode defining the interpretation of the opacity map. Values can be: *
    @@ -176,23 +191,27 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater * @property {string} glossMap - The URL of the gloss texture image. You can use this or roughnessMap, but not * both. * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. - * @property {string} metallicMap - The URL of the metallic texture image. You can use this or specularMap, but - * not both. + * @property {string} metallicMap - The URL of the metallic texture image, or an entity ID. An entity ID may be that of an + * Image or Web entity. You can use this or specularMap, but not both. * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. - * @property {string} specularMap - The URL of the specular texture image. You can use this or metallicMap, but - * not both. + * @property {string} specularMap - The URL of the specular texture image, or an entity ID. An entity ID may be that of an + * Image or Web entity. You can use this or metallicMap, but not both. * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. - * @property {string} normalMap - The URL of the normal texture image. You can use this or bumpMap, but not both. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. - * @property {string} bumpMap - The URL of the bump texture image. You can use this or normalMap, but not both. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. - * @property {string} occlusionMap - The URL of the occlusion texture image. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. - * @property {string} scatteringMap - The URL of the scattering texture image. Only used if normalMap or - * bumpMap is specified. - * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. - * @property {string} lightMap - The URL of the light map texture image. + * @property {string} normalMap - The URL of the normal texture image, or an entity ID. An entity ID may be that of an Image + * or Web entity. You can use this or bumpMap, but not both. Set to "fallthrough" to fall + * through to the material below. "hifi_pbr" model only. + * @property {string} bumpMap - The URL of the bump texture image, or an entity ID. An entity ID may be that of an Image + * or Web entity. You can use this or normalMap, but not both. Set to "fallthrough" to + * fall through to the material below. "hifi_pbr" model only. + * @property {string} occlusionMap - The URL of the occlusion texture image, or an entity ID. An entity ID may be that of + * an Image or Web entity. Set to "fallthrough" to fall through to the material below. + * "hifi_pbr" model only. + * @property {string} scatteringMap - The URL of the scattering texture image, or an entity ID. An entity ID may be that of an + * Image or Web entity. Only used if normalMap or bumpMap is specified. * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * @property {string} lightMap - The URL of the light map texture image, or an entity ID. An entity ID may be that of an Image + * or Web entity. Set to "fallthrough" to fall through to the material below. "hifi_pbr" + * model only. * @property {Mat4|string} texCoordTransform0 - The transform to use for all of the maps apart from occlusionMap * and lightMap. * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. @@ -210,10 +229,22 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater * @property {ProceduralData} procedural - The definition of a procedural shader material. "hifi_shader_simple" model only. */ // Note: See MaterialEntityItem.h for default values used in practice. -std::pair> NetworkMaterialResource::parseJSONMaterial(const QJsonObject& materialJSON, const QUrl& baseUrl) { +std::pair> NetworkMaterialResource::parseJSONMaterial(const QJsonValue& materialJSONValue, const QUrl& baseUrl) { std::string name = ""; std::shared_ptr networkMaterial; + if (materialJSONValue.isString()) { + QString uuidString = materialJSONValue.toString(); + name = uuidString.toStdString(); + QUuid uuid = QUuid(uuidString); + if (!uuid.isNull()) { + networkMaterial = std::make_shared(uuid); + } + return std::pair>(name, networkMaterial); + } + + QJsonObject materialJSON = materialJSONValue.toObject(); + std::string modelString = graphics::Material::HIFI_PBR; auto modelJSONIter = materialJSON.find("model"); if (modelJSONIter != materialJSON.end() && modelJSONIter.value().isString()) { diff --git a/libraries/procedural/src/procedural/ProceduralMaterialCache.h b/libraries/procedural/src/procedural/ProceduralMaterialCache.h index 0a44ccf0ef..ff0a8cf81c 100644 --- a/libraries/procedural/src/procedural/ProceduralMaterialCache.h +++ b/libraries/procedural/src/procedural/ProceduralMaterialCache.h @@ -33,8 +33,8 @@ public: void setScatteringMap(const QUrl& url); void setLightMap(const QUrl& url); - bool isMissingTexture(); - bool checkResetOpacityMap(); + virtual bool isMissingTexture(); + virtual bool checkResetOpacityMap(); class Texture { public: @@ -100,7 +100,8 @@ public: ParsedMaterials parsedMaterials; static ParsedMaterials parseJSONMaterials(const QJsonDocument& materialJSON, const QUrl& baseUrl); - static std::pair> parseJSONMaterial(const QJsonObject& materialJSON, const QUrl& baseUrl); + static ParsedMaterials parseMaterialForUUID(const QJsonValue& entityIDJSON); + static std::pair> parseJSONMaterial(const QJsonValue& materialJSONValue, const QUrl& baseUrl = QUrl()); private: static bool parseJSONColor(const QJsonValue& array, glm::vec3& color, bool& isSRGB); diff --git a/libraries/procedural/src/procedural/ReferenceMaterial.cpp b/libraries/procedural/src/procedural/ReferenceMaterial.cpp new file mode 100644 index 0000000000..97211eb737 --- /dev/null +++ b/libraries/procedural/src/procedural/ReferenceMaterial.cpp @@ -0,0 +1,266 @@ +// +// Created by HifiExperiments on 3/14/2021 +// Copyright 2021 Vircadia contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ReferenceMaterial.h" + +std::function ReferenceMaterial::_unboundMaterialForUUIDOperator = nullptr; + +ReferenceMaterial::ReferenceMaterial(QUuid uuid) : + graphics::ProceduralMaterial() { + if (_unboundMaterialForUUIDOperator) { + _materialForUUIDOperator = std::bind(_unboundMaterialForUUIDOperator, uuid); + } +} + +// Material +graphics::MaterialKey ReferenceMaterial::getKey() const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->getKey() : Parent::getKey(); + }); +} + +glm::vec3 ReferenceMaterial::getEmissive(bool SRGB) const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->getEmissive(SRGB) : glm::vec3(DEFAULT_EMISSIVE); + }); +} + +float ReferenceMaterial::getOpacity() const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->getOpacity() : DEFAULT_OPACITY; + }); +} + +graphics::MaterialKey::OpacityMapMode ReferenceMaterial::getOpacityMapMode() const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->getOpacityMapMode() : DEFAULT_OPACITY_MAP_MODE; + }); +} + +float ReferenceMaterial::getOpacityCutoff() const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->getOpacityCutoff() : DEFAULT_OPACITY_CUTOFF; + }); +} + +graphics::MaterialKey::CullFaceMode ReferenceMaterial::getCullFaceMode() const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->getCullFaceMode() : DEFAULT_CULL_FACE_MODE; + }); +} + +bool ReferenceMaterial::isUnlit() const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->isUnlit() : false; + }); +} + +glm::vec3 ReferenceMaterial::getAlbedo(bool SRGB) const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->getAlbedo(SRGB) : glm::vec3(DEFAULT_ALBEDO); + }); +} + +float ReferenceMaterial::getMetallic() const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->getMetallic() : DEFAULT_METALLIC; + }); +} + +float ReferenceMaterial::getRoughness() const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->getRoughness() : DEFAULT_ROUGHNESS; + }); +} + +float ReferenceMaterial::getScattering() const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->getScattering() : DEFAULT_SCATTERING; + }); +} + +bool ReferenceMaterial::resetOpacityMap() const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->resetOpacityMap() : false; + }); +} + +graphics::Material::TextureMaps ReferenceMaterial::getTextureMaps() const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->getTextureMaps() : Parent::getTextureMaps(); + }); +} + +glm::vec2 ReferenceMaterial::getLightmapParams() const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->getLightmapParams() : glm::vec2(0.0f, 1.0f); + }); +} + +bool ReferenceMaterial::getDefaultFallthrough() const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->getDefaultFallthrough() : false; + }); +} + +// NetworkMaterial +bool ReferenceMaterial::isMissingTexture() { + return resultWithLock([&] { + auto material = getNetworkMaterial(); + return material ? material->isMissingTexture() : false; + }); +} + +bool ReferenceMaterial::checkResetOpacityMap() { + return resultWithLock([&] { + auto material = getNetworkMaterial(); + return material ? material->checkResetOpacityMap() : false; + }); +} + +// ProceduralMaterial +bool ReferenceMaterial::isProcedural() const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->isProcedural() : false; + }); +} + +bool ReferenceMaterial::isEnabled() const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->isEnabled() : false; + }); +} + +bool ReferenceMaterial::isReady() const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->isReady() : false; + }); +} + +QString ReferenceMaterial::getProceduralString() const { + return resultWithLock([&] { + auto material = getMaterial(); + return material ? material->getProceduralString() : QString(); + }); +} + +glm::vec4 ReferenceMaterial::getColor(const glm::vec4& color) const { + return resultWithLock([&] { + auto material = getProceduralMaterial(); + return material ? material->getColor(color) : glm::vec4(); + }); +} + +bool ReferenceMaterial::isFading() const { + return resultWithLock([&] { + auto material = getProceduralMaterial(); + return material ? material->isFading() : false; + }); +} + +uint64_t ReferenceMaterial::getFadeStartTime() const { + return resultWithLock([&] { + auto material = getProceduralMaterial(); + return material ? material->getFadeStartTime() : 0; + }); +} + +bool ReferenceMaterial::hasVertexShader() const { + return resultWithLock([&] { + auto material = getProceduralMaterial(); + return material ? material->hasVertexShader() : false; + }); +} + +void ReferenceMaterial::prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size, const glm::quat& orientation, + const uint64_t& created, const ProceduralProgramKey key) { + withLock([&] { + if (auto material = getProceduralMaterial()) { + material->prepare(batch, position, size, orientation, created, key); + } + }); +} + +void ReferenceMaterial::initializeProcedural() { + withLock([&] { + if (auto material = getProceduralMaterial()) { + material->initializeProcedural(); + } + }); +} + +void ReferenceMaterial::setMaterialForUUIDOperator(std::function materialForUUIDOperator) { + _unboundMaterialForUUIDOperator = materialForUUIDOperator; +} + +graphics::MaterialPointer ReferenceMaterial::getMaterial() const { + if (_materialForUUIDOperator) { + auto material = _materialForUUIDOperator(); + return material; + } + return nullptr; +} + +std::shared_ptr ReferenceMaterial::getNetworkMaterial() const { + if (_materialForUUIDOperator) { + std::shared_ptr result = nullptr; + if (auto material = _materialForUUIDOperator()) { + return std::static_pointer_cast(material); + } + } + return nullptr; +} + +graphics::ProceduralMaterialPointer ReferenceMaterial::getProceduralMaterial() const { + if (_materialForUUIDOperator) { + auto material = _materialForUUIDOperator(); + if (material && material->isProcedural()) { + return std::static_pointer_cast(material); + } + } + return nullptr; +} + +template +inline T ReferenceMaterial::resultWithLock(F&& f) const { + if (_locked) { + return T(); + } else { + _locked = true; + T result = f(); + _locked = false; + return result; + } +} + +template +inline void ReferenceMaterial::withLock(F&& f) const { + if (!_locked) { + _locked = true; + f(); + _locked = false; + } +} diff --git a/libraries/procedural/src/procedural/ReferenceMaterial.h b/libraries/procedural/src/procedural/ReferenceMaterial.h new file mode 100644 index 0000000000..ac778f94b1 --- /dev/null +++ b/libraries/procedural/src/procedural/ReferenceMaterial.h @@ -0,0 +1,74 @@ +// +// Created by HifiExperiments on 3/14/2021 +// Copyright 2021 Vircadia contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once + +#include "Procedural.h" + +class ReferenceMaterial : public graphics::ProceduralMaterial { +public: + using Parent = graphics::ProceduralMaterial; + + ReferenceMaterial() {} + ReferenceMaterial(QUuid uuid); + + // Material + graphics::MaterialKey getKey() const override; + glm::vec3 getEmissive(bool SRGB = true) const override; + float getOpacity() const override; + graphics::MaterialKey::OpacityMapMode getOpacityMapMode() const override; + float getOpacityCutoff() const override; + graphics::MaterialKey::CullFaceMode getCullFaceMode() const override; + bool isUnlit() const override; + glm::vec3 getAlbedo(bool SRGB = true) const override; + float getMetallic() const override; + float getRoughness() const override; + float getScattering() const override; + bool resetOpacityMap() const override; + graphics::Material::TextureMaps getTextureMaps() const override; + glm::vec2 getLightmapParams() const override; + bool getDefaultFallthrough() const override; + + // NetworkMaterial + bool isMissingTexture() override; + bool checkResetOpacityMap() override; + + // ProceduralMaterial + bool isProcedural() const override; + bool isEnabled() const override; + bool isReady() const override; + QString getProceduralString() const override; + + glm::vec4 getColor(const glm::vec4& color) const override; + bool isFading() const override; + uint64_t getFadeStartTime() const override; + bool hasVertexShader() const override; + void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size, const glm::quat& orientation, + const uint64_t& created, const ProceduralProgramKey key = ProceduralProgramKey()) override; + void initializeProcedural() override; + + bool isReference() const override { return true; } + std::function getReferenceOperator() const { return _materialForUUIDOperator; } + + static void setMaterialForUUIDOperator(std::function materialForUUIDOperator); + +private: + static std::function _unboundMaterialForUUIDOperator; + std::function _materialForUUIDOperator; + mutable bool _locked { false }; + + graphics::MaterialPointer getMaterial() const; + std::shared_ptr getNetworkMaterial() const; + graphics::ProceduralMaterialPointer getProceduralMaterial() const; + + template + T resultWithLock(F&& f) const; + + template + void withLock(F&& f) const; +}; diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index fe42cc1d0e..ed9fb326c4 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "render-utils/ShaderConstants.h" #include "StencilMaskPass.h" @@ -378,6 +379,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial auto& drawMaterialTextures = multiMaterial.getTextureTable(); multiMaterial.setTexturesLoading(false); + multiMaterial.resetReferenceTexturesAndMaterials(); // The total list of things we need to look for static std::set allFlags; @@ -408,9 +410,13 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial } materials.pop(); + if (material->isReference()) { + multiMaterial.addReferenceMaterial(std::static_pointer_cast(material)->getReferenceOperator()); + } + bool defaultFallthrough = material->getDefaultFallthrough(); - const auto& materialKey = material->getKey(); - const auto& textureMaps = material->getTextureMaps(); + const auto materialKey = material->getKey(); + const auto textureMaps = material->getTextureMaps(); auto it = flagsToCheck.begin(); while (it != flagsToCheck.end()) { @@ -482,6 +488,9 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr->second->isDefined()) { material->resetOpacityMap(); drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } wasSet = true; } else { multiMaterial.setTexturesLoading(true); @@ -501,6 +510,9 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialMetallic, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } wasSet = true; } else { multiMaterial.setTexturesLoading(true); @@ -518,6 +530,9 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialRoughness, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } wasSet = true; } else { multiMaterial.setTexturesLoading(true); @@ -535,6 +550,9 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } wasSet = true; } else { multiMaterial.setTexturesLoading(true); @@ -552,6 +570,9 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } wasSet = true; } else { multiMaterial.setTexturesLoading(true); @@ -569,6 +590,9 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialScattering, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } wasSet = true; } else { multiMaterial.setTexturesLoading(true); @@ -587,6 +611,9 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } wasSet = true; } else { multiMaterial.setTexturesLoading(true); @@ -607,6 +634,9 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); + if (itr->second->getTextureView().isReference()) { + multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); + } wasSet = true; } else { multiMaterial.setTexturesLoading(true); diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index 253b96998f..65e14d7203 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -918,7 +918,6 @@ const GROUPS = [ label: "Material Scale", type: "vec2", vec2Type: "xyz", - min: 0, step: 0.1, decimals: 4, subLabels: [ "x", "y" ],