diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a1c6efa32d..f9ea7fa666 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2107,6 +2107,23 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo } return false; }); + EntityTree::setGetUnscaledDimensionsForEntityIDOperator([this](const QUuid& id) { + if (_aboutToQuit) { + return glm::vec3(1.0f); + } + + auto entity = getEntities()->getEntity(id); + if (entity) { + return entity->getUnscaledDimensions(); + } + + auto avatarManager = DependencyManager::get(); + auto avatar = static_pointer_cast(avatarManager->getAvatarBySessionID(id)); + if (avatar) { + return avatar->getSNScale(); + } + return glm::vec3(1.0f); + }); Procedural::opaqueStencil = [](gpu::StatePointer state) { PrepareStencil::testMaskDrawShape(*state); }; Procedural::transparentStencil = [](gpu::StatePointer state) { PrepareStencil::testMask(*state); }; diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index a013ff75b7..af121f2957 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -153,13 +153,13 @@ void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo if (urlChanged && !usingMaterialData) { _networkMaterial = DependencyManager::get()->getMaterial(_materialURL); - auto onMaterialRequestFinished = [this, oldParentID, oldParentMaterialName, newCurrentMaterialName](bool success) { + auto onMaterialRequestFinished = [this, entity, oldParentID, oldParentMaterialName, newCurrentMaterialName](bool success) { if (success) { deleteMaterial(oldParentID, oldParentMaterialName); _texturesLoaded = false; _parsedMaterials = _networkMaterial->parsedMaterials; setCurrentMaterialName(newCurrentMaterialName); - applyMaterial(); + applyMaterial(entity); } else { deleteMaterial(oldParentID, oldParentMaterialName); _retryApply = false; @@ -183,13 +183,13 @@ void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo _parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument::fromJson(_materialData.toUtf8()), _materialURL); // Since our material changed, the current name might not be valid anymore, so we need to update setCurrentMaterialName(newCurrentMaterialName); - applyMaterial(); + applyMaterial(entity); } else { if (deleteNeeded) { deleteMaterial(oldParentID, oldParentMaterialName); } if (addNeeded) { - applyMaterial(); + applyMaterial(entity); } } @@ -382,7 +382,7 @@ void MaterialEntityRenderer::applyTextureTransform(std::shared_ptrsetTextureTransforms(textureTransform, _materialMappingMode, _materialRepeat); } -void MaterialEntityRenderer::applyMaterial() { +void MaterialEntityRenderer::applyMaterial(const TypedEntityPointer& entity) { _retryApply = false; std::shared_ptr material = getMaterial(); @@ -396,6 +396,11 @@ void MaterialEntityRenderer::applyMaterial() { graphics::MaterialLayer materialLayer = graphics::MaterialLayer(material, _priority); + if (auto procedural = std::static_pointer_cast(material)) { + procedural->setBoundOperator([this] { return getBound(); }); + entity->setHasVertexShader(procedural->hasVertexShader()); + } + // Our parent could be an entity or an avatar std::string parentMaterialName = _parentMaterialName.toStdString(); if (EntityTreeRenderer::addMaterialToEntity(parentID, materialLayer, parentMaterialName)) { diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.h b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h index ff7367a44e..3a73c988eb 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.h +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h @@ -56,7 +56,7 @@ private: void setCurrentMaterialName(const std::string& currentMaterialName); void applyTextureTransform(std::shared_ptr& material); - void applyMaterial(); + void applyMaterial(const TypedEntityPointer& entity); void deleteMaterial(const QUuid& oldParentID, const QString& oldParentMaterialName); NetworkMaterialResourcePointer _networkMaterial; diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 88cc78b6b6..497294e45a 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -207,6 +207,18 @@ ShapeKey ShapeEntityRenderer::getShapeKey() { return builder.build(); } +Item::Bound ShapeEntityRenderer::getBound() { + auto mat = _materials.find("0"); + if (mat != _materials.end() && mat->second.top().material && mat->second.top().material->isProcedural() && + mat->second.top().material->isReady()) { + auto procedural = std::static_pointer_cast(mat->second.top().material); + if (procedural->hasVertexShader() && procedural->hasBoundOperator()) { + return procedural->getBound(); + } + } + return Parent::getBound(); +} + void ShapeEntityRenderer::doRender(RenderArgs* args) { PerformanceTimer perfTimer("RenderableShapeEntityItem::render"); Q_ASSERT(args->_batch); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 6061526f75..5bc61606ad 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -26,6 +26,7 @@ public: protected: ShapeKey getShapeKey() override; + Item::Bound getBound() override; private: virtual bool needsRenderUpdate() const override; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index fed9090ddb..01962bb79d 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -3154,6 +3154,7 @@ std::function EntityTree::_getEntityObjectOperator = nul std::function EntityTree::_textSizeOperator = nullptr; std::function EntityTree::_areEntityClicksCapturedOperator = nullptr; std::function EntityTree::_emitScriptEventOperator = nullptr; +std::function EntityTree::_getUnscaledDimensionsForEntityIDOperator = nullptr; QObject* EntityTree::getEntityObject(const QUuid& id) { if (_getEntityObjectOperator) { @@ -3182,6 +3183,13 @@ void EntityTree::emitScriptEvent(const QUuid& id, const QVariant& message) { } } +glm::vec3 EntityTree::getUnscaledDimensionsForEntityID(const QUuid& id) { + if (_getUnscaledDimensionsForEntityIDOperator) { + return _getUnscaledDimensionsForEntityIDOperator(id); + } + return glm::vec3(1.0f); +} + void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, MovingEntitiesOperator& moveOperator, bool force, bool tellServer) { // if the queryBox has changed, tell the entity-server diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 7b84e40ddd..4c942bf40a 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -273,6 +273,9 @@ public: static void setEmitScriptEventOperator(std::function emitScriptEventOperator) { _emitScriptEventOperator = emitScriptEventOperator; } static void emitScriptEvent(const QUuid& id, const QVariant& message); + static void setGetUnscaledDimensionsForEntityIDOperator(std::function getUnscaledDimensionsForEntityIDOperator) { _getUnscaledDimensionsForEntityIDOperator = getUnscaledDimensionsForEntityIDOperator; } + static glm::vec3 getUnscaledDimensionsForEntityID(const QUuid& id); + std::map getNamedPaths() const { return _namedPaths; } void updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, @@ -389,6 +392,7 @@ private: static std::function _textSizeOperator; static std::function _areEntityClicksCapturedOperator; static std::function _emitScriptEventOperator; + static std::function _getUnscaledDimensionsForEntityIDOperator; std::vector _staleProxies; diff --git a/libraries/entities/src/MaterialEntityItem.cpp b/libraries/entities/src/MaterialEntityItem.cpp index 1a7c3c601b..22143e88ba 100644 --- a/libraries/entities/src/MaterialEntityItem.cpp +++ b/libraries/entities/src/MaterialEntityItem.cpp @@ -139,10 +139,10 @@ void MaterialEntityItem::debugDump() const { void MaterialEntityItem::setUnscaledDimensions(const glm::vec3& value) { _desiredDimensions = value; - if (_materialMappingMode == MaterialMappingMode::UV) { - EntityItem::setUnscaledDimensions(ENTITY_ITEM_DEFAULT_DIMENSIONS); - } else if (_materialMappingMode == MaterialMappingMode::PROJECTED) { + if (_hasVertexShader || _materialMappingMode == MaterialMappingMode::PROJECTED) { EntityItem::setUnscaledDimensions(value); + } else if (_materialMappingMode == MaterialMappingMode::UV) { + EntityItem::setUnscaledDimensions(ENTITY_ITEM_DEFAULT_DIMENSIONS); } } @@ -264,6 +264,13 @@ void MaterialEntityItem::setMaterialRepeat(bool value) { }); } +void MaterialEntityItem::setParentID(const QUuid& parentID) { + if (parentID != getParentID()) { + EntityItem::setParentID(parentID); + _hasVertexShader = false; + } +} + AACube MaterialEntityItem::calculateInitialQueryAACube(bool& success) { AACube aaCube = EntityItem::calculateInitialQueryAACube(success); // A Material entity's queryAACube contains its parent's queryAACube @@ -278,3 +285,16 @@ AACube MaterialEntityItem::calculateInitialQueryAACube(bool& success) { } return aaCube; } + +void MaterialEntityItem::setHasVertexShader(bool hasVertexShader) { + bool prevHasVertexShader = _hasVertexShader; + _hasVertexShader = hasVertexShader; + + if (hasVertexShader && !prevHasVertexShader) { + setLocalPosition(glm::vec3(0.0f)); + setLocalOrientation(glm::quat()); + setUnscaledDimensions(EntityTree::getUnscaledDimensionsForEntityID(getParentID())); + } else if (!hasVertexShader && prevHasVertexShader) { + setUnscaledDimensions(_desiredDimensions); + } +} \ No newline at end of file diff --git a/libraries/entities/src/MaterialEntityItem.h b/libraries/entities/src/MaterialEntityItem.h index 3f32486f0b..d8de8c3bc6 100644 --- a/libraries/entities/src/MaterialEntityItem.h +++ b/libraries/entities/src/MaterialEntityItem.h @@ -64,6 +64,8 @@ public: QString getParentMaterialName() const; void setParentMaterialName(const QString& parentMaterialName); + void setParentID(const QUuid& parentID) override; + glm::vec2 getMaterialMappingPos() const; void setMaterialMappingPos(const glm::vec2& materialMappingPos); glm::vec2 getMaterialMappingScale() const; @@ -73,6 +75,8 @@ public: AACube calculateInitialQueryAACube(bool& success) override; + void setHasVertexShader(bool hasVertexShader); + private: // URL for this material. Currently, only JSON format is supported. Set to "materialData" to use the material data to live edit a material. // The following fields are supported in the JSON: @@ -108,6 +112,8 @@ private: float _materialMappingRot { 0 }; QString _materialData; + bool _hasVertexShader { false }; + }; #endif // hifi_MaterialEntityItem_h diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index 43c6b25dcb..22bfc3f1ad 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -29,6 +29,7 @@ Q_LOGGING_CATEGORY(proceduralLog, "hifi.gpu.procedural") // User-data parsing constants static const QString PROCEDURAL_USER_DATA_KEY = "ProceduralEntity"; +static const QString VERTEX_URL_KEY = "vertexShaderURL"; static const QString FRAGMENT_URL_KEY = "fragmentShaderURL"; static const QString URL_KEY = "shaderUrl"; static const QString VERSION_KEY = "version"; @@ -42,6 +43,7 @@ static const std::string PROCEDURAL_VERSION = "//PROCEDURAL_VERSION"; bool operator==(const ProceduralData& a, const ProceduralData& b) { return ((a.version == b.version) && (a.fragmentShaderUrl == b.fragmentShaderUrl) && + (a.vertexShaderUrl == b.vertexShaderUrl) && (a.uniforms == b.uniforms) && (a.channels == b.channels)); } @@ -101,9 +103,9 @@ void ProceduralData::parse(const QJsonObject& proceduralData) { } } - // Empty shader URL isn't valid - if (fragmentShaderUrl.isEmpty()) { - return; + { // Vertex shader URL + auto rawShaderUrl = proceduralData[VERTEX_URL_KEY].toString(); + vertexShaderUrl = DependencyManager::get()->normalizeURL(rawShaderUrl); } uniforms = proceduralData[UNIFORMS_KEY].toObject(); @@ -172,29 +174,57 @@ void Procedural::setProceduralData(const ProceduralData& proceduralData) { if (proceduralData.fragmentShaderUrl != _data.fragmentShaderUrl) { _data.fragmentShaderUrl = proceduralData.fragmentShaderUrl; - const auto& shaderUrl = _data.fragmentShaderUrl; _shaderDirty = true; _networkFragmentShader.reset(); _fragmentShaderPath.clear(); _fragmentShaderSource.clear(); - if (shaderUrl.isEmpty() || !shaderUrl.isValid()) { + if (!_data.fragmentShaderUrl.isValid()) { + qCWarning(proceduralLog) << "Invalid fragment shader URL: " << _data.fragmentShaderUrl; return; } - if (shaderUrl.isLocalFile()) { - if (!QFileInfo(shaderUrl.toLocalFile()).exists()) { + if (_data.fragmentShaderUrl.isLocalFile()) { + if (!QFileInfo(_data.fragmentShaderUrl.toLocalFile()).exists()) { + qCWarning(proceduralLog) << "Invalid fragment shader URL, missing local file: " << _data.fragmentShaderUrl; return; } - _fragmentShaderPath = shaderUrl.toLocalFile(); - } else if (shaderUrl.scheme() == URL_SCHEME_QRC) { - _fragmentShaderPath = ":" + shaderUrl.path(); + _fragmentShaderPath = _data.fragmentShaderUrl.toLocalFile(); + } else if (_data.fragmentShaderUrl.scheme() == URL_SCHEME_QRC) { + _fragmentShaderPath = ":" + _data.fragmentShaderUrl.path(); } else { - _networkFragmentShader = ShaderCache::instance().getShader(shaderUrl); + _networkFragmentShader = ShaderCache::instance().getShader(_data.fragmentShaderUrl); } } + if (proceduralData.vertexShaderUrl != _data.vertexShaderUrl) { + _data.vertexShaderUrl = proceduralData.vertexShaderUrl; + + _shaderDirty = true; + _networkVertexShader.reset(); + _vertexShaderPath.clear(); + _vertexShaderSource.clear(); + + if (!_data.vertexShaderUrl.isValid()) { + qCWarning(proceduralLog) << "Invalid vertex shader URL: " << _data.vertexShaderUrl; + return; + } + + if (_data.vertexShaderUrl.isLocalFile()) { + if (!QFileInfo(_data.vertexShaderUrl.toLocalFile()).exists()) { + qCWarning(proceduralLog) << "Invalid vertex shader URL, missing local file: " << _data.vertexShaderUrl; + return; + } + _vertexShaderPath = _data.vertexShaderUrl.toLocalFile(); + } else if (_data.vertexShaderUrl.scheme() == URL_SCHEME_QRC) { + _vertexShaderPath = ":" + _data.vertexShaderUrl.path(); + } else { + _networkVertexShader = ShaderCache::instance().getShader(_data.vertexShaderUrl); + } + + } + _enabled = true; } @@ -213,8 +243,12 @@ bool Procedural::isReady() const { _fadeStartTime = usecTimestampNow(); } - // Do we have a network or local shader, and if so, is it loaded? - if (_fragmentShaderPath.isEmpty() && (!_networkFragmentShader || !_networkFragmentShader->isLoaded())) { + // We need to have at least one shader, and whichever ones we have need to be loaded + bool hasFragmentShader = !_fragmentShaderPath.isEmpty() || _networkFragmentShader; + bool fragmentShaderLoaded = !_fragmentShaderPath.isEmpty() || (_networkFragmentShader && _networkFragmentShader->isLoaded()); + bool hasVertexShader = !_vertexShaderPath.isEmpty() || _networkVertexShader; + bool vertexShaderLoaded = !_vertexShaderPath.isEmpty() || (_networkVertexShader && _networkVertexShader->isLoaded()); + if ((!hasFragmentShader && !hasVertexShader) || (hasFragmentShader && !fragmentShaderLoaded) || (hasVertexShader && !vertexShaderLoaded)) { return false; } @@ -258,6 +292,20 @@ void Procedural::prepare(gpu::Batch& batch, _shaderDirty = true; } + if (!_vertexShaderPath.isEmpty()) { + auto lastModified = (uint64_t)QFileInfo(_vertexShaderPath).lastModified().toMSecsSinceEpoch(); + if (lastModified > _vertexShaderModified) { + QFile file(_vertexShaderPath); + file.open(QIODevice::ReadOnly); + _vertexShaderSource = QTextStream(&file).readAll(); + _shaderDirty = true; + _vertexShaderModified = lastModified; + } + } else if (_vertexShaderSource.isEmpty() && _networkVertexShader && _networkVertexShader->isLoaded()) { + _vertexShaderSource = _networkVertexShader->_source; + _shaderDirty = true; + } + if (_shaderDirty) { _proceduralPipelines.clear(); } @@ -276,25 +324,42 @@ void Procedural::prepare(gpu::Batch& batch, gpu::Shader::Source& fragmentSource = (key.isTransparent() && _transparentFragmentSource.valid()) ? _transparentFragmentSource : _opaqueFragmentSource; - // Build the fragment shader + // Build the fragment and vertex shaders + auto versionDefine = "#define PROCEDURAL_V" + std::to_string(_data.version); fragmentSource.replacements.clear(); - fragmentSource.replacements[PROCEDURAL_VERSION] = "#define PROCEDURAL_V" + std::to_string(_data.version); - fragmentSource.replacements[PROCEDURAL_BLOCK] = _fragmentShaderSource.toStdString(); + fragmentSource.replacements[PROCEDURAL_VERSION] = versionDefine; + if (!_fragmentShaderSource.isEmpty()) { + fragmentSource.replacements[PROCEDURAL_BLOCK] = _fragmentShaderSource.toStdString(); + } + vertexSource.replacements.clear(); + vertexSource.replacements[PROCEDURAL_VERSION] = versionDefine; + if (!_vertexShaderSource.isEmpty()) { + vertexSource.replacements[PROCEDURAL_BLOCK] = _vertexShaderSource.toStdString(); + } // Set any userdata specified uniforms (if any) if (!_data.uniforms.empty()) { - // First grab all the possible dialect/variant/Reflections - std::vector allReflections; + // First grab all the possible dialect/variant/reflections + std::vector allFragmentReflections; for (auto dialectIt = fragmentSource.dialectSources.begin(); dialectIt != fragmentSource.dialectSources.end(); ++dialectIt) { for (auto variantIt = (*dialectIt).second.variantSources.begin(); variantIt != (*dialectIt).second.variantSources.end(); ++variantIt) { - allReflections.push_back(&(*variantIt).second.reflection); + allFragmentReflections.push_back(&(*variantIt).second.reflection); + } + } + std::vector allVertexReflections; + for (auto dialectIt = vertexSource.dialectSources.begin(); dialectIt != vertexSource.dialectSources.end(); ++dialectIt) { + for (auto variantIt = (*dialectIt).second.variantSources.begin(); variantIt != (*dialectIt).second.variantSources.end(); ++variantIt) { + allVertexReflections.push_back(&(*variantIt).second.reflection); } } // Then fill in every reflections the new custom bindings int customSlot = procedural::slot::uniform::Custom; for (const auto& key : _data.uniforms.keys()) { std::string uniformName = key.toLocal8Bit().data(); - for (auto reflection : allReflections) { + for (auto reflection : allFragmentReflections) { + reflection->uniforms[uniformName] = customSlot; + } + for (auto reflection : allVertexReflections) { reflection->uniforms[uniformName] = customSlot; } ++customSlot; @@ -303,6 +368,7 @@ void Procedural::prepare(gpu::Batch& batch, // Leave this here for debugging //qCDebug(proceduralLog) << "FragmentShader:\n" << fragmentSource.getSource(shader::Dialect::glsl450, shader::Variant::Mono).c_str(); + //qCDebug(proceduralLog) << "VertexShader:\n" << vertexSource.getSource(shader::Dialect::glsl450, shader::Variant::Mono).c_str(); gpu::ShaderPointer vertexShader = gpu::Shader::createVertex(vertexSource); gpu::ShaderPointer fragmentShader = gpu::Shader::createPixel(fragmentSource); @@ -453,6 +519,11 @@ glm::vec4 Procedural::getColor(const glm::vec4& entityColor) const { return entityColor; } +bool Procedural::hasVertexShader() const { + std::lock_guard lock(_mutex); + return !_data.vertexShaderUrl.isEmpty(); +} + void graphics::ProceduralMaterial::initializeProcedural() { _procedural._vertexSource = gpu::Shader::getVertexShaderSource(shader::render_utils::vertex::simple_procedural); _procedural._vertexSourceSkinned = gpu::Shader::getVertexShaderSource(shader::render_utils::vertex::simple_procedural_deformed); diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h index aac353bf7c..89f21218e6 100644 --- a/libraries/procedural/src/procedural/Procedural.h +++ b/libraries/procedural/src/procedural/Procedural.h @@ -36,6 +36,8 @@ const size_t MAX_PROCEDURAL_TEXTURE_CHANNELS{ 4 }; * The data used to define a Procedural shader material. * @typedef {object} ProceduralData * @property {number} version=1 - The version of the procedural shader. + * @property {string} vertexShaderURL - A link to a vertex shader. Currently, only GLSL shaders are supported. The shader must implement a different method depending on the version. + * 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. @@ -50,6 +52,7 @@ struct ProceduralData { // Rendering object descriptions, from userData uint8_t version { 0 }; QUrl fragmentShaderUrl; + QUrl vertexShaderUrl; QJsonObject uniforms; QJsonArray channels; }; @@ -110,6 +113,11 @@ public: void setIsFading(bool isFading) { _isFading = isFading; } void setDoesFade(bool doesFade) { _doesFade = doesFade; } + bool hasVertexShader() const; + void setBoundOperator(const std::function& boundOperator) { _boundOperator = boundOperator; } + bool hasBoundOperator() const { return (bool)_boundOperator; } + AABox getBound() { return _boundOperator(); } + gpu::Shader::Source _vertexSource; gpu::Shader::Source _vertexSourceSkinned; gpu::Shader::Source _vertexSourceSkinnedDQ; @@ -156,7 +164,11 @@ protected: uint64_t _firstCompile { 0 }; int32_t _frameCount { 0 }; - // Rendering object descriptions, from userData + // Rendering object descriptions + QString _vertexShaderSource; + QString _vertexShaderPath; + uint64_t _vertexShaderModified { 0 }; + NetworkShaderPointer _networkVertexShader; QString _fragmentShaderSource; QString _fragmentShaderPath; uint64_t _fragmentShaderModified { 0 }; @@ -187,6 +199,9 @@ private: mutable bool _isFading { false }; bool _doesFade { true }; ProceduralProgramKey _prevKey; + + std::function _boundOperator { nullptr }; + mutable std::mutex _mutex; }; @@ -210,6 +225,7 @@ public: 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, const uint64_t& created, const ProceduralProgramKey key = ProceduralProgramKey()) { _procedural.prepare(batch, position, size, orientation, created, key); @@ -217,6 +233,10 @@ public: void initializeProcedural(); + void setBoundOperator(const std::function& boundOperator) { _procedural.setBoundOperator(boundOperator); } + bool hasBoundOperator() const { return _procedural.hasBoundOperator(); } + AABox getBound() { return _procedural.getBound(); } + private: QString _proceduralString; Procedural _procedural; diff --git a/libraries/procedural/src/procedural/ProceduralCommon.slh b/libraries/procedural/src/procedural/ProceduralCommon.slh index 2915f096e6..e2344dc14e 100644 --- a/libraries/procedural/src/procedural/ProceduralCommon.slh +++ b/libraries/procedural/src/procedural/ProceduralCommon.slh @@ -60,6 +60,17 @@ LAYOUT_STD140(binding=PROCEDURAL_BUFFER_INPUTS) uniform standardInputsBuffer { #define iChannelResolution standardInputs.channelResolution #define iWorldOrientation standardInputs.worldOrientation +struct ProceduralVertexData { + vec4 position; + vec4 nonSkinnedPosition; // input only + vec3 normal; + vec3 nonSkinnedNormal; // input only + vec3 tangent; // input only + vec3 nonSkinnedTangent; // input only + vec4 color; + vec2 texCoord0; +}; + struct ProceduralFragment { vec3 normal; vec3 diffuse; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 9b42139892..713e3f4f93 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -110,6 +110,13 @@ ItemKey MeshPartPayload::getKey() const { } Item::Bound MeshPartPayload::getBound() const { + graphics::MaterialPointer material = _drawMaterials.empty() ? nullptr : _drawMaterials.top().material; + if (material && material->isProcedural() && material->isReady()) { + auto procedural = std::static_pointer_cast(_drawMaterials.top().material); + if (procedural->hasVertexShader() && procedural->hasBoundOperator()) { + return procedural->getBound(); + } + } return _worldBound; } @@ -175,6 +182,9 @@ void MeshPartPayload::render(RenderArgs* args) { if (!_drawMaterials.empty() && _drawMaterials.top().material && _drawMaterials.top().material->isProcedural() && _drawMaterials.top().material->isReady()) { + if (!(enableMaterialProceduralShaders && ENABLE_MATERIAL_PROCEDURAL_SHADERS)) { + return; + } auto procedural = std::static_pointer_cast(_drawMaterials.top().material); auto& schema = _drawMaterials.getSchemaBuffer().get(); glm::vec4 outColor = glm::vec4(ColorUtils::tosRGBVec3(schema._albedo), schema._opacity); diff --git a/libraries/render-utils/src/simple_procedural.slv b/libraries/render-utils/src/simple_procedural.slv index a8d494f72d..70bce451d3 100644 --- a/libraries/render-utils/src/simple_procedural.slv +++ b/libraries/render-utils/src/simple_procedural.slv @@ -20,9 +20,9 @@ <@if HIFI_USE_DEFORMED or HIFI_USE_DEFORMEDDQ@> <@include MeshDeformer.slh@> <@if HIFI_USE_DEFORMED@> - <$declareMeshDeformer(1, _SCRIBE_NULL, 1, _SCRIBE_NULL, 1)$> + <$declareMeshDeformer(1, 1, 1, _SCRIBE_NULL, 1)$> <@else@> - <$declareMeshDeformer(1, _SCRIBE_NULL, 1, 1, 1)$> + <$declareMeshDeformer(1, 1, 1, 1, 1)$> <@endif@> <$declareMeshDeformerActivation(1, 1)$> <@endif@> @@ -34,24 +34,56 @@ layout(location=RENDER_UTILS_ATTR_NORMAL_WS) out vec3 _normalWS; layout(location=RENDER_UTILS_ATTR_COLOR) out vec4 _color; layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01; +<@include procedural/ProceduralCommon.slh@> + +#line 1001 +//PROCEDURAL_BLOCK_BEGIN + +void getProceduralVertex(inout ProceduralVertexData proceduralData) {} + +//PROCEDURAL_BLOCK_END + +#line 2030 void main(void) { vec4 positionMS = inPosition; vec3 normalMS = inNormal.xyz; + vec3 tangentMS = inTangent.xyz; + vec4 color = color_sRGBAToLinear(inColor); + vec2 texCoord0 = inTexCoord0.st; <@if HIFI_USE_DEFORMED or HIFI_USE_DEFORMEDDQ@> - evalMeshDeformer(inPosition, positionMS, inNormal.xyz, normalMS, + evalMeshDeformer(inPosition, positionMS, inNormal.xyz, normalMS, inTangent.xyz, tangentMS, meshDeformer_doSkinning(_drawCallInfo.y), inSkinClusterIndex, inSkinClusterWeight, meshDeformer_doBlendshape(_drawCallInfo.y), gl_VertexID); <@endif@> +#if defined(PROCEDURAL_V1) || defined(PROCEDURAL_V2) || defined(PROCEDURAL_V3) + ProceduralVertexData proceduralData = ProceduralVertexData( + positionMS, + inPosition, + normalMS, + inNormal.xyz, + tangentMS, + inTangent.xyz, + color, + texCoord0 + ); + + getProceduralVertex(proceduralData); + + positionMS = proceduralData.position; + normalMS = proceduralData.normal; + color = proceduralData.color; + texCoord0 = proceduralData.texCoord0; +#endif + _positionMS = positionMS; _normalMS = normalMS; + _color = color; + _texCoord01 = vec4(texCoord0, 0.0, 0.0); TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); <$transformModelToEyeAndClipPos(cam, obj, positionMS, _positionES, gl_Position)$> <$transformModelToWorldDir(cam, obj, normalMS, _normalWS)$> - - _color = color_sRGBAToLinear(inColor); - _texCoord01 = vec4(inTexCoord0.st, 0.0, 0.0); } \ No newline at end of file